Skip to content

Commit

Permalink
- Adding support for UEFI
Browse files Browse the repository at this point in the history
- Adding support for custom %post sections
- Adding support for adding users
- Adding support to enable FIPS in the custom ISO
  • Loading branch information
sscheib committed Oct 3, 2023
1 parent 7539910 commit 8102ea0
Show file tree
Hide file tree
Showing 13 changed files with 687 additions and 67 deletions.
262 changes: 219 additions & 43 deletions README.md

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ _def_pxelinux_cfg_path: 'isolinux/isolinux.cfg'
# relative path within the temporary_work_dir_source_files_path to the grub.conf file
_def_grub_cfg_path: 'isolinux/grub.conf'

# relative path within temporary_work_dir_source_files_path to the UEFI grub.conf file
_def_grub_cfg_path_uefi: 'EFI/BOOT/grub.cfg'

# whether to clean up the downloaded ISO
_def_cleanup_iso: false

Expand Down Expand Up @@ -84,5 +87,22 @@ _def_implant_md5: true
# package name that provides the implantisomd5 command
_def_implantisomd5_package_name: 'isomd5sum'

# relative path within the temporary_work_dir_source_files_path to the UEFI image file
_def_uefi_image_path: 'images/efiboot.img'

# whether to quiet asserts
_def_quiet_assert: true

# whether to enable FIPS mode for the ISO
_def_enable_fips: false

# post sections to insert into the kickstart file
_def_post_sections:
- name: 'User creation'
template: 'post__users.j2'

- name: 'FIPS'
template: 'post__fips.j2'

- name: 'Autorelabel'
template: 'post__autorelabel.j2'
152 changes: 151 additions & 1 deletion tasks/assert.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
- lookup('ansible.builtin.vars', item) is defined
- lookup('ansible.builtin.vars', item) | bool | string == lookup('ansible.builtin.vars', item) | string
- lookup('ansible.builtin.vars', item) is boolean
- lookup('ansible.builtin.vars', item) | type_debug == 'bool'
success_msg: "Variable '{{ item }}' defined properly - value: '{{ lookup('ansible.builtin.vars', item) }}'"
fail_msg: "Variable '{{ item }}' failed to validate"
quiet: '{{ _quiet_assert }}'
Expand All @@ -18,6 +17,7 @@
- '_cleanup_work_dir'
- '_force_recreate_custom_iso'
- '_implant_md5'
- '_enable_fips'
loop_control:
label: 'variable: {{ item }}'

Expand Down Expand Up @@ -114,3 +114,153 @@
fail_msg: "Variable '_api_token' failed to validate"
quiet: '{{ _quiet_assert }}'
no_log: true

- name: 'Ensure optional variables, are defined properly, if set (list)'
ansible.builtin.assert:
that:
- lookup('ansible.builtin.vars', __var) is defined
- lookup('ansible.builtin.vars', __var) | list | string == lookup('ansible.builtin.vars', __var) | string
- lookup('ansible.builtin.vars', __var) is sequence
success_msg: "Variable '{{ __var }}' defined properly"
fail_msg: "Variable '{{ __var }}' failed to validate"
quiet: '{{ _quiet_assert }}'
no_log: true
when: >
lookup('ansible.builtin.vars', __var, default='') != '' and
lookup('ansible.builtin.vars', __var, default='') | length > 0
loop:
- '_users'
- '_post_sections'
register: '__tmp_list_variables'
loop_control:
loop_var: '__var'
label: 'variable: {{ __var }}'

- name: 'Ensure post_sections are defined properly'
ansible.builtin.assert:
that:
- _section.name is defined
- _section.name is string
- _section.name != None
- _section.name != ''

- _section.template is defined
- _section.template is string
- _section.template != None
- _section.template != ''

# load the template to see if it exists
- lookup('template', _section.template) | length > 0

loop: '{{ _post_sections }}'
loop_control:
loop_var: '_section'
when: >
_post_sections is defined
and _post_sections | length > 0
- name: 'Ensure _users is defined properly'
ansible.builtin.assert:
that:
# _user.name
- _user.name is defined
- _user.name is string
- _user.name != None
- _user.name != ''

# _user.gecos
- (_user.gecos is defined) | ternary(_user.gecos | default(None) is string, true)
- (_user.gecos is defined) | ternary(_user.gecos | default(None) != None, true)
- (_user.gecos is defined) | ternary(_user.gecos | default(None) != '', true)

# _user.shell
- (_user.shell is defined) | ternary(_user.shell | default(None) is string, true)
- (_user.shell is defined) | ternary(_user.shell | default(None) != None, true)
- (_user.shell is defined) | ternary(_user.shell | default(None) != '', true)

