Move WireGuard gateway to Aegis

This commit is contained in:
Fabio Scotto di Santolo
2026-09-17 09:08:25 +02:00
parent add75d74e9
commit 77afdda0a3
13 changed files with 182 additions and 124 deletions

View File

@@ -59,10 +59,8 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
`ansible-playbook ansible/site.yml --limit atlas --tags storage,sharing,containers --check --diff` `ansible-playbook ansible/site.yml --limit atlas --tags storage,sharing,containers --check --diff`
- Atlas network/share hardening: - Atlas network/share hardening:
`ansible-playbook ansible/site.yml --limit atlas --tags hardening,sharing --check --diff` `ansible-playbook ansible/site.yml --limit atlas --tags hardening,sharing --check --diff`
- Atlas phase-one rootless services: - Prometheus/Aegis WireGuard gateway:
`ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff` `ansible-playbook ansible/site.yml --limit prometheus,aegis --tags wireguard --check --diff`
- Prometheus/Atlas WireGuard overlay:
`ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard --check --diff`
- DuckDNS config only: `ansible-playbook ansible/site.yml --limit prometheus --tags duckdns --check --diff` - DuckDNS config only: `ansible-playbook ansible/site.yml --limit prometheus --tags duckdns --check --diff`
## Conventions ## Conventions
@@ -151,36 +149,26 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
`1100`. Targeted SELinux is enforced persistently; a required reboot is reported but never initiated automatically. The primary LAN interface is assigned explicitly to the managed firewalld zone, and firewall rules are applied before NFS or SMB are started; their service state and TCP listeners are then verified. SMB3 exposes `Archive` to Vault-backed authorized accounts on mandatory encrypted, signed SMB3 over TCP/445 only and admits the configured LAN without host-specific exclusions. `1100`. Targeted SELinux is enforced persistently; a required reboot is reported but never initiated automatically. The primary LAN interface is assigned explicitly to the managed firewalld zone, and firewall rules are applied before NFS or SMB are started; their service state and TCP listeners are then verified. SMB3 exposes `Archive` to Vault-backed authorized accounts on mandatory encrypted, signed SMB3 over TCP/445 only and admits the configured LAN without host-specific exclusions.
- Atlas NPM and Immich share a rootful Podman network. NPM publishes HTTP/HTTPS, but its administration port remains - Atlas NPM and Immich share a rootful Podman network. NPM publishes HTTP/HTTPS, but its administration port remains
bound to `127.0.0.1:81`; do not expose it directly to the LAN or Internet. bound to `127.0.0.1:81`; do not expose it directly to the LAN or Internet.
- `profile_backend_phase1` is limited to rootless Navidrome and Syncthing user Quadlets on Atlas. Official Navidrome - Atlas is NAS-only. `profile_backend_phase1` is disabled in its host variables; do not reactivate its former
`0.63.2` uses SQLite below `/data` and does not support `ND_DATABASE_URL` or an external PostgreSQL backend; do not Navidrome or Syncthing Quadlets. Future application workloads belong to the Uranus K3s cluster.
recreate the obsolete Prometheus `navidromedb` service. The role requires the storage role's `zpool/media/music`, - `wireguard_overlay` manages `wg0` between Prometheus (`10.0.0.1`) and Aegis (`10.0.0.2`). It persists private
`zpool/services/data`, `zpool/services/data/navidrome`, and `zpool/services/data/syncthing` datasets at keys only on their respective hosts, exchanges only derived public keys through Ansible, and verifies a real peer
their exact paths. It never creates the pool. handshake. Prometheus opens `51820/udp`; Aegis is the LAN gateway. Its persistent IPv4 forwarding, narrowly scoped
- Keep `backend_phase1_start_services` false until the stopped Prometheus Navidrome data directory has been copied to WireGuard-to-LAN firewalld policy, and source masquerading permit Prometheus to reach LAN services without a static
Atlas and its SQLite database verified. The playbook renders the target but never migrates or deletes application route on the router. Prometheus includes `192.168.178.0/24` in Aegis' peer `AllowedIPs`; add the Uranus VIP there
data; after cutover, set the flag true to enable and start Navidrome and Syncthing. when it is assigned. After a firewalld reload, restore Prometheus' rootful Podman networking with
- Phase 1 must not change Prometheus' existing NPM deployment. NPM continues to be managed exactly by `profile_server`; `podman network reload --all` so the existing proxy stack retains container DNS.
use `10.0.0.2:4533` for Navidrome and `10.0.0.2:8384` for the Syncthing GUI. Syncthing does not use host networking:
its GUI, transfer, QUIC and discovery ports are explicitly published only on `10.0.0.2`; native transfer/discovery does not use the HTTP proxy.
- `wireguard_overlay` manages the required `wg0` path between Prometheus and Atlas, persists private keys only on their
respective hosts, exchanges only derived public keys, and verifies a real peer handshake. The initial run must
include both hosts. Prometheus
opens `51820/udp`; after creating the WireGuard firewalld zone, restore Prometheus' rootful Podman networking with
`podman network reload --all` so the existing proxy stack retains container DNS. The Atlas backend role admits
service ports only in the WireGuard firewalld zone.
## Atlas NAS TODO ## Atlas NAS TODO
Completed validation: the existing RAIDZ2 pool and datasets, SELinux, LAN firewall, SSH, Cockpit with Completed validation: the existing RAIDZ2 pool and datasets, SELinux, LAN firewall, SSH, Cockpit with
the selected 45Drives plugins, encrypted SMB3 `Archive`, the Aegis-only NFSv4 `photobook` export, and the selected 45Drives plugins, encrypted SMB3 `Archive`, the Aegis-only NFSv4 `photobook` export, and
the Prometheus--Atlas WireGuard path are operational. Aegis has validated NFSv4.2 read, write, delete, the former Prometheus--Atlas WireGuard path were operational. Aegis has validated NFSv4.2 read, write, delete,
and `all_squash` mapping to UID/GID `1100` end-to-end. and `all_squash` mapping to UID/GID `1100` end-to-end.
- Complete the Phase 1 Navidrome cutover: stop the Prometheus writer, copy and verify its complete - Validate the Prometheus--Aegis WireGuard gateway after migration: peer handshake and counters, Aegis IPv4
`/opt/navidrome/data/` directory (including SQLite sidecars) under forwarding and masquerading, and an NPM request from Prometheus to an Atlas LAN address. Add the Uranus VIP to
`/zpool/services/data/navidrome/`, then set `backend_phase1_start_services: true` and validate Prometheus' Aegis peer when the cluster control plane is assigned.
Navidrome on Atlas through WireGuard. Do not delete the source until a restore test succeeds. - Keep Atlas application Quadlets disabled. Plan Navidrome, Syncthing, Nextcloud, and Immich as Uranus workloads,
- Start and validate the rendered Syncthing Quadlet only after its device IDs, star topology, folders, with their storage and routing declared separately from the NAS baseline.
folder modes, ignore rules, and GUI/API protection are declared. Validate its GUI and native transfer
ports through WireGuard only.
- Decide whether a common SMB/NFS namespace is required. `Archive` (SMB) and `photobook` (NFS) are - Decide whether a common SMB/NFS namespace is required. `Archive` (SMB) and `photobook` (NFS) are
intentionally distinct today; only if a shared namespace is selected, finalize its UID/GID, group, intentionally distinct today; only if a shared namespace is selected, finalize its UID/GID, group,
and POSIX ACL model and test the same files through both protocols. and POSIX ACL model and test the same files through both protocols.
@@ -229,8 +217,9 @@ and `all_squash` mapping to UID/GID `1100` end-to-end.
- `aegis` is a remote Fedora IoT Raspberry Pi 4 node. Bootstrap it once with - `aegis` is a remote Fedora IoT Raspberry Pi 4 node. Bootstrap it once with
`ansible/bootstrap/aegis.bu`; the remaining configuration is applied by `profile_aegis` over SSH. `ansible/bootstrap/aegis.bu`; the remaining configuration is applied by `profile_aegis` over SSH.
- Fedora IoT is immutable. Do not add it to mutable Fedora package or shared dotfile roles. - Fedora IoT is immutable. Do not add it to mutable Fedora package or shared dotfile roles.
- `profile_aegis` owns the `nfs-utils` rpm-ostree layer used as the Atlas NFS client and reports the - `profile_aegis` owns the `nfs-utils` and `wireguard-tools` rpm-ostree layers and reports the required reboot
required reboot without initiating it. It also owns rootful Podman Quadlets, persistent container without initiating it. `wireguard_overlay` then configures Aegis as the WireGuard LAN gateway with persistent IPv4
forwarding, a scoped inter-zone policy, and source masquerading. It also owns rootful Podman Quadlets, persistent container
state under `/var/lib`, the Podman auto-update timer, LAN-restricted firewalld rules, and SSH hardening. Keep state under `/var/lib`, the Podman auto-update timer, LAN-restricted firewalld rules, and SSH hardening. Keep
`aegis_lan_subnet`, `aegis_adguard_web_port`, and `aegis_network_connection_uuid` host-specific; `aegis_lan_subnet`, `aegis_adguard_web_port`, and `aegis_network_connection_uuid` host-specific;
SSH permits only the declared SSH permits only the declared

