ebc: Add ibm-sel-ebc dracut module

Add 95ibm-sel-ebc dracut module for secure boot-time customization of
SEL guests.

Introduce the IBM SEL EBC dracut module (95ibm-sel-ebc) that enables
Early Boot Customization for SEL guests during the initramfs phase.

The module implements a critical security architecture to prevent
injection attacks: all EBC resources (.asr and .pol files) are copied from
/boot/sics (which resides in the qcow2 image on the host filesystem) to
/run/ibm-sel-ebc (a tmpfs/RAM-backed directory). Since guest RAM is
protected by the Ultravisor, this prevents malicious hosts from modifying
EBC resources during boot.

Systemd units and their purposes:
 - ibm-sel-ebc.target: Groups all EBC-related units
 - boot.mount: Mounts /dev/disk/by-label/boot to /boot
 - ibm-sel-ebc-ensure-sics.service: Fallback to create /boot/sics/ if boot
   partition mount fails (supports Kata VM scenarios)
 - ibm-sel-ebc-pvebc.service: Main unit that copies EBC resources to RAM,
   invokes pvebc tool to verify integrity and add ASRs to UV, retrieves
   LUKS passphrase from UV secret store
 - ibm-sel-ebc-override-crypttab.service: Replaces /etc/crypttab with
   prepared IBM SEL EBC crypttab, reloads systemd daemon, starts cryptsetup
   service
 - ibm-sel-ebc-paes-enforce.service: Verifies root filesystem uses PAES
   encryption to prevent root filesystem substitution attacks

All units write logs to /boot/sics/log for debugging, accessible even if
root filesystem fails to mount. Units are triggered by rd.ibm-sel-ebc
kernel parameter and only execute in initramfs
(ConditionPathExists=/etc/initrd-release).

Assisted-by: IBM Bob:1.0.1
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Finn Callies
2026-03-17 14:47:58 +01:00
committed by Jan Höppner
parent c04a0919f6
commit 3aa5c38714
10 changed files with 335 additions and 0 deletions

View File

