Compare commits

..

184 Commits

Author SHA1 Message Date
ce79728744 feat(cleanup): enroll Secure Boot keys in VM NVRAM after OS installation 2026-04-02 17:03:07 +02:00
b31a5a2580 feat(virtualization): enable TPM2 emulation for Secure Boot VMs 2026-04-02 17:03:01 +02:00
2055863673 feat(configuration): auto-bind PCR 7 when Secure Boot and FDE are both enabled 2026-04-02 17:02:56 +02:00
ceb11852ec feat(configuration): add Secure Boot tasks for shim and sbctl 2026-04-02 17:02:56 +02:00
57417514e3 feat(configuration): override EFI loader to shim when Secure Boot enabled 2026-04-02 04:34:47 +02:00
0928588c1f feat(bootstrap): add Secure Boot conditional packages for Debian, Ubuntu, and Arch 2026-04-02 04:34:16 +02:00
6d622f2db4 feat(global_defaults): add secure_boot feature toggle with normalization 2026-04-02 04:33:07 +02:00
b11d65a6f3 docs(bootstrap): document desktop, initramfs, and FDE features with SSH keepalive config 2026-04-01 15:07:58 +02:00
3623fc292c feat(configuration): generic FDE with systemd-cryptenroll, clevis fallback, and configurable initramfs 2026-04-01 15:07:58 +02:00
dfca7ec94b fix(configuration): RedHat EFI grub wrapper with btrfs subvol prefix and boot order 2026-04-01 15:07:58 +02:00
e8be84bf49 fix(partitioning): set btrfs default subvolume and restrict @pkg to Arch 2026-04-01 15:07:58 +02:00
322cc0b1ce fix(bootstrap): resolve interface-only network, sshd penalties, dnf scriptlets, and EFI cleanup 2026-04-01 15:07:58 +02:00
4b38754f8b feat(bootstrap): add desktop environment support with configurable DE, DM, and display target 2026-04-01 15:07:58 +02:00
a6bc7ffe04 fix(configuration): use /boot/grub2/grub.cfg for RedHat EFI grub config 2026-03-25 16:03:39 +01:00
c529e71ebc feat(packages): add needrestart to Debian and Ubuntu package lists 2026-03-20 18:06:14 +01:00
cb46de2b6d feat(bootstrap): add full package upgrade step for Debian and Ubuntu 2026-03-20 18:05:04 +01:00
9169117b25 fix(vim): use vimscript comment syntax for blockinfile markers in vimrc 2026-03-20 18:00:12 +01:00
6c94c519fb fix(sudo): use explicit string check instead of bool conditional for sudo field 2026-03-20 17:31:49 +01:00
efd96a42b8 fix(connection): set ansible_port explicitly at every connection transition 2026-03-20 17:31:49 +01:00
68661c3cca fix(vmware): use primary ansible_* vars for vmware_tools connection plugin precedence 2026-03-20 17:31:49 +01:00
1db20c7ac0 fix(vmware): use empty password for vmware_tools during live ISO bootstrap 2026-03-20 17:31:49 +01:00
7b155b427b fix(users): update cloud-init template and input validation for dict users 2026-03-20 17:31:49 +01:00
ca8721e98f refactor(prompts): remove vars_prompt, require users defined in inventory 2026-03-20 17:31:49 +01:00
cdb2559d8f fix(prompts): add default values to vars_prompt to skip in non-interactive mode 2026-03-20 17:31:49 +01:00
443f6623df refactor(users): change system.users from list to dict keyed by username 2026-03-20 17:31:49 +01:00
6cf418fe00 fix(configuration): make root password, user keys, and sudo all optional 2026-03-20 17:31:49 +01:00
47ec5fe621 fix(cloud-init): handle missing keys and make sudo conditional 2026-03-20 17:31:49 +01:00
240f945cce fix(cleanup): remove ansible_become override that blocks swapoff/umount 2026-03-20 17:31:49 +01:00
663a04556f feat(global_defaults): add system.features.aur schema for validation passthrough 2026-03-20 17:31:49 +01:00
6febd1acf1 refactor(virtualization): extract shared Xen disk definitions 2026-03-12 12:27:18 +01:00
008187860c refactor: remove unnecessary changed_when from set_fact tasks 2026-03-12 12:25:45 +01:00
cd1be6b5e1 refactor(partitioning): remove redundant blockdev --rereadpt calls 2026-03-12 12:25:15 +01:00
15be6149fd refactor(partitioning): remove unused register variables 2026-03-12 12:24:59 +01:00
ca29ad200d chore: suppress args[module] false positives from variable-based module_defaults 2026-03-12 12:12:27 +01:00
8079099cee fix(cleanup): add no_log to Proxmox VM restart task 2026-03-12 12:12:27 +01:00
9e79185b07 fix(virtualization): add missing changed_when to Xen VM stop task 2026-03-12 12:12:27 +01:00
b88bf2860f fix(configuration): replace fail+ignore_errors with debug for TPM2 fallback warning 2026-03-12 12:12:27 +01:00
81d26eb715 refactor(configuration): split encryption.yml into crypttab, dracut, grub, and initramfs subtasks 2026-03-12 09:40:40 +01:00
41691fcf0a feat(bootstrap): add rescue block with VM cleanup on failure 2026-03-12 07:43:51 +01:00
601f8a1ef9 feat(environment): VMware network config, DNS resolvers, and SSH switchover 2026-03-12 07:43:46 +01:00
49d362c860 fix(global_defaults): populate flat network fields from interfaces in pre-computed path 2026-03-12 07:43:39 +01:00
f9656cfbf5 feat(vmware): add VMware hypervisor support (node field, connection vars, validation) 2026-03-12 07:43:34 +01:00
c99daa3dbc fix(bootstrap): exclude tldr from Ubuntu rolling extra packages 2026-02-22 20:40:46 +01:00
d35976635c fix(global_defaults): use archive.ubuntu.com instead of mirror redirector 2026-02-22 16:26:35 +01:00
b13f89a250 fix(global_defaults): apply mirror default in pre-computed system_cfg path 2026-02-22 14:20:12 +01:00
b3b634f915 feat(configuration): add Debian/Ubuntu repository and apt configuration 2026-02-22 10:47:47 +01:00
b8dd400aea feat(bootstrap): use configurable mirror and write proper sources.list 2026-02-22 10:47:43 +01:00
f38e0a628f feat(global_defaults): add system.mirror to schema and normalization 2026-02-22 10:47:40 +01:00
3242d5a895 chore(bootstrap): update ubuntu non-lts codename to questing (25.10) 2026-02-22 03:08:54 +01:00
7e812dd74c fix(global_defaults): add missing ssh.enabled validation assertion 2026-02-22 03:08:31 +01:00
785eaab9a7 fix(global_defaults): correct fedora version upper bound to 43 2026-02-22 03:08:23 +01:00
81ff2b2b87 feat(global_defaults): add root.shell to system schema and normalization 2026-02-22 03:07:30 +01:00
2265e346b0 refactor(cleanup): remove duplicated libvirt path vars, reuse virtualization defaults 2026-02-22 03:07:04 +01:00
d9ae4ee809 refactor(bootstrap,configuration): rename validation-only _normalize.yml files 2026-02-22 03:06:34 +01:00
931d65df04 fix(partitioning): add | bool to all system_cfg.features.cis.enabled checks 2026-02-22 03:06:13 +01:00
59670e876a fix(partitioning): add partition separator for NVMe/mmcblk device paths 2026-02-22 02:39:36 +01:00
f7070343b9 refactor(configuration): centralize DNS list variables in network dispatch 2026-02-22 02:39:32 +01:00
1cce81366c refactor(configuration): extract shared BLS update task to reduce duplication 2026-02-22 02:39:28 +01:00
f6cb7bf78d fix(bootstrap): add missing --best flag to RHEL dnf commands 2026-02-22 02:39:23 +01:00
2c80c01b1a refactor(global_defaults): consolidate hypervisor auth into shared credential dicts 2026-02-22 02:35:04 +01:00
1b58a20c45 refactor(bootstrap,configuration,environment): add defaults/main.yml and extract hardcoded values 2026-02-22 02:32:36 +01:00
6b1686e652 refactor(bootstrap,configuration): add per-role _normalize.yml for platform resolution 2026-02-22 02:27:46 +01:00
a460584c5d refactor(configuration): add platform_config dict and replace is_rhel/is_debian with os_family lookups 2026-02-22 02:26:54 +01:00
9c0f00f1ec feat(global_defaults): add os_family_map and os_family fact for platform config lookups 2026-02-22 02:23:05 +01:00
6ebceb8ee2 fix(virtualization): add vTPM2 result validation before VMware power-on 2026-02-22 02:22:37 +01:00
5e72394bf8 feat(global_defaults): add semantic validations for IP, hostname, LUKS method, and interface prefix 2026-02-22 02:22:05 +01:00
5abdc76c86 refactor(global_defaults): extract physical_default_os to configurable default 2026-02-22 02:21:34 +01:00
bcfd5d5a89 fix(global_defaults): normalize system.type 'vm' to 'virtual' for main project compatibility 2026-02-22 02:21:22 +01:00
c91e049378 docs(bootstrap): add section comments, role boundary docs, and pipeline overview 2026-02-22 01:59:12 +01:00
b9e8aa283b refactor(global_defaults): data-driven hypervisor validation and shared constants 2026-02-22 01:59:09 +01:00
734ed822d6 refactor(extras): convert custom.sh from template to static copy 2026-02-22 01:59:04 +01:00
3f2f4055f0 fix(cleanup,config): xen tmp cleanup, tpm2 fallback warning, add code comments 2026-02-22 01:59:01 +01:00
a2b206127f fix(partitioning,network): swapon idempotency, DNS search domains, tune2fs changed_when 2026-02-22 01:58:56 +01:00
6985235e70 fix(encryption): add no_log to LUKS configuration block 2026-02-22 01:58:52 +01:00
25b1eeec45 fix(network): bind NM connections to detected interface names for multi-NIC 2026-02-21 16:51:15 +01:00
3f65585e5c fix(bootstrap): make dhcp-client conditional for EL < 10 (removed in EL 10) 2026-02-21 13:43:41 +01:00
74f1365a06 fix(bootstrap): remove --asexplicit from pacstrap to preserve dependency metadata 2026-02-21 13:26:59 +01:00
9d19f628aa fix(bootstrap): add kernel package to rocky and almalinux extra packages 2026-02-21 12:06:09 +01:00
ced0da7bd1 fix(bootstrap): detect kernel package name for dnf family reinstall step 2026-02-21 11:46:57 +01:00
cf49d30916 fix(bootstrap): ensure chroot DNS resolution before installing extra packages 2026-02-21 11:30:28 +01:00
46b5223da5 fix(environment): align repo IDs in rocky and almalinux templates with bootstrap config 2026-02-21 11:18:34 +01:00
494f0b58b2 fix(configuration): omit interface-name when not explicitly provided to avoid predictable naming mismatch 2026-02-21 08:29:24 +01:00
d84b867cef refactor(configuration): rename _uid to configuration_uid for role prefix convention 2026-02-21 05:14:33 +01:00
39c786305f fix(configuration): handle boolean sudo values in sudoers deployment 2026-02-21 05:14:29 +01:00
72e2263f5c fix(configuration): use full path for chpasswd in chroot 2026-02-21 05:03:36 +01:00
ac532578b8 fix(global_defaults): enrich pre-computed system_cfg with bootstrap defaults 2026-02-21 04:24:23 +01:00
34f35bb5ac chore(lint): suppress var-naming for user-facing API dicts 2026-02-21 02:58:10 +01:00
6de88a911a fix(configuration): remove unnecessary changed_when on set_fact tasks 2026-02-21 02:56:58 +01:00
fa78edf2e2 refactor(cis): align normalization with main project activation gate pattern 2026-02-21 02:56:39 +01:00
a1c8b5e2dd fix(global_defaults): remove dead /swap and make pacman cache arch-only in reserved mounts 2026-02-21 02:56:20 +01:00
19da8c0e68 fix(global_defaults): set filesystem default to ext4 instead of empty string 2026-02-21 02:56:08 +01:00
ff1a4df960 refactor(bootstrap): restructure package lists to self-contained per-OS dicts with base/extra/conditional 2026-02-21 02:39:06 +01:00
f0c0b54e7f refactor(environment): split main.yml into focused sub-task files 2026-02-21 02:39:05 +01:00
a868c6bb47 refactor(global_defaults): add idempotency guards to normalization tasks 2026-02-21 02:39:03 +01:00
dd0d70f4fd fix(global_defaults): default interface name to eth0 instead of empty string 2026-02-21 02:38:59 +01:00
c08e1fe4e0 docs(cis): add comment explaining squashfs/snap Ubuntu exclusion 2026-02-21 02:38:58 +01:00
c3ccce97ae chore(bootstrap): pin collection versions in requirements.yml 2026-02-21 02:38:57 +01:00
d9ca905b73 fix(bootstrap): move Jinja to end of task name and rename registers to bootstrap_dnf_* 2026-02-21 02:38:27 +01:00
6085336f96 docs: update README with cis dict API, execution pipeline, and cleanup defaults 2026-02-21 01:30:36 +01:00
2831479e77 fix(validation): align btrfs disk size check with new 2GB swap minimum 2026-02-21 01:28:32 +01:00
608cbf3196 refactor(bootstrap): unify rocky, almalinux, and fedora into shared _dnf_family.yml 2026-02-21 01:27:33 +01:00
382e48176d refactor(cis): extract hardcoded values to cis_defaults and add _normalize.yml 2026-02-21 01:26:31 +01:00
0372e35ea3 refactor(cleanup): prioritize source-match over target-match in libvirt media removal 2026-02-21 01:22:44 +01:00
6e055de457 docs(cis): explain Fedora exclusion from crypto-policy configuration 2026-02-21 01:22:41 +01:00
f7e1bd4d49 fix(bootstrap): replace brittle sed with ansible.builtin.replace for ubuntu universe repo 2026-02-21 01:22:37 +01:00
58c9b264f9 refactor(virtualization): simplify cloud-user-data sudo to unconditional NOPASSWD 2026-02-21 01:22:34 +01:00
11a4794ac2 fix(bootstrap): remove duplicate lrzsz and gate dbus-daemon on version in almalinux 2026-02-21 01:20:34 +01:00
d3c8c6c975 fix(virtualization): fix cloud-user-data sudo logic to respect sudo: false 2026-02-21 01:20:31 +01:00
ba8ab340f7 fix(partitioning): lower swap minimum from 4GB to 2GB for small VMs 2026-02-21 01:19:23 +01:00
474ebbb513 fix(partitioning): add wipefs before mkfs on extra disk partitions 2026-02-21 01:19:19 +01:00
5df369b151 fix(cis): strengthen kernel module blacklist and sysctl hardening 2026-02-21 01:18:52 +01:00
08c518bd5b refactor(partitioning): split monolithic main.yml into focused task files 2026-02-21 00:39:03 +01:00
e200774c8e fix(validation): add CIDR prefix range check and Ubuntu version validation 2026-02-21 00:38:57 +01:00
6e0c289226 refactor(cis): remove redundant AllowUsers/AllowGroups/DenyUsers/DenyGroups from sshd 2026-02-21 00:38:52 +01:00
3be725633e fix(cis): skip squashfs blacklist on Ubuntu to preserve snap functionality 2026-02-21 00:38:47 +01:00
6c02eab159 fix(partitioning): correct changed_when on btrfs quota and qgroup commands 2026-02-21 00:38:43 +01:00
99c579bec0 fix(cis): add regexp to all lineinfile entries in security_lines.yml for idempotency 2026-02-21 00:38:36 +01:00
be5d2e9f94 fix: add no_log to credential-handling pre_tasks and post_tasks in main.yml 2026-02-21 00:38:32 +01:00
e334c82b26 fix(virtualization): add no_log and secure temp file handling to libvirt cloud-init 2026-02-21 00:38:28 +01:00
5008d97bc8 refactor(cleanup): add configurable verify_boot, boot_timeout, and remove_on_failure defaults 2026-02-20 23:02:24 +01:00
06b8058c1d refactor: move playbook-root templates into their respective roles 2026-02-20 23:01:38 +01:00
aec82e4241 refactor: add loop_control labels to dict-based loops across all roles 2026-02-20 23:00:53 +01:00
f36d9b7ca3 refactor(partitioning): move btrfs home quota to configurable default 2026-02-20 22:55:37 +01:00
0950db7011 fix(environment): detect RHEL ISO device dynamically instead of hardcoded /dev/sr paths 2026-02-20 22:54:42 +01:00
4f3e39398f refactor(global_defaults): split system.yml into composable normalization stages 2026-02-20 22:54:05 +01:00
e3c21168fd refactor(global_defaults): extract OS family lists to single source of truth 2026-02-20 22:52:55 +01:00
643fec1cc6 fix(partitioning): add failed_when to all blkid commands to catch empty UUIDs 2026-02-20 22:52:18 +01:00
bbbdcfc9b6 fix(partitioning): add default fallbacks for is_rhel, os, os_version in defaults 2026-02-20 22:51:37 +01:00
9347140808 fix(virtualization): use hostname variable instead of hardcoded archiso in cloud-user-data 2026-02-20 22:51:32 +01:00
b8af8b3fdd fix(virtualization): avoid no-handler lint finding in xen VM created tracking 2026-02-20 22:29:03 +01:00
94ea082e63 fix(partitioning): fix line length violation in home size calculation 2026-02-20 22:28:58 +01:00
3361ee3de8 fix(configuration): add pipefail to root password shell pipe 2026-02-20 22:28:54 +01:00
06f6203674 fix(bootstrap): use release map for ubuntu version detection 2026-02-20 22:27:46 +01:00
a385c27963 chore: add .yamllint matching main project conventions 2026-02-20 22:27:31 +01:00
04340d1a04 fix(configuration): use chpasswd for root password and separate shell setting 2026-02-20 22:27:17 +01:00
4c8021fc2e fix(configuration): add explicit LUKS auto-decrypt fallback state tracking and logging 2026-02-20 22:26:47 +01:00
6a6a43ae96 refactor(partitioning): externalize hardcoded LVM and disk sizing constants to defaults 2026-02-20 22:26:23 +01:00
2a7340af37 fix(virtualization): add xen VM existence check and improve changed_when 2026-02-20 22:25:10 +01:00
e0687269d4 fix(cis): add pipefail to sshd version detection and define binary defaults 2026-02-20 22:24:14 +01:00
1634af552e feat(cleanup): gate RHEL ISO disk and fstab handling on rhel_repo.source 2026-02-20 21:51:20 +01:00
0077f05654 feat(global_defaults): add system.features.rhel_repo option (iso|satellite|none) 2026-02-20 21:51:16 +01:00
33d46274bd fix(encryption): add warning before silent TPM2-to-keyfile fallback 2026-02-20 21:51:12 +01:00
ed6b604302 fix(partitioning): correct wipefs changed_when to report actual disk modification 2026-02-20 21:51:09 +01:00
fc2ddfea8a fix(validation): require password for primary user in system.users[0] 2026-02-20 21:51:06 +01:00
efdbc0c04e fix(system_check): move no_log from block to individual API tasks 2026-02-20 21:51:02 +01:00
5769bd456d fix(cis): make mlkem768x25519-sha256 KexAlgorithm conditional on OpenSSH 9.9+ 2026-02-20 21:50:58 +01:00
b7ffcfecd4 fix(cis): use is_rhel for journald config path instead of fedora-only check 2026-02-20 21:50:55 +01:00
f18881328c refactor(configuration): add conditional dispatch to task includes 2026-02-20 21:16:52 +01:00
05aeb0676b refactor(cis): move OS-specific binary resolution to vars/main.yml 2026-02-20 21:16:48 +01:00
5b5c94cb8b refactor(configuration): split network.yml into per-init-system dispatch files 2026-02-20 21:16:45 +01:00
4a89911a54 refactor(bootstrap): restructure conditional package lists to list concatenation 2026-02-20 21:16:40 +01:00
b61fecfc88 refactor(configuration): convert services.yml to list-based loop 2026-02-20 21:16:37 +01:00
b690bddaec refactor(virt): adopt module_defaults for hypervisor credentials 2026-02-20 21:16:33 +01:00
8e92f40b2a refactor(cleanup): restructure dispatch to use hypervisor_type include 2026-02-20 21:16:28 +01:00
c8c9a9c9f5 refactor(partitioning): extract VG name to defaults variable 2026-02-20 21:16:25 +01:00
7a666239b6 fix(configuration): remove trailing blank line from extras.yml 2026-02-20 20:20:33 +01:00
7181679d7c docs(environment): document RPM GPG policy relaxation 2026-02-20 20:19:57 +01:00
32f22e94bd chore(bootstrap): align ansible.cfg with main project settings 2026-02-20 20:19:46 +01:00
15122b924d feat(system_check): add safety check for physical installs 2026-02-20 20:19:37 +01:00
be51bfe101 fix(cleanup): fix vmware CD-ROM omit fragility and add cross-role defaults 2026-02-20 20:19:25 +01:00
83610447e7 fix(virtualization): add XML safety attributes and switch xen to virtio 2026-02-20 20:18:49 +01:00
1fc64b9e5d fix(cis): remove deprecated sshd options and update hardening values 2026-02-20 20:17:52 +01:00
bbf83f7050 fix(configuration): disambiguate BLS task names and clean up misc noise 2026-02-20 20:17:05 +01:00
2a044dcc1d refactor(configuration): relocate login banner and fix blockinfile markers 2026-02-20 20:16:19 +01:00
c57323ff69 fix(configuration): use short hostname and allow per-user shell 2026-02-20 20:15:49 +01:00
b8c3b49419 fix(partitioning): mount extra disks by UUID instead of device path 2026-02-20 20:15:25 +01:00
80e7e2cdd6 fix(partitioning): correct LVM swap sizing and harden UUID fallbacks 2026-02-20 20:15:00 +01:00
ab9502ea49 fix(configuration): add trailing semicolons to NM keyfile DNS fields 2026-02-20 20:14:06 +01:00
b0c7a39749 fix(bootstrap): add missing packages and remove duplicates 2026-02-20 20:13:53 +01:00
64b1296fe2 fix(bootstrap): add devpts mount and use ephemeral state for RHEL DVD 2026-02-20 20:12:59 +01:00
bbe3ad9a07 fix(bootstrap): unify resolv.conf to live environment DNS symlink 2026-02-20 20:12:42 +01:00
e2241bb223 fix(global_defaults): add no_log to hypervisor tasks and expand validation 2026-02-20 20:11:37 +01:00
6236978e45 fix: configurable OVMF/machine type, routes syntax, package lists, interface names 2026-02-20 18:47:12 +01:00
ebc5db1c59 fix(cleanup): keep RHEL ISO ide1 attached as local repo 2026-02-20 18:41:40 +01:00
4d0bf3891a fix: deep analysis audit — no_log, resolv.conf, service conflicts, lint 2026-02-20 18:34:59 +01:00
14ff79cfd0 fix(bootstrap): RHEL 9 bootstrap from Arch ISO compatibility 2026-02-20 16:58:59 +01:00
8070cc4196 refactor: make bootstrap host target configurable 2026-02-20 16:58:59 +01:00
6e53af5e92 fix(ubuntu): add initramfs-tools to debootstrap base packages 2026-02-20 16:58:59 +01:00
6d84a21130 fix(bootstrap): use explicit keyring for debootstrap and copy resolv.conf 2026-02-20 16:58:59 +01:00
b3132329cb fix(cloud-init): handle boolean sudo values in user-data template 2026-02-20 16:58:59 +01:00
a85308185f fix: re-gather facts after reboot to detect target OS package manager 2026-02-20 16:58:59 +01:00
d1d579c658 fix: resolve Jinja2 .keys ambiguity, fastfetch availability, and python interpreter 2026-02-20 16:58:58 +01:00
e08532ffd0 fix(partitioning): create separate /boot for LVM-based filesystems 2026-02-20 04:50:32 +01:00
2a543fffc3 fix(bootloader): run efibootmgr on host for universal chroot compatibility 2026-02-20 03:36:20 +01:00
33 changed files with 928 additions and 140 deletions

