Compare commits

...

7 Commits

12 changed files with 248 additions and 6 deletions

View File

@@ -222,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
}}
@@ -285,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
}}
@@ -323,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

@@ -94,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

@@ -103,3 +103,17 @@
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('') }}"

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

@@ -141,6 +141,9 @@ system_defaults:
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

@@ -150,6 +150,9 @@
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 }}"

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)
}}