@@ -443,6 +443,66 @@ func WithUsername(username string) SpecOpts {
443443 }
444444}
445445
446+ // WithAdditionalGIDs sets the OCI spec's additionalGids array to any additional groups listed
447+ // for a particular user in the /etc/groups file of the image's root filesystem
448+ func WithAdditionalGIDs (username string ) SpecOpts {
449+ return func (ctx context.Context , client Client , c * containers.Container , s * specs.Spec ) (err error ) {
450+ setProcess (s )
451+ if c .Snapshotter == "" && c .SnapshotKey == "" {
452+ if ! isRootfsAbs (s .Root .Path ) {
453+ return errors .Errorf ("rootfs absolute path is required" )
454+ }
455+ gids , err := getSupplementalGroupsFromPath (s .Root .Path , func (g user.Group ) bool {
456+ // we only want supplemental groups
457+ if g .Name == username {
458+ return false
459+ }
460+ for _ , entry := range g .List {
461+ if entry == username {
462+ return true
463+ }
464+ }
465+ return false
466+ })
467+ if err != nil {
468+ return err
469+ }
470+ s .Process .User .AdditionalGids = gids
471+ return nil
472+ }
473+ if c .Snapshotter == "" {
474+ return errors .Errorf ("no snapshotter set for container" )
475+ }
476+ if c .SnapshotKey == "" {
477+ return errors .Errorf ("rootfs snapshot not created for container" )
478+ }
479+ snapshotter := client .SnapshotService (c .Snapshotter )
480+ mounts , err := snapshotter .Mounts (ctx , c .SnapshotKey )
481+ if err != nil {
482+ return err
483+ }
484+ return mount .WithTempMount (ctx , mounts , func (root string ) error {
485+ gids , err := getSupplementalGroupsFromPath (root , func (g user.Group ) bool {
486+ // we only want supplemental groups
487+ if g .Name == username {
488+ return false
489+ }
490+ for _ , entry := range g .List {
491+ if entry == username {
492+ return true
493+ }
494+ }
495+ return false
496+ })
497+ if err != nil {
498+ return err
499+ }
500+ s .Process .User .AdditionalGids = gids
501+ return nil
502+ })
503+ }
504+ }
505+
446506// WithCapabilities sets Linux capabilities on the process
447507func WithCapabilities (caps []string ) SpecOpts {
448508 return func (_ context.Context , _ Client , _ * containers.Container , s * specs.Spec ) error {
@@ -512,6 +572,26 @@ func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err
512572 return uint32 (g .Gid ), nil
513573}
514574
575+ func getSupplementalGroupsFromPath (root string , filter func (user.Group ) bool ) ([]uint32 , error ) {
576+ gpath , err := fs .RootPath (root , "/etc/group" )
577+ if err != nil {
578+ return []uint32 {}, err
579+ }
580+ groups , err := user .ParseGroupFileFilter (gpath , filter )
581+ if err != nil {
582+ return []uint32 {}, err
583+ }
584+ if len (groups ) == 0 {
585+ // if there are no additional groups; just return an empty set
586+ return []uint32 {}, nil
587+ }
588+ addlGids := []uint32 {}
589+ for _ , grp := range groups {
590+ addlGids = append (addlGids , uint32 (grp .Gid ))
591+ }
592+ return addlGids , nil
593+ }
594+
515595func isRootfsAbs (root string ) bool {
516596 return filepath .IsAbs (root )
517597}
0 commit comments