Harden Atlas network, SSH, firewall, and sharing

This commit is contained in:
Fabio Scotto di Santolo
2026-09-13 00:01:48 +02:00
parent db10d1296e
commit b4b3bd10b8
12 changed files with 260 additions and 65 deletions

View File

@@ -55,6 +55,8 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
- Server compose render: `podman-compose -f /opt/docker/server/docker-compose.yml config` and `systemctl status podman-compose-server` - Server compose render: `podman-compose -f /opt/docker/server/docker-compose.yml config` and `systemctl status podman-compose-server`
- 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 network/share hardening:
`ansible-playbook ansible/site.yml --limit atlas --tags hardening,sharing --check --diff`
- Atlas phase-one rootless services: - Atlas phase-one rootless services:
`ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff` `ansible-playbook ansible/site.yml --limit atlas --tags backend_phase1 --check --diff`
- Prometheus/Atlas WireGuard overlay: - Prometheus/Atlas WireGuard overlay:
@@ -142,8 +144,9 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
- The `immich` system account is fixed to UID/GID `1100`, has no login shell or `wheel` membership, and receives only - 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 the `video` and `render` supplementary groups. Immich's rootful Quadlets run as `1100:1100`; Server and ML receive
`/dev/dri`, while the Photobook external library is read-only at `/external/photobook`. `/dev/dri`, while the Photobook external library is read-only at `/external/photobook`.
- Atlas exports Photobook only to the configured Aegis IP with all access squashed to UID/GID `1100`. SMB3 exposes - Atlas applies persistent kernel network hardening: redirects and source routes are rejected, martians logged, reverse-path filtering remains loose for WireGuard, and IPv4 forwarding is disabled. SSH permits only the declared administrator using public-key authentication; root login, passwords, agent and remote forwarding
`Archive` to Vault-backed authorized accounts and admits the configured LAN without host-specific exclusions. are disabled, while local forwarding remains available for private administrative tunnels. Photobook is exported only to the configured Aegis IP with all access squashed to UID/GID
`1100`. Targeted SELinux is enforced persistently; a required reboot is reported but never initiated automatically. The primary LAN interface is assigned explicitly to the managed firewalld zone, and firewall rules are applied before NFS or SMB are started; their service state and TCP listeners are then verified. SMB3 exposes `Archive` to Vault-backed authorized accounts on mandatory encrypted, signed SMB3 over TCP/445 only and admits the configured LAN without host-specific exclusions.
- Atlas NPM and Immich share a rootful Podman network. NPM publishes HTTP/HTTPS, but its administration port remains - Atlas NPM and Immich share a rootful Podman network. NPM publishes HTTP/HTTPS, but its administration port remains
bound to `127.0.0.1:81`; do not expose it directly to the LAN or Internet. bound to `127.0.0.1:81`; do not expose it directly to the LAN or Internet.
- `profile_backend_phase1` is limited to rootless Navidrome and Syncthing user Quadlets on Atlas. Official Navidrome - `profile_backend_phase1` is limited to rootless Navidrome and Syncthing user Quadlets on Atlas. Official Navidrome
@@ -155,8 +158,8 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
Atlas and its SQLite database verified. The playbook renders the target but never migrates or deletes application Atlas and its SQLite database verified. The playbook renders the target but never migrates or deletes application
data; after cutover, set the flag true to enable and start Navidrome and Syncthing. data; after cutover, set the flag true to enable and start Navidrome and Syncthing.
- Phase 1 must not change Prometheus' existing NPM deployment. NPM continues to be managed exactly by `profile_server`; - Phase 1 must not change Prometheus' existing NPM deployment. NPM continues to be managed exactly by `profile_server`;
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. Syncthing does not use host networking:
not use the HTTP proxy. its GUI, transfer, QUIC and discovery ports are explicitly published only on `10.0.0.2`; native transfer/discovery does not use the HTTP proxy.
- `wireguard_overlay` manages the required `wg0` path between Prometheus and Atlas, persists private keys only on their - `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 initial 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.

View File

@@ -196,8 +196,8 @@ Nextcloud resta disabilitato e il profilo non crea directory `/srv/nextcloud`.
La fase 1 su Atlas non modifica questo deployment NPM ne i suoi dati persistenti. Dopo aver attivato La fase 1 su Atlas non modifica questo deployment NPM ne i suoi dati persistenti. Dopo aver attivato
WireGuard e i servizi Atlas, configurare i proxy host NPM correnti con upstream Navidrome WireGuard e i servizi Atlas, configurare i proxy host NPM correnti con upstream Navidrome
`http://10.0.0.2:4533` e upstream per la GUI Syncthing `http://10.0.0.2:8384`. Solo la GUI web di `http://10.0.0.2:4533` e upstream per la GUI Syncthing `http://10.0.0.2:8384`. Solo la GUI web di
Syncthing usa NPM; il traffico di sincronizzazione resta sulle porte native limitate a WireGuard. Syncthing usa NPM; il traffico di sincronizzazione resta sulle porte native pubblicate esplicitamente solo
Configurare l'autenticazione Syncthing e una policy di accesso NPM adeguata prima di pubblicare la GUI. sull'indirizzo WireGuard di Atlas. Configurare l'autenticazione Syncthing e una policy di accesso NPM adeguata prima di pubblicare la GUI.
### DuckDNS ### DuckDNS
@@ -274,9 +274,10 @@ Con la gestione storage attiva, Atlas crea l'intera gerarchia sotto il pool `zpo
`media/photobook`, `backups`, `backups/services` e `backup_prometheus`. I dataset applicativi e `media/photobook`, `backups`, `backups/services` e `backup_prometheus`. I dataset applicativi e
di archivio usano `zstd`; media, Syncthing e backup dei servizi usano `lz4`; di archivio usano `zstd`; media, Syncthing e backup dei servizi usano `lz4`;
`backups/services` mantiene inoltre una `refreservation` di `500G`. `backups/services` mantiene inoltre una `refreservation` di `500G`.
SMB3 pubblica `Archive` solo agli account Samba configurati con password in Vault e ammette la LAN Atlas impone SELinux targeted in modo persistente e segnala, senza avviarlo, l’eventuale reboot necessario per attivarlo. Assegna esplicitamente l’interfaccia LAN primaria alla zona firewalld gestita e applica hardening persistente del kernel di rete: rifiuta redirect e source-route, registra i martian, usa reverse-path filtering loose per WireGuard e disabilita il forwarding IPv4. SSH consente solo l’amministratore dichiarato tramite chiave pubblica; root, password, agent e forwarding
configurata senza esclusioni specifiche per host. NFSv4 esporta soltanto remoto sono disabilitati, mentre il forwarding locale resta disponibile per tunnel amministrativi privati. SMB3 pubblica `Archive` solo agli account Samba configurati con password in Vault e
`media/photobook` all'IP configurato di Aegis con `all_squash` verso UID/GID anonimi `1100`. ammette la LAN configurata su SMB3 cifrato e firmato, esclusivamente su TCP/445. NFSv4 esporta soltanto
`media/photobook` all'IP configurato di Aegis su TCP/2049, 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 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 i gruppi supplementari `video` e `render`. I Quadlet rootful di Immich Server, ML, cache compatibile