@@ -10,6 +10,8 @@ CARGO_TARGETS :=
PV_TARGETS :=
CARGO_TEST_TARGETS :=
SEL_EBC_MODDIR := 95sel-ebc
ifneq (${HAVE_CARGO},0)
CARGO_TARGETS := pvebc
@@ -29,6 +31,7 @@ ifneq (${HAVE_LIBCURL},0)
ifeq ($(HOST_ARCH),s390x)
PV_TARGETS += pvapconfig pvinfo
INSTALL_TARGETS += install-ebc-dracut-module
else
BUILD_TARGETS += skip-pvapconfig skip-pvinfo
endif #HOSTARCH
@@ -104,6 +107,23 @@ endif # CARGO
rust-test: $(CARGO_TEST_TARGETS)
install-ebc-dracut-module:
ifneq ($(HAVE_DRACUT),0)
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
$(INSTALL) -m 755 pvebc/$(SEL_EBC_MODDIR)/module-setup.sh \
pvebc/$(SEL_EBC_MODDIR)/override-crypttab.sh \
pvebc/$(SEL_EBC_MODDIR)/pvebc-wrapper.sh \
$(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
$(INSTALL) -m 644 pvebc/$(SEL_EBC_MODDIR)/boot.mount \
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-override-crypttab.service \
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-paes-enforce.service \
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-pvebc.service \
pvebc/$(SEL_EBC_MODDIR)/sel-ebc.crypttab \
pvebc/$(SEL_EBC_MODDIR)/sel-ebc.target \
$(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
endif
install-rust-tools: $(BUILD_TARGETS)
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
$(foreach target,$(CARGO_TARGETS),\

View File

@@ -0,0 +1,28 @@
[Unit]
Description=Mount /boot early in initramfs
# Initramfs requirement
DefaultDependencies=no
# Make absolutely sure this only runs in initramfs (and not post-pivot if the
# unit ever appears there)
ConditionPathExists=/etc/initrd-release
ConditionKernelCommandLine=root
# we use /dev/disk/by-label because it identifies the boot partition system
# independently IF set up correctly
Requires=dev-disk-by\x2dlabel-boot.device
# Ordering dependencies
After=dev-disk-by\x2dlabel-boot.device
Before=sel-ebc-pvebc.service
[Mount]
# system independent identification of boot partition requires that the label
# boot is set for the boot partition
What=/dev/disk/by-label/boot
Where=/boot
Type=auto
Options=defaults
[Install]
WantedBy=sel-ebc-pvebc.service

View File

@@ -0,0 +1,76 @@
#!/bin/bash
# SPDX-License-Identifier: MIT
#
# Copyright IBM Corp.
# Called by dracut
check() {
# always include
return 0
}
# Called by dracut
depends() {
# We need systemd in the initramfs
echo systemd
echo systemd-udevd
echo crypt
echo dm
return 0
}
# Called by dracut
installkernel() {
# kernel modules needed for opening an encrypted rfs
instmods -c uvdevice
instmods -c paes_s390
instmods -c pkey_uv
instmods -c pkey_pckmo
instmods -c pkey
}
# Called by dracut
install() {
# shellcheck disable=SC2154
# moddir, systemdsystemunitdir, and initdir are provided by dracut
# Copy the units into the initramfs' systemd unit dir
inst_simple "$moddir/sel-ebc.target" \
"$systemdsystemunitdir/sel-ebc.target"
inst_simple "$moddir/sel-ebc-pvebc.service" \
"$systemdsystemunitdir/sel-ebc-pvebc.service"
inst_simple "$moddir/sel-ebc-paes-enforce.service" \
"$systemdsystemunitdir/sel-ebc-paes-enforce.service"
inst_simple "$moddir/sel-ebc-override-crypttab.service" \
"$systemdsystemunitdir/sel-ebc-override-crypttab.service"
inst_simple "$moddir/boot.mount" \
"$systemdsystemunitdir/boot.mount"
# already exisitng unit we depend on for kernel modules
inst_simple /usr/lib/systemd/system/systemd-modules-load.service \
"$systemdsystemunitdir/systemd-modules-load.service"
# wrapper for sel-ebc.service
inst_simple "$moddir/pvebc-wrapper.sh" \
"/etc/sel-ebc/pvebc-wrapper.sh"
# override crypttab
inst_simple "$moddir/override-crypttab.sh" \
"/etc/sel-ebc/override-crypttab.sh"
# copy main application
inst_binary "/usr/bin/pvebc"
inst_binary "/usr/bin/pvsecret"
inst_simple "$moddir/sel-ebc.crypttab" "/etc/sel-ebc/crypttab"
# Create the enablement symlinks in the image using host systemctl:
# shellcheck disable=SC2154
inst_dir "$initdir/etc/systemd/system"
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc.target
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc-pvebc.service
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc-override-crypttab.service
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc-paes-enforce.service
systemctl --root "$initdir" --no-reload --quiet enable systemd-modules-load.service
systemctl --root "$initdir" --no-reload --quiet enable boot.mount
}

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# SPDX-License-Identifier: MIT
#
# Copyright IBM Corp.
IBM_RSRC_DIR="/etc/sel-ebc"
if [[ ! -f "${IBM_RSRC_DIR}/crypttab" ]]; then
echo "Error: source file $IBM_RSRC_DIR/crypttab does not exist"
exit 1
fi
# Unconditionally override /etc/crypttab to ensure correct EBC configuration
cp "${IBM_RSRC_DIR}/crypttab" "/etc/crypttab"
systemctl daemon-reload
systemctl restart systemd-cryptsetup@cryptroot_mapper.service
exit 0

View File

@@ -0,0 +1,63 @@
#!/bin/bash
# SPDX-License-Identifier: MIT
#
# Copyright IBM Corp.
SYSFS=/sys/firmware/uv/prot_virt_guest
SICS=/boot/sics
EBC_TMPFS=/run/sel-ebc
TOC=toc.asr
ASR_NAME=luks-rfs-passphrase
# Early exit for non SEL guests
if [[ ! -e $SYSFS ]]; then
echo "Not running in a SEL guest."
exit 1
fi
if [[ $(cat $SYSFS) -ne 1 ]]; then
echo "Not running in a SEL guest."
exit 1
fi
echo "Running in SEL guest."
# Copy EBC resources from /boot/sics to tmpfs for security
# This protects against host injection attacks by moving resources to UV-protected RAM
echo "Copying EBC resources from $SICS to $EBC_TMPFS"
if ! mkdir -p "$EBC_TMPFS"; then
echo "Failed to create $EBC_TMPFS"
exit 1
fi
# Copy only .asr and .pol files
for file in "$SICS"/*.asr "$SICS"/*.pol; do
if [[ -f "$file" && ! -L "$file" ]]; then
cp "$file" "$EBC_TMPFS/" || {
echo "Failed to copy $file to $EBC_TMPFS"
exit 1
}
fi
done
# Verify toc.asr was copied
if [[ ! -f "$EBC_TMPFS/$TOC" ]]; then
echo "Error: $EBC_TMPFS/$TOC does not exist after copy"
exit 1
fi
# execute the actual tool with the copied toc.asr
pvebc --toc "$EBC_TMPFS/$TOC"
rc=$?
if [[ $rc -ne 0 ]]; then
exit $rc
fi
# Retrieve and check for dummy LUKS passphrase
pvsecret retrieve --inform name -o "$EBC_TMPFS/$ASR_NAME" --outform bin "$ASR_NAME"
if [[ ! -f "$EBC_TMPFS/$ASR_NAME" ]]; then
echo "$EBC_TMPFS/$ASR_NAME does not exist"
fi
chmod 400 "$EBC_TMPFS/$ASR_NAME"
exit 0

View File

@@ -0,0 +1,37 @@
[Unit]
Description=Override crypttab
# boot partition contains SICS
# Loading of kernel modules is required which are needed for protected keys
Requires=systemd-modules-load.service
Requires=boot.mount
# Ensure this runs before the handoff to the real root, if that's required:
After=boot.mount
After=systemd-modules-load.service
Before=cryptsetup-pre.target
Before=cryptsetup.target
Before=systemd-cryptsetup@.service
Before=initrd-root-device.target
After=sel-ebc-pvebc.service
# Initramfs requirement
DefaultDependencies=no
# Make absolutely sure this only runs in initramfs
ConditionPathExists=/etc/initrd-release
ConditionKernelCommandLine=rd.sel-ebc
ConditionKernelCommandLine=root
[Service]
Type=oneshot
ExecStart=/bin/bash /etc/sel-ebc/override-crypttab.sh
RemainAfterExit=yes
# If pvebc fails immediately abort boot
FailureAction=poweroff-immediate
# boot partition is unencrypted and contains SICS so we can get logs out this way
# logs do not leek any sensitive information
StandardOutput=file:/boot/sics/log
StandardError=file:/boot/sics/log
[Install]
RequiredBy=sel-ebc.target

View File

@@ -0,0 +1,27 @@
[Unit]
Description=Enforce PAES encrypted root fs for SEL guests
# Ensure this runs before the handoff to the real root, if that's required:
After=cryptsetup.target
After=initrd-root-device.target
Before=sysroot.mount
# Initramfs requirement
DefaultDependencies=no
# Make absolutely sure this only runs in initramfs
ConditionPathExists=/etc/initrd-release
ConditionKernelCommandLine=rd.sel-ebc
ConditionKernelCommandLine=root
[Service]
Type=oneshot
ExecStart=bash -c 'dmsetup table /dev/disk/by-label/root | grep "paes-xts-plain64"'
FailureAction=poweroff-immediate
RemainAfterExit=yes
# logs do not leek any sensitive information
StandardOutput=file:/boot/sics/log
StandardError=file:/boot/sics/log
[Install]
RequiredBy=sel-ebc.target

View File

@@ -0,0 +1,36 @@
[Unit]
Description=Run pvebc during early boot to process SICS
# boot partition contains SICS
# Loading of kernel modules is required which are needed for protected keys
Requires=systemd-modules-load.service
Wants=boot.mount
# Ensure this runs before the handoff to the real root, if that's required:
Before=initrd-root-device.target
Before=cryptsetup-pre.target
Before=cryptsetup.target
After=boot.mount
After=systemd-modules-load.service
# Initramfs requirement
DefaultDependencies=no
# Make absolutely sure this only runs in initramfs
ConditionPathExists=/etc/initrd-release
AssertPathIsDirectory=/boot/sics
ConditionKernelCommandLine=rd.sel-ebc
[Service]
Type=oneshot
# execute pvebc
ExecStart=/bin/bash /etc/sel-ebc/pvebc-wrapper.sh
RemainAfterExit=yes
# If pvebc fails immediately abort boot
FailureAction=poweroff-immediate
# boot partition is unencrypted and contains SICS so we can get logs out this way
# logs do not leek any sensitive information
StandardOutput=file:/boot/sics/log
StandardError=file:/boot/sics/log
[Install]
RequiredBy=sel-ebc.target

View File

@@ -0,0 +1 @@
cryptroot_mapper /dev/disk/by-label/cryptroot /run/sel-ebc/luks-rfs-passphrase

View File

@@ -0,0 +1,27 @@
[Unit]
Description=Target unit for IBM Secure Execution for Linux early boot customization
# enable for use in initramfs
DefaultDependencies=no
# if not met start the target without starting its dependencies
ConditionPathExists=/etc/initrd-release
ConditionKernelCommandLine=rd.sel-ebc
# if not met the target is considered failed
# AssertVirtualization=kvm
# if target fails abort immediately
FailureAction=poweroff-immediate
Requires=sel-ebc-pvebc.service
Requires=sel-ebc-override-crypttab.service
Requires=sel-ebc-paes-enforce.service
# ordering dependencies
After=systemd-modules-load.service
Before=initrd-root-device.target
[Install]
# most basic hook to start target by default
WantedBy=initrd.target