View File

@@ -111,21 +111,19 @@ the Compose stack, update DNS, or perform a cutover.
The server profile installs platform-specific packages, Podman and podman-compose, declared systemd The server profile installs platform-specific packages, Podman and podman-compose, declared systemd
services, and firewalld. The manually activated `podman-compose-server` unit contains the existing services, and firewalld. The manually activated `podman-compose-server` unit contains the existing
Nginx Proxy Manager and Gitea services. The desired Compose file no longer includes Navidrome, Nginx Proxy Manager and Gitea services. The desired Compose file no longer includes Navidrome,
Syncthing, or the obsolete Navidrome PostgreSQL database. Navidrome and Syncthing belong to Atlas; Syncthing, or the obsolete Navidrome PostgreSQL database. Those future application workloads belong
official Navidrome uses SQLite instead. Applying the profile does not stop or remove legacy to Uranus rather than Atlas. Applying the profile does not stop or remove legacy containers and does
containers and does not delete `/opt/postgres/data`. not delete `/opt/postgres/data`.
Firewalld enables SSH, Cockpit (`9090/tcp`), HTTP and HTTPS. Nginx Proxy Manager publishes only Firewalld enables SSH, Cockpit (`9090/tcp`), HTTP and HTTPS. Nginx Proxy Manager publishes only
`80/tcp` and `443/tcp`; its administration interface is bound to `127.0.0.1:81` and can be reached `80/tcp` and `443/tcp`; its administration interface is bound to `127.0.0.1:81` and can be reached
from Ikaros or Nymph with the `npm-tunnel` Bash alias. Nextcloud remains disabled and the profile from Ikaros or Nymph with the `npm-tunnel` Bash alias. Nextcloud remains disabled and the profile
does not provision any `/srv/nextcloud` directories. does not provision any `/srv/nextcloud` directories.
The Atlas phase-one work does not change this NPM deployment or its persistent data. Once WireGuard NPM remains managed only by `profile_server`. Its WireGuard peer is Aegis (`10.0.0.2`), which forwards
and the Atlas services are active, configure the current NPM proxy hosts with Navidrome upstream selected requests to LAN addresses and source-NATs them so no static route is required on the router.
`http://10.0.0.2:4533` and Syncthing GUI upstream `http://10.0.0.2:8384`. Only the Syncthing web GUI Use an Atlas LAN address for any current NAS-backed upstream; when Uranus receives its VIP, add that VIP
uses NPM; synchronization traffic remains on explicitly published native ports bound only to the Atlas to Prometheus' Aegis peer `AllowedIPs` and declare the corresponding proxy target separately.
WireGuard address. Configure both Syncthing authentication and an appropriate NPM access policy before
publishing its GUI.
Server identity comes from `server_username`, `server_user_group`, and `server_user_home` in `ansible/inventory/group_vars/server.yml`. `server_username` defaults to `username`, but it can be overridden, for example: Server identity comes from `server_username`, `server_user_group`, and `server_user_home` in `ansible/inventory/group_vars/server.yml`. `server_username` defaults to `username`, but it can be overridden, for example:
@@ -193,8 +191,10 @@ ansible/bootstrap/generate-aegis-ign.sh --write IMAGE DEVICE
The controller manages it remotely as `pi@aegis`; unlike local desktop profiles, Aegis is The controller manages it remotely as `pi@aegis`; unlike local desktop profiles, Aegis is
intentionally an SSH inventory target. `profile_aegis` manages rootful Podman Quadlets for AdGuard intentionally an SSH inventory target. `profile_aegis` manages rootful Podman Quadlets for AdGuard
Home and iCloudPD, persistent data under `/var/lib`, the Podman auto-update timer, LAN-restricted Home and iCloudPD, persistent data under `/var/lib`, the Podman auto-update timer, LAN-restricted
firewalld rules, SSH key-only access for `pi`, the `nfs-utils` rpm-ostree layer required by the firewalld rules, SSH key-only access for `pi`, the `nfs-utils` and `wireguard-tools` rpm-ostree layers,
Atlas NFS client, and `wake-ikaros`. A new layered package deployment requires a manual reboot; the and `wake-ikaros`. `wireguard_overlay` makes Aegis the internal endpoint and LAN gateway for Prometheus:
it enables persistent IPv4 forwarding, installs a scoped WireGuard-to-LAN firewalld policy, and source-NATs
forwarded tunnel traffic so the router needs no static route. A new layered package deployment requires a manual reboot; the
role reports this condition but never reboots Aegis automatically. Set the host-local role reports this condition but never reboots Aegis automatically. Set the host-local
`aegis_lan_subnet`, `aegis_adguard_web_port`, and `aegis_network_connection_uuid` values before `aegis_lan_subnet`, `aegis_adguard_web_port`, and `aegis_network_connection_uuid` values before
applying it. The playbook permits applying it. The playbook permits
@@ -225,7 +225,7 @@ ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit aegis --tags dns --ask-become-pass ansible-playbook ansible/site.yml --limit aegis --tags dns --ask-become-pass
``` ```
Layer the Atlas NFS client package independently, then reboot Aegis manually when the role reports Layer the Aegis NFS and WireGuard client tools independently, then reboot Aegis manually when the role reports
that the new deployment is ready: that the new deployment is ready:
```bash ```bash
@@ -243,17 +243,9 @@ clients use NFSv4 and Windows/WSL clients use SMB; both are restricted to the co
For the first run, provide `vault_atlas_admin_password_hash`, `vault_atlas_samba_password`, and For the first run, provide `vault_atlas_admin_password_hash`, `vault_atlas_samba_password`, and
`vault_atlas_immich_db_password`. Bootstrap the host through its `vault_atlas_immich_db_password`. Bootstrap the host through its
existing administrator. Open `51820/udp` towards Prometheus in the provider firewall first, then existing administrator. The explicit pool gate is safe to repeat: the role creates the RAIDZ2 pool only when
include both WireGuard peers in the same idempotent playbook run: it is absent. Atlas no longer participates in the WireGuard overlay; its old interface is retired manually only after
Prometheus and Aegis have completed the replacement handshake.
```bash
ansible-playbook ansible/site.yml --limit prometheus,atlas \
-e atlas_connection_username=<existing-admin> \
-e atlas_create_pool=true
```
The explicit pool gate is safe to repeat: the role creates the RAIDZ2 pool only when it is absent.
WireGuard waits for a real peer handshake before the play continues.
`vault_atlas_admin_password_hash` must be an `/etc/shadow`-compatible hash, not a clear-text `vault_atlas_admin_password_hash` must be an `/etc/shadow`-compatible hash, not a clear-text
Cockpit password. Subsequent runs use `atlas_admin_username`. Atlas declares storage, sharing, and its Cockpit password. Subsequent runs use `atlas_admin_username`. Atlas declares storage, sharing, and its
@@ -279,49 +271,28 @@ and NPM Quadlets share one Podman network. Immich runs as `1100:1100`; Server an
and Photobook is mounted read-only at `/external/photobook`. NPM publishes ports `80` and `443`; its and Photobook is mounted read-only at `/external/photobook`. NPM publishes ports `80` and `443`; its
administration interface remains restricted to `127.0.0.1:81` for SSH-tunnel access. administration interface remains restricted to `127.0.0.1:81` for SSH-tunnel access.
Phase 1 is limited to rootless Navidrome and Syncthing user Quadlets on Atlas. It is enabled in the Atlas is a NAS-only host; its former phase-one Navidrome and Syncthing role is disabled. The
Atlas host configuration and can be set to `false` only for a deliberate suspension. Official Navidrome `0.63.2` uses its SQLite database below `/data`; it does `services/data` datasets remain storage namespaces, but no Atlas container service is enabled from this
not support `ND_DATABASE_URL` or an external PostgreSQL backend. The obsolete `navidromedb` service playbook. Future application workloads belong to the Uranus K3s cluster.
was therefore removed from Prometheus instead of being reproduced on Atlas. The role derives all
storage paths from the `zpool` mounted at `/zpool`: music is read-only at
`/zpool/media/music`, Navidrome application state and `navidrome.db` are stored at
`/zpool/services/data/navidrome`, and Syncthing persists at
`/zpool/services/data/syncthing`. `profile_atlas` creates these datasets when
`atlas_manage_storage` is enabled; the backend role verifies their exact mountpoints before starting
containers. The backend role never creates the pool. The separate `wireguard_overlay` role manages `wg0`
between Prometheus (`10.0.0.1`) and Atlas (`10.0.0.2`), generating private keys once
on their respective hosts and exchanging only public keys through Ansible. Prometheus alone opens
`51820/udp` publicly. When the WireGuard zone is created, Ansible reloads firewalld and immediately
reloads Prometheus' rootful Podman networks so the existing proxy stack retains container DNS and
connectivity. Backend ports are admitted only in the WireGuard firewalld zone.
`backend_phase1_start_services` stays false during the application-state transfer, so the first real The separate `wireguard_overlay` role manages `wg0` between Prometheus (`10.0.0.1`) and Aegis
backend run renders the Quadlets without creating an empty Atlas database. After stopping Navidrome (`10.0.0.2`), generating private keys once on their respective hosts and exchanging only public keys
on Prometheus, copy the complete `/opt/navidrome/data/` directory into through Ansible. Prometheus alone opens `51820/udp`. Aegis forwards only the declared overlay-to-LAN
`/zpool/services/data/navidrome/`, preserving `navidrome.db` and any SQLite sidecar files. Then set traffic and source-NATs it, so Atlas and future Uranus nodes require neither a VPN interface nor a router
this variable to true and rerun the role to enable and start Navidrome and Syncthing. The playbook static route. Prometheus' peer includes the LAN subnet in `AllowedIPs`; add the Uranus VIP there when it
never copies or deletes application data. exists. When the WireGuard zone is created, Ansible reloads firewalld and immediately reloads
Prometheus' rootful Podman networks so the existing proxy stack retains container DNS and connectivity.
Validate and render the Atlas services with: Validate the gateway with:
```bash ```bash
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \ ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit atlas --tags storage ansible-playbook ansible/site.yml --limit prometheus,aegis --tags wireguard --check --diff
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1
``` ```
For the cutover, stop the old Navidrome writer before copying its data directory, verify ownership by The first real WireGuard run must include both peers. If Fedora IoT has just layered `wireguard-tools`,
the Atlas `admin` account and confirm that the copied SQLite database is present before changing reboot Aegis manually and rerun the command without `--check`; the role then waits for a real peer
`backend_phase1_start_services: true` in `host_vars/atlas.yml`. Keep the source data and the stopped handshake.
legacy `navidromedb` container until Navidrome on Atlas and a restore test have been validated.
Snapshot retention, Syncthing topology, WireGuard/firewall validation, Prometheus backup pulls, Snapshot retention, Syncthing topology, WireGuard/firewall validation, Prometheus backup pulls,
encrypted Borg backups to a Hetzner Storage Box, USB backup, monitoring, and disaster-recovery tests encrypted Borg backups to a Hetzner Storage Box, USB backup, monitoring, and disaster-recovery tests
@@ -412,8 +383,8 @@ ansible-playbook ansible/site.yml --limit deadalus --tags ai_agents --check --di
| `profile_workstation_dev_wsl` | WSL development setup. | | `profile_workstation_dev_wsl` | WSL development setup. |
| `profile_server` | Server setup. | | `profile_server` | Server setup. |
| `profile_atlas` | Rocky Linux 9 NAS setup. | | `profile_atlas` | Rocky Linux 9 NAS setup. |
| `profile_backend_phase1` | Rootless Navidrome and Syncthing on Atlas. | | `profile_backend_phase1` | Retired Atlas phase-one role; disabled pending Uranus replacement. |
| `wireguard_overlay` | Prometheus/Atlas WireGuard overlay. | | `wireguard_overlay` | Prometheus/Aegis WireGuard LAN gateway. |
| `profile_aegis` | Fedora IoT always-on LAN node. | | `profile_aegis` | Fedora IoT always-on LAN node. |
| `dotfiles_common` | Shared user dotfiles. | | `dotfiles_common` | Shared user dotfiles. |
@@ -425,8 +396,8 @@ platform_void -> packages_void + services_runit
platform_void & graphical_desktop -> profile_desktop_common + profile_desktop_sway + profile_desktop_niri + profile_desktop_host platform_void & graphical_desktop -> profile_desktop_common + profile_desktop_sway + profile_desktop_niri + profile_desktop_host
platform_fedora -> packages_fedora + services_systemd platform_fedora -> packages_fedora + services_systemd
platform_rocky -> packages_rocky + services_systemd platform_rocky -> packages_rocky + services_systemd
wireguard_overlay -> wireguard_overlay (after platform_rocky)
role_aegis -> profile_aegis role_aegis -> profile_aegis
wireguard_overlay -> wireguard_overlay (after Aegis profile and platform_rocky)
atlas -> profile_atlas atlas -> profile_atlas
role_backend_phase1 -> profile_backend_phase1 (after atlas) role_backend_phase1 -> profile_backend_phase1 (after atlas)
rocky_server -> dotfiles_common + profile_server (after platform_rocky) rocky_server -> dotfiles_common + profile_server (after platform_rocky)
@@ -531,7 +502,7 @@ ansible-playbook ansible/site.yml --list-tags
| `sharing` | Atlas NFSv4 and SMB3 configuration. | | `sharing` | Atlas NFSv4 and SMB3 configuration. |
| `storage` | Atlas child ZFS datasets. | | `storage` | Atlas child ZFS datasets. |
| `tmux` | tmux configuration and plugins. | | `tmux` | tmux configuration and plugins. |
| `wireguard` | Prometheus/Atlas WireGuard overlay. | | `wireguard` | Prometheus/Aegis WireGuard LAN gateway. |
| `wsl` | WSL bootstrap and configuration. | | `wsl` | WSL bootstrap and configuration. |
## Bootstrapping a new machine ## Bootstrapping a new machine