View File

@@ -119,8 +119,9 @@ does not provision any `/srv/nextcloud` directories.
The Atlas phase-one work does not change this NPM deployment or its persistent data. Once WireGuard The Atlas phase-one work does not change this NPM deployment or its persistent data. Once WireGuard
and the Atlas services are active, configure the current NPM proxy hosts with Navidrome upstream and the Atlas services are active, configure the current NPM proxy hosts with Navidrome upstream
`http://10.0.0.2:4533` and Syncthing GUI upstream `http://10.0.0.2:8384`. Only the Syncthing web GUI `http://10.0.0.2:4533` and Syncthing GUI upstream `http://10.0.0.2:8384`. Only the Syncthing web GUI
uses NPM; synchronization traffic remains on its native WireGuard-restricted ports. Configure both uses NPM; synchronization traffic remains on explicitly published native ports bound only to the Atlas
Syncthing authentication and an appropriate NPM access policy before publishing its GUI. WireGuard address. Configure both Syncthing authentication and an appropriate NPM access policy before
publishing its GUI.
Server identity comes from `server_username`, `server_user_group`, and `server_user_home` in `ansible/inventory/group_vars/server.yml`. `server_username` defaults to `username`, but it can be overridden, for example: Server identity comes from `server_username`, `server_user_group`, and `server_user_home` in `ansible/inventory/group_vars/server.yml`. `server_username` defaults to `username`, but it can be overridden, for example:
@@ -230,9 +231,11 @@ explicitly bootstrapped `zpool`: `work`, `archive`, `archive/app_data`, the sepa
`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`
refreservation. SMB3 exposes `Archive` only to the configured Vault-backed Samba accounts and admits refreservation. Atlas enforces targeted SELinux persistently and reports, without initiating, any reboot required to activate it. It assigns its primary LAN interface explicitly to the managed firewalld zone and applies persistent kernel network hardening: redirects and source routes are rejected, martians logged, reverse-path filtering remains loose for WireGuard, and IPv4 forwarding is disabled. SSH permits only the declared administrator using public-key authentication; root login, passwords,
the configured LAN without host-specific exclusions. NFSv4 exports only agent and remote forwarding are disabled, while local forwarding remains available for private administrative tunnels. SMB3 exposes `Archive` only to the configured Vault-backed
`media/photobook` to the configured Aegis IP, using `all_squash` with anonymous UID/GID `1100`. Samba accounts on encrypted, signed SMB3 over TCP/445 only and admits the configured LAN without host-specific
exclusions. NFSv4 exports only `media/photobook` to the configured Aegis IP over TCP/2049, 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 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, receives `video` and `render` access. The rootful Immich Server, ML, Redis-compatible cache, PostgreSQL,

