diff --git a/cli/common_flags.go b/cli/common_flags.go index 136a6b76f6..38e60e6d03 100644 --- a/cli/common_flags.go +++ b/cli/common_flags.go @@ -69,6 +69,8 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container { // user flagSet.StringVarP(&c.user, "user", "u", "", "UID") + flagSet.StringSliceVar(&c.groupAdd, "group-add", nil, "Add additional groups to join") + flagSet.StringVar(&c.utsMode, "uts", "", "UTS namespace to use") flagSet.StringSliceVarP(&c.volume, "volume", "v", nil, "Bind mount volumes to container") diff --git a/cli/container.go b/cli/container.go index f6d2fee97d..c6f42e5b2b 100644 --- a/cli/container.go +++ b/cli/container.go @@ -23,6 +23,7 @@ type container struct { entrypoint string workdir string user string + groupAdd []string hostname string cpushare int64 cpusetcpus string @@ -169,6 +170,7 @@ func (c *container) config() (*types.ContainerCreateConfig, error) { IpcMode: c.ipcMode, PidMode: c.pidMode, UTSMode: c.utsMode, + GroupAdd: c.groupAdd, Sysctls: sysctls, SecurityOpt: c.securityOpt, NetworkMode: networkMode, diff --git a/daemon/mgr/cri.go b/daemon/mgr/cri.go index d88555883f..ba57552231 100644 --- a/daemon/mgr/cri.go +++ b/daemon/mgr/cri.go @@ -43,6 +43,9 @@ const ( nameDelimiter = "_" defaultSandboxImage = "k8s.gcr.io/pause-amd64:3.0" + + namespaceModeHost = "host" + namespaceModeNone = "none" ) var ( diff --git a/daemon/mgr/cri_utils.go b/daemon/mgr/cri_utils.go index da6958913f..50cc1bf512 100644 --- a/daemon/mgr/cri_utils.go +++ b/daemon/mgr/cri_utils.go @@ -157,12 +157,59 @@ func parseSandboxName(name string) (*runtime.PodSandboxMetadata, error) { }, nil } +func modifySandboxNamespaceOptions(nsOpts *runtime.NamespaceOption, hostConfig *apitypes.HostConfig) { + if nsOpts == nil { + return + } + if nsOpts.HostPid { + hostConfig.PidMode = namespaceModeHost + } + if nsOpts.HostIpc { + hostConfig.IpcMode = namespaceModeHost + } + if nsOpts.HostNetwork { + hostConfig.NetworkMode = namespaceModeHost + } +} + +func applySandboxSecurityContext(lc *runtime.LinuxPodSandboxConfig, config *apitypes.ContainerConfig, hc *apitypes.HostConfig) error { + if lc == nil { + return nil + } + + var sc *runtime.LinuxContainerSecurityContext + if lc.SecurityContext != nil { + sc = &runtime.LinuxContainerSecurityContext{ + SupplementalGroups: lc.SecurityContext.SupplementalGroups, + RunAsUser: lc.SecurityContext.RunAsUser, + ReadonlyRootfs: lc.SecurityContext.ReadonlyRootfs, + SelinuxOptions: lc.SecurityContext.SelinuxOptions, + NamespaceOptions: lc.SecurityContext.NamespaceOptions, + } + } + + modifyContainerConfig(sc, config) + err := modifyHostConfig(sc, hc) + if err != nil { + return err + } + modifySandboxNamespaceOptions(sc.GetNamespaceOptions(), hc) + + return nil +} + // applySandboxLinuxOptions applies LinuxPodSandboxConfig to pouch's HostConfig and ContainerCreateConfig. func applySandboxLinuxOptions(hc *apitypes.HostConfig, lc *runtime.LinuxPodSandboxConfig, createConfig *apitypes.ContainerCreateConfig, image string) error { if lc == nil { return nil } + // Apply security context. + err := applySandboxSecurityContext(lc, &createConfig.ContainerConfig, hc) + if err != nil { + return err + } + // Set sysctls. hc.Sysctls = lc.Sysctls return nil @@ -292,10 +339,36 @@ func parseContainerName(name string) (*runtime.ContainerMetadata, error) { func modifyContainerNamespaceOptions(nsOpts *runtime.NamespaceOption, podSandboxID string, hostConfig *apitypes.HostConfig) { sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID) - hostConfig.PidMode = sandboxNSMode - hostConfig.NetworkMode = sandboxNSMode - hostConfig.IpcMode = sandboxNSMode - hostConfig.UTSMode = sandboxNSMode + if nsOpts == nil { + hostConfig.PidMode = sandboxNSMode + hostConfig.IpcMode = sandboxNSMode + hostConfig.NetworkMode = sandboxNSMode + return + } + + for _, n := range []struct { + hostMode bool + nsMode *string + }{ + { + hostMode: nsOpts.HostPid, + nsMode: &hostConfig.PidMode, + }, + { + hostMode: nsOpts.HostIpc, + nsMode: &hostConfig.IpcMode, + }, + { + hostMode: nsOpts.HostNetwork, + nsMode: &hostConfig.NetworkMode, + }, + } { + if n.hostMode { + *n.nsMode = namespaceModeHost + } else { + *n.nsMode = sandboxNSMode + } + } } // getAppArmorSecurityOpts gets appArmor options from container config. @@ -346,6 +419,11 @@ func modifyHostConfig(sc *runtime.LinuxContainerSecurityContext, hostConfig *api return nil } + // Apply supplemental groups. + for _, group := range sc.SupplementalGroups { + hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.FormatInt(group, 10)) + } + // TODO: apply other security options. // Apply capability options. diff --git a/daemon/mgr/spec_process.go b/daemon/mgr/spec_process.go index f9c628225f..193fa02bbf 100644 --- a/daemon/mgr/spec_process.go +++ b/daemon/mgr/spec_process.go @@ -6,6 +6,7 @@ import ( "strings" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" ) func setupCap(ctx context.Context, c *ContainerMeta, spec *SpecWrapper) error { @@ -58,21 +59,30 @@ func setupProcessTTY(ctx context.Context, c *ContainerMeta, spec *SpecWrapper) e } func setupProcessUser(ctx context.Context, c *ContainerMeta, spec *SpecWrapper) (err error) { + user := specs.User{} + // The user option is complicated, now we only handle case "uid". // TODO: handle other cases like "user", "uid:gid", etc. - if c.Config.User == "" { - return nil + if c.Config.User != "" { + fields := strings.Split(c.Config.User, ":") + var u string + u = fields[0] + if uid, err := strconv.Atoi(u); err == nil { + user.UID = uint32(uid) + } else { + user.Username = u + } } - fields := strings.Split(c.Config.User, ":") - var u string - u = fields[0] - user := specs.User{} - if uid, err := strconv.Atoi(u); err == nil { - user.UID = uint32(uid) - } else { - user.Username = u + for _, group := range c.HostConfig.GroupAdd { + gid, err := strconv.ParseUint(group, 10, 32) + if err != nil { + logrus.Errorf("failed to parse supplemental group id %s: %v", group, err) + continue + } + user.AdditionalGids = append(user.AdditionalGids, uint32(gid)) } + spec.s.Process.User = user return nil diff --git a/hack/cri-test/test-cri.sh b/hack/cri-test/test-cri.sh index 68bb838557..5aff8318b3 100755 --- a/hack/cri-test/test-cri.sh +++ b/hack/cri-test/test-cri.sh @@ -23,10 +23,10 @@ POUCH_SOCK="/var/run/pouchcri.sock" # CRI_FOCUS focuses the test to run. # With the CRI manager completes its function, we may need to expand this field. -CRI_FOCUS=${CRI_FOCUS:-"PodSandbox|AppArmor|Privileged is true|basic operations on container|Runtime info|mount propagation|volume and device|RunAsUser|container port"} +CRI_FOCUS=${CRI_FOCUS:-"PodSandbox|AppArmor|Privileged is true|basic operations on container|Runtime info|mount propagation|volume and device|RunAsUser|container port|NamespaceOption|SupplementalGroups"} # CRI_SKIP skips the test to skip. -CRI_SKIP=${CRI_SKIP:-"RunAsUserName"} +CRI_SKIP=${CRI_SKIP:-"RunAsUserName|HostNetwork"} # REPORT_DIR is the the directory to store test logs. REPORT_DIR=${REPORT_DIR:-"/tmp/test-cri"}