From 63cdb6b2edfcfccf4d6dd8fd208998f7c5b99295 Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Fri, 30 Oct 2026 14:51:07 +0100 Subject: [PATCH] Label untethered diskless roots for SELinux On an untethered root, files the initramfs writes before policy load and sockets that unsquashfs creates without attributes have no label. Label every inode without one, as the upper layer is in tethered mode. --- .../profiles/default/scripts/imageboot.sh | 79 +++++++++++++------ .../profiles/default/scripts/imageboot.sh | 79 +++++++++++++------ 2 files changed, 108 insertions(+), 50 deletions(-) diff --git a/confluent_osdeploy/el10-diskless/profiles/default/scripts/imageboot.sh b/confluent_osdeploy/el10-diskless/profiles/default/scripts/imageboot.sh index a0dea1e1..988cc9f8 100644 --- a/confluent_osdeploy/el10-diskless/profiles/default/scripts/imageboot.sh +++ b/confluent_osdeploy/el10-diskless/profiles/default/scripts/imageboot.sh @@ -146,36 +146,65 @@ set_selinux_context system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/onboot set_selinux_context system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/apiclient ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service cp /etc/confluent/functions /sysroot/etc/confluent/functions -if [ $TETHERED -eq 1 ] && [ -x /sysroot/usr/sbin/matchpathcon ] && grep -Eq '^SELINUX=(enforcing|permissive)' /sysroot/etc/selinux/config 2> /dev/null; then - # Files written to the upper layer before policy load are unlabeled. - # Label only those, using globstar since the initramfs has no find. +if [ -x /sysroot/usr/sbin/matchpathcon ] && grep -Eq '^SELINUX=(enforcing|permissive)' /sysroot/etc/selinux/config 2> /dev/null; then + # Files written to /sysroot before policy load are unlabeled. selinuxpolicy=$(sed -n 's/^SELINUXTYPE=//p' /sysroot/etc/selinux/config) - shopt -s globstar dotglob nullglob - relabel=() - if [ -f /tmp/mountparts.sh ]; then - overlayparts=$(awk '{print $3}' /tmp/mountparts.sh) - else - overlayparts=/mnt/remote - fi - for srcmount in $overlayparts; do - upperdir=/mnt/overlay/upper + fcontexts=/etc/selinux/$selinuxpolicy/contexts/files/file_contexts + if [ $TETHERED -eq 1 ]; then + # Label only the upper layer, using globstar since the initramfs has no find. + shopt -s globstar dotglob nullglob + relabel=() if [ -f /tmp/mountparts.sh ]; then - upperdir=/mnt/overlay${srcmount}/upper + overlayparts=$(awk '{print $3}' /tmp/mountparts.sh) + else + overlayparts=/mnt/remote fi - prefix=${srcmount#/mnt/remote} - prefix=${prefix%/} - relabel+=("${prefix:-/}") - for relpath in "$upperdir"/**; do - relabel+=("$prefix${relpath#$upperdir}") + for srcmount in $overlayparts; do + upperdir=/mnt/overlay/upper + if [ -f /tmp/mountparts.sh ]; then + upperdir=/mnt/overlay${srcmount}/upper + fi + prefix=${srcmount#/mnt/remote} + prefix=${prefix%/} + relabel+=("${prefix:-/}") + for relpath in "$upperdir"/**; do + relabel+=("$prefix${relpath#$upperdir}") + done done - done - shopt -u globstar dotglob nullglob - # Not setfiles (recurses) or restorecon (no-op before policy load). - chroot /sysroot /usr/sbin/matchpathcon -f /etc/selinux/$selinuxpolicy/contexts/files/file_contexts "${relabel[@]}" | while IFS=$'\t' read -r relpath context; do - if [ "$context" != "<>" ]; then - chcon -h "$context" "/sysroot$relpath" + shopt -u globstar dotglob nullglob + # Not setfiles (recurses into the image) or restorecon (no-op before policy load). + chroot /sysroot /usr/sbin/matchpathcon -f $fcontexts "${relabel[@]}" | while IFS=$'\t' read -r relpath context; do + if [ "$context" != "<>" ]; then + chcon -h "$context" "/sysroot$relpath" + fi + done + elif [ -x /sysroot/usr/bin/python3 ] && [ -x /sysroot/usr/sbin/setfiles ]; then + # The root is a local copy: label every inode without a label, which + # also covers sockets that unsquashfs creates without attributes. + # setfiles only recurses into directories that are new here. + chroot /sysroot /usr/bin/python3 -c ' +import errno, os, sys +out = sys.stdout.buffer +rootdev = os.lstat(b"/").st_dev +def check(path): + try: + os.getxattr(path, b"security.selinux", follow_symlinks=False) + except OSError as e: + if e.errno == errno.ENODATA: + out.write(path + b"\0") +check(b"/") +for root, dirs, files in os.walk(b"/"): + for name in dirs + files: + check(os.path.join(root, name)) + dirs[:] = [d for d in dirs if os.lstat(os.path.join(root, d)).st_dev == rootdev] +' | chroot /sysroot /usr/sbin/setfiles -0 -F -f - $fcontexts + relabelstatus=("${PIPESTATUS[@]}") + if [ "${relabelstatus[0]}" != 0 ] || [ "${relabelstatus[1]}" != 0 ]; then + echo "Warning: SELinux labeling of the root filesystem failed" fi - done + else + echo "Warning: python3 or setfiles missing in the image, files written at boot stay unlabeled" + fi fi if grep installtodisk /proc/cmdline > /dev/null; then . /etc/confluent/functions diff --git a/confluent_osdeploy/el9-diskless/profiles/default/scripts/imageboot.sh b/confluent_osdeploy/el9-diskless/profiles/default/scripts/imageboot.sh index d508f3e5..ec939c9d 100644 --- a/confluent_osdeploy/el9-diskless/profiles/default/scripts/imageboot.sh +++ b/confluent_osdeploy/el9-diskless/profiles/default/scripts/imageboot.sh @@ -148,36 +148,65 @@ set_selinux_context system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/onboot set_selinux_context system_u:object_r:bin_t:s0 /sysroot/opt/confluent/bin/apiclient ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service cp /etc/confluent/functions /sysroot/etc/confluent/functions -if [ $TETHERED -eq 1 ] && [ -x /sysroot/usr/sbin/matchpathcon ] && grep -Eq '^SELINUX=(enforcing|permissive)' /sysroot/etc/selinux/config 2> /dev/null; then - # Files written to the upper layer before policy load are unlabeled. - # Label only those, using globstar since the initramfs has no find. +if [ -x /sysroot/usr/sbin/matchpathcon ] && grep -Eq '^SELINUX=(enforcing|permissive)' /sysroot/etc/selinux/config 2> /dev/null; then + # Files written to /sysroot before policy load are unlabeled. selinuxpolicy=$(sed -n 's/^SELINUXTYPE=//p' /sysroot/etc/selinux/config) - shopt -s globstar dotglob nullglob - relabel=() - if [ -f /tmp/mountparts.sh ]; then - overlayparts=$(awk '{print $3}' /tmp/mountparts.sh) - else - overlayparts=/mnt/remote - fi - for srcmount in $overlayparts; do - upperdir=/mnt/overlay/upper + fcontexts=/etc/selinux/$selinuxpolicy/contexts/files/file_contexts + if [ $TETHERED -eq 1 ]; then + # Label only the upper layer, using globstar since the initramfs has no find. + shopt -s globstar dotglob nullglob + relabel=() if [ -f /tmp/mountparts.sh ]; then - upperdir=/mnt/overlay${srcmount}/upper + overlayparts=$(awk '{print $3}' /tmp/mountparts.sh) + else + overlayparts=/mnt/remote fi - prefix=${srcmount#/mnt/remote} - prefix=${prefix%/} - relabel+=("${prefix:-/}") - for relpath in "$upperdir"/**; do - relabel+=("$prefix${relpath#$upperdir}") + for srcmount in $overlayparts; do + upperdir=/mnt/overlay/upper + if [ -f /tmp/mountparts.sh ]; then + upperdir=/mnt/overlay${srcmount}/upper + fi + prefix=${srcmount#/mnt/remote} + prefix=${prefix%/} + relabel+=("${prefix:-/}") + for relpath in "$upperdir"/**; do + relabel+=("$prefix${relpath#$upperdir}") + done done - done - shopt -u globstar dotglob nullglob - # Not setfiles (recurses) or restorecon (no-op before policy load). - chroot /sysroot /usr/sbin/matchpathcon -f /etc/selinux/$selinuxpolicy/contexts/files/file_contexts "${relabel[@]}" | while IFS=$'\t' read -r relpath context; do - if [ "$context" != "<>" ]; then - chcon -h "$context" "/sysroot$relpath" + shopt -u globstar dotglob nullglob + # Not setfiles (recurses into the image) or restorecon (no-op before policy load). + chroot /sysroot /usr/sbin/matchpathcon -f $fcontexts "${relabel[@]}" | while IFS=$'\t' read -r relpath context; do + if [ "$context" != "<>" ]; then + chcon -h "$context" "/sysroot$relpath" + fi + done + elif [ -x /sysroot/usr/bin/python3 ] && [ -x /sysroot/usr/sbin/setfiles ]; then + # The root is a local copy: label every inode without a label, which + # also covers sockets that unsquashfs creates without attributes. + # setfiles only recurses into directories that are new here. + chroot /sysroot /usr/bin/python3 -c ' +import errno, os, sys +out = sys.stdout.buffer +rootdev = os.lstat(b"/").st_dev +def check(path): + try: + os.getxattr(path, b"security.selinux", follow_symlinks=False) + except OSError as e: + if e.errno == errno.ENODATA: + out.write(path + b"\0") +check(b"/") +for root, dirs, files in os.walk(b"/"): + for name in dirs + files: + check(os.path.join(root, name)) + dirs[:] = [d for d in dirs if os.lstat(os.path.join(root, d)).st_dev == rootdev] +' | chroot /sysroot /usr/sbin/setfiles -0 -F -f - $fcontexts + relabelstatus=("${PIPESTATUS[@]}") + if [ "${relabelstatus[0]}" != 0 ] || [ "${relabelstatus[1]}" != 0 ]; then + echo "Warning: SELinux labeling of the root filesystem failed" fi - done + else + echo "Warning: python3 or setfiles missing in the image, files written at boot stay unlabeled" + fi fi if grep installtodisk /proc/cmdline > /dev/null; then . /etc/confluent/functions