diff --git a/roles/global_defaults/tasks/system.yml b/roles/global_defaults/tasks/system.yml index 0081855..7e78cd8 100644 --- a/roles/global_defaults/tasks/system.yml +++ b/roles/global_defaults/tasks/system.yml @@ -4,11 +4,35 @@ system: "{{ system | default({}) }}" changed_when: false -- name: Validate system input type +- name: Validate system input types ansible.builtin.assert: that: - system is mapping - fail_msg: "system must be a dictionary" + - system.dns is not defined or system.dns is mapping + - system.user is not defined or system.user is mapping + - system.root is not defined or system.root is mapping + - system.luks is not defined or system.luks is mapping + - system.features is not defined or system.features is mapping + fail_msg: "system and its nested keys (dns, user, root, luks, features) must be dictionaries." + quiet: true + +- name: Validate system features input types + when: system.features is defined + loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}" + loop_control: + label: "system.features.{{ item }}" + ansible.builtin.assert: + that: + - (system.features[item] | default({})) is mapping + fail_msg: "system.features.{{ item }} must be a dictionary." + quiet: true + +- name: Validate system LUKS TPM2 input type + when: system.luks is defined and system.luks is mapping + ansible.builtin.assert: + that: + - system.luks.tpm2 is not defined or system.luks.tpm2 is mapping + fail_msg: "system.luks.tpm2 must be a dictionary." quiet: true - name: Build normalized system configuration @@ -16,92 +40,18 @@ system_raw: "{{ system_defaults | combine(system, recursive=True) }}" system_type: "{{ system_raw.type | string | lower }}" system_os_input: "{{ system_raw.os | default('') | string | lower }}" - system_filesystem_input: "{{ system_raw.filesystem | default('') | string | lower }}" system_name: >- {{ system_raw.name | string | trim if (system_raw.name | default('') | string | trim | length) > 0 else inventory_hostname }} - system_dns: >- - {{ - system_defaults.dns - | combine((system_raw.dns if system_raw.dns is mapping else {}), recursive=True) - }} - system_user: >- - {{ - system_defaults.user - | combine((system_raw.user if system_raw.user is mapping else {}), recursive=True) - }} - system_root: >- - {{ - system_defaults.root - | combine((system_raw.root if system_raw.root is mapping else {}), recursive=True) - }} - system_luks: >- - {{ - system_defaults.luks - | combine((system_raw.luks if system_raw.luks is mapping else {}), recursive=True) - }} - system_luks_tpm2: >- - {{ - system_defaults.luks.tpm2 - | combine((system_luks.tpm2 if system_luks.tpm2 is mapping else {}), recursive=True) - }} - system_features: >- - {{ - system_defaults.features - | combine((system_raw.features if system_raw.features is mapping else {}), recursive=True) - }} - system_feature_cis: >- - {{ - system_defaults.features.cis - | combine((system_features.cis if system_features.cis is mapping else {}), recursive=True) - }} - system_feature_selinux: >- - {{ - system_defaults.features.selinux - | combine((system_features.selinux if system_features.selinux is mapping else {}), recursive=True) - }} - system_feature_firewall: >- - {{ - system_defaults.features.firewall - | combine((system_features.firewall if system_features.firewall is mapping else {}), recursive=True) - }} - system_feature_ssh: >- - {{ - system_defaults.features.ssh - | combine((system_features.ssh if system_features.ssh is mapping else {}), recursive=True) - }} - system_feature_zstd: >- - {{ - system_defaults.features.zstd - | combine((system_features.zstd if system_features.zstd is mapping else {}), recursive=True) - }} - system_feature_swap: >- - {{ - system_defaults.features.swap - | combine((system_features.swap if system_features.swap is mapping else {}), recursive=True) - }} - system_feature_banner: >- - {{ - system_defaults.features.banner - | combine((system_features.banner if system_features.banner is mapping else {}), recursive=True) - }} - system_feature_chroot: >- - {{ - system_defaults.features.chroot - | combine((system_features.chroot if system_features.chroot is mapping else {}), recursive=True) - }} - system_dns_servers_input: "{{ system_dns.servers | default([]) }}" - system_dns_search_input: "{{ system_dns.search | default([]) }}" - system_packages_input: "{{ system_raw.packages | default([]) }}" ansible.builtin.set_fact: system_cfg: type: "{{ system_type }}" os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}" version: "{{ system_raw.version | default('') | string }}" - filesystem: "{{ system_filesystem_input }}" + filesystem: "{{ system_raw.filesystem | default('') | string | lower }}" name: "{{ system_name }}" id: "{{ system_raw.id | default('') | string }}" cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}" @@ -121,9 +71,9 @@ servers: >- {{ ( - system_dns_servers_input - if system_dns_servers_input is iterable and system_dns_servers_input is not string - else (system_dns_servers_input | string).split(',') + system_raw.dns.servers + if system_raw.dns.servers is iterable and system_raw.dns.servers is not string + else (system_raw.dns.servers | string).split(',') ) | map('trim') | reject('equalto', '') @@ -132,9 +82,9 @@ search: >- {{ ( - system_dns_search_input - if system_dns_search_input is iterable and system_dns_search_input is not string - else (system_dns_search_input | string).split(',') + system_raw.dns.search + if system_raw.dns.search is iterable and system_raw.dns.search is not string + else (system_raw.dns.search | string).split(',') ) | map('trim') | reject('equalto', '') @@ -144,9 +94,9 @@ packages: >- {{ ( - system_packages_input - if system_packages_input is iterable and system_packages_input is not string - else (system_packages_input | string).split(',') + system_raw.packages + if system_raw.packages is iterable and system_raw.packages is not string + else (system_raw.packages | string).split(',') ) | map('trim') | reject('equalto', '') @@ -154,50 +104,50 @@ }} disks: "{{ system_raw.disks | default([]) }}" user: - name: "{{ system_user.name | string }}" - password: "{{ system_user.password | string }}" - key: "{{ system_user.key | string }}" + name: "{{ system_raw.user.name | string }}" + password: "{{ system_raw.user.password | string }}" + key: "{{ system_raw.user.key | string }}" root: - password: "{{ system_root.password | string }}" + password: "{{ system_raw.root.password | string }}" luks: - enabled: "{{ system_luks.enabled | bool }}" - passphrase: "{{ system_luks.passphrase | string }}" - mapper: "{{ system_luks.mapper | string }}" - auto: "{{ system_luks.auto | bool }}" - method: "{{ system_luks.method | string | lower }}" + enabled: "{{ system_raw.luks.enabled | bool }}" + passphrase: "{{ system_raw.luks.passphrase | string }}" + mapper: "{{ system_raw.luks.mapper | string }}" + auto: "{{ system_raw.luks.auto | bool }}" + method: "{{ system_raw.luks.method | string | lower }}" tpm2: - device: "{{ system_luks_tpm2.device | string }}" - pcrs: "{{ system_luks_tpm2.pcrs | string }}" - keysize: "{{ system_luks.keysize | int }}" - options: "{{ system_luks.options | string }}" - type: "{{ system_luks.type | string }}" - cipher: "{{ system_luks.cipher | string }}" - hash: "{{ system_luks.hash | string }}" - iter: "{{ system_luks.iter | int }}" - bits: "{{ system_luks.bits | int }}" - pbkdf: "{{ system_luks.pbkdf | string }}" - urandom: "{{ system_luks.urandom | bool }}" - verify: "{{ system_luks.verify | bool }}" + device: "{{ system_raw.luks.tpm2.device | string }}" + pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}" + keysize: "{{ system_raw.luks.keysize | int }}" + options: "{{ system_raw.luks.options | string }}" + type: "{{ system_raw.luks.type | string }}" + cipher: "{{ system_raw.luks.cipher | string }}" + hash: "{{ system_raw.luks.hash | string }}" + iter: "{{ system_raw.luks.iter | int }}" + bits: "{{ system_raw.luks.bits | int }}" + pbkdf: "{{ system_raw.luks.pbkdf | string }}" + urandom: "{{ system_raw.luks.urandom | bool }}" + verify: "{{ system_raw.luks.verify | bool }}" features: cis: - enabled: "{{ system_feature_cis.enabled | bool }}" + enabled: "{{ system_raw.features.cis.enabled | bool }}" selinux: - enabled: "{{ system_feature_selinux.enabled | bool }}" + enabled: "{{ system_raw.features.selinux.enabled | bool }}" firewall: - enabled: "{{ system_feature_firewall.enabled | bool }}" - backend: "{{ system_feature_firewall.backend | string | lower }}" - toolkit: "{{ system_feature_firewall.toolkit | string | lower }}" + enabled: "{{ system_raw.features.firewall.enabled | bool }}" + backend: "{{ system_raw.features.firewall.backend | string | lower }}" + toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}" ssh: - enabled: "{{ system_feature_ssh.enabled | bool }}" + enabled: "{{ system_raw.features.ssh.enabled | bool }}" zstd: - enabled: "{{ system_feature_zstd.enabled | bool }}" + enabled: "{{ system_raw.features.zstd.enabled | bool }}" swap: - enabled: "{{ system_feature_swap.enabled | bool }}" + enabled: "{{ system_raw.features.swap.enabled | bool }}" banner: - motd: "{{ system_feature_banner.motd | bool }}" - sudo: "{{ system_feature_banner.sudo | bool }}" + motd: "{{ system_raw.features.banner.motd | bool }}" + sudo: "{{ system_raw.features.banner.sudo | bool }}" chroot: - tool: "{{ system_feature_chroot.tool | string }}" + tool: "{{ system_raw.features.chroot.tool | string }}" hostname: "{{ system_name }}" os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}" os_version: "{{ system_raw.version | default('') | string }}" @@ -209,53 +159,30 @@ system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz" system_disk_device_prefix: >- {{ - '/dev/vd' - if system_cfg.type == 'virtual' and hypervisor_type == 'libvirt' - else ( - '/dev/xvd' - if system_cfg.type == 'virtual' and hypervisor_type == 'xen' - else ( - '/dev/sd' - if system_cfg.type == 'virtual' and hypervisor_type in ['proxmox', 'vmware'] - else '' - ) - ) + {'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.get(hypervisor_type, '') + if system_cfg.type == 'virtual' + else '' }} block: - - name: Validate system disks type + - name: Validate system disks structure ansible.builtin.assert: that: - system_disks is sequence - fail_msg: "system.disks must be a list" + - (system_disks | length) <= 26 + fail_msg: "system.disks must be a list with at most 26 entries." quiet: true - - name: Validate system disk items + - name: Validate system disk entries ansible.builtin.assert: that: - item is mapping - fail_msg: "Each system disk entry must be a dictionary" - quiet: true - loop: "{{ system_disks }}" - loop_control: - label: "{{ item | to_json }}" - - - name: Validate system disk mount schema - ansible.builtin.assert: - that: - item.mount is not defined or item.mount is mapping - fail_msg: "system.disks[].mount must be a dictionary (e.g. mount: {path: /data})." + fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary." quiet: true loop: "{{ system_disks }}" loop_control: label: "{{ item | to_json }}" - - name: Validate system disk count - ansible.builtin.assert: - that: - - (system_disks | length) <= 26 - fail_msg: "system.disks supports at most 26 entries." - quiet: true - - name: Initialize normalized disk list ansible.builtin.set_fact: system_disks_cfg: []