Skip to content

Commit 9277127

Browse files
committed
feat: Enable CEX based LUKS encryption
1. Configure the CEX in KVM host 2. Use the right device type ignition for MCO 3. Attach the CEX mediated device to guest vm Signed-off-by: Madhu Pillai <[email protected]>
1 parent 68d8afd commit 9277127

23 files changed

+556
-3
lines changed

docs/set-variables-group-vars.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
:--- | :--- | :---
1313
**installation_type** | Can be of type kvm or lpar. Some packages will be ignored for installation in case of non lpar based installation. | kvm
1414
**controller_sudo_pass** | The password to the machine running Ansible (localhost). This will only be used for two things. To ensure you've installed the pre-requisite packages if you're on Linux, and to add the login URL to your /etc/hosts file. | Pas$w0rd!
15+
**cex_device** | Specify the storage device type used for LUKS encryption. This setting determines enable cex MCO Ignition configuration will be applied. Use in combination with the cex parameter. [dasd, fcp, virt]
1516

1617
## 2 - LPAR(s)
1718
**Variable Name** | **Description** | **Example**
@@ -403,3 +404,9 @@
403404
**zvm.interface.ip** | IP addresses for to be used for zVM nodes | 192.168.10.1
404405
**zvm.nodes.dasd.disk_id** | Disk id for dasd disk to be used for zVM node | 4404
405406
**zvm.nodes.lun** | Disk details of fcp disk to be used for zVM node | 840a
407+
408+
## Crypto Express Card based LUKS encryption specific for zKVM ( Optional )
409+
**Variable Name** | **Description** | **Example**
410+
**cex** | Whether to enable cex based luks encryption, default to False
411+
**cex_device** | Specify the storage device type used for LUKS encryption. This setting determines which MCO Ignition configuration will be applied. Use in combination with the cex parameter. | [dasd, fcp, virt]
412+
**cex_uuid_map** | This var is required only for KVM installations using vfio_ap mediated device. Omit it when deploying on LPAR installation. Use in combination with cex and cex_device. Specify guest hostname: "UUID:domain" UUID can be generated from uuidgen command and domain can be retrieved from lszcrypt | upi-cex-control-1: "68cd2d83-3eef-4e45-b22c-534f90b16cb9:00.0035"

inventories/default/group_vars/all.yaml.template

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,16 @@ abi:
250250
# (Optional) Proxy
251251
# Pls check the documentation which vars are present (include examples). If use_proxy is set to true,
252252
# than proxy_http, proxy_https and proxy_no must be set.
253+
254+
255+
# Section 15 - CEX based Luks Encryption ( Optional )
256+
cex: false
257+
# cex_device: [dasd | fcp | virt]
258+
# The following variable is required only when CEX use as vfio_ap mediate device in KVM guest.
259+
# https://www.ibm.com/docs/en/linux-on-systems?topic=management-configuring-crypto-express-adapters-kvm-guests
260+
# cex_uuid_map:
261+
# hostname:UUID:domain
262+
# hostname:UUID:domain
263+
# Provide control and compute hostname with uuid and domain only for KVM based installation.
264+
# UUID can be generated from `uuidgen` command and domain from lszcrypt -V
265+
# eg upi-control-1:5c84eefb-cb45-4519-86d3-ba23e65e8896:12.0001

inventories/default/hosts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
[localhost]
2-
127.0.0.1 ansible_connection=local
2+
127.0.0.1 ansible_connection=local ansible_become_password=
3+
4+
[file_server]
5+
172.23.10.1 ansible_user= ansible_become_password=
6+
7+
[kvm_host]
8+
a3elp53.lnxero1.boe ansible_host=172.23.236.53 ansible_user=root ansible_become_password=pass4root
9+
10+
11+
12+
[bastion]
13+
upi-bastion ansible_host=192.168.122.10 ansible_user=root ansible_become_password=pass4root
14+
15+
[jumphost]
16+
a3elp53.lnxero1.boe ansible_host=172.23.236.53 ansible_user=root ansible_become_password=pass4root

