mirror of
https://github.com/opencontainers/runc.git
synced 2026-06-24 08:48:44 +00:00
(This is a cherry-pick ofcdff09ab87but modified so that changes like8e8b136c49anda60933bb24don't also need to be backported. Ideally we would backport the entire "remove all mount logic from nsexec" series, but that would be a bit too much.) Our previous test for whether we can mount on top of /proc incorrectly assumed that it would only be called with bind-mount sources. This meant that having a non bind-mount entry for a pseudo-filesystem (like overlayfs) with a dummy source set to /proc on the host would let you bypass the check, which could easily lead to security issues. In addition, the check should be applied more uniformly to all mount types, so fix that as well. And add some tests for some of the tricky cases to make sure we protect against them properly. Fixes:331692baa7("Only allow proc mount if it is procfs") Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
193 lines
4.5 KiB
Go
193 lines
4.5 KiB
Go
package libcontainer
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
)
|
|
|
|
func TestCheckMountDestInProc(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/proc/sys",
|
|
Source: "/proc/sys",
|
|
Device: "bind",
|
|
Flags: unix.MS_BIND,
|
|
}
|
|
dest := "/rootfs/proc/sys"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err == nil {
|
|
t.Fatal("destination inside proc should return an error")
|
|
}
|
|
}
|
|
|
|
func TestCheckProcMountOnProc(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/proc",
|
|
Source: "foo",
|
|
Device: "proc",
|
|
}
|
|
dest := "/rootfs/proc/"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err != nil {
|
|
t.Fatalf("procfs type mount on /proc should not return an error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckBindMountOnProc(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/proc",
|
|
Source: "/proc/self",
|
|
Device: "bind",
|
|
Flags: unix.MS_BIND,
|
|
}
|
|
dest := "/rootfs/proc/"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err != nil {
|
|
t.Fatalf("bind-mount of procfs on top of /proc should not return an error (for now): %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckTrickyMountOnProc(t *testing.T) {
|
|
// Make a non-bind mount that looks like a bit like a bind-mount.
|
|
m := &configs.Mount{
|
|
Destination: "/proc",
|
|
Source: "/proc",
|
|
Device: "overlay",
|
|
Data: "lowerdir=/tmp/fakeproc,upperdir=/tmp/fakeproc2,workdir=/tmp/work",
|
|
}
|
|
dest := "/rootfs/proc/"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err == nil {
|
|
t.Fatalf("dodgy overlayfs mount on top of /proc should return an error")
|
|
}
|
|
}
|
|
|
|
func TestCheckTrickyBindMountOnProc(t *testing.T) {
|
|
// Make a bind mount that looks like it might be a procfs mount.
|
|
m := &configs.Mount{
|
|
Destination: "/proc",
|
|
Source: "/sys",
|
|
Device: "proc",
|
|
Flags: unix.MS_BIND,
|
|
}
|
|
dest := "/rootfs/proc/"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err == nil {
|
|
t.Fatalf("dodgy bind-mount on top of /proc should return an error")
|
|
}
|
|
}
|
|
|
|
func TestCheckMountDestInSys(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/sys/fs/cgroup",
|
|
Source: "tmpfs",
|
|
Device: "tmpfs",
|
|
}
|
|
dest := "/rootfs//sys/fs/cgroup"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err != nil {
|
|
t.Fatalf("destination inside /sys should not return an error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckMountDestFalsePositive(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/sysfiles/fs/cgroup",
|
|
Source: "tmpfs",
|
|
Device: "tmpfs",
|
|
}
|
|
dest := "/rootfs/sysfiles/fs/cgroup"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCheckMountDestNsLastPid(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/proc/sys/kernel/ns_last_pid",
|
|
Source: "lxcfs",
|
|
Device: "fuse.lxcfs",
|
|
}
|
|
dest := "/rootfs/proc/sys/kernel/ns_last_pid"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err != nil {
|
|
t.Fatalf("/proc/sys/kernel/ns_last_pid should not return an error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckCryptoFipsEnabled(t *testing.T) {
|
|
m := &configs.Mount{
|
|
Destination: "/proc/sys/crypto/fips_enabled",
|
|
Source: "tmpfs",
|
|
Device: "tmpfs",
|
|
}
|
|
dest := "/rootfs/proc/sys/crypto/fips_enabled"
|
|
err := checkProcMount("/rootfs", dest, m, m.Source)
|
|
if err != nil {
|
|
t.Fatalf("/proc/sys/crypto/fips_enabled should not return an error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNeedsSetupDev(t *testing.T) {
|
|
config := &configs.Config{
|
|
Mounts: []*configs.Mount{
|
|
{
|
|
Device: "bind",
|
|
Source: "/dev",
|
|
Destination: "/dev",
|
|
},
|
|
},
|
|
}
|
|
if needsSetupDev(config) {
|
|
t.Fatal("expected needsSetupDev to be false, got true")
|
|
}
|
|
}
|
|
|
|
func TestNeedsSetupDevStrangeSource(t *testing.T) {
|
|
config := &configs.Config{
|
|
Mounts: []*configs.Mount{
|
|
{
|
|
Device: "bind",
|
|
Source: "/devx",
|
|
Destination: "/dev",
|
|
},
|
|
},
|
|
}
|
|
if needsSetupDev(config) {
|
|
t.Fatal("expected needsSetupDev to be false, got true")
|
|
}
|
|
}
|
|
|
|
func TestNeedsSetupDevStrangeDest(t *testing.T) {
|
|
config := &configs.Config{
|
|
Mounts: []*configs.Mount{
|
|
{
|
|
Device: "bind",
|
|
Source: "/dev",
|
|
Destination: "/devx",
|
|
},
|
|
},
|
|
}
|
|
if !needsSetupDev(config) {
|
|
t.Fatal("expected needsSetupDev to be true, got false")
|
|
}
|
|
}
|
|
|
|
func TestNeedsSetupDevStrangeSourceDest(t *testing.T) {
|
|
config := &configs.Config{
|
|
Mounts: []*configs.Mount{
|
|
{
|
|
Device: "bind",
|
|
Source: "/devx",
|
|
Destination: "/devx",
|
|
},
|
|
},
|
|
}
|
|
if !needsSetupDev(config) {
|
|
t.Fatal("expected needsSetupDev to be true, got false")
|
|
}
|
|
}
|