Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Commit

Permalink
Update restrictions for better handling of mounts
Browse files Browse the repository at this point in the history
This also cleans up some of the left over restriction paths code from
before.
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
  • Loading branch information
crosbymichael committed May 1, 2014
1 parent dcc4061 commit df78075
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 53 deletions.
7 changes: 2 additions & 5 deletions mount/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions nsinit/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
65 changes: 20 additions & 45 deletions security/restrict/restrict.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
2 changes: 1 addition & 1 deletion security/restrict/unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ package restrict

import "fmt"

func Restrict(rootfs, empty string) error {
func Restrict() error {
return fmt.Errorf("not supported")
}

0 comments on commit df78075

Please sign in to comment.