fix: encryption, partitioning, cis and virtualization hardening

This commit is contained in:
2026-05-30 18:05:14 +02:00
parent b1e938b7f0
commit 55b21eae5d
14 changed files with 46 additions and 25 deletions

View File

@@ -1,6 +1,6 @@
--- ---
# User-facing API: override via top-level `cis` dict in inventory. # User-facing API: override via top-level `cis` dict in inventory.
# Merged with these defaults in _normalize.yml cis_cfg. # Merged with these defaults in _normalize.yml -> cis_cfg.
cis_defaults: cis_defaults:
modules_blacklist: modules_blacklist:
- freevxfs - freevxfs

View File

@@ -1,7 +1,7 @@
--- ---
- name: Disable Kernel Modules - name: Disable Kernel Modules
vars: vars:
# Ubuntu uses squashfs for snap packages blacklisting it breaks snap entirely # Ubuntu uses squashfs for snap packages - blacklisting it breaks snap entirely
cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['squashfs'] }}" cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['squashfs'] }}"
cis_modules_all: "{{ cis_cfg.modules_blacklist + cis_modules_squashfs }}" cis_modules_all: "{{ cis_cfg.modules_blacklist + cis_modules_squashfs }}"
ansible.builtin.copy: ansible.builtin.copy:

View File

@@ -1,7 +1,5 @@
--- ---
# Network configuration dispatch maps OS name to the task file # Network configuration dispatch - maps OS name to the task file
# that writes network config. Default (NetworkManager) applies to # that writes network config. Default (NetworkManager) applies to
# all OSes not explicitly listed. # all OSes not explicitly listed.
configuration_network_task_map: configuration_network_task_map: {}
alpine: network_alpine.yml
void: network_void.yml

View File

@@ -16,9 +16,9 @@
RedHat: >- RedHat: >-
{{ chroot_command }} dnf install -y {{ chroot_command }} dnf install -y
clevis clevis-luks clevis-systemd tpm2-tools clevis clevis-luks clevis-systemd tpm2-tools
Suse: >- Archlinux: >-
{{ chroot_command }} zypper install -y {{ chroot_command }} pacman -S --noconfirm --needed
clevis clevis-systemd tpm2.0-tools clevis tpm2-tools
ansible.builtin.command: "{{ _clevis_install_cmd[os_family] }}" ansible.builtin.command: "{{ _clevis_install_cmd[os_family] }}"
register: _clevis_install_result register: _clevis_install_result
changed_when: _clevis_install_result.rc == 0 changed_when: _clevis_install_result.rc == 0

View File

@@ -3,7 +3,7 @@
# Sets _initramfs_generator and _tpm2_method facts. # Sets _initramfs_generator and _tpm2_method facts.
# #
# Generator detection: derived from the platform's initramfs_cmd # Generator detection: derived from the platform's initramfs_cmd
# (dracut dracut, mkinitcpio mkinitcpio, else initramfs-tools) # (dracut -> dracut, mkinitcpio -> mkinitcpio, else -> initramfs-tools)
# TPM2 method: systemd-cryptenroll when generator supports tpm2-device, # TPM2 method: systemd-cryptenroll when generator supports tpm2-device,
# clevis fallback otherwise. Non-native dracut installed automatically. # clevis fallback otherwise. Non-native dracut installed automatically.

View File

@@ -107,7 +107,7 @@
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0 when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
ansible.builtin.debug: ansible.builtin.debug:
msg: >- msg: >-
LUKS keyfile enrollment failed falling back to manual unlock at boot. LUKS keyfile enrollment failed - falling back to manual unlock at boot.
The system will prompt for the LUKS passphrase during startup. The system will prompt for the LUKS passphrase during startup.
- name: Fallback to manual LUKS unlock if keyfile enrollment failed - name: Fallback to manual LUKS unlock if keyfile enrollment failed

View File

@@ -1,7 +1,7 @@
--- ---
# TPM2 enrollment via systemd-cryptenroll. # TPM2 enrollment via systemd-cryptenroll.
# Works with dracut and mkinitcpio (sd-encrypt). The user-set passphrase # Works with dracut and mkinitcpio (sd-encrypt). The user-set passphrase
# remains as a backup unlock method no auto-generated keyfiles. # remains as a backup unlock method - no auto-generated keyfiles.
- name: Enroll TPM2 for LUKS - name: Enroll TPM2 for LUKS
block: block:
- name: Create temporary passphrase file for TPM2 enrollment - name: Create temporary passphrase file for TPM2 enrollment

