diff --git a/api/grpc/server/server_linux.go b/api/grpc/server/server_linux.go index 5b6a1e3..be7a148 100644 --- a/api/grpc/server/server_linux.go +++ b/api/grpc/server/server_linux.go @@ -15,7 +15,7 @@ import ( "github.com/opencontainers/runc/libcontainer" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/system" - ocs "github.com/opencontainers/specs/specs-go" + ocs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" diff --git a/hack/vendor.sh b/hack/vendor.sh index 31c7df2..bb074e1 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -14,8 +14,8 @@ clone git github.com/docker/go-units 5d2041e26a699eaca682e2ea41c8f891e1060444 clone git github.com/godbus/dbus e2cf28118e66a6a63db46cf6088a35d2054d3bb0 clone git github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998 clone git github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3 -clone git github.com/opencontainers/runc e87436998478d222be209707503c27f6f91be0c5 -clone git github.com/opencontainers/specs 3ce138b1934bf227a418e241ead496c383eaba1c +clone git github.com/opencontainers/runc 9c89737e6e117a8be5a4980bc9795fe1a2b1028e +clone git github.com/opencontainers/runtime-spec f955d90e70a98ddfb886bd930ffd076da9b67998 clone git github.com/rcrowley/go-metrics eeba7bd0dd01ace6e690fa833b3f22aaec29af43 clone git github.com/satori/go.uuid f9ab0dce87d815821e221626b772e3475a0d2749 clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 diff --git a/integration-test/bundle_utils_test.go b/integration-test/bundle_utils_test.go index b9d6be8..3ffad2e 100644 --- a/integration-test/bundle_utils_test.go +++ b/integration-test/bundle_utils_test.go @@ -8,7 +8,7 @@ import ( "path/filepath" "reflect" - ocs "github.com/opencontainers/specs/specs-go" + ocs "github.com/opencontainers/runtime-spec/specs-go" ) var ( diff --git a/runtime/container_linux.go b/runtime/container_linux.go index 2c6bc7f..4f03219 100644 --- a/runtime/container_linux.go +++ b/runtime/container_linux.go @@ -14,7 +14,7 @@ import ( "github.com/docker/containerd/specs" "github.com/opencontainers/runc/libcontainer" - ocs "github.com/opencontainers/specs/specs-go" + ocs "github.com/opencontainers/runtime-spec/specs-go" ) func getRootIDs(s *specs.Spec) (int, int, error) { diff --git a/specs/spec_linux.go b/specs/spec_linux.go index b3772c8..205f1c8 100644 --- a/specs/spec_linux.go +++ b/specs/spec_linux.go @@ -1,6 +1,6 @@ package specs -import ocs "github.com/opencontainers/specs/specs-go" +import ocs "github.com/opencontainers/runtime-spec/specs-go" type ( ProcessSpec ocs.Process diff --git a/specs/spec_windows.go b/specs/spec_windows.go index e21f6be..006a0ef 100644 --- a/specs/spec_windows.go +++ b/specs/spec_windows.go @@ -1,6 +1,6 @@ package specs -// Temporary Windows version of the spec in lieu of opencontainers/specs/specs-go having +// Temporary Windows version of the spec in lieu of opencontainers/runtime-spec/specs-go having // Windows support currently. type ( @@ -8,7 +8,7 @@ type ( ProcessSpec Process ) -// This is a temporary module in lieu of opencontainers/specs/specs-go being compatible +// This is a temporary module in lieu of opencontainers/runtime-spec/specs-go being compatible // currently on Windows. // Process contains information to start a specific application inside the container. diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/SPEC.md b/vendor/src/github.com/opencontainers/runc/libcontainer/SPEC.md index 221545c..70e24a0 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/SPEC.md +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/SPEC.md @@ -90,7 +90,7 @@ in tmpfs. After `/dev/null` has been setup we check for any external links between the container's io, STDIN, STDOUT, STDERR. If the container's io is pointing -to `/dev/null` outside the container we close and `dup2` the the `/dev/null` +to `/dev/null` outside the container we close and `dup2` the `/dev/null` that is local to the container's rootfs. diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go index 114f002..c3cfb03 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go @@ -349,7 +349,7 @@ func writeFile(dir, file, data string) error { // Normally dir should not be empty, one case is that cgroup subsystem // is not mounted, we will get empty dir, and we want it fail here. if dir == "" { - return fmt.Errorf("no such directory for %s.", file) + return fmt.Errorf("no such directory for %s", file) } if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700); err != nil { return fmt.Errorf("failed to write %v to %v: %v", data, file, err) diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/stats.go b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/stats.go index 797a923..b483f1b 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/stats.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/stats.go @@ -11,6 +11,7 @@ type ThrottlingData struct { ThrottledTime uint64 `json:"throttled_time,omitempty"` } +// CpuUsage denotes the usage of a CPU. // All CPU stats are aggregate since container inception. type CpuUsage struct { // Total CPU time consumed. diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go index b615809..5365bc8 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go @@ -273,7 +273,7 @@ func writeFile(dir, file, data string) error { // Normally dir should not be empty, one case is that cgroup subsystem // is not mounted, we will get empty dir, and we want it fail here. if dir == "" { - return fmt.Errorf("no such directory for %s.", file) + return fmt.Errorf("no such directory for %s", file) } return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700) } diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/utils.go b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/utils.go index 2352732..7c3dba0 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/utils.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/cgroups/utils.go @@ -23,6 +23,9 @@ func FindCgroupMountpoint(subsystem string) (string, error) { // We are not using mount.GetMounts() because it's super-inefficient, // parsing it directly sped up x10 times because of not using Sscanf. // It was one of two major performance drawbacks in container start. + if !isSubsystemAvailable(subsystem) { + return "", NewNotFoundError(subsystem) + } f, err := os.Open("/proc/self/mountinfo") if err != nil { return "", err @@ -47,6 +50,9 @@ func FindCgroupMountpoint(subsystem string) (string, error) { } func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) { + if !isSubsystemAvailable(subsystem) { + return "", "", NewNotFoundError(subsystem) + } f, err := os.Open("/proc/self/mountinfo") if err != nil { return "", "", err @@ -70,6 +76,15 @@ func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) { return "", "", NewNotFoundError(subsystem) } +func isSubsystemAvailable(subsystem string) bool { + cgroups, err := ParseCgroupFile("/proc/self/cgroup") + if err != nil { + return false + } + _, avail := cgroups[subsystem] + return avail +} + func FindCgroupMountpointDir() (string, error) { f, err := os.Open("/proc/self/mountinfo") if err != nil { @@ -124,7 +139,8 @@ func (m Mount) GetThisCgroupDir(cgroups map[string]string) (string, error) { func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) { res := make([]Mount, 0, len(ss)) scanner := bufio.NewScanner(mi) - for scanner.Scan() { + numFound := 0 + for scanner.Scan() && numFound < len(ss) { txt := scanner.Text() sepIdx := strings.Index(txt, " - ") if sepIdx == -1 { @@ -139,12 +155,15 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) { Root: fields[3], } for _, opt := range strings.Split(fields[len(fields)-1], ",") { + if !ss[opt] { + continue + } if strings.HasPrefix(opt, cgroupNamePrefix) { m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):]) - } - if ss[opt] { + } else { m.Subsystems = append(m.Subsystems, opt) } + numFound++ } res = append(res, m) } @@ -161,19 +180,19 @@ func GetCgroupMounts() ([]Mount, error) { } defer f.Close() - all, err := GetAllSubsystems() + all, err := ParseCgroupFile("/proc/self/cgroup") if err != nil { return nil, err } allMap := make(map[string]bool) - for _, s := range all { + for s := range all { allMap[s] = true } return getCgroupMountsHelper(allMap, f) } -// Returns all the cgroup subsystems supported by the kernel +// GetAllSubsystems returns all the cgroup subsystems supported by the kernel func GetAllSubsystems() ([]string, error) { f, err := os.Open("/proc/cgroups") if err != nil { @@ -199,7 +218,7 @@ func GetAllSubsystems() ([]string, error) { return subsystems, nil } -// Returns the relative path to the cgroup docker is running in. +// GetThisCgroupDir returns the relative path to the cgroup docker is running in. func GetThisCgroupDir(subsystem string) (string, error) { cgroups, err := ParseCgroupFile("/proc/self/cgroup") if err != nil { diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config.go b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config.go index 1221ce2..ef23222 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config.go @@ -33,7 +33,7 @@ type Seccomp struct { Syscalls []*Syscall `json:"syscalls"` } -// An action to be taken upon rule match in Seccomp +// Action is taken upon rule match in Seccomp type Action int const ( @@ -44,7 +44,7 @@ const ( Trace ) -// A comparison operator to be used when matching syscall arguments in Seccomp +// Operator is a comparison operator to be used when matching syscall arguments in Seccomp type Operator int const ( @@ -57,7 +57,7 @@ const ( MaskEqualTo ) -// A rule to match a specific syscall argument in Seccomp +// Arg is a rule to match a specific syscall argument in Seccomp type Arg struct { Index uint `json:"index"` Value uint64 `json:"value"` @@ -65,7 +65,7 @@ type Arg struct { Op Operator `json:"op"` } -// An rule to match a syscall in Seccomp +// Syscall is a rule to match a syscall in Seccomp type Syscall struct { Name string `json:"name"` Action Action `json:"action"` @@ -261,7 +261,7 @@ type Hook interface { Run(HookState) error } -// NewFunctionHooks will call the provided function when the hook is run. +// NewFunctionHook will call the provided function when the hook is run. func NewFunctionHook(f func(HookState) error) FuncHook { return FuncHook{ run: f, @@ -284,7 +284,7 @@ type Command struct { Timeout *time.Duration `json:"timeout"` } -// NewCommandHooks will execute the provided command when the hook is run. +// NewCommandHook will execute the provided command when the hook is run. func NewCommandHook(cmd Command) CommandHook { return CommandHook{ Command: cmd, diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config_unix.go b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config_unix.go index c447f3e..a60554a 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config_unix.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/config_unix.go @@ -4,7 +4,7 @@ package configs import "fmt" -// Gets the root uid for the process on host which could be non-zero +// HostUID gets the root uid for the process on host which could be non-zero // when user namespaces are enabled. func (c Config) HostUID() (int, error) { if c.Namespaces.Contains(NEWUSER) { @@ -21,7 +21,7 @@ func (c Config) HostUID() (int, error) { return 0, nil } -// Gets the root gid for the process on host which could be non-zero +// HostGID gets the root gid for the process on host which could be non-zero // when user namespaces are enabled. func (c Config) HostGID() (int, error) { if c.Namespaces.Contains(NEWUSER) { diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/device_defaults.go b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/device_defaults.go index e452992..ba1f437 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/device_defaults.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/device_defaults.go @@ -3,7 +3,7 @@ package configs var ( - // These are devices that are to be both allowed and created. + // DefaultSimpleDevices are devices that are to be both allowed and created. DefaultSimpleDevices = []*Device{ // /dev/null and zero { diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go similarity index 70% rename from vendor/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go rename to vendor/src/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go index e155ca1..448cde2 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/selinux" ) type Validator interface { @@ -42,7 +43,7 @@ func (v *ConfigValidator) Validate(config *configs.Config) error { return nil } -// rootfs validates the the rootfs is an absolute path and is not a symlink +// rootfs validates if the rootfs is an absolute path and is not a symlink // to the container's root filesystem. func (v *ConfigValidator) rootfs(config *configs.Config) error { cleaned, err := filepath.Abs(config.Rootfs) @@ -52,7 +53,7 @@ func (v *ConfigValidator) rootfs(config *configs.Config) error { if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil { return err } - if config.Rootfs != cleaned { + if filepath.Clean(config.Rootfs) != cleaned { return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs) } return nil @@ -80,6 +81,10 @@ func (v *ConfigValidator) security(config *configs.Config) error { !config.Namespaces.Contains(configs.NEWNS) { return fmt.Errorf("unable to restrict sys entries without a private MNT namespace") } + if config.ProcessLabel != "" && !selinux.SelinuxEnabled() { + return fmt.Errorf("selinux label is specified in config, but selinux is disabled or not supported") + } + return nil } @@ -100,38 +105,33 @@ func (v *ConfigValidator) usernamespace(config *configs.Config) error { // /proc/sys isn't completely namespaced and depending on which namespaces // are specified, a subset of sysctls are permitted. func (v *ConfigValidator) sysctl(config *configs.Config) error { - validSysctlPrefixes := []string{} - validSysctlMap := make(map[string]bool) - if config.Namespaces.Contains(configs.NEWNET) { - validSysctlPrefixes = append(validSysctlPrefixes, "net.") - } - if config.Namespaces.Contains(configs.NEWIPC) { - validSysctlPrefixes = append(validSysctlPrefixes, "fs.mqueue.") - validSysctlMap = map[string]bool{ - "kernel.msgmax": true, - "kernel.msgmnb": true, - "kernel.msgmni": true, - "kernel.sem": true, - "kernel.shmall": true, - "kernel.shmmax": true, - "kernel.shmmni": true, - "kernel.shm_rmid_forced": true, - } + validSysctlMap := map[string]bool{ + "kernel.msgmax": true, + "kernel.msgmnb": true, + "kernel.msgmni": true, + "kernel.sem": true, + "kernel.shmall": true, + "kernel.shmmax": true, + "kernel.shmmni": true, + "kernel.shm_rmid_forced": true, } + for s := range config.Sysctl { - if validSysctlMap[s] { - continue - } - valid := false - for _, vp := range validSysctlPrefixes { - if strings.HasPrefix(s, vp) { - valid = true - break + if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") { + if config.Namespaces.Contains(configs.NEWIPC) { + continue + } else { + return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s) } } - if !valid { - return fmt.Errorf("sysctl %q is not permitted in the config", s) + if strings.HasPrefix(s, "net.") { + if config.Namespaces.Contains(configs.NEWNET) { + continue + } else { + return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", s) + } } + return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s) } return nil diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/container.go b/vendor/src/github.com/opencontainers/runc/libcontainer/container.go index 32daa97..afc1b3e 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/container.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/container.go @@ -1,4 +1,4 @@ -// Libcontainer provides a native Go implementation for creating containers +// Package libcontainer provides a native Go implementation for creating containers // with namespaces, cgroups, capabilities, and filesystem access controls. // It allows you to manage the lifecycle of the container performing additional operations // after the container is created. @@ -11,23 +11,23 @@ import ( "github.com/opencontainers/runc/libcontainer/configs" ) -// The status of a container. +// Status is the status of a container. type Status int const ( - // The container exists but has not been run yet + // Created is the status that denotes the container exists but has not been run yet Created Status = iota - // The container exists and is running. + // Created is the status that denotes the container exists and is running. Running - // The container exists, it is in the process of being paused. + // Pausing is the status that denotes the container exists, it is in the process of being paused. Pausing - // The container exists, but all its processes are paused. + // Paused is the status that denotes the container exists, but all its processes are paused. Paused - // The container does not exist. + // Destroyed is the status that denotes the container does not exist. Destroyed ) @@ -67,7 +67,7 @@ type BaseState struct { Config configs.Config `json:"config"` } -// A libcontainer container object. +// BaseContainer is a libcontainer container object. // // Each container is thread-safe within the same process. Since a container can // be destroyed by a separate process, any function may return that the container diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/container_linux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/container_linux.go index 2ae50c4..6f12cbc 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/container_linux.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/container_linux.go @@ -62,7 +62,7 @@ type State struct { ExternalDescriptors []string `json:"external_descriptors,omitempty"` } -// A libcontainer container object. +// Container is a libcontainer container object. // // Each container is thread-safe within the same process. Since a container can // be destroyed by a separate process, any function may return that the container @@ -84,7 +84,7 @@ type Container interface { // Systemerror - System error. Restore(process *Process, criuOpts *CriuOpts) error - // If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses + // If the Container state is RUNNING, sets the Container state to PAUSING and pauses // the execution of any user processes. Asynchronously, when the container finished being paused the // state is changed to PAUSED. // If the Container state is PAUSED, do nothing. @@ -141,7 +141,7 @@ func (c *linuxContainer) State() (*State, error) { func (c *linuxContainer) Processes() ([]int, error) { pids, err := c.cgroupManager.GetAllPids() if err != nil { - return nil, newSystemError(err) + return nil, newSystemErrorWithCause(err, "getting all container pids from cgroups") } return pids, nil } @@ -152,14 +152,14 @@ func (c *linuxContainer) Stats() (*Stats, error) { stats = &Stats{} ) if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil { - return stats, newSystemError(err) + return stats, newSystemErrorWithCause(err, "getting container stats from cgroups") } for _, iface := range c.config.Networks { switch iface.Type { case "veth": istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) if err != nil { - return stats, newSystemError(err) + return stats, newSystemErrorWithCausef(err, "getting network stats for interface %q", iface.HostInterfaceName) } stats.Interfaces = append(stats.Interfaces, istats) } @@ -184,14 +184,14 @@ func (c *linuxContainer) Start(process *Process) error { doInit := status == Destroyed parent, err := c.newParentProcess(process, doInit) if err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "creating new parent process") } if err := parent.start(); err != nil { // terminate the process to ensure that it properly is reaped. if err := parent.terminate(); err != nil { logrus.Warn(err) } - return newSystemError(err) + return newSystemErrorWithCause(err, "starting container process") } // generate a timestamp indicating when the container was started c.created = time.Now().UTC() @@ -211,12 +211,12 @@ func (c *linuxContainer) Start(process *Process) error { Root: c.config.Rootfs, BundlePath: utils.SearchLabels(c.config.Labels, "bundle"), } - for _, hook := range c.config.Hooks.Poststart { + for i, hook := range c.config.Hooks.Poststart { if err := hook.Run(s); err != nil { if err := parent.terminate(); err != nil { logrus.Warn(err) } - return newSystemError(err) + return newSystemErrorWithCausef(err, "running poststart hook %d", i) } } } @@ -226,7 +226,7 @@ func (c *linuxContainer) Start(process *Process) error { func (c *linuxContainer) Signal(s os.Signal) error { if err := c.initProcess.signal(s); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "signaling init process") } return nil } @@ -234,11 +234,11 @@ func (c *linuxContainer) Signal(s os.Signal) error { func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) { parentPipe, childPipe, err := newPipe() if err != nil { - return nil, newSystemError(err) + return nil, newSystemErrorWithCause(err, "creating new init pipe") } cmd, err := c.commandTemplate(p, childPipe) if err != nil { - return nil, newSystemError(err) + return nil, newSystemErrorWithCause(err, "creating new command template") } if !doInit { return c.newSetnsProcess(p, cmd, parentPipe, childPipe) @@ -299,7 +299,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns)) state, err := c.currentState() if err != nil { - return nil, newSystemError(err) + return nil, newSystemErrorWithCause(err, "getting container's current state") } // for setns process, we dont have to set cloneflags as the process namespaces // will only be set via setns syscall @@ -408,13 +408,13 @@ func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struc return notifyMemoryPressure(c.cgroupManager.GetPaths(), level) } -// check Criu version greater than or equal to min_version -func (c *linuxContainer) checkCriuVersion(min_version string) error { +// checkCriuVersion checks Criu version greater than or equal to minVersion +func (c *linuxContainer) checkCriuVersion(minVersion string) error { var x, y, z, versionReq int - _, err := fmt.Sscanf(min_version, "%d.%d.%d\n", &x, &y, &z) // 1.5.2 + _, err := fmt.Sscanf(minVersion, "%d.%d.%d\n", &x, &y, &z) // 1.5.2 if err != nil { - _, err = fmt.Sscanf(min_version, "Version: %d.%d\n", &x, &y) // 1.6 + _, err = fmt.Sscanf(minVersion, "Version: %d.%d\n", &x, &y) // 1.6 } versionReq = x*10000 + y*100 + z @@ -459,7 +459,7 @@ func (c *linuxContainer) checkCriuVersion(min_version string) error { c.criuVersion = x*10000 + y*100 + z if c.criuVersion < versionReq { - return fmt.Errorf("CRIU version must be %s or higher", min_version) + return fmt.Errorf("CRIU version must be %s or higher", minVersion) } return nil @@ -955,9 +955,9 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc Pid: int(notify.GetPid()), Root: c.config.Rootfs, } - for _, hook := range c.config.Hooks.Prestart { + for i, hook := range c.config.Hooks.Prestart { if err := hook.Run(s); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } @@ -1046,7 +1046,7 @@ func (c *linuxContainer) isRunning() (bool, error) { if err == syscall.ESRCH { return false, nil } - return false, newSystemError(err) + return false, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", c.initProcess.pid()) } return true, nil } @@ -1057,7 +1057,7 @@ func (c *linuxContainer) isPaused() (bool, error) { if os.IsNotExist(err) { return false, nil } - return false, newSystemError(err) + return false, newSystemErrorWithCause(err, "checking if container is paused") } return bytes.Equal(bytes.TrimSpace(data), []byte("FROZEN")), nil } @@ -1125,7 +1125,7 @@ func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceTyp } // only set to join this namespace if it exists if _, err := os.Lstat(p); err != nil { - return nil, newSystemError(err) + return nil, newSystemErrorWithCausef(err, "running lstat on namespace path %q", p) } // do not allow namespace path with comma as we use it to separate // the namespace paths diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/criu_opts_unix.go b/vendor/src/github.com/opencontainers/runc/libcontainer/criu_opts_unix.go index 1332385..b163fbb 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/criu_opts_unix.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/criu_opts_unix.go @@ -3,13 +3,13 @@ package libcontainer // cgroup restoring strategy provided by criu -type cg_mode uint32 +type cgMode uint32 const ( - CRIU_CG_MODE_SOFT cg_mode = 3 + iota // restore cgroup properties if only dir created by criu - CRIU_CG_MODE_FULL // always restore all cgroups and their properties - CRIU_CG_MODE_STRICT // restore all, requiring them to not present in the system - CRIU_CG_MODE_DEFAULT // the same as CRIU_CG_MODE_SOFT + CRIU_CG_MODE_SOFT cgMode = 3 + iota // restore cgroup properties if only dir created by criu + CRIU_CG_MODE_FULL // always restore all cgroups and their properties + CRIU_CG_MODE_STRICT // restore all, requiring them to not present in the system + CRIU_CG_MODE_DEFAULT // the same as CRIU_CG_MODE_SOFT ) type CriuPageServerInfo struct { @@ -32,6 +32,6 @@ type CriuOpts struct { FileLocks bool // handle file locks, for safety PageServer CriuPageServerInfo // allow to dump to criu page server VethPairs []VethPairName // pass the veth to criu when restore - ManageCgroupsMode cg_mode // dump or restore cgroup mode + ManageCgroupsMode cgMode // dump or restore cgroup mode EmptyNs uint32 // don't c/r properties for namespace from this mask } diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/error.go b/vendor/src/github.com/opencontainers/runc/libcontainer/error.go index b50aaae..b063927 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/error.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/error.go @@ -2,7 +2,7 @@ package libcontainer import "io" -// API error code type. +// ErrorCode is the API error code type. type ErrorCode int // API error codes. @@ -56,7 +56,7 @@ func (c ErrorCode) String() string { } } -// API Error type. +// Error is the API error type. type Error interface { error diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/generic_error.go b/vendor/src/github.com/opencontainers/runc/libcontainer/generic_error.go index 3ed33da..9c3d324 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/generic_error.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/generic_error.go @@ -1,6 +1,7 @@ package libcontainer import ( + "fmt" "io" "text/template" "time" @@ -51,6 +52,21 @@ func newGenericError(err error, c ErrorCode) Error { } func newSystemError(err error) Error { + return createSystemError(err, "") +} + +func newSystemErrorWithCausef(err error, cause string, v ...interface{}) Error { + return createSystemError(err, fmt.Sprintf(cause, v...)) +} + +func newSystemErrorWithCause(err error, cause string) Error { + return createSystemError(err, cause) +} + +// createSystemError creates the specified error with the correct number of +// stack frames skipped. This is only to be called by the other functions for +// formatting the error. +func createSystemError(err error, cause string) Error { if le, ok := err.(Error); ok { return le } @@ -58,7 +74,8 @@ func newSystemError(err error) Error { Timestamp: time.Now(), Err: err, ECode: SystemError, - Stack: stacktrace.Capture(1), + Cause: cause, + Stack: stacktrace.Capture(2), } if err != nil { gerr.Message = err.Error() @@ -70,12 +87,17 @@ type genericError struct { Timestamp time.Time ECode ErrorCode Err error `json:"-"` + Cause string Message string Stack stacktrace.Stacktrace } func (e *genericError) Error() string { - return e.Message + if e.Cause == "" { + return e.Message + } + frame := e.Stack.Frames[0] + return fmt.Sprintf("%s:%d: %s caused %q", frame.File, frame.Line, e.Cause, e.Message) } func (e *genericError) Code() ErrorCode { diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/keys/keyctl.go b/vendor/src/github.com/opencontainers/runc/libcontainer/keys/keyctl.go index c37ca21..c67fd15 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/keys/keyctl.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/keys/keyctl.go @@ -4,9 +4,9 @@ package keyctl import ( "fmt" - "syscall" - "strings" "strconv" + "strings" + "syscall" "unsafe" ) @@ -17,7 +17,7 @@ const KEYCTL_DESCRIBE = 6 type KeySerial uint32 func JoinSessionKeyring(name string) (KeySerial, error) { - var _name *byte = nil + var _name *byte var err error if len(name) > 0 { @@ -34,7 +34,7 @@ func JoinSessionKeyring(name string) (KeySerial, error) { return KeySerial(sessKeyId), nil } -// modify permissions on a keyring by reading the current permissions, +// ModKeyringPerm modifies permissions on a keyring by reading the current permissions, // anding the bits with the given mask (clearing permissions) and setting // additional permission bits func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error { @@ -64,4 +64,3 @@ func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error { return nil } - diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/label/label_selinux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/label/label_selinux.go index d443df4..4493bda 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/label/label_selinux.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/label/label_selinux.go @@ -107,7 +107,7 @@ func SetFileLabel(path string, fileLabel string) error { return nil } -// Tell the kernel the label for all files to be created +// SetFileCreateLabel tells the kernel the label for all files to be created func SetFileCreateLabel(fileLabel string) error { if selinux.SelinuxEnabled() { return selinux.Setfscreatecon(fileLabel) @@ -115,7 +115,7 @@ func SetFileCreateLabel(fileLabel string) error { return nil } -// Change the label of path to the filelabel string. +// Relabel changes the label of path to the filelabel string. // It changes the MCS label to s0 if shared is true. // This will allow all containers to share the content. func Relabel(path string, fileLabel string, shared bool) error { diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/message_linux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/message_linux.go index 1663013..400bd36 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/message_linux.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/message_linux.go @@ -27,7 +27,8 @@ type Int32msg struct { Value uint32 } -// int32msg has the following representation +// Serialize serializes the message. +// Int32msg has the following representation // | nlattr len | nlattr type | // | uint32 value | func (msg *Int32msg) Serialize() []byte { @@ -43,7 +44,7 @@ func (msg *Int32msg) Len() int { return syscall_NLA_HDRLEN + 4 } -// bytemsg has the following representation +// Bytemsg has the following representation // | nlattr len | nlattr type | // | value | pad | type Bytemsg struct { diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/process_linux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/process_linux.go index 1a2ee0b..2b6b29b 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/process_linux.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/process_linux.go @@ -70,47 +70,47 @@ func (p *setnsProcess) start() (err error) { err = p.cmd.Start() p.childPipe.Close() if err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "starting setns process") } if p.bootstrapData != nil { if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "copying bootstrap data to pipe") } } if err = p.execSetns(); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "executing setns process") } if len(p.cgroupPaths) > 0 { if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "adding pid %d to cgroups", p.pid()) } } // set oom_score_adj if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting oom score") } // set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting rlimits for process") } if err := utils.WriteJSON(p.parentPipe, p.config); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "writing config to pipe") } if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "calling shutdown on init pipe") } // wait for the child process to fully complete and receive an error message // if one was encoutered var ierr *genericError if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF { - return newSystemError(err) + return newSystemErrorWithCause(err, "decoding init error from pipe") } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { p.wait() - return newSystemError(ierr) + return ierr } return nil } @@ -123,7 +123,7 @@ func (p *setnsProcess) execSetns() error { status, err := p.cmd.Process.Wait() if err != nil { p.cmd.Wait() - return newSystemError(err) + return newSystemErrorWithCause(err, "waiting on setns process to finish") } if !status.Success() { p.cmd.Wait() @@ -132,7 +132,7 @@ func (p *setnsProcess) execSetns() error { var pid *pid if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil { p.cmd.Wait() - return newSystemError(err) + return newSystemErrorWithCause(err, "reading pid from init pipe") } process, err := os.FindProcess(pid.Pid) if err != nil { @@ -231,26 +231,26 @@ func (p *initProcess) start() error { p.childPipe.Close() if err != nil { p.process.ops = nil - return newSystemError(err) + return newSystemErrorWithCause(err, "starting init process command") } if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { return err } if err := p.execSetns(); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "running exec setns process for init") } // Save the standard descriptor names before the container process // can potentially move them (e.g., via dup2()). If we don't do this now, // we won't know at checkpoint time which file descriptor to look up. fds, err := getPipeFds(p.pid()) if err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", p.pid()) } p.setExternalDescriptors(fds) // Do this before syncing with child so that no children // can escape the cgroup if err := p.manager.Apply(p.pid()); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "applying cgroup configuration for process") } defer func() { if err != nil { @@ -259,10 +259,10 @@ func (p *initProcess) start() error { } }() if err := p.createNetworkInterfaces(); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "creating nework interfaces") } if err := p.sendConfig(); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "sending config to init process") } var ( procSync syncT @@ -278,21 +278,21 @@ loop: if err == io.EOF { break loop } - return newSystemError(err) + return newSystemErrorWithCause(err, "decoding sync type from init pipe") } switch procSync.Type { case procReady: if err := p.manager.Set(p.config.Config); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting cgroup config for ready process") } // set oom_score_adj if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting oom score for ready process") } // set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting rlimits for ready process") } // call prestart hooks if !p.config.Config.Namespaces.Contains(configs.NEWNS) { @@ -303,16 +303,16 @@ loop: Pid: p.pid(), Root: p.config.Config.Rootfs, } - for _, hook := range p.config.Config.Hooks.Prestart { + for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } } // Sync with child. if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "reading syncT run type") } sentRun = true case procHooks: @@ -324,22 +324,22 @@ loop: Root: p.config.Config.Rootfs, BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"), } - for _, hook := range p.config.Config.Hooks.Prestart { + for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } // Sync with child. if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "reading syncT resume type") } sentResume = true case procError: // wait for the child process to fully complete and receive an error message // if one was encoutered if err := dec.Decode(&ierr); err != nil && err != io.EOF { - return newSystemError(err) + return newSystemErrorWithCause(err, "decoding proc error from init") } if ierr != nil { break loop @@ -347,22 +347,22 @@ loop: // Programmer error. panic("No error following JSON procError payload.") default: - return newSystemError(fmt.Errorf("invalid JSON synchronisation payload from child")) + return newSystemError(fmt.Errorf("invalid JSON payload from child")) } } if !sentRun { - return newSystemError(fmt.Errorf("could not synchronise with container process: %v", ierr)) + return newSystemErrorWithCause(ierr, "container init failed") } if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) } if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "shutting down init pipe") } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { p.wait() - return newSystemError(ierr) + return ierr } return nil } diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/rootfs_linux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/rootfs_linux.go index 4aa4cbd..fe1db75 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/rootfs_linux.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/rootfs_linux.go @@ -25,7 +25,7 @@ import ( const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV -// setupDev returns true if /dev needs to be set up. +// needsSetupDev returns true if /dev needs to be set up. func needsSetupDev(config *configs.Config) bool { for _, m := range config.Mounts { if m.Device == "bind" && (m.Destination == "/dev" || m.Destination == "/dev/") { @@ -39,35 +39,35 @@ func needsSetupDev(config *configs.Config) bool { // new mount namespace. func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) { if err := prepareRoot(config); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "preparing rootfs") } setupDev := needsSetupDev(config) for _, m := range config.Mounts { for _, precmd := range m.PremountCmds { if err := mountCmd(precmd); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "running premount command") } } if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "mounting %q to rootfs %q", m.Destination, config.Rootfs) } for _, postcmd := range m.PostmountCmds { if err := mountCmd(postcmd); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "running postmount command") } } } if setupDev { if err := createDevices(config); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "creating device nodes") } if err := setupPtmx(config, console); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting up ptmx") } if err := setupDevSymlinks(config.Rootfs); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting up /dev symlinks") } } // Signal the parent to run the pre-start hooks. @@ -78,7 +78,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit return err } if err := syscall.Chdir(config.Rootfs); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "changing dir to %q", config.Rootfs) } if config.NoPivotRoot { err = msMoveRoot(config.Rootfs) @@ -86,11 +86,11 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit err = pivotRoot(config.Rootfs, config.PivotDir) } if err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "jailing process inside rootfs") } if setupDev { if err := reOpenDevNull(); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "reopening /dev/null inside container") } } // remount dev as ro if specifed @@ -98,7 +98,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit if m.Destination == "/dev" { if m.Flags&syscall.MS_RDONLY != 0 { if err := remountReadonly(m.Destination); err != nil { - return newSystemError(err) + return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination) } } break @@ -107,7 +107,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit // set rootfs ( / ) as readonly if config.Readonlyfs { if err := setReadonly(); err != nil { - return newSystemError(err) + return newSystemErrorWithCause(err, "setting rootfs as readonly") } } syscall.Umask(0022) @@ -115,14 +115,12 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit } func mountCmd(cmd configs.Command) error { - command := exec.Command(cmd.Path, cmd.Args[:]...) command.Env = cmd.Env command.Dir = cmd.Dir if out, err := command.CombinedOutput(); err != nil { return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err) } - return nil } @@ -266,8 +264,10 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { if m.Flags&syscall.MS_RDONLY != 0 { // remount cgroup root as readonly mcgrouproot := &configs.Mount{ + Source: m.Destination, + Device: "bind", Destination: m.Destination, - Flags: defaultMountFlags | syscall.MS_RDONLY, + Flags: defaultMountFlags | syscall.MS_RDONLY | syscall.MS_BIND, } if err := remount(mcgrouproot, rootfs); err != nil { return err @@ -515,10 +515,10 @@ func getParentMount(rootfs string) (string, string, error) { } // Make parent mount private if it was shared -func rootfsParentMountPrivate(config *configs.Config) error { +func rootfsParentMountPrivate(rootfs string) error { sharedMount := false - parentMount, optionalOpts, err := getParentMount(config.Rootfs) + parentMount, optionalOpts, err := getParentMount(rootfs) if err != nil { return err } @@ -550,9 +550,10 @@ func prepareRoot(config *configs.Config) error { if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { return err } - - if err := rootfsParentMountPrivate(config); err != nil { - return err + if config.NoPivotRoot { + if err := rootfsParentMountPrivate(config.Rootfs); err != nil { + return err + } } return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "") @@ -595,7 +596,14 @@ func pivotRoot(rootfs, pivotBaseDir string) (err error) { } }() if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { - return fmt.Errorf("pivot_root %s", err) + // Make the parent mount private + if err := rootfsParentMountPrivate(rootfs); err != nil { + return err + } + // Try again + if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { + return fmt.Errorf("pivot_root %s", err) + } } if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %s", err) diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_unsupported.go b/vendor/src/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_unsupported.go index 888483e..44df1ad 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_unsupported.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_unsupported.go @@ -10,7 +10,7 @@ import ( var ErrSeccompNotEnabled = errors.New("seccomp: config provided but seccomp not supported") -// Seccomp not supported, do nothing +// InitSeccomp does nothing because seccomp is not supported. func InitSeccomp(config *configs.Seccomp) error { if config != nil { return ErrSeccompNotEnabled diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/selinux/selinux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/selinux/selinux.go new file mode 100644 index 0000000..a6cf7f2 --- /dev/null +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/selinux/selinux.go @@ -0,0 +1,499 @@ +// +build linux + +package selinux + +import ( + "bufio" + "crypto/rand" + "encoding/binary" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "sync" + "syscall" + + "github.com/opencontainers/runc/libcontainer/system" +) + +const ( + Enforcing = 1 + Permissive = 0 + Disabled = -1 + selinuxDir = "/etc/selinux/" + selinuxConfig = selinuxDir + "config" + selinuxTypeTag = "SELINUXTYPE" + selinuxTag = "SELINUX" + selinuxPath = "/sys/fs/selinux" + xattrNameSelinux = "security.selinux" + stRdOnly = 0x01 +) + +var ( + assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) + mcsList = make(map[string]bool) + mcsLock sync.Mutex + selinuxfs = "unknown" + selinuxEnabled = false // Stores whether selinux is currently enabled + selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet +) + +type SELinuxContext map[string]string + +// SetDisabled disables selinux support for the package +func SetDisabled() { + selinuxEnabled, selinuxEnabledChecked = false, true +} + +// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs +// filesystem or an empty string if no mountpoint is found. Selinuxfs is +// a proc-like pseudo-filesystem that exposes the selinux policy API to +// processes. The existence of an selinuxfs mount is used to determine +// whether selinux is currently enabled or not. +func getSelinuxMountPoint() string { + if selinuxfs != "unknown" { + return selinuxfs + } + selinuxfs = "" + + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return selinuxfs + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + txt := scanner.Text() + // Safe as mountinfo encodes mountpoints with spaces as \040. + sepIdx := strings.Index(txt, " - ") + if sepIdx == -1 { + continue + } + if !strings.Contains(txt[sepIdx:], "selinuxfs") { + continue + } + fields := strings.Split(txt, " ") + if len(fields) < 5 { + continue + } + selinuxfs = fields[4] + break + } + + if selinuxfs != "" { + var buf syscall.Statfs_t + syscall.Statfs(selinuxfs, &buf) + if (buf.Flags & stRdOnly) == 1 { + selinuxfs = "" + } + } + return selinuxfs +} + +// SelinuxEnabled returns whether selinux is currently enabled. +func SelinuxEnabled() bool { + if selinuxEnabledChecked { + return selinuxEnabled + } + selinuxEnabledChecked = true + if fs := getSelinuxMountPoint(); fs != "" { + if con, _ := Getcon(); con != "kernel" { + selinuxEnabled = true + } + } + return selinuxEnabled +} + +func readConfig(target string) (value string) { + var ( + val, key string + bufin *bufio.Reader + ) + + in, err := os.Open(selinuxConfig) + if err != nil { + return "" + } + defer in.Close() + + bufin = bufio.NewReader(in) + + for done := false; !done; { + var line string + if line, err = bufin.ReadString('\n'); err != nil { + if err != io.EOF { + return "" + } + done = true + } + line = strings.TrimSpace(line) + if len(line) == 0 { + // Skip blank lines + continue + } + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + if groups := assignRegex.FindStringSubmatch(line); groups != nil { + key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) + if key == target { + return strings.Trim(val, "\"") + } + } + } + return "" +} + +func getSELinuxPolicyRoot() string { + return selinuxDir + readConfig(selinuxTypeTag) +} + +func readCon(name string) (string, error) { + var val string + + in, err := os.Open(name) + if err != nil { + return "", err + } + defer in.Close() + + _, err = fmt.Fscanf(in, "%s", &val) + return val, err +} + +// Setfilecon sets the SELinux label for this path or returns an error. +func Setfilecon(path string, scon string) error { + return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) +} + +// Getfilecon returns the SELinux label for this path or returns an error. +func Getfilecon(path string) (string, error) { + con, err := system.Lgetxattr(path, xattrNameSelinux) + if err != nil { + return "", err + } + // Trim the NUL byte at the end of the byte buffer, if present. + if len(con) > 0 && con[len(con)-1] == '\x00' { + con = con[:len(con)-1] + } + return string(con), nil +} + +func Setfscreatecon(scon string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon) +} + +func Getfscreatecon() (string, error) { + return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid())) +} + +// Getcon returns the SELinux label of the current process thread, or an error. +func Getcon() (string, error) { + return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid())) +} + +// Getpidcon returns the SELinux label of the given pid, or an error. +func Getpidcon(pid int) (string, error) { + return readCon(fmt.Sprintf("/proc/%d/attr/current", pid)) +} + +func Getexeccon() (string, error) { + return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid())) +} + +func writeCon(name string, val string) error { + out, err := os.OpenFile(name, os.O_WRONLY, 0) + if err != nil { + return err + } + defer out.Close() + + if val != "" { + _, err = out.Write([]byte(val)) + } else { + _, err = out.Write(nil) + } + return err +} + +func Setexeccon(scon string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon) +} + +func (c SELinuxContext) Get() string { + return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"]) +} + +func NewContext(scon string) SELinuxContext { + c := make(SELinuxContext) + + if len(scon) != 0 { + con := strings.SplitN(scon, ":", 4) + c["user"] = con[0] + c["role"] = con[1] + c["type"] = con[2] + c["level"] = con[3] + } + return c +} + +func ReserveLabel(scon string) { + if len(scon) != 0 { + con := strings.SplitN(scon, ":", 4) + mcsAdd(con[3]) + } +} + +func selinuxEnforcePath() string { + return fmt.Sprintf("%s/enforce", selinuxPath) +} + +func SelinuxGetEnforce() int { + var enforce int + + enforceS, err := readCon(selinuxEnforcePath()) + if err != nil { + return -1 + } + + enforce, err = strconv.Atoi(string(enforceS)) + if err != nil { + return -1 + } + return enforce +} + +func SelinuxSetEnforce(mode int) error { + return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode)) +} + +func SelinuxGetEnforceMode() int { + switch readConfig(selinuxTag) { + case "enforcing": + return Enforcing + case "permissive": + return Permissive + } + return Disabled +} + +func mcsAdd(mcs string) error { + mcsLock.Lock() + defer mcsLock.Unlock() + if mcsList[mcs] { + return fmt.Errorf("MCS Label already exists") + } + mcsList[mcs] = true + return nil +} + +func mcsDelete(mcs string) { + mcsLock.Lock() + mcsList[mcs] = false + mcsLock.Unlock() +} + +func IntToMcs(id int, catRange uint32) string { + var ( + SETSIZE = int(catRange) + TIER = SETSIZE + ORD = id + ) + + if id < 1 || id > 523776 { + return "" + } + + for ORD > TIER { + ORD = ORD - TIER + TIER-- + } + TIER = SETSIZE - TIER + ORD = ORD + TIER + return fmt.Sprintf("s0:c%d,c%d", TIER, ORD) +} + +func uniqMcs(catRange uint32) string { + var ( + n uint32 + c1, c2 uint32 + mcs string + ) + + for { + binary.Read(rand.Reader, binary.LittleEndian, &n) + c1 = n % catRange + binary.Read(rand.Reader, binary.LittleEndian, &n) + c2 = n % catRange + if c1 == c2 { + continue + } else { + if c1 > c2 { + t := c1 + c1 = c2 + c2 = t + } + } + mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2) + if err := mcsAdd(mcs); err != nil { + continue + } + break + } + return mcs +} + +func FreeLxcContexts(scon string) { + if len(scon) != 0 { + con := strings.SplitN(scon, ":", 4) + mcsDelete(con[3]) + } +} + +func GetLxcContexts() (processLabel string, fileLabel string) { + var ( + val, key string + bufin *bufio.Reader + ) + + if !SelinuxEnabled() { + return "", "" + } + lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot()) + in, err := os.Open(lxcPath) + if err != nil { + return "", "" + } + defer in.Close() + + bufin = bufio.NewReader(in) + + for done := false; !done; { + var line string + if line, err = bufin.ReadString('\n'); err != nil { + if err == io.EOF { + done = true + } else { + goto exit + } + } + line = strings.TrimSpace(line) + if len(line) == 0 { + // Skip blank lines + continue + } + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + if groups := assignRegex.FindStringSubmatch(line); groups != nil { + key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) + if key == "process" { + processLabel = strings.Trim(val, "\"") + } + if key == "file" { + fileLabel = strings.Trim(val, "\"") + } + } + } + + if processLabel == "" || fileLabel == "" { + return "", "" + } + +exit: + // mcs := IntToMcs(os.Getpid(), 1024) + mcs := uniqMcs(1024) + scon := NewContext(processLabel) + scon["level"] = mcs + processLabel = scon.Get() + scon = NewContext(fileLabel) + scon["level"] = mcs + fileLabel = scon.Get() + return processLabel, fileLabel +} + +func SecurityCheckContext(val string) error { + return writeCon(fmt.Sprintf("%s.context", selinuxPath), val) +} + +func CopyLevel(src, dest string) (string, error) { + if src == "" { + return "", nil + } + if err := SecurityCheckContext(src); err != nil { + return "", err + } + if err := SecurityCheckContext(dest); err != nil { + return "", err + } + scon := NewContext(src) + tcon := NewContext(dest) + mcsDelete(tcon["level"]) + mcsAdd(scon["level"]) + tcon["level"] = scon["level"] + return tcon.Get(), nil +} + +// Prevent users from relabing system files +func badPrefix(fpath string) error { + var badprefixes = []string{"/usr"} + + for _, prefix := range badprefixes { + if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) { + return fmt.Errorf("Relabeling content in %s is not allowed.", prefix) + } + } + return nil +} + +// Chcon changes the fpath file object to the SELinux label scon. +// If the fpath is a directory and recurse is true Chcon will walk the +// directory tree setting the label +func Chcon(fpath string, scon string, recurse bool) error { + if scon == "" { + return nil + } + if err := badPrefix(fpath); err != nil { + return err + } + callback := func(p string, info os.FileInfo, err error) error { + return Setfilecon(p, scon) + } + + if recurse { + return filepath.Walk(fpath, callback) + } + + return Setfilecon(fpath, scon) +} + +// DupSecOpt takes an SELinux process label and returns security options that +// can will set the SELinux Type and Level for future container processes +func DupSecOpt(src string) []string { + if src == "" { + return nil + } + con := NewContext(src) + if con["user"] == "" || + con["role"] == "" || + con["type"] == "" || + con["level"] == "" { + return nil + } + return []string{"label:user:" + con["user"], + "label:role:" + con["role"], + "label:type:" + con["type"], + "label:level:" + con["level"]} +} + +// DisableSecOpt returns a security opt that can be used to disabling SELinux +// labeling support for future container processes +func DisableSecOpt() []string { + return []string{"label:disable"} +} diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/stacktrace/capture.go b/vendor/src/github.com/opencontainers/runc/libcontainer/stacktrace/capture.go index 5ee6e37..0bbe149 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/stacktrace/capture.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/stacktrace/capture.go @@ -2,14 +2,14 @@ package stacktrace import "runtime" -// Caputure captures a stacktrace for the current calling go program +// Capture captures a stacktrace for the current calling go program // // skip is the number of frames to skip func Capture(userSkip int) Stacktrace { var ( skip = userSkip + 1 // add one for our own function frames []Frame - prevPc uintptr = 0 + prevPc uintptr ) for i := skip; ; i++ { pc, file, line, ok := runtime.Caller(i) diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/system/linux.go b/vendor/src/github.com/opencontainers/runc/libcontainer/system/linux.go index 8b199d9..1afc52b 100644 --- a/vendor/src/github.com/opencontainers/runc/libcontainer/system/linux.go +++ b/vendor/src/github.com/opencontainers/runc/libcontainer/system/linux.go @@ -100,17 +100,12 @@ func Setctty() error { return nil } -/* - * Detect whether we are currently running in a user namespace. - * Copied from github.com/lxc/lxd/shared/util.go - */ +// RunningInUserNS detects whether we are currently running in a user namespace. +// Copied from github.com/lxc/lxd/shared/util.go func RunningInUserNS() bool { file, err := os.Open("/proc/self/uid_map") if err != nil { - /* - * This kernel-provided file only exists if user namespaces are - * supported - */ + // This kernel-provided file only exists if user namespaces are supported return false } defer file.Close() diff --git a/vendor/src/github.com/opencontainers/specs/LICENSE b/vendor/src/github.com/opencontainers/runtime-spec/LICENSE similarity index 100% rename from vendor/src/github.com/opencontainers/specs/LICENSE rename to vendor/src/github.com/opencontainers/runtime-spec/LICENSE diff --git a/vendor/src/github.com/opencontainers/specs/specs-go/config.go b/vendor/src/github.com/opencontainers/runtime-spec/specs-go/config.go similarity index 95% rename from vendor/src/github.com/opencontainers/specs/specs-go/config.go rename to vendor/src/github.com/opencontainers/runtime-spec/specs-go/config.go index ba66ff1..45e604c 100644 --- a/vendor/src/github.com/opencontainers/specs/specs-go/config.go +++ b/vendor/src/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -49,7 +49,7 @@ type Process struct { // ApparmorProfile specified the apparmor profile for the container. (this field is platform dependent) ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"` - // SelinuxProcessLabel specifies the selinux context that the container process is run as. (this field is platform dependent) + // SelinuxLabel specifies the selinux context that the container process is run as. (this field is platform dependent) SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"` } @@ -96,9 +96,10 @@ type Mount struct { // Hook specifies a command that is run at a particular event in the lifecycle of a container type Hook struct { - Path string `json:"path"` - Args []string `json:"args,omitempty"` - Env []string `json:"env,omitempty"` + Path string `json:"path"` + Args []string `json:"args,omitempty"` + Env []string `json:"env,omitempty"` + Timeout *int `json:"timeout,omitempty"` } // Hooks for container setup and teardown @@ -128,13 +129,19 @@ type Linux struct { // If resources are specified, the cgroups at CgroupsPath will be updated based on resources. CgroupsPath *string `json:"cgroupsPath,omitempty"` // Namespaces contains the namespaces that are created and/or joined by the container - Namespaces []Namespace `json:"namespaces"` + Namespaces []Namespace `json:"namespaces,omitempty"` // Devices are a list of device nodes that are created for the container - Devices []Device `json:"devices"` + Devices []Device `json:"devices,omitempty"` // Seccomp specifies the seccomp security settings for the container. Seccomp *Seccomp `json:"seccomp,omitempty"` // RootfsPropagation is the rootfs mount propagation mode for the container. RootfsPropagation string `json:"rootfsPropagation,omitempty"` + // MaskedPaths masks over the provided paths inside the container. + MaskedPaths []string `json:"maskedPaths,omitempty"` + // ReadonlyPaths sets the provided paths as RO inside the container. + ReadonlyPaths []string `json:"readonlyPaths,omitempty"` + // MountLabel specifies the selinux context for the mounts in the container. + MountLabel string `json:"mountLabel,omitempty"` } // Namespace is the configuration for a Linux namespace diff --git a/vendor/src/github.com/opencontainers/specs/specs-go/state.go b/vendor/src/github.com/opencontainers/runtime-spec/specs-go/state.go similarity index 100% rename from vendor/src/github.com/opencontainers/specs/specs-go/state.go rename to vendor/src/github.com/opencontainers/runtime-spec/specs-go/state.go diff --git a/vendor/src/github.com/opencontainers/specs/specs-go/version.go b/vendor/src/github.com/opencontainers/runtime-spec/specs-go/version.go similarity index 92% rename from vendor/src/github.com/opencontainers/specs/specs-go/version.go rename to vendor/src/github.com/opencontainers/runtime-spec/specs-go/version.go index f11c897..371289a 100644 --- a/vendor/src/github.com/opencontainers/specs/specs-go/version.go +++ b/vendor/src/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -6,12 +6,12 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 0 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 4 + VersionMinor = 6 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "" + VersionDev = "-dev" ) // Version is the specification version that the package types support.