Skip to content

Commit

Permalink
allow running without securityContext.procMount=Unmasked (BuildKit v…
Browse files Browse the repository at this point in the history
…0.4) (genuinetools#221)

* vendor BuildKit v0.4.0

Signed-off-by: Akihiro Suda <[email protected]>

* Dockerfile: remove extra runc binary

Signed-off-by: Akihiro Suda <[email protected]>

* allow running without securityContext.procMount=Unmasked

When `/proc` is not mountable, img now automatically disables process
sandbox (PID namespace isolation).

Note that this allows build containers to `kill(2)` (and potentially `ptrace(2)`
when seccomp is unavailable) the `img` process.

To run `img` in a Docker container, you no longer need to specify
`--privileged`, but you still need to specify `--security-opt seccomp=unconfined
 --security-opt apparmor=unconfined` (which are unconfined on Kubernetes by default).

Signed-off-by: Akihiro Suda <[email protected]>

* e2e: suppress log output

So as to avoid Travis quota

Signed-off-by: Akihiro Suda <[email protected]>
  • Loading branch information
AkihiroSuda authored and jessfraz committed Apr 2, 2019
1 parent 2e8ff3a commit 3d1a167
Show file tree
Hide file tree
Showing 706 changed files with 82,348 additions and 7,264 deletions.
21 changes: 7 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
ARG RUNC_VERSION=9f9c96235cc97674e935002fc3d78361b696a69e
FROM golang:1.10-alpine AS gobuild-base
FROM golang:1.11-alpine AS gobuild-base
RUN apk add --no-cache \
bash \
build-base \
Expand All @@ -9,13 +8,6 @@ RUN apk add --no-cache \
linux-headers \
make

FROM gobuild-base AS runc
ARG RUNC_VERSION
RUN git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& make static BUILDTAGS="seccomp" EXTRA_FLAGS="-buildmode pie" EXTRA_LDFLAGS="-extldflags \\\"-fno-PIC -static\\\"" \
&& mv runc /usr/bin/runc

FROM gobuild-base AS img
WORKDIR /go/src/github.com/genuinetools/img
COPY . .
Expand Down Expand Up @@ -43,18 +35,19 @@ FROM alpine:3.8 AS base
MAINTAINER Jessica Frazelle <[email protected]>
RUN apk add --no-cache git
COPY --from=img /usr/bin/img /usr/bin/img
COPY --from=runc /usr/bin/runc /usr/bin/runc
COPY --from=idmap /usr/bin/newuidmap /usr/bin/newuidmap
COPY --from=idmap /usr/bin/newgidmap /usr/bin/newgidmap
RUN chmod u+s /usr/bin/newuidmap /usr/bin/newgidmap \
&& adduser -D -u 1000 user \
&& mkdir -p /run/user/1000 \
&& chown -R user /run/user/1000 /home/user \
&& echo user:100000:65536 | tee /etc/subuid | tee /etc/subgid
# As of v3.8.1, Alpine does not set SUID bit on the busybox version of /bin/su.
# However, future version may set SUID bit on /bin/su.
# We lock the root account so as to disable su completely.
RUN passwd -l root
# In previous version of `alpine:3.8`, the root was not locked and su-able
# without any password when SUID bit is set on `/bin/su`.
#
# As of 3/15/2019, the root is locked by default, but we expliciltly lock the
# root just in case.
RUN passwd -l root || true

FROM base AS debug
RUN apk add --no-cache bash strace
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ include basic.mk
prebuild: runc

RUNCBUILDDIR=$(BUILDDIR)/src/github.com/opencontainers/runc
RUNCCOMMIT=7cb3cde1f49eae53fb8fff5012c0750a64eb928b
$(RUNCBUILDDIR):
git clone --depth 1 https://github.com/opencontainers/runc.git "$@"
git clone https://github.com/opencontainers/runc.git "$@"
( cd "$@" ; git checkout $(RUNCCOMMIT) )

$(RUNCBUILDDIR)/runc: $(RUNCBUILDDIR)
GOPATH=$(BUILDDIR) $(MAKE) -C "$(RUNCBUILDDIR)" static BUILDTAGS="seccomp apparmor"
Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,32 @@ $ sudo emerge -a app-emulation/img

#### Running with Docker

This currently **does not work** without `--privileged`, to track the progress of making this work in a container without the `--privileged` flag see [upstream patches](#upstream-patches).
Docker image `r.j3ss.co/img` is configured to be executed as an unprivileged user with UID 1000 and it does not need `--privileged` since `img` v0.6.0.

```console
$ docker run --rm -it \
--name img \
--volume $(pwd):/home/user/src:ro \ # for the build context and dockerfile, can be read-only since we won't modify it
--workdir /home/user/src \ # set the builder working directory
--volume "${HOME}/.docker:/root/.docker:ro" \ # for credentials to push to docker hub or a registry
--privileged \
--security-opt seccomp=unconfined --security-opt apparmor=unconfined \ # required by runc
r.j3ss.co/img build -t user/myimage .
```

To enable PID namespace isolation (which disallows build containers to `kill(2)` the `img` process), you need to specify
`--privileged` so that build containers can mount `/proc` with unshared PID namespaces.
Note that even with `--privileged`, `img` works as an unprivileged user with UID 1000.

See [docker/cli patch](#upstream-patches) for how to allow mounting `/proc` without `--privileged`.

### Running with Kubernetes

Since `img` v0.6.0, you don't need to specify any `securityContext` for running `img` as a Kubernetes container.

However, to enable PID namespace isolation, you need to set `securityContext.procMount` to `Unmasked` (or simply set
`securityContext.privileged` to `true`).
`securityContext.procMount` is available since Kubernetes 1.12 with Docker 18.06/containerd 1.2/CRI-O 1.12.

## Usage

Make sure you have user namespace support enabled. On some distros (Debian and
Expand Down Expand Up @@ -608,4 +622,4 @@ be unprivileged.

A lot of this is based on the work of [moby/buildkit](https://github.com/moby/buildkit).
Thanks [@tonistiigi](https://github.com/tonistiigi) and
[@AkihiroSuda](https://github.com/AkihiroSuda)!
[@AkihiroSuda](https://github.com/AkihiroSuda)!
22 changes: 12 additions & 10 deletions client/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (

// Pull retrieves an image from a remote registry.
func (c *Client) Pull(ctx context.Context, image string) (*ListedImage, error) {
sm, err := c.getSessionManager()
if err != nil {
return nil, err
}
// Parse the image name and tag.
named, err := reference.ParseNormalizedNamed(image)
if err != nil {
Expand Down Expand Up @@ -46,18 +50,17 @@ func (c *Client) Pull(ctx context.Context, image string) (*ListedImage, error) {

// Create the source for the pull.
srcOpt := containerimage.SourceOpt{
SessionManager: opt.SessionManager,
Snapshotter: opt.Snapshotter,
ContentStore: opt.ContentStore,
Applier: opt.Applier,
CacheAccessor: cm,
ImageStore: opt.ImageStore,
Snapshotter: opt.Snapshotter,
ContentStore: opt.ContentStore,
Applier: opt.Applier,
CacheAccessor: cm,
ImageStore: opt.ImageStore,
}
src, err := containerimage.NewSource(srcOpt)
if err != nil {
return nil, err
}
s, err := src.Resolve(ctx, identifier)
s, err := src.Resolve(ctx, identifier, sm)
if err != nil {
return nil, err
}
Expand All @@ -76,9 +79,8 @@ func (c *Client) Pull(ctx context.Context, image string) (*ListedImage, error) {
return nil, err
}
expOpt := imageexporter.Opt{
SessionManager: opt.SessionManager,
Images: opt.ImageStore,
ImageWriter: iw,
Images: opt.ImageStore,
ImageWriter: iw,
}
exp, err := imageexporter.New(expOpt)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion client/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,9 @@ func (c *Client) Push(ctx context.Context, image string, insecure bool) error {
return fmt.Errorf("getting image %q failed: %v", image, err)
}

return push.Push(ctx, opt.SessionManager, opt.ContentStore, imgObj.Target.Digest, image, insecure, opt.ResolveOptionsFunc)
sm, err := c.getSessionManager()
if err != nil {
return err
}
return push.Push(ctx, sm, opt.ContentStore, imgObj.Target.Digest, image, insecure, opt.ResolveOptionsFunc)
}
28 changes: 21 additions & 7 deletions client/workeropt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package client
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"syscall"
"time"

"github.com/containerd/containerd/content/local"
Expand All @@ -17,6 +19,7 @@ import (
"github.com/genuinetools/img/types"
"github.com/moby/buildkit/cache/metadata"
"github.com/moby/buildkit/executor"
executoroci "github.com/moby/buildkit/executor/oci"
"github.com/moby/buildkit/executor/runcexecutor"
containerdsnapshot "github.com/moby/buildkit/snapshot/containerd"
"github.com/moby/buildkit/util/network"
Expand All @@ -31,10 +34,6 @@ import (

// createWorkerOpt creates a base.WorkerOpt to be used for a new worker.
func (c *Client) createWorkerOpt(withExecutor bool) (opt base.WorkerOpt, err error) {
sm, err := c.getSessionManager()
if err != nil {
return opt, err
}
// Create the metadata store.
md, err := metadata.NewStore(filepath.Join(c.root, "metadata.db"))
if err != nil {
Expand Down Expand Up @@ -65,8 +64,9 @@ func (c *Client) createWorkerOpt(withExecutor bool) (opt base.WorkerOpt, err err
var exe executor.Executor
if withExecutor {
exeOpt := runcexecutor.Opt{
Root: filepath.Join(c.root, "executor"),
Rootless: unprivileged,
Root: filepath.Join(c.root, "executor"),
Rootless: unprivileged,
ProcessMode: processMode(),
}
exe, err = runcexecutor.New(exeOpt, network.Default())
if err != nil {
Expand Down Expand Up @@ -121,7 +121,6 @@ func (c *Client) createWorkerOpt(withExecutor bool) (opt base.WorkerOpt, err err
opt = base.WorkerOpt{
ID: id,
Labels: xlabels,
SessionManager: sm,
MetadataStore: md,
Executor: exe,
Snapshotter: containerdsnapshot.NewSnapshotter(mdb.Snapshotter(c.backend), contentStore, md, "buildkit", gc),
Expand All @@ -135,3 +134,18 @@ func (c *Client) createWorkerOpt(withExecutor bool) (opt base.WorkerOpt, err err

return opt, err
}

func processMode() executoroci.ProcessMode {
mountArgs := []string{"-t", "proc", "none", "/proc"}
cmd := exec.Command("mount", mountArgs...)
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGKILL,
Cloneflags: syscall.CLONE_NEWPID,
Unshareflags: syscall.CLONE_NEWNS,
}
if b, err := cmd.CombinedOutput(); err != nil {
logrus.Warnf("Process sandbox is not available, consider unmasking procfs: %v", string(b))
return executoroci.NoProcessSandbox
}
return executoroci.ProcessSandbox
}
12 changes: 11 additions & 1 deletion contrib/e2e-dockerfiles-build-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ build_and_push(){

echo "[In Container] Building ${REPO_URL}/${base}:${suite} for context ${build_dir}"
# Do the same but in a docker container.
docker run --rm --name img --volume $(pwd):/home/user/src:ro --workdir /home/user/src --privileged r.j3ss.co/img build -t ${REPO_URL}/${base}:${suite} ${build_dir}
name=${REPO_URL}-${base}-${suite}
set +e
docker run --name $name --volume $(pwd):/home/user/src:ro --workdir /home/user/src --privileged r.j3ss.co/img build --no-console -t ${REPO_URL}/${base}:${suite} ${build_dir} > /dev/null 2>&1
status=$?
set -e
if [[ $status != 0 ]]; then
docker logs $name
docker rm -f $name
exit $status
fi
docker rm -f $name

# on successful build, push the image
echo " --- "
Expand Down
48 changes: 19 additions & 29 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,41 @@ module github.com/genuinetools/img
replace github.com/hashicorp/go-immutable-radix => github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe

require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/Microsoft/hcsshim v0.7.4 // indirect
github.com/containerd/cgroups v0.0.0-20180917172123-5017d4e9a9cf // indirect
github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1
github.com/containerd/containerd v0.0.0-20180925133938-3bc4ba271e1c
github.com/containerd/cri v1.11.1 // indirect
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 // indirect
github.com/containerd/containerd v1.3.0-0.20190212172151-f5b0fa220df8
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd // indirect
github.com/coreos/go-systemd v0.0.0-20180828140353-eee3db372b31 // indirect
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/cyphar/filepath-securejoin v0.2.2
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f
github.com/docker/docker-ce v17.12.1-ce-rc2+incompatible // indirect
github.com/docker/cli v0.0.0-20190131223713-234462756460
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
github.com/docker/docker v0.7.3-0.20190315021241-05e7d000f2fc
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
github.com/docker/go-units v0.3.3
github.com/docker/libnetwork v0.0.0-20171208192558-822e5b59d346 // indirect
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797
github.com/genuinetools/reg v0.16.0
github.com/godbus/dbus v4.1.0+incompatible // indirect
github.com/godbus/dbus v4.1.0+incompatible
github.com/gogo/googleapis v1.1.0 // indirect
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/golang/protobuf v1.2.0
github.com/hashicorp/golang-lru v0.5.0 // indirect
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c // indirect
github.com/mitchellh/hashstructure v1.0.0 // indirect
github.com/moby/buildkit v0.3.2
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/moby/buildkit v0.4.0
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc v1.0.0-rc2.0.20181113215238-10d38b660a77
github.com/opencontainers/runtime-spec v1.0.1 // indirect
github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/selinux v1.0.0
github.com/opentracing-contrib/go-stdlib v0.0.0-20180702182724-07a764486eb1 // indirect
github.com/opentracing/opentracing-go v1.0.2 // indirect
github.com/pkg/errors v0.8.0
github.com/pkg/errors v0.8.1
github.com/seccomp/libseccomp-golang v0.9.0
github.com/sirupsen/logrus v1.0.6
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
github.com/tonistiigi/fsutil v0.0.0-20181011223333-2862f6bc5ac9 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/vishvananda/netlink v1.0.0 // indirect
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5
github.com/vishvananda/netlink v1.0.0
go.etcd.io/bbolt v1.3.1-etcd.8
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e
google.golang.org/grpc v1.15.0
)
Loading

0 comments on commit 3d1a167

Please sign in to comment.