View File

@@ -30,7 +30,6 @@
- name: Create zram config - name: Create zram config
when: when:
- (os != "debian" or (os_version | string) != "11") and os != "rhel" - (os != "debian" or (os_version | string) != "11") and os != "rhel"
- os not in ["alpine", "void"]
- system_cfg.features.swap.enabled | bool - system_cfg.features.swap.enabled | bool
ansible.builtin.copy: ansible.builtin.copy:
dest: /mnt/etc/systemd/zram-generator.conf dest: /mnt/etc/systemd/zram-generator.conf

View File

@@ -15,7 +15,8 @@
validate: /usr/sbin/visudo --check --file=%s validate: /usr/sbin/visudo --check --file=%s
- name: Deploy per-user sudoers rules - name: Deploy per-user sudoers rules
when: item.value.sudo is defined and (item.value.sudo | string | length > 0) # Jinja truthiness: bool true / a rule string => deploy; false / '' / unset => skip.
when: item.value.sudo | default(false)
vars: vars:
configuration_sudoers_rule: >- configuration_sudoers_rule: >-
{{ item.value.sudo if item.value.sudo is string else 'ALL=(ALL) NOPASSWD: ALL' }} {{ item.value.sudo if item.value.sudo is string else 'ALL=(ALL) NOPASSWD: ALL' }}

View File

@@ -1,7 +1,7 @@
--- ---
partitioning_btrfs_compress_opt: "{{ 'compress=zstd:15' if system_cfg.features.zstd.enabled | bool else '' }}" partitioning_btrfs_compress_opt: "{{ 'compress=zstd:15' if system_cfg.features.zstd.enabled | bool else '' }}"
# Partition separator: 'p' for NVMe/mmcblk (device path ends in digit), empty for SCSI/virtio. # Partition separator: 'p' for NVMe/mmcblk (device path ends in digit), empty for SCSI/virtio.
# Examples: /dev/sda /dev/sda1, /dev/nvme0n1 /dev/nvme0n1p1 # Examples: /dev/sda -> /dev/sda1, /dev/nvme0n1 -> /dev/nvme0n1p1
partitioning_part_sep: "{{ 'p' if (install_drive | default('') | regex_search('\\d$')) else '' }}" partitioning_part_sep: "{{ 'p' if (install_drive | default('') | regex_search('\\d$')) else '' }}"
partitioning_boot_partition_suffix: 1 partitioning_boot_partition_suffix: 1
partitioning_main_partition_suffix: 2 partitioning_main_partition_suffix: 2

View File

@@ -4,14 +4,14 @@
# Sizes are computed from disk_size_gb, memory_mb, and feature flags. # Sizes are computed from disk_size_gb, memory_mb, and feature flags.
# #
# Swap sizing: # Swap sizing:
# - RAM >= 16 GB swap = RAM/2 (in GB) # - RAM >= 16 GB -> swap = RAM/2 (in GB)
# - RAM < 16 GB swap = max(RAM_GB, 2) # - RAM < 16 GB -> swap = max(RAM_GB, 2)
# - Capped to: min(target, 4 + max(disk - overhead, 0)) # - Capped to: min(target, 4 + max(disk - overhead, 0))
# - Further capped to: max available after subtracting reserved + CIS + extent reserve + 4 GB buffer # - Further capped to: max available after subtracting reserved + CIS + extent reserve + 4 GB buffer
# #
# Root sizing: # Root sizing:
# - Full-disk mode (default): disk - reserved - swap - extent_reserve - (CIS volumes if enabled) # - Full-disk mode (default): disk - reserved - swap - extent_reserve - (CIS volumes if enabled)
# - Partial mode: tiered <4 GB available 4 GB, 4-12 GB all available, >12 GB 40% of disk # - Partial mode: tiered - <4 GB available -> 4 GB, 4-12 GB -> all available, >12 GB -> 40% of disk
# #
# CIS volumes (only when CIS enabled): # CIS volumes (only when CIS enabled):
# - /home: max(min(home_raw, home_max), home_min) where home_raw = (disk - overhead) * 10% # - /home: max(min(home_raw, home_max), home_min) where home_raw = (disk - overhead) * 10%