View File

@@ -14,6 +14,7 @@ atlas_admin_uid: 1000
atlas_admin_gid: 1000 atlas_admin_gid: 1000
atlas_admin_ssh_keys: "{{ vault_atlas_authorized_ssh_keys | default([]) }}" atlas_admin_ssh_keys: "{{ vault_atlas_authorized_ssh_keys | default([]) }}"
atlas_admin_password_hash: "{{ vault_atlas_admin_password_hash | default('!') }}" atlas_admin_password_hash: "{{ vault_atlas_admin_password_hash | default('!') }}"
atlas_ssh_allow_tcp_forwarding: local
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
@@ -63,7 +64,9 @@ rocky_podman_packages:
host_packages: host_packages:
- cockpit - cockpit
- nfs-utils - nfs-utils
- policycoreutils
- policycoreutils-python-utils - policycoreutils-python-utils
- python3-libselinux
- samba - samba
- samba-client - samba-client
- samba-common-tools - samba-common-tools
@@ -82,6 +85,7 @@ atlas_nfs_exports:
atlas_samba_share_name: Archive atlas_samba_share_name: Archive
atlas_samba_workgroup: WORKGROUP atlas_samba_workgroup: WORKGROUP
atlas_samba_encryption: required
# Append any pre-existing laptop Unix account here and keep its Samba password in Vault. # Append any pre-existing laptop Unix account here and keep its Samba password in Vault.
atlas_samba_accounts: atlas_samba_accounts:
- username: "{{ atlas_admin_username }}" - username: "{{ atlas_admin_username }}"
@@ -91,12 +95,10 @@ atlas_samba_valid_users: "{{ atlas_samba_accounts | map(attribute='username') |
atlas_immich_db_password: "{{ vault_atlas_immich_db_password | default('') }}" atlas_immich_db_password: "{{ vault_atlas_immich_db_password | default('') }}"
atlas_firewalld_rich_rules: atlas_firewalld_rich_rules:
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="ssh" accept' - 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" port port="22" protocol="tcp" accept'
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="cockpit" accept' - 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" port port="9090" protocol="tcp" accept'
- 'rule family="ipv4" source address="{{ atlas_aegis_ip }}" service name="nfs" accept' - 'rule family="ipv4" source address="{{ atlas_aegis_ip }}" port port="2049" protocol="tcp" accept'
- 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" service name="samba" accept' - 'rule family="ipv4" source address="{{ atlas_lan_subnet }}" port port="445" protocol="tcp" 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: host_enabled_services:
- sshd - sshd

View File