View File

@@ -15,6 +15,26 @@ aegis_host_dns_servers:
- 192.168.178.1 - 192.168.178.1
aegis_host_dns_search_domains: aegis_host_dns_search_domains:
- fritz.box - fritz.box
# Aegis is the internal WireGuard endpoint and NAT gateway for the LAN.
wireguard_overlay_enabled: true
wireguard_address: 10.0.0.2/24
wireguard_enable_ipv4_forwarding: true
wireguard_reload_rootful_podman_networks: true
wireguard_forwarding_policies:
- name: aegis-wireguard-to-lan
ingress_zone: wireguard
egress_zone: public
source: 10.0.0.0/24
destination: "{{ aegis_lan_subnet }}"
masquerade: true
wireguard_peers:
- name: prometheus
host: prometheus
endpoint: "{{ hostvars['prometheus']['ansible_host'] }}:{{ hostvars['prometheus']['wireguard_listen_port'] }}"
allowed_ips:
- 10.0.0.0/24
persistent_keepalive: 25
aegis_ssh_authorized_keys: aegis_ssh_authorized_keys:
- name: ikaros - name: ikaros
key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrIxXjA3ffPwziKGR5gzc4gAoBehQPlnEMcXF4Wl0ZS ikaros" key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrIxXjA3ffPwziKGR5gzc4gAoBehQPlnEMcXF4Wl0ZS ikaros"

