Skip to content

Commit b449440

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]> (cherry picked from commit 66307d0) Signed-off-by: Danny Canter <[email protected]>
1 parent fe457eb commit b449440

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
@@ -252,7 +252,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
252252
}
253253
}()
254254

255-
specOpts, err := c.containerSpecOpts(config, &image.ImageSpec.Config)
255+
specOpts, err := c.platformSpecOpts(platform, config, &image.ImageSpec.Config)
256256
if err != nil {
257257
return nil, fmt.Errorf("failed to get container spec opts: %w", err)
258258
}
@@ -426,6 +426,93 @@ const (
426426
hostnameEnv = "HOSTNAME"
427427
)
428428

429+
// generateUserString generates valid user string based on OCI Image Spec
430+
// v1.0.0.
431+
//
432+
// CRI defines that the following combinations are valid:
433+
//
434+
// (none) -> ""
435+
// username -> username
436+
// username, uid -> username
437+
// username, uid, gid -> username:gid
438+
// username, gid -> username:gid
439+
// uid -> uid
440+
// uid, gid -> uid:gid
441+
// gid -> error
442+
//
443+
// TODO(random-liu): Add group name support in CRI.
444+
func generateUserString(username string, uid, gid *runtime.Int64Value) (string, error) {
445+
var userstr, groupstr string
446+
if uid != nil {
447+
userstr = strconv.FormatInt(uid.GetValue(), 10)
448+
}
449+
if username != "" {
450+
userstr = username
451+
}
452+
if gid != nil {
453+
groupstr = strconv.FormatInt(gid.GetValue(), 10)
454+
}
455+
if userstr == "" {
456+
if groupstr != "" {
457+
return "", fmt.Errorf("user group %q is specified without user", groupstr)
458+
}
459+
return "", nil
460+
}
461+
if groupstr != "" {
462+
userstr = userstr + ":" + groupstr
463+
}
464+
return userstr, nil
465+
}
466+
467+
// platformSpecOpts adds additional runtime spec options that may rely on
468+
// runtime information (rootfs mounted), or platform specific checks with
469+
// no defined workaround (yet) to specify for other platforms.
470+
func (c *criService) platformSpecOpts(
471+
platform platforms.Platform,
472+
config *runtime.ContainerConfig,
473+
imageConfig *imagespec.ImageConfig,
474+
) ([]oci.SpecOpts, error) {
475+
var specOpts []oci.SpecOpts
476+
477+
// First deal with the set of options we can use across platforms currently.
478+
// Linux user strings have workarounds on other platforms to avoid needing to
479+
// mount the rootfs, but on Linux hosts it must be mounted
480+
//
481+
// TODO(dcantah): I think the seccomp package can be made to compile on
482+
// !linux and used here as well.
483+
if platform.OS == "linux" {
484+
// Set container username. This could only be done by containerd, because it needs
485+
// access to the container rootfs. Pass user name to containerd, and let it overwrite
486+
// the spec for us.
487+
securityContext := config.GetLinux().GetSecurityContext()
488+
userstr, err := generateUserString(
489+
securityContext.GetRunAsUsername(),
490+
securityContext.GetRunAsUser(),
491+
securityContext.GetRunAsGroup())
492+
if err != nil {
493+
return nil, fmt.Errorf("failed to generate user string: %w", err)
494+
}
495+
if userstr == "" {
496+
// Lastly, since no user override was passed via CRI try to set via OCI
497+
// Image
498+
userstr = imageConfig.User
499+
}
500+
if userstr != "" {
501+
specOpts = append(specOpts, oci.WithUser(userstr))
502+
}
503+
}
504+
505+
// Now grab the truly platform specific options (seccomp, apparmor etc. for linux
506+
// for example).
507+
ctrSpecOpts, err := c.containerSpecOpts(config, imageConfig)
508+
if err != nil {
509+
return nil, err
510+
}
511+
specOpts = append(specOpts, ctrSpecOpts...)
512+
513+
return specOpts, nil
514+
}
515+
429516
// buildContainerSpec build container's OCI spec depending on controller's target platform OS.
430517
func (c *criService) buildContainerSpec(
431518
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"
@@ -1398,7 +1399,7 @@ additional-group-for-root:x:22222:root
13981399
require.NoError(t, err)
13991400

14001401
spec.Root.Path = tempRootDir // simulating /etc/{passwd, group}
1401-
opts, err := c.containerSpecOpts(containerConfig, imageConfig)
1402+
opts, err := c.platformSpecOpts(platforms.DefaultSpec(), containerConfig, imageConfig)
14021403
require.NoError(t, err)
14031404
oci.ApplyOpts(ctx, nil, testContainer, spec, opts...)
14041405

0 commit comments

Comments
 (0)