diff --git a/cli/common_flags.go b/cli/common_flags.go index 63bb21f738..7d75316768 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 e3e5247d87..c29151c29a 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 @@ -177,6 +178,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 309ea8d486..ca91118b38 100644 --- a/daemon/mgr/cri.go +++ b/daemon/mgr/cri.go @@ -48,6 +48,9 @@ const ( // TODO: specify them in the parameters of pouchd. streamServerAddress = "" streamServerPort = "10010" + + namespaceModeHost = "host" + namespaceModeNone = "none" ) var ( diff --git a/daemon/mgr/cri_utils.go b/daemon/mgr/cri_utils.go index 23d1989dd2..048d5138c4 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 31d454fec0..bd77686cc5 100644 --- a/daemon/mgr/spec_process.go +++ b/daemon/mgr/spec_process.go @@ -63,7 +63,6 @@ func setupProcessTTY(ctx context.Context, c *ContainerMeta, spec *SpecWrapper) e } func setupProcessUser(ctx context.Context, c *ContainerMeta, spec *SpecWrapper) (err error) { - // The user option is complicated, now we only handle case "uid". // resolve uid:gid and user:group. if c.Config.User == "" { return nil @@ -84,10 +83,12 @@ func setupProcessUser(ctx context.Context, c *ContainerMeta, spec *SpecWrapper) } } + additionalGids := user.GetAdditionalGids(c.HostConfig.GroupAdd) + spec.s.Process.User = specs.User{ - UID: uid, - GID: gid, + UID: uid, + GID: gid, + AdditionalGids: additionalGids, } - return nil } diff --git a/hack/cri-test/test-cri.sh b/hack/cri-test/test-cri.sh index 2b62f656bb..3046b10724 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|Streaming"} +CRI_FOCUS=${CRI_FOCUS:-"PodSandbox|AppArmor|Privileged is true|basic operations on container|Runtime info|mount propagation|volume and device|RunAsUser|container port|Streaming|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"} diff --git a/pkg/user/user.go b/pkg/user/user.go index dcbebff10f..2796edb20c 100644 --- a/pkg/user/user.go +++ b/pkg/user/user.go @@ -119,6 +119,22 @@ func GetIntegerID(user string) (uint32, uint32) { return uint32(uid), uint32(gid) } +// GetAdditionalGids parse supplementary gids from slice groups. +func GetAdditionalGids(groups []string) []uint32 { + var additionalGids []uint32 + + // TODO: check whether group is valid and support group name format like "nobody". + for _, group := range groups { + gid, err := strconv.ParseUint(group, 10, 32) + if err != nil { + continue + } + additionalGids = append(additionalGids, uint32(gid)) + } + + return additionalGids +} + // parseID parses uid from /etc/passwd. func parseID(file, str string, parserFilter filterFunc) (uint32, error) { idInt, idErr := strconv.Atoi(str)