Compare commits

..

171 Commits

Author SHA1 Message Date
MORAWSKI Norbert
5a3f55b8de fix(configuration): skip grub-mkconfig for RedHat EFI systems 2026-03-25 15:46:01 +01:00
d982e0af83 feat(packages): add needrestart to Debian and Ubuntu package lists 2026-03-20 18:06:14 +01:00
535e831f4e feat(bootstrap): add full package upgrade step for Debian and Ubuntu 2026-03-20 18:05:04 +01:00
5ea7ef3c70 fix(vim): use vimscript comment syntax for blockinfile markers in vimrc 2026-03-20 18:00:12 +01:00
a1f223eb62 fix(sudo): use explicit string check instead of bool conditional for sudo field 2026-03-20 17:31:49 +01:00
4c9adb4ddc fix(connection): set ansible_port explicitly at every connection transition 2026-03-20 17:31:49 +01:00
019ad9734a fix(vmware): use primary ansible_* vars for vmware_tools connection plugin precedence 2026-03-20 17:31:49 +01:00
48ed7acb38 fix(vmware): use empty password for vmware_tools during live ISO bootstrap 2026-03-20 17:31:49 +01:00
93aa27c1fd fix(users): update cloud-init template and input validation for dict users 2026-03-20 17:31:49 +01:00
6afe9dbd1c refactor(prompts): remove vars_prompt, require users defined in inventory 2026-03-20 17:31:49 +01:00
fc53b6c786 fix(prompts): add default values to vars_prompt to skip in non-interactive mode 2026-03-20 17:31:49 +01:00
1232484b40 refactor(users): change system.users from list to dict keyed by username 2026-03-20 17:31:49 +01:00
d03179844a fix(configuration): make root password, user keys, and sudo all optional 2026-03-20 17:31:49 +01:00
321fc79467 fix(cloud-init): handle missing keys and make sudo conditional 2026-03-20 17:31:49 +01:00
17c55c7c5c fix(cleanup): remove ansible_become override that blocks swapoff/umount 2026-03-20 17:31:49 +01:00
21a31795aa feat(global_defaults): add system.features.aur schema for validation passthrough 2026-03-20 17:31:49 +01:00
4336d864b3 refactor(virtualization): extract shared Xen disk definitions 2026-03-12 12:27:18 +01:00
62e50c19ff refactor: remove unnecessary changed_when from set_fact tasks 2026-03-12 12:25:45 +01:00
b7cf1b10a9 refactor(partitioning): remove redundant blockdev --rereadpt calls 2026-03-12 12:25:15 +01:00
fc2d924349 refactor(partitioning): remove unused register variables 2026-03-12 12:24:59 +01:00
67e3753ece chore: suppress args[module] false positives from variable-based module_defaults 2026-03-12 12:12:27 +01:00
da9e287e56 fix(cleanup): add no_log to Proxmox VM restart task 2026-03-12 12:12:27 +01:00
a8ea4f0962 fix(virtualization): add missing changed_when to Xen VM stop task 2026-03-12 12:12:27 +01:00
754668b734 fix(configuration): replace fail+ignore_errors with debug for TPM2 fallback warning 2026-03-12 12:12:27 +01:00
5b3076d9e1 refactor(configuration): split encryption.yml into crypttab, dracut, grub, and initramfs subtasks 2026-03-12 09:40:40 +01:00
1f778a7aaa feat(bootstrap): add rescue block with VM cleanup on failure 2026-03-12 07:43:51 +01:00
54ffe5ff91 feat(environment): VMware network config, DNS resolvers, and SSH switchover 2026-03-12 07:43:46 +01:00
335534176f fix(global_defaults): populate flat network fields from interfaces in pre-computed path 2026-03-12 07:43:39 +01:00
79227b4391 feat(vmware): add VMware hypervisor support (node field, connection vars, validation) 2026-03-12 07:43:34 +01:00
13faf33296 fix(bootstrap): exclude tldr from Ubuntu rolling extra packages 2026-02-22 20:40:46 +01:00
de451be77b fix(global_defaults): use archive.ubuntu.com instead of mirror redirector 2026-02-22 16:26:35 +01:00
af9f264cd3 fix(global_defaults): apply mirror default in pre-computed system_cfg path 2026-02-22 14:20:12 +01:00
750a085e19 feat(configuration): add Debian/Ubuntu repository and apt configuration 2026-02-22 10:47:47 +01:00
f2eb9f2c8e feat(bootstrap): use configurable mirror and write proper sources.list 2026-02-22 10:47:43 +01:00
79988619c6 feat(global_defaults): add system.mirror to schema and normalization 2026-02-22 10:47:40 +01:00
fb69c96e4a chore(bootstrap): update ubuntu non-lts codename to questing (25.10) 2026-02-22 03:08:54 +01:00
d586c087f8 fix(global_defaults): add missing ssh.enabled validation assertion 2026-02-22 03:08:31 +01:00
9dd71b2559 fix(global_defaults): correct fedora version upper bound to 43 2026-02-22 03:08:23 +01:00
35f1702447 feat(global_defaults): add root.shell to system schema and normalization 2026-02-22 03:07:30 +01:00
8b18fbdb4c refactor(cleanup): remove duplicated libvirt path vars, reuse virtualization defaults 2026-02-22 03:07:04 +01:00
909a0a6021 refactor(bootstrap,configuration): rename validation-only _normalize.yml files 2026-02-22 03:06:34 +01:00
2f3fce42b5 fix(partitioning): add | bool to all system_cfg.features.cis.enabled checks 2026-02-22 03:06:13 +01:00
b72816e985 fix(partitioning): add partition separator for NVMe/mmcblk device paths 2026-02-22 02:39:36 +01:00
ac0b5caf83 refactor(configuration): centralize DNS list variables in network dispatch 2026-02-22 02:39:32 +01:00
3ddc3c72ed refactor(configuration): extract shared BLS update task to reduce duplication 2026-02-22 02:39:28 +01:00
f1af7ccbca fix(bootstrap): add missing --best flag to RHEL dnf commands 2026-02-22 02:39:23 +01:00
51ca969ff4 refactor(global_defaults): consolidate hypervisor auth into shared credential dicts 2026-02-22 02:35:04 +01:00
1221249546 refactor(bootstrap,configuration,environment): add defaults/main.yml and extract hardcoded values 2026-02-22 02:32:36 +01:00
87fd69b825 refactor(bootstrap,configuration): add per-role _normalize.yml for platform resolution 2026-02-22 02:27:46 +01:00
3deb3ea751 refactor(configuration): add platform_config dict and replace is_rhel/is_debian with os_family lookups 2026-02-22 02:26:54 +01:00
cc30637f09 feat(global_defaults): add os_family_map and os_family fact for platform config lookups 2026-02-22 02:23:05 +01:00
23721aac96 fix(virtualization): add vTPM2 result validation before VMware power-on 2026-02-22 02:22:37 +01:00
5a9b346d72 feat(global_defaults): add semantic validations for IP, hostname, LUKS method, and interface prefix 2026-02-22 02:22:05 +01:00
75267e5140 refactor(global_defaults): extract physical_default_os to configurable default 2026-02-22 02:21:34 +01:00
f0fb68992d fix(global_defaults): normalize system.type 'vm' to 'virtual' for main project compatibility 2026-02-22 02:21:22 +01:00
0e3edb41f7 docs(bootstrap): add section comments, role boundary docs, and pipeline overview 2026-02-22 01:59:12 +01:00
2bf0cb901e refactor(global_defaults): data-driven hypervisor validation and shared constants 2026-02-22 01:59:09 +01:00
1216c79619 refactor(extras): convert custom.sh from template to static copy 2026-02-22 01:59:04 +01:00
4efd64664d fix(cleanup,config): xen tmp cleanup, tpm2 fallback warning, add code comments 2026-02-22 01:59:01 +01:00
dc5aa5077e fix(partitioning,network): swapon idempotency, DNS search domains, tune2fs changed_when 2026-02-22 01:58:56 +01:00
c65934c290 fix(encryption): add no_log to LUKS configuration block 2026-02-22 01:58:52 +01:00
5b8438ac3b fix(network): bind NM connections to detected interface names for multi-NIC 2026-02-21 16:51:15 +01:00
45df803131 fix(bootstrap): make dhcp-client conditional for EL < 10 (removed in EL 10) 2026-02-21 13:43:41 +01:00
30f74fa4bd fix(bootstrap): remove --asexplicit from pacstrap to preserve dependency metadata 2026-02-21 13:26:59 +01:00
19372db27e fix(bootstrap): add kernel package to rocky and almalinux extra packages 2026-02-21 12:06:09 +01:00
d55fc5799d fix(bootstrap): detect kernel package name for dnf family reinstall step 2026-02-21 11:46:57 +01:00
98231be0bd fix(bootstrap): ensure chroot DNS resolution before installing extra packages 2026-02-21 11:30:28 +01:00
c46a4a5a0a fix(environment): align repo IDs in rocky and almalinux templates with bootstrap config 2026-02-21 11:18:34 +01:00
b84688f1d6 fix(configuration): omit interface-name when not explicitly provided to avoid predictable naming mismatch 2026-02-21 08:29:24 +01:00
b1d2294d63 refactor(configuration): rename _uid to configuration_uid for role prefix convention 2026-02-21 05:14:33 +01:00
ac339b54c4 fix(configuration): handle boolean sudo values in sudoers deployment 2026-02-21 05:14:29 +01:00
cb46a6989f fix(configuration): use full path for chpasswd in chroot 2026-02-21 05:03:36 +01:00
73ea7a177b fix(global_defaults): enrich pre-computed system_cfg with bootstrap defaults 2026-02-21 04:24:23 +01:00
0f8faf0a22 chore(lint): suppress var-naming for user-facing API dicts 2026-02-21 02:58:10 +01:00
b520126253 fix(configuration): remove unnecessary changed_when on set_fact tasks 2026-02-21 02:56:58 +01:00
a4ca4c4ff4 refactor(cis): align normalization with main project activation gate pattern 2026-02-21 02:56:39 +01:00
d9efb54bec fix(global_defaults): remove dead /swap and make pacman cache arch-only in reserved mounts 2026-02-21 02:56:20 +01:00
e7a0cc4f62 fix(global_defaults): set filesystem default to ext4 instead of empty string 2026-02-21 02:56:08 +01:00
a76f317f8f refactor(bootstrap): restructure package lists to self-contained per-OS dicts with base/extra/conditional 2026-02-21 02:39:06 +01:00
e5bd152fb3 refactor(environment): split main.yml into focused sub-task files 2026-02-21 02:39:05 +01:00
6d1c3577df refactor(global_defaults): add idempotency guards to normalization tasks 2026-02-21 02:39:03 +01:00
86f0284acb fix(global_defaults): default interface name to eth0 instead of empty string 2026-02-21 02:38:59 +01:00
221bb4d517 docs(cis): add comment explaining squashfs/snap Ubuntu exclusion 2026-02-21 02:38:58 +01:00
e81ba76446 chore(bootstrap): pin collection versions in requirements.yml 2026-02-21 02:38:57 +01:00
54bbb9d15c fix(bootstrap): move Jinja to end of task name and rename registers to bootstrap_dnf_* 2026-02-21 02:38:27 +01:00
f94b220020 docs: update README with cis dict API, execution pipeline, and cleanup defaults 2026-02-21 01:30:36 +01:00
3fd470d63e fix(validation): align btrfs disk size check with new 2GB swap minimum 2026-02-21 01:28:32 +01:00
a3cd507b2a refactor(bootstrap): unify rocky, almalinux, and fedora into shared _dnf_family.yml 2026-02-21 01:27:33 +01:00
f74ec325ea refactor(cis): extract hardcoded values to cis_defaults and add _normalize.yml 2026-02-21 01:26:31 +01:00
bef15af69f refactor(cleanup): prioritize source-match over target-match in libvirt media removal 2026-02-21 01:22:44 +01:00
7970d933e8 docs(cis): explain Fedora exclusion from crypto-policy configuration 2026-02-21 01:22:41 +01:00
a123a32feb fix(bootstrap): replace brittle sed with ansible.builtin.replace for ubuntu universe repo 2026-02-21 01:22:37 +01:00
54c704de4e refactor(virtualization): simplify cloud-user-data sudo to unconditional NOPASSWD 2026-02-21 01:22:34 +01:00
9308d09d7b fix(bootstrap): remove duplicate lrzsz and gate dbus-daemon on version in almalinux 2026-02-21 01:20:34 +01:00
f367844239 fix(virtualization): fix cloud-user-data sudo logic to respect sudo: false 2026-02-21 01:20:31 +01:00
53e4499d2b fix(partitioning): lower swap minimum from 4GB to 2GB for small VMs 2026-02-21 01:19:23 +01:00
eb63a4fa83 fix(partitioning): add wipefs before mkfs on extra disk partitions 2026-02-21 01:19:19 +01:00
9e3688ae2b fix(cis): strengthen kernel module blacklist and sysctl hardening 2026-02-21 01:18:52 +01:00
dea01cc8a0 refactor(partitioning): split monolithic main.yml into focused task files 2026-02-21 00:39:03 +01:00
92c9702e1d fix(validation): add CIDR prefix range check and Ubuntu version validation 2026-02-21 00:38:57 +01:00
c837a52a24 refactor(cis): remove redundant AllowUsers/AllowGroups/DenyUsers/DenyGroups from sshd 2026-02-21 00:38:52 +01:00
fbd57e0603 fix(cis): skip squashfs blacklist on Ubuntu to preserve snap functionality 2026-02-21 00:38:47 +01:00
40a9ee9882 fix(partitioning): correct changed_when on btrfs quota and qgroup commands 2026-02-21 00:38:43 +01:00
3448e95e5c fix(cis): add regexp to all lineinfile entries in security_lines.yml for idempotency 2026-02-21 00:38:36 +01:00
074831833f fix: add no_log to credential-handling pre_tasks and post_tasks in main.yml 2026-02-21 00:38:32 +01:00
d1a5217e88 fix(virtualization): add no_log and secure temp file handling to libvirt cloud-init 2026-02-21 00:38:28 +01:00
07492b5b57 refactor(cleanup): add configurable verify_boot, boot_timeout, and remove_on_failure defaults 2026-02-20 23:02:24 +01:00
14913bcd3d refactor: move playbook-root templates into their respective roles 2026-02-20 23:01:38 +01:00
041650c287 refactor: add loop_control labels to dict-based loops across all roles 2026-02-20 23:00:53 +01:00
a63ffbc731 refactor(partitioning): move btrfs home quota to configurable default 2026-02-20 22:55:37 +01:00
9d2f1cc5bd fix(environment): detect RHEL ISO device dynamically instead of hardcoded /dev/sr paths 2026-02-20 22:54:42 +01:00
f72f9feb9a refactor(global_defaults): split system.yml into composable normalization stages 2026-02-20 22:54:05 +01:00
417737f904 refactor(global_defaults): extract OS family lists to single source of truth 2026-02-20 22:52:55 +01:00
a06c2ebdcf fix(partitioning): add failed_when to all blkid commands to catch empty UUIDs 2026-02-20 22:52:18 +01:00
e174ecda42 fix(partitioning): add default fallbacks for is_rhel, os, os_version in defaults 2026-02-20 22:51:37 +01:00
5246a905bb fix(virtualization): use hostname variable instead of hardcoded archiso in cloud-user-data 2026-02-20 22:51:32 +01:00
d00d84b69c fix(virtualization): avoid no-handler lint finding in xen VM created tracking 2026-02-20 22:29:03 +01:00
4dafa8c596 fix(partitioning): fix line length violation in home size calculation 2026-02-20 22:28:58 +01:00
53584b8730 fix(configuration): add pipefail to root password shell pipe 2026-02-20 22:28:54 +01:00
ce40468b77 fix(bootstrap): use release map for ubuntu version detection 2026-02-20 22:27:46 +01:00
4b4fab3c33 chore: add .yamllint matching main project conventions 2026-02-20 22:27:31 +01:00
db2fab5e7d fix(configuration): use chpasswd for root password and separate shell setting 2026-02-20 22:27:17 +01:00
42be0a5919 fix(configuration): add explicit LUKS auto-decrypt fallback state tracking and logging 2026-02-20 22:26:47 +01:00
17400fa6ff refactor(partitioning): externalize hardcoded LVM and disk sizing constants to defaults 2026-02-20 22:26:23 +01:00
deb14d2c94 fix(virtualization): add xen VM existence check and improve changed_when 2026-02-20 22:25:10 +01:00
65c5b1029b fix(cis): add pipefail to sshd version detection and define binary defaults 2026-02-20 22:24:14 +01:00
a1fbb7c21d feat(cleanup): gate RHEL ISO disk and fstab handling on rhel_repo.source 2026-02-20 21:51:20 +01:00
d076ac8fef feat(global_defaults): add system.features.rhel_repo option (iso|satellite|none) 2026-02-20 21:51:16 +01:00
c82e4afc4d fix(encryption): add warning before silent TPM2-to-keyfile fallback 2026-02-20 21:51:12 +01:00
ac72fdc4a6 fix(partitioning): correct wipefs changed_when to report actual disk modification 2026-02-20 21:51:09 +01:00
b2e050c467 fix(validation): require password for primary user in system.users[0] 2026-02-20 21:51:06 +01:00
914d7dd9d1 fix(system_check): move no_log from block to individual API tasks 2026-02-20 21:51:02 +01:00
21bf8f79e2 fix(cis): make mlkem768x25519-sha256 KexAlgorithm conditional on OpenSSH 9.9+ 2026-02-20 21:50:58 +01:00
38feff4369 fix(cis): use is_rhel for journald config path instead of fedora-only check 2026-02-20 21:50:55 +01:00
404529e8a4 refactor(configuration): add conditional dispatch to task includes 2026-02-20 21:16:52 +01:00
3db18858c3 refactor(cis): move OS-specific binary resolution to vars/main.yml 2026-02-20 21:16:48 +01:00
72a9576abe refactor(configuration): split network.yml into per-init-system dispatch files 2026-02-20 21:16:45 +01:00
462c2c7dfe refactor(bootstrap): restructure conditional package lists to list concatenation 2026-02-20 21:16:40 +01:00
ef8bfeaf84 refactor(configuration): convert services.yml to list-based loop 2026-02-20 21:16:37 +01:00
ba6be037ac refactor(virt): adopt module_defaults for hypervisor credentials 2026-02-20 21:16:33 +01:00
5ca1c7f570 refactor(cleanup): restructure dispatch to use hypervisor_type include 2026-02-20 21:16:28 +01:00
cd8e477534 refactor(partitioning): extract VG name to defaults variable 2026-02-20 21:16:25 +01:00
c439e9741e fix(configuration): remove trailing blank line from extras.yml 2026-02-20 20:20:33 +01:00
0a5c70e49f docs(environment): document RPM GPG policy relaxation 2026-02-20 20:19:57 +01:00
19f2c9efe2 chore(bootstrap): align ansible.cfg with main project settings 2026-02-20 20:19:46 +01:00
230c74fd9b feat(system_check): add safety check for physical installs 2026-02-20 20:19:37 +01:00
a2c19e2e49 fix(cleanup): fix vmware CD-ROM omit fragility and add cross-role defaults 2026-02-20 20:19:25 +01:00
9f9a4b38b8 fix(virtualization): add XML safety attributes and switch xen to virtio 2026-02-20 20:18:49 +01:00
524356cf8d fix(cis): remove deprecated sshd options and update hardening values 2026-02-20 20:17:52 +01:00
a2993212ca fix(configuration): disambiguate BLS task names and clean up misc noise 2026-02-20 20:17:05 +01:00
fba2e5fc94 refactor(configuration): relocate login banner and fix blockinfile markers 2026-02-20 20:16:19 +01:00
cf68a93b45 fix(configuration): use short hostname and allow per-user shell 2026-02-20 20:15:49 +01:00
3000268a0e fix(partitioning): mount extra disks by UUID instead of device path 2026-02-20 20:15:25 +01:00
196c5be67a fix(partitioning): correct LVM swap sizing and harden UUID fallbacks 2026-02-20 20:15:00 +01:00
33bad193b4 fix(configuration): add trailing semicolons to NM keyfile DNS fields 2026-02-20 20:14:06 +01:00
d5277802f7 fix(bootstrap): add missing packages and remove duplicates 2026-02-20 20:13:53 +01:00
28e6cf50d1 fix(bootstrap): add devpts mount and use ephemeral state for RHEL DVD 2026-02-20 20:12:59 +01:00
42cb5071c2 fix(bootstrap): unify resolv.conf to live environment DNS symlink 2026-02-20 20:12:42 +01:00
23a798a63a fix(global_defaults): add no_log to hypervisor tasks and expand validation 2026-02-20 20:11:37 +01:00
5dd84c6b39 fix: configurable OVMF/machine type, routes syntax, package lists, interface names 2026-02-20 18:47:12 +01:00
d0ae20911b fix(cleanup): keep RHEL ISO ide1 attached as local repo 2026-02-20 18:41:40 +01:00
b6d06dd96d fix: deep analysis audit — no_log, resolv.conf, service conflicts, lint 2026-02-20 18:34:59 +01:00
09b3ed44ba fix(bootstrap): RHEL 9 bootstrap from Arch ISO compatibility
- Generate resolv.conf from inventory DNS settings instead of copying
  host file (Arch ISO has systemd-resolved stub 127.0.0.53)