@@ -11,6 +11,8 @@ atlas_admin_uid: 1000
atlas_admin_gid: 1000 atlas_admin_gid: 1000
atlas_admin_ssh_keys: [] atlas_admin_ssh_keys: []
atlas_admin_password_hash: "!" atlas_admin_password_hash: "!"
# Local forwarding permits an administrator to tunnel a private service without allowing remote forwards.
atlas_ssh_allow_tcp_forwarding: local
atlas_immich_username: immich atlas_immich_username: immich
atlas_immich_group: immich atlas_immich_group: immich
@@ -24,6 +26,25 @@ atlas_lan_subnet: CHANGEME_LAN_SUBNET
atlas_aegis_ip: CHANGEME_AEGIS_IP atlas_aegis_ip: CHANGEME_AEGIS_IP
atlas_manage_firewall: false atlas_manage_firewall: false
atlas_firewalld_zone: public atlas_firewalld_zone: public
atlas_hardening_sysctls:
net.ipv4.conf.all.accept_redirects: "0"
net.ipv4.conf.default.accept_redirects: "0"
net.ipv4.conf.all.send_redirects: "0"
net.ipv4.conf.default.send_redirects: "0"
net.ipv4.conf.all.accept_source_route: "0"
net.ipv4.conf.default.accept_source_route: "0"
net.ipv4.conf.all.log_martians: "1"
net.ipv4.conf.default.log_martians: "1"
net.ipv4.conf.all.rp_filter: "2"
net.ipv4.conf.default.rp_filter: "2"
net.ipv4.tcp_syncookies: "1"
net.ipv4.icmp_echo_ignore_broadcasts: "1"
net.ipv4.icmp_ignore_bogus_error_responses: "1"
net.ipv4.ip_forward: "0"
net.ipv6.conf.all.accept_redirects: "0"
net.ipv6.conf.default.accept_redirects: "0"
net.ipv6.conf.all.accept_source_route: "0"
net.ipv6.conf.default.accept_source_route: "0"
atlas_zfs_pool: CHANGEME_ZFS_POOL atlas_zfs_pool: CHANGEME_ZFS_POOL
atlas_zfs_dataset_work: work atlas_zfs_dataset_work: work
atlas_zfs_dataset_archive: archive atlas_zfs_dataset_archive: archive
@@ -72,6 +93,8 @@ atlas_nfs_exports:
- "anongid={{ atlas_immich_gid }}" - "anongid={{ atlas_immich_gid }}"
atlas_samba_share_name: Archive atlas_samba_share_name: Archive
atlas_samba_workgroup: WORKGROUP atlas_samba_workgroup: WORKGROUP
# Keep encryption mandatory unless a verified client-compatibility exception is explicitly required.
atlas_samba_encryption: required
atlas_samba_valid_users: [] atlas_samba_valid_users: []
atlas_samba_accounts: [] atlas_samba_accounts: []
atlas_samba_password_marker_dir: /var/lib/samba/private/ansible-passwords atlas_samba_password_marker_dir: /var/lib/samba/private/ansible-passwords

View File

