Skip to content

Commit c446cfd

Browse files
committed
feat(encrypt_disk): add systemd-cryptenroll option
When Secret Registration Client is not enabled, allow disk encryption with systemd-cryptenroll. This binds disk encryption with TPM PCR7. Use crypttab and fstab for auto mount. Signed-off-by: Li Tian <litian@redhat.com>
1 parent 589e35d commit c446cfd

6 files changed

Lines changed: 184 additions & 13 deletions

File tree

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@ When enabled, this task:
6666
6767
## Encrypt Disk
6868
69-
When enabled, this task:
69+
An unpartitioned empty disk must be attached to the target. When enabled, this task:
7070
7171
1. Finds the first unpartitioned and unmounted disk
72-
2. Requests disk encryption key from Secret Registration Client
73-
3. Encrypts the disk using above encryption key and mounts it at the designated path
72+
2. Encrypts the disk using a key from either:
73+
a. secret key fetched using Secret Registration Client (when enabled), or
74+
b. `systemd-cryptenroll` which binds to PCR 7
75+
3. Mounts it at the designated path
76+
4. Sets up automatic unlock and mount either with Secret Registration Client service or /etc/crypttab with `systemd-cryptenroll`
7477

7578
## License
7679

tasks/encrypt_disk.yml

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@
4343
ansible.builtin.shell: |
4444
/usr/local/bin/secret_registration_client.sh --fetch-key-to {{ __trustee_client_secret_key_tempfile.path }}
4545
changed_when: true
46+
when: trustee_client_secret_registration_enabled | bool
47+
no_log: true
48+
49+
- name: Check systemd-cryptenroll command exists
50+
ansible.builtin.command: command -v systemd-cryptenroll
51+
register: __trustee_client_cryptenroll_check
52+
changed_when: false
53+
when: not trustee_client_secret_registration_enabled | bool
54+
55+
- name: Create a random disk encryption key
56+
ansible.builtin.shell: |
57+
set -o pipefail
58+
head -c 32 /dev/urandom | base64 > {{ __trustee_client_secret_key_tempfile.path }}
59+
changed_when: true
60+
when:
61+
- __trustee_client_cryptenroll_check is success
62+
- not trustee_client_secret_registration_enabled | bool
4663
no_log: true
4764

4865
- name: Encrypt the partition
@@ -51,13 +68,54 @@
5168
parted -s {{ __trustee_client_disk_device }} mklabel gpt
5269
parted -s {{ __trustee_client_disk_device }} mkpart primary ext4 0% 100%
5370
cryptsetup luksFormat --key-file {{ __trustee_client_secret_key_tempfile.path }} --batch-mode {{ __trustee_client_disk_partition }}
54-
cryptsetup open --key-file {{ __trustee_client_secret_key_tempfile.path }} {{ __trustee_client_disk_partition }} encrypted-disk
55-
mkfs.ext4 /dev/mapper/encrypted-disk
71+
cryptsetup open --key-file {{ __trustee_client_secret_key_tempfile.path }} {{ __trustee_client_disk_partition }} trustee_client_encrypted_disk_0
72+
mkfs.ext4 /dev/mapper/trustee_client_encrypted_disk_0
5673
[ -d {{ trustee_client_encrypt_disk_mount_point }} ] || mkdir -p {{ trustee_client_encrypt_disk_mount_point }}
57-
mount /dev/mapper/encrypted-disk {{ trustee_client_encrypt_disk_mount_point }}
74+
mount /dev/mapper/trustee_client_encrypted_disk_0 {{ trustee_client_encrypt_disk_mount_point }}
5875
changed_when: true
5976
no_log: true
6077

78+
- name: TPM2 cryptenroll and configure crypttab/fstab
79+
when:
80+
- __trustee_client_cryptenroll_check is success
81+
- not trustee_client_secret_registration_enabled | bool
82+
block:
83+
- name: Bind TPM2 slot to LUKS volume
84+
ansible.builtin.shell: |
85+
systemd-cryptenroll {{ __trustee_client_disk_partition }} --tpm2-device=auto --tpm2-pcrs=7 --unlock-key-file={{ __trustee_client_secret_key_tempfile.path }}
86+
systemd-cryptenroll {{ __trustee_client_disk_partition }} --wipe-slot=password
87+
changed_when: true
88+
no_log: true
89+
90+
- name: Get UUID of the LUKS partition
91+
ansible.builtin.command: "lsblk -dno UUID {{ __trustee_client_disk_partition }}"
92+
register: __luks_uuid
93+
changed_when: false
94+
95+
- name: Retrieve facts for the /etc/crypttab file
96+
stat:
97+
path: /etc/crypttab
98+
register: __trustee_client_crypttab
99+
changed_when: false
100+
101+
- name: Configure /etc/crypttab for TPM auto-unlock
102+
ansible.builtin.lineinfile:
103+
path: /etc/crypttab
104+
line: "trustee_client_encrypted_disk_0 UUID={{ __luks_uuid.stdout }} none tpm2-device=auto"
105+
regexp: '^trustee_client_encrypted_disk_0\s+'
106+
create: true
107+
mode: "{{ __trustee_client_crypttab.stat.mode | d('0600') }}"
108+
owner: "{{ __trustee_client_crypttab.stat.pw_name | d('root') }}"
109+
group: "{{ __trustee_client_crypttab.stat.gr_name | d('root') }}"
110+
111+
- name: Configure /etc/fstab and mount volume
112+
ansible.builtin.mount:
113+
path: "{{ trustee_client_encrypt_disk_mount_point }}"
114+
src: /dev/mapper/trustee_client_encrypted_disk_0
115+
fstype: ext4
116+
opts: defaults,x-systemd.requires=systemd-cryptsetup@trustee_client_encrypted_disk0.service
117+
state: mounted
118+
61119
- name: Clean up disk encryption key file
62120
ansible.builtin.file:
63121
path: "{{ __trustee_client_secret_key_tempfile.path }}"