View File

@@ -257,7 +257,15 @@ Users must be defined in inventory. The dict format enables additive merging acr
| Key | Type | Default | Description |
| -------- | ------------- | ------- | ---------------------------------------------- |
| `device` | string | `auto` | TPM2 device selector |
| `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`) |
| `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`); empty = no PCR binding |
**TPM2 auto-unlock:** Uses `systemd-cryptenroll` on all distros. The user-set passphrase
remains as a backup unlock method. TPM2 enrollment runs in the chroot during bootstrap;
if it fails (e.g. no TPM2 hardware), the system boots with passphrase-only unlock and
TPM2 can be enrolled post-deployment via `systemd-cryptenroll --tpm2-device=auto <device>`.
On Debian/Ubuntu, TPM2 auto-unlock requires dracut (initramfs-tools does not support `tpm2-device`).
The bootstrap auto-switches to dracut when `method: tpm2` is set. Override via `features.initramfs.generator`.
#### `system.features`
@@ -274,6 +282,26 @@ Users must be defined in inventory. The dict format enables additive merging acr
| `banner.motd` | bool | `false` | MOTD banner |
| `banner.sudo` | bool | `true` | Sudo banner |
| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, or `systemd-nspawn` |
| `initramfs.generator` | string | auto-detected | Override initramfs generator (see below) |
| `desktop.*` | dict | see below | Desktop environment settings (see [4.2.5](#425-systemfeaturesdesktop)) |
**Initramfs generator auto-detection:** RedHat → dracut, Arch → mkinitcpio, Debian/Ubuntu → initramfs-tools.
Override with `dracut`, `mkinitcpio`, or `initramfs-tools`. When LUKS TPM2 auto-unlock is enabled and the
native generator does not support `tpm2-device`, the generator is automatically upgraded to dracut.
On distros with older dracut (no `tpm2-tss` module), clevis is used as a fallback for TPM2 binding.
#### 4.2.5 `system.features.desktop`
| Key | Type | Default | Description |
| ----------------- | ------ | -------------- | ----------------------------------------- |
| `enabled` | bool | `false` | Install desktop environment |
| `environment` | string | -- | `gnome`, `kde`, `xfce`, `sway`, `hyprland`, `cinnamon`, `mate`, `lxqt`, `budgie` |
| `display_manager` | string | auto-detected | Override DM: `gdm`, `sddm`, `lightdm`, `ly`, `greetd` |
When `enabled: true`, the bootstrap installs the desktop environment packages, enables the display manager
and bluetooth services, and sets the systemd default target to `graphical.target`.
Display manager auto-detection: gnome→gdm, kde→sddm, xfce→lightdm, sway→greetd, hyprland→ly.
### 4.3 `hypervisor` Dictionary

View File

@@ -3,3 +3,6 @@ hash_behaviour = merge
interpreter_python = auto_silent
deprecation_warnings = False
host_key_checking = False
[ssh_connection]
ssh_args = -C -o ControlMaster=auto -o ControlPersist=600s -o ServerAliveInterval=30 -o ServerAliveCountMax=10

View File

@@ -0,0 +1,48 @@
---
- name: Load desktop package definitions
ansible.builtin.include_vars:
file: desktop.yml
- name: Resolve desktop packages
vars:
_de: "{{ system_cfg.features.desktop.environment }}"
_family_pkgs: "{{ bootstrap_desktop_packages[os_family] | default({}) }}"
_de_config: "{{ _family_pkgs[_de] | default({}) }}"
ansible.builtin.set_fact:
_desktop_groups: "{{ _de_config.groups | default([]) }}"
_desktop_packages: "{{ _de_config.packages | default([]) }}"
- name: Validate desktop environment is supported
ansible.builtin.assert:
that:
- (_desktop_groups | length > 0) or (_desktop_packages | length > 0)
fail_msg: >-
Desktop environment '{{ system_cfg.features.desktop.environment }}'
is not defined for os_family '{{ os_family }}'.
Supported: {{ (bootstrap_desktop_packages[os_family] | default({})).keys() | join(', ') }}
quiet: true
- name: Install desktop package groups
when: _desktop_groups | length > 0
ansible.builtin.command: >-
{{ chroot_command }} dnf --releasever={{ os_version }}
--setopt=install_weak_deps=False group install -y {{ _desktop_groups | join(' ') }}
register: _desktop_group_result
changed_when: _desktop_group_result.rc == 0
- name: Install desktop packages
when: _desktop_packages | length > 0
vars:
_install_commands:
RedHat: >-
{{ chroot_command }} dnf --releasever={{ os_version }}
--setopt=install_weak_deps=False install -y {{ _desktop_packages | join(' ') }}
Debian: >-
{{ chroot_command }} apt install -y {{ _desktop_packages | join(' ') }}
Archlinux: >-
pacstrap /mnt {{ _desktop_packages | join(' ') }}
Suse: >-
{{ chroot_command }} zypper install -y {{ _desktop_packages | join(' ') }}
ansible.builtin.command: "{{ _install_commands[os_family] }}"
register: _desktop_pkg_result
changed_when: _desktop_pkg_result.rc == 0

View File

@@ -18,6 +18,9 @@
groupinstall -y {{ _dnf_groups }}
register: bootstrap_dnf_base_result
changed_when: bootstrap_dnf_base_result.rc == 0
failed_when:
- bootstrap_dnf_base_result.rc != 0
- "'scriptlet' not in bootstrap_dnf_base_result.stderr"
- name: Ensure chroot has DNS resolution
ansible.builtin.file:

View File

@@ -55,6 +55,11 @@
register: bootstrap_debian_update_result
changed_when: bootstrap_debian_update_result.rc == 0
- name: Upgrade all packages to latest versions
ansible.builtin.command: "{{ chroot_command }} apt full-upgrade -y"
register: bootstrap_debian_upgrade_result
changed_when: "'0 upgraded' not in bootstrap_debian_upgrade_result.stdout"
- name: Install extra packages
when: bootstrap_debian_extra_args | trim | length > 0
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_debian_extra_args }}"

