From 5e28049c11df191efe3eb4735f74fbe6749fc777 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Thu, 26 Sep 2024 13:39:27 +0200 Subject: [PATCH 1/5] Consistently use tabs in selinux-autorelabel-generator Don't mix tabs and spaces of varying indentation depth. --- selinux/selinux-autorelabel-generator | 80 +++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/selinux/selinux-autorelabel-generator b/selinux/selinux-autorelabel-generator index 2801ddc..83d2174 100755 --- a/selinux/selinux-autorelabel-generator +++ b/selinux/selinux-autorelabel-generator @@ -9,64 +9,64 @@ set -e # If invoked with no arguments (for testing) write to /tmp. generatordir="/tmp" if [ -n "$1" ]; then - generatordir="$1" + generatordir="$1" fi enable_units() { - mkdir -p "${generatordir}"/local-fs.target.requires + mkdir -p "${generatordir}"/local-fs.target.requires - relabel_unit_list="" + relabel_unit_list="" - for realdir in "/.snapshots" "/home" "/opt" "/root" "/srv" "/usr/local" \ - "/boot/grub2/i386-pc" "/boot/grub2/x86_64-efi" \ - "/boot/grub2/arm64-efi" "/boot/writable"; do - # Make sure the directory exist, else we create - # services for non existing mount points - test -d "${realdir}" || continue - mountunit=$(systemd-escape --path ${realdir}) - unitfile="${mountunit}-relabel.service" - relabel_unit_list="$unitfile $relabel_unit_list" + for realdir in "/.snapshots" "/home" "/opt" "/root" "/srv" "/usr/local" \ + "/boot/grub2/i386-pc" "/boot/grub2/x86_64-efi" \ + "/boot/grub2/arm64-efi" "/boot/writable"; do + # Make sure the directory exist, else we create + # services for non existing mount points + test -d "${realdir}" || continue + mountunit=$(systemd-escape --path ${realdir}) + unitfile="${mountunit}-relabel.service" + relabel_unit_list="$unitfile $relabel_unit_list" - opts="-T 0" - [ "${realdir}" == "/.snapshots" ] && opts="${opts} -x" + opts="-T 0" + [ "${realdir}" == "/.snapshots" ] && opts="${opts} -x" + cat >"${generatordir}/${unitfile}" <<-EOF + [Unit] + Description=Relabel ${realdir} + DefaultDependencies=no + RequiresMountsFor=${realdir} + Before=local-fs.target + ConditionSecurity=selinux + + [Service] + Type=oneshot + ExecStart=/sbin/restorecon -R ${opts} ${realdir} + EOF + + ln -sf ../"${unitfile}" "${generatordir}"/local-fs.target.requires/"${unitfile}" + done + + unitfile="mark-autorelabel-done.service" cat >"${generatordir}/${unitfile}" <<-EOF [Unit] - Description=Relabel ${realdir} + Description=Mark autorelabel as done DefaultDependencies=no - RequiresMountsFor=${realdir} Before=local-fs.target + After=${relabel_unit_list} + Requires=${relabel_unit_list} ConditionSecurity=selinux + ConditionPathExists=/etc/selinux/.autorelabel [Service] Type=oneshot - ExecStart=/sbin/restorecon -R ${opts} ${realdir} - EOF - - ln -sf ../"${unitfile}" "${generatordir}"/local-fs.target.requires/"${unitfile}" - done - - unitfile="mark-autorelabel-done.service" - cat >"${generatordir}/${unitfile}" <<-EOF - [Unit] - Description=Mark autorelabel as done - DefaultDependencies=no - Before=local-fs.target - After=${relabel_unit_list} - Requires=${relabel_unit_list} - ConditionSecurity=selinux - ConditionPathExists=/etc/selinux/.autorelabel - - [Service] - Type=oneshot - ExecStart=/usr/bin/rm /etc/selinux/.autorelabel + ExecStart=/usr/bin/rm /etc/selinux/.autorelabel EOF - ln -sf "../${unitfile}" "${generatordir}/local-fs.target.requires/${unitfile}" + ln -sf "../${unitfile}" "${generatordir}/local-fs.target.requires/${unitfile}" } if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then - if [ -f /etc/selinux/.autorelabel ] || grep -wq autorelabel /proc/cmdline; then - enable_units - fi + if [ -f /etc/selinux/.autorelabel ] || grep -wq autorelabel /proc/cmdline; then + enable_units + fi fi From 136dc173cf3b6f285da12eb75c36f92400b03966 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Thu, 26 Sep 2024 13:42:47 +0200 Subject: [PATCH 2/5] selinux-autorelabel-generator: Don't hardcode mountpoints Iterate /etc/fstab entries using findmnt. --- microos-tools.spec | 1 + selinux/selinux-autorelabel-generator | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/microos-tools.spec b/microos-tools.spec index c5a7207..a2fd5ec 100644 --- a/microos-tools.spec +++ b/microos-tools.spec @@ -32,6 +32,7 @@ BuildRequires: pkgconfig BuildRequires: pkgconfig(dracut) BuildRequires: pkgconfig(rpm) BuildRequires: pkgconfig(systemd) +Requires: /usr/bin/findmnt Requires: read-only-root-fs # for man-online Requires: mandoc-bin diff --git a/selinux/selinux-autorelabel-generator b/selinux/selinux-autorelabel-generator index 83d2174..46a3826 100755 --- a/selinux/selinux-autorelabel-generator +++ b/selinux/selinux-autorelabel-generator @@ -1,5 +1,5 @@ -#!/bin/sh -set -e +#!/bin/bash +set -euo pipefail # This systemd.generator(7) detects if SELinux is running and if the # user requested an autorelabel. If so, services will be enabled to @@ -8,7 +8,7 @@ set -e # If invoked with no arguments (for testing) write to /tmp. generatordir="/tmp" -if [ -n "$1" ]; then +if [ -n "${1-}" ]; then generatordir="$1" fi @@ -17,13 +17,15 @@ enable_units() { relabel_unit_list="" - for realdir in "/.snapshots" "/home" "/opt" "/root" "/srv" "/usr/local" \ - "/boot/grub2/i386-pc" "/boot/grub2/x86_64-efi" \ - "/boot/grub2/arm64-efi" "/boot/writable"; do - # Make sure the directory exist, else we create - # services for non existing mount points - test -d "${realdir}" || continue - mountunit=$(systemd-escape --path ${realdir}) + while read -r realdir; do + # Skip non-fs (swap) mounts, /, /var, /etc (already done in the initrd) and mountpoints with noauto + if [ "${realdir:0:1}" != "/" ] \ + || [ "${realdir}" = "/" ] || [ "${realdir}" = "/var" ] || [ "${realdir}" = "/etc" ] \ + || findmnt --fstab --noheadings --output OPTIONS --target "${realdir}" | grep -qw noauto; then + continue + fi + + mountunit=$(systemd-escape --path "${realdir}") unitfile="${mountunit}-relabel.service" relabel_unit_list="$unitfile $relabel_unit_list" @@ -44,7 +46,7 @@ enable_units() { EOF ln -sf ../"${unitfile}" "${generatordir}"/local-fs.target.requires/"${unitfile}" - done + done < <(findmnt --fstab --noheadings --output TARGET) unitfile="mark-autorelabel-done.service" cat >"${generatordir}/${unitfile}" <<-EOF From 2080ba4809c175a758a6dde94b9c8e394d0bb155 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Thu, 26 Sep 2024 13:43:29 +0200 Subject: [PATCH 3/5] Make 98selinux-microos usable on non-transactional systems Detect whether / is btrfs and /etc is an overlay mount and take care of mounting /var and /etc if necessary. --- selinux/98selinux-microos/module-setup.sh | 21 +++++++- .../selinux-microos-relabel.sh | 53 ++++++++++++++----- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/selinux/98selinux-microos/module-setup.sh b/selinux/98selinux-microos/module-setup.sh index 45166df..fb0a7f8 100755 --- a/selinux/98selinux-microos/module-setup.sh +++ b/selinux/98selinux-microos/module-setup.sh @@ -3,6 +3,25 @@ # called by dracut check() { test -f /etc/selinux/config || return 1 + + # Relabelling /etc and /var from the initrd needs support for mounting, + # "chroot mount /..." still loads modules from the initrd. + # Dracut handles /etc already, but for /var we need to DIY. + if [[ -f $dracutsysrootdir/etc/fstab ]]; then + _dev="$(findmnt --fstab --noheadings --output SOURCE /var --tab-file "$dracutsysrootdir/etc/fstab")" + if [[ -n $_dev ]]; then + _fstype="$(findmnt --fstab --noheadings --output FSTYPE /var --tab-file "$dracutsysrootdir/etc/fstab")" + _dev="$(expand_persistent_dev "$_dev")" + _dev="$(readlink -f "$_dev")" + if [[ -b $_dev ]]; then + push_host_devs "$_dev" + if [[ -z ${host_fs_types["$_dev"]} ]]; then + host_fs_types["$_dev"]="$_fstype" + fi + fi + fi + fi + return 0 } @@ -14,5 +33,5 @@ depends() { # called by dracut install() { inst_hook pre-pivot 50 "$moddir/selinux-microos-relabel.sh" - inst_multiple chroot cut grep + inst_multiple chroot cut findmnt grep } diff --git a/selinux/98selinux-microos/selinux-microos-relabel.sh b/selinux/98selinux-microos/selinux-microos-relabel.sh index 6b5ff87..3f71790 100755 --- a/selinux/98selinux-microos/selinux-microos-relabel.sh +++ b/selinux/98selinux-microos/selinux-microos-relabel.sh @@ -26,10 +26,19 @@ rd_microos_relabel() { info "SELinux: relabeling root filesystem" + root_is_btrfs= + if [ "$(findmnt --noheadings --output FSTYPE --target "$NEWROOT")" = "btrfs" ]; then + root_is_btrfs=y + fi + etc_is_overlay= + if [ "$(findmnt --fstab --noheadings --output FSTYPE /etc --tab-file "${NEWROOT}/etc/fstab")" = "overlay" ]; then + etc_is_overlay=y + fi + # If this doesn't exist because e.g. it's not mounted yet due to a bug # (boo#1197309), the exclusion is ignored. If it gets mounted during # the relabel, it gets wrong labels assigned. - if ! [ -d "$NEWROOT/var/lib/overlay" ]; then + if [ -n "$etc_is_overlay" ] && ! [ -d "$NEWROOT/var/lib/overlay" ]; then warn "ERROR: /var/lib/overlay doesn't exist - /var not mounted (yet)?" return 1 fi @@ -48,24 +57,41 @@ rd_microos_relabel() ret=1 fi done + if [ $ret -eq 0 ]; then + # Mount /var and /etc, need to be relabelled as well for booting. + for mp in /var /etc; do + if ! findmnt "${ROOT_SELINUX}${mp}" >/dev/null \ + && findmnt --fstab --output TARGET --tab-file "${ROOT_SELINUX}/etc/fstab" "$mp" >/dev/null; then + chroot "$ROOT_SELINUX" mount "$mp" || ret=1 + fi + done + fi if [ $ret -eq 0 ]; then info "SELinux: mount root read-write and relabel" mount -o remount,rw "${ROOT_SELINUX}" - oldrovalue="$(btrfs prop get "${ROOT_SELINUX}" ro | cut -d= -f2)" - btrfs prop set "${ROOT_SELINUX}" ro false + if [ -n "$root_is_btrfs" ]; then + oldrovalue="$(btrfs prop get "${ROOT_SELINUX}" ro | cut -d= -f2)" + btrfs prop set "${ROOT_SELINUX}" ro false + fi FORCE= [ -e "${ROOT_SELINUX}"/etc/selinux/.autorelabel ] && FORCE="$(cat "${ROOT_SELINUX}"/etc/selinux/.autorelabel)" . "${ROOT_SELINUX}"/etc/selinux/config # Marker when we had relabelled the filesystem. This is relabelled as well. > "${ROOT_SELINUX}"/etc/selinux/.relabelled - LANG=C chroot "$ROOT_SELINUX" /sbin/setfiles $FORCE -T 0 -e /var/lib/overlay -e /proc -e /sys -e /dev -e /etc "/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts" $(chroot "$ROOT_SELINUX" cut -d" " -f2 /proc/mounts) - # On overlayfs, st_dev isn't consistent so setfiles thinks it's a different mountpoint, ignoring it. - # st_dev changes also on copy-up triggered by setfiles itself, so the only way to relabel properly - # is to list every file explicitly. - # That's not all: There's a kernel bug that security.selinux of parent directories is lost on copy-up (bsc#1210690). - # Work around that by visiting children first and only then the parent directories. - LANG=C chroot "$ROOT_SELINUX" find /etc -depth -exec /sbin/setfiles $FORCE "/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts" \{\} + - btrfs prop set "${ROOT_SELINUX}" ro "${oldrovalue}" + if [ -n "$etc_is_overlay" ]; then + LANG=C chroot "$ROOT_SELINUX" /sbin/setfiles $FORCE -T 0 -e /var/lib/overlay -e /proc -e /sys -e /dev -e /etc "/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts" $(chroot "$ROOT_SELINUX" cut -d" " -f2 /proc/mounts) + # On overlayfs, st_dev isn't consistent so setfiles thinks it's a different mountpoint, ignoring it. + # st_dev changes also on copy-up triggered by setfiles itself, so the only way to relabel properly + # is to list every file explicitly. + # That's not all: There's a kernel bug that security.selinux of parent directories is lost on copy-up (bsc#1210690). + # Work around that by visiting children first and only then the parent directories. + LANG=C chroot "$ROOT_SELINUX" find /etc -depth -exec /sbin/setfiles $FORCE "/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts" \{\} + + else + LANG=C chroot "$ROOT_SELINUX" /sbin/setfiles $FORCE -T 0 -e /proc -e /sys -e /dev "/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts" $(chroot "$ROOT_SELINUX" cut -d" " -f2 /proc/mounts) + fi + if [ -n "$root_is_btrfs" ]; then + btrfs prop set "${ROOT_SELINUX}" ro "${oldrovalue}" + fi fi umount -R "${ROOT_SELINUX}" @@ -79,8 +105,9 @@ rd_microos_relabel() return $ret } -if test -e "$NEWROOT"/.autorelabel -a "$NEWROOT"/.autorelabel -nt "$NEWROOT"/etc/selinux/.relabelled ; then - cp -a "$NEWROOT"/.autorelabel "$NEWROOT"/etc/selinux/.autorelabel +if [ -e "$NEWROOT"/.autorelabel ] && [ "$NEWROOT"/.autorelabel -nt "$NEWROOT"/etc/selinux/.relabelled ]; then + mount -o remount,rw "$NEWROOT" || return 1 + cp -a "$NEWROOT"/.autorelabel "$NEWROOT"/etc/selinux/.autorelabel || return 1 rm -f "$NEWROOT"/.autorelabel 2>/dev/null fi From caa042497c3ad5f86c4003df41217ff89e58490a Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Thu, 26 Sep 2024 13:50:56 +0200 Subject: [PATCH 4/5] Split SELinux relabelling code into separate package To replace selinux-autorelabel on non-transactional systems. --- microos-tools.spec | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/microos-tools.spec b/microos-tools.spec index a2fd5ec..10137c1 100644 --- a/microos-tools.spec +++ b/microos-tools.spec @@ -32,14 +32,23 @@ BuildRequires: pkgconfig BuildRequires: pkgconfig(dracut) BuildRequires: pkgconfig(rpm) BuildRequires: pkgconfig(systemd) -Requires: /usr/bin/findmnt Requires: read-only-root-fs +Requires: selinux-autorelabel = %{version} # for man-online Requires: mandoc-bin %description Files, scripts and directories for openSUSE MicroOS. +%package -n selinux-autorelabel +Summary: Automatic SELinux relabelling during early boot +Requires: /usr/bin/findmnt +Requires: policycoreutils + +%description -n selinux-autorelabel +This package contains a dracut module and systemd generator for relabelling +the system during early boot. + %package -n microos-devel-tools Summary: Tools to develop MicroOS @@ -87,7 +96,6 @@ This package contains tools to make developing of MicroOS easier. %service_del_postun microos-ro.service %files -%license COPYING %dir %{_sysconfdir}/selinux %config %{_sysconfdir}/selinux/fixfiles_exclude_dirs %{_unitdir}/printenv.service @@ -99,12 +107,15 @@ This package contains tools to make developing of MicroOS easier. %dir %{_distconfdir}/tukit.conf.d %{_distconfdir}/tukit.conf.d/salt-tukit.conf %{_sbindir}/setup-systemd-proxy-env +%{_bindir}/man-online +%{_distconfdir}/profile.d/man-online.sh + +%files -n selinux-autorelabel +%license COPYING %dir %{_prefix}/lib/dracut %dir %{_prefix}/lib/dracut/modules.d %{_prefix}/lib/dracut/modules.d/98selinux-microos %{_systemdgeneratordir}/selinux-autorelabel-generator -%{_bindir}/man-online -%{_distconfdir}/profile.d/man-online.sh %files -n microos-devel-tools %{_unitdir}/microos-ro.service From 0dad3cfab5544a247cf7f400eef2fd9536aaf712 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Mon, 7 Oct 2024 15:08:10 +0200 Subject: [PATCH 5/5] Have the autorelabel hook propagate failure from relabelling If relabelling fails, it should be noticable. The system might not work properly anyway. --- selinux/98selinux-microos/selinux-microos-relabel.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/selinux/98selinux-microos/selinux-microos-relabel.sh b/selinux/98selinux-microos/selinux-microos-relabel.sh index 3f71790..8150673 100755 --- a/selinux/98selinux-microos/selinux-microos-relabel.sh +++ b/selinux/98selinux-microos/selinux-microos-relabel.sh @@ -112,17 +112,18 @@ if [ -e "$NEWROOT"/.autorelabel ] && [ "$NEWROOT"/.autorelabel -nt "$NEWROOT"/et fi if rd_is_selinux_enabled; then - if test -f "$NEWROOT"/etc/selinux/.autorelabel; then - rd_microos_relabel - elif getarg "autorelabel" > /dev/null; then - rd_microos_relabel + if [ -f "$NEWROOT"/etc/selinux/.autorelabel ] || getarg "autorelabel" > /dev/null; then + if ! rd_microos_relabel; then + warn "SELinux autorelabelling failed!" + return 1 + fi fi elif test -e "$NEWROOT"/etc/selinux/.relabelled; then # SELinux is off but looks like some labeling took place before. # So probably a boot with manually disabled SELinux. Make sure # the system gets relabelled next time SELinux is on. > "$NEWROOT"/etc/selinux/.autorelabel - warn "SElinux is off in labelled system!" + warn "SELinux is off in labelled system!" fi return 0