Merge pull request #218 from crosbymichael/bump_runc
Update runc and runtime-specs dependencies
This commit is contained in:
commit
79c35e0306
33 changed files with 731 additions and 180 deletions
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
"github.com/opencontainers/runc/libcontainer/system"
|
"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"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
|
|
@ -14,8 +14,8 @@ clone git github.com/docker/go-units 5d2041e26a699eaca682e2ea41c8f891e1060444
|
||||||
clone git github.com/godbus/dbus e2cf28118e66a6a63db46cf6088a35d2054d3bb0
|
clone git github.com/godbus/dbus e2cf28118e66a6a63db46cf6088a35d2054d3bb0
|
||||||
clone git github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
|
clone git github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
|
||||||
clone git github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3
|
clone git github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3
|
||||||
clone git github.com/opencontainers/runc e87436998478d222be209707503c27f6f91be0c5
|
clone git github.com/opencontainers/runc 9c89737e6e117a8be5a4980bc9795fe1a2b1028e
|
||||||
clone git github.com/opencontainers/specs 3ce138b1934bf227a418e241ead496c383eaba1c
|
clone git github.com/opencontainers/runtime-spec f955d90e70a98ddfb886bd930ffd076da9b67998
|
||||||
clone git github.com/rcrowley/go-metrics eeba7bd0dd01ace6e690fa833b3f22aaec29af43
|
clone git github.com/rcrowley/go-metrics eeba7bd0dd01ace6e690fa833b3f22aaec29af43
|
||||||
clone git github.com/satori/go.uuid f9ab0dce87d815821e221626b772e3475a0d2749
|
clone git github.com/satori/go.uuid f9ab0dce87d815821e221626b772e3475a0d2749
|
||||||
clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
|
clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
ocs "github.com/opencontainers/specs/specs-go"
|
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/containerd/specs"
|
"github.com/docker/containerd/specs"
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
"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) {
|
func getRootIDs(s *specs.Spec) (int, int, error) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package specs
|
package specs
|
||||||
|
|
||||||
import ocs "github.com/opencontainers/specs/specs-go"
|
import ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
|
||||||
type (
|
type (
|
||||||
ProcessSpec ocs.Process
|
ProcessSpec ocs.Process
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package specs
|
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.
|
// Windows support currently.
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -8,7 +8,7 @@ type (
|
||||||
ProcessSpec Process
|
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.
|
// currently on Windows.
|
||||||
|
|
||||||
// Process contains information to start a specific application inside the container.
|
// Process contains information to start a specific application inside the container.
|
||||||
|
|
|
@ -90,7 +90,7 @@ in tmpfs.
|
||||||
|
|
||||||
After `/dev/null` has been setup we check for any external links between
|
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
|
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.
|
that is local to the container's rootfs.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ func writeFile(dir, file, data string) error {
|
||||||
// Normally dir should not be empty, one case is that cgroup subsystem
|
// 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.
|
// is not mounted, we will get empty dir, and we want it fail here.
|
||||||
if dir == "" {
|
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 {
|
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)
|
return fmt.Errorf("failed to write %v to %v: %v", data, file, err)
|
||||||
|
|
|
@ -11,6 +11,7 @@ type ThrottlingData struct {
|
||||||
ThrottledTime uint64 `json:"throttled_time,omitempty"`
|
ThrottledTime uint64 `json:"throttled_time,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CpuUsage denotes the usage of a CPU.
|
||||||
// All CPU stats are aggregate since container inception.
|
// All CPU stats are aggregate since container inception.
|
||||||
type CpuUsage struct {
|
type CpuUsage struct {
|
||||||
// Total CPU time consumed.
|
// Total CPU time consumed.
|
||||||
|
|
|
@ -273,7 +273,7 @@ func writeFile(dir, file, data string) error {
|
||||||
// Normally dir should not be empty, one case is that cgroup subsystem
|
// 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.
|
// is not mounted, we will get empty dir, and we want it fail here.
|
||||||
if dir == "" {
|
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)
|
return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ func FindCgroupMountpoint(subsystem string) (string, error) {
|
||||||
// We are not using mount.GetMounts() because it's super-inefficient,
|
// We are not using mount.GetMounts() because it's super-inefficient,
|
||||||
// parsing it directly sped up x10 times because of not using Sscanf.
|
// parsing it directly sped up x10 times because of not using Sscanf.
|
||||||
// It was one of two major performance drawbacks in container start.
|
// It was one of two major performance drawbacks in container start.
|
||||||
|
if !isSubsystemAvailable(subsystem) {
|
||||||
|
return "", NewNotFoundError(subsystem)
|
||||||
|
}
|
||||||
f, err := os.Open("/proc/self/mountinfo")
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -47,6 +50,9 @@ func FindCgroupMountpoint(subsystem string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) {
|
func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) {
|
||||||
|
if !isSubsystemAvailable(subsystem) {
|
||||||
|
return "", "", NewNotFoundError(subsystem)
|
||||||
|
}
|
||||||
f, err := os.Open("/proc/self/mountinfo")
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
|
@ -70,6 +76,15 @@ func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) {
|
||||||
return "", "", NewNotFoundError(subsystem)
|
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) {
|
func FindCgroupMountpointDir() (string, error) {
|
||||||
f, err := os.Open("/proc/self/mountinfo")
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
if err != nil {
|
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) {
|
func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) {
|
||||||
res := make([]Mount, 0, len(ss))
|
res := make([]Mount, 0, len(ss))
|
||||||
scanner := bufio.NewScanner(mi)
|
scanner := bufio.NewScanner(mi)
|
||||||
for scanner.Scan() {
|
numFound := 0
|
||||||
|
for scanner.Scan() && numFound < len(ss) {
|
||||||
txt := scanner.Text()
|
txt := scanner.Text()
|
||||||
sepIdx := strings.Index(txt, " - ")
|
sepIdx := strings.Index(txt, " - ")
|
||||||
if sepIdx == -1 {
|
if sepIdx == -1 {
|
||||||
|
@ -139,12 +155,15 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) {
|
||||||
Root: fields[3],
|
Root: fields[3],
|
||||||
}
|
}
|
||||||
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||||
|
if !ss[opt] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if strings.HasPrefix(opt, cgroupNamePrefix) {
|
if strings.HasPrefix(opt, cgroupNamePrefix) {
|
||||||
m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
|
m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
|
||||||
}
|
} else {
|
||||||
if ss[opt] {
|
|
||||||
m.Subsystems = append(m.Subsystems, opt)
|
m.Subsystems = append(m.Subsystems, opt)
|
||||||
}
|
}
|
||||||
|
numFound++
|
||||||
}
|
}
|
||||||
res = append(res, m)
|
res = append(res, m)
|
||||||
}
|
}
|
||||||
|
@ -161,19 +180,19 @@ func GetCgroupMounts() ([]Mount, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
all, err := GetAllSubsystems()
|
all, err := ParseCgroupFile("/proc/self/cgroup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
allMap := make(map[string]bool)
|
allMap := make(map[string]bool)
|
||||||
for _, s := range all {
|
for s := range all {
|
||||||
allMap[s] = true
|
allMap[s] = true
|
||||||
}
|
}
|
||||||
return getCgroupMountsHelper(allMap, f)
|
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) {
|
func GetAllSubsystems() ([]string, error) {
|
||||||
f, err := os.Open("/proc/cgroups")
|
f, err := os.Open("/proc/cgroups")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -199,7 +218,7 @@ func GetAllSubsystems() ([]string, error) {
|
||||||
return subsystems, nil
|
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) {
|
func GetThisCgroupDir(subsystem string) (string, error) {
|
||||||
cgroups, err := ParseCgroupFile("/proc/self/cgroup")
|
cgroups, err := ParseCgroupFile("/proc/self/cgroup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -33,7 +33,7 @@ type Seccomp struct {
|
||||||
Syscalls []*Syscall `json:"syscalls"`
|
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
|
type Action int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -44,7 +44,7 @@ const (
|
||||||
Trace
|
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
|
type Operator int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -57,7 +57,7 @@ const (
|
||||||
MaskEqualTo
|
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 {
|
type Arg struct {
|
||||||
Index uint `json:"index"`
|
Index uint `json:"index"`
|
||||||
Value uint64 `json:"value"`
|
Value uint64 `json:"value"`
|
||||||
|
@ -65,7 +65,7 @@ type Arg struct {
|
||||||
Op Operator `json:"op"`
|
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 {
|
type Syscall struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Action Action `json:"action"`
|
Action Action `json:"action"`
|
||||||
|
@ -261,7 +261,7 @@ type Hook interface {
|
||||||
Run(HookState) error
|
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 {
|
func NewFunctionHook(f func(HookState) error) FuncHook {
|
||||||
return FuncHook{
|
return FuncHook{
|
||||||
run: f,
|
run: f,
|
||||||
|
@ -284,7 +284,7 @@ type Command struct {
|
||||||
Timeout *time.Duration `json:"timeout"`
|
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 {
|
func NewCommandHook(cmd Command) CommandHook {
|
||||||
return CommandHook{
|
return CommandHook{
|
||||||
Command: cmd,
|
Command: cmd,
|
||||||
|
|
|
@ -4,7 +4,7 @@ package configs
|
||||||
|
|
||||||
import "fmt"
|
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.
|
// when user namespaces are enabled.
|
||||||
func (c Config) HostUID() (int, error) {
|
func (c Config) HostUID() (int, error) {
|
||||||
if c.Namespaces.Contains(NEWUSER) {
|
if c.Namespaces.Contains(NEWUSER) {
|
||||||
|
@ -21,7 +21,7 @@ func (c Config) HostUID() (int, error) {
|
||||||
return 0, nil
|
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.
|
// when user namespaces are enabled.
|
||||||
func (c Config) HostGID() (int, error) {
|
func (c Config) HostGID() (int, error) {
|
||||||
if c.Namespaces.Contains(NEWUSER) {
|
if c.Namespaces.Contains(NEWUSER) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
var (
|
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{
|
DefaultSimpleDevices = []*Device{
|
||||||
// /dev/null and zero
|
// /dev/null and zero
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/selinux"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Validator interface {
|
type Validator interface {
|
||||||
|
@ -42,7 +43,7 @@ func (v *ConfigValidator) Validate(config *configs.Config) error {
|
||||||
return nil
|
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.
|
// to the container's root filesystem.
|
||||||
func (v *ConfigValidator) rootfs(config *configs.Config) error {
|
func (v *ConfigValidator) rootfs(config *configs.Config) error {
|
||||||
cleaned, err := filepath.Abs(config.Rootfs)
|
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 {
|
if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil {
|
||||||
return err
|
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 fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -80,6 +81,10 @@ func (v *ConfigValidator) security(config *configs.Config) error {
|
||||||
!config.Namespaces.Contains(configs.NEWNS) {
|
!config.Namespaces.Contains(configs.NEWNS) {
|
||||||
return fmt.Errorf("unable to restrict sys entries without a private MNT namespace")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,14 +105,7 @@ func (v *ConfigValidator) usernamespace(config *configs.Config) error {
|
||||||
// /proc/sys isn't completely namespaced and depending on which namespaces
|
// /proc/sys isn't completely namespaced and depending on which namespaces
|
||||||
// are specified, a subset of sysctls are permitted.
|
// are specified, a subset of sysctls are permitted.
|
||||||
func (v *ConfigValidator) sysctl(config *configs.Config) error {
|
func (v *ConfigValidator) sysctl(config *configs.Config) error {
|
||||||
validSysctlPrefixes := []string{}
|
validSysctlMap := map[string]bool{
|
||||||
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.msgmax": true,
|
||||||
"kernel.msgmnb": true,
|
"kernel.msgmnb": true,
|
||||||
"kernel.msgmni": true,
|
"kernel.msgmni": true,
|
||||||
|
@ -117,22 +115,24 @@ func (v *ConfigValidator) sysctl(config *configs.Config) error {
|
||||||
"kernel.shmmni": true,
|
"kernel.shmmni": true,
|
||||||
"kernel.shm_rmid_forced": true,
|
"kernel.shm_rmid_forced": true,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for s := range config.Sysctl {
|
for s := range config.Sysctl {
|
||||||
if validSysctlMap[s] {
|
if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") {
|
||||||
|
if config.Namespaces.Contains(configs.NEWIPC) {
|
||||||
continue
|
continue
|
||||||
}
|
} else {
|
||||||
valid := false
|
return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s)
|
||||||
for _, vp := range validSysctlPrefixes {
|
|
||||||
if strings.HasPrefix(s, vp) {
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !valid {
|
if strings.HasPrefix(s, "net.") {
|
||||||
return fmt.Errorf("sysctl %q is not permitted in the config", s)
|
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
|
return nil
|
||||||
}
|
}
|
|
@ -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.
|
// with namespaces, cgroups, capabilities, and filesystem access controls.
|
||||||
// It allows you to manage the lifecycle of the container performing additional operations
|
// It allows you to manage the lifecycle of the container performing additional operations
|
||||||
// after the container is created.
|
// after the container is created.
|
||||||
|
@ -11,23 +11,23 @@ import (
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The status of a container.
|
// Status is the status of a container.
|
||||||
type Status int
|
type Status int
|
||||||
|
|
||||||
const (
|
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
|
Created Status = iota
|
||||||
|
|
||||||
// The container exists and is running.
|
// Created is the status that denotes the container exists and is running.
|
||||||
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
|
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
|
Paused
|
||||||
|
|
||||||
// The container does not exist.
|
// Destroyed is the status that denotes the container does not exist.
|
||||||
Destroyed
|
Destroyed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ type BaseState struct {
|
||||||
Config configs.Config `json:"config"`
|
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
|
// 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
|
// be destroyed by a separate process, any function may return that the container
|
||||||
|
|
|
@ -62,7 +62,7 @@ type State struct {
|
||||||
ExternalDescriptors []string `json:"external_descriptors,omitempty"`
|
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
|
// 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
|
// be destroyed by a separate process, any function may return that the container
|
||||||
|
@ -84,7 +84,7 @@ type Container interface {
|
||||||
// Systemerror - System error.
|
// Systemerror - System error.
|
||||||
Restore(process *Process, criuOpts *CriuOpts) 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
|
// the execution of any user processes. Asynchronously, when the container finished being paused the
|
||||||
// state is changed to PAUSED.
|
// state is changed to PAUSED.
|
||||||
// If the Container state is PAUSED, do nothing.
|
// If the Container state is PAUSED, do nothing.
|
||||||
|
@ -141,7 +141,7 @@ func (c *linuxContainer) State() (*State, error) {
|
||||||
func (c *linuxContainer) Processes() ([]int, error) {
|
func (c *linuxContainer) Processes() ([]int, error) {
|
||||||
pids, err := c.cgroupManager.GetAllPids()
|
pids, err := c.cgroupManager.GetAllPids()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newSystemError(err)
|
return nil, newSystemErrorWithCause(err, "getting all container pids from cgroups")
|
||||||
}
|
}
|
||||||
return pids, nil
|
return pids, nil
|
||||||
}
|
}
|
||||||
|
@ -152,14 +152,14 @@ func (c *linuxContainer) Stats() (*Stats, error) {
|
||||||
stats = &Stats{}
|
stats = &Stats{}
|
||||||
)
|
)
|
||||||
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
|
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 {
|
for _, iface := range c.config.Networks {
|
||||||
switch iface.Type {
|
switch iface.Type {
|
||||||
case "veth":
|
case "veth":
|
||||||
istats, err := getNetworkInterfaceStats(iface.HostInterfaceName)
|
istats, err := getNetworkInterfaceStats(iface.HostInterfaceName)
|
||||||
if err != nil {
|
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)
|
stats.Interfaces = append(stats.Interfaces, istats)
|
||||||
}
|
}
|
||||||
|
@ -184,14 +184,14 @@ func (c *linuxContainer) Start(process *Process) error {
|
||||||
doInit := status == Destroyed
|
doInit := status == Destroyed
|
||||||
parent, err := c.newParentProcess(process, doInit)
|
parent, err := c.newParentProcess(process, doInit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "creating new parent process")
|
||||||
}
|
}
|
||||||
if err := parent.start(); err != nil {
|
if err := parent.start(); err != nil {
|
||||||
// terminate the process to ensure that it properly is reaped.
|
// terminate the process to ensure that it properly is reaped.
|
||||||
if err := parent.terminate(); err != nil {
|
if err := parent.terminate(); err != nil {
|
||||||
logrus.Warn(err)
|
logrus.Warn(err)
|
||||||
}
|
}
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "starting container process")
|
||||||
}
|
}
|
||||||
// generate a timestamp indicating when the container was started
|
// generate a timestamp indicating when the container was started
|
||||||
c.created = time.Now().UTC()
|
c.created = time.Now().UTC()
|
||||||
|
@ -211,12 +211,12 @@ func (c *linuxContainer) Start(process *Process) error {
|
||||||
Root: c.config.Rootfs,
|
Root: c.config.Rootfs,
|
||||||
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
|
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 := hook.Run(s); err != nil {
|
||||||
if err := parent.terminate(); err != nil {
|
if err := parent.terminate(); err != nil {
|
||||||
logrus.Warn(err)
|
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 {
|
func (c *linuxContainer) Signal(s os.Signal) error {
|
||||||
if err := c.initProcess.signal(s); err != nil {
|
if err := c.initProcess.signal(s); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "signaling init process")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -234,11 +234,11 @@ func (c *linuxContainer) Signal(s os.Signal) error {
|
||||||
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
|
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
|
||||||
parentPipe, childPipe, err := newPipe()
|
parentPipe, childPipe, err := newPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newSystemError(err)
|
return nil, newSystemErrorWithCause(err, "creating new init pipe")
|
||||||
}
|
}
|
||||||
cmd, err := c.commandTemplate(p, childPipe)
|
cmd, err := c.commandTemplate(p, childPipe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newSystemError(err)
|
return nil, newSystemErrorWithCause(err, "creating new command template")
|
||||||
}
|
}
|
||||||
if !doInit {
|
if !doInit {
|
||||||
return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
|
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))
|
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns))
|
||||||
state, err := c.currentState()
|
state, err := c.currentState()
|
||||||
if err != nil {
|
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
|
// for setns process, we dont have to set cloneflags as the process namespaces
|
||||||
// will only be set via setns syscall
|
// 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)
|
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check Criu version greater than or equal to min_version
|
// checkCriuVersion checks Criu version greater than or equal to minVersion
|
||||||
func (c *linuxContainer) checkCriuVersion(min_version string) error {
|
func (c *linuxContainer) checkCriuVersion(minVersion string) error {
|
||||||
var x, y, z, versionReq int
|
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 {
|
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
|
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
|
c.criuVersion = x*10000 + y*100 + z
|
||||||
|
|
||||||
if c.criuVersion < versionReq {
|
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
|
return nil
|
||||||
|
@ -955,9 +955,9 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
|
||||||
Pid: int(notify.GetPid()),
|
Pid: int(notify.GetPid()),
|
||||||
Root: c.config.Rootfs,
|
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 {
|
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 {
|
if err == syscall.ESRCH {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, newSystemError(err)
|
return false, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", c.initProcess.pid())
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -1057,7 +1057,7 @@ func (c *linuxContainer) isPaused() (bool, error) {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return false, nil
|
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
|
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
|
// only set to join this namespace if it exists
|
||||||
if _, err := os.Lstat(p); err != nil {
|
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
|
// do not allow namespace path with comma as we use it to separate
|
||||||
// the namespace paths
|
// the namespace paths
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
package libcontainer
|
package libcontainer
|
||||||
|
|
||||||
// cgroup restoring strategy provided by criu
|
// cgroup restoring strategy provided by criu
|
||||||
type cg_mode uint32
|
type cgMode uint32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CRIU_CG_MODE_SOFT cg_mode = 3 + iota // restore cgroup properties if only dir created by criu
|
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_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_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_DEFAULT // the same as CRIU_CG_MODE_SOFT
|
||||||
|
@ -32,6 +32,6 @@ type CriuOpts struct {
|
||||||
FileLocks bool // handle file locks, for safety
|
FileLocks bool // handle file locks, for safety
|
||||||
PageServer CriuPageServerInfo // allow to dump to criu page server
|
PageServer CriuPageServerInfo // allow to dump to criu page server
|
||||||
VethPairs []VethPairName // pass the veth to criu when restore
|
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
|
EmptyNs uint32 // don't c/r properties for namespace from this mask
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package libcontainer
|
||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
// API error code type.
|
// ErrorCode is the API error code type.
|
||||||
type ErrorCode int
|
type ErrorCode int
|
||||||
|
|
||||||
// API error codes.
|
// API error codes.
|
||||||
|
@ -56,7 +56,7 @@ func (c ErrorCode) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// API Error type.
|
// Error is the API error type.
|
||||||
type Error interface {
|
type Error interface {
|
||||||
error
|
error
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package libcontainer
|
package libcontainer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
@ -51,6 +52,21 @@ func newGenericError(err error, c ErrorCode) Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSystemError(err error) 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 {
|
if le, ok := err.(Error); ok {
|
||||||
return le
|
return le
|
||||||
}
|
}
|
||||||
|
@ -58,7 +74,8 @@ func newSystemError(err error) Error {
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Err: err,
|
Err: err,
|
||||||
ECode: SystemError,
|
ECode: SystemError,
|
||||||
Stack: stacktrace.Capture(1),
|
Cause: cause,
|
||||||
|
Stack: stacktrace.Capture(2),
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gerr.Message = err.Error()
|
gerr.Message = err.Error()
|
||||||
|
@ -70,13 +87,18 @@ type genericError struct {
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
ECode ErrorCode
|
ECode ErrorCode
|
||||||
Err error `json:"-"`
|
Err error `json:"-"`
|
||||||
|
Cause string
|
||||||
Message string
|
Message string
|
||||||
Stack stacktrace.Stacktrace
|
Stack stacktrace.Stacktrace
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *genericError) Error() string {
|
func (e *genericError) Error() string {
|
||||||
|
if e.Cause == "" {
|
||||||
return e.Message
|
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 {
|
func (e *genericError) Code() ErrorCode {
|
||||||
return e.ECode
|
return e.ECode
|
||||||
|
|
|
@ -4,9 +4,9 @@ package keyctl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"syscall"
|
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ const KEYCTL_DESCRIBE = 6
|
||||||
type KeySerial uint32
|
type KeySerial uint32
|
||||||
|
|
||||||
func JoinSessionKeyring(name string) (KeySerial, error) {
|
func JoinSessionKeyring(name string) (KeySerial, error) {
|
||||||
var _name *byte = nil
|
var _name *byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
|
@ -34,7 +34,7 @@ func JoinSessionKeyring(name string) (KeySerial, error) {
|
||||||
return KeySerial(sessKeyId), nil
|
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
|
// anding the bits with the given mask (clearing permissions) and setting
|
||||||
// additional permission bits
|
// additional permission bits
|
||||||
func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error {
|
func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error {
|
||||||
|
@ -64,4 +64,3 @@ func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ func SetFileLabel(path string, fileLabel string) error {
|
||||||
return nil
|
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 {
|
func SetFileCreateLabel(fileLabel string) error {
|
||||||
if selinux.SelinuxEnabled() {
|
if selinux.SelinuxEnabled() {
|
||||||
return selinux.Setfscreatecon(fileLabel)
|
return selinux.Setfscreatecon(fileLabel)
|
||||||
|
@ -115,7 +115,7 @@ func SetFileCreateLabel(fileLabel string) error {
|
||||||
return nil
|
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.
|
// It changes the MCS label to s0 if shared is true.
|
||||||
// This will allow all containers to share the content.
|
// This will allow all containers to share the content.
|
||||||
func Relabel(path string, fileLabel string, shared bool) error {
|
func Relabel(path string, fileLabel string, shared bool) error {
|
||||||
|
|
|
@ -27,7 +27,8 @@ type Int32msg struct {
|
||||||
Value uint32
|
Value uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// int32msg has the following representation
|
// Serialize serializes the message.
|
||||||
|
// Int32msg has the following representation
|
||||||
// | nlattr len | nlattr type |
|
// | nlattr len | nlattr type |
|
||||||
// | uint32 value |
|
// | uint32 value |
|
||||||
func (msg *Int32msg) Serialize() []byte {
|
func (msg *Int32msg) Serialize() []byte {
|
||||||
|
@ -43,7 +44,7 @@ func (msg *Int32msg) Len() int {
|
||||||
return syscall_NLA_HDRLEN + 4
|
return syscall_NLA_HDRLEN + 4
|
||||||
}
|
}
|
||||||
|
|
||||||
// bytemsg has the following representation
|
// Bytemsg has the following representation
|
||||||
// | nlattr len | nlattr type |
|
// | nlattr len | nlattr type |
|
||||||
// | value | pad |
|
// | value | pad |
|
||||||
type Bytemsg struct {
|
type Bytemsg struct {
|
||||||
|
|
|
@ -70,47 +70,47 @@ func (p *setnsProcess) start() (err error) {
|
||||||
err = p.cmd.Start()
|
err = p.cmd.Start()
|
||||||
p.childPipe.Close()
|
p.childPipe.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "starting setns process")
|
||||||
}
|
}
|
||||||
if p.bootstrapData != nil {
|
if p.bootstrapData != nil {
|
||||||
if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != 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 {
|
if err = p.execSetns(); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "executing setns process")
|
||||||
}
|
}
|
||||||
if len(p.cgroupPaths) > 0 {
|
if len(p.cgroupPaths) > 0 {
|
||||||
if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil {
|
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
|
// set oom_score_adj
|
||||||
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
|
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
|
// set rlimits, this has to be done here because we lose permissions
|
||||||
// to raise the limits once we enter a user-namespace
|
// to raise the limits once we enter a user-namespace
|
||||||
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
|
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 {
|
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 {
|
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
|
// wait for the child process to fully complete and receive an error message
|
||||||
// if one was encoutered
|
// if one was encoutered
|
||||||
var ierr *genericError
|
var ierr *genericError
|
||||||
if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF {
|
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.
|
// Must be done after Shutdown so the child will exit and we can wait for it.
|
||||||
if ierr != nil {
|
if ierr != nil {
|
||||||
p.wait()
|
p.wait()
|
||||||
return newSystemError(ierr)
|
return ierr
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ func (p *setnsProcess) execSetns() error {
|
||||||
status, err := p.cmd.Process.Wait()
|
status, err := p.cmd.Process.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.cmd.Wait()
|
p.cmd.Wait()
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "waiting on setns process to finish")
|
||||||
}
|
}
|
||||||
if !status.Success() {
|
if !status.Success() {
|
||||||
p.cmd.Wait()
|
p.cmd.Wait()
|
||||||
|
@ -132,7 +132,7 @@ func (p *setnsProcess) execSetns() error {
|
||||||
var pid *pid
|
var pid *pid
|
||||||
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
|
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
|
||||||
p.cmd.Wait()
|
p.cmd.Wait()
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "reading pid from init pipe")
|
||||||
}
|
}
|
||||||
process, err := os.FindProcess(pid.Pid)
|
process, err := os.FindProcess(pid.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -231,26 +231,26 @@ func (p *initProcess) start() error {
|
||||||
p.childPipe.Close()
|
p.childPipe.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.process.ops = 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 {
|
if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.execSetns(); err != nil {
|
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
|
// Save the standard descriptor names before the container process
|
||||||
// can potentially move them (e.g., via dup2()). If we don't do this now,
|
// 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.
|
// we won't know at checkpoint time which file descriptor to look up.
|
||||||
fds, err := getPipeFds(p.pid())
|
fds, err := getPipeFds(p.pid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", p.pid())
|
||||||
}
|
}
|
||||||
p.setExternalDescriptors(fds)
|
p.setExternalDescriptors(fds)
|
||||||
// Do this before syncing with child so that no children
|
// Do this before syncing with child so that no children
|
||||||
// can escape the cgroup
|
// can escape the cgroup
|
||||||
if err := p.manager.Apply(p.pid()); err != nil {
|
if err := p.manager.Apply(p.pid()); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "applying cgroup configuration for process")
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -259,10 +259,10 @@ func (p *initProcess) start() error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := p.createNetworkInterfaces(); err != nil {
|
if err := p.createNetworkInterfaces(); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "creating nework interfaces")
|
||||||
}
|
}
|
||||||
if err := p.sendConfig(); err != nil {
|
if err := p.sendConfig(); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "sending config to init process")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
procSync syncT
|
procSync syncT
|
||||||
|
@ -278,21 +278,21 @@ loop:
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "decoding sync type from init pipe")
|
||||||
}
|
}
|
||||||
switch procSync.Type {
|
switch procSync.Type {
|
||||||
case procReady:
|
case procReady:
|
||||||
if err := p.manager.Set(p.config.Config); err != nil {
|
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
|
// set oom_score_adj
|
||||||
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
|
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
|
// set rlimits, this has to be done here because we lose permissions
|
||||||
// to raise the limits once we enter a user-namespace
|
// to raise the limits once we enter a user-namespace
|
||||||
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
|
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "setting rlimits for ready process")
|
||||||
}
|
}
|
||||||
// call prestart hooks
|
// call prestart hooks
|
||||||
if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
|
if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
|
||||||
|
@ -303,16 +303,16 @@ loop:
|
||||||
Pid: p.pid(),
|
Pid: p.pid(),
|
||||||
Root: p.config.Config.Rootfs,
|
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 {
|
if err := hook.Run(s); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCausef(err, "running prestart hook %d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sync with child.
|
// Sync with child.
|
||||||
if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil {
|
if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "reading syncT run type")
|
||||||
}
|
}
|
||||||
sentRun = true
|
sentRun = true
|
||||||
case procHooks:
|
case procHooks:
|
||||||
|
@ -324,22 +324,22 @@ loop:
|
||||||
Root: p.config.Config.Rootfs,
|
Root: p.config.Config.Rootfs,
|
||||||
BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
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 {
|
if err := hook.Run(s); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCausef(err, "running prestart hook %d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sync with child.
|
// Sync with child.
|
||||||
if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil {
|
if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "reading syncT resume type")
|
||||||
}
|
}
|
||||||
sentResume = true
|
sentResume = true
|
||||||
case procError:
|
case procError:
|
||||||
// wait for the child process to fully complete and receive an error message
|
// wait for the child process to fully complete and receive an error message
|
||||||
// if one was encoutered
|
// if one was encoutered
|
||||||
if err := dec.Decode(&ierr); err != nil && err != io.EOF {
|
if err := dec.Decode(&ierr); err != nil && err != io.EOF {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "decoding proc error from init")
|
||||||
}
|
}
|
||||||
if ierr != nil {
|
if ierr != nil {
|
||||||
break loop
|
break loop
|
||||||
|
@ -347,22 +347,22 @@ loop:
|
||||||
// Programmer error.
|
// Programmer error.
|
||||||
panic("No error following JSON procError payload.")
|
panic("No error following JSON procError payload.")
|
||||||
default:
|
default:
|
||||||
return newSystemError(fmt.Errorf("invalid JSON synchronisation payload from child"))
|
return newSystemError(fmt.Errorf("invalid JSON payload from child"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !sentRun {
|
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 {
|
if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume {
|
||||||
return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process"))
|
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 {
|
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.
|
// Must be done after Shutdown so the child will exit and we can wait for it.
|
||||||
if ierr != nil {
|
if ierr != nil {
|
||||||
p.wait()
|
p.wait()
|
||||||
return newSystemError(ierr)
|
return ierr
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
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 {
|
func needsSetupDev(config *configs.Config) bool {
|
||||||
for _, m := range config.Mounts {
|
for _, m := range config.Mounts {
|
||||||
if m.Device == "bind" && (m.Destination == "/dev" || m.Destination == "/dev/") {
|
if m.Device == "bind" && (m.Destination == "/dev" || m.Destination == "/dev/") {
|
||||||
|
@ -39,35 +39,35 @@ func needsSetupDev(config *configs.Config) bool {
|
||||||
// new mount namespace.
|
// new mount namespace.
|
||||||
func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) {
|
func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) {
|
||||||
if err := prepareRoot(config); err != nil {
|
if err := prepareRoot(config); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "preparing rootfs")
|
||||||
}
|
}
|
||||||
|
|
||||||
setupDev := needsSetupDev(config)
|
setupDev := needsSetupDev(config)
|
||||||
for _, m := range config.Mounts {
|
for _, m := range config.Mounts {
|
||||||
for _, precmd := range m.PremountCmds {
|
for _, precmd := range m.PremountCmds {
|
||||||
if err := mountCmd(precmd); err != nil {
|
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 {
|
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 {
|
for _, postcmd := range m.PostmountCmds {
|
||||||
if err := mountCmd(postcmd); err != nil {
|
if err := mountCmd(postcmd); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "running postmount command")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if setupDev {
|
if setupDev {
|
||||||
if err := createDevices(config); err != nil {
|
if err := createDevices(config); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "creating device nodes")
|
||||||
}
|
}
|
||||||
if err := setupPtmx(config, console); err != nil {
|
if err := setupPtmx(config, console); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "setting up ptmx")
|
||||||
}
|
}
|
||||||
if err := setupDevSymlinks(config.Rootfs); err != nil {
|
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.
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
if err := syscall.Chdir(config.Rootfs); err != nil {
|
if err := syscall.Chdir(config.Rootfs); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCausef(err, "changing dir to %q", config.Rootfs)
|
||||||
}
|
}
|
||||||
if config.NoPivotRoot {
|
if config.NoPivotRoot {
|
||||||
err = msMoveRoot(config.Rootfs)
|
err = msMoveRoot(config.Rootfs)
|
||||||
|
@ -86,11 +86,11 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
|
||||||
err = pivotRoot(config.Rootfs, config.PivotDir)
|
err = pivotRoot(config.Rootfs, config.PivotDir)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "jailing process inside rootfs")
|
||||||
}
|
}
|
||||||
if setupDev {
|
if setupDev {
|
||||||
if err := reOpenDevNull(); err != nil {
|
if err := reOpenDevNull(); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "reopening /dev/null inside container")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remount dev as ro if specifed
|
// 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.Destination == "/dev" {
|
||||||
if m.Flags&syscall.MS_RDONLY != 0 {
|
if m.Flags&syscall.MS_RDONLY != 0 {
|
||||||
if err := remountReadonly(m.Destination); err != nil {
|
if err := remountReadonly(m.Destination); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -107,7 +107,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
|
||||||
// set rootfs ( / ) as readonly
|
// set rootfs ( / ) as readonly
|
||||||
if config.Readonlyfs {
|
if config.Readonlyfs {
|
||||||
if err := setReadonly(); err != nil {
|
if err := setReadonly(); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemErrorWithCause(err, "setting rootfs as readonly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syscall.Umask(0022)
|
syscall.Umask(0022)
|
||||||
|
@ -115,14 +115,12 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
|
||||||
}
|
}
|
||||||
|
|
||||||
func mountCmd(cmd configs.Command) error {
|
func mountCmd(cmd configs.Command) error {
|
||||||
|
|
||||||
command := exec.Command(cmd.Path, cmd.Args[:]...)
|
command := exec.Command(cmd.Path, cmd.Args[:]...)
|
||||||
command.Env = cmd.Env
|
command.Env = cmd.Env
|
||||||
command.Dir = cmd.Dir
|
command.Dir = cmd.Dir
|
||||||
if out, err := command.CombinedOutput(); err != nil {
|
if out, err := command.CombinedOutput(); err != nil {
|
||||||
return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err)
|
return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,8 +264,10 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
|
||||||
if m.Flags&syscall.MS_RDONLY != 0 {
|
if m.Flags&syscall.MS_RDONLY != 0 {
|
||||||
// remount cgroup root as readonly
|
// remount cgroup root as readonly
|
||||||
mcgrouproot := &configs.Mount{
|
mcgrouproot := &configs.Mount{
|
||||||
|
Source: m.Destination,
|
||||||
|
Device: "bind",
|
||||||
Destination: m.Destination,
|
Destination: m.Destination,
|
||||||
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
Flags: defaultMountFlags | syscall.MS_RDONLY | syscall.MS_BIND,
|
||||||
}
|
}
|
||||||
if err := remount(mcgrouproot, rootfs); err != nil {
|
if err := remount(mcgrouproot, rootfs); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -515,10 +515,10 @@ func getParentMount(rootfs string) (string, string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make parent mount private if it was shared
|
// Make parent mount private if it was shared
|
||||||
func rootfsParentMountPrivate(config *configs.Config) error {
|
func rootfsParentMountPrivate(rootfs string) error {
|
||||||
sharedMount := false
|
sharedMount := false
|
||||||
|
|
||||||
parentMount, optionalOpts, err := getParentMount(config.Rootfs)
|
parentMount, optionalOpts, err := getParentMount(rootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -550,10 +550,11 @@ func prepareRoot(config *configs.Config) error {
|
||||||
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
|
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if config.NoPivotRoot {
|
||||||
if err := rootfsParentMountPrivate(config); err != nil {
|
if err := rootfsParentMountPrivate(config.Rootfs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
|
return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
|
||||||
}
|
}
|
||||||
|
@ -594,9 +595,16 @@ func pivotRoot(rootfs, pivotBaseDir string) (err error) {
|
||||||
err = errVal
|
err = errVal
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
if err := syscall.PivotRoot(rootfs, pivotDir); err != nil {
|
||||||
|
// Make the parent mount private
|
||||||
|
if err := rootfsParentMountPrivate(rootfs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Try again
|
||||||
if err := syscall.PivotRoot(rootfs, pivotDir); err != nil {
|
if err := syscall.PivotRoot(rootfs, pivotDir); err != nil {
|
||||||
return fmt.Errorf("pivot_root %s", err)
|
return fmt.Errorf("pivot_root %s", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err := syscall.Chdir("/"); err != nil {
|
if err := syscall.Chdir("/"); err != nil {
|
||||||
return fmt.Errorf("chdir / %s", err)
|
return fmt.Errorf("chdir / %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
var ErrSeccompNotEnabled = errors.New("seccomp: config provided but seccomp not supported")
|
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 {
|
func InitSeccomp(config *configs.Seccomp) error {
|
||||||
if config != nil {
|
if config != nil {
|
||||||
return ErrSeccompNotEnabled
|
return ErrSeccompNotEnabled
|
||||||
|
|
499
vendor/src/github.com/opencontainers/runc/libcontainer/selinux/selinux.go
vendored
Normal file
499
vendor/src/github.com/opencontainers/runc/libcontainer/selinux/selinux.go
vendored
Normal file
|
@ -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"}
|
||||||
|
}
|
|
@ -2,14 +2,14 @@ package stacktrace
|
||||||
|
|
||||||
import "runtime"
|
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
|
// skip is the number of frames to skip
|
||||||
func Capture(userSkip int) Stacktrace {
|
func Capture(userSkip int) Stacktrace {
|
||||||
var (
|
var (
|
||||||
skip = userSkip + 1 // add one for our own function
|
skip = userSkip + 1 // add one for our own function
|
||||||
frames []Frame
|
frames []Frame
|
||||||
prevPc uintptr = 0
|
prevPc uintptr
|
||||||
)
|
)
|
||||||
for i := skip; ; i++ {
|
for i := skip; ; i++ {
|
||||||
pc, file, line, ok := runtime.Caller(i)
|
pc, file, line, ok := runtime.Caller(i)
|
||||||
|
|
|
@ -100,17 +100,12 @@ func Setctty() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// RunningInUserNS detects whether we are currently running in a user namespace.
|
||||||
* Detect whether we are currently running in a user namespace.
|
// Copied from github.com/lxc/lxd/shared/util.go
|
||||||
* Copied from github.com/lxc/lxd/shared/util.go
|
|
||||||
*/
|
|
||||||
func RunningInUserNS() bool {
|
func RunningInUserNS() bool {
|
||||||
file, err := os.Open("/proc/self/uid_map")
|
file, err := os.Open("/proc/self/uid_map")
|
||||||
if err != nil {
|
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
|
return false
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
|
@ -49,7 +49,7 @@ type Process struct {
|
||||||
|
|
||||||
// ApparmorProfile specified the apparmor profile for the container. (this field is platform dependent)
|
// ApparmorProfile specified the apparmor profile for the container. (this field is platform dependent)
|
||||||
ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"`
|
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"`
|
SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ type Hook struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Args []string `json:"args,omitempty"`
|
Args []string `json:"args,omitempty"`
|
||||||
Env []string `json:"env,omitempty"`
|
Env []string `json:"env,omitempty"`
|
||||||
|
Timeout *int `json:"timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks for container setup and teardown
|
// 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.
|
// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
|
||||||
CgroupsPath *string `json:"cgroupsPath,omitempty"`
|
CgroupsPath *string `json:"cgroupsPath,omitempty"`
|
||||||
// Namespaces contains the namespaces that are created and/or joined by the container
|
// 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 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 specifies the seccomp security settings for the container.
|
||||||
Seccomp *Seccomp `json:"seccomp,omitempty"`
|
Seccomp *Seccomp `json:"seccomp,omitempty"`
|
||||||
// RootfsPropagation is the rootfs mount propagation mode for the container.
|
// RootfsPropagation is the rootfs mount propagation mode for the container.
|
||||||
RootfsPropagation string `json:"rootfsPropagation,omitempty"`
|
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
|
// Namespace is the configuration for a Linux namespace
|
|
@ -6,12 +6,12 @@ const (
|
||||||
// VersionMajor is for an API incompatible changes
|
// VersionMajor is for an API incompatible changes
|
||||||
VersionMajor = 0
|
VersionMajor = 0
|
||||||
// VersionMinor is for functionality in a backwards-compatible manner
|
// VersionMinor is for functionality in a backwards-compatible manner
|
||||||
VersionMinor = 4
|
VersionMinor = 6
|
||||||
// VersionPatch is for backwards-compatible bug fixes
|
// VersionPatch is for backwards-compatible bug fixes
|
||||||
VersionPatch = 0
|
VersionPatch = 0
|
||||||
|
|
||||||
// VersionDev indicates development branch. Releases will be empty string.
|
// VersionDev indicates development branch. Releases will be empty string.
|
||||||
VersionDev = ""
|
VersionDev = "-dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the specification version that the package types support.
|
// Version is the specification version that the package types support.
|
Loading…
Reference in a new issue