View File

@@ -34,6 +34,10 @@
bootstrap_var_key: "{{ 'bootstrap_' + (os | replace('-lts', '') | replace('-', '_')) }}"
ansible.builtin.include_tasks: "{{ bootstrap_os_task_map[os] }}"
- name: Install desktop environment packages
when: system_cfg.features.desktop.enabled | bool
ansible.builtin.include_tasks: _desktop.yml
- name: Ensure chroot uses live environment DNS
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf

View File

@@ -54,6 +54,11 @@
register: bootstrap_ubuntu_update_result
changed_when: bootstrap_ubuntu_update_result.rc == 0
- name: Upgrade all packages to latest versions
ansible.builtin.command: "{{ chroot_command }} apt full-upgrade -y"
register: bootstrap_ubuntu_upgrade_result
changed_when: "'0 upgraded' not in bootstrap_ubuntu_upgrade_result.stdout"
- name: Install extra packages
when: bootstrap_ubuntu_extra_args | trim | length > 0
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_ubuntu_extra_args }}"

View File

@@ -0,0 +1,149 @@
---
# Per-family desktop environment package definitions.
# Keyed by os_family -> environment -> groups (dnf groupinstall) / packages.
# Kept intentionally minimal: base DE + essential tools, no full suites.
bootstrap_desktop_packages:
RedHat:
gnome:
groups:
- workstation-product-environment
packages: []
kde:
groups: []
packages:
- plasma-desktop
- plasma-nm
- plasma-pa
- plasma-systemmonitor
- sddm
- konsole
- dolphin
- kate
- kscreen
- kde-gtk-config
- xdg-user-dirs
- xdg-desktop-portal-kde
- bluez
- pipewire
- wireplumber
xfce:
groups:
- xfce-desktop-environment
packages:
- lightdm
Debian:
gnome:
groups: []
packages:
- gnome-core
- gdm3
- gnome-tweaks
- xdg-user-dirs
kde:
groups: []
packages:
- plasma-desktop
- plasma-nm
- plasma-pa
- sddm
- konsole
- dolphin
- kate
- kscreen
- xdg-user-dirs
- xdg-desktop-portal-kde
- bluez
- pipewire
- wireplumber
xfce:
groups: []
packages:
- xfce4
- xfce4-goodies
- lightdm
- xdg-user-dirs
Archlinux:
gnome:
groups: []
packages:
- gnome
- gdm
- xdg-user-dirs
kde:
groups: []
packages:
- plasma-desktop
- plasma-nm
- plasma-pa
- sddm
- konsole
- dolphin
- kate
- kscreen
- kde-gtk-config
- xdg-user-dirs
- xdg-desktop-portal-kde
- bluez
- pipewire
- wireplumber
xfce:
groups: []
packages:
- xfce4
- xfce4-goodies
- lightdm
- xdg-user-dirs
sway:
groups: []
packages:
- sway
- waybar
- foot
- wofi
- greetd
- xdg-user-dirs
- xdg-desktop-portal-wlr
- bluez
- pipewire
- wireplumber
hyprland:
groups: []
packages:
- hyprland
- kitty
- wofi
- waybar
- ly
- xdg-user-dirs
- xdg-desktop-portal-hyprland
- polkit-kde-agent
- qt5-wayland
- qt6-wayland
- bluez
- pipewire
- wireplumber
Suse:
gnome:
groups: []
packages:
- patterns-gnome-gnome_basic
- gdm
- xdg-user-dirs
kde:
groups: []
packages:
- patterns-kde-kde_plasma
- sddm
- xdg-user-dirs
# Display manager auto-detection from desktop environment.
bootstrap_desktop_dm_map:
gnome: gdm
kde: sddm
xfce: lightdm
sway: greetd
hyprland: ly@tty2
cinnamon: lightdm
mate: lightdm
lxqt: sddm
budgie: gdm

