mirror of
https://github.com/fscotto/infra.git
synced 2026-09-27 11:02:47 +00:00
Enable Atlas NAS management and document bootstrap workflow
This commit is contained in:
26
AGENTS.md
26
AGENTS.md
@@ -56,7 +56,7 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
|
|||||||
- Atlas media stack:
|
- Atlas media stack:
|
||||||
`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 phase-one rootless services:
|
- Atlas phase-one rootless services:
|
||||||
`ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff -e backend_phase1_enabled=true`
|
`ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff`
|
||||||
- Prometheus/Atlas WireGuard overlay:
|
- Prometheus/Atlas WireGuard overlay:
|
||||||
`ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard --check --diff`
|
`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`
|
||||||
@@ -126,15 +126,16 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
|||||||
- `atlas` is a remote Rocky Linux 9 NAS. Keep its connection, LAN, pool and mountpoint values in
|
- `atlas` is a remote Rocky Linux 9 NAS. Keep its connection, LAN, pool and mountpoint values in
|
||||||
`host_vars/atlas.yml`. Bootstrap it once with `-e atlas_connection_username=<existing-admin>`;
|
`host_vars/atlas.yml`. Bootstrap it once with `-e atlas_connection_username=<existing-admin>`;
|
||||||
subsequent runs use the dedicated Atlas account.
|
subsequent runs use the dedicated Atlas account.
|
||||||
- The pool is pre-existing: never add pool creation, disk partitioning, RAIDZ creation, rollback,
|
- The pool is normally pre-existing. A one-time bootstrap may create it only when `atlas_create_pool=true`
|
||||||
or destruction to the Atlas profile.
|
is explicitly supplied and `atlas_zpool_disks` contains exactly four real `/dev/disk/by-id/...` paths.
|
||||||
- `atlas_manage_storage`, `atlas_manage_firewall`, and `atlas_manage_media_stack` remain false until their placeholders
|
Never partition, force, destroy, roll back, or modify the vdev layout of an existing pool.
|
||||||
and Vault inputs are replaced; only then may the profile manage datasets, shares, LAN-restricted firewall rules, and
|
- `atlas_manage_storage`, `atlas_manage_sharing`, and `atlas_manage_firewall` are enabled in Atlas host vars as
|
||||||
rootful media Quadlets.
|
the declared steady state; set one false only for a deliberate suspension. `atlas_manage_media_stack` remains false
|
||||||
|
until the future rootful Immich stack has its required Vault inputs and target validation.
|
||||||
- Atlas requires `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash` for Cockpit
|
- Atlas requires `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash` for Cockpit
|
||||||
and, when the relevant gates are enabled, `vault_atlas_samba_password` and
|
and, while sharing is enabled, `vault_atlas_samba_password`. The future rootful media stack also requires
|
||||||
`vault_atlas_immich_db_password`. Never print these values.
|
`vault_atlas_immich_db_password`. Never print these values.
|
||||||
- Atlas creates the complete declared hierarchy only under the verified pre-existing pool: `work`, `archive`,
|
- Atlas creates the complete declared hierarchy only under the verified existing or explicitly bootstrapped pool: `work`, `archive`,
|
||||||
`archive/app_data`, `archive/app_data/navidrome`, `archive/app_data/syncthing`, `media`, `media/music`,
|
`archive/app_data`, `archive/app_data/navidrome`, `archive/app_data/syncthing`, `media`, `media/music`,
|
||||||
`media/photobook`, `backups`, `backups/services`, and `backup_prometheus`. `backups/services` has a `500G`
|
`media/photobook`, `backups`, `backups/services`, and `backup_prometheus`. `backups/services` has a `500G`
|
||||||
refreservation. There is no separate legacy `zpool/syncthing` dataset.
|
refreservation. There is no separate legacy `zpool/syncthing` dataset.
|
||||||
@@ -157,14 +158,13 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
|||||||
use `10.0.0.2:4533` for Navidrome and `10.0.0.2:8384` for the Syncthing GUI. Native Syncthing transfer/discovery does
|
use `10.0.0.2:4533` for Navidrome and `10.0.0.2:8384` for the Syncthing GUI. Native Syncthing transfer/discovery does
|
||||||
not use the HTTP proxy.
|
not use the HTTP proxy.
|
||||||
- `wireguard_overlay` manages the required `wg0` path between Prometheus and Atlas, persists private keys only on their
|
- `wireguard_overlay` manages the required `wg0` path between Prometheus and Atlas, persists private keys only on their
|
||||||
respective hosts, and exchanges only derived public keys. The first gated run must include both hosts. Prometheus
|
respective hosts, and exchanges only derived public keys. The initial run must include both hosts. Prometheus
|
||||||
opens `51820/udp`; the Atlas backend role admits service ports only in the WireGuard firewalld zone.
|
opens `51820/udp`; the Atlas backend role admits service ports only in the WireGuard firewalld zone.
|
||||||
|
|
||||||
## Atlas NAS TODO
|
## Atlas NAS TODO
|
||||||
- Replace every Atlas `CHANGEME` value, provide the required Vault variables and validate the first
|
- Provide the required Vault variables and validate the first remote bootstrap on the real Rocky Linux 9 host.
|
||||||
remote bootstrap on the real Rocky Linux 9 host. Enable `atlas_manage_storage` first and
|
Before the first apply, confirm the pool, mountpoints, LAN subnet and firewalld zone. Keep
|
||||||
`atlas_manage_firewall` only after confirming the pool, mountpoints, LAN subnet and firewalld zone. Enable
|
`atlas_manage_media_stack` disabled until `/dev/dri`, the container paths and the Immich database secret are validated.
|
||||||
`atlas_manage_media_stack` last, after validating `/dev/dri`, the container paths and the Immich database secret.
|
|
||||||
- Validate the complete baseline on the target: OpenZFS kmod loading, existing pool import, dataset
|
- Validate the complete baseline on the target: OpenZFS kmod loading, existing pool import, dataset
|
||||||
mounts, SSH reconnect, Cockpit and all selected 45Drives plugins, NFSv4, SMB and Syncthing.
|
mounts, SSH reconnect, Cockpit and all selected 45Drives plugins, NFSv4, SMB and Syncthing.
|
||||||
- Finalize dataset properties and the shared UID/GID, group and POSIX ACL model; test the same files
|
- Finalize dataset properties and the shared UID/GID, group and POSIX ACL model; test the same files
|
||||||
|
|||||||
44
README.it.md
44
README.it.md
@@ -246,13 +246,14 @@ ansible-playbook ansible/site.yml --limit prometheus -e server_username=myuser -
|
|||||||
|
|
||||||
## NAS
|
## NAS
|
||||||
|
|
||||||
`atlas` e un NAS Rocky Linux 9 raggiunto tramite SSH. Il pool ZFS esiste gia: il profilo gestisce
|
`atlas` e un NAS Rocky Linux 9 raggiunto tramite SSH. Normalmente il pool ZFS esiste gia e il profilo
|
||||||
solo i dataset figli e non deve mai creare, partizionare, distruggere, fare rollback o modificare il
|
gestisce solo i dataset figli. Un bootstrap RAIDZ2 una tantum e disponibile solo con conferma esplicita
|
||||||
pool. I client Linux usano NFSv4, quelli Windows/WSL SMB; entrambi restano limitati alla LAN
|
(`atlas_create_pool=true`) e quattro percorsi reali e verificati `/dev/disk/by-id/...` in
|
||||||
|
`atlas_zpool_disks`. Non partiziona, forza, distrugge, esegue rollback o modifica il layout vdev di un
|
||||||
|
pool esistente. I client Linux usano NFSv4, quelli Windows/WSL SMB; entrambi restano limitati alla LAN
|
||||||
configurata.
|
configurata.
|
||||||
|
|
||||||
Per il primo avvio sostituire i placeholder di host, pool, mount root, LAN e IP di Aegis e
|
Per il primo avvio fornire `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash`,
|
||||||
fornire `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash`,
|
|
||||||
`vault_atlas_samba_password` e `vault_atlas_immich_db_password`. Eseguire il bootstrap tramite
|
`vault_atlas_samba_password` e `vault_atlas_immich_db_password`. Eseguire il bootstrap tramite
|
||||||
l'amministratore esistente:
|
l'amministratore esistente:
|
||||||
|
|
||||||
@@ -262,13 +263,12 @@ ansible-playbook ansible/site.yml --limit atlas \
|
|||||||
```
|
```
|
||||||
|
|
||||||
`vault_atlas_admin_password_hash` deve essere un hash compatibile con `/etc/shadow`, non una
|
`vault_atlas_admin_password_hash` deve essere un hash compatibile con `/etc/shadow`, non una
|
||||||
password Cockpit in chiaro. Le esecuzioni successive usano `atlas_admin_username`. Abilitare
|
password Cockpit in chiaro. Le esecuzioni successive usano `atlas_admin_username`. Atlas dichiara
|
||||||
`atlas_manage_storage` solo dopo aver verificato pool e mountpoint esistenti; abilitare
|
abilitati storage, condivisioni e regole firewall LAN. Prima della prima applicazione verificare pool e
|
||||||
`atlas_manage_firewall` solo dopo aver verificato subnet LAN e zona firewalld attiva. Abilitare
|
mountpoint esistenti, subnet LAN e zona firewalld attiva. `atlas_manage_media_stack` resta disabilitato
|
||||||
`atlas_manage_media_stack` per ultimo, dopo aver verificato `/dev/dri`, i percorsi dei container e il
|
finche non saranno validati `/dev/dri`, i percorsi dei container e il segreto del database Immich.
|
||||||
segreto del database Immich.
|
|
||||||
|
|
||||||
Con la gestione storage attiva, Atlas crea l'intera gerarchia sotto il pool `zpool` preesistente:
|
Con la gestione storage attiva, Atlas crea l'intera gerarchia sotto il pool `zpool` esistente o creato esplicitamente:
|
||||||
`work`, `archive`, `archive/app_data`, i dataset applicativi separati
|
`work`, `archive`, `archive/app_data`, i dataset applicativi separati
|
||||||
`archive/app_data/navidrome` e `archive/app_data/syncthing`, `media`, `media/music`,
|
`archive/app_data/navidrome` e `archive/app_data/syncthing`, `media`, `media/music`,
|
||||||
`media/photobook`, `backups`, `backups/services` e `backup_prometheus`. I dataset applicativi e
|
`media/photobook`, `backups`, `backups/services` e `backup_prometheus`. I dataset applicativi e
|
||||||
@@ -284,15 +284,15 @@ Redis, PostgreSQL e NPM condividono una rete Podman. Immich viene eseguito come
|
|||||||
ML ricevono `/dev/dri` e Photobook e montato in sola lettura su `/external/photobook`. NPM pubblica `80` e
|
ML ricevono `/dev/dri` e Photobook e montato in sola lettura su `/external/photobook`. NPM pubblica `80` e
|
||||||
`443`, mentre l'amministrazione resta vincolata a `127.0.0.1:81` per l'accesso tramite tunnel SSH.
|
`443`, mentre l'amministrazione resta vincolata a `127.0.0.1:81` per l'accesso tramite tunnel SSH.
|
||||||
|
|
||||||
La fase 1 e limitata ai Quadlet utente rootless di Navidrome e Syncthing su Atlas ed e protetta dal
|
La fase 1 e limitata ai Quadlet utente rootless di Navidrome e Syncthing su Atlas. E abilitata nella
|
||||||
gate `backend_phase1_enabled`. Navidrome ufficiale `0.63.2` usa il database SQLite sotto `/data` e
|
configurazione host di Atlas e puo essere impostata a `false` solo per una sospensione intenzionale. Navidrome ufficiale `0.63.2` usa il database SQLite sotto `/data` e
|
||||||
non supporta `ND_DATABASE_URL` ne un backend PostgreSQL esterno. Il servizio obsoleto `navidromedb`
|
non supporta `ND_DATABASE_URL` ne un backend PostgreSQL esterno. Il servizio obsoleto `navidromedb`
|
||||||
e quindi rimosso da Prometheus invece di essere replicato su Atlas. Il ruolo deriva i percorsi dal
|
e quindi rimosso da Prometheus invece di essere replicato su Atlas. Il ruolo deriva i percorsi dal
|
||||||
pool esistente `zpool`, montato in `/zpool`: musica in sola lettura da `/zpool/media/music`, stato
|
pool `zpool`, montato in `/zpool`: musica in sola lettura da `/zpool/media/music`, stato
|
||||||
applicativo Navidrome e `navidrome.db` in `/zpool/archive/app_data/navidrome` e dati Syncthing in
|
applicativo Navidrome e `navidrome.db` in `/zpool/archive/app_data/navidrome` e dati Syncthing in
|
||||||
`/zpool/archive/app_data/syncthing`. `profile_atlas` crea questi dataset quando
|
`/zpool/archive/app_data/syncthing`. `profile_atlas` crea questi dataset quando
|
||||||
`atlas_manage_storage` e attivo; il ruolo backend verifica i mountpoint esatti prima di avviare i
|
`atlas_manage_storage` e attivo; il ruolo backend verifica i mountpoint esatti prima di avviare i
|
||||||
container. Nessun ruolo crea il pool. Il ruolo separato `wireguard_overlay`
|
container. Il ruolo backend non crea mai il pool. Il ruolo separato `wireguard_overlay`
|
||||||
gestisce `wg0` tra Prometheus (`10.0.0.1`) e Atlas (`10.0.0.2`), genera una sola volta le chiavi
|
gestisce `wg0` tra Prometheus (`10.0.0.1`) e Atlas (`10.0.0.2`), genera una sola volta le chiavi
|
||||||
private sui rispettivi host e scambia tramite Ansible soltanto quelle pubbliche. Solo Prometheus apre
|
private sui rispettivi host e scambia tramite Ansible soltanto quelle pubbliche. Solo Prometheus apre
|
||||||
pubblicamente `51820/udp`. Le porte backend sono ammesse esclusivamente nella zona firewalld WireGuard.
|
pubblicamente `51820/udp`. Le porte backend sono ammesse esclusivamente nella zona firewalld WireGuard.
|
||||||
@@ -308,20 +308,16 @@ Validare e generare i servizi Atlas con:
|
|||||||
|
|
||||||
```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 atlas --tags storage
|
||||||
-e atlas_manage_storage=true
|
|
||||||
|
|
||||||
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
||||||
ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard \
|
ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard
|
||||||
-e wireguard_overlay_enabled=true
|
|
||||||
|
|
||||||
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
||||||
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff \
|
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff
|
||||||
-e backend_phase1_enabled=true
|
|
||||||
|
|
||||||
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
||||||
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 \
|
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1
|
||||||
-e backend_phase1_enabled=true
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Per il cutover, arrestare il vecchio Navidrome prima di copiare la sua directory dati, verificare
|
Per il cutover, arrestare il vecchio Navidrome prima di copiare la sua directory dati, verificare
|
||||||
@@ -538,7 +534,7 @@ ansible-lint ansible/roles/<role>
|
|||||||
yamllint ansible/path/to/file.yml
|
yamllint ansible/path/to/file.yml
|
||||||
podman-compose -f /opt/docker/server/docker-compose.yml config
|
podman-compose -f /opt/docker/server/docker-compose.yml config
|
||||||
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
|
||||||
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff -e backend_phase1_enabled=true
|
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tag supportati dal playbook
|
## Tag supportati dal playbook
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -204,13 +204,13 @@ ansible-playbook ansible/site.yml --limit aegis --check --diff --ask-become-pass
|
|||||||
|
|
||||||
## NAS
|
## NAS
|
||||||
|
|
||||||
`atlas` is a Rocky Linux 9 NAS reached through SSH. Its pool already exists: the profile only
|
`atlas` is a Rocky Linux 9 NAS reached through SSH. Normally its pool already exists and the profile
|
||||||
manages child datasets and must never create, partition, destroy, roll back, or otherwise alter the
|
only manages child datasets. A one-time RAIDZ2 bootstrap is available only with explicit confirmation
|
||||||
pool itself. Linux clients use NFSv4 and Windows/WSL clients use SMB; both are restricted to the
|
(`atlas_create_pool=true`) and exactly four verified `/dev/disk/by-id/...` paths in `atlas_zpool_disks`.
|
||||||
configured LAN.
|
It never partitions, forces, destroys, rolls back, or changes the vdev layout of an existing pool. Linux
|
||||||
|
clients use NFSv4 and Windows/WSL clients use SMB; both are restricted to the configured LAN.
|
||||||
|
|
||||||
For the first run, replace the Atlas host, pool, mount-root, LAN, and Aegis-IP
|
For the first run, provide `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash`,
|
||||||
placeholders and provide `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash`,
|
|
||||||
`vault_atlas_samba_password`, and `vault_atlas_immich_db_password`. Bootstrap the host through its
|
`vault_atlas_samba_password`, and `vault_atlas_immich_db_password`. Bootstrap the host through its
|
||||||
existing administrator:
|
existing administrator:
|
||||||
|
|
||||||
@@ -220,13 +220,13 @@ ansible-playbook ansible/site.yml --limit atlas \
|
|||||||
```
|
```
|
||||||
|
|
||||||
`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`. Enable `atlas_manage_storage` only after
|
Cockpit password. Subsequent runs use `atlas_admin_username`. Atlas declares storage, sharing, and its
|
||||||
checking the existing pool and mountpoints; enable `atlas_manage_firewall` only after checking the LAN
|
LAN firewall rules enabled. Before the first apply, check the existing pool and mountpoints, LAN subnet,
|
||||||
subnet and active firewalld zone. Enable `atlas_manage_media_stack` last, after validating `/dev/dri`,
|
and active firewalld zone. `atlas_manage_media_stack` remains disabled until `/dev/dri`, the container
|
||||||
the container paths and the Immich database secret.
|
paths, and the Immich database secret are validated.
|
||||||
|
|
||||||
With storage management enabled, Atlas creates the complete dataset hierarchy below the pre-existing
|
With storage management enabled, Atlas creates the complete dataset hierarchy below the existing or
|
||||||
`zpool`: `work`, `archive`, `archive/app_data`, the separate `archive/app_data/navidrome` and
|
explicitly bootstrapped `zpool`: `work`, `archive`, `archive/app_data`, the separate `archive/app_data/navidrome` and
|
||||||
`archive/app_data/syncthing` application datasets, `media`, `media/music`, `media/photobook`,
|
`archive/app_data/syncthing` application datasets, `media`, `media/music`, `media/photobook`,
|
||||||
`backups`, `backups/services`, and `backup_prometheus`. Application/archive datasets use `zstd`,
|
`backups`, `backups/services`, and `backup_prometheus`. Application/archive datasets use `zstd`,
|
||||||
while media, Syncthing and service-backup datasets use `lz4`; `backups/services` also has a `500G`
|
while media, Syncthing and service-backup datasets use `lz4`; `backups/services` also has a `500G`
|
||||||
@@ -240,16 +240,16 @@ 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 and is gated by
|
Phase 1 is limited to rootless Navidrome and Syncthing user Quadlets on Atlas. It is enabled in the
|
||||||
`backend_phase1_enabled`. Official Navidrome `0.63.2` uses its SQLite database below `/data`; it does
|
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
|
||||||
not support `ND_DATABASE_URL` or an external PostgreSQL backend. The obsolete `navidromedb` service
|
not support `ND_DATABASE_URL` or an external PostgreSQL backend. The obsolete `navidromedb` service
|
||||||
was therefore removed from Prometheus instead of being reproduced on Atlas. The role derives all
|
was therefore removed from Prometheus instead of being reproduced on Atlas. The role derives all
|
||||||
storage paths from the existing `zpool` mounted at `/zpool`: music is read-only at
|
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/media/music`, Navidrome application state and `navidrome.db` are stored at
|
||||||
`/zpool/archive/app_data/navidrome`, and Syncthing persists at
|
`/zpool/archive/app_data/navidrome`, and Syncthing persists at
|
||||||
`/zpool/archive/app_data/syncthing`. `profile_atlas` creates these datasets when
|
`/zpool/archive/app_data/syncthing`. `profile_atlas` creates these datasets when
|
||||||
`atlas_manage_storage` is enabled; the backend role verifies their exact mountpoints before starting
|
`atlas_manage_storage` is enabled; the backend role verifies their exact mountpoints before starting
|
||||||
containers. Neither role creates the pool. The separate `wireguard_overlay` role manages `wg0`
|
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
|
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
|
on their respective hosts and exchanging only public keys through Ansible. Prometheus alone opens
|
||||||
`51820/udp` publicly. Backend ports are admitted only in the WireGuard firewalld zone.
|
`51820/udp` publicly. Backend ports are admitted only in the WireGuard firewalld zone.
|
||||||
@@ -265,20 +265,16 @@ Validate and render the Atlas services 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 atlas --tags storage
|
||||||
-e atlas_manage_storage=true
|
|
||||||
|
|
||||||
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
||||||
ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard \
|
ansible-playbook ansible/site.yml --limit prometheus,atlas --tags wireguard
|
||||||
-e wireguard_overlay_enabled=true
|
|
||||||
|
|
||||||
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
||||||
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff \
|
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff
|
||||||
-e backend_phase1_enabled=true
|
|
||||||
|
|
||||||
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
ANSIBLE_LOCAL_TEMP=/tmp/ansible-local \
|
||||||
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 \
|
ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1
|
||||||
-e backend_phase1_enabled=true
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For the cutover, stop the old Navidrome writer before copying its data directory, verify ownership by
|
For the cutover, stop the old Navidrome writer before copying its data directory, verify ownership by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
# Keep Atlas management gates disabled until the NAS and required Vault inputs are ready.
|
# Atlas declares its intended steady state; set a feature flag false only for a deliberate suspension.
|
||||||
hostname: atlas
|
hostname: atlas
|
||||||
ansible_host: 192.168.178.55
|
ansible_host: 192.168.178.55
|
||||||
ansible_user: "{{ atlas_connection_username }}"
|
ansible_user: "{{ atlas_connection_username }}"
|
||||||
@@ -17,9 +17,13 @@ atlas_admin_password_hash: "{{ vault_atlas_admin_password_hash | default('!') }}
|
|||||||
|
|
||||||
atlas_lan_subnet: 192.168.178.0/24
|
atlas_lan_subnet: 192.168.178.0/24
|
||||||
atlas_aegis_ip: 192.168.178.54
|
atlas_aegis_ip: 192.168.178.54
|
||||||
atlas_manage_firewall: false
|
atlas_manage_firewall: true
|
||||||
atlas_firewalld_zone: public
|
atlas_firewalld_zone: public
|
||||||
atlas_zfs_pool: zpool
|
atlas_zfs_pool: zpool
|
||||||
|
# Populate only for the first pool bootstrap with four real persistent disk paths.
|
||||||
|
# Example: /dev/disk/by-id/ata-Seagate_IronWolf_...
|
||||||
|
atlas_zpool_disks: []
|
||||||
|
atlas_create_pool: false
|
||||||
atlas_zfs_dataset_work: work
|
atlas_zfs_dataset_work: work
|
||||||
atlas_zfs_dataset_archive: archive
|
atlas_zfs_dataset_archive: archive
|
||||||
atlas_zfs_dataset_app_data: archive/app_data
|
atlas_zfs_dataset_app_data: archive/app_data
|
||||||
@@ -32,10 +36,11 @@ atlas_zfs_dataset_photobook: media/photobook
|
|||||||
atlas_zfs_dataset_backups: backups
|
atlas_zfs_dataset_backups: backups
|
||||||
atlas_zfs_dataset_service_backups: backups/services
|
atlas_zfs_dataset_service_backups: backups/services
|
||||||
atlas_mount_root: /zpool
|
atlas_mount_root: /zpool
|
||||||
atlas_manage_storage: false
|
atlas_manage_storage: true
|
||||||
|
atlas_manage_sharing: true
|
||||||
atlas_manage_media_stack: false
|
atlas_manage_media_stack: false
|
||||||
|
|
||||||
wireguard_overlay_enabled: false
|
wireguard_overlay_enabled: true
|
||||||
wireguard_address: 10.0.0.2/24
|
wireguard_address: 10.0.0.2/24
|
||||||
wireguard_peers:
|
wireguard_peers:
|
||||||
- name: prometheus
|
- name: prometheus
|
||||||
@@ -45,7 +50,7 @@ wireguard_peers:
|
|||||||
- 10.0.0.0/24
|
- 10.0.0.0/24
|
||||||
persistent_keepalive: 25
|
persistent_keepalive: 25
|
||||||
|
|
||||||
backend_phase1_enabled: false
|
backend_phase1_enabled: true
|
||||||
backend_phase1_start_services: false
|
backend_phase1_start_services: false
|
||||||
backend_phase1_wireguard_address: 10.0.0.2
|
backend_phase1_wireguard_address: 10.0.0.2
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ ansible_ssh_private_key_file: /home/fscotto/.ssh/id_ed25519
|
|||||||
server_username: rocky
|
server_username: rocky
|
||||||
server_duckdns_domain: fscotto
|
server_duckdns_domain: fscotto
|
||||||
|
|
||||||
wireguard_overlay_enabled: false
|
wireguard_overlay_enabled: true
|
||||||
wireguard_address: 10.0.0.1/24
|
wireguard_address: 10.0.0.1/24
|
||||||
wireguard_listen_port: 51820
|
wireguard_listen_port: 51820
|
||||||
wireguard_enable_ipv4_forwarding: true
|
wireguard_enable_ipv4_forwarding: true
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
---
|
---
|
||||||
atlas_manage_storage: false
|
atlas_manage_storage: false
|
||||||
|
atlas_manage_sharing: false
|
||||||
|
# Destructive first-boot action; normally false once the pool exists.
|
||||||
|
atlas_create_pool: false
|
||||||
|
atlas_zpool_disks: []
|
||||||
atlas_admin_username: CHANGEME_ATLAS_ADMIN
|
atlas_admin_username: CHANGEME_ATLAS_ADMIN
|
||||||
atlas_admin_group: "{{ atlas_admin_username }}"
|
atlas_admin_group: "{{ atlas_admin_username }}"
|
||||||
atlas_admin_home: "/home/{{ atlas_admin_username }}"
|
atlas_admin_home: "/home/{{ atlas_admin_username }}"
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
- name: Import Atlas 45Drives tasks
|
- name: Import Atlas 45Drives tasks
|
||||||
ansible.builtin.import_tasks: cockpit.yml
|
ansible.builtin.import_tasks: cockpit.yml
|
||||||
|
|
||||||
|
- name: Import Atlas pool bootstrap tasks
|
||||||
|
ansible.builtin.import_tasks: pool.yml
|
||||||
|
|
||||||
- name: Import Atlas storage tasks
|
- name: Import Atlas storage tasks
|
||||||
ansible.builtin.import_tasks: storage.yml
|
ansible.builtin.import_tasks: storage.yml
|
||||||
|
|
||||||
|
|||||||
56
ansible/roles/profile_atlas/tasks/pool.yml
Normal file
56
ansible/roles/profile_atlas/tasks/pool.yml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Atlas ZFS pool
|
||||||
|
tags: [atlas, storage, pool]
|
||||||
|
when: atlas_create_pool | bool
|
||||||
|
block:
|
||||||
|
- name: Validate Atlas pool bootstrap inputs
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- atlas_zpool_disks | length == 4
|
||||||
|
- atlas_zpool_disks | unique | length == 4
|
||||||
|
- atlas_zpool_disks | select('match', '^/dev/disk/by-id/') | list | length == 4
|
||||||
|
fail_msg: >-
|
||||||
|
Set exactly four distinct persistent /dev/disk/by-id paths in
|
||||||
|
atlas_zpool_disks before creating the RAIDZ2 pool.
|
||||||
|
|
||||||
|
- name: Inspect declared Atlas pool disks
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item }}"
|
||||||
|
loop: "{{ atlas_zpool_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
register: atlas_zpool_disk_stats
|
||||||
|
|
||||||
|
- name: Require every declared Atlas pool disk
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.stat.exists
|
||||||
|
- item.stat.isblk | default(false)
|
||||||
|
fail_msg: "Declared Atlas pool disk is unavailable or is not a block device: {{ item.item }}"
|
||||||
|
loop: "{{ atlas_zpool_disk_stats.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
|
|
||||||
|
- name: Check whether the Atlas ZFS pool already exists
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- zpool
|
||||||
|
- list
|
||||||
|
- -H
|
||||||
|
- -o
|
||||||
|
- name
|
||||||
|
- "{{ atlas_zfs_pool }}"
|
||||||
|
register: atlas_zpool_bootstrap_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: atlas_zpool_bootstrap_check.rc not in [0, 1]
|
||||||
|
|
||||||
|
- name: Create the Atlas RAIDZ2 pool when absent
|
||||||
|
community.general.zpool:
|
||||||
|
name: "{{ atlas_zfs_pool }}"
|
||||||
|
state: present
|
||||||
|
mountpoint: "{{ atlas_mount_root }}"
|
||||||
|
force: false
|
||||||
|
vdevs:
|
||||||
|
- type: raidz2
|
||||||
|
disks: "{{ atlas_zpool_disks }}"
|
||||||
|
when: atlas_zpool_bootstrap_check.rc == 1
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Reload NFS exports
|
notify: Reload NFS exports
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Enable Atlas file-sharing services
|
- name: Enable Atlas file-sharing services
|
||||||
tags: [atlas, sharing, services]
|
tags: [atlas, sharing, services]
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
- smb.service
|
- smb.service
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item }}"
|
label: "{{ item }}"
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Configure Atlas NFSv4-only service
|
- name: Configure Atlas NFSv4-only service
|
||||||
tags: [atlas, sharing]
|
tags: [atlas, sharing]
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Restart NFS server
|
notify: Restart NFS server
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Mask Atlas NFSv3 RPC services
|
- name: Mask Atlas NFSv3 RPC services
|
||||||
tags: [atlas, sharing, services]
|
tags: [atlas, sharing, services]
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
- rpcbind.socket
|
- rpcbind.socket
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item }}"
|
label: "{{ item }}"
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Ensure Atlas NFS mount daemon drop-in directory exists
|
- name: Ensure Atlas NFS mount daemon drop-in directory exists
|
||||||
tags: [atlas, sharing, services]
|
tags: [atlas, sharing, services]
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Disable Atlas NFSv3 mount daemon listeners
|
- name: Disable Atlas NFSv3 mount daemon listeners
|
||||||
tags: [atlas, sharing, services]
|
tags: [atlas, sharing, services]
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Restart NFS mount daemon
|
notify: Restart NFS mount daemon
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Enable SELinux access for Atlas file sharing
|
- name: Enable SELinux access for Atlas file sharing
|
||||||
tags: [atlas, sharing, services]
|
tags: [atlas, sharing, services]
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item }}"
|
label: "{{ item }}"
|
||||||
when:
|
when:
|
||||||
- atlas_manage_storage | bool
|
- atlas_manage_sharing | bool
|
||||||
- (ansible_facts['selinux'] | default({})).get('status', 'disabled') == 'enabled'
|
- (ansible_facts['selinux'] | default({})).get('status', 'disabled') == 'enabled'
|
||||||
|
|
||||||
- name: Render Atlas Samba configuration
|
- name: Render Atlas Samba configuration
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
validate: "testparm --suppress-prompt %s"
|
validate: "testparm --suppress-prompt %s"
|
||||||
notify: Restart Samba service
|
notify: Restart Samba service
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Require Vault-backed Atlas Samba accounts
|
- name: Require Vault-backed Atlas Samba accounts
|
||||||
tags: [atlas, sharing]
|
tags: [atlas, sharing]
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
- atlas_samba_accounts | selectattr('password', 'equalto', '') | list | length == 0
|
- atlas_samba_accounts | selectattr('password', 'equalto', '') | list | length == 0
|
||||||
fail_msg: Define every authorized Samba account and its Vault-backed password.
|
fail_msg: Define every authorized Samba account and its Vault-backed password.
|
||||||
no_log: true
|
no_log: true
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Ensure Atlas Samba private state directory exists
|
- name: Ensure Atlas Samba private state directory exists
|
||||||
tags: [atlas, sharing]
|
tags: [atlas, sharing]
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Manage Vault-backed Atlas Samba credentials
|
- name: Manage Vault-backed Atlas Samba credentials
|
||||||
tags: [atlas, sharing]
|
tags: [atlas, sharing]
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
loop_var: atlas_samba_account
|
loop_var: atlas_samba_account
|
||||||
label: "{{ atlas_samba_account.username }}"
|
label: "{{ atlas_samba_account.username }}"
|
||||||
no_log: true
|
no_log: true
|
||||||
when: atlas_manage_storage | bool
|
when: atlas_manage_sharing | bool
|
||||||
|
|
||||||
- name: Require completed Atlas firewall placeholders
|
- name: Require completed Atlas firewall placeholders
|
||||||
tags: [atlas, sharing, services]
|
tags: [atlas, sharing, services]
|
||||||
|
|||||||
Reference in New Issue
Block a user