Skip to content

Commit 66307d0

Browse files
committed
CRI: Support Linux usernames for !linux platforms
The oci.WithUser option was being applied in container_create_linux.go instead of the cross plat buildLinuxSpec method. There's been recent work to try and make every spec option that can be applied on any platform able to do so, and this falls under that. However, WithUser on linux platforms relies on the containers SnapshotKey being filled out, which means the spec option needs to be applied during container creation. To make this a little more generic, I've created a new platformSpecOpts method that handles any spec opts that rely on runtime state (rootfs mounted for example) for some platforms, or just platform options that we still don't have workarounds for to be able to specify them for other platforms (apparmor, seccomp etc.) by internally calling the already existing containerSpecOpts method. Signed-off-by: Danny Canter <[email protected]>
1 parent f84b2b4 commit 66307d0

3 files changed

Lines changed: 95 additions & 61 deletions

File tree

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)