tasks/main.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
include_tasks: encrypt_disk.yml
1818
when:
1919
- trustee_client_encrypt_disk | bool
20-
- trustee_client_secret_registration_enabled | bool
2120

2221
- name: Flush handlers to ensure services are started
2322
ansible.builtin.meta: flush_handlers

templates/secret_registration_client.sh.j2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ mount_disk() {
6161
trap 'rm -f "$key_path"' EXIT
6262
fetch_key "$key_path"
6363
mkdir -p "$MOUNT_POINT" || die "Failed to create mount point directory"
64-
cryptsetup open --key-file "$key_path" "$disk_partition" encrypted-disk || die "Failed to open encrypted disk"
65-
mount /dev/mapper/encrypted-disk "$MOUNT_POINT" || die "Failed to mount encrypted disk"
64+
cryptsetup open --key-file "$key_path" "$disk_partition" trustee_client_encrypted_disk_0 || die "Failed to open encrypted disk"
65+
mount /dev/mapper/trustee_client_encrypted_disk_0 "$MOUNT_POINT" || die "Failed to mount encrypted disk"
6666
log "Mounted encrypted disk at $MOUNT_POINT"
6767
}
6868

tests/tests_encrypt_disk.yml

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,25 @@
7575
ansible.builtin.assert:
7676
that:
7777
- >-
78-
'/dev/mapper/encrypted-disk' in
78+
'/dev/mapper/trustee_client_encrypted_disk_0' in
7979
(__test_findmnt.stdout | trim)
8080
fail_msg: >-
81-
Expected /dev/mapper/encrypted-disk to be mounted at
81+
Expected /dev/mapper/trustee_client_encrypted_disk_0 to be mounted at
8282
{{ trustee_client_encrypt_disk_mount_point }} but found:
8383
{{ __test_findmnt.stdout }}
8484
when: not __test_skip_encrypt_assertions
8585

8686
- name: Stat the LUKS mapper device
8787
ansible.builtin.stat:
88-
path: /dev/mapper/encrypted-disk
88+
path: /dev/mapper/trustee_client_encrypted_disk_0
8989
register: __test_mapper_dev
9090
when: not __test_skip_encrypt_assertions
9191

9292
- name: Assert LUKS mapper device exists
9393
ansible.builtin.assert:
9494
that:
9595
- __test_mapper_dev.stat.exists
96-
fail_msg: "LUKS mapper device /dev/mapper/encrypted-disk does not exist"
96+
fail_msg: "LUKS mapper device /dev/mapper/trustee_client_encrypted_disk_0 does not exist"
9797
when: not __test_skip_encrypt_assertions
9898

