fix(deploy): home's tunnel rules loaded by systemd, outside wg-quick's AppArmor profile (ADR 0039, D9)
Ubuntu 26.04 confines wg-quick in an AppArmor profile whose nft sub-profile may read nothing in /etc/wireguard, so the tunnel failed to come up at home. The unit's drop-in (wg-quick-halkyon.conf, before-docker.conf before) loads the rules before the interface exists and deletes them after it is gone, so the tunnel never runs without them. ramiel, on 24.04, keeps PostUp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
4d8ea9c3a5
commit
3f6581d6e2
7 changed files with 54 additions and 21 deletions
|
|
@ -14,7 +14,7 @@ end of the tunnel.
|
||||||
| `docker-after-data.conf` | `docker.service.d/` and `containerd.service.d/` | both wait for it (D12) |
|
| `docker-after-data.conf` | `docker.service.d/` and `containerd.service.d/` | both wait for it (D12) |
|
||||||
| `wg-halkyon.conf` | `/etc/wireguard/wg-halkyon.conf` | home's end of the tunnel (D8) |
|
| `wg-halkyon.conf` | `/etc/wireguard/wg-halkyon.conf` | home's end of the tunnel (D8) |
|
||||||
| `wg-halkyon.nft` | `/etc/wireguard/wg-halkyon.nft` | what the tunnel may carry (D9, D17) |
|
| `wg-halkyon.nft` | `/etc/wireguard/wg-halkyon.nft` | what the tunnel may carry (D9, D17) |
|
||||||
| `wg-quick-before-docker.conf` | `wg-quick@wg-halkyon.service.d/` | the tunnel before Docker (D12) |
|
| `wg-quick-halkyon.conf` | `wg-quick@wg-halkyon.service.d/halkyon.conf` | the tunnel before Docker, its rules around it (D9, D12) |
|
||||||
|
|
||||||
`test/deploy/halkyon_li_forward_test.dart` holds these files to ADR 0039's rules.
|
`test/deploy/halkyon_li_forward_test.dart` holds these files to ADR 0039's rules.
|
||||||
|
|
||||||
|
|
@ -116,8 +116,8 @@ for the later.
|
||||||
sudo install -m 600 deploy/host/wg-halkyon.conf deploy/host/wg-halkyon.nft /etc/wireguard/
|
sudo install -m 600 deploy/host/wg-halkyon.conf deploy/host/wg-halkyon.nft /etc/wireguard/
|
||||||
sudoedit /etc/wireguard/wg-halkyon.conf
|
sudoedit /etc/wireguard/wg-halkyon.conf
|
||||||
sudo nft -c -f /etc/wireguard/wg-halkyon.nft # checks the rules, loads nothing
|
sudo nft -c -f /etc/wireguard/wg-halkyon.nft # checks the rules, loads nothing
|
||||||
sudo install -D -m 644 deploy/host/wg-quick-before-docker.conf \
|
sudo install -D -m 644 deploy/host/wg-quick-halkyon.conf \
|
||||||
/etc/systemd/system/wg-quick@wg-halkyon.service.d/before-docker.conf
|
/etc/systemd/system/wg-quick@wg-halkyon.service.d/halkyon.conf
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable --now wg-quick@wg-halkyon
|
sudo systemctl enable --now wg-quick@wg-halkyon
|
||||||
sudo wg show wg-halkyon # a handshake once ramiel is up too
|
sudo wg show wg-halkyon # a handshake once ramiel is up too
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
# Home's end of the tunnel through which ramiel publishes halkyon.li (ADR 0039, D8), as
|
# Home's end of the tunnel through which ramiel publishes halkyon.li (ADR 0039, D8), as
|
||||||
# /etc/wireguard/wg-halkyon.conf on the instance's host at home (root, 0600), brought
|
# /etc/wireguard/wg-halkyon.conf on the instance's host at home (root, 0600), brought
|
||||||
# up by wg-quick@wg-halkyon before Docker (wg-quick-before-docker.conf). Its private key
|
# up by wg-quick@wg-halkyon before Docker, its rules loaded around it (both
|
||||||
# is a file of its own beside it, never here. Setting it up: host/README.md.
|
# wg-quick-halkyon.conf). Its private key is a file of its own beside it, never here.
|
||||||
|
# Setting it up: host/README.md.
|
||||||
|
|
||||||
[Interface]
|
[Interface]
|
||||||
Address = 10.61.97.2/30
|
Address = 10.61.97.2/30
|
||||||
PostUp = wg set %i private-key /etc/wireguard/wg-halkyon.key
|
PostUp = wg set %i private-key /etc/wireguard/wg-halkyon.key
|
||||||
# From ramiel only Caddy's 443 and sshd's 22, nothing towards it (D9, D17).
|
|
||||||
PostUp = nft -f /etc/wireguard/wg-halkyon.nft
|
|
||||||
PreDown = nft delete table inet halkyon_tunnel
|
|
||||||
|
|
||||||
# ramiel, dialled by its address: home opens nothing on the FRITZ!Box, and a new home
|
# ramiel, dialled by its address: home opens nothing on the FRITZ!Box, and a new home
|
||||||
# address needs no change anywhere. The keepalive keeps the way back open through the
|
# address needs no change anywhere. The keepalive keeps the way back open through the
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# The rules around home's end of the tunnel from ramiel (ADR 0039, D9, D17), loaded by
|
# The rules around home's end of the tunnel from ramiel (ADR 0039, D9, D17), loaded by
|
||||||
# wg-quick as the tunnel comes up and deleted as it goes (wg-halkyon.conf), so they live
|
# systemd before the tunnel comes up and deleted after it goes (wg-quick-halkyon.conf),
|
||||||
# exactly as long as it. As /etc/wireguard/wg-halkyon.nft on the instance's host at home
|
# so it never runs without them. As /etc/wireguard/wg-halkyon.nft on the instance's host at home
|
||||||
# (root, 0600).
|
# (root, 0600).
|
||||||
#
|
#
|
||||||
# A table of its own, apart from Docker's and ufw's, at a priority before theirs: what it
|
# A table of its own, apart from Docker's and ufw's, at a priority before theirs: what it
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# The tunnel comes up before Docker (ADR 0039, D12): Caddy publishes 443 on the
|
|
||||||
# tunnel's address alone (compose.forwarded.yaml), which must exist when Docker starts
|
|
||||||
# the container. As /etc/systemd/system/wg-quick@wg-halkyon.service.d/before-docker.conf
|
|
||||||
# on the instance's host at home.
|
|
||||||
[Unit]
|
|
||||||
Before=docker.service
|
|
||||||
17
deploy/host/wg-quick-halkyon.conf
Normal file
17
deploy/host/wg-quick-halkyon.conf
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# What wg-quick@wg-halkyon needs beyond wg-quick itself on the instance's host at home
|
||||||
|
# (ADR 0039, D9, D12), as /etc/systemd/system/wg-quick@wg-halkyon.service.d/halkyon.conf.
|
||||||
|
#
|
||||||
|
# The tunnel comes up before Docker: Caddy publishes 443 on the tunnel's address alone
|
||||||
|
# (compose.forwarded.yaml), which must exist when Docker starts the container.
|
||||||
|
#
|
||||||
|
# Its rules (wg-halkyon.nft) are loaded here, before the interface exists, and deleted
|
||||||
|
# after it is gone, so the tunnel is never up without them: they match it by name. Not
|
||||||
|
# by wg-quick's PostUp, as on ramiel: Ubuntu 26.04 confines wg-quick in an AppArmor
|
||||||
|
# profile whose nft may read nothing in /etc/wireguard (found 8 Nov 2026), and which
|
||||||
|
# no local override reaches. systemd runs these unconfined.
|
||||||
|
[Unit]
|
||||||
|
Before=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=/usr/sbin/nft -f /etc/wireguard/wg-halkyon.nft
|
||||||
|
ExecStopPost=-/usr/sbin/nft delete table inet halkyon_tunnel
|
||||||
|
|
@ -94,8 +94,11 @@
|
||||||
balthasar dials out with `PersistentKeepalive = 25`, so the FRITZ!Box opens nothing
|
balthasar dials out with `PersistentKeepalive = 25`, so the FRITZ!Box opens nothing
|
||||||
and a new home address needs no change. Each side's `AllowedIPs` is the other's
|
and a new home address needs no change. Each side's `AllowedIPs` is the other's
|
||||||
address alone. The keys stay in `/etc/wireguard/` (root, 0600) and in Proton Pass.
|
address alone. The keys stay in `/etc/wireguard/` (root, 0600) and in Proton Pass.
|
||||||
- **D9 What the tunnel may carry**, by rules `PostUp` sets and `PreDown` removes, in
|
- **D9 What the tunnel may carry**, in chains of their own as ADR 0032's, living as long
|
||||||
chains of their own as ADR 0032's:
|
as the tunnel: on ramiel set by wg-quick's `PostUp` and removed by its `PreDown`; at
|
||||||
|
home loaded by systemd before the interface exists and deleted after it is gone
|
||||||
|
(`ExecStartPre`, `ExecStopPost`), as Ubuntu 26.04's AppArmor profile for wg-quick lets
|
||||||
|
its `nft` read nothing in `/etc/wireguard` (found 8 Nov 2026):
|
||||||
- on ramiel: HAProxy's connections to `10.61.97.2:443`, nothing else; nothing new
|
- on ramiel: HAProxy's connections to `10.61.97.2:443`, nothing else; nothing new
|
||||||
from the tunnel into ramiel (so a taken-over balthasar can't reach ramiel's sshd or
|
from the tunnel into ramiel (so a taken-over balthasar can't reach ramiel's sshd or
|
||||||
Forgejo);
|
Forgejo);
|
||||||
|
|
|
||||||
|
|
@ -222,9 +222,30 @@ void main() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('comes up before Docker, which publishes on its address', () {
|
group("wg-quick's unit", () {
|
||||||
final dropIn = read('deploy/host/wg-quick-before-docker.conf');
|
final dropIn = read('deploy/host/wg-quick-halkyon.conf');
|
||||||
expect(dropIn, contains('Before=docker.service'));
|
|
||||||
|
test('comes up before Docker, which publishes on its address', () {
|
||||||
|
expect(dropIn, contains('Before=docker.service'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('loads the rules before the tunnel exists and deletes them after '
|
||||||
|
"it is gone, outside wg-quick's AppArmor profile, which lets nft "
|
||||||
|
'read nothing in /etc/wireguard (Ubuntu 26.04)', () {
|
||||||
|
expect(
|
||||||
|
dropIn,
|
||||||
|
contains(
|
||||||
|
'ExecStartPre=/usr/sbin/nft -f /etc/wireguard/wg-halkyon.nft',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
dropIn,
|
||||||
|
contains(
|
||||||
|
'ExecStopPost=-/usr/sbin/nft delete table inet halkyon_tunnel',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(config, isNot(contains('nft')));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('lets sshd take keys alone, for two users alone', () {
|
test('lets sshd take keys alone, for two users alone', () {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue