mirror of
https://github.com/fscotto/infra.git
synced 2026-09-27 19:03:47 +00:00
Add Rocky 9 Atlas NAS profile
This commit is contained in:
118
ansible/roles/profile_atlas/tasks/account.yml
Normal file
118
ansible/roles/profile_atlas/tasks/account.yml
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
- name: Reject incomplete Atlas account configuration
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_admin_username != 'CHANGEME_ATLAS_ADMIN'
|
||||
- (atlas_admin_ssh_keys | default([])) | length > 0
|
||||
- atlas_admin_password_hash not in ['', '!', '*']
|
||||
fail_msg: >-
|
||||
Define atlas_admin_username, vault_atlas_authorized_ssh_keys and
|
||||
vault_atlas_admin_password_hash before applying the Atlas profile.
|
||||
no_log: true
|
||||
|
||||
- name: Create Atlas administrator group
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.group:
|
||||
name: "{{ atlas_admin_group }}"
|
||||
gid: "{{ atlas_admin_gid }}"
|
||||
state: present
|
||||
|
||||
- name: Create Atlas administrator account
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.user:
|
||||
name: "{{ atlas_admin_username }}"
|
||||
uid: "{{ atlas_admin_uid }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
home: "{{ atlas_admin_home }}"
|
||||
shell: /bin/bash
|
||||
password: "{{ atlas_admin_password_hash }}"
|
||||
create_home: true
|
||||
state: present
|
||||
no_log: true
|
||||
|
||||
- name: Grant Atlas administrator passwordless sudo
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.copy:
|
||||
content: "{{ atlas_admin_username }} ALL=(ALL) NOPASSWD: ALL\n"
|
||||
dest: "/etc/sudoers.d/{{ atlas_admin_username }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
validate: "visudo -cf %s"
|
||||
|
||||
- name: Manage Atlas administrator authorized SSH keys exclusively
|
||||
tags: [atlas, services]
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ atlas_admin_username }}"
|
||||
key: "{{ atlas_admin_ssh_keys | join('\n') }}"
|
||||
state: present
|
||||
exclusive: true
|
||||
|
||||
- name: Check whether the Atlas SSH host key exists
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.stat:
|
||||
path: /etc/ssh/ssh_host_ed25519_key
|
||||
register: atlas_ssh_host_ed25519_key
|
||||
|
||||
- name: Generate missing Atlas SSH host keys
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- ssh-keygen
|
||||
- -A
|
||||
changed_when: true
|
||||
when: not atlas_ssh_host_ed25519_key.stat.exists
|
||||
|
||||
- name: Ensure Atlas SSH configuration drop-in directory exists
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.file:
|
||||
path: /etc/ssh/sshd_config.d
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Ensure Atlas SSH drop-ins are loaded before other settings
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^\s*Include\s+/etc/ssh/sshd_config\.d/\*\.conf\s*$'
|
||||
line: Include /etc/ssh/sshd_config.d/*.conf
|
||||
insertbefore: BOF
|
||||
state: present
|
||||
validate: "sshd -t -f %s"
|
||||
|
||||
- name: Render Atlas SSH hardening drop-in
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.template:
|
||||
src: 00-atlas-hardening.conf.j2
|
||||
dest: /etc/ssh/sshd_config.d/00-atlas-hardening.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
notify: Reload SSH service
|
||||
|
||||
- name: Read effective Atlas SSH daemon configuration
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- sshd
|
||||
- -T
|
||||
- -C
|
||||
- "user={{ atlas_admin_username }},host=atlas,addr=127.0.0.1"
|
||||
register: atlas_sshd_effective_configuration
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Verify effective Atlas SSH hardening
|
||||
tags: [atlas, services]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'permitrootlogin no' 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"
|
||||
- "'kbdinteractiveauthentication no' 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.
|
||||
when: not ansible_check_mode
|
||||
15
ansible/roles/profile_atlas/tasks/cockpit.yml
Normal file
15
ansible/roles/profile_atlas/tasks/cockpit.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Configure the official 45Drives repository
|
||||
tags: [atlas, packages]
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ atlas_45drives_repo_url }}"
|
||||
dest: "{{ atlas_45drives_repo_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Install 45Drives Cockpit plugins
|
||||
tags: [atlas, packages]
|
||||
ansible.builtin.dnf:
|
||||
name: "{{ atlas_45drives_packages }}"
|
||||
state: present
|
||||
15
ansible/roles/profile_atlas/tasks/main.yml
Normal file
15
ansible/roles/profile_atlas/tasks/main.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Import Atlas account tasks
|
||||
ansible.builtin.import_tasks: account.yml
|
||||
|
||||
- name: Import Atlas 45Drives tasks
|
||||
ansible.builtin.import_tasks: cockpit.yml
|
||||
|
||||
- name: Import Atlas storage tasks
|
||||
ansible.builtin.import_tasks: storage.yml
|
||||
|
||||
- name: Import Atlas file sharing tasks
|
||||
ansible.builtin.import_tasks: sharing.yml
|
||||
|
||||
- name: Import Atlas Syncthing tasks
|
||||
ansible.builtin.import_tasks: syncthing.yml
|
||||
211
ansible/roles/profile_atlas/tasks/sharing.yml
Normal file
211
ansible/roles/profile_atlas/tasks/sharing.yml
Normal file
@@ -0,0 +1,211 @@
|
||||
---
|
||||
- name: Render Atlas NFS exports
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.template:
|
||||
src: atlas.exports.j2
|
||||
dest: /etc/exports.d/atlas.exports
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload NFS exports
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Configure Atlas NFSv4-only service
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.template:
|
||||
src: atlas-nfs.conf.j2
|
||||
dest: /etc/nfs.conf.d/atlas.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart NFS server
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Mask Atlas NFSv3 RPC services
|
||||
tags: [atlas, sharing, services]
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: false
|
||||
state: stopped
|
||||
masked: true
|
||||
loop:
|
||||
- rpc-statd.service
|
||||
- rpcbind.service
|
||||
- rpcbind.socket
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Ensure Atlas NFS mount daemon drop-in directory exists
|
||||
tags: [atlas, sharing, services]
|
||||
ansible.builtin.file:
|
||||
path: /etc/systemd/system/nfs-mountd.service.d
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Disable Atlas NFSv3 mount daemon listeners
|
||||
tags: [atlas, sharing, services]
|
||||
ansible.builtin.template:
|
||||
src: nfs-mountd-v4only.conf.j2
|
||||
dest: /etc/systemd/system/nfs-mountd.service.d/v4only.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart NFS mount daemon
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Enable SELinux access for Atlas file sharing
|
||||
tags: [atlas, sharing, services]
|
||||
ansible.posix.seboolean:
|
||||
name: "{{ item }}"
|
||||
state: true
|
||||
persistent: true
|
||||
loop: "{{ atlas_selinux_booleans }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when:
|
||||
- atlas_manage_storage | bool
|
||||
- (ansible_facts['selinux'] | default({})).get('status', 'disabled') == 'enabled'
|
||||
|
||||
- name: Render Atlas Samba configuration
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.template:
|
||||
src: smb.conf.j2
|
||||
dest: /etc/samba/smb.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
validate: "testparm --suppress-prompt %s"
|
||||
notify: Restart Samba service
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Require Atlas Samba password
|
||||
tags: [atlas, sharing]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_samba_password | length > 0
|
||||
fail_msg: Define vault_atlas_samba_password before enabling Atlas storage.
|
||||
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 }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Check Atlas Samba password marker
|
||||
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'))
|
||||
}}
|
||||
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_firewalld_zone | length > 0
|
||||
fail_msg: Replace the Atlas LAN subnet 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: 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
|
||||
77
ansible/roles/profile_atlas/tasks/storage.yml
Normal file
77
ansible/roles/profile_atlas/tasks/storage.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
- name: Define Atlas ZFS datasets
|
||||
tags: [atlas, storage]
|
||||
ansible.builtin.set_fact:
|
||||
atlas_zfs_datasets:
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_work }}"
|
||||
mountpoint: "{{ atlas_work_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_syncthing }}"
|
||||
mountpoint: "{{ atlas_syncthing_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_backup_prometheus }}"
|
||||
mountpoint: "{{ atlas_backup_prometheus_mountpoint }}"
|
||||
- name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_icloud_photos }}"
|
||||
mountpoint: "{{ atlas_icloud_photos_mountpoint }}"
|
||||
|
||||
- name: Require completed Atlas storage placeholders
|
||||
tags: [atlas, storage]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_zfs_pool != 'CHANGEME_ZFS_POOL'
|
||||
- atlas_mount_root != '/CHANGEME_ATLAS_MOUNT_ROOT'
|
||||
- atlas_mount_root.startswith('/')
|
||||
- (atlas_zfs_datasets | map(attribute='name') | unique | list | length) == (atlas_zfs_datasets | length)
|
||||
- (atlas_zfs_datasets | map(attribute='mountpoint') | unique | list | length) == (atlas_zfs_datasets | length)
|
||||
fail_msg: >-
|
||||
Replace the Atlas ZFS pool and mount-root placeholders, then set
|
||||
atlas_manage_storage to true before managing datasets.
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Check whether the configured Atlas ZFS pool exists
|
||||
tags: [atlas, storage]
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- zpool
|
||||
- list
|
||||
- -H
|
||||
- -o
|
||||
- name
|
||||
- "{{ atlas_zfs_pool }}"
|
||||
register: atlas_zfs_pool_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Require the configured Atlas ZFS pool
|
||||
tags: [atlas, storage]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- atlas_zfs_pool_check.rc == 0
|
||||
fail_msg: >-
|
||||
The configured Atlas ZFS pool does not exist. The Atlas profile only
|
||||
manages child datasets and never creates pools.
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Manage Atlas ZFS datasets declaratively
|
||||
tags: [atlas, storage]
|
||||
community.general.zfs:
|
||||
name: "{{ item.name }}"
|
||||
state: present
|
||||
extra_zfs_properties:
|
||||
mountpoint: "{{ item.mountpoint }}"
|
||||
loop: "{{ atlas_zfs_datasets }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Set Atlas dataset ownership
|
||||
tags: [atlas, storage]
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.mountpoint }}"
|
||||
state: directory
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
mode: "0770"
|
||||
loop: "{{ atlas_zfs_datasets }}"
|
||||
loop_control:
|
||||
label: "{{ item.mountpoint }}"
|
||||
when: atlas_manage_storage | bool
|
||||
40
ansible/roles/profile_atlas/tasks/syncthing.yml
Normal file
40
ansible/roles/profile_atlas/tasks/syncthing.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
- name: Create Atlas Syncthing configuration directory
|
||||
tags: [atlas, syncthing]
|
||||
ansible.builtin.file:
|
||||
path: "{{ atlas_syncthing_config_dir }}"
|
||||
state: directory
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
mode: "0700"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Create Atlas Syncthing default data directory
|
||||
tags: [atlas, syncthing]
|
||||
ansible.builtin.file:
|
||||
path: "{{ atlas_syncthing_default_dir }}"
|
||||
state: directory
|
||||
owner: "{{ atlas_admin_username }}"
|
||||
group: "{{ atlas_admin_group }}"
|
||||
mode: "0770"
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Render Atlas Syncthing systemd service
|
||||
tags: [atlas, syncthing]
|
||||
ansible.builtin.template:
|
||||
src: atlas-syncthing.service.j2
|
||||
dest: /etc/systemd/system/atlas-syncthing.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart Atlas Syncthing service
|
||||
when: atlas_manage_storage | bool
|
||||
|
||||
- name: Enable Atlas Syncthing service
|
||||
tags: [atlas, syncthing]
|
||||
ansible.builtin.systemd:
|
||||
name: atlas-syncthing
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
when: atlas_manage_storage | bool
|
||||
Reference in New Issue
Block a user