1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# The imported shared plays target {{ ansible_limit }} and match nothing
# without it, so a bare run would apply this host's plays and skip theirs.
- name: Require --limit
hosts: legacy-of-lunatic-kingdom
gather_facts: false
tags: always
tasks:
- name: Fail without --limit
ansible.builtin.assert:
that: ansible_limit is defined
fail_msg: >-
Run with --limit legacy-of-lunatic-kingdom; without it the imported
shared plays match nothing and this playbook half-applies.
quiet: true
- name: Basic requirements
hosts: legacy-of-lunatic-kingdom
become: true
tasks:
- name: Install basic packages
ansible.builtin.apt:
pkg:
- git
- net-tools # ifconfig
- neovim
- prometheus-node-exporter
- prometheus-node-exporter-collectors # apt_info + reboot-required metrics
- python3-lxml # syncthing config (community.general.xml)
- rsync
state: present
update_cache: true
- name: Configure node-exporter collectors
vars:
# The systemd + apt collectors expose unattended-upgrades health to the
# node-exporter target (last run, pending security updates, reboot
# needed). See playbooks/unattended-upgrades.yml.
node_exporter_args:
- --collector.textfile.directory=/var/lib/prometheus/node-exporter
- --collector.systemd
- --collector.systemd.unit-include=apt-daily.*\.(timer|service)
- >-
--collector.filesystem.mount-points-exclude=^/(dev|proc|run|sys|media|var/lib/containers/storage/.+)($|/)
ansible.builtin.lineinfile:
path: /etc/default/prometheus-node-exporter
regexp: '^ARGS='
line: 'ARGS="{{ node_exporter_args | join(" ") }}"'
create: true
mode: '0644'
notify: Restart node exporter
# Periodically regenerates apt.prom (pending upgrades, reboot-required) in
# the textfile directory above.
- name: Enable node-exporter apt collector
ansible.builtin.systemd:
name: prometheus-node-exporter-apt.timer
enabled: true
state: started
handlers:
- name: Restart node exporter
ansible.builtin.systemd:
name: prometheus-node-exporter
state: restarted
# TODO disallow network access from non-tailscale interfaces
- import_playbook: ../playbooks/secure.yml # noqa: name[play]
- import_playbook: ../playbooks/tailscale.yml # noqa: name[play]
- import_playbook: ../playbooks/podman.yml # noqa: name[play]
- import_playbook: caddy.yml # noqa: name[play]
- import_playbook: ../playbooks/syncthing.yml # noqa: name[play]
- import_playbook: ../playbooks/unattended-upgrades.yml # noqa: name[play]
- import_playbook: ../playbooks/journal-upload.yml # noqa: name[play]
- name: Host a Restic repository
tags: restic
hosts: legacy-of-lunatic-kingdom
become: true
tasks:
# Applied before the account changes below so sftp never briefly depends
# on the user's shell; internal-sftp serves absolute paths, so the
# repository URL stays as it is.
- name: Restrict the restic user to sftp
ansible.builtin.blockinfile:
path: /etc/ssh/sshd_config
block: |
Match User restic
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
validate: /usr/sbin/sshd -t -f %s
mode: "0644"
notify: Restart sshd
- name: Create Restic user
ansible.builtin.user:
name: restic
system: true
# sshd's account check under UsePAM no refuses locked ('!') users,
# which is how Debian creates system users. '*' is not locked but
# can never match a password.
password: "*"
# internal-sftp needs no shell.
shell: /usr/sbin/nologin
state: present
- name: Add SSH key for restic user
ansible.posix.authorized_key:
user: restic
key: "{{ item }}"
# ForceCommand confines; restrict also kills forwarding and pty at
# the auth layer.
key_options: restrict
loop:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrlVay1GlpCuhYSUup/K/gRYmltycViPBAdZ8t9OKtu # lotus-land-story
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPgX0tA28NO3djYaV++JxggR0t47xou+R0HQVwVjzj8s # 1password
- name: Create Restic directory
ansible.builtin.file:
path: "{{ (data_dir, 'restic') | path_join }}"
state: directory
owner: restic
group: restic
mode: "0755"
handlers:
- name: Restart sshd
ansible.builtin.systemd:
name: sshd
state: restarted
- name: Keep dhcpcd out of resolv.conf
# Trixie's ifupdown uses dhcpcd for the dhcp method, and it rewrites
# /etc/resolv.conf on every lease event, stomping the file tailscaled owns
# until its next check restores MagicDNS. Takes effect at the next dhcpcd
# start, i.e. a reboot or a restart of the networking service.
tags: dns
hosts: legacy-of-lunatic-kingdom
become: true
tasks:
- name: Disable dhcpcd resolv.conf hook
ansible.builtin.lineinfile:
path: /etc/dhcpcd.conf
line: nohook resolv.conf
state: present
# TODO
# - jellyfin
# - immich
# vim: ft=yaml.ansible