Skip to content

Commit e337166

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 e337166

File tree

5 files changed

+174
-13
lines changed

5 files changed

+174
-13
lines changed

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: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@
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+
head -c 32 /dev/urandom | base64 > {{ __trustee_client_secret_key_tempfile.path }}
58+
changed_when: true
59+
when:
60+
- __trustee_client_cryptenroll_check.rc == 0
61+
- not trustee_client_secret_registration_enabled | bool
4662
no_log: true
4763

4864
- name: Encrypt the partition
@@ -51,13 +67,46 @@
5167
parted -s {{ __trustee_client_disk_device }} mklabel gpt
5268
parted -s {{ __trustee_client_disk_device }} mkpart primary ext4 0% 100%
5369
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
70+
cryptsetup open --key-file {{ __trustee_client_secret_key_tempfile.path }} {{ __trustee_client_disk_partition }} encrypted_disk
71+
mkfs.ext4 /dev/mapper/encrypted_disk
5672
[ -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 }}
73+
mount /dev/mapper/encrypted_disk {{ trustee_client_encrypt_disk_mount_point }}
5874
changed_when: true
5975
no_log: true
6076

77+
- name: TPM2 cryptenroll and configure crypttab/fstab
78+
when:
79+
- __trustee_client_cryptenroll_check.rc == 0
80+
- not trustee_client_secret_registration_enabled | bool
81+
block:
82+
- name: Bind TPM2 slot to LUKS volume
83+
ansible.builtin.shell: |
84+
set -o pipefail
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+
no_log: true
88+
89+
- name: Get UUID of the LUKS partition
90+
ansible.builtin.command: "lsblk -dno UUID {{ __trustee_client_disk_partition }}"
91+
register: __luks_uuid
92+
changed_when: false
93+
94+
- name: Configure /etc/crypttab for TPM auto-unlock
95+
ansible.builtin.lineinfile:
96+
path: /etc/crypttab
97+
line: "encrypted_disk UUID={{ __luks_uuid.stdout }} none tpm2-device=auto"
98+
regexp: '^encrypted_disk\s+'
99+
create: true
100+
mode: "0600"
101+
102+
- name: Configure /etc/fstab and mount volume
103+
ansible.builtin.mount:
104+
path: "{{ trustee_client_encrypt_disk_mount_point }}"
105+
src: /dev/mapper/encrypted_disk
106+
fstype: ext4
107+
opts: defaults,x-systemd.requires=systemd-cryptsetup@encrypted_disk.service
108+
state: mounted
109+
61110
- name: Clean up disk encryption key file
62111
ansible.builtin.file:
63112
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" encrypted_disk || die "Failed to open encrypted disk"
65+
mount /dev/mapper/encrypted_disk "$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/encrypted_disk' in
7979
(__test_findmnt.stdout | trim)
8080
fail_msg: >-
81-
Expected /dev/mapper/encrypted-disk to be mounted at
81+
Expected /dev/mapper/encrypted_disk 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/encrypted_disk
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/encrypted_disk 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/encrypted_disk' in (__test_findmnt.stdout | default('') | trim)"
217+
fail_msg: >-
218+
Expected /dev/mapper/encrypted_disk 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 encrypted_disk 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 encrypted_disk with tpm2-device=auto
230+
ansible.builtin.assert:
231+
that:
232+
- "'encrypted_disk' in (__test_crypttab.content | b64decode)"
233+
- "'tpm2-device=auto' in (__test_crypttab.content | b64decode)"
234+
fail_msg: "/etc/crypttab does not contain encrypted_disk 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/encrypted_disk' in (__test_fstab.content | b64decode)"
248+
fail_msg: "/etc/fstab does not contain encrypted disk mount entry"
249+
when: not __test_skip_cryptenroll_assertions

0 commit comments

Comments
 (0)