Skip to content

Commit 5bf7052

Browse files
committed
add WithAppendAdditionalGroups helper
Signed-off-by: Ye Sijun <[email protected]>
1 parent 16992a4 commit 5bf7052

2 files changed

Lines changed: 134 additions & 1 deletion

File tree

oci/spec_opts.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,63 @@ func WithAdditionalGIDs(userstr string) SpecOpts {
875875
}
876876
}
877877

878+
// WithAppendAdditionalGroups append additional groups within the container.
879+
// The passed in groups can be either a gid or a groupname.
880+
func WithAppendAdditionalGroups(groups ...string) SpecOpts {
881+
return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
882+
// For LCOW or on Darwin additional GID's are not supported
883+
if s.Windows != nil || runtime.GOOS == "darwin" {
884+
return nil
885+
}
886+
setProcess(s)
887+
setAdditionalGids := func(root string) error {
888+
gpath, err := fs.RootPath(root, "/etc/group")
889+
if err != nil {
890+
return err
891+
}
892+
ugroups, err := user.ParseGroupFile(gpath)
893+
if err != nil {
894+
return err
895+
}
896+
groupMap := make(map[string]user.Group)
897+
for _, group := range ugroups {
898+
groupMap[group.Name] = group
899+
groupMap[strconv.Itoa(group.Gid)] = group
900+
}
901+
var gids []uint32
902+
for _, group := range groups {
903+
g, ok := groupMap[group]
904+
if !ok {
905+
return fmt.Errorf("unable to find group %s", group)
906+
}
907+
gids = append(gids, uint32(g.Gid))
908+
}
909+
s.Process.User.AdditionalGids = append(s.Process.User.AdditionalGids, gids...)
910+
return nil
911+
}
912+
if c.Snapshotter == "" && c.SnapshotKey == "" {
913+
if !filepath.IsAbs(s.Root.Path) {
914+
return errors.New("rootfs absolute path is required")
915+
}
916+
return setAdditionalGids(s.Root.Path)
917+
}
918+
if c.Snapshotter == "" {
919+
return errors.New("no snapshotter set for container")
920+
}
921+
if c.SnapshotKey == "" {
922+
return errors.New("rootfs snapshot not created for container")
923+
}
924+
snapshotter := client.SnapshotService(c.Snapshotter)
925+
mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
926+
if err != nil {
927+
return err
928+
}
929+
930+
mounts = tryReadonlyMounts(mounts)
931+
return mount.WithTempMount(ctx, mounts, setAdditionalGids)
932+
}
933+
}
934+
878935
// WithCapabilities sets Linux capabilities on the process
879936
func WithCapabilities(caps []string) SpecOpts {
880937
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
@@ -979,7 +1036,7 @@ func UserFromPath(root string, filter func(user.User) bool) (user.User, error) {
9791036
// ErrNoGroupsFound can be returned from GIDFromPath
9801037
var ErrNoGroupsFound = errors.New("no groups found")
9811038

982-
// GIDFromPath inspects the GID using /etc/passwd in the specified rootfs.
1039+
// GIDFromPath inspects the GID using /etc/group in the specified rootfs.
9831040
// filter can be nil.
9841041
func GIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) {
9851042
gpath, err := fs.RootPath(root, "/etc/group")

oci/spec_opts_linux_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ import (
2222
"path/filepath"
2323
"testing"
2424

25+
"github.com/containerd/containerd/containers"
2526
"github.com/containerd/containerd/pkg/testutil"
27+
"github.com/containerd/continuity/fs/fstest"
2628
specs "github.com/opencontainers/runtime-spec/specs-go"
29+
"github.com/stretchr/testify/assert"
2730
"golang.org/x/sys/unix"
2831
)
2932

@@ -247,3 +250,76 @@ func TestGetDevices(t *testing.T) {
247250
})
248251
})
249252
}
253+
254+
func TestWithAppendAdditionalGroups(t *testing.T) {
255+
t.Parallel()
256+
expectedContent := `root:x:0:root
257+
bin:x:1:root,bin,daemon
258+
daemon:x:2:root,bin,daemon
259+
`
260+
td := t.TempDir()
261+
apply := fstest.Apply(
262+
fstest.CreateDir("/etc", 0777),
263+
fstest.CreateFile("/etc/group", []byte(expectedContent), 0777),
264+
)
265+
if err := apply.Apply(td); err != nil {
266+
t.Fatalf("failed to apply: %v", err)
267+
}
268+
c := containers.Container{ID: t.Name()}
269+
270+
testCases := []struct {
271+
name string
272+
additionalGIDs []uint32
273+
groups []string
274+
expected []uint32
275+
err string
276+
}{
277+
{
278+
name: "no additional gids",
279+
groups: []string{},
280+
},
281+
{
282+
name: "no additional gids, append root gid",
283+
groups: []string{"root"},
284+
expected: []uint32{0},
285+
},
286+
{
287+
name: "no additional gids, append bin and daemon gids",
288+
groups: []string{"bin", "daemon"},
289+
expected: []uint32{1, 2},
290+
},
291+
{
292+
name: "has root additional gids, append bin and daemon gids",
293+
additionalGIDs: []uint32{0},
294+
groups: []string{"bin", "daemon"},
295+
expected: []uint32{0, 1, 2},
296+
},
297+
{
298+
name: "unknown group",
299+
groups: []string{"unknown"},
300+
err: "unable to find group unknown",
301+
},
302+
}
303+
304+
for _, testCase := range testCases {
305+
t.Run(testCase.name, func(t *testing.T) {
306+
t.Parallel()
307+
s := Spec{
308+
Version: specs.Version,
309+
Root: &specs.Root{
310+
Path: td,
311+
},
312+
Process: &specs.Process{
313+
User: specs.User{
314+
AdditionalGids: testCase.additionalGIDs,
315+
},
316+
},
317+
}
318+
err := WithAppendAdditionalGroups(testCase.groups...)(context.Background(), nil, &c, &s)
319+
if err != nil {
320+
assert.EqualError(t, err, testCase.err)
321+
}
322+
assert.Equal(t, testCase.expected, s.Process.User.AdditionalGids)
323+
})
324+
}
325+
}

0 commit comments

Comments
 (0)