- Add XFS compat options for GRUB 2.06 and kernel 5.14 across LVM
  volumes, /boot partition, and data disks
- Mount API filesystems (proc, sys, dev) into chroot for RPM scriptlets
- Bypass GPG Sequoia validation with _pkgverify_level none
- Tolerate grub2-common scriptlet warnings
- Handle libvirt VM destroy gracefully during cleanup
2026-02-20 16:58:59 +01:00
603abe63cb refactor: make bootstrap host target configurable 2026-02-20 16:58:59 +01:00
1c0e6533ae fix(ubuntu): add initramfs-tools to debootstrap base packages 2026-02-20 16:58:59 +01:00
00aa614cfd fix(bootstrap): use explicit keyring for debootstrap and copy resolv.conf 2026-02-20 16:58:59 +01:00
4905d10bc0 fix(cloud-init): handle boolean sudo values in user-data template 2026-02-20 16:58:59 +01:00
b4e8ccb77f fix: re-gather facts after reboot to detect target OS package manager
The live ISO (Arch) caches ansible_pkg_mgr=pacman. After rebooting
into the target OS (e.g. Debian), package module fails because pacman
is not available. Re-gather minimal facts including pkg_mgr.
2026-02-20 16:58:59 +01:00
2a82ee4d5c fix: resolve Jinja2 .keys ambiguity, fastfetch availability, and python interpreter
- Use bracket notation item['keys'] instead of item.keys to avoid
  conflict with Python dict .keys() method
