Skip to content

Commit 0a06c28

Browse files
junnplusAkihiroSuda
authored andcommitted
add WithAppendAdditionalGroups helper
Signed-off-by: Ye Sijun <[email protected]> (cherry picked from commit 5bf7052) Signed-off-by: Akihiro Suda <[email protected]>
1 parent 839086b commit 0a06c28

File tree

2 files changed

+134
-1
lines changed

2 files changed

+134
-1
lines changed

oci/spec_opts.go

+58-1
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,63 @@ func WithAdditionalGIDs(userstr string) SpecOpts {
803803
}
804804
}
805805

806+
// WithAppendAdditionalGroups append additional groups within the container.
807+
// The passed in groups can be either a gid or a groupname.
808+
func WithAppendAdditionalGroups(groups ...string) SpecOpts {
809+
return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
810+
// For LCOW or on Darwin additional GID's are not supported
811+
if s.Windows != nil || runtime.GOOS == "darwin" {
812+
return nil
813+
}
814+
setProcess(s)
815+
setAdditionalGids := func(root string) error {
816+
gpath, err := fs.RootPath(root, "/etc/group")
817+
if err != nil {
818+
return err
819+
}
820+
ugroups, err := user.ParseGroupFile(gpath)
821+
if err != nil {
822+
return err
823+
}
824+
groupMap := make(map[string]user.Group)
825+
for _, group := range ugroups {
826+
groupMap[group.Name] = group
827+
groupMap[strconv.Itoa(group.Gid)] = group
828+
}
829+
var gids []uint32
830+
for _, group := range groups {
831+
g, ok := groupMap[group]
832+
if !ok {
833+
return fmt.Errorf("unable to find group %s", group)
834+
}
835+
gids = append(gids, uint32(g.Gid))
836+
}
837+
s.Process.User.AdditionalGids = append(s.Process.User.AdditionalGids, gids...)
838+
return nil
839+
}
840+
if c.Snapshotter == "" && c.SnapshotKey == "" {
841+
if !filepath.IsAbs(s.Root.Path) {
842+
return errors.New("rootfs absolute path is required")
843+
}
844+
return setAdditionalGids(s.Root.Path)
845+
}
846+
if c.Snapshotter == "" {
847+
return errors.New("no snapshotter set for container")
848+
}
849+
if c.SnapshotKey == "" {
850+
return errors.New("rootfs snapshot not created for container")
851+
}
852+
snapshotter := client.SnapshotService(c.Snapshotter)
853+
mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
854+
if err != nil {
855+
return err
856+
}
857+
858+
mounts = tryReadonlyMounts(mounts)
859+
return mount.WithTempMount(ctx, mounts, setAdditionalGids)
860+
}
861+
}
862+
806863
// WithCapabilities sets Linux capabilities on the process
807864
func WithCapabilities(caps []string) SpecOpts {
808865
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
@@ -907,7 +964,7 @@ func UserFromPath(root string, filter func(user.User) bool) (user.User, error) {
907964
// ErrNoGroupsFound can be returned from GIDFromPath
908965
var ErrNoGroupsFound = errors.New("no groups found")
909966

910-
// GIDFromPath inspects the GID using /etc/passwd in the specified rootfs.
967+
// GIDFromPath inspects the GID using /etc/group in the specified rootfs.
911968
// filter can be nil.
912969
func GIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) {
913970
gpath, err := fs.RootPath(root, "/etc/group")

oci/spec_opts_linux_test.go

+76
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)