Skip to content

Commit

Permalink
Merge pull request #203 from tonistiigi/user
Browse files Browse the repository at this point in the history
Add support for setting user
  • Loading branch information
AkihiroSuda authored Dec 13, 2017
2 parents 9dee46b + 6626255 commit 571e14e
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 66 deletions.
54 changes: 54 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func TestClientIntegration(t *testing.T) {
testBuildHTTPSource,
testBuildPushAndValidate,
testResolveAndHosts,
testUser,
})
}

Expand Down Expand Up @@ -200,6 +201,59 @@ func testResolveAndHosts(t *testing.T, sb integration.Sandbox) {

}

func testUser(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
t.Parallel()
c, err := New(sb.Address())
require.NoError(t, err)
defer c.Close()

st := llb.Image("busybox:latest").Run(llb.Shlex(`sh -c "mkdir -m 0777 /wd"`))

run := func(user, cmd string) {
st = st.Run(llb.Shlex(cmd), llb.Dir("/wd"), llb.User(user))
}

run("daemon", `sh -c "id -nu > user"`)
run("daemon:daemon", `sh -c "id -ng > group"`)
run("daemon:nogroup", `sh -c "id -ng > nogroup"`)
run("1:1", `sh -c "id -g > userone"`)

st = st.Run(llb.Shlex("cp -a /wd/. /out/"))
out := st.AddMount("/out", llb.Scratch())

def, err := out.Marshal()
require.NoError(t, err)

destDir, err := ioutil.TempDir("", "buildkit")
require.NoError(t, err)
defer os.RemoveAll(destDir)

err = c.Solve(context.TODO(), def, SolveOpt{
Exporter: ExporterLocal,
ExporterAttrs: map[string]string{
"output": destDir,
},
}, nil)
require.NoError(t, err)

dt, err := ioutil.ReadFile(filepath.Join(destDir, "user"))
require.NoError(t, err)
require.Contains(t, string(dt), "daemon")

dt, err = ioutil.ReadFile(filepath.Join(destDir, "group"))
require.NoError(t, err)
require.Contains(t, string(dt), "daemon")

dt, err = ioutil.ReadFile(filepath.Join(destDir, "nogroup"))
require.NoError(t, err)
require.Contains(t, string(dt), "nogroup")

dt, err = ioutil.ReadFile(filepath.Join(destDir, "userone"))
require.NoError(t, err)
require.Contains(t, string(dt), "1")
}

func testBuildPushAndValidate(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
t.Parallel()
Expand Down
8 changes: 8 additions & 0 deletions client/llb/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Meta struct {
Args []string
Env EnvList
Cwd string
User string
}

func NewExecOp(root Output, meta Meta, readOnly bool, md OpMetadata) *ExecOp {
Expand Down Expand Up @@ -110,6 +111,7 @@ func (e *ExecOp) Marshal() ([]byte, *OpMetadata, error) {
Args: e.meta.Args,
Env: e.meta.Env.ToArray(),
Cwd: e.meta.Cwd,
User: e.meta.User,
},
}

Expand Down Expand Up @@ -271,6 +273,12 @@ func AddEnvf(key, value string, v ...interface{}) RunOption {
})
}

func User(str string) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.User(str)
})
}

func Dir(str string) RunOption {
return Dirf(str)
}
Expand Down
15 changes: 15 additions & 0 deletions client/llb/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var (
keyArgs = contextKeyT("llb.exec.args")
keyDir = contextKeyT("llb.exec.dir")
keyEnv = contextKeyT("llb.exec.env")
keyUser = contextKeyT("llb.exec.user")
)

func addEnv(key, value string) StateOption {
Expand Down Expand Up @@ -42,6 +43,12 @@ func dirf(str string, v ...interface{}) StateOption {
}
}

func user(str string) StateOption {
return func(s State) State {
return s.WithValue(keyUser, str)
}
}

