From 4d2d63546bcf77aec32a35bff9eb00e009be1072 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 10 Apr 2014 23:03:52 +0000 Subject: [PATCH] Add restrictions to proc in libcontainer Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- nsinit/init.go | 2 +- nsinit/mount.go | 67 ++++++++++++++++++++--------------- security/restrict/restrict.go | 46 ++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 29 deletions(-) create mode 100644 security/restrict/restrict.go diff --git a/nsinit/init.go b/nsinit/init.go index ce51db37..fb3a895a 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -61,7 +61,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol label.Init() ns.logger.Println("setup mount namespace") - if err := setupNewMountNamespace(rootfs, container.Mounts, console, container.ReadonlyFs, container.NoPivotRoot, container.Context["mount_label"]); err != nil { + if err := setupNewMountNamespace(rootfs, console, container); err != nil { return fmt.Errorf("setup mount namespace %s", err) } if err := system.Sethostname(container.Hostname); err != nil { diff --git a/nsinit/mount.go b/nsinit/mount.go index dd6b1c8a..e4869a0e 100644 --- a/nsinit/mount.go +++ b/nsinit/mount.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" + "github.com/dotcloud/docker/pkg/libcontainer/security/restrict" "github.com/dotcloud/docker/pkg/system" "io/ioutil" "os" @@ -21,9 +22,9 @@ const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NOD // // There is no need to unmount the new mounts because as soon as the mount namespace // is no longer in use, the mounts will be removed automatically -func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, console string, readonly, noPivotRoot bool, mountLabel string) error { +func setupNewMountNamespace(rootfs, console string, container *libcontainer.Container) error { flag := syscall.MS_PRIVATE - if noPivotRoot { + if container.NoPivotRoot { flag = syscall.MS_SLAVE } if err := system.Mount("", "/", "", uintptr(flag|syscall.MS_REC), ""); err != nil { @@ -32,44 +33,28 @@ func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, cons if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mouting %s as bind %s", rootfs, err) } - if err := mountSystem(rootfs, mountLabel); err != nil { + if err := mountSystem(rootfs, container.Context["mount_label"]); err != nil { return fmt.Errorf("mount system %s", err) } - - for _, m := range bindMounts { - var ( - flags = syscall.MS_BIND | syscall.MS_REC - dest = filepath.Join(rootfs, m.Destination) - ) - if !m.Writable { - flags = flags | syscall.MS_RDONLY - } - if err := system.Mount(m.Source, dest, "bind", uintptr(flags), ""); err != nil { - return fmt.Errorf("mounting %s into %s %s", m.Source, dest, err) - } - if !m.Writable { - if err := system.Mount(m.Source, dest, "bind", uintptr(flags|syscall.MS_REMOUNT), ""); err != nil { - return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) - } - } - if m.Private { - if err := system.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { - return fmt.Errorf("mounting %s private %s", dest, err) - } + if err := setupBindmounts(rootfs, container.Mounts); err != nil { + return fmt.Errorf("bind mounts %s", err) + } + if restrictionPath := container.Context["restriction_path"]; restrictionPath != "" { + if err := restrict.Restrict(rootfs, restrictionPath); err != nil { + return fmt.Errorf("restrict %s", err) } } - if err := copyDevNodes(rootfs); err != nil { return fmt.Errorf("copy dev nodes %s", err) } - if err := setupPtmx(rootfs, console, mountLabel); err != nil { + if err := setupPtmx(rootfs, console, container.Context["mount_label"]); err != nil { return err } if err := system.Chdir(rootfs); err != nil { return fmt.Errorf("chdir into %s %s", rootfs, err) } - if noPivotRoot { + if container.NoPivotRoot { if err := rootMsMove(rootfs); err != nil { return err } @@ -79,7 +64,7 @@ func setupNewMountNamespace(rootfs string, bindMounts []libcontainer.Mount, cons } } - if readonly { + if container.ReadonlyFs { if err := system.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mounting %s as readonly %s", rootfs, err) } @@ -263,3 +248,29 @@ func remountSys() error { } return nil } + +func setupBindmounts(rootfs string, bindMounts []libcontainer.Mount) error { + for _, m := range bindMounts { + var ( + flags = syscall.MS_BIND | syscall.MS_REC + dest = filepath.Join(rootfs, m.Destination) + ) + if !m.Writable { + flags = flags | syscall.MS_RDONLY + } + if err := system.Mount(m.Source, dest, "bind", uintptr(flags), ""); err != nil { + return fmt.Errorf("mounting %s into %s %s", m.Source, dest, err) + } + if !m.Writable { + if err := system.Mount(m.Source, dest, "bind", uintptr(flags|syscall.MS_REMOUNT), ""); err != nil { + return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) + } + } + if m.Private { + if err := system.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { + return fmt.Errorf("mounting %s private %s", dest, err) + } + } + } + return nil +} diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go new file mode 100644 index 00000000..d5c1dbbe --- /dev/null +++ b/security/restrict/restrict.go @@ -0,0 +1,46 @@ +package restrict + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/system" + "path/filepath" + "syscall" +) + +const flags = syscall.MS_BIND | syscall.MS_REC | syscall.MS_RDONLY + +var restrictions = map[string]string{ + // dirs + "/proc/sys": "", + "/proc/irq": "", + "/proc/acpi": "", + + // files + "/proc/sysrq-trigger": "/dev/null", + "/proc/kcore": "/dev/null", +} + +// Restrict locks down access to many areas of proc +// by using the asumption that the user does not have mount caps to +// revert the changes made here +func Restrict(rootfs, empty string) error { + for dest, source := range restrictions { + dest = filepath.Join(rootfs, dest) + + // we don't have a "/dev/null" for dirs so have the requester pass a dir + // for us to bind mount + switch source { + case "": + source = empty + default: + source = filepath.Join(rootfs, source) + } + if err := system.Mount(source, dest, "bind", flags, ""); err != nil { + return fmt.Errorf("unable to mount %s over %s %s", source, dest, err) + } + if err := system.Mount("", dest, "bind", flags|syscall.MS_REMOUNT, ""); err != nil { + return fmt.Errorf("unable to mount %s over %s %s", source, dest, err) + } + } + return nil +}