View File

@@ -52,19 +52,9 @@ atlas_manage_storage: true
atlas_manage_sharing: true atlas_manage_sharing: true
atlas_manage_media_stack: false atlas_manage_media_stack: false
wireguard_overlay_enabled: true # Atlas is a NAS-only host. WireGuard is retired manually on the host and is
wireguard_address: 10.0.0.2/24 # intentionally no longer managed here.
wireguard_peers: backend_phase1_enabled: false
- name: prometheus
host: prometheus
endpoint: "{{ hostvars['prometheus']['ansible_host'] }}:{{ hostvars['prometheus']['wireguard_listen_port'] }}"
allowed_ips:
- 10.0.0.0/24
persistent_keepalive: 25
backend_phase1_enabled: true
backend_phase1_start_services: false
backend_phase1_wireguard_address: 10.0.0.2
rocky_manage_openzfs_repo: true rocky_manage_openzfs_repo: true
rocky_manage_syncthing_binary: false rocky_manage_syncthing_binary: false

View File

@@ -21,10 +21,12 @@ wireguard_listen_port: 51820
wireguard_enable_ipv4_forwarding: true wireguard_enable_ipv4_forwarding: true
wireguard_reload_rootful_podman_networks: true wireguard_reload_rootful_podman_networks: true
wireguard_peers: wireguard_peers:
- name: atlas - name: aegis
host: atlas host: aegis
# TODO: Aggiungere VIP di Uranus in futuro
allowed_ips: allowed_ips:
- 10.0.0.2/32 - 10.0.0.2/32
- 192.168.178.0/24
host_packages: host_packages:
- cockpit - cockpit