playbooks/3_setup_kvm_host.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,9 @@
188188
roles:
189189
- configure_storage
190190
- { role: macvtap, when: env.network_mode | upper != 'NAT' }
191+
192+
- hosts: kvm_host
193+
tags: setup, section_3
194+
become: true
195+
roles:
196+
- { role: configure_cex, when: cex | bool and cex_uuid_map is defined }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
3+
- name: Set cex_cards from cex_uuid_map values (uuid:domain only)
4+
set_fact:
5+
cex_cards: "{{ cex_uuid_map.values() | list | unique }}"
6+
7+
- name: Debug final list of CEX UUID assignments
8+
debug:
9+
var: cex_cards
10+
11+
- name: Create VFIO assignment script for all CEX cards
12+
template:
13+
src: assign_cards.sh.j2
14+
dest: /tmp/assign_all_cex_cards.sh
15+
mode: '0755'
16+
17+
- name: Execute VFIO assignment script
18+
shell: /tmp/assign_all_cex_cards.sh
19+
args:
20+
executable: /bin/bash
21+
22+
- name: Housekeep temporary assignment script
23+
file:
24+
path: /tmp/assign_all_cex_cards.sh
25+
state: absent
26+
27+
- name: Initialize empty cex_hostdev_map
28+
set_fact:
29+
cex_hostdev_map: {}
30+
31+
- name: Populate cex_hostdev_map with mdev format
32+
set_fact:
33+
cex_hostdev_map: "{{ cex_hostdev_map | combine({ item.key : 'mdev_' + (item.value.split(':')[0] | regex_replace('-', '_')) + '_matrix' }) }}"
34+
loop: "{{ cex_uuid_map | dict2items }}"
35+
36+
37+
- name: Save cex_hostdev_map to a file for reuse
38+
copy:
39+
dest: "/root/.cex_hostdev_map.json"
40+
content: "{{ cex_hostdev_map | to_nice_json }}"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
# Reference document for the cex configuration in zKVM
3+
# https://www.ibm.com/docs/en/linux-on-systems?topic=management-configuring-crypto-express-adapters-kvm-guests
4+
5+
# Configure each CEX card
6+
{% for entry in cex_cards %}
7+
{% set uuid = entry.split(':')[0] %}
8+
{% set matrix_val = entry.split(':')[1] %}
9+
{% set adapter = matrix_val.split('.')[0] %}
10+
{% set domain = matrix_val.split('.')[1] %}
11+
12+
uuid="{{ uuid }}"
13+
matrix_val="{{ matrix_val }}"
14+
adapter="{{ adapter }}"
15+
domain="{{ domain }}"
16+
17+
uuid_path="/sys/devices/vfio_ap/matrix/$uuid"
18+
matrix_file="$uuid_path/matrix"
19+
20+
if [ -d "$uuid_path" ]; then
21+
if grep -q "{{ matrix_val }}" "$matrix_file" 2>/dev/null; then
22+
echo "[INFO] UUID $uuid already configured with matrix {{ matrix_val }} — skipping."
23+
else
24+
echo "[WARN] UUID $uuid exists, but matrix entry '{{ matrix_val }}' not found!"
25+
echo "[WARN] Please reboot the node and try again."
26+
exit 1
27+
fi
28+
else
29+
modprobe vfio_ap
30+
echo 0x0 > /sys/bus/ap/apmask
31+
echo 0x0 > /sys/bus/ap/aqmask
32+
echo "[INFO] Creating UUID $uuid with adapter $adapter and domain $domain"
33+
echo "$uuid" > /sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/create
34+
echo "0x$adapter" > "$uuid_path/assign_adapter"
35+
echo "0x$domain" > "$uuid_path/assign_domain"
36+
fi
37+
38+
{% endfor %}

roles/create_compute_nodes/tasks/main.yaml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
11
---
2+
- name: Load cex_hostdev_map from JSON
3+
set_fact:
4+
cex_hostdev_map: "{{ lookup('file', '/root/.cex_hostdev_map.json') | from_json }}"
5+
6+
- name: Debug rendered cex_hostdev per compute node
7+
debug:
8+
msg: "VM: {{ vm_name }}, Hostdev: {{ cex_hostdev }}"
9+
vars:
10+
vm_name: "{{ env.cluster.nodes.compute.vm_name[i] }}"
11+
cex_hostdev: >-
12+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
13+
--hostdev={{ cex_hostdev_map[vm_name] }}
14+
{% else %}
15+
""
16+
{% endif %}
17+
with_sequence: start=0 end={{ (env.cluster.nodes.compute.hostname | length) - 1 }}
18+
loop_control:
19+
index_var: i
220

