diff --git a/hack/test.sh b/hack/test.sh index 68348c0a..4a637295 100755 --- a/hack/test.sh +++ b/hack/test.sh @@ -3,6 +3,21 @@ set -eu -o pipefail -x cd $(dirname $0)/.. +ref=rootless-containers/rootlesskit:test # build -q but keep printing something (....) so as to avoid Travis timeout -(while sleep 60; do echo -n .; done) & docker build -q -t rootless-containers/rootlesskit:test -f ./hack/test/Dockerfile . ; kill %1 -docker run -it --rm --privileged rootless-containers/rootlesskit:test +(while sleep 60; do echo -n .; done) & docker build -q -t ${ref} -f ./hack/test/Dockerfile . ; kill %1 + +# TODO: add `--security-opt unconfined-paths=/sys` when https://github.com/docker/cli/pull/1347 gets merged. +# See https://github.com/rootless-containers/rootlesskit/pull/23 for the context. +# +# NOTE: bind-mounting /dev/net/tun is needed because we cannot mknod the tun device, at least with kernel < 4.18 +# +# FIXME: --privilege is needed because the test suite compares rootlesskit with rootful `ip netns`. +# TODO: split the rootful test to separate container and remove --privilege +docker run \ + --rm \ + --security-opt seccomp=unconfined \ + --security-opt apparmor=unconfined \ + -v /dev/net/tun:/dev/net/tun \ + --privileged \ + ${ref} diff --git a/hack/test/Dockerfile b/hack/test/Dockerfile index 7ce58e5c..280931fb 100644 --- a/hack/test/Dockerfile +++ b/hack/test/Dockerfile @@ -1,19 +1,29 @@ -FROM golang:1.10 AS rootlesskit +FROM golang:1.11 AS rootlesskit ADD . /go/src/github.com/rootless-containers/rootlesskit RUN go build -o /rootlesskit github.com/rootless-containers/rootlesskit/cmd/rootlesskit -FROM ubuntu as build-c +FROM ubuntu:18.04 as build-c RUN apt update && apt install -y git make gcc automake autotools-dev libtool +# As of October 2018, the upstream shadow newuidmap/newgidmap requires `docker run --cap-add sys_admin`. +# We can use https://github.com/shadow-maint/shadow/pull/132 for eliminating unneeded dependency on CAP_SYS_ADMIN. +FROM build-c as idmap +RUN apt update && apt install -y byacc gettext autopoint libcap-dev libxslt1-dev +RUN ( git clone -b no-cap-sys-admin https://github.com/giuseppe/shadow.git /shadow && cd /shadow ) +WORKDIR /shadow +RUN ./autogen.sh --disable-nls --disable-man --without-audit --without-selinux --without-acl --without-attr --without-tcb --without-nscd \ +&& make \ +&& cp src/newuidmap src/newgidmap /usr/bin + FROM build-c AS slirp4netns -ARG SLIRP4NETNS_COMMIT=0037042e2facc8818d0a254e320a739648283f2e +ARG SLIRP4NETNS_COMMIT=39e87c0e66345edf7fd6e0bd1f61aa842617e757 RUN git clone https://github.com/rootless-containers/slirp4netns.git /slirp4netns && \ cd /slirp4netns && git checkout ${SLIRP4NETNS_COMMIT} && \ ./autogen.sh && ./configure && make -FROM ubuntu AS vpnkit +FROM ubuntu:18.04 AS vpnkit RUN apt update && apt install -y ocaml opam -ARG VPNKIT_COMMIT=bbedcf42d9e6ba0afba65aca18dd5fc087192b13 +ARG VPNKIT_COMMIT=7d18ec48982340f49461379d718a4538f3090e13 RUN git clone https://github.com/moby/vpnkit.git /vpnkit && \ cd /vpnkit && git checkout ${VPNKIT_COMMIT} && \ make @@ -36,24 +46,33 @@ RUN git clone https://github.com/rd235/vdeplug_slirp.git /vdeplug_slirp && \ cd /vdeplug_slirp && git checkout ${VDEPLUGSLIRP_COMMIT} && \ autoreconf -if && ./configure && make && make install -FROM ubuntu AS test -# busybox is only for debugging purpose -# sudo is only for rootful veth benchmark (for comparison) -RUN apt update && apt install -y iperf3 uidmap iproute2 busybox sudo -RUN useradd --create-home --home-dir /home/user --uid 1000 user \ +FROM ubuntu:18.04 AS test-base +# iproute2: for `ip` command that rootlesskit needs to exec +# iperf3: only for benchmark purpose +# busybox: only for debugging purpose +# sudo: only for rootful veth benchmark (for comparison) +RUN apt update && apt install -y iproute2 iperf3 busybox sudo +COPY --from=idmap /usr/bin/newuidmap /usr/bin/newuidmap +COPY --from=idmap /usr/bin/newgidmap /usr/bin/newgidmap +COPY --from=rootlesskit /rootlesskit /home/user/bin/ +RUN chmod u+s /usr/bin/newuidmap /usr/bin/newgidmap \ + && useradd --create-home --home-dir /home/user --uid 1000 user \ && mkdir -p /run/user/1000 \ + && chown -R user:user /run/user/1000 /home/user \ && echo "user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/user -COPY --from=rootlesskit /rootlesskit /home/user/bin/ -COPY --from=slirp4netns /slirp4netns/slirp4netns /home/user/bin/ -COPY --from=vpnkit /vpnkit/vpnkit.exe /home/user/bin/vpnkit -COPY --from=vdeplug_slirp /usr/local/bin/* /home/user/bin/ -COPY --from=vdeplug_slirp /usr/local/lib/* /home/user/lib/ -ADD ./hack/test/docker-entrypoint.sh /home/user/bin/ -RUN chown -R user:user /run/user/1000 /home/user USER user ENV HOME /home/user ENV USER user ENV XDG_RUNTIME_DIR=/run/user/1000 ENV PATH /home/user/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ENV LD_LIBRARY_PATH=/home/user/lib + +FROM test-base AS test-light +COPY --from=slirp4netns /slirp4netns/slirp4netns /home/user/bin/ + +FROM test-light AS test-full +COPY --from=vpnkit /vpnkit/vpnkit.exe /home/user/bin/vpnkit +COPY --from=vdeplug_slirp /usr/local/bin/* /home/user/bin/ +COPY --from=vdeplug_slirp /usr/local/lib/* /home/user/lib/ +ADD ./hack/test/docker-entrypoint.sh /home/user/bin/ ENTRYPOINT ["/home/user/bin/docker-entrypoint.sh"] diff --git a/pkg/child/child.go b/pkg/child/child.go index e55c561e..4d710726 100644 --- a/pkg/child/child.go +++ b/pkg/child/child.go @@ -83,11 +83,25 @@ func mountSysfs() error { return errors.Wrap(err, "creating a directory under /tmp") } defer os.RemoveAll(tmp) - cmds := [][]string{ - {"mount", "--rbind", "/sys/fs/cgroup", tmp}, - {"mount", "-t", "sysfs", "none", "/sys"}, - {"mount", "-n", "--move", tmp, "/sys/fs/cgroup"}, + cmds := [][]string{{"mount", "--rbind", "/sys/fs/cgroup", tmp}} + if err := common.Execs(os.Stderr, os.Environ(), cmds); err != nil { + return errors.Wrapf(err, "executing %v", cmds) + } + cmds = [][]string{{"mount", "-t", "sysfs", "none", "/sys"}} + if err := common.Execs(os.Stderr, os.Environ(), cmds); err != nil { + // when the sysfs in the parent namespace is RO, + // we can't mount RW sysfs even in the child namespace. + // https://github.com/rootless-containers/rootlesskit/pull/23#issuecomment-429292632 + // https://github.com/torvalds/linux/blob/9f203e2f2f065cd74553e6474f0ae3675f39fb0f/fs/namespace.c#L3326-L3328 + cmdsRo := [][]string{{"mount", "-t", "sysfs", "-o", "ro", "none", "/sys"}} + logrus.Warnf("failed to mount sysfs (%v), falling back to read-only mount (%v): %v", + cmds, cmdsRo, err) + if err := common.Execs(os.Stderr, os.Environ(), cmdsRo); err != nil { + // when /sys/firmware is masked, even RO sysfs can't be mounted + logrus.Warnf("failed to mount sysfs (%v): %v", cmdsRo, err) + } } + cmds = [][]string{{"mount", "-n", "--move", tmp, "/sys/fs/cgroup"}} if err := common.Execs(os.Stderr, os.Environ(), cmds); err != nil { return errors.Wrapf(err, "executing %v", cmds) }