mirror of
https://github.com/fscotto/infra.git
synced 2026-09-27 11:02:47 +00:00
Add Atlas media and storage services
This commit is contained in:
39
AGENTS.md
39
AGENTS.md
@@ -53,6 +53,10 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
|
||||
- AI coding agents: `ansible-playbook ansible/site.yml --limit <host> --tags ai_agents --check --diff`
|
||||
- Mail bootstrap: `sh -n scripts/bootstrap_mail.sh` and `shellcheck scripts/bootstrap_mail.sh`
|
||||
- Server compose render: `podman-compose -f /opt/docker/server/docker-compose.yml config` and `systemctl status podman-compose-server`
|
||||
- Atlas media stack:
|
||||
`ansible-playbook ansible/site.yml --limit atlas --tags storage,sharing,containers --check --diff`
|
||||
- Prometheus media mount:
|
||||
`ansible-playbook ansible/site.yml --limit prometheus --tags rclone,navidrome --check --diff`
|
||||
- DuckDNS config only: `ansible-playbook ansible/site.yml --limit prometheus --tags duckdns --check --diff`
|
||||
|
||||
## Conventions
|
||||
@@ -104,8 +108,12 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
||||
- `rocky_server` is a child of both `platform_rocky` and `server`; `prometheus` is its active target.
|
||||
- The target must already provide `server_username` with local sudo access before the profile runs.
|
||||
- The Rocky profile installs Podman and podman-compose, uses firewalld, preserves SELinux enforcement, and renders the
|
||||
same server Compose stack with a `podman-compose-server` systemd unit. It does not start, enable, transfer data,
|
||||
update DNS, or cut over traffic; activating the stack is a manual step.
|
||||
Nginx Proxy Manager/Gitea/Navidrome-PostgreSQL Compose stack with a `podman-compose-server` systemd unit. It does not
|
||||
start or enable that Compose stack, transfer data, update DNS, or cut over traffic; activating it remains manual.
|
||||
- Prometheus has a gated system `rclone-music.service` and rootless Navidrome Quadlet. They remain disabled until the
|
||||
Atlas WireGuard address, pinned SSH host key and Vault-backed SFTP private key are configured. The rclone mount is
|
||||
read-only at `/mnt/music_atlas`; Navidrome must not start against the underlying empty mountpoint or while the legacy
|
||||
rootful Navidrome container is still running. The role never removes that legacy container or its data.
|
||||
- Firewalld enables SSH, Cockpit (`9090/tcp`), HTTP and HTTPS. Nginx Proxy Manager publishes `80/tcp` and
|
||||
`443/tcp`; bind its administration interface only to `127.0.0.1:81` and use `npm-tunnel` from Ikaros or Nymph.
|
||||
Nextcloud remains disabled; do not provision `/srv/nextcloud` directories.
|
||||
@@ -121,17 +129,28 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
||||
subsequent runs use the dedicated Atlas account.
|
||||
- The pool is pre-existing: never add pool creation, disk partitioning, RAIDZ creation, rollback,
|
||||
or destruction to the Atlas profile.
|
||||
- `atlas_manage_storage` and `atlas_manage_firewall` remain false until their placeholders are
|
||||
replaced; only then may the profile manage datasets, shares and LAN-restricted firewall rules.
|
||||
- `atlas_manage_storage`, `atlas_manage_firewall`, and `atlas_manage_media_stack` remain false until their placeholders
|
||||
and Vault inputs are replaced; only then may the profile manage datasets, shares, LAN-restricted firewall rules, and
|
||||
rootful media Quadlets.
|
||||
- Atlas requires `vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash` for Cockpit
|
||||
and, when storage is enabled, `vault_atlas_samba_password`. Never print these values.
|
||||
- Atlas uses NFSv4 for Linux and SMB for Windows/WSL, restricted to the configured LAN. Snapshot,
|
||||
Borg/Hetzner offsite backup, Prometheus pull and USB backup automation are intentionally deferred.
|
||||
and, when the relevant gates are enabled, `vault_atlas_samba_password` and `vault_atlas_immich_db_password`. Never
|
||||
print these values.
|
||||
- Atlas creates `archive`, `media/music`, `media/icloud_photos`, and `backups/services` only under the verified
|
||||
pre-existing pool; `backups/services` has a `500G` refreservation. Existing Work, Syncthing, and
|
||||
Prometheus-backup datasets remain managed and separate.
|
||||
- The `immich` system account is fixed to UID/GID `1100`, has no login shell or `wheel` membership, and receives only
|
||||
the `video` and `render` supplementary groups. Immich's rootful Quadlets run as `1100:1100`; Server and ML receive
|
||||
`/dev/dri`, while the iCloud Photos external library is read-only.
|
||||
- Atlas exports iCloud Photos only to the configured Aegis IP with all access squashed to UID/GID `1100`. SMB3 exposes
|
||||
`Archive` to Vault-backed authorized accounts 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
|
||||
bound to `127.0.0.1:81`; do not expose it directly to the LAN or Internet.
|
||||
|
||||
## Atlas NAS TODO
|
||||
- Replace every Atlas `CHANGEME` value, provide the required Vault variables and validate the first
|
||||
remote bootstrap on the real Rocky Linux 9 host. Enable `atlas_manage_storage` first and
|
||||
`atlas_manage_firewall` only after confirming the pool, mountpoints, LAN subnet and firewalld zone.
|
||||
`atlas_manage_firewall` only after confirming the pool, mountpoints, LAN subnet and firewalld zone. Enable
|
||||
`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
|
||||
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
|
||||
@@ -140,8 +159,8 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
||||
or manual operations, not as the only source of configuration, and never automate snapshot rollback.
|
||||
- Manage the Syncthing star topology, device IDs, folders, folder modes, ignore rules and protected GUI
|
||||
or API access for the selected clients.
|
||||
- Add Tailscale or WireGuard and corresponding LAN/VPN-only firewalld rules before enabling remote
|
||||
services; never expose SSH, Cockpit, NFS, SMB or Syncthing through public port forwarding.
|
||||
- Validate the existing WireGuard path and add its LAN/VPN-only firewalld rules before enabling remote services;
|
||||
never expose SSH, Cockpit, NFS, SMB or Syncthing through public port forwarding.
|
||||
- Add the least-privilege Prometheus backup flow: remote dump generation, dedicated SSH identity,
|
||||
pinned host key, atomic pull, verification, retention and an Atlas systemd service/timer.
|
||||
- Add the encrypted offsite backup with Borg to a Hetzner Storage Box: use a dedicated SSH identity,
|
||||
|
||||
60
README.it.md
60
README.it.md
@@ -180,7 +180,10 @@ Lo stato attuale del profilo server include:
|
||||
- installazione pacchetti Rocky via DNF, EPEL e CRB
|
||||
- installazione di Podman e podman-compose
|
||||
- abilitazione dei servizi systemd dichiarati in inventory/group vars
|
||||
- copia dei dotfiles server e rendering dei template server, incluso il `docker-compose.yml` dello stack servizi e dell'unit `podman-compose-server` (attivazione manuale)
|
||||
- copia dei dotfiles server e rendering del `docker-compose.yml` per Nginx Proxy Manager, Gitea e il
|
||||
database PostgreSQL esistente di Navidrome, piu l'unita `podman-compose-server` (attivazione manuale)
|
||||
- mount di `/pool/media/music` da Atlas su `/mnt/music_atlas` tramite il servizio di sistema
|
||||
`rclone-music.service`, e Navidrome tramite Quadlet utente rootless
|
||||
- attivazione di firewalld con SSH, Cockpit (`9090/tcp`), HTTP e HTTPS abilitati
|
||||
- Syncthing escluso dal profilo server Rocky
|
||||
|
||||
@@ -188,6 +191,18 @@ Nginx Proxy Manager pubblica solo `80/tcp` e `443/tcp`; la sua interfaccia di am
|
||||
associata a `127.0.0.1:81` ed e raggiungibile da Ikaros o Nymph con l'alias Bash `npm-tunnel`.
|
||||
Nextcloud resta disabilitato e il profilo non crea directory `/srv/nextcloud`.
|
||||
|
||||
Il mount musicale e protetto da `server_atlas_music_enabled`. Prima di abilitarlo, sostituire
|
||||
l'indirizzo WireGuard e la chiave host SSH fissata in `host_vars/prometheus.yml`, quindi fornire
|
||||
`vault_prometheus_atlas_sftp_private_key` tramite Vault cifrato o variabili locali non tracciate. La
|
||||
chiave pubblica corrispondente deve essere gia presente nelle chiavi autorizzate gestite su Atlas.
|
||||
Rclone usa il percorso remoto esatto `/pool/media/music` in sola lettura e una cache VFS completa da
|
||||
`15G`; systemd lingering mantiene disponibile il manager utente per il Quadlet rootless.
|
||||
Configurare il proxy host NPM di Prometheus per Navidrome come `host.containers.internal:4533`; la
|
||||
porta Navidrome non viene aperta in firewalld.
|
||||
Prima della prima attivazione, arrestare il vecchio container rootful `navidrome`. Il ruolo rifiuta
|
||||
di avviare il sostituto rootless mentre il container precedente e in esecuzione e non rimuove mai
|
||||
automaticamente il container o i dati esistenti.
|
||||
|
||||
### DuckDNS
|
||||
|
||||
`profile_server` genera `~/duckdns/duck.sh` con permessi `0700`, mantenendo il percorso dello
|
||||
@@ -239,9 +254,10 @@ solo i dataset figli e non deve mai creare, partizionare, distruggere, fare roll
|
||||
pool. I client Linux usano NFSv4, quelli Windows/WSL SMB; entrambi restano limitati alla LAN
|
||||
configurata.
|
||||
|
||||
Per il primo avvio sostituire i placeholder Atlas e fornire
|
||||
`vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash` e
|
||||
`vault_atlas_samba_password`. Eseguire il bootstrap tramite l'amministratore esistente:
|
||||
Per il primo avvio sostituire i placeholder di host, pool, mount root, LAN e IP di Aegis e
|
||||
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
|
||||
l'amministratore esistente:
|
||||
|
||||
```bash
|
||||
ansible-playbook ansible/site.yml --limit atlas \
|
||||
@@ -251,11 +267,26 @@ ansible-playbook ansible/site.yml --limit atlas \
|
||||
`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
|
||||
`atlas_manage_storage` solo dopo aver verificato pool e mountpoint esistenti; abilitare
|
||||
`atlas_manage_firewall` solo dopo aver verificato subnet LAN e zona firewalld attiva.
|
||||
`atlas_manage_firewall` solo dopo aver verificato subnet LAN e zona firewalld attiva. Abilitare
|
||||
`atlas_manage_media_stack` per ultimo, dopo aver verificato `/dev/dri`, i percorsi dei container e il
|
||||
segreto del database Immich.
|
||||
|
||||
Restano da implementare retention delle snapshot, topologia Syncthing, VPN, pull da Prometheus,
|
||||
backup cifrati con Borg su una Hetzner Storage Box, backup USB, monitoraggio e test di disaster
|
||||
recovery. Il backlog operativo dettagliato e in `AGENTS.md`.
|
||||
Con la gestione storage attiva, Atlas crea `archive` (`zstd`), `media/music` (`lz4`),
|
||||
`media/icloud_photos` (`lz4`) e `backups/services` (`lz4`, `refreservation=500G`) sotto il pool
|
||||
preesistente. I dataset esistenti Work, Syncthing e backup Prometheus restano gestiti e separati.
|
||||
SMB3 pubblica `Archive` solo agli account Samba configurati con password in Vault e ammette la LAN
|
||||
configurata senza esclusioni specifiche per host. NFSv4 esporta soltanto
|
||||
`media/icloud_photos` all'IP configurato di Aegis con `all_squash` verso UID/GID anonimi `1100`.
|
||||
|
||||
L'account di sistema `immich` usa UID/GID `1100`, shell senza login, nessuna appartenenza a `wheel` e
|
||||
i gruppi supplementari `video` e `render`. I Quadlet rootful di Immich Server, ML, cache compatibile
|
||||
Redis, PostgreSQL e NPM condividono una rete Podman. Immich viene eseguito come `1100:1100`; Server e
|
||||
ML ricevono `/dev/dri` e la libreria iCloud Photos e montata in sola lettura. NPM pubblica `80` e
|
||||
`443`, mentre l'amministrazione resta vincolata a `127.0.0.1:81` per l'accesso tramite tunnel SSH.
|
||||
|
||||
Restano da completare retention delle snapshot, topologia Syncthing, validazione WireGuard/firewall,
|
||||
pull di backup da Prometheus, backup cifrati con Borg su una Hetzner Storage Box, backup USB,
|
||||
monitoraggio e test di disaster recovery. Il backlog operativo dettagliato e in `AGENTS.md`.
|
||||
|
||||
---
|
||||
|
||||
@@ -348,7 +379,8 @@ Questo significa che, allo stato attuale:
|
||||
- `deadalus` riceve il profilo Fedora WSL tramite play dev dedicati
|
||||
- il server Rocky (`prometheus`) e gestito con pacchetti, servizi, dotfiles server e firewalld
|
||||
- il NAS Rocky (`atlas`) usa un pool ZFS gia esistente, condivisioni NFSv4/SMB limitate alla LAN e Cockpit/45Drives
|
||||
- lo stack container server include `navidrome`, `postgres`, `gitea` e `nginx-proxy-manager`
|
||||
- lo stack Compose server include `gitea`, `nginx-proxy-manager` e il database PostgreSQL di
|
||||
Navidrome; Navidrome usa un Quadlet rootless separato e legge il mount rclone di Atlas
|
||||
|
||||
# Dotfiles
|
||||
|
||||
@@ -455,6 +487,8 @@ ansible-playbook ansible/site.yml --limit <host> --start-at-task "<task name>" -
|
||||
ansible-lint ansible/roles/<role>
|
||||
yamllint ansible/path/to/file.yml
|
||||
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 prometheus --tags rclone,navidrome --check --diff
|
||||
```
|
||||
|
||||
## Tag supportati dal playbook
|
||||
@@ -471,6 +505,8 @@ Allo stato attuale `ansible/site.yml` espone questi tag:
|
||||
| --- | --- | --- |
|
||||
| `always` | pre-task sempre eseguiti, inclusi caricamento vault e validazioni preliminari | common |
|
||||
| `ai_agents` | installazione agenti AI condivisi | Fedora, WSL |
|
||||
| `atlas` | account, storage, condivisioni e container Atlas | NAS Atlas |
|
||||
| `containers` | Quadlet rootful Atlas | NAS Atlas |
|
||||
| `dotfiles` | distribuzione/configurazione dotfiles | tutti i profili |
|
||||
| `dotfiles:common` | dotfiles comuni condivisi | common, workstation, server |
|
||||
| `dotfiles:desktop` | dotfiles desktop | desktop Void, Fedora/GNOME |
|
||||
@@ -484,13 +520,19 @@ Allo stato attuale `ansible/site.yml` espone questi tag:
|
||||
| `fzf` | configurazione FZF | dotfiles comuni |
|
||||
| `git` | configurazione Git e GPG desktop | Fedora/GNOME, desktop Void |
|
||||
| `gnome` | configurazione host GNOME | Fedora/GNOME desktop |
|
||||
| `immich` | account e Quadlet Immich | NAS Atlas |
|
||||
| `navidrome` | mount rclone e Quadlet Navidrome rootless | Prometheus |
|
||||
| `sway` | sessione/configurazione sway / SwayFX (Wayland) | desktop Void |
|
||||
| `niri` | sessione/configurazione Niri (Wayland) | desktop Void |
|
||||
| `npm` | installazione pacchetti npm globali | Fedora/GNOME, desktop Void, WSL |
|
||||
| `nvidia` | componenti NVIDIA desktop | desktop Void |
|
||||
| `packages` | installazione e aggiornamento pacchetti | tutti i profili |
|
||||
| `podman` | integrazione Podman Compose e Quadlet rootless | server |
|
||||
| `rclone` | mount musica Atlas | Prometheus |
|
||||
| `portal` | configurazione xdg-desktop-portal | desktop Void |
|
||||
| `services` | gestione servizi runit/systemd | tutti i profili |
|
||||
| `sharing` | condivisioni NFSv4 e SMB3 | NAS Atlas |
|
||||
| `storage` | dataset ZFS figli | NAS Atlas |
|
||||
| `theme` | configurazione del tema GTK/Qt | desktop Void |
|
||||
| `tmux` | configurazione e plugin tmux | desktop Fedora/Void, WSL |
|
||||
| `vim` | configurazione Vim | dotfiles comuni |
|
||||
|
||||
65
README.md
65
README.md
@@ -104,10 +104,24 @@ That gives it Fedora packages through DNF, Docker from the official repository,
|
||||
dotfiles and templates. The profile provisions configuration only: it does not transfer data, start
|
||||
the Compose stack, update DNS, or perform a cutover.
|
||||
|
||||
The server profile installs platform-specific packages, Podman and podman-compose,
|
||||
declared systemd services, the server Compose stack behind the `podman-compose-server` systemd unit, and firewalld. The Rocky server excludes
|
||||
Syncthing. Rocky bind mounts use private SELinux relabeling for application data while host system
|
||||
files remain unchanged.
|
||||
The server profile installs platform-specific packages, Podman and podman-compose, declared systemd
|
||||
services, and firewalld. The manually activated `podman-compose-server` unit now contains Nginx Proxy
|
||||
Manager, Gitea, and the existing Navidrome PostgreSQL database. Navidrome itself runs as a rootless
|
||||
user Quadlet and reads the Atlas music dataset from the system `rclone-music.service` mount at
|
||||
`/mnt/music_atlas`. The Rocky server excludes Syncthing.
|
||||
Rocky bind mounts use private SELinux relabeling where supported; the read-only FUSE music mount is
|
||||
passed to Navidrome without relabeling.
|
||||
|
||||
The Atlas music path is gated by `server_atlas_music_enabled`. Before enabling it, replace the
|
||||
WireGuard address and pinned SSH host-key placeholders in `host_vars/prometheus.yml`, and provide
|
||||
`vault_prometheus_atlas_sftp_private_key` through encrypted Vault or untracked local vars. The SFTP
|
||||
key's public half must already be present in Atlas' managed authorized keys. Rclone mounts the exact
|
||||
remote path `/pool/media/music` read-only and uses a `15G` full VFS cache; the rootless user manager is
|
||||
kept alive through systemd lingering.
|
||||
Configure the Prometheus NPM proxy host for Navidrome as `host.containers.internal:4533`; the
|
||||
Navidrome port is not opened through firewalld.
|
||||
Before the first enablement, stop the legacy rootful `navidrome` container. The role refuses to start
|
||||
the rootless replacement while that container is running and never removes the old container or data.
|
||||
|
||||
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
|
||||
@@ -201,9 +215,10 @@ manages child datasets and must never create, partition, destroy, roll back, or
|
||||
pool itself. Linux clients use NFSv4 and Windows/WSL clients use SMB; both are restricted to the
|
||||
configured LAN.
|
||||
|
||||
For the first run, replace the Atlas placeholders and provide
|
||||
`vault_atlas_authorized_ssh_keys`, `vault_atlas_admin_password_hash`, and
|
||||
`vault_atlas_samba_password`. Bootstrap the host through its existing administrator:
|
||||
For the first run, replace the Atlas host, pool, mount-root, LAN, and Aegis-IP
|
||||
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
|
||||
existing administrator:
|
||||
|
||||
```bash
|
||||
ansible-playbook ansible/site.yml --limit atlas \
|
||||
@@ -211,13 +226,27 @@ ansible-playbook ansible/site.yml --limit atlas \
|
||||
```
|
||||
|
||||
`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 checking the existing pool and mountpoints; enable
|
||||
`atlas_manage_firewall` only after checking the LAN subnet and active firewalld zone.
|
||||
Cockpit password. Subsequent runs use `atlas_admin_username`. Enable `atlas_manage_storage` only after
|
||||
checking the existing pool and mountpoints; enable `atlas_manage_firewall` only after checking the LAN
|
||||
subnet and active firewalld zone. Enable `atlas_manage_media_stack` last, after validating `/dev/dri`,
|
||||
the container paths and the Immich database secret.
|
||||
|
||||
Snapshot retention, Syncthing topology, VPN access, Prometheus pulls, encrypted Borg backups to a
|
||||
Hetzner Storage Box, USB backup, monitoring, and disaster-recovery tests remain follow-up work. The
|
||||
detailed operational backlog is kept in `AGENTS.md`.
|
||||
With storage management enabled, Atlas creates `archive` (`zstd`), `media/music` (`lz4`),
|
||||
`media/icloud_photos` (`lz4`), and `backups/services` (`lz4`, `refreservation=500G`) beneath the
|
||||
pre-existing pool. The existing Work, Syncthing, and Prometheus-backup datasets remain managed and
|
||||
separate. SMB3 exposes `Archive` only to the configured Vault-backed Samba accounts and admits the
|
||||
configured LAN without host-specific exclusions. NFSv4 exports only
|
||||
`media/icloud_photos` to the configured Aegis IP, using `all_squash` with anonymous UID/GID `1100`.
|
||||
|
||||
The `immich` system account is fixed to UID/GID `1100`, has no login shell or `wheel` membership, and
|
||||
receives `video` and `render` access. The rootful Immich Server, ML, Redis-compatible cache, PostgreSQL,
|
||||
and NPM Quadlets share one Podman network. Immich runs as `1100:1100`; Server and ML receive `/dev/dri`,
|
||||
and iCloud Photos is mounted read-only as an external library. NPM publishes ports `80` and `443`; its
|
||||
administration interface remains restricted to `127.0.0.1:81` for SSH-tunnel access.
|
||||
|
||||
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
|
||||
remain follow-up work. The detailed operational backlog is kept in `AGENTS.md`.
|
||||
|
||||
## How layering works
|
||||
|
||||
@@ -384,6 +413,8 @@ ansible-playbook ansible/site.yml --limit <host> --start-at-task "<task name>" -
|
||||
ansible-lint ansible/roles/<role>
|
||||
yamllint ansible/path/to/file.yml
|
||||
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 prometheus --tags rclone,navidrome --check --diff
|
||||
```
|
||||
|
||||
## Tags
|
||||
@@ -398,6 +429,8 @@ ansible-playbook ansible/site.yml --list-tags
|
||||
| --- | --- |
|
||||
| `always` | Common pre-tasks, including optional vault loading. |
|
||||
| `ai_agents` | AI coding-agent install, configuration deployment, and managed-binary removal. |
|
||||
| `atlas` | Atlas NAS account, storage, sharing, and container configuration. |
|
||||
| `containers` | Rootful Atlas Quadlets. |
|
||||
| `dotfiles` | User configuration across all profiles. |
|
||||
| `dotfiles:common` | Shared dotfiles. |
|
||||
| `dotfiles:desktop` | Void and Fedora/GNOME desktop dotfiles. |
|
||||
@@ -406,9 +439,15 @@ ansible-playbook ansible/site.yml --list-tags
|
||||
| `dotfiles:workstation` | Personal workstation and WSL dotfiles. |
|
||||
| `emacs` | Shared Emacs setup and authoring dependencies. |
|
||||
| `gnome` | Fedora/GNOME desktop configuration. |
|
||||
| `immich` | Atlas Immich account and Quadlets. |
|
||||
| `navidrome` | Prometheus rclone mount and rootless Navidrome Quadlet. |
|
||||
| `npm` | Global npm packages. |
|
||||
| `packages` | Package installation and updates. |
|
||||
| `podman` | Podman Compose and rootless Quadlet integration. |
|
||||
| `rclone` | Prometheus Atlas music mount. |
|
||||
| `services` | runit and systemd services. |
|
||||
| `sharing` | Atlas NFSv4 and SMB3 configuration. |
|
||||
| `storage` | Atlas child ZFS datasets. |
|
||||
| `tmux` | tmux configuration and plugins. |
|
||||
| `wsl` | WSL bootstrap and configuration. |
|
||||
|
||||
|
||||
@@ -6,6 +6,29 @@ effective_username: "{{ server_username }}"
|
||||
effective_user_group: "{{ server_user_group }}"
|
||||
effective_user_home: "{{ server_user_home }}"
|
||||
server_container_stack_dir: /opt/docker/server
|
||||
server_atlas_music_enabled: false
|
||||
server_atlas_sftp_remote_name: atlas
|
||||
server_atlas_sftp_host: CHANGEME_ATLAS_WIREGUARD_IP
|
||||
server_atlas_sftp_port: 22
|
||||
server_atlas_sftp_username: admin
|
||||
server_atlas_sftp_remote_path: /pool/media/music
|
||||
server_atlas_sftp_private_key: "{{ vault_prometheus_atlas_sftp_private_key | default('') }}"
|
||||
server_atlas_sftp_private_key_file: /etc/rclone/atlas_ed25519
|
||||
server_atlas_sftp_known_host: ""
|
||||
server_atlas_sftp_known_hosts_file: /etc/rclone/known_hosts
|
||||
server_atlas_wireguard_unit: ""
|
||||
server_rclone_config_dir: /etc/rclone
|
||||
server_rclone_config_file: /etc/rclone/rclone.conf
|
||||
server_rclone_music_service: rclone-music.service
|
||||
server_rclone_music_mountpoint: /mnt/music_atlas
|
||||
server_rclone_music_cache_dir: /var/cache/rclone-music
|
||||
server_navidrome_data_dir: /opt/navidrome/data
|
||||
server_navidrome_quadlet_dir: "{{ server_user_home }}/.config/containers/systemd"
|
||||
server_navidrome_env_dir: "{{ server_user_home }}/.config/navidrome"
|
||||
server_navidrome_env_file: "{{ server_navidrome_env_dir }}/navidrome.env"
|
||||
server_navidrome_image: docker.io/deluan/navidrome:latest
|
||||
server_navidrome_port: 4533
|
||||
server_navidrome_db_password: "{{ vault_navidrome_db_password | default('') }}"
|
||||
ai_agents: {}
|
||||
vim_plugins_enabled: false
|
||||
|
||||
@@ -62,11 +85,7 @@ server_directories:
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
- path: /opt/navidrome/data
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0755"
|
||||
- path: /opt/music
|
||||
- path: "{{ server_navidrome_data_dir }}"
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0755"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
# Replace every CHANGEME value before enabling Atlas storage management.
|
||||
# Keep Atlas management gates disabled until the NAS and required Vault inputs are ready.
|
||||
hostname: atlas
|
||||
ansible_host: CHANGEME_ATLAS_HOST
|
||||
ansible_host: 192.168.178.55
|
||||
ansible_user: "{{ atlas_connection_username }}"
|
||||
ansible_port: 22
|
||||
ansible_ssh_private_key_file: CHANGEME_ATLAS_SSH_PRIVATE_KEY_PATH
|
||||
ansible_ssh_private_key_file: /home/fscotto/.ssh/id_ed25519
|
||||
|
||||
atlas_admin_username: admin
|
||||
atlas_connection_username: "{{ atlas_admin_username }}"
|
||||
@@ -15,46 +15,67 @@ atlas_admin_gid: 1000
|
||||
atlas_admin_ssh_keys: "{{ vault_atlas_authorized_ssh_keys | default([]) }}"
|
||||
atlas_admin_password_hash: "{{ vault_atlas_admin_password_hash | default('!') }}"
|
||||
|
||||
atlas_lan_subnet: CHANGEME_LAN_SUBNET
|
||||
atlas_lan_subnet: 192.168.178.0/24
|
||||
atlas_aegis_ip: 192.168.178.54
|
||||
atlas_manage_firewall: false
|
||||
atlas_firewalld_zone: public
|
||||
atlas_zfs_pool: CHANGEME_ZFS_POOL
|
||||
atlas_zfs_pool: zpool
|
||||
atlas_zfs_dataset_work: work
|
||||
atlas_zfs_dataset_archive: archive
|
||||
atlas_zfs_dataset_music: media/music
|
||||
atlas_zfs_dataset_syncthing: syncthing
|
||||
atlas_zfs_dataset_backup_prometheus: backup_prometheus
|
||||
atlas_zfs_dataset_icloud_photos: icloud_photos
|
||||
atlas_mount_root: /CHANGEME_ATLAS_MOUNT_ROOT
|
||||
atlas_zfs_dataset_icloud_photos: media/icloud_photos
|
||||
atlas_zfs_dataset_service_backups: backups/services
|
||||
atlas_mount_root: /zpool
|
||||
atlas_manage_storage: false
|
||||
atlas_manage_media_stack: false
|
||||
|
||||
rocky_manage_openzfs_repo: true
|
||||
rocky_manage_syncthing_binary: true
|
||||
rocky_manage_podman: true
|
||||
rocky_podman_packages:
|
||||
- podman
|
||||
|
||||
host_packages:
|
||||
- cockpit
|
||||
- nfs-utils
|
||||
- policycoreutils-python-utils
|
||||
- samba
|
||||
- samba-client
|
||||
- samba-common-tools
|
||||
- zfs
|
||||
|
||||
atlas_nfs_exports:
|
||||
- path: "{{ atlas_work_mountpoint }}"
|
||||
clients: "{{ atlas_lan_subnet }}(rw,sync,no_subtree_check,root_squash)"
|
||||
- path: "{{ atlas_icloud_photos_mountpoint }}"
|
||||
client: "{{ atlas_aegis_ip }}"
|
||||
options:
|
||||
- rw
|
||||
- sync
|
||||
- no_subtree_check
|
||||
- all_squash
|
||||
- "anonuid={{ atlas_immich_uid }}"
|
||||
- "anongid={{ atlas_immich_gid }}"
|
||||
|
||||
atlas_samba_share_name: work
|
||||
atlas_samba_share_name: Archive
|
||||
atlas_samba_workgroup: WORKGROUP
|
||||
atlas_samba_valid_users:
|
||||
- "{{ atlas_admin_username }}"
|
||||
atlas_samba_password: "{{ vault_atlas_samba_password | default('') }}"
|
||||
# Append any pre-existing laptop Unix account here and keep its Samba password in Vault.
|
||||
atlas_samba_accounts:
|
||||
- username: "{{ atlas_admin_username }}"
|
||||
password: "{{ vault_atlas_samba_password | default('') }}"
|
||||
atlas_samba_valid_users: "{{ atlas_samba_accounts | map(attribute='username') | list }}"
|
||||
|
||||
atlas_immich_db_password: "{{ vault_atlas_immich_db_password | default('') }}"
|
||||
|
||||
atlas_firewalld_rich_rules:
|
||||
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="ssh" accept'
|
||||
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="cockpit" accept'
|
||||
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" port port="2049" protocol="tcp" accept'
|
||||
- 'rule family="ipv4" source address="{{ atlas_aegis_ip }}" service name="nfs" accept'
|
||||
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="samba" accept'
|
||||
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="http" accept'
|
||||
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="https" accept'
|
||||
|
||||
host_enabled_services:
|
||||
- sshd
|
||||
- cockpit.socket
|
||||
- nfs-server
|
||||
- smb
|
||||
- zfs.target
|
||||
|
||||
@@ -7,11 +7,19 @@ ansible_ssh_private_key_file: /home/fscotto/.ssh/id_ed25519
|
||||
|
||||
server_username: rocky
|
||||
server_duckdns_domain: fscotto
|
||||
server_atlas_music_enabled: false
|
||||
server_atlas_sftp_host: CHANGEME_ATLAS_WIREGUARD_IP
|
||||
server_atlas_sftp_username: admin
|
||||
server_atlas_sftp_known_host: CHANGEME_ATLAS_SSH_HOST_KEY
|
||||
server_atlas_wireguard_unit: wg-quick@wg0.service
|
||||
server_atlas_sftp_private_key: "{{ vault_prometheus_atlas_sftp_private_key | default('') }}"
|
||||
|
||||
host_packages:
|
||||
- cockpit
|
||||
- cockpit-navigator
|
||||
- cockpit-podman
|
||||
- fuse3
|
||||
- rclone
|
||||
|
||||
host_enabled_services:
|
||||
- cockpit.socket
|
||||
|
||||
@@ -8,20 +8,36 @@ atlas_admin_gid: 1000
|
||||
atlas_admin_ssh_keys: []
|
||||
atlas_admin_password_hash: "!"
|
||||
|
||||
atlas_immich_username: immich
|
||||
atlas_immich_group: immich
|
||||
atlas_immich_uid: 1100
|
||||
atlas_immich_gid: 1100
|
||||
atlas_immich_supplementary_groups:
|
||||
- video
|
||||
- render
|
||||
|
||||
atlas_lan_subnet: CHANGEME_LAN_SUBNET
|
||||
atlas_aegis_ip: CHANGEME_AEGIS_IP
|
||||
atlas_manage_firewall: false
|
||||
atlas_firewalld_zone: public
|
||||
atlas_zfs_pool: CHANGEME_ZFS_POOL
|
||||
atlas_zfs_dataset_work: work
|
||||
atlas_zfs_dataset_archive: archive
|
||||
atlas_zfs_dataset_music: media/music
|
||||
atlas_zfs_dataset_syncthing: syncthing
|
||||
atlas_zfs_dataset_backup_prometheus: backup_prometheus
|
||||
atlas_zfs_dataset_icloud_photos: icloud_photos
|
||||
atlas_zfs_dataset_icloud_photos: media/icloud_photos
|
||||
atlas_zfs_dataset_service_backups: backups/services
|
||||
atlas_zfs_service_backups_refreservation: 500G
|
||||
atlas_mount_root: /CHANGEME_ATLAS_MOUNT_ROOT
|
||||
|
||||
atlas_archive_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_archive }}"
|
||||
atlas_music_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_music }}"
|
||||
atlas_work_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_work }}"
|
||||
atlas_syncthing_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_syncthing }}"
|
||||
atlas_backup_prometheus_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_backup_prometheus }}"
|
||||
atlas_icloud_photos_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_icloud_photos }}"
|
||||
atlas_service_backups_mountpoint: "{{ atlas_mount_root }}/{{ atlas_zfs_dataset_service_backups }}"
|
||||
atlas_syncthing_config_dir: "{{ atlas_admin_home }}/.local/state/syncthing"
|
||||
atlas_syncthing_default_dir: "{{ atlas_syncthing_mountpoint }}/Sync"
|
||||
|
||||
@@ -34,18 +50,59 @@ atlas_45drives_packages:
|
||||
- cockpit-zfs
|
||||
- cockpit-scheduler
|
||||
|
||||
atlas_nfs_exports: []
|
||||
atlas_samba_share_name: work
|
||||
atlas_nfs_exports:
|
||||
- path: "{{ atlas_icloud_photos_mountpoint }}"
|
||||
client: "{{ atlas_aegis_ip }}"
|
||||
options:
|
||||
- rw
|
||||
- sync
|
||||
- no_subtree_check
|
||||
- all_squash
|
||||
- "anonuid={{ atlas_immich_uid }}"
|
||||
- "anongid={{ atlas_immich_gid }}"
|
||||
atlas_samba_share_name: Archive
|
||||
atlas_samba_workgroup: WORKGROUP
|
||||
atlas_samba_valid_users: []
|
||||
atlas_samba_password: ""
|
||||
atlas_samba_password_marker: /var/lib/samba/private/.atlas-password.sha256
|
||||
atlas_samba_accounts: []
|
||||
atlas_samba_password_marker_dir: /var/lib/samba/private/ansible-passwords
|
||||
atlas_firewalld_rich_rules: []
|
||||
atlas_firewalld_restricted_services:
|
||||
- ssh
|
||||
- cockpit
|
||||
- nfs
|
||||
- samba
|
||||
- http
|
||||
- https
|
||||
atlas_selinux_booleans:
|
||||
- samba_export_all_rw
|
||||
- nfs_export_all_rw
|
||||
|
||||
atlas_manage_media_stack: false
|
||||
atlas_container_quadlet_dir: /etc/containers/systemd
|
||||
atlas_container_data_root: /var/lib/atlas-containers
|
||||
atlas_npm_data_dir: "{{ atlas_container_data_root }}/npm/data"
|
||||
atlas_npm_letsencrypt_dir: "{{ atlas_container_data_root }}/npm/letsencrypt"
|
||||
atlas_npm_admin_bind_address: 127.0.0.1
|
||||
atlas_timezone: Europe/Rome
|
||||
atlas_immich_upload_dir: "{{ atlas_container_data_root }}/immich/upload"
|
||||
atlas_immich_model_cache_dir: "{{ atlas_container_data_root }}/immich/model-cache"
|
||||
atlas_immich_machine_learning_config_dir: "{{ atlas_container_data_root }}/immich/machine-learning-config"
|
||||
atlas_immich_machine_learning_cache_dir: "{{ atlas_container_data_root }}/immich/machine-learning-cache"
|
||||
atlas_immich_redis_data_dir: "{{ atlas_container_data_root }}/immich/redis"
|
||||
atlas_immich_postgres_data_dir: "{{ atlas_container_data_root }}/immich/postgres"
|
||||
|
||||
atlas_npm_image: docker.io/jc21/nginx-proxy-manager:latest
|
||||
atlas_immich_version: release
|
||||
atlas_immich_server_image: "ghcr.io/immich-app/immich-server:{{ atlas_immich_version }}"
|
||||
atlas_immich_machine_learning_image: "ghcr.io/immich-app/immich-machine-learning:{{ atlas_immich_version }}-openvino"
|
||||
atlas_immich_redis_image: docker.io/valkey/valkey:9
|
||||
atlas_immich_postgres_image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||
atlas_immich_db_username: postgres
|
||||
atlas_immich_db_name: immich
|
||||
atlas_immich_db_password: "{{ vault_atlas_immich_db_password | default('') }}"
|
||||
atlas_media_quadlet_services:
|
||||
- atlas-immich-redis.service
|
||||
- atlas-immich-postgres.service
|
||||
- atlas-immich-machine-learning.service
|
||||
- atlas-immich-server.service
|
||||
- atlas-npm.service
|
||||
|
||||
@@ -32,3 +32,15 @@
|
||||
ansible.builtin.systemd:
|
||||
name: atlas-syncthing
|
||||
state: restarted
|
||||
|
||||
- name: Restart Atlas media Quadlets
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
loop: "{{ atlas_media_quadlet_services }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when:
|
||||
- atlas_manage_media_stack | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
- atlas_admin_username != 'CHANGEME_ATLAS_ADMIN'
|
||||
- (atlas_admin_ssh_keys | default([])) | length > 0
|
||||
- atlas_admin_password_hash not in ['', '!', '*']
|
||||
- "'wheel' not in atlas_immich_supplementary_groups"
|
||||
fail_msg: >-
|
||||
Define atlas_admin_username, vault_atlas_authorized_ssh_keys and
|
||||
vault_atlas_admin_password_hash before applying the Atlas profile.
|
||||
@@ -31,6 +32,41 @@
|
||||
state: present
|
||||
no_log: true
|
||||
|
||||
- name: Create Immich primary group
|
||||
tags: [atlas, accounts, containers, immich]
|
||||
ansible.builtin.group:
|
||||
name: "{{ atlas_immich_group }}"
|
||||
gid: "{{ atlas_immich_gid }}"
|
||||
state: present
|
||||
|
||||
- name: Ensure Immich hardware-access groups exist
|
||||
tags: [atlas, accounts, containers, immich]
|
||||
ansible.builtin.group:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop: "{{ atlas_immich_supplementary_groups }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
|
||||
- name: Create unprivileged Immich account
|
||||
tags: [atlas, accounts, containers, immich]
|
||||
ansible.builtin.user:
|
||||
name: "{{ atlas_immich_username }}"
|
||||
uid: "{{ atlas_immich_uid }}"
|
||||
group: "{{ atlas_immich_group }}"
|
||||
groups: "{{ atlas_immich_supplementary_groups }}"
|
||||
append: false
|
||||
home: /nonexistent
|
||||
create_home: false
|
||||
shell: /sbin/nologin
|
||||
system: true
|
||||
state: present
|
||||
|
||||
- name: Read Immich hardware-access group IDs
|
||||
tags: [atlas, accounts, containers, immich]
|
||||
ansible.builtin.getent:
|
||||
database: group
|
||||
|
||||
- name: Grant Atlas administrator passwordless sudo
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.copy:
|
||||
|
||||
140
ansible/roles/profile_atlas/tasks/containers.yml
Normal file
140
ansible/roles/profile_atlas/tasks/containers.yml
Normal file
@@ -0,0 +1,140 @@
|
||||
---
|
||||
- name: Require completed Atlas media-stack configuration
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_manage_storage | bool
|
||||
- atlas_zfs_pool != 'CHANGEME_ZFS_POOL'
|
||||
- atlas_mount_root != '/CHANGEME_ATLAS_MOUNT_ROOT'
|
||||
- atlas_immich_db_password | length > 0
|
||||
- atlas_npm_admin_bind_address == '127.0.0.1'
|
||||
fail_msg: >-
|
||||
Enable and configure Atlas storage, provide vault_atlas_immich_db_password,
|
||||
and keep the NPM administration port bound to loopback before enabling the media stack.
|
||||
no_log: true
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Check Atlas Intel graphics device
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.builtin.stat:
|
||||
path: /dev/dri
|
||||
register: atlas_dri_device
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Require Atlas Intel graphics device
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_dri_device.stat.isdir | default(false)
|
||||
fail_msg: /dev/dri is required for Immich QuickSync and OpenVINO acceleration.
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Create Atlas rootful Quadlet directory
|
||||
tags: [atlas, containers]
|
||||
ansible.builtin.file:
|
||||
path: "{{ atlas_container_quadlet_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Create Atlas Immich configuration directory
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.builtin.file:
|
||||
path: /etc/immich
|
||||
state: directory
|
||||
owner: root
|
||||
group: "{{ atlas_immich_group }}"
|
||||
mode: "0750"
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Create Atlas Immich persistent directories
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ atlas_immich_username }}"
|
||||
group: "{{ atlas_immich_group }}"
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ atlas_immich_upload_dir }}"
|
||||
- "{{ atlas_immich_model_cache_dir }}"
|
||||
- "{{ atlas_immich_machine_learning_config_dir }}"
|
||||
- "{{ atlas_immich_machine_learning_cache_dir }}"
|
||||
- "{{ atlas_immich_redis_data_dir }}"
|
||||
- "{{ atlas_immich_postgres_data_dir }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Create Atlas NPM persistent directories
|
||||
tags: [atlas, containers, npm]
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ atlas_npm_data_dir }}"
|
||||
- "{{ atlas_npm_letsencrypt_dir }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Allow confined containers to use Atlas graphics devices
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.posix.seboolean:
|
||||
name: container_use_devices
|
||||
state: true
|
||||
persistent: true
|
||||
when:
|
||||
- atlas_manage_media_stack | bool
|
||||
- (ansible_facts['selinux'] | default({})).get('status', 'disabled') == 'enabled'
|
||||
|
||||
- name: Render Vault-backed Immich environment
|
||||
tags: [atlas, containers, immich]
|
||||
ansible.builtin.template:
|
||||
src: immich.env.j2
|
||||
dest: /etc/immich/immich.env
|
||||
owner: root
|
||||
group: "{{ atlas_immich_group }}"
|
||||
mode: "0640"
|
||||
no_log: true
|
||||
diff: false
|
||||
notify: Restart Atlas media Quadlets
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Render Atlas media Quadlets
|
||||
tags: [atlas, containers]
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}.j2"
|
||||
dest: "{{ atlas_container_quadlet_dir }}/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- atlas-media.network
|
||||
- atlas-immich-redis.container
|
||||
- atlas-immich-postgres.container
|
||||
- atlas-immich-machine-learning.container
|
||||
- atlas-immich-server.container
|
||||
- atlas-npm.container
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
notify: Restart Atlas media Quadlets
|
||||
when: atlas_manage_media_stack | bool
|
||||
|
||||
- name: Start Atlas media Quadlet services
|
||||
tags: [atlas, containers, services]
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: started
|
||||
daemon_reload: true
|
||||
loop: "{{ atlas_media_quadlet_services }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when:
|
||||
- atlas_manage_media_stack | bool
|
||||
- not ansible_check_mode
|
||||
@@ -11,5 +11,8 @@
|
||||
- name: Import Atlas file sharing tasks
|
||||
ansible.builtin.import_tasks: sharing.yml
|
||||
|
||||
- name: Import Atlas media-container tasks
|
||||
ansible.builtin.import_tasks: containers.yml
|
||||
|
||||
- name: Import Atlas Syncthing tasks
|
||||
ansible.builtin.import_tasks: syncthing.yml
|
||||
|
||||
64
ansible/roles/profile_atlas/tasks/samba_account.yml
Normal file
64
ansible/roles/profile_atlas/tasks/samba_account.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
- name: Require an existing Unix account for Atlas Samba
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ atlas_samba_account.username }}"
|
||||
|
||||
- name: Read Atlas Samba account
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pdbedit
|
||||
- --list
|
||||
- --user
|
||||
- "{{ atlas_samba_account.username }}"
|
||||
register: atlas_samba_account_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Check Atlas Samba password marker
|
||||
ansible.builtin.stat:
|
||||
path: "{{ atlas_samba_password_marker_dir }}/{{ atlas_samba_account.username }}.sha256"
|
||||
register: atlas_samba_password_marker_stat
|
||||
|
||||
- name: Read Atlas Samba password marker
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ atlas_samba_password_marker_dir }}/{{ atlas_samba_account.username }}.sha256"
|
||||
register: atlas_samba_password_marker_content
|
||||
no_log: true
|
||||
when: atlas_samba_password_marker_stat.stat.exists
|
||||
|
||||
- name: Determine whether Atlas Samba credentials must change
|
||||
ansible.builtin.set_fact:
|
||||
atlas_samba_password_digest: "{{ atlas_samba_account.password | hash('sha256') }}"
|
||||
atlas_samba_password_update_required: >-
|
||||
{{
|
||||
atlas_samba_account_check.rc != 0
|
||||
or not atlas_samba_password_marker_stat.stat.exists
|
||||
or (
|
||||
atlas_samba_password_marker_content.content | default('')
|
||||
| b64decode | trim
|
||||
) != (atlas_samba_account.password | hash('sha256'))
|
||||
}}
|
||||
no_log: true
|
||||
|
||||
- name: Set Atlas Samba account password
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- smbpasswd
|
||||
- -s
|
||||
- -a
|
||||
- "{{ atlas_samba_account.username }}"
|
||||
stdin: "{{ atlas_samba_account.password }}\n{{ atlas_samba_account.password }}"
|
||||
changed_when: true
|
||||
no_log: true
|
||||
when: atlas_samba_password_update_required | bool
|
||||
|
||||
- name: Record managed Atlas Samba password digest
|
||||
ansible.builtin.copy:
|
||||
content: "{{ atlas_samba_password_digest }}\n"
|
||||
dest: "{{ atlas_samba_password_marker_dir }}/{{ atlas_samba_account.username }}.sha256"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
when: atlas_samba_password_update_required | bool
|
||||
@@ -10,6 +10,19 @@
|
||||
notify: Reload NFS exports
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Enable Atlas file-sharing services
|
||||
tags: [atlas, sharing, services]
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
state: started
|
||||
loop:
|
||||
- nfs-server.service
|
||||
- smb.service
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Configure Atlas NFSv4-only service
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.template:
|
||||
@@ -82,106 +95,50 @@
|
||||
notify: Restart Samba service
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Require Atlas Samba password
|
||||
- name: Require Vault-backed Atlas Samba accounts
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_samba_password | length > 0
|
||||
fail_msg: Define vault_atlas_samba_password before enabling Atlas storage.
|
||||
- atlas_samba_accounts | length > 0
|
||||
- >-
|
||||
atlas_samba_accounts | map(attribute='username') | list
|
||||
| difference(atlas_samba_valid_users) | length == 0
|
||||
- >-
|
||||
atlas_samba_valid_users
|
||||
| difference(atlas_samba_accounts | map(attribute='username') | list) | length == 0
|
||||
- atlas_samba_accounts | selectattr('password', 'equalto', '') | list | length == 0
|
||||
fail_msg: Define every authorized Samba account and its Vault-backed password.
|
||||
no_log: true
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Read Atlas Samba account
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pdbedit
|
||||
- --list
|
||||
- --user
|
||||
- "{{ atlas_admin_username }}"
|
||||
register: atlas_samba_account
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Ensure Atlas Samba private state directory exists
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.file:
|
||||
path: "{{ atlas_samba_password_marker | dirname }}"
|
||||
path: "{{ atlas_samba_password_marker_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Check Atlas Samba password marker
|
||||
- name: Manage Vault-backed Atlas Samba credentials
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.stat:
|
||||
path: "{{ atlas_samba_password_marker }}"
|
||||
register: atlas_samba_password_marker_stat
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Read Atlas Samba password marker
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ atlas_samba_password_marker }}"
|
||||
register: atlas_samba_password_marker_content
|
||||
no_log: true
|
||||
when:
|
||||
- atlas_manage_storage | bool
|
||||
- atlas_samba_password_marker_stat.stat.exists
|
||||
|
||||
- name: Determine whether Atlas Samba credentials must change
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.set_fact:
|
||||
atlas_samba_password_digest: "{{ atlas_samba_password | hash('sha256') }}"
|
||||
atlas_samba_password_update_required: >-
|
||||
{{
|
||||
atlas_samba_account.rc != 0
|
||||
or not atlas_samba_password_marker_stat.stat.exists
|
||||
or (
|
||||
atlas_samba_password_marker_content.content | default('')
|
||||
| b64decode | trim
|
||||
) != (atlas_samba_password | hash('sha256'))
|
||||
}}
|
||||
ansible.builtin.include_tasks: samba_account.yml
|
||||
loop: "{{ atlas_samba_accounts }}"
|
||||
loop_control:
|
||||
loop_var: atlas_samba_account
|
||||
label: "{{ atlas_samba_account.username }}"
|
||||
no_log: true
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Set Atlas Samba account password
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- smbpasswd
|
||||
- -s
|
||||
- -a
|
||||
- "{{ atlas_admin_username }}"
|
||||
stdin: "{{ atlas_samba_password }}\n{{ atlas_samba_password }}"
|
||||
changed_when: true
|
||||
no_log: true
|
||||
when:
|
||||
- atlas_manage_storage | bool
|
||||
- atlas_samba_password_update_required | bool
|
||||
|
||||
- name: Record managed Atlas Samba password digest
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.copy:
|
||||
content: "{{ atlas_samba_password_digest }}\n"
|
||||
dest: "{{ atlas_samba_password_marker }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
when:
|
||||
- atlas_manage_storage | bool
|
||||
- atlas_samba_password_update_required | bool
|
||||
|
||||
- name: Require completed Atlas firewall placeholders
|
||||
tags: [atlas, sharing, services]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_lan_subnet != 'CHANGEME_LAN_SUBNET'
|
||||
- atlas_aegis_ip != 'CHANGEME_AEGIS_IP'
|
||||
- atlas_firewalld_zone | length > 0
|
||||
fail_msg: Replace the Atlas LAN subnet and firewall zone placeholders.
|
||||
fail_msg: Replace the Atlas LAN, Aegis and firewall-zone placeholders.
|
||||
when: atlas_manage_firewall | bool
|
||||
|
||||
- name: Apply Atlas firewalld rich rules
|
||||
|
||||
@@ -5,12 +5,52 @@
|
||||
atlas_zfs_datasets:
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_work }}"
|
||||
mountpoint: "{{ atlas_work_mountpoint }}"
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
properties:
|
||||
mountpoint: "{{ atlas_work_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_archive }}"
|
||||
mountpoint: "{{ atlas_archive_mountpoint }}"
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
properties:
|
||||
compression: zstd
|
||||
mountpoint: "{{ atlas_archive_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_music }}"
|
||||
mountpoint: "{{ atlas_music_mountpoint }}"
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
properties:
|
||||
compression: lz4
|
||||
mountpoint: "{{ atlas_music_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_syncthing }}"
|
||||
mountpoint: "{{ atlas_syncthing_mountpoint }}"
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
properties:
|
||||
compression: lz4
|
||||
mountpoint: "{{ atlas_syncthing_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_backup_prometheus }}"
|
||||
mountpoint: "{{ atlas_backup_prometheus_mountpoint }}"
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
properties:
|
||||
mountpoint: "{{ atlas_backup_prometheus_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_icloud_photos }}"
|
||||
mountpoint: "{{ atlas_icloud_photos_mountpoint }}"
|
||||
owner: "{{ atlas_immich_username }}"
|
||||
group: "{{ atlas_immich_group }}"
|
||||
properties:
|
||||
compression: lz4
|
||||
mountpoint: "{{ atlas_icloud_photos_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_service_backups }}"
|
||||
mountpoint: "{{ atlas_service_backups_mountpoint }}"
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
properties:
|
||||
compression: lz4
|
||||
mountpoint: "{{ atlas_service_backups_mountpoint }}"
|
||||
refreservation: "{{ atlas_zfs_service_backups_refreservation }}"
|
||||
|
||||
- name: Require completed Atlas storage placeholders
|
||||
tags: [atlas, storage]
|
||||
@@ -56,8 +96,7 @@
|
||||
community.general.zfs:
|
||||
name: "{{ item.name }}"
|
||||
state: present
|
||||
extra_zfs_properties:
|
||||
mountpoint: "{{ item.mountpoint }}"
|
||||
extra_zfs_properties: "{{ item.properties }}"
|
||||
loop: "{{ atlas_zfs_datasets }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
@@ -68,8 +107,8 @@
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.mountpoint }}"
|
||||
state: directory
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ item.group }}"
|
||||
mode: "0770"
|
||||
loop: "{{ atlas_zfs_datasets }}"
|
||||
loop_control:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Atlas Immich machine learning
|
||||
|
||||
[Container]
|
||||
ContainerName=atlas-immich-machine-learning
|
||||
Image={{ atlas_immich_machine_learning_image }}
|
||||
User={{ atlas_immich_uid }}:{{ atlas_immich_gid }}
|
||||
{% for group_name in atlas_immich_supplementary_groups %}
|
||||
GroupAdd={{ ansible_facts['getent_group'][group_name][1] }}
|
||||
{% endfor %}
|
||||
AddDevice=/dev/dri
|
||||
Network=atlas-media.network
|
||||
NetworkAlias=atlas-immich-machine-learning
|
||||
Environment=TZ={{ atlas_timezone }}
|
||||
Volume={{ atlas_immich_model_cache_dir }}:/cache:Z
|
||||
Volume={{ atlas_immich_machine_learning_config_dir }}:/.config:Z
|
||||
Volume={{ atlas_immich_machine_learning_cache_dir }}:/.cache:Z
|
||||
NoNewPrivileges=true
|
||||
DropCapability=NET_RAW
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,22 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Atlas Immich PostgreSQL database
|
||||
|
||||
[Container]
|
||||
ContainerName=atlas-immich-postgres
|
||||
Image={{ atlas_immich_postgres_image }}
|
||||
User={{ atlas_immich_uid }}:{{ atlas_immich_gid }}
|
||||
Network=atlas-media.network
|
||||
NetworkAlias=atlas-immich-postgres
|
||||
EnvironmentFile=/etc/immich/immich.env
|
||||
Volume={{ atlas_immich_postgres_data_dir }}:/var/lib/postgresql/data:Z
|
||||
ShmSize=128m
|
||||
NoNewPrivileges=true
|
||||
DropCapability=NET_RAW
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,20 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Atlas Immich Redis-compatible cache
|
||||
|
||||
[Container]
|
||||
ContainerName=atlas-immich-redis
|
||||
Image={{ atlas_immich_redis_image }}
|
||||
User={{ atlas_immich_uid }}:{{ atlas_immich_gid }}
|
||||
Network=atlas-media.network
|
||||
NetworkAlias=atlas-immich-redis
|
||||
Volume={{ atlas_immich_redis_data_dir }}:/data:Z
|
||||
NoNewPrivileges=true
|
||||
DropCapability=NET_RAW
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,29 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Atlas Immich server
|
||||
Requires=atlas-immich-redis.service atlas-immich-postgres.service atlas-immich-machine-learning.service
|
||||
After=atlas-immich-redis.service atlas-immich-postgres.service atlas-immich-machine-learning.service
|
||||
|
||||
[Container]
|
||||
ContainerName=atlas-immich-server
|
||||
Image={{ atlas_immich_server_image }}
|
||||
User={{ atlas_immich_uid }}:{{ atlas_immich_gid }}
|
||||
{% for group_name in atlas_immich_supplementary_groups %}
|
||||
GroupAdd={{ ansible_facts['getent_group'][group_name][1] }}
|
||||
{% endfor %}
|
||||
AddDevice=/dev/dri
|
||||
Network=atlas-media.network
|
||||
NetworkAlias=atlas-immich-server
|
||||
EnvironmentFile=/etc/immich/immich.env
|
||||
Volume={{ atlas_immich_upload_dir }}:/data:Z
|
||||
Volume={{ atlas_icloud_photos_mountpoint }}:/external/icloud_photos:ro,Z
|
||||
Volume=/etc/localtime:/etc/localtime:ro
|
||||
NoNewPrivileges=true
|
||||
DropCapability=NET_RAW
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,3 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Network]
|
||||
NetworkName=atlas-media
|
||||
21
ansible/roles/profile_atlas/templates/atlas-npm.container.j2
Normal file
21
ansible/roles/profile_atlas/templates/atlas-npm.container.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Atlas Nginx Proxy Manager
|
||||
|
||||
[Container]
|
||||
ContainerName=atlas-npm
|
||||
Image={{ atlas_npm_image }}
|
||||
Network=atlas-media.network
|
||||
NetworkAlias=atlas-npm
|
||||
PublishPort=80:80
|
||||
PublishPort=443:443
|
||||
PublishPort={{ atlas_npm_admin_bind_address }}:81:81
|
||||
Volume={{ atlas_npm_data_dir }}:/data:Z
|
||||
Volume={{ atlas_npm_letsencrypt_dir }}:/etc/letsencrypt:Z
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,4 +1,4 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
{% for export in atlas_nfs_exports %}
|
||||
{{ export.path }} {{ export.clients }}
|
||||
{{ export.path }} {{ export.client }}({{ export.options | join(',') }})
|
||||
{% endfor %}
|
||||
|
||||
13
ansible/roles/profile_atlas/templates/immich.env.j2
Normal file
13
ansible/roles/profile_atlas/templates/immich.env.j2
Normal file
@@ -0,0 +1,13 @@
|
||||
# Managed by Ansible. Contains a Vault secret; do not edit or copy.
|
||||
TZ={{ atlas_timezone | to_json }}
|
||||
DB_HOSTNAME="atlas-immich-postgres"
|
||||
DB_USERNAME={{ atlas_immich_db_username | to_json }}
|
||||
DB_PASSWORD={{ atlas_immich_db_password | to_json }}
|
||||
DB_DATABASE_NAME={{ atlas_immich_db_name | to_json }}
|
||||
DB_VECTOR_EXTENSION="vectorchord"
|
||||
POSTGRES_USER={{ atlas_immich_db_username | to_json }}
|
||||
POSTGRES_PASSWORD={{ atlas_immich_db_password | to_json }}
|
||||
POSTGRES_DB={{ atlas_immich_db_name | to_json }}
|
||||
POSTGRES_INITDB_ARGS="--data-checksums"
|
||||
REDIS_HOSTNAME="atlas-immich-redis"
|
||||
IMMICH_MACHINE_LEARNING_URL="http://atlas-immich-machine-learning:3003"
|
||||
@@ -3,11 +3,12 @@
|
||||
workgroup = {{ atlas_samba_workgroup }}
|
||||
security = user
|
||||
map to guest = Never
|
||||
server min protocol = SMB2
|
||||
server min protocol = SMB3
|
||||
hosts allow = {{ atlas_lan_subnet }}
|
||||
include = registry
|
||||
|
||||
[{{ atlas_samba_share_name }}]
|
||||
path = {{ atlas_work_mountpoint }}
|
||||
path = {{ atlas_archive_mountpoint }}
|
||||
browseable = yes
|
||||
read only = no
|
||||
valid users = {{ atlas_samba_valid_users | join(' ') }}
|
||||
|
||||
@@ -9,3 +9,29 @@
|
||||
tags: [services]
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Restart rclone music mount
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ server_rclone_music_service }}"
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
when:
|
||||
- server_atlas_music_enabled | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
- name: Restart rootless Navidrome
|
||||
tags: [services, podman, rclone, navidrome]
|
||||
become_user: "{{ server_username }}"
|
||||
ansible.builtin.systemd:
|
||||
name: navidrome.service
|
||||
scope: user
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ ansible_facts['getent_passwd'][server_username][1] }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: >-
|
||||
unix:path=/run/user/{{ ansible_facts['getent_passwd'][server_username][1] }}/bus
|
||||
when:
|
||||
- server_atlas_music_enabled | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
@@ -8,15 +8,16 @@
|
||||
fail_msg: >-
|
||||
server_firewall_backend must be firewalld for the Rocky server profile.
|
||||
|
||||
- name: Require server container secret variables
|
||||
tags: [dotfiles, dotfiles:server, services]
|
||||
- name: Require server database secret variables
|
||||
tags: [dotfiles, dotfiles:server, services, navidrome]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (vault_navidrome_db_password | default('')) | length > 0
|
||||
- server_navidrome_db_password | length > 0
|
||||
- (vault_postgres_root_password | default('')) | length > 0
|
||||
fail_msg: >-
|
||||
Server container secrets are missing. Define vault_navidrome_db_password and
|
||||
vault_postgres_root_password in secrets/vault.yml or another vars source.
|
||||
Define vault_navidrome_db_password and vault_postgres_root_password in Vault
|
||||
before rendering the Navidrome database configuration.
|
||||
no_log: true
|
||||
|
||||
- name: Configure DuckDNS updater
|
||||
tags: [dotfiles, dotfiles:server, duckdns]
|
||||
@@ -71,6 +72,12 @@
|
||||
tags: [services, podman]
|
||||
ansible.builtin.include_tasks: podman-compose.yml
|
||||
|
||||
- name: Configure Atlas music mount
|
||||
ansible.builtin.import_tasks: rclone-music.yml
|
||||
|
||||
- name: Configure rootless Navidrome
|
||||
ansible.builtin.import_tasks: navidrome.yml
|
||||
|
||||
- name: Disable SSH root login on server
|
||||
tags: [services]
|
||||
ansible.builtin.lineinfile:
|
||||
|
||||
114
ansible/roles/profile_server/tasks/navidrome.yml
Normal file
114
ansible/roles/profile_server/tasks/navidrome.yml
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
- name: Read server account details for rootless Navidrome
|
||||
tags: [services, podman, rclone, navidrome]
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ server_username }}"
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Check for a running legacy rootful Navidrome container
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- podman
|
||||
- container
|
||||
- inspect
|
||||
- --format
|
||||
- "{{ '{{.State.Running}}' }}"
|
||||
- navidrome
|
||||
register: server_legacy_navidrome_container
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
check_mode: false
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Refuse concurrent legacy and rootless Navidrome
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- >-
|
||||
server_legacy_navidrome_container.rc != 0
|
||||
or server_legacy_navidrome_container.stdout | trim != 'true'
|
||||
fail_msg: >-
|
||||
Stop the legacy rootful Navidrome container before enabling the rootless Quadlet.
|
||||
The playbook does not remove the old container or its data automatically.
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Enable lingering for rootless Navidrome
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- loginctl
|
||||
- enable-linger
|
||||
- "{{ server_username }}"
|
||||
creates: "/var/lib/systemd/linger/{{ server_username }}"
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Start server user systemd manager
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.systemd:
|
||||
name: "user@{{ ansible_facts['getent_passwd'][server_username][1] }}.service"
|
||||
state: started
|
||||
when:
|
||||
- server_atlas_music_enabled | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
- name: Create rootless Navidrome Quadlet directory
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.file:
|
||||
path: "{{ server_navidrome_quadlet_dir }}"
|
||||
state: directory
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0700"
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Create private rootless Navidrome configuration directory
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.file:
|
||||
path: "{{ server_navidrome_env_dir }}"
|
||||
state: directory
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0700"
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Render Vault-backed rootless Navidrome environment
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.template:
|
||||
src: navidrome.env.j2
|
||||
dest: "{{ server_navidrome_env_file }}"
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
diff: false
|
||||
notify: Restart rootless Navidrome
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Render rootless Navidrome Quadlet
|
||||
tags: [services, podman, navidrome]
|
||||
ansible.builtin.template:
|
||||
src: navidrome.container.j2
|
||||
dest: "{{ server_navidrome_quadlet_dir }}/navidrome.container"
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0644"
|
||||
notify: Restart rootless Navidrome
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Start rootless Navidrome Quadlet
|
||||
tags: [services, podman, navidrome]
|
||||
become_user: "{{ server_username }}"
|
||||
ansible.builtin.systemd:
|
||||
name: navidrome.service
|
||||
scope: user
|
||||
state: started
|
||||
daemon_reload: true
|
||||
environment:
|
||||
XDG_RUNTIME_DIR: "/run/user/{{ ansible_facts['getent_passwd'][server_username][1] }}"
|
||||
DBUS_SESSION_BUS_ADDRESS: >-
|
||||
unix:path=/run/user/{{ ansible_facts['getent_passwd'][server_username][1] }}/bus
|
||||
when:
|
||||
- server_atlas_music_enabled | bool
|
||||
- not ansible_check_mode
|
||||
105
ansible/roles/profile_server/tasks/rclone-music.yml
Normal file
105
ansible/roles/profile_server/tasks/rclone-music.yml
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
- name: Require completed Atlas SFTP mount configuration
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- server_atlas_sftp_host != 'CHANGEME_ATLAS_WIREGUARD_IP'
|
||||
- server_atlas_sftp_known_host != 'CHANGEME_ATLAS_SSH_HOST_KEY'
|
||||
- server_atlas_sftp_known_host | length > 0
|
||||
- server_atlas_sftp_private_key | length > 0
|
||||
- server_atlas_sftp_remote_path == '/pool/media/music'
|
||||
fail_msg: >-
|
||||
Define the Atlas WireGuard address, pinned SSH host key and Vault-backed
|
||||
SFTP private key before enabling the music mount.
|
||||
no_log: true
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Create private rclone configuration directory
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.file:
|
||||
path: "{{ server_rclone_config_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0750"
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Install Vault-backed Atlas SFTP private key
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.copy:
|
||||
content: "{{ server_atlas_sftp_private_key | trim }}\n"
|
||||
dest: "{{ server_atlas_sftp_private_key_file }}"
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
diff: false
|
||||
notify:
|
||||
- Restart rclone music mount
|
||||
- Restart rootless Navidrome
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Install pinned Atlas SSH host key
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.copy:
|
||||
content: "{{ server_atlas_sftp_known_host | trim }}\n"
|
||||
dest: "{{ server_atlas_sftp_known_hosts_file }}"
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- Restart rclone music mount
|
||||
- Restart rootless Navidrome
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Render Atlas SFTP rclone configuration
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.template:
|
||||
src: rclone.conf.j2
|
||||
dest: "{{ server_rclone_config_file }}"
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0600"
|
||||
notify:
|
||||
- Restart rclone music mount
|
||||
- Restart rootless Navidrome
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Create rclone music directories
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ server_username }}"
|
||||
group: "{{ server_user_group }}"
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ server_rclone_music_mountpoint }}"
|
||||
- "{{ server_rclone_music_cache_dir }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Render rclone music system service
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.template:
|
||||
src: rclone-music.service.j2
|
||||
dest: "/etc/systemd/system/{{ server_rclone_music_service }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify:
|
||||
- Restart rclone music mount
|
||||
- Restart rootless Navidrome
|
||||
when: server_atlas_music_enabled | bool
|
||||
|
||||
- name: Enable and start rclone music mount
|
||||
tags: [services, rclone, navidrome]
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ server_rclone_music_service }}"
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
when:
|
||||
- server_atlas_music_enabled | bool
|
||||
- not ansible_check_mode
|
||||
@@ -0,0 +1,21 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=Rootless Navidrome music server
|
||||
|
||||
[Container]
|
||||
ContainerName=navidrome
|
||||
Image={{ server_navidrome_image }}
|
||||
Network=host
|
||||
EnvironmentFile={{ server_navidrome_env_file }}
|
||||
Volume={{ server_navidrome_data_dir }}:/data
|
||||
Volume={{ server_rclone_music_mountpoint }}:/music:ro
|
||||
SecurityLabelDisable=true
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/mountpoint -q {{ server_rclone_music_mountpoint }}
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
7
ansible/roles/profile_server/templates/navidrome.env.j2
Normal file
7
ansible/roles/profile_server/templates/navidrome.env.j2
Normal file
@@ -0,0 +1,7 @@
|
||||
# Managed by Ansible. Contains a Vault secret; do not edit or copy.
|
||||
ND_DATABASE_URL={{ ('postgres://navidrome:' ~ server_navidrome_db_password ~ '@127.0.0.1:5432/navidrome_db?sslmode=disable') | to_json }}
|
||||
ND_PORT={{ server_navidrome_port }}
|
||||
ND_SCANSCHEDULE="1h"
|
||||
ND_SESSIONTIMEOUT="24h"
|
||||
ND_ENABLETRANSCODING="true"
|
||||
ND_LOGLEVEL="info"
|
||||
@@ -0,0 +1,29 @@
|
||||
[Unit]
|
||||
Description=Read-only Atlas music mount via rclone SFTP
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
{% if server_atlas_wireguard_unit | length > 0 %}
|
||||
Wants={{ server_atlas_wireguard_unit }}
|
||||
After={{ server_atlas_wireguard_unit }}
|
||||
{% endif %}
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User={{ server_username }}
|
||||
Group={{ server_user_group }}
|
||||
ExecStart=/usr/bin/rclone mount \
|
||||
{{ server_atlas_sftp_remote_name }}:{{ server_atlas_sftp_remote_path }} \
|
||||
{{ server_rclone_music_mountpoint }} \
|
||||
--config {{ server_rclone_config_file }} \
|
||||
--cache-dir {{ server_rclone_music_cache_dir }} \
|
||||
--read-only \
|
||||
--vfs-cache-mode full \
|
||||
--vfs-cache-max-size 15G \
|
||||
--vfs-read-chunk-size 5M \
|
||||
--dir-cache-time 24h
|
||||
ExecStop=-/usr/bin/fusermount3 -uz {{ server_rclone_music_mountpoint }}
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
9
ansible/roles/profile_server/templates/rclone.conf.j2
Normal file
9
ansible/roles/profile_server/templates/rclone.conf.j2
Normal file
@@ -0,0 +1,9 @@
|
||||
# Managed by Ansible. Authentication uses the separately deployed Vault-backed key.
|
||||
[{{ server_atlas_sftp_remote_name }}]
|
||||
type = sftp
|
||||
host = {{ server_atlas_sftp_host }}
|
||||
user = {{ server_atlas_sftp_username }}
|
||||
port = {{ server_atlas_sftp_port }}
|
||||
key_file = {{ server_atlas_sftp_private_key_file }}
|
||||
known_hosts_file = {{ server_atlas_sftp_known_hosts_file }}
|
||||
shell_type = unix
|
||||
@@ -3,25 +3,6 @@
|
||||
name: server
|
||||
|
||||
services:
|
||||
navidrome:
|
||||
image: docker.io/deluan/navidrome:latest
|
||||
container_name: navidrome
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "4533"
|
||||
environment:
|
||||
ND_DATABASE_URL: "postgres://navidrome:{{ vault_navidrome_db_password }}@navidromedb:5432/navidrome_db?sslmode=disable"
|
||||
ND_SESSIONTIMEOUT: 24h
|
||||
ND_ENABLETRANSCODING: "true"
|
||||
|
||||
volumes:
|
||||
- "/opt/navidrome/data:/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||
- "/opt/music:/music:ro{{ ',' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||
user: "1000:1000"
|
||||
networks:
|
||||
- web
|
||||
depends_on:
|
||||
- navidromedb
|
||||
nginx-proxy-manager:
|
||||
image: docker.io/jc21/nginx-proxy-manager:latest
|
||||
container_name: nginx-proxy-manager
|
||||
@@ -30,6 +11,8 @@ services:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "127.0.0.1:81:81"
|
||||
extra_hosts:
|
||||
- "host.containers.internal:host-gateway"
|
||||
volumes:
|
||||
- "/opt/npm/data:/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||
- "/opt/npm/letsencrypt:/etc/letsencrypt{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||
@@ -64,11 +47,13 @@ services:
|
||||
POSTGRES_DB: "navidrome_db"
|
||||
POSTGRES_USER: "navidrome"
|
||||
POSTGRES_PASSWORD: "{{ vault_postgres_root_password }}"
|
||||
|
||||
volumes:
|
||||
- "/opt/postgres/data:/var/lib/postgresql/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
networks:
|
||||
- web
|
||||
|
||||
gitea:
|
||||
image: docker.gitea.com/gitea:1.25.2
|
||||
container_name: gitea
|
||||
|
||||
@@ -1,45 +1,61 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
62633966363435396136666635303362663932366464323231363563373462383134326361346533
|
||||
3465353461326565356435633866393230383838356235340a333036383038653134323066663031
|
||||
37373135643331356366373831343163363835656462343962636538623937356231376337616330
|
||||
3831626261343761310a306133633264613430613165343265303664383439333230663665333231
|
||||
30393934323438333066383737663665316634613932353232343334626261636661393434646163
|
||||
63316537383866353338353261373161623562343037383032633932383630666339633234326639
|
||||
64303735663532376433393464616230616236353134383734623132333061356664613039323265
|
||||
35373434336261313438633866643765323330623336343732346465613264613666616631303633
|
||||
31333437623437626135623032306434306538323461623161336431353630336539646531313365
|
||||
30356330356438353037663262643463363636386162393764323133373261376263663338636333
|
||||
33336537383061643234356365653137333639623863303530666665373663376166316533623737
|
||||
31616262363132326133666230396636663035343432633830323430336332326261613932633837
|
||||
62303232306331663936366435666164383335313335326662663363643563643938623739386663
|
||||
65356431353362623634333437346566363166366161313132653131613861386437306562633934
|
||||
38306135636161366530303461636665386639396639356265323361623639616265323462333534
|
||||
61626439616631356532353437653539333631636265346238653439313130326239633035613130
|
||||
65366161356231356638313633393735356465393339343034356138316366656431336435386437
|
||||
36366339366463396638303062366263356436666232393437613766623631313264646565316534
|
||||
32373534366363373737616563616161346265323936336665653934656365323731633637376331
|
||||
31336262356632313533393366346565373434373134653630303431643037363561613830663339
|
||||
39313837356362643164386432653030306132643234303162363463376130666239653461623736
|
||||
65333734303532356462636538326430353831336562666563336333323764346234643037373064
|
||||
63343533386564356232633661346232666135393365323133323365663637373261396264333432
|
||||
64616463643931653162373666323731653635616633386231653863623830666637623638353062
|
||||
61363339613364343239363336626266386561343032333262396436663736353835303863643865
|
||||
35616136363830326661383066316537633537616565343361633036373163316566653638343638
|
||||
33323162376537353961383965666165663631396663356533643432356630396435356636366636
|
||||
32363264303633656335333335356166323164386338363437663435336635643334646532616539
|
||||
66646336393834613337336661336638613463626230336132386265396165656461623533333738
|
||||
33353063306336623463396130386335383338383861376330616566643638333230303862356462
|
||||
63356464313263666538306230636363643665643933323236363864303634343539326239363134
|
||||
65376231623836363730376631303032393330326362383438373434383633656134373539653461
|
||||
38306632636463346365396131316234623736626335633361663861316437326635633237346262
|
||||
38643730666130656335663232366362333164353433613731363131366138633766623433363031
|
||||
37613337383961386661393733623238393062663065663931313231366366626261666336656134
|
||||
63646237643864643462653337306462646631663433663461343764306163646466326334323335
|
||||
61396162353032633937333066313438333461376161666166336337356430373737313266363238
|
||||
63633365613331636234333765633536653639343234396262653438353132623239303431316134
|
||||
35663930323535373830313766353630636237343330346232316637336531336466326564306630
|
||||
62356639356330396465353432333135313130653366303931616165663638633461613961643766
|
||||
62633564666566616665366336396436386330323536356633323965343566396232303434666534
|
||||
33333636313136326535303136643265393834613733343362346333336339636437353236326161
|
||||
65633230656531633238623830343432663365313532343435623133636239333763633930316666
|
||||
34363535313731666432
|
||||
38633239386338323332633564613266393665666562353139646234343261303131336663316634
|
||||
6535313265356434373464326664303937353132643636630a373966646565663564613463643732
|
||||
35646136346564636565616134303064313636643263343765396138653830646664633366656365
|
||||
3137643164396265650a663338346161333832643732653539366462323039313563383735626661
|
||||
62626461656636366634353731613932653235323463336263363866336466356534636435313831
|
||||
64373235383930663232613932623935346465643633666233386461346635353663323638633964
|
||||
61366664323631313631663662643437386365616162333161636166666635636633333061323739
|
||||
38386330326534303966613165656363633838313366666164656133386263646561306139383032
|
||||
66646133643239396436653564323837623031393439303938323138326330636633613864623565
|
||||
62616335633563333962653561633665643336303230623934356565363531323237643433306436
|
||||
33363238306438333765323034636261333830653836656538636662303363363733356462353764
|
||||
34636136666563333138383663643931363039303737313763373765356336656564633665666530
|
||||
32346262636530646365636565656139623639346262373963346462626638316162616666373339
|
||||
33353031313365356537646237356139613834343737343361636133343837613638643432346134
|
||||
64313232646638633035383234303934666139616162356431306161353866316136663830343737
|
||||
38646564393936383939373435313363653338653737346333643165626437386366346339303764
|
||||
34623735613130643636623733353161393034303964656635366466396362393435376565313832
|
||||
32623936646534656166383930666133663132663363623435393434663734363533653036663230
|
||||
30663130626237303938613236306465613534346466663333663432316539653836323030643636
|
||||
64646564643435373938363430303531333461613438633435356231656666373261636135396534
|
||||
66343435663835646161376636633662353337386238316131623737353364383130653334643864
|
||||
63353836303230303263343939373436646262346565313039323037383966636636656638366231
|
||||
31343431373632363031346635663834393036623166346262393736346361323837303462346130
|
||||
66643563306633323433616461343364326162336435363534326237366665316638353365376138
|
||||
61633263643637323031613462303138303133623136323637633862623032623465616538643331
|
||||
31343134353166383964666431353231636339636366626164346461386339356437386261336365
|
||||
36373936356534623035663531353132633837333366663732393661613162316234373137333861
|
||||
34656465343231623532343231646565316664373662626134346530653164393966333738656130
|
||||
62663238373135643033396334386236313531646131313765323039306431323938386163316434
|
||||
64616530343431303865336431666662343766303931653336376131313633306431346165623335
|
||||
64383262626332336430373337313462613766613931613337393335393237616463646631623562
|
||||
35343938323933663835313334306536343231663866616566653434366564313134393831343362
|
||||
63396630366338626133383232666265383266396166306536633233306139666531613031643262
|
||||
37356262363466303032336264333864646632373533396239393565653538383161653762313432
|
||||
37313230666237626530316133393134303133613463363362323132653932333663313236623639
|
||||
65326665626432386132323131353838346439393061343963616565306464636264626630396230
|
||||
31373963326134383762373737626238353964383565336132623836333535313635316637653966
|
||||
39386563396661663439343463353262656237616633646466323638346362376335306330373161
|
||||
37343431316163626365386130383862643461363132636138646634663832383933303937643934
|
||||
34326131353262643165336138643835333066663432353263333939633534383137393136633034
|
||||
35386538616633336230353332306631353566343831376662306264386330623631653437393666
|
||||
30646438666663383334333431336230373635663162626661373539336434646131346238623435
|
||||
32303361366565353161646136386237366639343664616637303232376232316334366437636630
|
||||
37633866376433633537373166613436303335663766303539336332366231353435646163386538
|
||||
66323963303034393237323035366634303736653735623937323763336234376563663563333132
|
||||
39646237306334326635353563326536633764306534313535383836633033373832316336383530
|
||||
33316432356366363934666264306661373037663563333165633132643834353262376661636463
|
||||
63326662663739616139393438393734353833376239363531376261303639376363646532666465
|
||||
66356533393331393535393161653131343536353535373432323463346331646663353439383237
|
||||
37386262633765326331383335613263653331303561343636626337333265313633373533363331
|
||||
32373564646464626633323833663432663562363639373931623535306534646664346165356161
|
||||
33306330363239386439646563353865646339323633646535316336363239303564373937636233
|
||||
37396434353830386130316237386532333939363330306165633630336662333537663265363736
|
||||
61636437623832383032643539626338313932303335333461363566363532663633353631353933
|
||||
62376531303638303838663737373462323339633632626439353439396139656639373331656636
|
||||
61303531383433343961666336363231353638656137666364323133326233623964363536303065
|
||||
33623130653564363261653333343732653539386565326133386165313536613732366463303537
|
||||
63616538383536636232636562653863323334633839656366373761626561656462316161323630
|
||||
33396566333463326136626231653536336134653232306536396434636438366335633137623433
|
||||
31636362366431396364
|
||||
|
||||
@@ -13,3 +13,10 @@ vault_navidrome_db_password: "REPLACE_ME"
|
||||
vault_postgres_root_password: "REPLACE_ME"
|
||||
vault_ikaros_authorized_ssh_keys:
|
||||
- "ssh-ed25519 REPLACE_ME"
|
||||
vault_atlas_authorized_ssh_keys:
|
||||
- "ssh-ed25519 REPLACE_ME atlas-admin"
|
||||
vault_atlas_admin_password_hash: "REPLACE_WITH_A_SHADOW_COMPATIBLE_HASH"
|
||||
vault_atlas_samba_password: "REPLACE_ME"
|
||||
vault_atlas_immich_db_password: "REPLACE_ME"
|
||||
vault_prometheus_atlas_sftp_private_key: |
|
||||
REPLACE_WITH_A_DEDICATED_ATLAS_SFTP_PRIVATE_KEY
|
||||
|
||||
Reference in New Issue
Block a user