Skip to content

Commit 5fc3f5c

Browse files
madhu-pillaiSuper User
authored andcommitted
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 <madhupillai80@gmail.com>
1 parent 3088328 commit 5fc3f5c

File tree

11 files changed

+254
-3
lines changed

11 files changed

+254
-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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[localhost]
2-
127.0.0.1 ansible_connection=local
2+
127.0.0.1 ansible_connection=local ansible_become_password=

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: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
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+
when: cex_uuid_map is defined
6+
7+
- name: Debug rendered cex_hostdev per compute node
8+
debug:
9+
msg: "VM: {{ vm_name }}, Hostdev: {{ cex_hostdev }}"
10+
vars:
11+
vm_name: "{{ env.cluster.nodes.compute.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.compute.hostname | length) - 1 }}
19+
loop_control:
20+
index_var: i
221

322
- name: Install CoreOS on compute nodes
423
tags: create_compute_nodes
24+
vars:
25+
vm_name: "{{ env.cluster.nodes.compute.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
virsh destroy {{ env.cluster.nodes.compute.vm_name[i] }} || true
732
virsh undefine {{ env.cluster.nodes.compute.vm_name[i] }} --remove-all-storage --nvram || true
@@ -30,7 +55,8 @@
3055
--memballoon none \
3156
--graphics none \
3257
--wait=-1 \
33-
--noautoconsole
58+
--noautoconsole \
59+
{{ cex_hostdev }}
3460
timeout: 360
3561
with_sequence: start=0 end={{ (env.cluster.nodes.compute.hostname | length) - 1 }} stride=1
3662
loop_control:

roles/create_control_nodes/tasks/main.yaml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
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+
when: cex_uuid_map is defined
7+
8+
- name: Debug rendered cex_hostdev per control node
9+
debug:
10+
msg: "VM: {{ vm_name }}, Hostdev: {{ cex_hostdev }}"
11+
vars:
12+
vm_name: "{{ env.cluster.nodes.control.vm_name[i] }}"
13+
cex_hostdev: >-
14+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
15+
--hostdev={{ cex_hostdev_map[vm_name] }}
16+
{% else %}
17+
""
18+
{% endif %}
19+
with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }}
20+
loop_control:
21+
index_var: i
22+
323
- name: Create CoreOS control nodes on the the KVM host.
424
tags: create_control_nodes
25+
vars:
26+
vm_name: "{{ env.cluster.nodes.control.vm_name[i] }}"
27+
cex_hostdev: >-
28+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
29+
--hostdev={{ cex_hostdev_map[vm_name] }}
30+
{% endif %}
531
shell: |
632
virt-install \
733
--name {{ env.cluster.nodes.control.vm_name[i] }} \
@@ -29,7 +55,8 @@
2955
--graphics none \
3056
--console pty,target_type=serial \
3157
--wait=-1 \
32-
--noautoconsole
58+
--noautoconsole \
59+
{{ cex_hostdev }}
3360
timeout: 360
3461
with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }} stride=1
3562
loop_control:
@@ -67,6 +94,7 @@
6794
--graphics none \
6895
--wait=-1 \
6996
--noautoconsole
97+
7098
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
7199

72100
- 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

roles/get_ocp/tasks/main.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,49 @@
129129
- .openshift_install.log
130130
- .openshift_install_state.json
131131

132+
- name: Generate Butane Ignition configs if CEX device is defined
133+
tags: get_ocp
134+
become: true
135+
block:
136+
- name: Generate Butane file for nodes
137+
template:
138+
src: cex-butane-machineconfig.bu.j2
139+
dest: "{{ output_dir }}/99-{{ node_role }}-s390x-cex-luks-config.bu"
140+
loop:
141+
- master
142+
- worker
143+
loop_control:
144+
loop_var: node_role
145+
vars:
146+
luks_device: "{{ butane_default[cex_device].device }}"
147+
layout: "{{ butane_default[cex_device].layout }}"
148+
149+
- name: Download Butane binary for s390x
150+
get_url:
151+
url: https://mirror.openshift.com/pub/openshift-v4/clients/butane/latest/butane-s390x
152+
dest: /usr/local/bin/butane
153+
mode: '0755'
154+
155+
- name: Convert Butane YAML to Ignition for each role
156+
shell: |
157+
/usr/local/bin/butane "{{ output_dir }}/99-{{ item }}-s390x-cex-luks-config.bu" \
158+
-o "{{ output_dir }}/99-{{ item }}-s390x-cex-luks-config.yaml"
159+
loop:
160+
- master
161+
- worker
162+
163+
- name: Copy generated ignition files to final directory
164+
copy:
165+
src: "{{ output_dir }}/99-{{ item }}-s390x-cex-luks-config.yaml"
166+
dest: "/root/ocpinst/openshift/99-{{ item }}-s390x-cex-luks-config.yaml"
167+
remote_src: yes
168+
mode: '0644'
169+
loop:
170+
- master
171+
- worker
172+
when:
173+
- cex | bool
174+
132175
- name: Set ownership of ocpinst directory contents to root
133176
tags: get_ocp
134177
become: true

0 commit comments

Comments
 (0)