View File

@@ -99,7 +99,7 @@ all:
wireguard_overlay: wireguard_overlay:
hosts: hosts:
prometheus: prometheus:
atlas: aegis:
role_backend_phase1: role_backend_phase1:
hosts: hosts:

View File

@@ -2,6 +2,7 @@
aegis_hostname: aegis aegis_hostname: aegis
aegis_layered_packages: aegis_layered_packages:
- nfs-utils - nfs-utils
- wireguard-tools
aegis_network_connection_name: "" aegis_network_connection_name: ""
aegis_network_connection_uuid: "" aegis_network_connection_uuid: ""
aegis_host_dns_servers: [] aegis_host_dns_servers: []

View File

@@ -1,6 +1,6 @@
--- ---
- name: Manage Aegis layered packages - name: Manage Aegis layered packages
tags: [aegis, packages, nfs] tags: [aegis, packages, nfs, wireguard]
community.general.rpm_ostree_pkg: community.general.rpm_ostree_pkg:
name: "{{ aegis_layered_packages }}" name: "{{ aegis_layered_packages }}"
state: present state: present
@@ -8,7 +8,7 @@
when: aegis_layered_packages | length > 0 when: aegis_layered_packages | length > 0
- name: Report reboot required for Aegis layered packages - name: Report reboot required for Aegis layered packages
tags: [aegis, packages, nfs] tags: [aegis, packages, nfs, wireguard]
ansible.builtin.debug: ansible.builtin.debug:
msg: Reboot Aegis to activate the newly layered packages, then rerun the playbook. msg: Reboot Aegis to activate the newly layered packages, then rerun the playbook.
when: aegis_layered_packages_result.needs_reboot | default(false) when: aegis_layered_packages_result.needs_reboot | default(false)

