diff --git a/main.yml b/main.yml index c64493a..baecbce 100644 --- a/main.yml +++ b/main.yml @@ -1,14 +1,4 @@ --- -# Bootstrap pipeline - role execution order: -# 1. global_defaults - normalize + validate system/hypervisor/disk input -# 2. system_check - pre-flight hardware/environment safety checks -# 3. virtualization - create VM on hypervisor (libvirt/proxmox/vmware/xen) -# 4. environment - detect live ISO, configure installer network, install tools, detect hardware -# 5. partitioning - partition disk, create FS, LUKS, LVM, mount everything -# 6. bootstrap - debootstrap/pacstrap/dnf install the target OS into /mnt -# 7. configuration - users, network, encryption, fstab, bootloader, services -# 8. cis - CIS hardening (optional, per system.features.cis.enabled) -# 9. cleanup - unmount, remove cloud-init artifacts, reboot/shutdown - name: Create and configure VMs hosts: "{{ bootstrap_target | default('all') }}" strategy: free # noqa: run-once[play] @@ -166,6 +156,15 @@ - system_cfg.os | lower in os_family_rhel ansible.builtin.include_tasks: "{{ playbook_dir }}/roles/configuration/tasks/satellite_register.yml" + - name: Activate the firewall on the rebooted host + when: + - post_reboot_can_connect | bool + - system_cfg.features.firewall.enabled | bool + - system_cfg.features.firewall.backend == 'ufw' + ansible.builtin.include_tasks: "{{ playbook_dir }}/roles/configuration/tasks/firewall.yml" + vars: + firewall_phase: postreboot + - name: Install post-reboot packages when: - post_reboot_can_connect | bool diff --git a/roles/configuration/tasks/firewall.yml b/roles/configuration/tasks/firewall.yml new file mode 100644 index 0000000..0f1c7dd --- /dev/null +++ b/roles/configuration/tasks/firewall.yml @@ -0,0 +1,34 @@ +--- +- name: Enable the firewall daemon in the install chroot + when: + - firewall_phase == 'install' + - _configuration_platform.init_system == 'systemd' + - system_cfg.features.firewall.enabled | bool + ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ system_cfg.features.firewall.backend }}" + register: _firewall_enable + changed_when: _firewall_enable.rc == 0 + failed_when: >- + _firewall_enable.rc != 0 + and 'No such file or directory' not in (_firewall_enable.stderr | default('')) + and 'does not exist' not in (_firewall_enable.stderr | default('')) + +# ufw's CLI needs a running kernel and is a no-op in the chroot (leaves ENABLED=no), +# so its activation and SSH rule are applied here, after reboot. +- name: Allow SSH through ufw before enabling + when: + - firewall_phase == 'postreboot' + - system_cfg.features.firewall.backend == 'ufw' + - system_cfg.features.firewall.enabled | bool + - system_cfg.features.ssh.enabled | bool + ansible.builtin.command: ufw allow 22/tcp + register: _ufw_allow + changed_when: "'added' in _ufw_allow.stdout or 'updated' in _ufw_allow.stdout" + +- name: Activate ufw on the booted target + when: + - firewall_phase == 'postreboot' + - system_cfg.features.firewall.backend == 'ufw' + - system_cfg.features.firewall.enabled | bool + ansible.builtin.command: ufw --force enable + register: _ufw_enable + changed_when: "'active' in _ufw_enable.stdout" diff --git a/roles/configuration/tasks/main.yml b/roles/configuration/tasks/main.yml index 422345a..92c1191 100644 --- a/roles/configuration/tasks/main.yml +++ b/roles/configuration/tasks/main.yml @@ -5,6 +5,8 @@ - name: Include configuration tasks when: configuration_task.when | default(true) ansible.builtin.include_tasks: "{{ configuration_task.file }}" + vars: + firewall_phase: install loop: - file: repositories.yml - file: banner.yml @@ -12,6 +14,7 @@ - file: locales.yml - file: ssh.yml - file: services.yml + - file: firewall.yml - file: grub.yml - file: encryption.yml when: "{{ system_cfg.luks.enabled | bool }}" diff --git a/roles/configuration/tasks/services.yml b/roles/configuration/tasks/services.yml index 79ac238..4b13ec8 100644 --- a/roles/configuration/tasks/services.yml +++ b/roles/configuration/tasks/services.yml @@ -41,8 +41,6 @@ configuration_systemd_services: >- {{ ['NetworkManager'] - + (['firewalld'] if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else []) - + (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else []) + ([_configuration_platform.ssh_service] if system_cfg.features.ssh.enabled | bool else []) + (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' else []) + (['bluetooth'] if system_cfg.features.desktop.enabled | bool else []) diff --git a/roles/global_defaults/defaults/main.yml b/roles/global_defaults/defaults/main.yml index 96ca246..3ec408a 100644 --- a/roles/global_defaults/defaults/main.yml +++ b/roles/global_defaults/defaults/main.yml @@ -132,7 +132,7 @@ system_defaults: enabled: true firewall: enabled: true - backend: "firewalld" # firewalld|ufw + backend: "" # '' -> family default (EL/arch=firewalld, debian/ubuntu=ufw); override: firewalld|ufw toolkit: "nftables" # nftables|iptables ssh: enabled: true diff --git a/roles/global_defaults/tasks/_normalize_system.yml b/roles/global_defaults/tasks/_normalize_system.yml index 4747223..c3442a3 100644 --- a/roles/global_defaults/tasks/_normalize_system.yml +++ b/roles/global_defaults/tasks/_normalize_system.yml @@ -165,7 +165,10 @@ enabled: "{{ system_raw.features.selinux.enabled | bool }}" firewall: enabled: "{{ system_raw.features.firewall.enabled | bool }}" - backend: "{{ system_raw.features.firewall.backend | string | lower }}" + backend: >- + {{ (system_raw.features.firewall.backend | default('') | string | lower | trim) + if (system_raw.features.firewall.backend | default('') | string | lower | trim | length > 0) + else ('ufw' if (system_raw.os | default('') | string | lower) in ['debian', 'ubuntu', 'ubuntu-lts'] else 'firewalld') }} toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}" ssh: enabled: "{{ system_raw.features.ssh.enabled | bool }}" diff --git a/roles/global_defaults/tasks/system.yml b/roles/global_defaults/tasks/system.yml index dd59e16..c80f31e 100644 --- a/roles/global_defaults/tasks/system.yml +++ b/roles/global_defaults/tasks/system.yml @@ -50,7 +50,7 @@ ansible.builtin.set_fact: system_cfg: "{{ system_defaults | combine(system | default({}), recursive=True) | combine(system_cfg, recursive=True) }}" -- name: Apply content-source family defaults for pre-computed system_cfg +- name: Apply family defaults (content source, firewall backend) for pre-computed system_cfg when: - system_cfg is defined - _bootstrap_needs_enrichment | default(false) | bool @@ -64,14 +64,21 @@ ansible.builtin.set_fact: system_cfg: >- {{ - system_cfg | combine({'content': { - 'source': system_cfg.content.source - if (system_cfg.content.source | default('') | string | trim | length > 0) - else ('dvd' if _os == 'rhel' else 'mirror'), - 'url': system_cfg.content.url - if (system_cfg.content.url | default('') | string | trim | length > 0) - else (_mirror_defaults[_os] | default('')), - }}, recursive=True) + system_cfg | combine({ + 'content': { + 'source': system_cfg.content.source + if (system_cfg.content.source | default('') | string | trim | length > 0) + else ('dvd' if _os == 'rhel' else 'mirror'), + 'url': system_cfg.content.url + if (system_cfg.content.url | default('') | string | trim | length > 0) + else (_mirror_defaults[_os] | default('')), + }, + 'features': {'firewall': {'backend': + system_cfg.features.firewall.backend + if (system_cfg.features.firewall.backend | default('') | string | trim | length > 0) + else ('ufw' if _os in ['debian', 'ubuntu', 'ubuntu-lts'] else 'firewalld') + }}, + }, recursive=True) }} - name: Populate primary network fields from first interface (pre-computed)