Skip to content

Commit

Permalink
Add mok role and tests.
Browse files Browse the repository at this point in the history
This commit does not have a working test setup, and has not updated the
galaxy.yml file to advertise the role. Right now we are having the same
issues discussed in the testing platform issue, where we don't have a
way to programatically test this.

Next line of inquiry will be ehther we can use the already-enrolled
fedora CA certificate as the "enrolled" cert and a generated one as the
"not enrolled cert."

This commit assumes that we want to keep enrollment functionality, but
this is not yet decided at a project level. This could be turned into a
role that fails if the provided certificate is not enrolled. This would
simplify it too, since auto-generation of certificates would not be
required.
  • Loading branch information
Christopher Palmer-Richez committed Aug 30, 2024
1 parent 877b1f1 commit 962d32a
Show file tree
Hide file tree
Showing 8 changed files with 593 additions and 0 deletions.
61 changes: 61 additions & 0 deletions roles/mok/meta/argument_specs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
argument_specs:
main:
short_description: Machine owner key enrollment
description: Declare the desired state of shim's trust database
version_added: "1.0.0"
author: Christopher Palmer-Richez
options:
mok_certificate:
type: dict
description: The details of the machine owner key certificate.
required: true
options:
path:
type: path
description: The path to the machine owner key certificate.
required: true

owner:
type: str
description: The owner of the machine owner key certificate.
required: true

group:
type: str
description: The group that owns the machine owner key certificate.
required: true

mode:
type: str
description: The `chmod` mode of the certificate file.

mok_private_key:
type: dict
description: The details of the machine owner key.
required: true
options:
path:
type: path
description: The path to the machine owner key.
required: true

owner:
type: str
description: The owner of the machine owner key.
required: true

group:
type: str
description: The group that owns the machine owner key.
required: true

mode:
type: str
description: The `chmod` mode of the private key file.

mok_state:
type: str
description: Must be 'trusted'.
choices:
- trusted
300 changes: 300 additions & 0 deletions roles/mok/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
- name: Gather keys and certificates
become: true
block:
- name: Gather private key
community.crypto.openssl_privatekey:
path: "{{ uki_config_mok.private_key }}"
size: 2048
mode: "{{ uki_config_mok.mode }}"
owner: "{{ uki_config_mok.owner }}"
group: "{{ uki_config_mok.group }}"
seuser: "{{ uki_config_mok.seuser }}"
serole: "{{ uki_config_mok.serole }}"
setype: "{{ uki_config_mok.setype }}"
selevel: "{{ uki_config_mok.selevel }}"

- name: Gather x509 certificate
community.crypto.x509_certificate:
path: "{{ uki_config_mok.certificate }}"
privatekey_path: "{{ uki_config_mok.private_key }}"
provider: selfsigned
owner: "{{ uki_config_mok.owner }}"
group: "{{ uki_config_mok.group }}"
mode: "{{ uki_config_mok.mode }}"
seuser: "{{ uki_config_mok.seuser }}"
serole: "{{ uki_config_mok.serole }}"
setype: "{{ uki_config_mok.setype }}"
selevel: "{{ uki_config_mok.selevel }}"

- name: Gather x509 certificate in DER format
community.crypto.x509_certificate_convert:
src_path: "{{ uki_config_mok.certificate }}"
dest_path: "{{ uki_config_mok_der_path }}"
format: der
owner: "{{ uki_config_mok.owner }}"
group: "{{ uki_config_mok.group }}"
mode: "{{ uki_config_mok.mode }}"
seuser: "{{ uki_config_mok.seuser }}"
serole: "{{ uki_config_mok.serole }}"
setype: "{{ uki_config_mok.setype }}"
selevel: "{{ uki_config_mok.selevel }}"

- name: Query MOK keyring
become: true
ansible.builtin.command:
argv:
- mokutil
- -t
- "{{ uki_config_mok_der_path }}"
changed_when: false
register: test_key_request
failed_when:
- test_key_request.stdout is not ansible.builtin.match('^.*\sis\snot\senrolled$')
- test_key_request.stdout is not ansible.builtin.match('^.*\sis\salready\sin\sthe\senrollment\srequest$')
- test_key_request.stdout is not ansible.builtin.match('^.*\sis\salready\senrolled$')

- name: Import new MOK
when: test_key_request.rc == 0
block:
- name: Get MOK password
register: mok_password_prompt
ansible.builtin.pause:
prompt: Enter MOK password
echo: false

- name: Import MOK
become: true
ansible.builtin.shell:
cmd: |
spawn /usr/bin/mokutil --import {{ uki_config_mok_der_path }}
expect "input password:"
send -- "{{ mok_password_prompt.user_input }}\n"
expect "input password again:"
send -- "{{ mok_password_prompt.user_input }}\n"
expect eof
executable: /usr/bin/expect
changed_when: true

