From 310de658362ae47cddff25d47722a3f3574054cf Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 11 Aug 2026 08:54:48 -0700 Subject: [PATCH] fix(os): make the SELinux assertion real and match it across both backends CONFIG_SECURITY_SELINUX=y has sat in dstack-docker.cfg without effect. The kernel is built from the linux-yocto "tiny" type, which leaves CONFIG_SECURITY off, and SELINUX is "depends on SECURITY_NETWORK && AUDIT && NET && INET" with SECURITY_NETWORK depending on SECURITY. Kconfig drops a symbol whose dependencies are unmet without saying so, so every production image built from this fragment has run without SELinux while the fragment claimed otherwise. os/mkosi has had it all along, because x86_64_defconfig sets CONFIG_SECURITY -- so the two guest images have been measuring different feature sets. Enabling the chain is not sufficient on its own, and the second failure is silent in the same way: an LSM only registers if CONFIG_LSM names it, and that string's default is selected by DEFAULT_SECURITY_*. Turning on SECURITY without naming a default lands on DEFAULT_SECURITY_DAC, whose list is "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" -- selinux absent -- so the hooks would compile in and never initialize. Both the DEFAULT_SECURITY_SELINUX choice and the resulting CONFIG_LSM order are therefore asserted in both fragments, so neither a defconfig change nor a new DEFAULT_SECURITY_* branch can drop selinux without failing the build. This does not change enforcement. SELinux needs a loaded policy and neither image ships one; measured in a CVM on the mkosi image, which has had SELinux all along, /sys/fs/selinux exists with enforce=0, /etc/selinux holds only semanage.conf, and `docker info` reports no security options. On the Yocto image /sys/fs/selinux does not exist at all. What this buys is that both images measure the same feature set and the hooks can be used later without another kernel change. It does change the kernel's measurements, so it needs the same release coordination as any guest-image change. Verified by merging the fragment onto a kernel-config artifact from a real build (linux-yocto 6.18.24) and by reproducing the mkosi path from x86_64_defconfig, both with olddefconfig against Linux 6.18.40 source. SECURITY, SECURITY_NETWORK, AUDIT, AUDITSYSCALL, SECURITY_SELINUX, DEFAULT_SECURITY_SELINUX, NETWORK_SECMARK and SECURITYFS all come out =y on both backends, and the two CONFIG_LSM strings are byte-identical. os/mkosi's own kernel-config gate passes. --- os/mkosi/components/kernel/kernel.config | 9 ++++++++ .../linux/files/dstack-docker.cfg | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/os/mkosi/components/kernel/kernel.config b/os/mkosi/components/kernel/kernel.config index 767d34f45..6384ceac0 100644 --- a/os/mkosi/components/kernel/kernel.config +++ b/os/mkosi/components/kernel/kernel.config @@ -207,7 +207,16 @@ CONFIG_NFT_LIMIT=m CONFIG_NFT_REJECT=m CONFIG_NFT_REJECT_INET=m CONFIG_NFT_HASH=m +# SELinux, matching dstack-docker.cfg symbol for symbol. CONFIG_SECURITY comes +# from x86_64_defconfig here, but the rest of the chain is asserted because two +# separate steps fail silently: SELINUX needs SECURITY_NETWORK && AUDIT, and an +# LSM only registers if CONFIG_LSM names it, which depends on which +# DEFAULT_SECURITY_* is chosen. +CONFIG_SECURITY_NETWORK=y +CONFIG_AUDIT=y CONFIG_SECURITY_SELINUX=y +CONFIG_DEFAULT_SECURITY_SELINUX=y +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf" CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_XFRM=y CONFIG_XFRM_USER=y diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg index df656498c..1a23f7ecf 100644 --- a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg @@ -20,7 +20,28 @@ CONFIG_BPF_SYSCALL=y CONFIG_IP_VS=m CONFIG_SECCOMP=y CONFIG_SECCOMP_FILTER=y +# SELinux. This line alone never took effect: the kernel is built from the +# linux-yocto "tiny" type, which leaves CONFIG_SECURITY off, and SELINUX is +# "depends on SECURITY_NETWORK && AUDIT && NET && INET" with SECURITY_NETWORK in +# turn depending on SECURITY. So every image this fragment produced ran without +# SELinux while claiming otherwise, and os/mkosi -- which starts from +# x86_64_defconfig -- had it. +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_AUDIT=y CONFIG_SECURITY_SELINUX=y +# Compiling it in is still not enough, and the second failure is silent too: an +# LSM only registers if CONFIG_LSM names it, and that string's default is picked +# by DEFAULT_SECURITY_*. Enabling SECURITY without naming a default lands on +# DEFAULT_SECURITY_DAC, whose list is +# "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" -- no selinux -- so the +# hooks would build and never initialize. +CONFIG_DEFAULT_SECURITY_SELINUX=y +# The resulting order is asserted rather than left to a default, in both +# backends, so neither a defconfig change nor a new DEFAULT_SECURITY_* branch +# can drop selinux without failing the build. Entries for LSMs that are not +# compiled in are ignored by the kernel. +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf" CONFIG_VLAN_8021Q=y CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_XFRM=y