321
- name: Install CoreOS on compute nodes
422
tags: create_compute_nodes
23+
vars:
24+
vm_name: "{{ env.cluster.nodes.compute.vm_name[i] }}"
25+
cex_hostdev: >-
26+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
27+
--hostdev={{ cex_hostdev_map[vm_name] }}
28+
{% endif %}
529
shell: |
630
virsh destroy {{ env.cluster.nodes.compute.vm_name[i] }} || true
731
virsh undefine {{ env.cluster.nodes.compute.vm_name[i] }} --remove-all-storage --nvram || true
@@ -30,7 +54,8 @@
3054
--memballoon none \
3155
--graphics none \
3256
--wait=-1 \
33-
--noautoconsole
57+
--noautoconsole \
58+
{{ cex_hostdev }}
3459
timeout: 360
3560
with_sequence: start=0 end={{ (env.cluster.nodes.compute.hostname | length) - 1 }} stride=1
3661
loop_control:

roles/create_control_nodes/tasks/main.yaml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
11
---
22

3+
- name: Load cex_hostdev_map from JSON file
4+
set_fact:
5+
cex_hostdev_map: "{{ lookup('file', '/root/.cex_hostdev_map.json') | from_json }}"
6+
7+
- name: Debug rendered cex_hostdev per control node
8+
debug:
9+
msg: "VM: {{ vm_name }}, Hostdev: {{ cex_hostdev }}"
10+
vars:
11+
vm_name: "{{ env.cluster.nodes.control.vm_name[i] }}"
12+
cex_hostdev: >-
13+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
14+
--hostdev={{ cex_hostdev_map[vm_name] }}
15+
{% else %}
16+
""
17+
{% endif %}
18+
with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }}
19+
loop_control:
20+
index_var: i
21+
322
- name: Create CoreOS control nodes on the the KVM host.
423
tags: create_control_nodes
24+
vars:
25+
vm_name: "{{ env.cluster.nodes.control.vm_name[i] }}"
26+
cex_hostdev: >-
27+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
28+
--hostdev={{ cex_hostdev_map[vm_name] }}
29+
{% endif %}
530
shell: |
631
virt-install \
732
--name {{ env.cluster.nodes.control.vm_name[i] }} \
@@ -29,7 +54,8 @@
2954
--graphics none \
3055
--console pty,target_type=serial \
3156
--wait=-1 \
32-
--noautoconsole
57+
--noautoconsole \
58+
{{ cex_hostdev }}
3359
timeout: 360
3460
with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }} stride=1
3561
loop_control:
@@ -67,6 +93,7 @@
6793
--graphics none \
6894
--wait=-1 \
6995
--noautoconsole
96+
7097
when: env.z.high_availability == True and inventory_hostname == env.z.lpar1.hostname and env.cluster.nodes.control.vm_name[0] not in hosts_with_host_vars
7198

7299
- name: Create the second CoreOS control node on the second KVM host, if cluster is to be highly available.

roles/get_ocp/defaults/main.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,18 @@ use_proxy: false
3232
proxy_http:
3333
proxy_https:
3434
proxy_no:
35+
36+
# (Optional) CEX Ignition specific
37+
# Default mappings based on cex_device
38+
output_dir: "/tmp"
39+
butane_default:
40+
version: 4.19.0
41+
dasd:
42+
layout: s390x-eckd
43+
device: /dev/dasd
44+
virt:
45+
layout: s390x-virt
46+
device: /dev/disk/by-partlabel/root
47+
fcp:
48+
layout: s390x-fcp # or s390x-fcp if needed
49+
device: /dev/disk/by-label/root
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
variant: openshift
2+
version: 4.19.0
3+
metadata:
4+
name: master-luks-storage
5+
labels:
6+
machineconfiguration.openshift.io/role: master
7+
openshift:
8+
fips: true
9+
kernel_arguments:
10+
- rd.luks.key=/etc/luks/cex.key
11+
boot_device:
12+
layout: s390x-eckd
13+
luks:
14+
device: /dev/dasda
15+
cex:
16+
enabled: true

0 commit comments

Comments
 (0)