diff --git a/roles/configuration/tasks/main.yml b/roles/configuration/tasks/main.yml index 756a26c..d82761d 100644 --- a/roles/configuration/tasks/main.yml +++ b/roles/configuration/tasks/main.yml @@ -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 diff --git a/roles/configuration/tasks/secure_boot.yml b/roles/configuration/tasks/secure_boot.yml new file mode 100644 index 0000000..14fbb04 --- /dev/null +++ b/roles/configuration/tasks/secure_boot.yml @@ -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 diff --git a/roles/configuration/tasks/secure_boot/sbctl.yml b/roles/configuration/tasks/secure_boot/sbctl.yml new file mode 100644 index 0000000..207fff5 --- /dev/null +++ b/roles/configuration/tasks/secure_boot/sbctl.yml @@ -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 diff --git a/roles/configuration/tasks/secure_boot/shim.yml b/roles/configuration/tasks/secure_boot/shim.yml new file mode 100644 index 0000000..8a24c6a --- /dev/null +++ b/roles/configuration/tasks/secure_boot/shim.yml @@ -0,0 +1,57 @@ +--- +- 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.command: >- + {{ chroot_command }} find /usr/lib/shim /boot/efi/EFI + -name 'shimx64.efi*' -type f -print -quit + 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 {{ _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: Enroll Secure Boot keys via efi-updatevar + when: system_cfg.type == 'virtual' + block: + - name: Check if efi-updatevar is available + ansible.builtin.command: which efi-updatevar + register: _efi_updatevar_check + changed_when: false + failed_when: false + + - name: Enroll default UEFI Secure Boot keys + when: _efi_updatevar_check.rc == 0 + ansible.builtin.command: >- + {{ chroot_command }} sbctl enroll-keys --microsoft + register: _sb_enroll_result + changed_when: _sb_enroll_result.rc == 0 + failed_when: false + + - 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' + if (_shim_stat.stat.exists | default(false)) + else 'shimx64.efi not found, shim package may handle placement on first boot' + }}