View File

@@ -201,6 +201,7 @@ bootstrap_debian:
- lrzsz
- mtr
- ncdu
- needrestart
- net-tools
- network-manager
- python-is-python3
@@ -221,6 +222,7 @@ bootstrap_debian:
+ (['software-properties-common'] if (os_version | string) not in ['13', 'unstable'] else [])
+ (['systemd-zram-generator'] if (os_version | string) not in ['10', '11'] else [])
+ (['tldr'] if (os_version | string) not in ['13', 'unstable'] else [])
+ (['shim-signed'] if system_cfg.features.secure_boot.enabled | bool else [])
+ bootstrap_common_conditional
}}
@@ -262,6 +264,7 @@ bootstrap_ubuntu:
- mtr
- ncdu
- ncurses-term
- needrestart
- net-tools
- network-manager
- python-is-python3
@@ -283,6 +286,7 @@ bootstrap_ubuntu:
conditional: >-
{{
(['tldr'] if (os_version | default('') | string | length) > 0 else [])
+ (['shim-signed'] if system_cfg.features.secure_boot.enabled | bool else [])
+ bootstrap_common_conditional
}}
@@ -321,6 +325,7 @@ bootstrap_archlinux:
{{
(['openssh'] if system_cfg.features.ssh.enabled | bool else [])
+ (['iptables-nft'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else [])
+ (['sbctl'] if system_cfg.features.secure_boot.enabled | bool else [])
+ (bootstrap_common_conditional | reject('equalto', 'nftables') | list)
}}

View File

@@ -72,6 +72,12 @@
| trim
}}
- name: Ensure boot device is set to hard disk in VM XML
when: "'<boot ' not in cleanup_libvirt_domain_xml_clean"
ansible.builtin.set_fact:
cleanup_libvirt_domain_xml_clean: >-
{{ cleanup_libvirt_domain_xml_clean | regex_replace('(</type>)', '\1\n <boot dev="hd"/>') }}
- name: Update VM definition without installer media
community.libvirt.virt:
command: define
@@ -88,6 +94,35 @@
state: destroyed
failed_when: false
- name: Enroll Secure Boot keys in VM NVRAM
when:
- system_cfg.features.secure_boot.enabled | default(false) | bool
- os != 'archlinux'
block:
- name: Find VM NVRAM file path
ansible.builtin.shell:
cmd: >-
set -o pipefail &&
virsh -c {{ libvirt_uri | default('qemu:///system') }} dumpxml {{ hostname }}
| grep -oP '<nvram[^>]*>\K[^<]+'
executable: /bin/bash
register: _sb_nvram_path
changed_when: false
failed_when: false
- name: Enroll Secure Boot keys via virt-fw-vars
when: _sb_nvram_path.stdout | default('') | length > 0
ansible.builtin.command:
argv:
- virt-fw-vars
- --inplace
- "{{ _sb_nvram_path.stdout | trim }}"
- --enroll-redhat
- --secure-boot
register: _sb_enroll_result
changed_when: _sb_enroll_result.rc == 0
failed_when: false
- name: Start the VM
community.libvirt.virt:
name: "{{ hostname }}"

View File

@@ -14,3 +14,12 @@
- name: Set platform configuration
ansible.builtin.set_fact:
_configuration_platform: "{{ configuration_platform_config[os_family] }}"
- name: Override EFI loader to shim for Secure Boot
when:
- system_cfg.features.secure_boot.enabled | bool
- _configuration_platform.efi_loader != 'shimx64.efi'
- os != 'archlinux'
ansible.builtin.set_fact:
_configuration_platform: >-
{{ _configuration_platform | combine({'efi_loader': 'shimx64.efi'}) }}

View File