- Remove fastfetch from Debian 12 package list (only available in 13+)
- Set explicit python interpreter path for post-reboot tasks
2026-02-20 16:58:58 +01:00
7b213e7456 fix(partitioning): create separate /boot for LVM-based filesystems
VMware EFI firmware may not initialize all SCSI devices before GRUB
runs, preventing LVM assembly when the root LV spans multiple disks.
A separate /boot partition (the standard RHEL Anaconda layout) lets
GRUB load kernels without LVM; the kernel initramfs handles LVM
activation with proper device waiting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 04:50:32 +01:00
cfc261878a fix(bootloader): run efibootmgr on host for universal chroot compatibility
The previous approach ran efibootmgr inside the chroot, which only works
with arch-chroot (auto-mounts efivars) but fails silently with
systemd-nspawn or plain chroot. Move EFI boot entry creation to the host
where efivars is always available.

Also fixes wrong EFI loader path (\efi\EFI\... -> \EFI\...) and uses
the correct vendor label (e.g. "redhat" instead of raw os variable).

For non-RHEL distros, grub-install now uses --no-nvram to avoid
redundant NVRAM writes; the host efibootmgr handles entry creation
for all distros uniformly with idempotent pre-check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 03:36:20 +01:00
30 changed files with 129 additions and 913 deletions

