From fa1882c5c7d41f0c72dd4b6a88758551fbdbc766 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 18 Oct 2023 20:15:07 -0700 Subject: [PATCH] Add runc_dmz_selinux_compat build tag Add a new build tag, runc_dmz_selinux_compat, that enables a workaround for dmz vs selinux. Document it in the top-level and libct/dmz READMEs. Use the new tag in our CI builds for Fedora, CentOS 7, and CentOS Stream 8. Do not use it for CS9 since it already has container-selinux 2.224.0 available. Signed-off-by: Kir Kolyshkin --- .cirrus.yml | 10 +++++++++- README.md | 2 ++ libcontainer/container_linux.go | 4 ++++ libcontainer/dmz/README.md | 15 +++++++++++++++ libcontainer/dmz/selinux.go | 10 ++++++++++ libcontainer/dmz/selinux_compat.go | 25 +++++++++++++++++++++++++ 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 libcontainer/dmz/selinux.go create mode 100644 libcontainer/dmz/selinux_compat.go diff --git a/.cirrus.yml b/.cirrus.yml index 0b24e5c5075..3ddba3a0ba4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -56,8 +56,10 @@ task: vagrant up --no-tty || vagrant up --no-tty mkdir -p -m 0700 /root/.ssh vagrant ssh-config >> /root/.ssh/config + # TODO: drop this once Fedora has container-selinux >= 2.224.0 (F39?) + echo 'EXTRA_BUILDTAGS=runc_dmz_selinux_compat' >> /root/.ssh/environment guest_info_script: | - ssh default 'sh -exc "uname -a && systemctl --version && df -T && cat /etc/os-release && go version && sestatus"' + ssh default 'sh -exc "uname -a && systemctl --version && df -T && cat /etc/os-release && go version && sestatus && rpm -q container-selinux"' check_config_script: | ssh default /vagrant/script/check-config.sh unit_tests_script: | @@ -159,6 +161,12 @@ task: echo -e "Host localhost\n\tStrictHostKeyChecking no\t\nIdentityFile /root/.ssh/id_ed25519\n" >> /root/.ssh/config sed -e "s,PermitRootLogin.*,PermitRootLogin prohibit-password,g" -i /etc/ssh/sshd_config systemctl restart sshd + # TODO: remove centos-stream-8 once it has container-selinux >= 2.224.0. + case $DISTRO in + centos-7|centos-stream-8) + echo 'EXTRA_BUILDTAGS=runc_dmz_selinux_compat' >> /root/.ssh/environment + ;; + esac host_info_script: | uname -a # ----- diff --git a/README.md b/README.md index f36eea6b6ef..47e7dd13289 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ make BUILDTAGS="" |---------------|---------------------------------------|--------------------|---------------------| | `seccomp` | Syscall filtering using `libseccomp`. | yes | `libseccomp` | | `!runc_nodmz` | Reduce memory usage for CVE-2019-5736 protection by using a small C binary, [see `memfd-bind` for more details][contrib-memfd-bind]. `runc_nodmz` disables this feature and causes runc to use a different protection mechanism which will further increases memory usage temporarily during container startup. This feature can also be disabled at runtime by setting the `RUNC_DMZ=legacy` environment variable. | yes || +| `runc_dmz_selinux_compat` | Enables a SELinux workaround for older distributions. See [dmz README] for details. | no || The following build tags were used earlier, but are now obsoleted: - **nokmem** (since runc v1.0.0-rc94 kernel memory settings are ignored) @@ -76,6 +77,7 @@ The following build tags were used earlier, but are now obsoleted: - **selinux** (since runc v1.0.0-rc93 the feature is always enabled) [contrib-memfd-bind]: /contrib/cmd/memfd-bind/README.md + [dmz README]: /libcontainer/dmz/README.md ### Running the test suite diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 35f4f5df390..f5d55a1649e 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -457,6 +457,10 @@ func slicesContains[S ~[]E, E comparable](slice S, needle E) bool { } func isDmzBinarySafe(c *configs.Config) bool { + if !dmz.WorksWithSELinux(c) { + return false + } + // Because we set the dumpable flag in nsexec, the only time when it is // unsafe to use runc-dmz is when the container process would be able to // race against "runc init" and bypass the ptrace_may_access() checks. diff --git a/libcontainer/dmz/README.md b/libcontainer/dmz/README.md index 3cfa913ff68..b895c1a2fed 100644 --- a/libcontainer/dmz/README.md +++ b/libcontainer/dmz/README.md @@ -15,4 +15,19 @@ It also support all the architectures we support in runc. If the GOARCH we use for compiling doesn't support nolibc, it fallbacks to using the C stdlib. +## SELinux compatibility issue and a workaround + +Older SELinux policy can prevent runc to execute the dmz binary. The issue is +fixed in [container-selinux v2.224.0]. + +Distributions that do not have the above fix have to build runc with the +`runc_dmz_selinux_compat` build flag. If built with this flag, runc disables +dmz during runtime if SELinux is in enforced mode and the container SELinux +label is set. + +The alternative is to use [memfd-bind]. +>>>>>>> 4a472aeb (Workaround for dmz-vs-selinux issue) + [nolibc-upstream]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/include/nolibc?h=v6.6-rc3 +[container-selinux v2.224.0]: https://github.com/containers/container-selinux/releases/tag/v2.224.0 +[memfd-bind]: /contrib/cmd/memfd-bind/README.md diff --git a/libcontainer/dmz/selinux.go b/libcontainer/dmz/selinux.go new file mode 100644 index 00000000000..7a58e44fe2b --- /dev/null +++ b/libcontainer/dmz/selinux.go @@ -0,0 +1,10 @@ +//go:build !runc_dmz_selinux_compat || !linux + +package dmz + +import "github.com/opencontainers/runc/libcontainer/configs" + +// WorksWithSELinux tells whether runc-dmz can work with SELinux. +func WorksWithSELinux(*configs.Config) bool { + return true +} diff --git a/libcontainer/dmz/selinux_compat.go b/libcontainer/dmz/selinux_compat.go new file mode 100644 index 00000000000..066360a5b91 --- /dev/null +++ b/libcontainer/dmz/selinux_compat.go @@ -0,0 +1,25 @@ +//go:build linux && runc_dmz_selinux_compat + +package dmz + +import ( + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/selinux/go-selinux" +) + +// WorksWithSELinux tells whether runc-dmz can work with SELinux. +// +// Older SELinux policy can prevent runc to execute the dmz binary. The issue is +// fixed in container-selinux >= 2.224.0: +// +// - https://github.com/containers/container-selinux/issues/274 +// - https://github.com/containers/container-selinux/pull/280 +// +// Alas, there is is no easy way to do a runtime check if dmz works with +// SELinux, so distributions that do not have the above fix have to build runc +// with runc_dmz_selinux_compat build flag. If the flag is set, the code below +// is used, which results in disabling dmz in case container SELinux label is +// set and the selinux is in enforced mode. +func WorksWithSELinux(c *configs.Config) bool { + return c.ProcessLabel == "" || selinux.EnforceMode() != selinux.Enforcing +}