From 5a6b042e5395660ac8a6e3cc33227ca66df7c835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Petazzoni?= Date: Wed, 30 Apr 2014 18:00:42 -0700 Subject: [PATCH 1/6] Mount /proc and /sys read-only, except in privileged containers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been pointed out that some files in /proc and /sys can be used to break out of containers. However, if those filesystems are mounted read-only, most of the known exploits are mitigated, since they rely on writing some file in those filesystems. This does not replace security modules (like SELinux or AppArmor), it is just another layer of security. Likewise, it doesn't mean that the other mitigations (shadowing parts of /proc or /sys with bind mounts) are useless. Those measures are still useful. As such, the shadowing of /proc/kcore is still enabled with both LXC and native drivers. Special care has to be taken with /proc/1/attr, which still needs to be mounted read-write in order to enable the AppArmor profile. It is bind-mounted from a private read-write mount of procfs. All that enforcement is done in dockerinit. The code doing the real work is in libcontainer. The init function for the LXC driver calls the function from libcontainer to avoid code duplication. Docker-DCO-1.1-Signed-off-by: Jérôme Petazzoni (github: jpetazzo) --- mount/init.go | 12 +---- nsinit/init.go | 14 ++++-- security/restrict/restrict.go | 86 ++++++++++++++++++++++------------- 3 files changed, 68 insertions(+), 44 deletions(-) diff --git a/mount/init.go b/mount/init.go index 735970cde..cc3ce2158 100644 --- a/mount/init.go +++ b/mount/init.go @@ -11,7 +11,6 @@ import ( "github.com/dotcloud/docker/pkg/label" "github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer/mount/nodes" - "github.com/dotcloud/docker/pkg/libcontainer/security/restrict" "github.com/dotcloud/docker/pkg/system" ) @@ -51,11 +50,6 @@ func InitializeMountNamespace(rootfs, console string, container *libcontainer.Co if err := nodes.CopyN(rootfs, nodes.DefaultNodes); err != nil { return fmt.Errorf("copy dev nodes %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 := SetupPtmx(rootfs, console, container.Context["mount_label"]); err != nil { return err } @@ -124,10 +118,11 @@ func setupBindmounts(rootfs string, bindMounts libcontainer.Mounts) error { } // TODO: this is crappy right now and should be cleaned up with a better way of handling system and -// standard bind mounts allowing them to be more dymanic +// standard bind mounts allowing them to be more dynamic func newSystemMounts(rootfs, mountLabel string, mounts libcontainer.Mounts) []mount { systemMounts := []mount{ {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags}, + {source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags}, } if len(mounts.OfType("devtmpfs")) == 1 { @@ -138,8 +133,5 @@ func newSystemMounts(rootfs, mountLabel string, mounts libcontainer.Mounts) []mo mount{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)}, ) - if len(mounts.OfType("sysfs")) == 1 { - systemMounts = append(systemMounts, mount{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags}) - } return systemMounts } diff --git a/nsinit/init.go b/nsinit/init.go index faec12af3..bafb877cd 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -16,6 +16,7 @@ import ( "github.com/dotcloud/docker/pkg/libcontainer/mount" "github.com/dotcloud/docker/pkg/libcontainer/network" "github.com/dotcloud/docker/pkg/libcontainer/security/capabilities" + "github.com/dotcloud/docker/pkg/libcontainer/security/restrict" "github.com/dotcloud/docker/pkg/libcontainer/utils" "github.com/dotcloud/docker/pkg/system" "github.com/dotcloud/docker/pkg/user" @@ -68,18 +69,25 @@ func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, if err := system.Sethostname(container.Hostname); err != nil { return fmt.Errorf("sethostname %s", err) } - if err := FinalizeNamespace(container); err != nil { - return fmt.Errorf("finalize namespace %s", err) - } runtime.LockOSThread() + if restrictionPath := container.Context["restriction_path"]; restrictionPath != "" { + if err := restrict.Restrict("/", restrictionPath); err != nil { + return err + } + } + if err := apparmor.ApplyProfile(os.Getpid(), container.Context["apparmor_profile"]); err != nil { return err } if err := label.SetProcessLabel(container.Context["process_label"]); err != nil { return fmt.Errorf("set process label %s", err) } + + if err := FinalizeNamespace(container); err != nil { + return fmt.Errorf("finalize namespace %s", err) + } return system.Execv(args[0], args[0:], container.Env) } diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go index 291d6ca5d..8c08ea180 100644 --- a/security/restrict/restrict.go +++ b/security/restrict/restrict.go @@ -9,43 +9,67 @@ import ( "github.com/dotcloud/docker/pkg/system" ) -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", +// "restrictions" are container paths (files, directories, whatever) that have to be masked. +// maskPath is a "safe" path to be mounted over maskedPath. It can take two special values: +// - if it is "", then nothing is mounted; +// - if it is "EMPTY", then an empty directory is mounted instead. +// If remountRO is true then the maskedPath is remounted read-only (regardless of whether a maskPath was used). +type restriction struct { + maskedPath string + maskPath string + remountRO bool } -// 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) +var restrictions = []restriction{ + {"/proc", "", true}, + {"/sys", "", true}, + {"/proc/kcore", "/dev/null", false}, +} - // 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 { - if os.IsNotExist(err) { - continue +// This has to be called while the container still has CAP_SYS_ADMIN (to be able to perform mounts). +// However, afterwards, CAP_SYS_ADMIN should be dropped (otherwise the user will be able to revert those changes). +// "empty" should be the path to an empty directory. +func Restrict(rootfs, empty string) error { + for _, restriction := range restrictions { + dest := filepath.Join(rootfs, restriction.maskedPath) + if restriction.maskPath != "" { + var source string + if restriction.maskPath == "EMPTY" { + source = empty + } else { + source = filepath.Join(rootfs, restriction.maskPath) + } + if err := system.Mount(source, dest, "", syscall.MS_BIND, ""); err != nil { + return fmt.Errorf("unable to bind-mount %s over %s: %s", source, dest, err) } - 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) + if restriction.remountRO { + if err := system.Mount("", dest, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil { + return fmt.Errorf("unable to remount %s readonly: %s", dest, err) + } } } + + // This weird trick will allow us to mount /proc read-only, while being able to use AppArmor. + // This is because apparently, loading an AppArmor profile requires write access to /proc/1/attr. + // So we do another mount of procfs, ensure it's write-able, and bind-mount a subset of it. + tmpProcPath := filepath.Join(rootfs, ".proc") + if err := os.Mkdir(tmpProcPath, 0700); err != nil { + return fmt.Errorf("unable to create temporary proc mountpoint %s: %s", tmpProcPath, err) + } + if err := system.Mount("proc", tmpProcPath, "proc", 0, ""); err != nil { + return fmt.Errorf("unable to mount proc on temporary proc mountpoint: %s", err) + } + if err := system.Mount("proc", tmpProcPath, "", syscall.MS_REMOUNT, ""); err != nil { + return fmt.Errorf("unable to remount proc read-write: %s", err) + } + rwAttrPath := filepath.Join(rootfs, ".proc", "1", "attr") + roAttrPath := filepath.Join(rootfs, "proc", "1", "attr") + if err := system.Mount(rwAttrPath, roAttrPath, "", syscall.MS_BIND, ""); err != nil { + return fmt.Errorf("unable to bind-mount %s on %s: %s", rwAttrPath, roAttrPath, err) + } + if err := system.Unmount(tmpProcPath, 0); err != nil { + return fmt.Errorf("unable to unmount temporary proc filesystem: %s", err) + } return nil } From dcc4061db37e26193253d7e049f8494d2a8f4c4c Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 30 Apr 2014 19:09:25 -0700 Subject: [PATCH 2/6] Update to enable cross compile Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- nsinit/init.go | 1 - security/restrict/restrict.go | 2 ++ security/restrict/unsupported.go | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 security/restrict/unsupported.go diff --git a/nsinit/init.go b/nsinit/init.go index bafb877cd..90b97a9f9 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -84,7 +84,6 @@ func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, if err := label.SetProcessLabel(container.Context["process_label"]); err != nil { return fmt.Errorf("set process label %s", err) } - if err := FinalizeNamespace(container); err != nil { return fmt.Errorf("finalize namespace %s", err) } diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go index 8c08ea180..a9bdc4bac 100644 --- a/security/restrict/restrict.go +++ b/security/restrict/restrict.go @@ -1,3 +1,5 @@ +// +build linux + package restrict import ( diff --git a/security/restrict/unsupported.go b/security/restrict/unsupported.go new file mode 100644 index 000000000..6898baab3 --- /dev/null +++ b/security/restrict/unsupported.go @@ -0,0 +1,9 @@ +// +build !linux + +package restrict + +import "fmt" + +func Restrict(rootfs, empty string) error { + return fmt.Errorf("not supported") +} From df78075a8e295561f824db71bae82c525a6c1f7c Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 1 May 2014 10:08:18 -0700 Subject: [PATCH 3/6] Update restrictions for better handling of mounts This also cleans up some of the left over restriction paths code from before. Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- mount/init.go | 7 +--- nsinit/init.go | 4 +- security/restrict/restrict.go | 65 ++++++++++---------------------- security/restrict/unsupported.go | 2 +- 4 files changed, 25 insertions(+), 53 deletions(-) diff --git a/mount/init.go b/mount/init.go index cc3ce2158..6a54f2444 100644 --- a/mount/init.go +++ b/mount/init.go @@ -123,15 +123,12 @@ func newSystemMounts(rootfs, mountLabel string, mounts libcontainer.Mounts) []mo systemMounts := []mount{ {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags}, {source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags}, + {source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1777,size=65536k", mountLabel)}, + {source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)}, } if len(mounts.OfType("devtmpfs")) == 1 { systemMounts = append(systemMounts, mount{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)}) } - systemMounts = append(systemMounts, - mount{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1777,size=65536k", mountLabel)}, - mount{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)}, - ) - return systemMounts } diff --git a/nsinit/init.go b/nsinit/init.go index 90b97a9f9..755847948 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -72,8 +72,8 @@ func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, runtime.LockOSThread() - if restrictionPath := container.Context["restriction_path"]; restrictionPath != "" { - if err := restrict.Restrict("/", restrictionPath); err != nil { + if container.Context["restrictions"] != "" { + if err := restrict.Restrict(); err != nil { return err } } diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go index a9bdc4bac..2b7cea5a4 100644 --- a/security/restrict/restrict.go +++ b/security/restrict/restrict.go @@ -11,67 +11,42 @@ import ( "github.com/dotcloud/docker/pkg/system" ) -// "restrictions" are container paths (files, directories, whatever) that have to be masked. -// maskPath is a "safe" path to be mounted over maskedPath. It can take two special values: -// - if it is "", then nothing is mounted; -// - if it is "EMPTY", then an empty directory is mounted instead. -// If remountRO is true then the maskedPath is remounted read-only (regardless of whether a maskPath was used). -type restriction struct { - maskedPath string - maskPath string - remountRO bool -} - -var restrictions = []restriction{ - {"/proc", "", true}, - {"/sys", "", true}, - {"/proc/kcore", "/dev/null", false}, -} - // This has to be called while the container still has CAP_SYS_ADMIN (to be able to perform mounts). // However, afterwards, CAP_SYS_ADMIN should be dropped (otherwise the user will be able to revert those changes). -// "empty" should be the path to an empty directory. -func Restrict(rootfs, empty string) error { - for _, restriction := range restrictions { - dest := filepath.Join(rootfs, restriction.maskedPath) - if restriction.maskPath != "" { - var source string - if restriction.maskPath == "EMPTY" { - source = empty - } else { - source = filepath.Join(rootfs, restriction.maskPath) - } - if err := system.Mount(source, dest, "", syscall.MS_BIND, ""); err != nil { - return fmt.Errorf("unable to bind-mount %s over %s: %s", source, dest, err) - } - } - if restriction.remountRO { - if err := system.Mount("", dest, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil { - return fmt.Errorf("unable to remount %s readonly: %s", dest, err) - } +func Restrict() error { + // remount proc and sys as readonly + for _, dest := range []string{"proc", "sys"} { + if err := system.Mount("", dest, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil { + return fmt.Errorf("unable to remount %s readonly: %s", dest, err) } } + if err := system.Mount("/proc/kcore", "/dev/null", "", syscall.MS_BIND, ""); err != nil { + return fmt.Errorf("unable to bind-mount /dev/null over /proc/kcore") + } + // This weird trick will allow us to mount /proc read-only, while being able to use AppArmor. // This is because apparently, loading an AppArmor profile requires write access to /proc/1/attr. // So we do another mount of procfs, ensure it's write-able, and bind-mount a subset of it. - tmpProcPath := filepath.Join(rootfs, ".proc") - if err := os.Mkdir(tmpProcPath, 0700); err != nil { - return fmt.Errorf("unable to create temporary proc mountpoint %s: %s", tmpProcPath, err) + var ( + rwAttrPath = filepath.Join(".proc", "1", "attr") + roAttrPath = filepath.Join("proc", "1", "attr") + ) + + if err := os.Mkdir(".proc", 0700); err != nil { + return fmt.Errorf("unable to create temporary proc mountpoint .proc: %s", err) } - if err := system.Mount("proc", tmpProcPath, "proc", 0, ""); err != nil { + if err := system.Mount("proc", ".proc", "proc", 0, ""); err != nil { return fmt.Errorf("unable to mount proc on temporary proc mountpoint: %s", err) } - if err := system.Mount("proc", tmpProcPath, "", syscall.MS_REMOUNT, ""); err != nil { + if err := system.Mount("proc", ".proc", "", syscall.MS_REMOUNT, ""); err != nil { return fmt.Errorf("unable to remount proc read-write: %s", err) } - rwAttrPath := filepath.Join(rootfs, ".proc", "1", "attr") - roAttrPath := filepath.Join(rootfs, "proc", "1", "attr") if err := system.Mount(rwAttrPath, roAttrPath, "", syscall.MS_BIND, ""); err != nil { return fmt.Errorf("unable to bind-mount %s on %s: %s", rwAttrPath, roAttrPath, err) } - if err := system.Unmount(tmpProcPath, 0); err != nil { + if err := system.Unmount(".proc", 0); err != nil { return fmt.Errorf("unable to unmount temporary proc filesystem: %s", err) } - return nil + return os.RemoveAll(".proc") } diff --git a/security/restrict/unsupported.go b/security/restrict/unsupported.go index 6898baab3..464e8d498 100644 --- a/security/restrict/unsupported.go +++ b/security/restrict/unsupported.go @@ -4,6 +4,6 @@ package restrict import "fmt" -func Restrict(rootfs, empty string) error { +func Restrict() error { return fmt.Errorf("not supported") } From ab60f78e8b75b118d5b97da2fe3346486928eb19 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 1 May 2014 11:11:29 -0700 Subject: [PATCH 4/6] Mount attr and task as rw for selinux support Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- security/restrict/restrict.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go index 2b7cea5a4..74de70aa6 100644 --- a/security/restrict/restrict.go +++ b/security/restrict/restrict.go @@ -28,11 +28,6 @@ func Restrict() error { // This weird trick will allow us to mount /proc read-only, while being able to use AppArmor. // This is because apparently, loading an AppArmor profile requires write access to /proc/1/attr. // So we do another mount of procfs, ensure it's write-able, and bind-mount a subset of it. - var ( - rwAttrPath = filepath.Join(".proc", "1", "attr") - roAttrPath = filepath.Join("proc", "1", "attr") - ) - if err := os.Mkdir(".proc", 0700); err != nil { return fmt.Errorf("unable to create temporary proc mountpoint .proc: %s", err) } @@ -42,8 +37,10 @@ func Restrict() error { if err := system.Mount("proc", ".proc", "", syscall.MS_REMOUNT, ""); err != nil { return fmt.Errorf("unable to remount proc read-write: %s", err) } - if err := system.Mount(rwAttrPath, roAttrPath, "", syscall.MS_BIND, ""); err != nil { - return fmt.Errorf("unable to bind-mount %s on %s: %s", rwAttrPath, roAttrPath, err) + for _, path := range []string{"attr", "task"} { + if err := system.Mount(filepath.Join(".proc", "1", path), filepath.Join("proc", "1", path), "", syscall.MS_BIND, ""); err != nil { + return fmt.Errorf("unable to bind-mount %s: %s", path, err) + } } if err := system.Unmount(".proc", 0); err != nil { return fmt.Errorf("unable to unmount temporary proc filesystem: %s", err) From 3720dca2417ec0eb345296b7a7acaca51f742f40 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 1 May 2014 13:55:23 -0700 Subject: [PATCH 5/6] Fix /proc/kcore mount of /dev/null Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- security/restrict/restrict.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go index 74de70aa6..411bc0680 100644 --- a/security/restrict/restrict.go +++ b/security/restrict/restrict.go @@ -20,8 +20,7 @@ func Restrict() error { return fmt.Errorf("unable to remount %s readonly: %s", dest, err) } } - - if err := system.Mount("/proc/kcore", "/dev/null", "", syscall.MS_BIND, ""); err != nil { + if err := system.Mount("/dev/null", "/proc/kcore", "", syscall.MS_BIND, ""); err != nil { return fmt.Errorf("unable to bind-mount /dev/null over /proc/kcore") } From 08dbd5370697470cbbe0ae92982a7a8ca9b48546 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 1 May 2014 19:09:12 -0700 Subject: [PATCH 6/6] Apply apparmor before restrictions There is not need for the remount hack, we use aa_change_onexec so the apparmor profile is not applied until we exec the users app. Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- console/console.go | 5 +++-- nsinit/init.go | 13 ++++++------- security/restrict/restrict.go | 25 +------------------------ 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/console/console.go b/console/console.go index 05cd08a92..5f06aea22 100644 --- a/console/console.go +++ b/console/console.go @@ -4,11 +4,12 @@ package console import ( "fmt" - "github.com/dotcloud/docker/pkg/label" - "github.com/dotcloud/docker/pkg/system" "os" "path/filepath" "syscall" + + "github.com/dotcloud/docker/pkg/label" + "github.com/dotcloud/docker/pkg/system" ) // Setup initializes the proper /dev/console inside the rootfs path diff --git a/nsinit/init.go b/nsinit/init.go index 755847948..22345f603 100644 --- a/nsinit/init.go +++ b/nsinit/init.go @@ -72,18 +72,17 @@ func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, runtime.LockOSThread() + if err := apparmor.ApplyProfile(container.Context["apparmor_profile"]); err != nil { + return fmt.Errorf("set apparmor profile %s: %s", container.Context["apparmor_profile"], err) + } + if err := label.SetProcessLabel(container.Context["process_label"]); err != nil { + return fmt.Errorf("set process label %s", err) + } if container.Context["restrictions"] != "" { if err := restrict.Restrict(); err != nil { return err } } - - if err := apparmor.ApplyProfile(os.Getpid(), container.Context["apparmor_profile"]); err != nil { - return err - } - if err := label.SetProcessLabel(container.Context["process_label"]); err != nil { - return fmt.Errorf("set process label %s", err) - } if err := FinalizeNamespace(container); err != nil { return fmt.Errorf("finalize namespace %s", err) } diff --git a/security/restrict/restrict.go b/security/restrict/restrict.go index 411bc0680..cfff09f51 100644 --- a/security/restrict/restrict.go +++ b/security/restrict/restrict.go @@ -4,8 +4,6 @@ package restrict import ( "fmt" - "os" - "path/filepath" "syscall" "github.com/dotcloud/docker/pkg/system" @@ -23,26 +21,5 @@ func Restrict() error { if err := system.Mount("/dev/null", "/proc/kcore", "", syscall.MS_BIND, ""); err != nil { return fmt.Errorf("unable to bind-mount /dev/null over /proc/kcore") } - - // This weird trick will allow us to mount /proc read-only, while being able to use AppArmor. - // This is because apparently, loading an AppArmor profile requires write access to /proc/1/attr. - // So we do another mount of procfs, ensure it's write-able, and bind-mount a subset of it. - if err := os.Mkdir(".proc", 0700); err != nil { - return fmt.Errorf("unable to create temporary proc mountpoint .proc: %s", err) - } - if err := system.Mount("proc", ".proc", "proc", 0, ""); err != nil { - return fmt.Errorf("unable to mount proc on temporary proc mountpoint: %s", err) - } - if err := system.Mount("proc", ".proc", "", syscall.MS_REMOUNT, ""); err != nil { - return fmt.Errorf("unable to remount proc read-write: %s", err) - } - for _, path := range []string{"attr", "task"} { - if err := system.Mount(filepath.Join(".proc", "1", path), filepath.Join("proc", "1", path), "", syscall.MS_BIND, ""); err != nil { - return fmt.Errorf("unable to bind-mount %s: %s", path, err) - } - } - if err := system.Unmount(".proc", 0); err != nil { - return fmt.Errorf("unable to unmount temporary proc filesystem: %s", err) - } - return os.RemoveAll(".proc") + return nil }