View File

@@ -257,15 +257,7 @@ Users must be defined in inventory. The dict format enables additive merging acr
| Key | Type | Default | Description | | Key | Type | Default | Description |
| -------- | ------------- | ------- | ---------------------------------------------- | | -------- | ------------- | ------- | ---------------------------------------------- |
| `device` | string | `auto` | TPM2 device selector | | `device` | string | `auto` | TPM2 device selector |
| `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`); empty = no PCR binding | | `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`) |
**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` #### `system.features`
@@ -282,26 +274,6 @@ The bootstrap auto-switches to dracut when `method: tpm2` is set. Override via `
| `banner.motd` | bool | `false` | MOTD banner | | `banner.motd` | bool | `false` | MOTD banner |
| `banner.sudo` | bool | `true` | Sudo banner | | `banner.sudo` | bool | `true` | Sudo banner |
| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, or `systemd-nspawn` | | `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 ### 4.3 `hypervisor` Dictionary

View File

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

View File

@@ -1,48 +0,0 @@
---
- 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,9 +18,6 @@
groupinstall -y {{ _dnf_groups }} groupinstall -y {{ _dnf_groups }}
register: bootstrap_dnf_base_result register: bootstrap_dnf_base_result
changed_when: bootstrap_dnf_base_result.rc == 0 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 - name: Ensure chroot has DNS resolution
ansible.builtin.file: ansible.builtin.file:

View File

@@ -34,10 +34,6 @@
bootstrap_var_key: "{{ 'bootstrap_' + (os | replace('-lts', '') | replace('-', '_')) }}" bootstrap_var_key: "{{ 'bootstrap_' + (os | replace('-lts', '') | replace('-', '_')) }}"
ansible.builtin.include_tasks: "{{ bootstrap_os_task_map[os] }}" 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 - name: Ensure chroot uses live environment DNS
ansible.builtin.file: ansible.builtin.file:
src: /run/NetworkManager/resolv.conf src: /run/NetworkManager/resolv.conf

View File

@@ -1,149 +0,0 @@
---
# 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

@@ -222,7 +222,6 @@ bootstrap_debian:
+ (['software-properties-common'] if (os_version | string) not in ['13', 'unstable'] else []) + (['software-properties-common'] if (os_version | string) not in ['13', 'unstable'] else [])
+ (['systemd-zram-generator'] if (os_version | string) not in ['10', '11'] else []) + (['systemd-zram-generator'] if (os_version | string) not in ['10', '11'] else [])
+ (['tldr'] if (os_version | string) not in ['13', 'unstable'] 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 + bootstrap_common_conditional
}} }}
@@ -286,7 +285,6 @@ bootstrap_ubuntu:
conditional: >- conditional: >-
{{ {{
(['tldr'] if (os_version | default('') | string | length) > 0 else []) (['tldr'] if (os_version | default('') | string | length) > 0 else [])
+ (['shim-signed'] if system_cfg.features.secure_boot.enabled | bool else [])
+ bootstrap_common_conditional + bootstrap_common_conditional
}} }}
@@ -325,7 +323,6 @@ bootstrap_archlinux:
{{ {{
(['openssh'] if system_cfg.features.ssh.enabled | bool else []) (['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 []) + (['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) + (bootstrap_common_conditional | reject('equalto', 'nftables') | list)
}} }}

View File

@@ -72,12 +72,6 @@
| trim | 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 - name: Update VM definition without installer media
community.libvirt.virt: community.libvirt.virt:
command: define command: define
@@ -94,35 +88,6 @@
state: destroyed state: destroyed
failed_when: false 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 - name: Start the VM
community.libvirt.virt: community.libvirt.virt:
name: "{{ hostname }}" name: "{{ hostname }}"

View File

@@ -14,12 +14,3 @@
- name: Set platform configuration - name: Set platform configuration
ansible.builtin.set_fact: ansible.builtin.set_fact:
_configuration_platform: "{{ configuration_platform_config[os_family] }}" _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,16 +34,6 @@
register: configuration_efi_entry_result register: configuration_efi_entry_result
changed_when: configuration_efi_entry_result.rc == 0 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 - name: Ensure lvm2 for non btrfs filesystems
when: os == "archlinux" and system_cfg.filesystem != "btrfs" when: os == "archlinux" and system_cfg.filesystem != "btrfs"
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
@@ -58,62 +48,8 @@
register: configuration_initramfs_result register: configuration_initramfs_result
changed_when: configuration_initramfs_result.rc == 0 changed_when: configuration_initramfs_result.rc == 0
- name: Generate grub config (RedHat) - name: Generate grub config
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' when: os_family != 'RedHat'
ansible.builtin.command: "{{ chroot_command }} /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg" ansible.builtin.command: "{{ chroot_command }} /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg"
register: configuration_grub_result register: configuration_grub_result
changed_when: configuration_grub_result.rc == 0 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: block:
- name: Set LUKS configuration facts - name: Set LUKS configuration facts
vars: vars:
_raw_pcrs: >- luks_tpm2_pcrs: >-
{{ {{
( (
system_cfg.luks.tpm2.pcrs system_cfg.luks.tpm2.pcrs
@@ -20,17 +20,6 @@
| regex_replace('\\s+', '') | regex_replace('\\s+', '')
| regex_replace('^\\+|\\+$', '') | 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: ansible.builtin.set_fact:
configuration_luks_mapper_name: "{{ system_cfg.luks.mapper }}" configuration_luks_mapper_name: "{{ system_cfg.luks.mapper }}"
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}" configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
@@ -47,12 +36,6 @@
configuration_luks_tpm2_device: "{{ system_cfg.luks.tpm2.device }}" configuration_luks_tpm2_device: "{{ system_cfg.luks.tpm2.device }}"
configuration_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}" configuration_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}"
configuration_luks_keyfile_path: "/etc/cryptsetup-keys.d/{{ system_cfg.luks.mapper }}.key" 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 - name: Validate LUKS UUID is available
ansible.builtin.assert: ansible.builtin.assert:
@@ -68,13 +51,8 @@
fail_msg: system.luks.passphrase must be set for LUKS auto-decrypt. fail_msg: system.luks.passphrase must be set for LUKS auto-decrypt.
no_log: true no_log: true
- name: Detect TPM2 unlock method - name: Enroll TPM2 for LUKS
ansible.builtin.include_tasks: encryption/initramfs_detect.yml when: configuration_luks_auto_method == 'tpm2'
- 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 ansible.builtin.include_tasks: encryption/tpm2.yml
- name: Configure LUKS keyfile auto-decrypt - name: Configure LUKS keyfile auto-decrypt
@@ -100,7 +78,7 @@
}} }}
luks_tpm2_option_list: >- luks_tpm2_option_list: >-
{{ {{
(configuration_luks_auto_method == 'tpm2' and (_tpm2_method | default('systemd-cryptenroll')) == 'systemd-cryptenroll') (configuration_luks_auto_method == 'tpm2')
| ternary( | ternary(
['tpm2-device=' + configuration_luks_tpm2_device] ['tpm2-device=' + configuration_luks_tpm2_device]
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs] + (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
@@ -144,16 +122,16 @@
path: /mnt{{ configuration_luks_keyfile_path }} path: /mnt{{ configuration_luks_keyfile_path }}
state: absent state: absent
- name: Configure initramfs for LUKS
ansible.builtin.include_tasks: encryption/initramfs.yml
- name: Configure crypttab - name: Configure crypttab
ansible.builtin.include_tasks: encryption/crypttab.yml ansible.builtin.include_tasks: encryption/crypttab.yml
- name: Configure dracut for LUKS - name: Configure initramfs
when: _initramfs_generator | default('') == 'dracut' ansible.builtin.include_tasks: encryption/initramfs.yml
- name: Configure dracut
when: os_family == 'RedHat'
ansible.builtin.include_tasks: encryption/dracut.yml ansible.builtin.include_tasks: encryption/dracut.yml
- name: Configure GRUB for LUKS - name: Configure GRUB for LUKS
when: _initramfs_generator | default('') != 'dracut' or os_family != 'RedHat' when: not os_family == 'RedHat'
ansible.builtin.include_tasks: encryption/grub.yml ansible.builtin.include_tasks: encryption/grub.yml

View File

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

View File

@@ -1,94 +1,8 @@
--- ---
# Initramfs configuration for LUKS auto-unlock. - name: Ensure keyfile pattern for initramfs-tools
# 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: when:
- configuration_luks_auto_method == 'tpm2' - os_family == 'Debian'
- _tpm2_method | default('') == 'clevis' - configuration_luks_keyfile_in_use
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: ansible.builtin.lineinfile:
path: /mnt/etc/cryptsetup-initramfs/conf-hook path: /mnt/etc/cryptsetup-initramfs/conf-hook
regexp: "^KEYFILE_PATTERN=" regexp: "^KEYFILE_PATTERN="
@@ -96,9 +10,8 @@
create: true create: true
mode: "0644" mode: "0644"
# --- mkinitcpio: systemd + sd-encrypt hooks ---
- name: Configure mkinitcpio hooks for LUKS - name: Configure mkinitcpio hooks for LUKS
when: _initramfs_generator | default('') == 'mkinitcpio' when: os == 'archlinux'
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf path: /mnt/etc/mkinitcpio.conf
regexp: "^HOOKS=" regexp: "^HOOKS="
@@ -107,13 +20,13 @@
block sd-encrypt{{ ' lvm2' if system_cfg.filesystem != 'btrfs' else '' }} filesystems fsck) block sd-encrypt{{ ' lvm2' if system_cfg.filesystem != 'btrfs' else '' }} filesystems fsck)
- name: Read mkinitcpio configuration - name: Read mkinitcpio configuration
when: _initramfs_generator | default('') == 'mkinitcpio' when: os == 'archlinux'
ansible.builtin.slurp: ansible.builtin.slurp:
src: /mnt/etc/mkinitcpio.conf src: /mnt/etc/mkinitcpio.conf
register: configuration_mkinitcpio_slurp register: configuration_mkinitcpio_slurp
- name: Build mkinitcpio FILES list - name: Build mkinitcpio FILES list
when: _initramfs_generator | default('') == 'mkinitcpio' when: os == 'archlinux'
vars: vars:
mkinitcpio_files_list: >- mkinitcpio_files_list: >-
{{ {{
@@ -129,7 +42,7 @@
{{ {{
( (
(mkinitcpio_files_list + [configuration_luks_keyfile_path]) (mkinitcpio_files_list + [configuration_luks_keyfile_path])
if (configuration_luks_keyfile_in_use | default(false)) if configuration_luks_keyfile_in_use
else ( else (
mkinitcpio_files_list mkinitcpio_files_list
| reject('equalto', configuration_luks_keyfile_path) | reject('equalto', configuration_luks_keyfile_path)
@@ -142,7 +55,7 @@
configuration_mkinitcpio_files_list_new: "{{ mkinitcpio_files_list_new }}" configuration_mkinitcpio_files_list_new: "{{ mkinitcpio_files_list_new }}"
- name: Configure mkinitcpio FILES list - name: Configure mkinitcpio FILES list
when: _initramfs_generator | default('') == 'mkinitcpio' when: os == 'archlinux'
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf path: /mnt/etc/mkinitcpio.conf
regexp: "^FILES=" regexp: "^FILES="

View File

@@ -1,98 +0,0 @@
---
# 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,35 +1,26 @@
--- ---
# 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 - name: Enroll TPM2 for LUKS
block: block:
# Tempfile in chroot /tmp — accessible by both chroot and host commands
- name: Create temporary passphrase file for TPM2 enrollment - name: Create temporary passphrase file for TPM2 enrollment
ansible.builtin.tempfile: ansible.builtin.tempfile:
path: /mnt/root path: /mnt/tmp
prefix: luks-passphrase- prefix: luks-passphrase-
state: file state: file
register: _tpm2_passphrase_tempfile register: configuration_luks_tpm2_passphrase_tempfile
- name: Write passphrase into temporary file - name: Write passphrase into temporary file for TPM2 enrollment
ansible.builtin.copy: ansible.builtin.copy:
dest: "{{ _tpm2_passphrase_tempfile.path }}" dest: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
content: "{{ configuration_luks_passphrase }}" content: "{{ configuration_luks_passphrase }}"
owner: root owner: root
group: root group: root
mode: "0600" mode: "0600"
no_log: true no_log: true
- name: Ensure TPM device is accessible in chroot - name: Enroll TPM2 token
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: vars:
_enroll_args: >- configuration_luks_enroll_args: >-
{{ {{
[ [
'/usr/bin/systemd-cryptenroll', '/usr/bin/systemd-cryptenroll',
@@ -37,28 +28,69 @@
'--tpm2-with-pin=false', '--tpm2-with-pin=false',
'--wipe-slot=tpm2', '--wipe-slot=tpm2',
'--unlock-key-file=' + ( '--unlock-key-file=' + (
_tpm2_passphrase_tempfile.path | regex_replace('^/mnt', '') configuration_luks_tpm2_passphrase_tempfile.path
| regex_replace('^/mnt', '')
) )
] ]
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs] + (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
if configuration_luks_tpm2_pcrs | length > 0 else []) if configuration_luks_tpm2_pcrs | length > 0 else [])
+ [configuration_luks_device] + [configuration_luks_device]
}} }}
ansible.builtin.command: "{{ chroot_command }} {{ _enroll_args | join(' ') }}" configuration_luks_enroll_chroot_cmd: >-
register: _tpm2_enroll_result {{ chroot_command }} {{ configuration_luks_enroll_args | join(' ') }}
changed_when: _tpm2_enroll_result.rc == 0 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
- 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: rescue:
- name: TPM2 enrollment failed - name: Warn about TPM2 enrollment failure
ansible.builtin.debug: ansible.builtin.debug:
msg: >- msg: >-
TPM2 enrollment failed: {{ _tpm2_enroll_result.stderr | default('unknown') }}. WARNING: TPM2 enrollment failed — falling back to keyfile auto-decrypt.
The system will require the passphrase for LUKS unlock on boot. The system will use a keyfile instead of TPM2 for automatic LUKS unlock.
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: always:
- name: Remove temporary passphrase file - name: Remove TPM2 enrollment passphrase file
when: _tpm2_passphrase_tempfile.path is defined when: configuration_luks_tpm2_passphrase_tempfile.path is defined
ansible.builtin.file: ansible.builtin.file:
path: "{{ _tpm2_passphrase_tempfile.path }}" path: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
state: absent state: absent

View File

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

View File

@@ -1,8 +0,0 @@
---
- 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

@@ -1,115 +0,0 @@
---
- 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

@@ -1,45 +0,0 @@
---
- 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,12 +2,6 @@
- name: Enable systemd services - name: Enable systemd services
when: _configuration_platform.init_system == 'systemd' when: _configuration_platform.init_system == 'systemd'
vars: 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: >- configuration_systemd_services: >-
{{ {{
['NetworkManager'] ['NetworkManager']
@@ -15,31 +9,12 @@
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' 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 []) + ([_configuration_platform.ssh_service] if system_cfg.features.ssh.enabled | bool else [])
+ (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' 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 }}" ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}"
loop: "{{ configuration_systemd_services }}" loop: "{{ configuration_systemd_services }}"
register: configuration_enable_service_result register: configuration_enable_service_result
changed_when: configuration_enable_service_result.rc == 0 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 - name: Enable OpenRC services
when: _configuration_platform.init_system == 'openrc' when: _configuration_platform.init_system == 'openrc'
vars: vars:

View File

@@ -65,15 +65,3 @@ configuration_platform_config:
grub_mkconfig_prefix: grub-mkconfig grub_mkconfig_prefix: grub-mkconfig
locale_gen: false locale_gen: false
init_system: runit 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,23 +68,6 @@
Boot from a live installer (Arch, Debian, Ubuntu, etc.) and retry. Boot from a live installer (Arch, Debian, Ubuntu, etc.) and retry.
quiet: true 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 - name: Abort if the host is not booted from the Arch install media
when: when:
- not (custom_iso | bool) - not (custom_iso | bool)

View File

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

View File

@@ -135,15 +135,6 @@ system_defaults:
user: "_aur_builder" user: "_aur_builder"
chroot: chroot:
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn 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. # Per-hypervisor required fields — drives data-driven validation.
# All virtual types additionally require network bridge or interfaces. # All virtual types additionally require network bridge or interfaces.

View File

@@ -144,16 +144,27 @@
url: "{{ system_raw.features.rhel_repo.url | default('') | string }}" url: "{{ system_raw.features.rhel_repo.url | default('') | string }}"
chroot: chroot:
tool: "{{ system_raw.features.chroot.tool | string }}" 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 }}" hostname: "{{ system_name }}"
os: "{{ system_os_input if system_os_input | length > 0 else (physical_default_os if system_type == 'physical' else '') }}" 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 }}" os_version: "{{ system_raw.version | default('') | string }}"
no_log: true 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,27 +17,6 @@
- name: Normalize disk configuration - name: Normalize disk configuration
ansible.builtin.include_tasks: _normalize_disks.yml 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 - name: Check if pre-computed system_cfg needs enrichment
when: system_cfg is defined when: system_cfg is defined
ansible.builtin.set_fact: ansible.builtin.set_fact:

View File

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

View File

@@ -43,7 +43,6 @@
when: when:
- system_cfg.features.cis.enabled | bool or item.subvol not in ['var_log_audit'] - system_cfg.features.cis.enabled | bool or item.subvol not in ['var_log_audit']
- system_cfg.features.swap.enabled | bool or item.subvol != 'swap' - 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 }} ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
args: args:
creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }} creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
@@ -52,19 +51,12 @@
- { subvol: swap } - { subvol: swap }
- { subvol: home } - { subvol: home }
- { subvol: var } - { subvol: var }
- { subvol: pkg, os: [archlinux] } - { subvol: pkg }
- { subvol: var_log } - { subvol: var_log }
- { subvol: var_log_audit } - { subvol: var_log_audit }
loop_control: loop_control:
label: "{{ item.subvol }}" 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 - name: Set quotas for subvolumes
when: system_cfg.features.cis.enabled | bool when: system_cfg.features.cis.enabled | bool
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }} 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: >- virtualization_tpm2_enabled: >-
{{ {{
( (system_cfg.luks.enabled | bool)
(system_cfg.luks.enabled | bool) and (system_cfg.luks.auto | bool)
and (system_cfg.luks.auto | bool) and (
and (system_cfg.luks.method | lower == 'tpm2') (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 failed_when: false
- name: Undefine libvirt VM - name: Undefine libvirt VM
ansible.builtin.command: community.libvirt.virt:
cmd: "virsh -c {{ libvirt_uri | default('qemu:///system') }} undefine {{ hostname }} --nvram" name: "{{ hostname }}"
register: _libvirt_undefine_result command: undefine
changed_when: _libvirt_undefine_result.rc == 0 uri: "{{ libvirt_uri | default('qemu:///system') }}"
failed_when: false failed_when: false
- name: Remove libvirt disk images - name: Remove libvirt disk images