View File

@@ -10,6 +10,12 @@ wireguard_mtu: 1420
wireguard_firewalld_zone: wireguard wireguard_firewalld_zone: wireguard
wireguard_public_firewalld_zone: public wireguard_public_firewalld_zone: public
wireguard_enable_ipv4_forwarding: false wireguard_enable_ipv4_forwarding: false
# Legacy zone-level masquerading; policy-level masquerading is required for inter-zone forwarding.
wireguard_enable_masquerade: false
# A list of narrowly scoped inter-zone forwarding rules, rendered as persistent
# firewalld policies. Each item requires name, ingress_zone, egress_zone,
# source, and destination.
wireguard_forwarding_policies: []
wireguard_reload_rootful_podman_networks: false wireguard_reload_rootful_podman_networks: false
wireguard_handshake_retries: 12 wireguard_handshake_retries: 12
wireguard_handshake_delay: 5 wireguard_handshake_delay: 5

View File

@@ -14,10 +14,44 @@
Configure this host's WireGuard address and peers, and run the first Configure this host's WireGuard address and peers, and run the first
key bootstrap against every peer in the same play. key bootstrap against every peer in the same play.
- name: Install WireGuard userspace tools - name: Validate WireGuard forwarding policies
ansible.builtin.assert:
that:
- item.name is defined
- item.ingress_zone is defined
- item.egress_zone is defined
- item.source is defined
- item.destination is defined
fail_msg: >-
Every WireGuard forwarding policy requires name, ingress_zone,
egress_zone, source, and destination.
loop: "{{ wireguard_forwarding_policies }}"
loop_control:
label: "{{ item.name | default('unnamed policy') }}"
- name: Install WireGuard userspace tools on mutable hosts
ansible.builtin.dnf: ansible.builtin.dnf:
name: "{{ wireguard_packages }}" name: "{{ wireguard_packages }}"
state: present state: present
when: "'platform_fedora_iot' not in group_names"
- name: Require WireGuard userspace tools in the booted deployment
ansible.builtin.command:
argv:
- wg
- --version
changed_when: false
failed_when: false
register: wireguard_userspace_tools
- name: Require active WireGuard userspace tools
ansible.builtin.assert:
that:
- wireguard_userspace_tools.rc == 0
fail_msg: >-
WireGuard userspace tools are not present in the booted deployment.
On Fedora IoT, reboot after rpm-ostree layers wireguard-tools, then
rerun the WireGuard play.
- name: Create private WireGuard configuration directory - name: Create private WireGuard configuration directory
ansible.builtin.file: ansible.builtin.file:
@@ -124,12 +158,33 @@
permanent: true permanent: true
register: wireguard_firewalld_zone_result register: wireguard_firewalld_zone_result
- name: Reload firewalld after creating the WireGuard zone - name: Create the firewalld policy directory
ansible.builtin.file:
path: /etc/firewalld/policies
state: directory
owner: root
group: root
mode: "0755"
when: wireguard_forwarding_policies | length > 0
- name: Render WireGuard forwarding policies
ansible.builtin.template:
src: wireguard-forwarding-policy.xml.j2
dest: "/etc/firewalld/policies/{{ item.name }}.xml"
owner: root
group: root
mode: "0644"
loop: "{{ wireguard_forwarding_policies }}"
loop_control:
label: "{{ item.name }}"
register: wireguard_forwarding_policy_result
- name: Reload firewalld after WireGuard firewall changes
ansible.builtin.systemd: ansible.builtin.systemd:
name: firewalld.service name: firewalld.service
state: reloaded state: reloaded
when: when:
- wireguard_firewalld_zone_result is changed - wireguard_firewalld_zone_result is changed or (wireguard_forwarding_policy_result | default({})) is changed
- not ansible_check_mode - not ansible_check_mode
- name: Restore rootful Podman networking after firewalld reload - name: Restore rootful Podman networking after firewalld reload
@@ -142,7 +197,7 @@
register: wireguard_podman_network_reload register: wireguard_podman_network_reload
changed_when: wireguard_podman_network_reload.stdout_lines | length > 0 changed_when: wireguard_podman_network_reload.stdout_lines | length > 0
when: when:
- wireguard_firewalld_zone_result is changed - wireguard_firewalld_zone_result is changed or (wireguard_forwarding_policy_result | default({})) is changed
- wireguard_reload_rootful_podman_networks | bool - wireguard_reload_rootful_podman_networks | bool
- not ansible_check_mode - not ansible_check_mode
@@ -154,6 +209,14 @@
permanent: true permanent: true
immediate: true immediate: true
- name: Manage legacy WireGuard zone masquerading
ansible.posix.firewalld:
zone: "{{ wireguard_firewalld_zone }}"
masquerade: true
state: "{{ 'enabled' if wireguard_enable_masquerade | bool else 'disabled' }}"
permanent: true
immediate: true
- name: Permit this host's public WireGuard listener - name: Permit this host's public WireGuard listener
ansible.posix.firewalld: ansible.posix.firewalld:
port: "{{ wireguard_listen_port }}/udp" port: "{{ wireguard_listen_port }}/udp"

