Merge pull request #1425 from Random-Liu/add-with-user-id
Add WithUserID which gets uid and gid from image's /etc/passwd.
This commit is contained in:
commit
e4a77fcc0a
@ -275,12 +275,10 @@ func TestContainerAttach(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
image, err = client.GetImage(ctx, testImage)
|
||||||
image, err = client.GetImage(ctx, testImage)
|
if err != nil {
|
||||||
if err != nil {
|
t.Error(err)
|
||||||
t.Error(err)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withCat()), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withCat()), withNewSnapshot(id, image))
|
||||||
@ -382,12 +380,10 @@ func TestContainerUsername(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
image, err = client.GetImage(ctx, testImage)
|
||||||
image, err = client.GetImage(ctx, testImage)
|
if err != nil {
|
||||||
if err != nil {
|
t.Error(err)
|
||||||
t.Error(err)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
direct, err := NewDirectIO(ctx, false)
|
direct, err := NewDirectIO(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -466,12 +462,10 @@ func TestContainerAttachProcess(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
image, err = client.GetImage(ctx, testImage)
|
||||||
image, err = client.GetImage(ctx, testImage)
|
if err != nil {
|
||||||
if err != nil {
|
t.Error(err)
|
||||||
t.Error(err)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
||||||
@ -578,3 +572,78 @@ func TestContainerAttachProcess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
<-status
|
<-status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContainerUserID(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client, err := newClient(t, address)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
image Image
|
||||||
|
ctx, cancel = testContext()
|
||||||
|
id = t.Name()
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
image, err = client.GetImage(ctx, testImage)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
direct, err := NewDirectIO(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer direct.Delete()
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
buf = bytes.NewBuffer(nil)
|
||||||
|
)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
io.Copy(buf, direct.Stdout)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// adm user in the alpine image has a uid of 3 and gid of 4.
|
||||||
|
container, err := client.NewContainer(ctx, id,
|
||||||
|
withNewSnapshot(id, image),
|
||||||
|
WithNewSpec(withImageConfig(image), WithUserID(3), WithProcessArgs("sh", "-c", "echo $(id -u):$(id -g)")),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer container.Delete(ctx, WithSnapshotCleanup)
|
||||||
|
|
||||||
|
task, err := container.NewTask(ctx, direct.IOCreate)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer task.Delete(ctx)
|
||||||
|
|
||||||
|
statusC, err := task.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.Start(ctx); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
<-statusC
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
output := strings.TrimSuffix(buf.String(), "\n")
|
||||||
|
if output != "3:4" {
|
||||||
|
t.Errorf("expected uid:gid to be 3:4, but received %q", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -94,9 +94,6 @@ func WithImageConfig(i Image) SpecOpts {
|
|||||||
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
|
return fmt.Errorf("unknown image config media type %s", ic.MediaType)
|
||||||
}
|
}
|
||||||
s.Process.Env = append(s.Process.Env, config.Env...)
|
s.Process.Env = append(s.Process.Env, config.Env...)
|
||||||
var (
|
|
||||||
uid, gid uint32
|
|
||||||
)
|
|
||||||
cmd := config.Cmd
|
cmd := config.Cmd
|
||||||
s.Process.Args = append(config.Entrypoint, cmd...)
|
s.Process.Args = append(config.Entrypoint, cmd...)
|
||||||
if config.User != "" {
|
if config.User != "" {
|
||||||
@ -111,22 +108,24 @@ func WithImageConfig(i Image) SpecOpts {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
uid, gid = uint32(v), uint32(v)
|
if err := WithUserID(uint32(v))(ctx, client, c, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
case 2:
|
case 2:
|
||||||
v, err := strconv.ParseUint(parts[0], 0, 10)
|
v, err := strconv.ParseUint(parts[0], 0, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
uid = uint32(v)
|
uid := uint32(v)
|
||||||
if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
|
if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gid = uint32(v)
|
gid := uint32(v)
|
||||||
|
s.Process.User.UID, s.Process.User.GID = uid, gid
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid USER value %s", config.User)
|
return fmt.Errorf("invalid USER value %s", config.User)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.Process.User.UID, s.Process.User.GID = uid, gid
|
|
||||||
cwd := config.WorkingDir
|
cwd := config.WorkingDir
|
||||||
if cwd == "" {
|
if cwd == "" {
|
||||||
cwd = "/"
|
cwd = "/"
|
||||||
@ -287,8 +286,8 @@ func WithNamespacedCgroup() SpecOpts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithUserIDs allows the UID and GID for the Process to be set
|
// WithUidGid allows the UID and GID for the Process to be set
|
||||||
func WithUserIDs(uid, gid uint32) SpecOpts {
|
func WithUidGid(uid, gid uint32) SpecOpts {
|
||||||
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
|
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
|
||||||
s.Process.User.UID = uid
|
s.Process.User.UID = uid
|
||||||
s.Process.User.GID = gid
|
s.Process.User.GID = gid
|
||||||
@ -296,9 +295,57 @@ func WithUserIDs(uid, gid uint32) SpecOpts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithUserID sets the correct UID and GID for the container based
|
||||||
|
// on the image's /etc/passwd contents. If uid is not found in
|
||||||
|
// /etc/passwd, it sets uid but leaves gid 0, and not returns error.
|
||||||
|
func WithUserID(uid uint32) SpecOpts {
|
||||||
|
return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
|
||||||
|
if c.Snapshotter == "" {
|
||||||
|
return errors.Errorf("no snapshotter set for container")
|
||||||
|
}
|
||||||
|
if c.RootFS == "" {
|
||||||
|
return errors.Errorf("rootfs not created for container")
|
||||||
|
}
|
||||||
|
snapshotter := client.SnapshotService(c.Snapshotter)
|
||||||
|
mounts, err := snapshotter.Mounts(ctx, c.RootFS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root, err := ioutil.TempDir("", "ctd-username")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(root)
|
||||||
|
for _, m := range mounts {
|
||||||
|
if err := m.Mount(root); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer unix.Unmount(root, 0)
|
||||||
|
f, err := os.Open(filepath.Join(root, "/etc/passwd"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
|
||||||
|
return u.Uid == int(uid)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(users) == 0 {
|
||||||
|
s.Process.User.UID = uid
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
u := users[0]
|
||||||
|
s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithUsername sets the correct UID and GID for the container
|
// WithUsername sets the correct UID and GID for the container
|
||||||
// based on the the image's /etc/passwd contents.
|
// based on the the image's /etc/passwd contents. If the username
|
||||||
// id is the snapshot id that is used
|
// is not found in /etc/passwd, it returns error.
|
||||||
func WithUsername(username string) SpecOpts {
|
func WithUsername(username string) SpecOpts {
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
|
return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
|
||||||
if c.Snapshotter == "" {
|
if c.Snapshotter == "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user