@@ -6,6 +6,7 @@
- atlas_admin_username != 'CHANGEME_ATLAS_ADMIN' - atlas_admin_username != 'CHANGEME_ATLAS_ADMIN'
- (atlas_admin_ssh_keys | default([])) | length > 0 - (atlas_admin_ssh_keys | default([])) | length > 0
- atlas_admin_password_hash not in ['', '!', '*'] - atlas_admin_password_hash not in ['', '!', '*']
- atlas_ssh_allow_tcp_forwarding in ['no', 'yes', 'local', 'remote', 'all']
- "'wheel' not in atlas_immich_supplementary_groups" - "'wheel' not in atlas_immich_supplementary_groups"
fail_msg: >- fail_msg: >-
Define atlas_admin_username, vault_atlas_authorized_ssh_keys and Define atlas_admin_username, vault_atlas_authorized_ssh_keys and
@@ -149,6 +150,18 @@
- "'pubkeyauthentication yes' in atlas_sshd_effective_configuration.stdout_lines" - "'pubkeyauthentication yes' in atlas_sshd_effective_configuration.stdout_lines"
- "'passwordauthentication no' in atlas_sshd_effective_configuration.stdout_lines" - "'passwordauthentication no' in atlas_sshd_effective_configuration.stdout_lines"
- "'kbdinteractiveauthentication no' in atlas_sshd_effective_configuration.stdout_lines" - "'kbdinteractiveauthentication no' in atlas_sshd_effective_configuration.stdout_lines"
- "'authenticationmethods publickey' in atlas_sshd_effective_configuration.stdout_lines"
- "'x11forwarding no' in atlas_sshd_effective_configuration.stdout_lines"
- "('allowtcpforwarding ' ~ atlas_ssh_allow_tcp_forwarding) in atlas_sshd_effective_configuration.stdout_lines"
- "'allowagentforwarding no' in atlas_sshd_effective_configuration.stdout_lines"
- "'gatewayports no' in atlas_sshd_effective_configuration.stdout_lines"
- "'permittunnel no' in atlas_sshd_effective_configuration.stdout_lines"
- "'permituserenvironment no' in atlas_sshd_effective_configuration.stdout_lines"
- "'maxauthtries 3' in atlas_sshd_effective_configuration.stdout_lines"
- "'logingracetime 30' in atlas_sshd_effective_configuration.stdout_lines"
- "'clientaliveinterval 300' in atlas_sshd_effective_configuration.stdout_lines"
- "'clientalivecountmax 2' in atlas_sshd_effective_configuration.stdout_lines"
- "'loglevel VERBOSE' in atlas_sshd_effective_configuration.stdout_lines"
- "('allowusers ' ~ atlas_admin_username) in atlas_sshd_effective_configuration.stdout_lines" - "('allowusers ' ~ atlas_admin_username) in atlas_sshd_effective_configuration.stdout_lines"
fail_msg: The effective Atlas SSH configuration does not match the required hardening. fail_msg: The effective Atlas SSH configuration does not match the required hardening.
when: not ansible_check_mode when: not ansible_check_mode

View File

@@ -0,0 +1,45 @@
---
- name: Enforce targeted SELinux on Atlas
tags: [atlas, hardening, security]
ansible.posix.selinux:
policy: targeted
state: enforcing
update_kernel_param: true
register: atlas_selinux_enforcement
- name: Report when Atlas requires a reboot for SELinux enforcement
tags: [atlas, hardening, security]
ansible.builtin.debug:
msg: Reboot Atlas before relying on SELinux enforcement.
when: atlas_selinux_enforcement.reboot_required | default(false)
- name: Apply Atlas network-kernel hardening
tags: [atlas, hardening, security]
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
sysctl_set: true
reload: true
loop: "{{ atlas_hardening_sysctls | dict2items }}"
loop_control:
label: "{{ item.key }}={{ item.value }}"
- name: Read active Atlas SELinux mode
tags: [atlas, hardening, security]
ansible.builtin.command:
argv:
- getenforce
register: atlas_selinux_mode
changed_when: false
when: not ansible_check_mode
- name: Require active SELinux enforcement on Atlas
tags: [atlas, hardening, security]
ansible.builtin.assert:
that:
- atlas_selinux_mode.stdout == 'Enforcing'
fail_msg: >-
Atlas SELinux is not enforcing. Reboot if requested by the preceding
SELinux task, then rerun the hardening role before enabling services.
when: not ansible_check_mode

View File

@@ -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 network hardening tasks
ansible.builtin.import_tasks: hardening.yml
- name: Import Atlas pool bootstrap tasks - name: Import Atlas pool bootstrap tasks
ansible.builtin.import_tasks: pool.yml ansible.builtin.import_tasks: pool.yml

View File

