Skip to content

Commit 8eea15f

Browse files
authored
Merge pull request #8464 from dcantah/user-nonlinux
CRI: Support Linux username for !linux platforms
2 parents e52fbfd + 66307d0 commit 8eea15f

File tree

3 files changed

+95
-61
lines changed

3 files changed

+95
-61
lines changed

pkg/cri/sbserver/container_create.go

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
251251
}
252252
}()
253253

254-
specOpts, err := c.containerSpecOpts(config, &image.ImageSpec.Config)
254+
specOpts, err := c.platformSpecOpts(platform, config, &image.ImageSpec.Config)
255255
if err != nil {
256256
return nil, fmt.Errorf("failed to get container spec opts: %w", err)
257257
}
@@ -404,6 +404,93 @@ const (
404404
hostnameEnv = "HOSTNAME"
405405
)
406406

407+
// generateUserString generates valid user string based on OCI Image Spec
408+
// v1.0.0.
409+
//
410+
// CRI defines that the following combinations are valid:
411+
//
412+
// (none) -> ""
413+
// username -> username
414+
// username, uid -> username
415+
// username, uid, gid -> username:gid
416+
// username, gid -> username:gid
417+
// uid -> uid
418+
// uid, gid -> uid:gid
419+
// gid -> error
420+
//
421+
// TODO(random-liu): Add group name support in CRI.
422+
func generateUserString(username string, uid, gid *runtime.Int64Value) (string, error) {
423+
var userstr, groupstr string
424+
if uid != nil {
425+
userstr = strconv.FormatInt(uid.GetValue(), 10)
426+
}
427+
if username != "" {
428+
userstr = username
429+
}
430+
if gid != nil {
431+
groupstr = strconv.FormatInt(gid.GetValue(), 10)
432+
}
433+
if userstr == "" {
434+
if groupstr != "" {
435+
return "", fmt.Errorf("user group %q is specified without user", groupstr)
436+
}
437+
return "", nil
438+
}
439+
if groupstr != "" {
440+
userstr = userstr + ":" + groupstr
441+
}
442+
return userstr, nil
443+
}
444+
445+
// platformSpecOpts adds additional runtime spec options that may rely on
446+
// runtime information (rootfs mounted), or platform specific checks with
447+
// no defined workaround (yet) to specify for other platforms.
448+
func (c *criService) platformSpecOpts(
449+
platform platforms.Platform,
450+
config *runtime.ContainerConfig,
451+
imageConfig *imagespec.ImageConfig,
452+
) ([]oci.SpecOpts, error) {
453+
var specOpts []oci.SpecOpts
454+
455+
// First deal with the set of options we can use across platforms currently.
456+
// Linux user strings have workarounds on other platforms to avoid needing to
457+
// mount the rootfs, but on Linux hosts it must be mounted
458+
//
459+
// TODO(dcantah): I think the seccomp package can be made to compile on
460+
// !linux and used here as well.
461+
if platform.OS == "linux" {
462+
// Set container username. This could only be done by containerd, because it needs
463+
// access to the container rootfs. Pass user name to containerd, and let it overwrite
464+
// the spec for us.
465+
securityContext := config.GetLinux().GetSecurityContext()
466+
userstr, err := generateUserString(
467+
securityContext.GetRunAsUsername(),
468+
securityContext.GetRunAsUser(),
469+
securityContext.GetRunAsGroup())
470+
if err != nil {
471+
return nil, fmt.Errorf("failed to generate user string: %w", err)
472+
}
473+
if userstr == "" {
474+
// Lastly, since no user override was passed via CRI try to set via OCI
475+
// Image
476+
userstr = imageConfig.User
477+
}
478+
if userstr != "" {
479+
specOpts = append(specOpts, oci.WithUser(userstr))
480+
}
481+
}
482+
483+
// Now grab the truly platform specific options (seccomp, apparmor etc. for linux
484+
// for example).
485+
ctrSpecOpts, err := c.containerSpecOpts(config, imageConfig)
486+
if err != nil {
487+
return nil, err
488+
}
489+
specOpts = append(specOpts, ctrSpecOpts...)
490+
491+
return specOpts, nil
492+
}
493+
407494
// buildContainerSpec build container's OCI spec depending on controller's target platform OS.
408495
func (c *criService) buildContainerSpec(
409496
platform platforms.Platform,

pkg/cri/sbserver/container_create_linux.go

Lines changed: 5 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,12 @@ const (
5252
)
5353

5454
func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
55-
var specOpts []oci.SpecOpts
55+
var (
56+
specOpts []oci.SpecOpts
57+
err error
58+
)
5659
securityContext := config.GetLinux().GetSecurityContext()
57-
// Set container username. This could only be done by containerd, because it needs
58-
// access to the container rootfs. Pass user name to containerd, and let it overwrite
59-
// the spec for us.
60-
userstr, err := generateUserString(
61-
securityContext.GetRunAsUsername(),
62-
securityContext.GetRunAsUser(),
63-
securityContext.GetRunAsGroup())
64-
if err != nil {
65-
return nil, fmt.Errorf("failed to generate user string: %w", err)
66-
}
67-
if userstr == "" {
68-
// Lastly, since no user override was passed via CRI try to set via OCI
69-
// Image
70-
userstr = imageConfig.User
71-
}
72-
if userstr != "" {
73-
specOpts = append(specOpts, oci.WithUser(userstr))
74-
}
75-
76-
userstr = "0" // runtime default
60+
userstr := "0" // runtime default
7761
if securityContext.GetRunAsUsername() != "" {
7862
userstr = securityContext.GetRunAsUsername()
7963
} else if securityContext.GetRunAsUser() != nil {
@@ -279,44 +263,6 @@ func appArmorProfileExists(profile string) (bool, error) {
279263
}
280264
}
281265

282-
// generateUserString generates valid user string based on OCI Image Spec
283-
// v1.0.0.
284-
//
285-
// CRI defines that the following combinations are valid:
286-
//
287-
// (none) -> ""
288-
// username -> username
289-
// username, uid -> username
290-
// username, uid, gid -> username:gid
291-
// username, gid -> username:gid
292-
// uid -> uid
293-
// uid, gid -> uid:gid
294-
// gid -> error
295-
//
296-
// TODO(random-liu): Add group name support in CRI.
297-
func generateUserString(username string, uid, gid *runtime.Int64Value) (string, error) {
298-
var userstr, groupstr string
299-
if uid != nil {
300-
userstr = strconv.FormatInt(uid.GetValue(), 10)
301-
}
302-
if username != "" {
303-
userstr = username
304-
}
305-
if gid != nil {
306-
groupstr = strconv.FormatInt(gid.GetValue(), 10)
307-
}
308-
if userstr == "" {
309-
if groupstr != "" {
310-
return "", fmt.Errorf("user group %q is specified without user", groupstr)
311-
}
312-
return "", nil
313-
}
314-
if groupstr != "" {
315-
userstr = userstr + ":" + groupstr
316-
}
317-
return userstr, nil
318-
}
319-
320266
// snapshotterOpts returns any Linux specific snapshotter options for the rootfs snapshot
321267
func snapshotterOpts(snapshotterName string, config *runtime.ContainerConfig) []snapshots.Opt {
322268
return []snapshots.Opt{}

pkg/cri/sbserver/container_create_linux_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/containerd/containerd/contrib/seccomp"
3232
"github.com/containerd/containerd/mount"
3333
"github.com/containerd/containerd/oci"
34+
"github.com/containerd/containerd/platforms"
3435
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
3536
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
3637
"github.com/opencontainers/selinux/go-selinux"
@@ -1506,7 +1507,7 @@ additional-group-for-root:x:22222:root
15061507
require.NoError(t, err)
15071508

15081509
spec.Root.Path = tempRootDir // simulating /etc/{passwd, group}
1509-
opts, err := c.containerSpecOpts(containerConfig, imageConfig)
1510+
opts, err := c.platformSpecOpts(platforms.DefaultSpec(), containerConfig, imageConfig)
15101511
require.NoError(t, err)
15111512
oci.ApplyOpts(ctx, nil, testContainer, spec, opts...)
15121513

0 commit comments

Comments
 (0)