# _user.home
- (_user.home is defined) | ternary(_user.home | default(None) is string, true)
- (_user.home is defined) | ternary(_user.home | default(None) != None, true)
- (_user.home is defined) | ternary(_user.home | default(None) != '', true)

# _user.password
- (_user.password is defined) | ternary(_user.password | default(None) is string, true)
- (_user.password is defined) | ternary(_user.password | default(None) != None, true)
- (_user.password is defined) | ternary(_user.password | default(None) != '', true)

# _user.gid
- (_user.gid is defined) | ternary(_user.gid | default(None) | int | string == _user.gid | default(None) | string, true)
- (_user.gid is defined) | ternary(_user.gid | default(None) is number, true)
- (_user.gid is defined) | ternary(_user.gid | default(None) is integer, true)
- (_user.gid is defined) | ternary(_user.gid | default(None) >= 1, true)

# _user.uid
- (_user.uid is defined) | ternary(_user.uid | default(None) | int | string == _user.uid | default(None) | string, true)
- (_user.uid is defined) | ternary(_user.uid | default(None) is number, true)
- (_user.uid is defined) | ternary(_user.uid | default(None) is integer, true)
- (_user.uid is defined) | ternary(_user.uid | default(None) >= 1, true)

# _user.create_user_group
- (_user.create_user_group is defined) | ternary(_user.create_user_group | default(None) | bool | string == _user.create_user_group | default(None) | string, true)
- (_user.create_user_group is defined) | ternary(_user.create_user_group | default(None) is boolean, true)

# _user.lock
- (_user.lock is defined) | ternary(_user.lock | default(None) | bool | string == _user.lock | default(None) | string, true)
- (_user.lock is defined) | ternary(_user.lock | default(None) is boolean, true)

# _user.privileged
- (_user.privileged is defined) | ternary(_user.privileged | default(None) | bool | string == _user.privileged | default(None) | string, true)
- (_user.privileged is defined) | ternary(_user.privileged | default(None) is boolean, true)

# _user.groups
- (_user.groups is defined) | ternary(_user.groups | default([]) | list | string == _user.groups | default(None) | string, true)
- (_user.groups is defined) | ternary(_user.groups | default([]) is sequence, true)

# _user.authorized_keys
- (_user.authorized_keys is defined) | ternary(_user.authorized_keys | default([]) | list | string == _user.authorized_keys | default(None) | string, true)
- (_user.authorized_keys is defined) | ternary(_user.authorized_keys | default([]) is sequence, true)
success_msg: 'Users are defined correctly'
fail_msg: 'One or more users failed to validated correctly'
no_log: true
loop: '{{ _users }}'
loop_control:
loop_var: '_user'
when: >
_users is defined
and _users | length > 0
- name: 'Ensure _users does not specify conflicting options'
ansible.builtin.assert:
that:
- _user.home is defined
- _user.home is string
- _user.home != ''
- _user.home != None
success_msg: 'No conflicting options specified'
fail_msg: 'A user needs a home directory when authorized keys are specified'
no_log: true
loop: '{{ _users }}'
loop_control:
loop_var: '_user'
when: >
_user.authorized_keys is defined
and _user.authorized_keys | length > 0
- name: 'Skip block if no variables defined beforehand'
when: >
__tmp_list_variables.results is defined and
__tmp_list_variables.results | map(attribute='skipped', default=[]) | select() | length > 0
block:

- name: 'Show variables that have been skipped to check, due to being undefined'
ansible.builtin.debug:
msg: 'Variable name: {{ __var }}'
loop:
- "{{ __tmp_list_variables['results'] | default([]) }}"
loop_control:
loop_var: '__var'
label: '{{ __var }}'

- name: 'Ensure above variables are not important to you, as they are not going to be used!'
ansible.builtin.pause:
seconds: 5
74 changes: 74 additions & 0 deletions tasks/build_user_statement.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
- name: 'Set fact: Start building the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ 'user --name=' ~ _user.name }}"

