From bc4f1a4d25fb0edd5e15d8899f8f1c58c6c86699 Mon Sep 17 00:00:00 2001 From: Phil Estes Date: Wed, 24 Aug 2016 11:24:08 -0400 Subject: [PATCH 1/2] Add system.Stat support for darwin/macOS darwin had unbuildable support for our system.Stat() implementation. Docker-DCO-1.1-Signed-off-by: Phil Estes Upstream-commit: 76a416ac37e5bd184990c93ed19aa6fa544b7b03 Component: engine --- components/engine/pkg/system/stat_darwin.go | 32 +++++++++++++++++++ .../engine/pkg/system/stat_unsupported.go | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 components/engine/pkg/system/stat_darwin.go diff --git a/components/engine/pkg/system/stat_darwin.go b/components/engine/pkg/system/stat_darwin.go new file mode 100644 index 0000000000..f0742f59e5 --- /dev/null +++ b/components/engine/pkg/system/stat_darwin.go @@ -0,0 +1,32 @@ +package system + +import ( + "syscall" +) + +// fromStatT creates a system.StatT type from a syscall.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} + +// FromStatT loads a system.StatT from a syscall.Stat_t. +func FromStatT(s *syscall.Stat_t) (*StatT, error) { + return fromStatT(s) +} + +// Stat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Stat(path string) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Stat(path, s); err != nil { + return nil, err + } + return fromStatT(s) +} diff --git a/components/engine/pkg/system/stat_unsupported.go b/components/engine/pkg/system/stat_unsupported.go index f53e9de4d1..5d85f523cf 100644 --- a/components/engine/pkg/system/stat_unsupported.go +++ b/components/engine/pkg/system/stat_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!windows,!freebsd,!solaris,!openbsd +// +build !linux,!windows,!freebsd,!solaris,!openbsd,!darwin package system From b5333a8cfd1e00c03fb48c2be154445de2a272f3 Mon Sep 17 00:00:00 2001 From: Phil Estes Date: Tue, 23 Aug 2016 12:49:13 -0400 Subject: [PATCH 2/2] Don't start daemon in userns mode if graphdir inaccessible Warn the user and fail daemon start if the graphdir path has any elements which will deny access to the remapped root uid/gid. Docker-DCO-1.1-Signed-off-by: Phil Estes Upstream-commit: 43a1df6be2fa0c76b521680bbd5dc84db2cfd898 Component: engine --- components/engine/daemon/daemon_unix.go | 14 ++++++++++ components/engine/pkg/idtools/idtools_unix.go | 26 +++++++++++++++++++ .../engine/pkg/idtools/idtools_windows.go | 7 +++++ 3 files changed, 47 insertions(+) diff --git a/components/engine/daemon/daemon_unix.go b/components/engine/daemon/daemon_unix.go index 9d5fba366e..7e5b313e55 100644 --- a/components/engine/daemon/daemon_unix.go +++ b/components/engine/daemon/daemon_unix.go @@ -1004,6 +1004,20 @@ func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil { return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) } + // we also need to verify that any pre-existing directories in the path to + // the graphroot won't block access to remapped root--if any pre-existing directory + // has strict permissions that don't allow "x", container start will fail, so + // better to warn and fail now + dirPath := config.Root + for { + dirPath = filepath.Dir(dirPath) + if dirPath == "/" { + break + } + if !idtools.CanAccess(dirPath, rootUID, rootGID) { + return fmt.Errorf("A subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories.", config.Root) + } + } } return nil } diff --git a/components/engine/pkg/idtools/idtools_unix.go b/components/engine/pkg/idtools/idtools_unix.go index b57d6ef125..dcbb305f69 100644 --- a/components/engine/pkg/idtools/idtools_unix.go +++ b/components/engine/pkg/idtools/idtools_unix.go @@ -58,3 +58,29 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown } return nil } + +// CanAccess takes a valid (existing) directory and a uid, gid pair and determines +// if that uid, gid pair has access (execute bit) to the directory +func CanAccess(path string, uid, gid int) bool { + statInfo, err := system.Stat(path) + if err != nil { + return false + } + fileMode := os.FileMode(statInfo.Mode()) + permBits := fileMode.Perm() + return accessible(statInfo.UID() == uint32(uid), + statInfo.GID() == uint32(gid), permBits) +} + +func accessible(isOwner, isGroup bool, perms os.FileMode) bool { + if isOwner && (perms&0100 == 0100) { + return true + } + if isGroup && (perms&0010 == 0010) { + return true + } + if perms&0001 == 0001 { + return true + } + return false +} diff --git a/components/engine/pkg/idtools/idtools_windows.go b/components/engine/pkg/idtools/idtools_windows.go index c9e3c937cd..49f67e78c1 100644 --- a/components/engine/pkg/idtools/idtools_windows.go +++ b/components/engine/pkg/idtools/idtools_windows.go @@ -16,3 +16,10 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown } return nil } + +// CanAccess takes a valid (existing) directory and a uid, gid pair and determines +// if that uid, gid pair has access (execute bit) to the directory +// Windows does not require/support this function, so always return true +func CanAccess(path string, uid, gid int) bool { + return true +}