@@ -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.
408495func (c * criService ) buildContainerSpec (
409496 platform platforms.Platform ,
0 commit comments