View File

@@ -11,7 +11,8 @@ ListenPort = {{ wireguard_listen_port }}
{% for peer in wireguard_peers %} {% for peer in wireguard_peers %}
[Peer] [Peer]
# {{ peer.name }} # {{ peer.name }}
PublicKey = {{ hostvars[peer.host].wireguard_public_key }} {% if peer.comment is defined %}# {{ peer.comment }}
{% endif %}PublicKey = {{ hostvars[peer.host].wireguard_public_key }}
AllowedIPs = {{ peer.allowed_ips | join(', ') }} AllowedIPs = {{ peer.allowed_ips | join(', ') }}
{% if peer.endpoint is defined %} {% if peer.endpoint is defined %}
Endpoint = {{ peer.endpoint }} Endpoint = {{ peer.endpoint }}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<policy target="CONTINUE">
<short>WireGuard forwarding: {{ item.name }}</short>
<description>Managed WireGuard-to-LAN forwarding policy.</description>
{% if item.masquerade | default(false) %}
<masquerade/>
{% endif %}
<ingress-zone name="{{ item.ingress_zone }}"/>
<egress-zone name="{{ item.egress_zone }}"/>
<rule family="ipv4">
<source address="{{ item.source }}"/>
<destination address="{{ item.destination }}"/>
<accept/>
</rule>
</policy>

View File

@@ -73,13 +73,6 @@
- packages_rocky - packages_rocky
- services_systemd - services_systemd
- name: Configure WireGuard overlay
hosts: wireguard_overlay
become: true
roles:
- wireguard_overlay
- name: Configure Aegis Fedora IoT profile - name: Configure Aegis Fedora IoT profile
hosts: role_aegis hosts: role_aegis
become: true become: true
@@ -87,6 +80,13 @@
roles: roles:
- profile_aegis - profile_aegis
- name: Configure WireGuard overlay
hosts: wireguard_overlay
become: true
roles:
- wireguard_overlay
- name: Configure Atlas NAS profile - name: Configure Atlas NAS profile
hosts: atlas hosts: atlas
become: true become: true