Skip to content

Commit 616c630

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 e7de9db commit 616c630

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**
@@ -409,3 +410,9 @@
409410
**zvm.interface.ip** | IP addresses for to be used for zVM nodes | 192.168.10.1
410411
**zvm.nodes.dasd.disk_id** | Disk id for dasd disk to be used for zVM node | 4404
411412
**zvm.nodes.lun** | Disk details of fcp disk to be used for zVM node | 840a
413+
414+
## Crypto Express Card based LUKS encryption specific for zKVM ( Optional )
415+
**Variable Name** | **Description** | **Example**
416+
**cex** | Whether to enable cex based luks encryption, default to False
417+
**cex_device** | Specify the storage device type used for LUKS encryption. This setting determines which MCO Ignition configuration will be applied from the defaults. Do not override the default value. Use in combination with the cex parameter. | [dasd, fcp, virt]
418+
**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,4 +1,23 @@
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: 'Include matching lpar yml file'
423
tags: create_teuthology_node
@@ -7,6 +26,12 @@
726

827
- name: Install CoreOS on compute nodes
928
tags: create_compute_nodes
29+
vars:
30+
vm_name: "{{ env.cluster.nodes.compute.vm_name[i] }}"
31+
cex_hostdev: >-
32+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
33+
--hostdev={{ cex_hostdev_map[vm_name] }}
34+
{% endif %}
1035
shell: |
1136
virsh destroy {{ env.cluster.nodes.compute.vm_name[i] }} || true
1237
virsh undefine {{ env.cluster.nodes.compute.vm_name[i] }} --remove-all-storage --nvram || true
@@ -35,7 +60,8 @@
3560
--memballoon none \
3661
--graphics none \
3762
--wait=-1 \
38-
--noautoconsole
63+
--noautoconsole \
64+
{{ cex_hostdev }}
3965
timeout: 360
4066
with_sequence: start=0 end={{ (env.cluster.nodes.compute.hostname | length) - 1 }} stride=1
4167
loop_control:

roles/create_control_nodes/tasks/main.yaml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,34 @@
55
ansible.builtin.include_vars:
66
file: "{{ inventory_dir }}/host_vars/{{ inventory_hostname }}.yaml"
77

8+
- name: Load cex_hostdev_map from JSON file
9+
set_fact:
10+
cex_hostdev_map: "{{ lookup('file', '/root/.cex_hostdev_map.json') | from_json }}"
11+
when: cex_uuid_map is defined
12+
13+
- name: Debug rendered cex_hostdev per control node
14+
debug:
15+
msg: "VM: {{ vm_name }}, Hostdev: {{ cex_hostdev }}"
16+
vars:
17+
vm_name: "{{ env.cluster.nodes.control.vm_name[i] }}"
18+
cex_hostdev: >-
19+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
20+
--hostdev={{ cex_hostdev_map[vm_name] }}
21+
{% else %}
22+
""
23+
{% endif %}
24+
with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }}
25+
loop_control:
26+
index_var: i
27+
828
- name: Create CoreOS control nodes on the the KVM host.
929
tags: create_control_nodes
30+
vars:
31+
vm_name: "{{ env.cluster.nodes.control.vm_name[i] }}"
32+
cex_hostdev: >-
33+
{% if cex and cex_device is defined and cex_hostdev_map is defined and vm_name in cex_hostdev_map %}
34+
--hostdev={{ cex_hostdev_map[vm_name] }}
35+
{% endif %}
1036
shell: |
1137
virt-install \
1238
--name {{ env.cluster.nodes.control.vm_name[i] }} \
@@ -34,7 +60,8 @@
3460
--graphics none \
3561
--console pty,target_type=serial \
3662
--wait=-1 \
37-
--noautoconsole
63+
--noautoconsole \
64+
{{ cex_hostdev }}
3865
timeout: 360
3966
with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }} stride=1
4067
loop_control:
@@ -72,6 +99,7 @@
7299
--graphics none \
73100
--wait=-1 \
74101
--noautoconsole
102+
75103
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
76104

77105
- name: Create the second CoreOS control node on the first 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)