- name: Reboot into MokManager
when:
test_key_request.stdout is ansible.builtin.match('^.*\sis\salready\sin\sthe\senrollment\srequest$') or
test_key_request.rc == 0
block:
- name: MokManager warning
ansible.builtin.debug:
msg: >
A new Machine Owner Key (MOK) was enrolled. Before this playbook can
continue, the import will need to be manually validated in a program
called MokManager. This will happen automatically when the machine is
rebooted, but requires an administrator with access to a local console.
MokManager is very easy to use. Once in the menu select "Enroll MOK,"
enter the same password you just provided, and reboot. This playbook
will resume when the host is back online, or will automatically fail
after ten minutes. If something strange happens, just run the play
again.
- name: Reboot prompt
ansible.builtin.pause:
prompt: Ready to reboot? (y/n)
echo: true
register: reboot_prompt
changed_when: false
failed_when: reboot_prompt.user_input != 'y'

- name: Reboot
become: true
ansible.builtin.reboot:

- name: Verify MOK was enrolled
become: true
ansible.builtin.command:
argv:
- mokutil
- -t
- "{{ uki_config_mok_der_path }}"
register: validate_mok_request
changed_when: false
failed_when: validate_mok_request.rc == 0

- name: Configure kernel-install to generate and sign UKIs
become: true
vars:
install_conf_path: "{{ uki_config_kernel_install_config_root }}/install.conf"
ukify_conf_path: "{{ uki_config_kernel_install_config_root }}/uki.conf"
block:
- name: Configure kernel-install to generate UKIs
block:
- name: Save the original file
ansible.builtin.slurp:
src: "{{ install_conf_path }}"
register: kernel_install_config_backup
changed_when: false
ignore_errors: true

- name: Set the install layout to UKI
community.general.ini_file:
path: "{{ install_conf_path }}"
option: layout
value: uki
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: kernel_install_layout

- name: Configure kernel-install to use the requested initrd generator
community.general.ini_file:
path: "{{ install_conf_path }}"
option: initrd_generator
value: "{{ uki_config_initrd_generator }}"
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: kernel_install_initrd_generator

- name: Configure kernel-install to use the requested UKI generator
community.general.ini_file:
path: "{{ install_conf_path }}"
option: uki_generator
value: ukify
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: kernel_install_uki_generator

- name: Configure ukify to sign generated UKIs
block:
- name: Save original file
ansible.builtin.slurp:
src: "{{ ukify_conf_path }}"
register: ukify_config_backup
changed_when: false
ignore_errors: true

- name: Set singing tool
community.general.ini_file:
path: "{{ ukify_conf_path }}"
section: UKI
option: SecureBootSigningTool
value: sbsign
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: ukify_signing_tool

- name: Set kernel command line
community.general.ini_file:
path: "{{ ukify_conf_path }}"
section: UKI
option: Cmdline
value: "@{{ uki_config_cmdline }}"
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: ukify_kernel_command_line

- name: Set signing private key
community.general.ini_file:
path: "{{ ukify_conf_path }}"
section: UKI
option: SecureBootPrivateKey
value: "{{ uki_config_mok.private_key }}"
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: ukify_singing_private_key

- name: Set signing certificate
community.general.ini_file:
path: "{{ ukify_conf_path }}"
section: UKI
option: SecureBootCertificate
value: "{{ uki_config_mok.certificate }}"
mode: '0644'
owner: root
group: root
seuser: system_u
serole: object_r
setype: etc_t
register: ukify_signing_certificate

- name: Rebuild UKI
when:
kernel_install_layout is changed or
kernel_install_initrd_generator is changed or
kernel_install_uki_generator is changed or
(ukify_signing_tool is defined and ukify_signing_tool is changed) or
(ukify_signing_private_key is defined and ukify_signing_private_key is changed) or
(ukify_signing_certificate is defined and ukify_signing_certificate is changed) or
(ukify_kernel_command_line is defined and ukify_kernel_command_line is changed)
ansible.builtin.command:
argv:
- kernel-install
- --verbose
- add
changed_when: true

rescue:
- name: Restore ukify config
block:
- name: Restore ukify configuration file
when:
- ukify_config_backup is not failed
- ukify_config_backup is not skipped
ansible.builtin.copy:
content: "{{ ukify_config_backup.content | b64decode }}"
dest: "{{ ukify_conf_path }}"
owner: root
group: root
mode: "0644"
seuser: system_u
serole: object_r
setype: etc_t
changed_when: false

- name: Delete new ukify configuration file
when: ukify_config_backup is failed
ansible.builtin.file:
path: "{{ ukify_conf_path }}"
state: absent
changed_when: false

- name: Restore kernel-install config
block:
- name: Restore original kernel-install configuration file
when: kernel_install_config_backup is not failed
ansible.builtin.copy:
content: "{{ kernel_install_config_backup.content | b64decode }}"
dest: "{{ install_conf_path }}"
owner: root
group: root
mode: "0644"
seuser: system_u
serole: object_r
setype: etc_t
changed_when: false

- name: Remove new kernel-install configuration file
when: kernel_install_config_backup is failed
ansible.builtin.file:
path: "{{ install_conf_path }}"
state: absent
changed_when: false
Loading

0 comments on commit 962d32a

Please sign in to comment.