func reset(s_ State) StateOption {
return func(s State) State {
s = NewState(s.Output())
Expand Down Expand Up @@ -74,6 +81,14 @@ func getArgs(s State) []string {
return nil
}

func getUser(s State) string {
v := s.Value(keyUser)
if v != nil {
return v.(string)
}
return ""
}

func args(args ...string) StateOption {
return func(s State) State {
return s.WithValue(keyArgs, args)
Expand Down
5 changes: 5 additions & 0 deletions client/llb/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func (s State) Run(ro ...RunOption) ExecState {
Args: getArgs(ei.State),
Cwd: getDir(ei.State),
Env: getEnv(ei.State),
User: getUser(ei.State),
}

exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Metadata())
Expand Down Expand Up @@ -173,6 +174,10 @@ func (s State) Reset(s2 State) State {
return reset(s2)(s)
}

func (s State) User(v string) State {
return user(v)(s)
}

func (s State) With(so ...StateOption) State {
for _, o := range so {
s = o(s)
Expand Down
23 changes: 20 additions & 3 deletions executor/containerdexecutor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (

"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
containerdoci "github.com/containerd/containerd/oci"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/executor/oci"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/snapshot"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
Expand Down Expand Up @@ -40,16 +42,31 @@ func (w containerdExecutor) Exec(ctx context.Context, meta executor.Meta, root c
return err
}

spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile)
rootMounts, err := root.Mount(ctx, false)
if err != nil {
return err
}
defer cleanup()

rootMounts, err := root.Mount(ctx, false)
uid, gid, err := oci.ParseUser(meta.User)
if err != nil {
lm := snapshot.LocalMounter(rootMounts)
rootfsPath, err := lm.Mount()
if err != nil {
return err
}
uid, gid, err = oci.GetUser(ctx, rootfsPath, meta.User)
if err != nil {
lm.Unmount()
return err
}
lm.Unmount()
}

spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, containerdoci.WithUIDGID(uid, gid))
if err != nil {
return err
}
defer cleanup()

container, err := w.client.NewContainer(ctx, id,
containerd.WithSpec(spec),
Expand Down
11 changes: 7 additions & 4 deletions executor/oci/spec_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,24 @@ import (
// Ideally we don't have to import whole containerd just for the default spec

// GenerateSpec generates spec using containerd functionality.
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id string, resolvConf, hostsFile string) (*specs.Spec, func(), error) {
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
c := &containers.Container{
ID: id,
}
_, ok := namespaces.Namespace(ctx)
if !ok {
ctx = namespaces.WithNamespace(ctx, "buildkit")
}
// Note that containerd.GenerateSpec is namespaced so as to make
// specs.Linux.CgroupsPath namespaced
s, err := oci.GenerateSpec(ctx, nil, c,

opts = append(opts,
oci.WithHostNamespace(specs.NetworkNamespace),
withROBind(resolvConf, "/etc/resolv.conf"),
withROBind(hostsFile, "/etc/hosts"),
)

// Note that containerd.GenerateSpec is namespaced so as to make
// specs.Linux.CgroupsPath namespaced
s, err := oci.GenerateSpec(ctx, nil, c, opts...)
if err != nil {
return nil, nil, err
}
Expand Down
86 changes: 86 additions & 0 deletions executor/oci/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package oci

import (
"context"
"os"
"strconv"
"strings"

"github.com/containerd/containerd/fs"
"github.com/opencontainers/runc/libcontainer/user"
)

func GetUser(ctx context.Context, root, username string) (uint32, uint32, error) {
// fast path from uid/gid
if uid, gid, err := ParseUser(username); err == nil {
return uid, gid, nil
}

passwdPath, err := user.GetPasswdPath()
if err != nil {
return 0, 0, err
}
groupPath, err := user.GetGroupPath()
if err != nil {
return 0, 0, err
}
passwdFile, err := openUserFile(root, passwdPath)
if err == nil {
defer passwdFile.Close()
}
groupFile, err := openUserFile(root, groupPath)
if err == nil {
defer groupFile.Close()
}

execUser, err := user.GetExecUser(username, nil, passwdFile, groupFile)
if err != nil {
return 0, 0, err
}

return uint32(execUser.Uid), uint32(execUser.Gid), nil
}

func ParseUser(str string) (uid uint32, gid uint32, err error) {
if str == "" {
return 0, 0, nil
}
parts := strings.SplitN(str, ":", 2)
for i, v := range parts {
switch i {
case 0:
uid, err = parseUID(v)
if err != nil {
return 0, 0, err
}
if len(parts) == 1 {
gid = uid
}
case 1:
gid, err = parseUID(v)
if err != nil {
return 0, 0, err
}
}
}
return
}

func openUserFile(root, p string) (*os.File, error) {
p, err := fs.RootPath(root, p)
if err != nil {
return nil, err
}
return os.Open(p)
}

func parseUID(str string) (uint32, error) {
if str == "root" {
return 0, nil
}
uid, err := strconv.ParseUint(str, 10, 32)
if err != nil {
return 0, err
}
return uint32(uid), nil
}
17 changes: 12 additions & 5 deletions executor/runcexecutor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"syscall"

"github.com/containerd/containerd/mount"
containerdoci "github.com/containerd/containerd/oci"
runc "github.com/containerd/go-runc"
"github.com/docker/docker/pkg/symlink"
"github.com/moby/buildkit/cache"
Expand Down Expand Up @@ -82,21 +83,27 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
if err := os.Mkdir(rootFSPath, 0700); err != nil {
return err
}
if err := mount.All(rootMount, rootFSPath); err != nil {
return err
}
defer mount.Unmount(rootFSPath, 0)

uid, gid, err := oci.GetUser(ctx, rootFSPath, meta.User)
if err != nil {
return err
}

f, err := os.Create(filepath.Join(bundle, "config.json"))
if err != nil {
return err
}
defer f.Close()
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile)
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, containerdoci.WithUIDGID(uid, gid))
if err != nil {
return err
}
defer cleanup()

if err := mount.All(rootMount, rootFSPath); err != nil {
return err
}
defer mount.Unmount(rootFSPath, 0)
spec.Root.Path = rootFSPath
if _, ok := root.(cache.ImmutableRef); ok { // TODO: pass in with mount, not ref type
spec.Root.Readonly = true
Expand Down
1 change: 1 addition & 0 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ func dispatchExpose(d *dispatchState, c *instructions.ExposeCommand, shlex *Shel
return commitToHistory(&d.image, fmt.Sprintf("EXPOSE %v", ps), false, nil)
}
func dispatchUser(d *dispatchState, c *instructions.UserCommand, commit bool) error {
d.state = d.state.User(c.User)
d.image.Config.User = c.User
if commit {
return commitToHistory(&d.image, fmt.Sprintf("USER %v", c.User), false, nil)
Expand Down
Loading

0 comments on commit 571e14e

Please sign in to comment.