View File

@@ -1,9 +1,9 @@
--- ---
# Cloud-init support matrix: # Cloud-init support matrix:
# libvirt cloud-init ISO attached as CDROM (user-data + network-config) # libvirt - cloud-init ISO attached as CDROM (user-data + network-config)
# proxmox cloud-init via Proxmox API (cicustom, ciuser, cipassword, etc.) # proxmox - cloud-init via Proxmox API (cicustom, ciuser, cipassword, etc.)
# vmware no cloud-init; configuration is applied post-install via chroot # vmware - no cloud-init; configuration is applied post-install via chroot
# xen no cloud-init; configuration is applied post-install via chroot # xen - no cloud-init; configuration is applied post-install via chroot
virtualization_libvirt_image_dir: >- virtualization_libvirt_image_dir: >-
{{ {{
system_cfg.path system_cfg.path
@@ -17,8 +17,18 @@ virtualization_libvirt_cloudinit_path: >-
virtualization_xen_disk_path: /var/lib/xen/images virtualization_xen_disk_path: /var/lib/xen/images
virtualization_libvirt_machine_type: q35 virtualization_libvirt_machine_type: q35
virtualization_libvirt_ovmf_code: /usr/share/edk2/x64/OVMF_CODE.secboot.4m.fd # Secboot OVMF firmware candidates, ordered Arch, Debian/Ubuntu, Fedora/RHEL.
virtualization_libvirt_ovmf_vars: /usr/share/edk2/x64/OVMF_VARS.4m.fd # libvirt.yml resolves these to the first file present on the controller.
virtualization_libvirt_ovmf_code_candidates:
- /usr/share/edk2/x64/OVMF_CODE.secboot.4m.fd
- /usr/share/OVMF/OVMF_CODE_4M.secboot.fd
- /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd
- /usr/share/OVMF/OVMF_CODE.secboot.fd
virtualization_libvirt_ovmf_vars_candidates:
- /usr/share/edk2/x64/OVMF_VARS.4m.fd
- /usr/share/OVMF/OVMF_VARS_4M.fd
- /usr/share/edk2/ovmf/OVMF_VARS.fd
- /usr/share/OVMF/OVMF_VARS.fd
virtualization_tpm2_enabled: >- virtualization_tpm2_enabled: >-
{{ {{

View File

@@ -70,6 +70,19 @@
- /tmp/cloud-user-data-{{ hostname }}.yml - /tmp/cloud-user-data-{{ hostname }}.yml
- /tmp/cloud-network-config-{{ hostname }}.yml - /tmp/cloud-network-config-{{ hostname }}.yml
# Resolve OVMF firmware to the first candidate present on the controller
# unless the user pinned an explicit path. first_found needs the localhost
# delegation since the candidates live on the libvirt host, not the target.
- name: Resolve OVMF firmware paths
delegate_to: localhost
ansible.builtin.set_fact:
virtualization_libvirt_ovmf_code: >-
{{ virtualization_libvirt_ovmf_code if virtualization_libvirt_ovmf_code | default('', true) | length > 0
else lookup('ansible.builtin.first_found', virtualization_libvirt_ovmf_code_candidates) }}
virtualization_libvirt_ovmf_vars: >-
{{ virtualization_libvirt_ovmf_vars if virtualization_libvirt_ovmf_vars | default('', true) | length > 0
else lookup('ansible.builtin.first_found', virtualization_libvirt_ovmf_vars_candidates) }}
# uri defaults to qemu:///system (local libvirtd) # uri defaults to qemu:///system (local libvirtd)
- name: Create VM using libvirt - name: Create VM using libvirt
delegate_to: localhost delegate_to: localhost

View File

@@ -36,7 +36,7 @@
esxi_hostname: "{{ hypervisor_cfg.node if (hypervisor_cfg.node | default('') | length > 0) else omit }}" esxi_hostname: "{{ hypervisor_cfg.node if (hypervisor_cfg.node | default('') | length > 0) else omit }}"
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}" folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
name: "{{ hostname }}" name: "{{ hostname }}"
# Generic guest ID VMware auto-detects OS post-install # Generic guest ID - VMware auto-detects OS post-install
guest_id: otherLinux64Guest guest_id: otherLinux64Guest
annotation: | annotation: |
{{ note if note is defined else '' }} {{ note if note is defined else '' }}