@@ -34,6 +34,16 @@
register: configuration_efi_entry_result
changed_when: configuration_efi_entry_result.rc == 0
- name: Set installed OS as first EFI boot entry
ansible.builtin.shell:
cmd: >-
set -o pipefail &&
efibootmgr | grep -i '{{ _efi_vendor }}' | grep -oP 'Boot\K[0-9A-F]+' | head -1
| xargs -I{} efibootmgr -o {}
executable: /bin/bash
register: _efi_bootorder_result
changed_when: _efi_bootorder_result.rc == 0
- name: Ensure lvm2 for non btrfs filesystems
when: os == "archlinux" and system_cfg.filesystem != "btrfs"
ansible.builtin.lineinfile:
@@ -48,16 +58,62 @@
register: configuration_initramfs_result
changed_when: configuration_initramfs_result.rc == 0
- name: Generate grub config
vars:
configuration_grub_cfg_cmd: >-
{{
'/usr/sbin/' + _configuration_platform.grub_mkconfig_prefix + ' -o '
+ partitioning_efi_mountpoint
+ '/EFI/' + _efi_vendor + '/grub.cfg'
if os_family == 'RedHat'
else '/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg'
}}
ansible.builtin.command: "{{ chroot_command }} {{ configuration_grub_cfg_cmd }}"
- name: Generate grub config (RedHat)
when: os_family == 'RedHat'
ansible.builtin.command: >-
{{ chroot_command }} /usr/sbin/{{ _configuration_platform.grub_mkconfig_prefix }}
-o /boot/grub2/grub.cfg
register: configuration_grub_result
changed_when: configuration_grub_result.rc == 0
- name: Fix btrfs BLS boot variable in grub config
when:
- os_family == 'RedHat'
- system_cfg.filesystem == 'btrfs'
ansible.builtin.replace:
path: /mnt/boot/grub2/grub.cfg
regexp: 'search --no-floppy --fs-uuid --set=boot \S+'
replace: 'set boot=$root'
- name: Create EFI grub.cfg wrapper for RedHat
when: os_family == 'RedHat'
vars:
_grub2_path: >-
{{
'/grub2'
if (partitioning_separate_boot | bool)
else ('/@/boot/grub2' if system_cfg.filesystem == 'btrfs' else '/boot/grub2')
}}
ansible.builtin.shell:
cmd: |
set -o pipefail
uuid=$(grep -m1 'search.*--set=root' /mnt/boot/grub2/grub.cfg | grep -oP '[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}')
cat > /mnt{{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/grub.cfg <<GRUBEOF
search --no-floppy --fs-uuid --set=dev $uuid
set prefix=(\$dev){{ _grub2_path }}
export \$prefix
configfile \$prefix/grub.cfg
GRUBEOF
executable: /bin/bash
register: _grub_wrapper_result
changed_when: _grub_wrapper_result.rc == 0
- name: Generate grub config (non-RedHat)
when: os_family != 'RedHat'
ansible.builtin.command: "{{ chroot_command }} /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg"
register: configuration_grub_result
changed_when: configuration_grub_result.rc == 0
- name: Rebuild GRUB as standalone EFI for Secure Boot
when:
- system_cfg.features.secure_boot.enabled | default(false) | bool
- os == 'archlinux'
ansible.builtin.command: >-
{{ chroot_command }} grub-mkstandalone
-d /usr/lib/grub/x86_64-efi
-O x86_64-efi
--disable-shim-lock
-o {{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/grubx64.efi
boot/grub/grub.cfg=/boot/grub/grub.cfg
register: _grub_standalone_result
changed_when: _grub_standalone_result.rc == 0

View File

@@ -8,7 +8,7 @@
block:
- name: Set LUKS configuration facts
vars:
luks_tpm2_pcrs: >-
_raw_pcrs: >-
{{
(
system_cfg.luks.tpm2.pcrs
@@ -20,6 +20,17 @@
| regex_replace('\\s+', '')
| regex_replace('^\\+|\\+$', '')
}}
_sb_pcr7_safe: >-
{{
system_cfg.features.secure_boot.enabled | bool
and system_cfg.type | default('virtual') != 'virtual'
}}
luks_tpm2_pcrs: >-
{{
_raw_pcrs
if _raw_pcrs | length > 0
else ('7' if (_sb_pcr7_safe | bool) else '')
}}
ansible.builtin.set_fact:
configuration_luks_mapper_name: "{{ system_cfg.luks.mapper }}"
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
@@ -36,6 +47,12 @@
configuration_luks_tpm2_device: "{{ system_cfg.luks.tpm2.device }}"
configuration_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}"
configuration_luks_keyfile_path: "/etc/cryptsetup-keys.d/{{ system_cfg.luks.mapper }}.key"
configuration_luks_tpm2_token_lib: >-
{{
'/usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-tpm2.so'
if os_family == 'Debian'
else '/usr/lib64/cryptsetup/libcryptsetup-token-systemd-tpm2.so'
}}
- name: Validate LUKS UUID is available
ansible.builtin.assert:
@@ -51,8 +68,13 @@
fail_msg: system.luks.passphrase must be set for LUKS auto-decrypt.
no_log: true
- name: Enroll TPM2 for LUKS
when: configuration_luks_auto_method == 'tpm2'
- name: Detect TPM2 unlock method
ansible.builtin.include_tasks: encryption/initramfs_detect.yml
- name: Enroll TPM2 via systemd-cryptenroll
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('systemd-cryptenroll') == 'systemd-cryptenroll'
ansible.builtin.include_tasks: encryption/tpm2.yml
- name: Configure LUKS keyfile auto-decrypt
@@ -78,7 +100,7 @@
}}
luks_tpm2_option_list: >-
{{
(configuration_luks_auto_method == 'tpm2')
(configuration_luks_auto_method == 'tpm2' and (_tpm2_method | default('systemd-cryptenroll')) == 'systemd-cryptenroll')
| ternary(
['tpm2-device=' + configuration_luks_tpm2_device]
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
@@ -122,16 +144,16 @@
path: /mnt{{ configuration_luks_keyfile_path }}
state: absent
- name: Configure initramfs for LUKS
ansible.builtin.include_tasks: encryption/initramfs.yml
- name: Configure crypttab
ansible.builtin.include_tasks: encryption/crypttab.yml
- name: Configure initramfs
ansible.builtin.include_tasks: encryption/initramfs.yml
- name: Configure dracut
when: os_family == 'RedHat'
- name: Configure dracut for LUKS
when: _initramfs_generator | default('') == 'dracut'
ansible.builtin.include_tasks: encryption/dracut.yml
- name: Configure GRUB for LUKS
when: not os_family == 'RedHat'
when: _initramfs_generator | default('') != 'dracut' or os_family != 'RedHat'
ansible.builtin.include_tasks: encryption/grub.yml

View File

@@ -9,48 +9,58 @@
ansible.builtin.copy:
dest: /mnt/etc/dracut.conf.d/crypt.conf
content: |
add_dracutmodules+=" crypt "
{% if configuration_luks_keyfile_in_use %}
add_dracutmodules+=" crypt systemd "
{% if configuration_luks_keyfile_in_use | default(false) %}
install_items+=" {{ configuration_luks_keyfile_path }} "
{% endif %}
{% if configuration_luks_auto_method == 'tpm2' %}
install_items+=" {{ configuration_luks_tpm2_token_lib | default('') }} "
{% endif %}
mode: "0644"
- name: Read kernel cmdline defaults
# --- Kernel cmdline: write rd.luks.* args for dracut ---
- name: Ensure kernel cmdline directory exists
ansible.builtin.file:
path: /mnt/etc/kernel
state: directory
mode: "0755"
- name: Read existing kernel cmdline
ansible.builtin.slurp:
src: /mnt/etc/kernel/cmdline
register: configuration_kernel_cmdline_slurp
register: _kernel_cmdline_slurp
failed_when: false
- name: Build kernel cmdline with LUKS args
vars:
kernel_cmdline_current: >-
{{ configuration_kernel_cmdline_slurp.content | b64decode | trim }}
kernel_cmdline_list: >-
_cmdline_current: >-
{{ (_kernel_cmdline_slurp.content | default('') | b64decode | default('')) | trim }}
_cmdline_list: >-
{{ _cmdline_current.split() if _cmdline_current | length > 0 else [] }}
_cmdline_filtered: >-
{{
kernel_cmdline_current.split()
if kernel_cmdline_current | length > 0 else []
}}
kernel_cmdline_filtered: >-
{{
kernel_cmdline_list
_cmdline_list
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
| list
}}
kernel_cmdline_new: >-
_cmdline_new: >-
{{
(kernel_cmdline_filtered + configuration_luks_kernel_args.split())
(_cmdline_filtered + configuration_luks_kernel_args.split())
| unique
| join(' ')
}}
ansible.builtin.set_fact:
configuration_kernel_cmdline_new: "{{ kernel_cmdline_new }}"
_dracut_kernel_cmdline: "{{ _cmdline_new }}"
- name: Write kernel cmdline with LUKS args
ansible.builtin.copy:
dest: /mnt/etc/kernel/cmdline
mode: "0644"
content: "{{ configuration_kernel_cmdline_new }}\n"
content: "{{ _dracut_kernel_cmdline }}\n"
# --- BLS entries: RedHat-specific ---
- name: Update BLS entries with LUKS kernel cmdline
when: os_family == 'RedHat'
vars:
_bls_cmdline: "{{ configuration_kernel_cmdline_new }}"
_bls_cmdline: "{{ _dracut_kernel_cmdline }}"
ansible.builtin.include_tasks: ../_bls_update.yml

View File

@@ -1,8 +1,94 @@
---
- name: Ensure keyfile pattern for initramfs-tools
# Initramfs configuration for LUKS auto-unlock.
# Runs AFTER Build LUKS parameters (so configuration_luks_keyfile_in_use is set).
# _initramfs_generator and _tpm2_method are set by initramfs_detect.yml.
# --- clevis: install and bind TPM2 ---
- name: Install clevis in target system
when:
- os_family == 'Debian'
- configuration_luks_keyfile_in_use
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
ansible.builtin.command: >-
{{ chroot_command }} apt install -y clevis clevis-luks clevis-tpm2 clevis-initramfs tpm2-tools
register: _clevis_install_result
changed_when: _clevis_install_result.rc == 0
- name: Install clevis on installer for LUKS binding
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
community.general.pacman:
name:
- clevis
- tpm2-tools
state: present
retries: 3
delay: 5
- name: Create clevis passphrase file
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
ansible.builtin.copy:
dest: /mnt/root/.luks-enroll-key
content: "{{ configuration_luks_passphrase }}"
mode: "0600"
no_log: true
- name: Ensure TPM device accessible for clevis
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
ansible.builtin.shell: >-
ls /mnt/dev/tpmrm0 2>/dev/null
|| (ls /dev/tpmrm0 && cp -a /dev/tpmrm0 /mnt/dev/tpmrm0)
changed_when: false
failed_when: false
- name: Bind LUKS to TPM2 via clevis
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
vars:
_clevis_config: >-
{{
'{"pcr_ids":"' + configuration_luks_tpm2_pcrs + '"}'
if configuration_luks_tpm2_pcrs | length > 0
else '{}'
}}
ansible.builtin.command: >-
clevis luks bind -f -k /mnt/root/.luks-enroll-key
-d {{ configuration_luks_device }} tpm2 '{{ _clevis_config }}'
register: _clevis_bind_result
changed_when: _clevis_bind_result.rc == 0
failed_when: false
# Initramfs regeneration is handled by the bootloader task which runs after
# encryption configuration. Clevis hooks are included automatically by
# update-initramfs when clevis-initramfs is installed.
- name: Remove clevis passphrase file
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
ansible.builtin.file:
path: /mnt/root/.luks-enroll-key
state: absent
- name: Report clevis binding result
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method | default('') == 'clevis'
ansible.builtin.debug:
msg: >-
{{ 'Clevis TPM2 binding succeeded' if (_clevis_bind_result.rc | default(1)) == 0
else 'Clevis TPM2 binding failed: ' + (_clevis_bind_result.stderr | default('unknown')) + '. System will require passphrase at boot.' }}
# --- initramfs-tools: keyfile support (non-TPM2) ---
- name: Configure initramfs-tools keyfile pattern
when:
- _initramfs_generator | default('') == 'initramfs-tools'
- configuration_luks_keyfile_in_use | default(false) | bool
ansible.builtin.lineinfile:
path: /mnt/etc/cryptsetup-initramfs/conf-hook
regexp: "^KEYFILE_PATTERN="
@@ -10,8 +96,9 @@
create: true
mode: "0644"
# --- mkinitcpio: systemd + sd-encrypt hooks ---
- name: Configure mkinitcpio hooks for LUKS
when: os == 'archlinux'
when: _initramfs_generator | default('') == 'mkinitcpio'
ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf
regexp: "^HOOKS="
@@ -20,13 +107,13 @@
block sd-encrypt{{ ' lvm2' if system_cfg.filesystem != 'btrfs' else '' }} filesystems fsck)
- name: Read mkinitcpio configuration
when: os == 'archlinux'
when: _initramfs_generator | default('') == 'mkinitcpio'
ansible.builtin.slurp:
src: /mnt/etc/mkinitcpio.conf
register: configuration_mkinitcpio_slurp
- name: Build mkinitcpio FILES list
when: os == 'archlinux'
when: _initramfs_generator | default('') == 'mkinitcpio'
vars:
mkinitcpio_files_list: >-
{{
@@ -42,7 +129,7 @@
{{
(
(mkinitcpio_files_list + [configuration_luks_keyfile_path])
if configuration_luks_keyfile_in_use
if (configuration_luks_keyfile_in_use | default(false))
else (
mkinitcpio_files_list
| reject('equalto', configuration_luks_keyfile_path)
@@ -55,7 +142,7 @@
configuration_mkinitcpio_files_list_new: "{{ mkinitcpio_files_list_new }}"
- name: Configure mkinitcpio FILES list
when: os == 'archlinux'
when: _initramfs_generator | default('') == 'mkinitcpio'
ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf
regexp: "^FILES="

View File

@@ -0,0 +1,98 @@
---
# Resolve initramfs generator and TPM2 unlock method.
# Sets _initramfs_generator and _tpm2_method facts.
#
# Generator detection: derived from the platform's initramfs_cmd
# (dracut → dracut, mkinitcpio → mkinitcpio, else → initramfs-tools)
# TPM2 method: systemd-cryptenroll when generator supports tpm2-device,
# clevis fallback otherwise. Non-native dracut installed automatically.
- name: Resolve initramfs generator
vars:
_user_generator: "{{ system_cfg.features.initramfs.generator | default('') }}"
_native_generator: >-
{{
'dracut' if _configuration_platform.initramfs_cmd is search('dracut')
else ('mkinitcpio' if _configuration_platform.initramfs_cmd is search('mkinitcpio')
else 'initramfs-tools')
}}
ansible.builtin.set_fact:
_initramfs_generator: >-
{{ _user_generator if _user_generator | length > 0 else _native_generator }}
_initramfs_native_generator: "{{ _native_generator }}"
# --- Install non-native dracut if overridden or needed ---
- name: Install dracut in chroot when not native
when:
- _initramfs_generator == 'dracut'
- _initramfs_native_generator != 'dracut'
ansible.builtin.shell: >-
{{ chroot_command }} sh -c '
command -v apt >/dev/null 2>&1 && apt install -y dracut ||
command -v pacman >/dev/null 2>&1 && pacman -S --noconfirm dracut ||
command -v dnf >/dev/null 2>&1 && dnf install -y dracut
'
register: _dracut_install_result
changed_when: _dracut_install_result.rc == 0
failed_when: false
- name: Override initramfs command to dracut
when:
- _initramfs_generator == 'dracut'
- _initramfs_native_generator != 'dracut'
vars:
# Generate dracut initramfs with output name matching what GRUB expects:
# mkinitcpio native: /boot/initramfs-linux.img (Arch convention)
# initramfs-tools native: /boot/initrd.img-<kver> (Debian convention)
_dracut_cmd: >-
{{
'bash -c "for kver in /lib/modules/*/; do kver=$(basename $kver); dracut --force /boot/initramfs-linux.img $kver; done"'
if _initramfs_native_generator == 'mkinitcpio'
else 'bash -c "for kver in /lib/modules/*/; do kver=$(basename $kver); dracut --force /boot/initrd.img-$kver $kver; done"'
}}
ansible.builtin.set_fact:
_configuration_platform: >-
{{ _configuration_platform | combine({'initramfs_cmd': _dracut_cmd}) }}
# --- TPM2 method detection ---
- name: Probe dracut for TPM2 module support
when:
- configuration_luks_auto_method == 'tpm2'
- _initramfs_generator != 'mkinitcpio'
ansible.builtin.command: "{{ chroot_command }} dracut --list-modules"
register: _dracut_modules_check
changed_when: false
failed_when: false
- name: Resolve TPM2 unlock method
when: configuration_luks_auto_method == 'tpm2'
vars:
# mkinitcpio sd-encrypt supports tpm2-device natively
# dracut with tpm2-tss module supports tpm2-device natively
# everything else needs clevis
_supports_tpm2_native: >-
{{
_initramfs_generator == 'mkinitcpio'
or ('tpm2-tss' in (_dracut_modules_check.stdout | default('')))
}}
ansible.builtin.set_fact:
_tpm2_method: "{{ 'systemd-cryptenroll' if _supports_tpm2_native | bool else 'clevis' }}"
# --- Auto-upgrade to dracut when tpm2-tss available but generator isn't dracut ---
- name: Switch to dracut for TPM2 support
when:
- configuration_luks_auto_method == 'tpm2'
- _tpm2_method == 'systemd-cryptenroll'
- _initramfs_generator not in ['dracut', 'mkinitcpio']
vars:
_dracut_cmd: >-
bash -c "for kver in /lib/modules/*/; do kver=$(basename $kver); dracut --force /boot/initrd.img-$kver $kver; done"
ansible.builtin.set_fact:
_initramfs_generator: dracut
_configuration_platform: >-
{{ _configuration_platform | combine({'initramfs_cmd': _dracut_cmd}) }}
- name: Report TPM2 configuration
when: configuration_luks_auto_method == 'tpm2'
ansible.builtin.debug:
msg: "TPM2 unlock: {{ _tpm2_method | default('none') }} | initramfs: {{ _initramfs_generator }}"

View File

@@ -1,26 +1,35 @@
---
# TPM2 enrollment via systemd-cryptenroll.
# Works with dracut and mkinitcpio (sd-encrypt). The user-set passphrase
# remains as a backup unlock method — no auto-generated keyfiles.
- name: Enroll TPM2 for LUKS
block:
# Tempfile in chroot /tmp — accessible by both chroot and host commands
- name: Create temporary passphrase file for TPM2 enrollment
ansible.builtin.tempfile:
path: /mnt/tmp
path: /mnt/root
prefix: luks-passphrase-
state: file
register: configuration_luks_tpm2_passphrase_tempfile
register: _tpm2_passphrase_tempfile
- name: Write passphrase into temporary file for TPM2 enrollment
- name: Write passphrase into temporary file
ansible.builtin.copy:
dest: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
dest: "{{ _tpm2_passphrase_tempfile.path }}"
content: "{{ configuration_luks_passphrase }}"
owner: root
group: root
mode: "0600"
no_log: true
- name: Enroll TPM2 token
- name: Ensure TPM device is accessible in chroot
ansible.builtin.shell: >-
ls /mnt/dev/tpmrm0 2>/dev/null
|| (ls /dev/tpmrm0 && cp -a /dev/tpmrm0 /mnt/dev/tpmrm0)
changed_when: false
failed_when: false
- name: Enroll TPM2 token via systemd-cryptenroll
vars:
configuration_luks_enroll_args: >-
_enroll_args: >-
{{
[
'/usr/bin/systemd-cryptenroll',
@@ -28,69 +37,28 @@
'--tpm2-with-pin=false',
'--wipe-slot=tpm2',
'--unlock-key-file=' + (
configuration_luks_tpm2_passphrase_tempfile.path
| regex_replace('^/mnt', '')
_tpm2_passphrase_tempfile.path | regex_replace('^/mnt', '')
)
]
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
if configuration_luks_tpm2_pcrs | length > 0 else [])
+ [configuration_luks_device]
}}
configuration_luks_enroll_chroot_cmd: >-
{{ chroot_command }} {{ configuration_luks_enroll_args | join(' ') }}
ansible.builtin.command: "{{ configuration_luks_enroll_chroot_cmd }}"
register: configuration_luks_tpm2_enroll_chroot
changed_when: configuration_luks_tpm2_enroll_chroot.rc == 0
failed_when: false
ansible.builtin.command: "{{ chroot_command }} {{ _enroll_args | join(' ') }}"
register: _tpm2_enroll_result
changed_when: _tpm2_enroll_result.rc == 0
- name: Retry TPM2 enrollment in installer environment
when:
- (configuration_luks_tpm2_enroll_chroot.rc | default(1)) != 0
vars:
configuration_luks_enroll_args: >-
{{
[
'/usr/bin/systemd-cryptenroll',
'--tpm2-device=' + configuration_luks_tpm2_device,
'--tpm2-with-pin=false',
'--wipe-slot=tpm2',
'--unlock-key-file=' + configuration_luks_tpm2_passphrase_tempfile.path
]
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
if configuration_luks_tpm2_pcrs | length > 0 else [])
+ [configuration_luks_device]
}}
ansible.builtin.command:
argv: "{{ configuration_luks_enroll_args }}"
register: configuration_luks_tpm2_enroll_host
changed_when: configuration_luks_tpm2_enroll_host.rc == 0
failed_when: false
- name: Validate TPM2 enrollment succeeded
ansible.builtin.assert:
that:
- >-
(configuration_luks_tpm2_enroll_chroot.rc | default(1)) == 0
or (configuration_luks_tpm2_enroll_host.rc | default(1)) == 0
fail_msg: >-
TPM2 enrollment failed.
chroot rc={{ configuration_luks_tpm2_enroll_chroot.rc | default('n/a') }},
host rc={{ configuration_luks_tpm2_enroll_host.rc | default('n/a') }},
chroot stderr={{ configuration_luks_tpm2_enroll_chroot.stderr | default('') }},
host stderr={{ configuration_luks_tpm2_enroll_host.stderr | default('') }}
rescue:
- name: Warn about TPM2 enrollment failure
- name: TPM2 enrollment failed
ansible.builtin.debug:
msg: >-
WARNING: TPM2 enrollment failed — falling back to keyfile auto-decrypt.
The system will use a keyfile instead of TPM2 for automatic LUKS unlock.
TPM2 enrollment failed: {{ _tpm2_enroll_result.stderr | default('unknown') }}.
The system will require the passphrase for LUKS unlock on boot.
TPM2 can be enrolled post-deployment via: systemd-cryptenroll --tpm2-device=auto {{ configuration_luks_device }}
- name: Fallback to keyfile auto-decrypt
ansible.builtin.set_fact:
configuration_luks_auto_method: keyfile
always:
- name: Remove TPM2 enrollment passphrase file
when: configuration_luks_tpm2_passphrase_tempfile.path is defined
- name: Remove temporary passphrase file
when: _tpm2_passphrase_tempfile.path is defined
ansible.builtin.file:
path: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
path: "{{ _tpm2_passphrase_tempfile.path }}"
state: absent

View File

@@ -9,7 +9,7 @@
set smartindent
set mouse=a
insertafter: EOF
marker: "# {mark} CUSTOM VIM CONFIG"
marker: "\" {mark} CUSTOM VIM CONFIG"
failed_when: false
# Tuned for VM workloads: low swappiness, aggressive writeback, large page-cluster

View File

@@ -17,6 +17,8 @@
- file: encryption.yml
when: "{{ system_cfg.luks.enabled | bool }}"
- file: bootloader.yml
- file: secure_boot.yml
when: "{{ system_cfg.features.secure_boot.enabled | bool }}"
- file: extras.yml
- file: network.yml
- file: users.yml

View File

@@ -0,0 +1,8 @@
---
- name: Configure shim-based Secure Boot
when: os != 'archlinux'
ansible.builtin.include_tasks: secure_boot/shim.yml
- name: Configure sbctl Secure Boot
when: os == 'archlinux'
ansible.builtin.include_tasks: secure_boot/sbctl.yml

View File

@@ -0,0 +1,115 @@
---
- name: Configure sbctl Secure Boot
block:
- name: Create Secure Boot signing keys
ansible.builtin.command: "{{ chroot_command }} sbctl create-keys"
register: _sbctl_create_keys
changed_when: _sbctl_create_keys.rc == 0
failed_when:
- _sbctl_create_keys.rc != 0
- "'already exists' not in (_sbctl_create_keys.stderr | default(''))"
- name: Enroll Secure Boot keys in firmware
ansible.builtin.command: "{{ chroot_command }} sbctl enroll-keys --microsoft"
register: _sbctl_enroll
changed_when: _sbctl_enroll.rc == 0
failed_when: false
- name: Install first-boot enrollment service if chroot enrollment failed
when: _sbctl_enroll.rc | default(1) != 0
block:
- name: Create first-boot sbctl enrollment service
ansible.builtin.copy:
dest: /mnt/etc/systemd/system/sbctl-enroll.service
mode: "0644"
content: |
[Unit]
Description=Enroll Secure Boot keys via sbctl
ConditionPathExists=!/var/lib/sbctl/.enrolled
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/bin/sbctl enroll-keys --microsoft
ExecStartPost=/usr/bin/touch /var/lib/sbctl/.enrolled
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
- name: Enable first-boot enrollment service
ansible.builtin.command: "{{ chroot_command }} systemctl enable sbctl-enroll.service"
register: _sbctl_service_enable
changed_when: _sbctl_service_enable.rc == 0
- name: Find kernel images to sign
ansible.builtin.find:
paths: /mnt/boot
patterns: "vmlinuz-*"
file_type: file
register: _sbctl_kernel_images
- name: Sign kernel images
ansible.builtin.command: >-
{{ chroot_command }} sbctl sign -s {{ item.path | regex_replace('^/mnt', '') }}
loop: "{{ _sbctl_kernel_images.files }}"
loop_control:
label: "{{ item.path | basename }}"
register: _sbctl_sign_kernel
changed_when: _sbctl_sign_kernel.rc == 0
failed_when: false
- name: Sign GRUB EFI binary
vars:
_grub_efi_path: "{{ partitioning_efi_mountpoint }}/EFI/archlinux/grubx64.efi"
ansible.builtin.command: >-
{{ chroot_command }} sbctl sign -s {{ _grub_efi_path }}
register: _sbctl_sign_grub
changed_when: _sbctl_sign_grub.rc == 0
failed_when: false
- name: Ensure pacman hooks directory exists
ansible.builtin.file:
path: /mnt/etc/pacman.d/hooks
state: directory
mode: "0755"
- name: Install sbctl auto-signing pacman hook
ansible.builtin.copy:
dest: /mnt/etc/pacman.d/hooks/99-sbctl-sign.hook
mode: "0644"
content: |
[Trigger]
Operation = Install
Operation = Upgrade
Type = Path
Target = boot/vmlinuz-*
Target = usr/lib/modules/*/vmlinuz
[Action]
Description = Signing kernel images for Secure Boot...
When = PostTransaction
Exec = /usr/bin/sbctl sign-all
Depends = sbctl
- name: Verify sbctl signing status
ansible.builtin.command: "{{ chroot_command }} sbctl verify"
register: _sbctl_verify
changed_when: false
failed_when: false
- name: Report sbctl Secure Boot status
ansible.builtin.debug:
msg: >-
Secure Boot (sbctl):
Enrollment={{ 'done' if (_sbctl_enroll.rc | default(1)) == 0 else 'deferred to first boot' }}.
{{ _sbctl_verify.stdout | default('Verify not available') }}
rescue:
- name: Secure Boot setup failed
ansible.builtin.debug:
msg: >-
sbctl Secure Boot setup failed.
On VMs make sure the OVMF firmware is in Setup Mode (fresh NVRAM).
On bare metal enter the firmware setup and switch to Setup Mode first.
To recover manually: sbctl create-keys && sbctl enroll-keys --microsoft && sbctl sign-all

View File

@@ -0,0 +1,45 @@
---
- name: Configure shim-based Secure Boot
vars:
_efi_vendor: >-
{{
"redhat" if os == "rhel"
else ("ubuntu" if os in ["ubuntu", "ubuntu-lts"] else os)
}}
block:
- name: Find shim binary in target system
ansible.builtin.shell:
cmd: >-
set -o pipefail &&
{{ chroot_command }} find /usr/lib/shim /boot/efi/EFI
\( -name 'shimx64.efi.signed.latest' -o -name 'shimx64.efi.dualsigned'
-o -name 'shimx64.efi.signed' -o -name 'shimx64.efi' \)
-type f | sort -r | head -1
executable: /bin/bash
register: _shim_find_result
changed_when: false
failed_when: false
- name: Copy shim to EFI vendor directory
when:
- _shim_find_result.stdout | default('') | length > 0
- _configuration_platform.grub_install | bool
ansible.builtin.command: >-
cp /mnt{{ _shim_find_result.stdout_lines | first }}
/mnt{{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/shimx64.efi
register: _shim_copy_result
changed_when: _shim_copy_result.rc == 0
- name: Verify shim is present
ansible.builtin.stat:
path: "/mnt{{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/shimx64.efi"
register: _shim_stat
- name: Report Secure Boot status
ansible.builtin.debug:
msg: >-
Secure Boot (shim): {{
'shimx64.efi installed at ' ~ partitioning_efi_mountpoint ~ '/EFI/' ~ _efi_vendor
if (_shim_stat.stat.exists | default(false))
else 'shimx64.efi not found, shim package may handle placement on first boot'
}}

View File

@@ -2,6 +2,12 @@
- name: Enable systemd services
when: _configuration_platform.init_system == 'systemd'
vars:
_desktop_dm: >-
{{
system_cfg.features.desktop.display_manager
if (system_cfg.features.desktop.display_manager | length > 0)
else (configuration_desktop_dm_map[system_cfg.features.desktop.environment] | default(''))
}}
configuration_systemd_services: >-
{{
['NetworkManager']
@@ -9,12 +15,31 @@
+ (['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 [])
+ ([_desktop_dm] if system_cfg.features.desktop.enabled | bool and _desktop_dm | length > 0 else [])
+ (['bluetooth'] if system_cfg.features.desktop.enabled | bool else [])
}}
ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}"
loop: "{{ configuration_systemd_services }}"
register: configuration_enable_service_result
changed_when: configuration_enable_service_result.rc == 0
- name: Activate UFW firewall
when:
- system_cfg.features.firewall.backend == 'ufw'
- system_cfg.features.firewall.enabled | bool
ansible.builtin.command: "{{ chroot_command }} ufw --force enable"
register: _ufw_enable_result
changed_when: _ufw_enable_result.rc == 0
failed_when: false
- name: Set default systemd target to graphical
when:
- _configuration_platform.init_system == 'systemd'
- system_cfg.features.desktop.enabled | bool
ansible.builtin.command: "{{ chroot_command }} systemctl set-default graphical.target"
register: _desktop_target_result
changed_when: _desktop_target_result.rc == 0
- name: Enable OpenRC services
when: _configuration_platform.init_system == 'openrc'
vars:

View File

@@ -65,3 +65,15 @@ configuration_platform_config:
grub_mkconfig_prefix: grub-mkconfig
locale_gen: false
init_system: runit
# Display manager auto-detection from desktop environment name.
configuration_desktop_dm_map:
gnome: gdm
kde: sddm
xfce: lightdm
sway: greetd
hyprland: ly@tty2
cinnamon: lightdm
mate: lightdm
lxqt: sddm
budgie: gdm

View File

@@ -68,6 +68,23 @@
Boot from a live installer (Arch, Debian, Ubuntu, etc.) and retry.
quiet: true
- name: Harden sshd for Ansible automation
ansible.builtin.blockinfile:
path: /etc/ssh/sshd_config
marker: "# {mark} BOOTSTRAP ANSIBLE SETTINGS"
block: |
PerSourcePenalties no
MaxStartups 50:30:100
ClientAliveInterval 30
ClientAliveCountMax 10
register: _sshd_config_result
- name: Restart sshd immediately if config was changed
when: _sshd_config_result is changed
ansible.builtin.service:
name: sshd
state: restarted
- name: Abort if the host is not booted from the Arch install media
when:
- not (custom_iso | bool)

View File

@@ -25,6 +25,7 @@
state: latest
loop:
- { name: glibc }
- { name: lua, os: [almalinux, fedora, rhel, rocky] }
- { name: dnf, os: [almalinux, fedora, rhel, rocky] }
- { name: debootstrap, os: [debian, ubuntu, ubuntu-lts] }
- { name: debian-archive-keyring, os: [debian] }

View File

@@ -135,6 +135,15 @@ system_defaults:
user: "_aur_builder"
chroot:
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
initramfs:
generator: "" # auto-detected; override: dracut|mkinitcpio|initramfs-tools
desktop:
enabled: false
environment: "" # gnome|kde|xfce|sway|hyprland|cinnamon|mate|lxqt|budgie
display_manager: "" # auto from environment when empty; override: gdm|sddm|lightdm|greetd
secure_boot:
enabled: false
method: "" # arch only: sbctl (default) or uki; ignored for other distros
# Per-hypervisor required fields — drives data-driven validation.
# All virtual types additionally require network bridge or interfaces.

View File

@@ -144,27 +144,16 @@
url: "{{ system_raw.features.rhel_repo.url | default('') | string }}"
chroot:
tool: "{{ system_raw.features.chroot.tool | string }}"
initramfs:
generator: "{{ system_raw.features.initramfs.generator | default('') | string | lower }}"
desktop:
enabled: "{{ system_raw.features.desktop.enabled | bool }}"
environment: "{{ system_raw.features.desktop.environment | default('') | string | lower }}"
display_manager: "{{ system_raw.features.desktop.display_manager | default('') | string | lower }}"
secure_boot:
enabled: "{{ system_raw.features.secure_boot.enabled | bool }}"
method: "{{ system_raw.features.secure_boot.method | default('') | string | lower }}"
hostname: "{{ system_name }}"
os: "{{ system_os_input if system_os_input | length > 0 else (physical_default_os if system_type == 'physical' else '') }}"
os_version: "{{ system_raw.version | default('') | string }}"
no_log: true
- name: Populate primary network fields from first interface
when:
- system_cfg.network.interfaces | length > 0
- system_cfg.network.bridge | default('') | string | length == 0
vars:
_primary: "{{ system_cfg.network.interfaces[0] }}"
ansible.builtin.set_fact:
system_cfg: >-
{{
system_cfg | combine({
'network': system_cfg.network | combine({
'bridge': _primary.bridge | default(''),
'vlan': _primary.vlan | default(''),
'ip': _primary.ip | default(''),
'prefix': _primary.prefix | default(''),
'gateway': _primary.gateway | default('')
})
}, recursive=True)
}}

View File

@@ -17,6 +17,27 @@
- name: Normalize disk configuration
ansible.builtin.include_tasks: _normalize_disks.yml
- name: Populate primary network fields from first interface
when:
- system_cfg is defined
- system_cfg.network.interfaces | default([]) | length > 0
- system_cfg.network.ip | default('') | string | length == 0
vars:
_primary: "{{ system_cfg.network.interfaces[0] }}"
ansible.builtin.set_fact:
system_cfg: >-
{{
system_cfg | combine({
'network': system_cfg.network | combine({
'bridge': _primary.bridge | default(''),
'vlan': _primary.vlan | default(''),
'ip': _primary.ip | default(''),
'prefix': _primary.prefix | default(''),
'gateway': _primary.gateway | default('')
})
}, recursive=True)
}}
- name: Check if pre-computed system_cfg needs enrichment
when: system_cfg is defined
ansible.builtin.set_fact:

View File

@@ -9,12 +9,13 @@
- >-
system_cfg.features.cis.enabled | bool or (
not (system_cfg.features.cis.enabled | bool) and (
(system_cfg.filesystem == 'btrfs' and item.path in ['/home', '/var/log', '/var/cache/pacman/pkg'])
(system_cfg.filesystem == 'btrfs' and item.path in ['/home', '/var/log']
+ (['/var/cache/pacman/pkg'] if os == 'archlinux' else []))
or (item.path not in ['/home', '/var', '/var/log', '/var/log/audit', '/var/cache/pacman/pkg'])
)
)
- >-
not (item.path in ['/swap', '/var/cache/pacman/pkg'] and system_cfg.filesystem != 'btrfs')
not (item.path in ['/swap', '/var/cache/pacman/pkg'] and (system_cfg.filesystem != 'btrfs' or os != 'archlinux'))
- system_cfg.features.swap.enabled | bool or item.path != '/swap'
ansible.posix.mount:
path: /mnt{{ item.path }}

View File

@@ -43,6 +43,7 @@
when:
- system_cfg.features.cis.enabled | bool or item.subvol not in ['var_log_audit']
- system_cfg.features.swap.enabled | bool or item.subvol != 'swap'
- item.os is not defined or os in item.os
ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
args:
creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
@@ -51,12 +52,19 @@
- { subvol: swap }
- { subvol: home }
- { subvol: var }
- { subvol: pkg }
- { subvol: pkg, os: [archlinux] }
- { subvol: var_log }
- { subvol: var_log_audit }
loop_control:
label: "{{ item.subvol }}"
- name: Set default btrfs subvolume to @
ansible.builtin.shell: >-
btrfs subvolume list /mnt | awk '/ path @$/ {print $2}'
| xargs -I{} btrfs subvolume set-default {} /mnt
register: partitioning_btrfs_default_result
changed_when: partitioning_btrfs_default_result.rc == 0
- name: Set quotas for subvolumes
when: system_cfg.features.cis.enabled | bool
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}

View File

@@ -22,10 +22,10 @@ virtualization_libvirt_ovmf_vars: /usr/share/edk2/x64/OVMF_VARS.4m.fd
virtualization_tpm2_enabled: >-
{{
(system_cfg.luks.enabled | bool)
and (system_cfg.luks.auto | bool)
and (
(system_cfg.luks.method | lower)
== 'tpm2'
(
(system_cfg.luks.enabled | bool)
and (system_cfg.luks.auto | bool)
and (system_cfg.luks.method | lower == 'tpm2')
)
or (system_cfg.features.secure_boot.enabled | default(false) | bool)
}}

View File

@@ -40,10 +40,10 @@
failed_when: false
- name: Undefine libvirt VM
community.libvirt.virt:
name: "{{ hostname }}"
command: undefine
uri: "{{ libvirt_uri | default('qemu:///system') }}"
ansible.builtin.command:
cmd: "virsh -c {{ libvirt_uri | default('qemu:///system') }} undefine {{ hostname }} --nvram"
register: _libvirt_undefine_result
changed_when: _libvirt_undefine_result.rc == 0
failed_when: false
- name: Remove libvirt disk images