- name: 'Set fact: Insert gecos into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --gecos=\"' ~ _user.gecos ~ '\"' }}"
when: >
_user.gecos is defined
and _user.gecos != ''
and _user.gecos != None
- name: 'Set fact: Insert uid into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --uid=' ~ _user.uid }}"
when: >
_user.uid is defined
and _user.uid | string != ''
- name: 'Set fact: Insert gid into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --gid=' ~ _user.gid }}"
when: >
_user.gid is defined
and _user.gid | string != ''
- name: 'Set fact: Insert groups into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --groups=' ~ _user.groups | join(',') }}"
when: >
_user.groups is defined
and _user.groups | string != ''
- name: 'Set fact: Insert homedir into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --homedir=' ~ _user.home }}"
when: >
_user.home is defined
and _user.home | string != ''
- name: 'Set fact: Insert shell into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --shell=' ~ _user.shell }}"
when: >
_user.shell is defined
and _user.shell | string != ''
- name: 'Set fact: Insert lock into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: "{{ _user_statement ~ ' --lock' }}"
when: >
_user.lock is defined
and _user.lock
- name: 'Set fact: Insert password into the user statement for user {{ _user.name }}'
ansible.builtin.set_fact:
_user_statement: >
{{
_user_statement ~ ' --iscrypted --password=' ~
_user.password | string | ansible.builtin.password_hash(hashtype='sha512')
}}
no_log: true
when: >
_user.gid is defined
and _user.gid | string != ''
- name: 'Insert user creation statement into the provided kickstart for user {{ _user.name }}'
ansible.builtin.lineinfile:
path: '{{ __work_dir_kickstart_path }}'
regex: '^user\s--name={{ _user.name }}.+$'
line: '{{ _user_statement }}'
no_log: true
become: true
23 changes: 17 additions & 6 deletions tasks/create_iso.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
- name: 'Ensure xorriso is present'
ansible.builtin.package:
name: '{{ _xorriso_package_name }}'
name: '{{ _xorriso_package_name }},syslinux'
state: 'present'
become: true

Expand Down Expand Up @@ -29,6 +29,9 @@
ansible.builtin.set_fact:
__iso_label: '{{ __t_label.stdout }}'

# Note: Adding '-no-emul-boot' *twice* is necessary to avoid the following issue:
# https://unix.stackexchange.com/questions/491043/boot-grub-efi-img-invalid-image-size
# Command has been built according to https://access.redhat.com/solutions/60959
- name: 'Create the ISO with the included kickstart at: {{ __dest_iso_path }}'
ansible.builtin.command:
argv:
Expand All @@ -37,27 +40,35 @@
- '{{ __dest_iso_path }}'
- '-b'
- '{{ _isolinux_bin_path }}'
- '-c'
- '{{ _boot_cat_path }}'
- '-J'
- '-R'
- '-l'
- '-v'
- '-c'
- '{{ _boot_cat_path }}'
- '-no-emul-boot'
- '-boot-load-size'
- '4'
- '-boot-info-table'
- '-eltorito-alt-boot'
- '-e'
- '{{ _uefi_image_path }}'
- '-no-emul-boot'
- '-graft-points'
- '-joliet-long'
- '-V'
- '{{ __iso_label }}'
- '-volid'
- '{{ __iso_label }}'
- '{{ __src_files_path }}'
creates: '{{ __dest_iso_path }}'
become: true

- name: 'Ensure ISO is bootable via BIOS and UEFI' # noqa: no-changed-when
ansible.builtin.command:
argv:
- 'isohybrid'
- '--uefi'
- '{{ __dest_iso_path }}'
become: true

- name: 'Block: Handle implanting of MD5 into ISO'
become: true
when: >
Expand Down
1 change: 1 addition & 0 deletions tasks/download_iso.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@
mode: '{{ _iso_mode }}'
checksum: 'sha256:{{ _checksum }}'
timeout: '{{ _download_timeout }}'
become: true
29 changes: 29 additions & 0 deletions tasks/fips.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
- name: 'Update kernel parameters to enable FIPS compliant cryptography (MD5 implanting requested)'
ansible.builtin.lineinfile:
path: '{{ _cfg_path }}'
regexp: '^(.+)(hd:LABEL[A-z0-9_=-]+)(\sinst\.ks=\2:/ks\.cfg)?(\srd.live.check\squiet)(\sfips=1)?$'
line: '\1\2 inst.ks=\2:/ks.cfg\4 fips=1'
backrefs: true
become: true
loop:
- '{{ __src_files_path }}/{{ _pxelinux_cfg_path }}' # BIOS
- '{{ __src_files_path }}/{{ _grub_cfg_path_uefi }}' # UEFI
loop_control:
loop_var: '_cfg_path'
when: >
_implant_md5 is defined
and _implant_md5
- name: 'BIOS/UEFI: Update kernel parameters to enable FIPS compliant cryptography'
ansible.builtin.lineinfile:
path: '{{ _cfg_path }}'
regexp: '^(.+)(hd:LABEL[A-z0-9_=-]+)(\sinst\.ks=\2:/ks\.cfg)?(\squiet)(\sfips=1)?$'
line: '\1\2 inst.ks=\2:/ks.cfg\4 fips=1'
backrefs: true
become: true
loop:
- '{{ __src_files_path }}/{{ _pxelinux_cfg_path }}' # BIOS
- '{{ __src_files_path }}/{{ _grub_cfg_path_uefi }}' # UEFI
loop_control:
loop_var: '_cfg_path'
Loading

0 comments on commit 8102ea0

Please sign in to comment.