From 1b1e60de6a10760ae704f633942ae853bfa34a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Wed, 28 Oct 2026 12:58:27 +0100 Subject: [PATCH] overlay2: Allow rootless mode with SELinux enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rootless dockerd refused to use overlay2 whenever SELinux was enabled on the host, forcing users onto fuse-overlayfs: ``` overlay is not supported for Rootless with SELinux ``` The check was added for Fedora 34, where rootless overlayfs on kernel 5.11 hit SELinux denials. That no longer happens on current hosts such as Fedora 41. The containerd overlayfs snapshotter never had an equivalent check and already runs natively in rootless mode with SELinux enforcing. Signed-off-by: Paweł Gronowski --- daemon/graphdriver/overlayutils/overlayutils.go | 10 ---------- docs/contributing/test.md | 2 +- integration/daemon/default_storage_test.go | 9 ++------- integration/daemon/migration_test.go | 15 +-------------- internal/testutil/environment/environment.go | 15 --------------- 5 files changed, 4 insertions(+), 47 deletions(-) diff --git a/daemon/graphdriver/overlayutils/overlayutils.go b/daemon/graphdriver/overlayutils/overlayutils.go index 324129fe07..f572dbe388 100644 --- a/daemon/graphdriver/overlayutils/overlayutils.go +++ b/daemon/graphdriver/overlayutils/overlayutils.go @@ -38,16 +38,6 @@ func ErrDTypeNotSupported(driver, backingFs string) error { // checkMultipleLowers parameter enables check for multiple lowerdirs, // which is required for the overlay2 driver. func SupportsOverlay(d string, checkMultipleLowers bool) error { - // We can't rely on go-selinux.GetEnabled() to detect whether SELinux is enabled, - // because RootlessKit doesn't mount /sys/fs/selinux in the child: https://github.com/rootless-containers/rootlesskit/issues/94 - // So we check $_DOCKERD_ROOTLESS_SELINUX, which is set by dockerd-rootless.sh . - if os.Getenv("_DOCKERD_ROOTLESS_SELINUX") == "1" { - // Kernel 5.11 introduced support for rootless overlayfs, but incompatible with SELinux, - // so fallback to fuse-overlayfs. - // https://github.com/moby/moby/issues/42333 - return errors.New("overlay is not supported for Rootless with SELinux") - } - td, err := os.MkdirTemp(d, "check-overlayfs-support") if err != nil { return err diff --git a/docs/contributing/test.md b/docs/contributing/test.md index d60e7312c3..d516430fa4 100644 --- a/docs/contributing/test.md +++ b/docs/contributing/test.md @@ -241,7 +241,7 @@ additional daemons and modify host networking and other system configuration. For rootless coverage, add `DOCKER_ROOTLESS=1` to the command. The test runner still runs as root and starts the rootless daemons as `unprivilegeduser`, which the provisioning script creates. Use `DOCKER_GRAPHDRIVER=fuse-overlayfs` when -SELinux is enabled or the kernel predates 5.11. Reports and daemon logs are +the kernel predates 5.11. Reports and daemon logs are written under `bundles/test-integration/`. ## Test the Windows binary against a Linux daemon diff --git a/integration/daemon/default_storage_test.go b/integration/daemon/default_storage_test.go index d7a2b1e2ab..0f48ae3104 100644 --- a/integration/daemon/default_storage_test.go +++ b/integration/daemon/default_storage_test.go @@ -40,12 +40,6 @@ func TestGraphDriverPersistence(t *testing.T) { t.Setenv("DOCKER_GRAPHDRIVER", "") t.Setenv("TEST_INTEGRATION_USE_GRAPHDRIVER", "") - graphdriver := "overlay2" - // overlay2 doesn't work rootless with SELinux enforcing; use fuse-overlayfs instead. - if testEnv.IsRootless() && testEnv.IsSELinuxEnforcing() { - graphdriver = "fuse-overlayfs" - } - ctx := testutil.StartSpan(baseContext, t) // Phase 1: Start daemon with explicit graphdriver (overlay2) @@ -55,7 +49,8 @@ func TestGraphDriverPersistence(t *testing.T) { }) const testImage = "busybox:latest" - d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false", "--storage-driver="+graphdriver) + + d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false", "--storage-driver=overlay2") c := d.NewClientT(t) // Verify we're using graphdriver diff --git a/integration/daemon/migration_test.go b/integration/daemon/migration_test.go index e97ffa3498..78ea4daf77 100644 --- a/integration/daemon/migration_test.go +++ b/integration/daemon/migration_test.go @@ -16,14 +16,7 @@ import ( ) func TestMigrateOverlaySnapshotter(t *testing.T) { - graphdriver := "overlay2" - snapshotter := "overlayfs" - // overlay2 doesn't work rootless with SELinux enforcing; use fuse-overlayfs instead. - if testEnv.IsRootless() && testEnv.IsSELinuxEnforcing() { - graphdriver = "fuse-overlayfs" - snapshotter = "fuse-overlayfs" - } - testMigrateSnapshotter(t, graphdriver, snapshotter) + testMigrateSnapshotter(t, "overlay2", "overlayfs") } func TestMigrateNativeSnapshotter(t *testing.T) { @@ -109,12 +102,6 @@ func TestMigrateSaveLoad(t *testing.T) { ) defer d.Stop(t) - // overlay2 doesn't work rootless with SELinux enforcing; use fuse-overlayfs instead. - if testEnv.IsRootless() && testEnv.IsSELinuxEnforcing() { - graphdriver = "fuse-overlayfs" - snapshotter = "fuse-overlayfs" - } - d.Start(t, "--iptables=false", "--ip6tables=false", "-s", graphdriver) info := d.Info(t) id := info.ID diff --git a/internal/testutil/environment/environment.go b/internal/testutil/environment/environment.go index ac5c304922..fd0ac5d925 100644 --- a/internal/testutil/environment/environment.go +++ b/internal/testutil/environment/environment.go @@ -221,21 +221,6 @@ func (e *Execution) HasExistingImage(t testing.TB, reference string) bool { return len(imageList.Items) > 0 } -// IsSELinuxEnforcing returns true if SELinux is enabled and running in enforcing mode. -// Returns false on non-Linux systems or if SELinux is not available or not enabled. -func (e *Execution) IsSELinuxEnforcing() bool { - if runtime.GOOS != "linux" { - return false - } - - data, err := os.ReadFile("/sys/fs/selinux/enforce") - if err != nil { - return false - } - - return strings.TrimSpace(string(data)) == "1" -} - // IsKernelModuleLoadable returns whether the given kernel module is loadable. // Returns true when the module is already loaded or built-in into the kernel. func (e *Execution) IsKernelModuleLoadable(module string) bool {