9999
- name: Assert encrypted_disk_key fact was set
@@ -137,3 +137,113 @@
137137
/etc/containers/storage.conf does not reference the encrypted
138138
disk mount point {{ trustee_client_encrypt_disk_mount_point }}
139139
when: not __test_skip_encrypt_assertions
140+
141+
- name: Ensure disk encryption works with systemd-cryptenroll when secret_registration_client is disabled
142+
hosts: all
143+
gather_facts: false
144+
vars:
145+
trustee_client_trustee_gc: false
146+
trustee_client_encrypt_disk: true
147+
trustee_client_secret_registration_enabled: false
148+
trustee_client_encrypt_disk_mount_point: /mnt/encrypted-disk
149+
tasks:
150+
- name: Check for an unpartitioned disk device
151+
ansible.builtin.shell: |
152+
set -o pipefail
153+
lsblk -n -o NAME,TYPE,PKNAME | awk '
154+
$2=="disk" && $1 !~ /^zram|^loop|^dm/ { disk=$1; haspart[disk]=0 }
155+
$2=="part" { parent=$3; if (parent in haspart) haspart[parent]=1 }
156+
END {
157+
for (d in haspart) {
158+
if (haspart[d] == 0) {
159+
print d
160+
exit 0
161+
}
162+
}
163+
}
164+
'
165+
register: __test_unpartitioned_disk
166+
changed_when: false
167+
failed_when: false
168+
169+
- name: Set fact when no unpartitioned disk is available
170+
ansible.builtin.set_fact:
171+
__test_skip_cryptenroll_assertions: "{{ __test_unpartitioned_disk.stdout | trim == '' }}"
172+
173+
- name: Check systemd-cryptenroll exists
174+
ansible.builtin.command: type systemd-cryptenroll
175+
register: __test_cryptenroll_check
176+
changed_when: false
177+
failed_when: false
178+
when: not __test_skip_cryptenroll_assertions
179+
180+
- name: Set fact when systemd-cryptenroll is not available
181+
ansible.builtin.set_fact:
182+
__test_skip_cryptenroll_assertions: "{{ __test_skip_cryptenroll_assertions or __test_cryptenroll_check.rc != 0 }}"
183+
when: not __test_skip_cryptenroll_assertions
184+
185+
- name: Run trustee_client role with disk encryption (cryptenroll path)
186+
ansible.builtin.include_role:
187+
name: linux-system-roles.trustee_client
188+
when: not __test_skip_cryptenroll_assertions
189+
190+
- name: Stat the encrypted disk mount point
191+
ansible.builtin.stat:
192+
path: "{{ trustee_client_encrypt_disk_mount_point }}"
193+
register: __test_mount_point
194+
when: not __test_skip_cryptenroll_assertions
195+
196+
- name: Assert mount point directory exists
197+
ansible.builtin.assert:
198+
that:
199+
- __test_mount_point.stat.exists
200+
- __test_mount_point.stat.isdir
201+
fail_msg: >-
202+
Encrypted disk mount point
203+
{{ trustee_client_encrypt_disk_mount_point }} does not exist
204+
when: not __test_skip_cryptenroll_assertions
205+
206+
- name: Assert the encrypted disk is mounted
207+
ansible.builtin.command: findmnt --noheadings --output SOURCE {{ trustee_client_encrypt_disk_mount_point }}
208+
register: __test_findmnt
209+
changed_when: false
210+
when: not __test_skip_cryptenroll_assertions
211+
212+
- name: Assert the mounted device is the LUKS mapper device
213+
ansible.builtin.assert:
214+
that:
215+
- __test_findmnt.rc == 0
216+
- "'/dev/mapper/trustee_client_encrypted_disk_0' in (__test_findmnt.stdout | default('') | trim)"
217+
fail_msg: >-
218+
Expected /dev/mapper/trustee_client_encrypted_disk_0 in the output of findmnt to be mounted at
219+
{{ trustee_client_encrypt_disk_mount_point }} but found:
220+
{{ __test_findmnt.stdout | default('') }}
221+
when: not __test_skip_cryptenroll_assertions
222+
223+
- name: Assert crypttab contains trustee_client_encrypted_disk_0 entry
224+
ansible.builtin.slurp:
225+
src: /etc/crypttab
226+
register: __test_crypttab
227+
when: not __test_skip_cryptenroll_assertions
228+
229+
- name: Verify crypttab has trustee_client_encrypted_disk_0 with tpm2-device=auto
230+
ansible.builtin.assert:
231+
that:
232+
- "'trustee_client_encrypted_disk_0' in (__test_crypttab.content | b64decode)"
233+
- "'tpm2-device=auto' in (__test_crypttab.content | b64decode)"
234+
fail_msg: "/etc/crypttab does not contain trustee_client_encrypted_disk_0 entry with tpm2-device=auto"
235+
when: not __test_skip_cryptenroll_assertions
236+
237+
- name: Assert fstab contains encrypted disk mount
238+
ansible.builtin.slurp:
239+
src: /etc/fstab
240+
register: __test_fstab
241+
when: not __test_skip_cryptenroll_assertions
242+
243+
- name: Verify fstab has encrypted disk mount point
244+
ansible.builtin.assert:
245+
that:
246+
- trustee_client_encrypt_disk_mount_point in (__test_fstab.content | b64decode)
247+
- "'/dev/mapper/trustee_client_encrypted_disk_0' in (__test_fstab.content | b64decode)"
248+
fail_msg: "/etc/fstab does not contain encrypted disk mount entry"
249+
when: not __test_skip_cryptenroll_assertions

tests/tests_kbs_config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
ansible.builtin.copy:
110110
content: "{{ __test_cert_content }}"
111111
dest: "{{ playbook_dir }}/kbs_test_cert.crt"
112+
mode: "0644"
112113
run_once: true
113114
delegate_to: localhost
114115

0 commit comments

Comments
 (0)