@@ -1,4 +1,50 @@
--- - 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
- ansible_facts.default_ipv4.interface | default('') | length > 0
fail_msg: Replace the Atlas LAN, Aegis and firewall-zone placeholders.
when: atlas_manage_firewall | bool
- name: Apply Atlas firewalld rich rules
tags: [atlas, sharing, services]
ansible.posix.firewalld:
rich_rule: "{{ item }}"
permanent: true
immediate: true
state: enabled
zone: "{{ atlas_firewalld_zone }}"
loop: "{{ atlas_firewalld_rich_rules }}"
loop_control:
label: "{{ item }}"
when: atlas_manage_firewall | bool
- name: Assign primary Atlas LAN interface to managed firewalld zone
tags: [atlas, sharing, services]
ansible.posix.firewalld:
interface: "{{ ansible_facts.default_ipv4.interface }}"
zone: "{{ atlas_firewalld_zone }}"
state: enabled
permanent: true
immediate: true
when: atlas_manage_firewall | bool
- name: Remove unrestricted Atlas services from firewalld zone
tags: [atlas, sharing, services]
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
immediate: true
state: disabled
zone: "{{ atlas_firewalld_zone }}"
loop: "{{ atlas_firewalld_restricted_services }}"
loop_control:
label: "{{ item }}"
when: atlas_manage_firewall | bool
- name: Render Atlas NFS exports - name: Render Atlas NFS exports
tags: [atlas, sharing] tags: [atlas, sharing]
ansible.builtin.template: ansible.builtin.template:
@@ -10,19 +56,6 @@
notify: Reload NFS exports notify: Reload NFS exports
when: atlas_manage_sharing | bool when: atlas_manage_sharing | 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_sharing | bool
- name: Configure Atlas NFSv4-only service - name: Configure Atlas NFSv4-only service
tags: [atlas, sharing] tags: [atlas, sharing]
ansible.builtin.template: ansible.builtin.template:
@@ -99,6 +132,7 @@
tags: [atlas, sharing] tags: [atlas, sharing]
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- atlas_samba_encryption in ['required', 'desired']
- atlas_samba_accounts | length > 0 - atlas_samba_accounts | length > 0
- >- - >-
atlas_samba_accounts | map(attribute='username') | list atlas_samba_accounts | map(attribute='username') | list
@@ -131,38 +165,87 @@
no_log: true no_log: true
when: atlas_manage_sharing | bool when: atlas_manage_sharing | bool
- name: Require completed Atlas firewall placeholders - name: Enable Atlas file-sharing services
tags: [atlas, 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_sharing | bool
- name: Gather effective Atlas firewalld zone configuration
tags: [atlas, sharing, services, security]
ansible.posix.firewalld_info:
zones:
- "{{ atlas_firewalld_zone }}"
register: atlas_firewalld_info
when:
- atlas_manage_firewall | bool
- not ansible_check_mode
- name: Verify effective Atlas firewalld restrictions
tags: [atlas, sharing, services, security]
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- atlas_lan_subnet != 'CHANGEME_LAN_SUBNET' - atlas_firewalld_zone in atlas_firewalld_info.firewalld_info.zones
- atlas_aegis_ip != 'CHANGEME_AEGIS_IP' - >-
- atlas_firewalld_zone | length > 0 ansible_facts.default_ipv4.interface
fail_msg: Replace the Atlas LAN, Aegis and firewall-zone placeholders. in atlas_firewalld_info.firewalld_info.zones[atlas_firewalld_zone].interfaces
when: atlas_manage_firewall | bool - >-
atlas_firewalld_restricted_services
| intersect(atlas_firewalld_info.firewalld_info.zones[atlas_firewalld_zone].services)
| length == 0
fail_msg: >-
The effective Atlas firewalld zone does not restrict the primary LAN
interface and unmanaged service exposure as declared.
when:
- atlas_manage_firewall | bool
- not ansible_check_mode
- name: Apply Atlas firewalld rich rules - name: Apply pending Atlas sharing handlers before verification
tags: [atlas, sharing, services] tags: [atlas, sharing, services, security]
ansible.posix.firewalld: ansible.builtin.meta: flush_handlers
rich_rule: "{{ item }}" when:
permanent: true - atlas_manage_sharing | bool
immediate: true - not ansible_check_mode
state: enabled
zone: "{{ atlas_firewalld_zone }}"
loop: "{{ atlas_firewalld_rich_rules }}"
loop_control:
label: "{{ item }}"
when: atlas_manage_firewall | bool
- name: Remove unrestricted Atlas services from firewalld zone - name: Read Atlas file-sharing service state
tags: [atlas, sharing, services] tags: [atlas, sharing, services, security]
ansible.posix.firewalld: ansible.builtin.service_facts:
service: "{{ item }}" when:
permanent: true - atlas_manage_sharing | bool
immediate: true - not ansible_check_mode
state: disabled
zone: "{{ atlas_firewalld_zone }}" - name: Require active Atlas file-sharing services
loop: "{{ atlas_firewalld_restricted_services }}" tags: [atlas, sharing, services, security]
ansible.builtin.assert:
that:
- ansible_facts.services['nfs-server.service'] is defined
- ansible_facts.services['nfs-server.service'].state == 'running'
- ansible_facts.services['smb.service'] is defined
- ansible_facts.services['smb.service'].state == 'running'
fail_msg: Atlas NFSv4 or SMB3 did not start after its managed configuration was applied.
when:
- atlas_manage_sharing | bool
- not ansible_check_mode
- name: Require Atlas file-sharing TCP listeners
tags: [atlas, sharing, services, security]
ansible.builtin.wait_for:
host: 127.0.0.1
port: "{{ item }}"
state: started
timeout: 15
loop:
- 2049
- 445
loop_control: loop_control:
label: "{{ item }}" label: "{{ item }}/tcp"
when: atlas_manage_firewall | bool when:
- atlas_manage_sharing | bool
- not ansible_check_mode

View File

@@ -3,4 +3,16 @@ PermitRootLogin no
PubkeyAuthentication yes PubkeyAuthentication yes
PasswordAuthentication no PasswordAuthentication no
KbdInteractiveAuthentication no KbdInteractiveAuthentication no
AuthenticationMethods publickey
X11Forwarding no
AllowTcpForwarding {{ atlas_ssh_allow_tcp_forwarding }}
AllowAgentForwarding no
GatewayPorts no
PermitTunnel no
PermitUserEnvironment no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
LogLevel VERBOSE
AllowUsers {{ atlas_admin_username }} AllowUsers {{ atlas_admin_username }}

View File

@@ -4,6 +4,10 @@
security = user security = user
map to guest = Never map to guest = Never
server min protocol = SMB3 server min protocol = SMB3
server signing = mandatory
smb encrypt = {{ atlas_samba_encryption }}
disable netbios = yes
smb ports = 445
hosts allow = {{ atlas_lan_subnet }} hosts allow = {{ atlas_lan_subnet }}
include = registry include = registry

View File

@@ -6,13 +6,16 @@ Description=Atlas rootless Syncthing backend
ContainerName=atlas-syncthing ContainerName=atlas-syncthing
Image={{ backend_phase1_syncthing_image }} Image={{ backend_phase1_syncthing_image }}
HostName=atlas-syncthing HostName=atlas-syncthing
Network=host
UserNS=keep-id UserNS=keep-id
User={{ backend_phase1_uid }} User={{ backend_phase1_uid }}
Group={{ backend_phase1_gid }} Group={{ backend_phase1_gid }}
PublishPort={{ backend_phase1_wireguard_address }}:{{ backend_phase1_syncthing_gui_port }}:{{ backend_phase1_syncthing_gui_port }}
PublishPort={{ backend_phase1_wireguard_address }}:{{ backend_phase1_syncthing_transfer_port }}:{{ backend_phase1_syncthing_transfer_port }}
PublishPort={{ backend_phase1_wireguard_address }}:{{ backend_phase1_syncthing_transfer_port }}:{{ backend_phase1_syncthing_transfer_port }}/udp
PublishPort={{ backend_phase1_wireguard_address }}:{{ backend_phase1_syncthing_discovery_port }}:{{ backend_phase1_syncthing_discovery_port }}/udp
Environment=HOME=/var/syncthing Environment=HOME=/var/syncthing
Environment=STHOMEDIR=/var/syncthing/config Environment=STHOMEDIR=/var/syncthing/config
Environment=STGUIADDRESS={{ backend_phase1_wireguard_address }}:{{ backend_phase1_syncthing_gui_port }} Environment=STGUIADDRESS=0.0.0.0:{{ backend_phase1_syncthing_gui_port }}
Environment=TZ={{ backend_phase1_timezone }} Environment=TZ={{ backend_phase1_timezone }}
Volume={{ backend_phase1_syncthing_root }}:/var/syncthing:Z Volume={{ backend_phase1_syncthing_root }}:/var/syncthing:Z
NoNewPrivileges=true NoNewPrivileges=true