From 7db7eccb7b7507b1a6ba3ef81c6f37b16149cc55 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 26 Jul 2019 16:45:31 -0700 Subject: [PATCH 1/2] Add experimental "rootless" dind variant --- 18.09-rc/dind/Dockerfile | 1 + 18.09-rc/dind/dockerd-entrypoint.sh | 61 ++++++++++++++++++++-- 18.09-rc/docker-entrypoint.sh | 17 ++++-- 18.09/dind/Dockerfile | 1 + 18.09/dind/dockerd-entrypoint.sh | 61 ++++++++++++++++++++-- 18.09/docker-entrypoint.sh | 17 ++++-- 19.03-rc/dind-rootless/Dockerfile | 81 +++++++++++++++++++++++++++++ 19.03-rc/dind/Dockerfile | 1 + 19.03-rc/dind/dockerd-entrypoint.sh | 61 ++++++++++++++++++++-- 19.03-rc/docker-entrypoint.sh | 17 ++++-- 19.03/dind-rootless/Dockerfile | 81 +++++++++++++++++++++++++++++ 19.03/dind/Dockerfile | 1 + 19.03/dind/dockerd-entrypoint.sh | 61 ++++++++++++++++++++-- 19.03/docker-entrypoint.sh | 17 ++++-- Dockerfile-dind-rootless.template | 70 +++++++++++++++++++++++++ Dockerfile-dind.template | 1 + docker-entrypoint.sh | 17 ++++-- dockerd-entrypoint.sh | 61 ++++++++++++++++++++-- generate-stackbrew-library.sh | 2 +- update.sh | 2 +- 20 files changed, 599 insertions(+), 32 deletions(-) create mode 100644 19.03-rc/dind-rootless/Dockerfile create mode 100644 19.03/dind-rootless/Dockerfile create mode 100644 Dockerfile-dind-rootless.template diff --git a/18.09-rc/dind/Dockerfile b/18.09-rc/dind/Dockerfile index f608e00b9..06dd28bac 100644 --- a/18.09-rc/dind/Dockerfile +++ b/18.09-rc/dind/Dockerfile @@ -8,6 +8,7 @@ RUN set -eux; \ e2fsprogs-extra \ iptables \ openssl \ + shadow-uidmap \ xfsprogs \ xz \ # pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation) diff --git a/18.09-rc/dind/dockerd-entrypoint.sh b/18.09-rc/dind/dockerd-entrypoint.sh index 6e6e61dad..3926eaebd 100755 --- a/18.09-rc/dind/dockerd-entrypoint.sh +++ b/18.09-rc/dind/dockerd-entrypoint.sh @@ -92,6 +92,17 @@ _tls_generate_certs() { # no arguments passed # or first arg is `-f` or `--some-option` if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + # set DOCKER_HOST to the default "--host" value (for both standard or rootless) + uid="$(id -u)" + if [ "$uid" = '0' ]; then + : "${DOCKER_HOST:=unix:///var/run/docker.sock}" + else + # if we're not root, we must be trying to run rootless + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + : "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}" + fi + export DOCKER_HOST + # add our default arguments if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \ && _tls_generate_certs "$DOCKER_TLS_CERTDIR" \ @@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then ; then # generate certs and use TLS if requested/possible (default in 19.03+) set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2376 \ --tlsverify \ --tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \ --tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \ --tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp" else # TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2375 \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp" fi fi @@ -124,7 +137,49 @@ if [ "$1" = 'dockerd' ]; then fi # explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file) - find /run /var/run -iname 'docker*.pid' -delete + find /run /var/run -iname 'docker*.pid' -delete || : + + uid="$(id -u)" + if [ "$uid" != '0' ]; then + # if we're not root, we must be trying to run rootless + if ! command -v rootlesskit > /dev/null; then + echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)" + exit 1 + fi + user="$(id -un 2>/dev/null || :)" + if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then + echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid" + exit 1 + fi + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + export XDG_RUNTIME_DIR + if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then + echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid" + exit 1 + fi + if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1" + exit 1 + fi + if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value" + exit 1 + fi + # TODO overlay support detection? + exec rootlesskit \ + --net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \ + --mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \ + --disable-host-loopback \ + --port-driver=builtin \ + --copy-up=/etc --copy-up=/run \ + ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ + sh -c ' + rm -f /run/docker /run/xtables.lock + exec "$@" --userland-proxy-path=rootlesskit-docker-proxy + ' -- "$@" + fi +elif [ "$1" = 'docker' ]; then + exec docker-entrypoint.sh "$@" fi exec "$@" diff --git a/18.09-rc/docker-entrypoint.sh b/18.09-rc/docker-entrypoint.sh index 9799f4251..d41cef8e3 100755 --- a/18.09-rc/docker-entrypoint.sh +++ b/18.09-rc/docker-entrypoint.sh @@ -19,15 +19,26 @@ _should_tls() { && [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ] } -# if DOCKER_HOST isn't set and we don't have the default unix socket, let's set DOCKER_HOST to a sane remote value -if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then +# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly +if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then + export DOCKER_HOST=unix:///var/run/docker.sock +elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then + export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" +fi + +# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value +if [ -z "${DOCKER_HOST:-}" ]; then if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then export DOCKER_HOST='tcp://docker:2376' else export DOCKER_HOST='tcp://docker:2375' fi fi -if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then +if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \ + && [ -z "${DOCKER_TLS_VERIFY:-}" ] \ + && [ -z "${DOCKER_CERT_PATH:-}" ] \ + && _should_tls \ +; then export DOCKER_TLS_VERIFY=1 export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client" fi diff --git a/18.09/dind/Dockerfile b/18.09/dind/Dockerfile index f9c4366a5..42d2d4833 100644 --- a/18.09/dind/Dockerfile +++ b/18.09/dind/Dockerfile @@ -8,6 +8,7 @@ RUN set -eux; \ e2fsprogs-extra \ iptables \ openssl \ + shadow-uidmap \ xfsprogs \ xz \ # pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation) diff --git a/18.09/dind/dockerd-entrypoint.sh b/18.09/dind/dockerd-entrypoint.sh index 6e6e61dad..3926eaebd 100755 --- a/18.09/dind/dockerd-entrypoint.sh +++ b/18.09/dind/dockerd-entrypoint.sh @@ -92,6 +92,17 @@ _tls_generate_certs() { # no arguments passed # or first arg is `-f` or `--some-option` if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + # set DOCKER_HOST to the default "--host" value (for both standard or rootless) + uid="$(id -u)" + if [ "$uid" = '0' ]; then + : "${DOCKER_HOST:=unix:///var/run/docker.sock}" + else + # if we're not root, we must be trying to run rootless + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + : "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}" + fi + export DOCKER_HOST + # add our default arguments if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \ && _tls_generate_certs "$DOCKER_TLS_CERTDIR" \ @@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then ; then # generate certs and use TLS if requested/possible (default in 19.03+) set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2376 \ --tlsverify \ --tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \ --tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \ --tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp" else # TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2375 \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp" fi fi @@ -124,7 +137,49 @@ if [ "$1" = 'dockerd' ]; then fi # explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file) - find /run /var/run -iname 'docker*.pid' -delete + find /run /var/run -iname 'docker*.pid' -delete || : + + uid="$(id -u)" + if [ "$uid" != '0' ]; then + # if we're not root, we must be trying to run rootless + if ! command -v rootlesskit > /dev/null; then + echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)" + exit 1 + fi + user="$(id -un 2>/dev/null || :)" + if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then + echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid" + exit 1 + fi + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + export XDG_RUNTIME_DIR + if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then + echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid" + exit 1 + fi + if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1" + exit 1 + fi + if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value" + exit 1 + fi + # TODO overlay support detection? + exec rootlesskit \ + --net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \ + --mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \ + --disable-host-loopback \ + --port-driver=builtin \ + --copy-up=/etc --copy-up=/run \ + ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ + sh -c ' + rm -f /run/docker /run/xtables.lock + exec "$@" --userland-proxy-path=rootlesskit-docker-proxy + ' -- "$@" + fi +elif [ "$1" = 'docker' ]; then + exec docker-entrypoint.sh "$@" fi exec "$@" diff --git a/18.09/docker-entrypoint.sh b/18.09/docker-entrypoint.sh index 9799f4251..d41cef8e3 100755 --- a/18.09/docker-entrypoint.sh +++ b/18.09/docker-entrypoint.sh @@ -19,15 +19,26 @@ _should_tls() { && [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ] } -# if DOCKER_HOST isn't set and we don't have the default unix socket, let's set DOCKER_HOST to a sane remote value -if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then +# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly +if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then + export DOCKER_HOST=unix:///var/run/docker.sock +elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then + export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" +fi + +# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value +if [ -z "${DOCKER_HOST:-}" ]; then if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then export DOCKER_HOST='tcp://docker:2376' else export DOCKER_HOST='tcp://docker:2375' fi fi -if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then +if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \ + && [ -z "${DOCKER_TLS_VERIFY:-}" ] \ + && [ -z "${DOCKER_CERT_PATH:-}" ] \ + && _should_tls \ +; then export DOCKER_TLS_VERIFY=1 export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client" fi diff --git a/19.03-rc/dind-rootless/Dockerfile b/19.03-rc/dind-rootless/Dockerfile new file mode 100644 index 000000000..9879af618 --- /dev/null +++ b/19.03-rc/dind-rootless/Dockerfile @@ -0,0 +1,81 @@ +FROM docker:19.03-rc-dind + +# busybox "ip" is insufficient: +# [rootlesskit:child ] error: executing [[ip tuntap add name tap0 mode tap] [ip link set tap0 address 02:50:00:00:00:01]]: exit status 1 +RUN apk add --no-cache iproute2 + +# "/run/user/UID" will be used by default as the value of XDG_RUNTIME_DIR +RUN mkdir /run/user && chmod 1777 /run/user + +# create a default user preconfigured for running rootless dockerd +RUN set -eux; \ + adduser -h /home/rootless -g 'Rootless' -D -u 1000 rootless; \ + echo 'rootless:100000:65536' >> /etc/subuid; \ + echo 'rootless:100000:65536' >> /etc/subgid + +RUN set -eux; \ + \ +# this "case" statement is generated via "update.sh" + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ +# amd64 + x86_64) dockerArch='x86_64' ;; \ +# arm32v6 + armhf) dockerArch='armel' ;; \ +# arm32v7 + armv7) dockerArch='armhf' ;; \ +# arm64v8 + aarch64) dockerArch='aarch64' ;; \ + *) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\ + esac; \ + \ + if ! wget -O rootless.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-rootless-extras-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-rootless-extras-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + \ + tar --extract \ + --file rootless.tgz \ + --strip-components 1 \ + --directory /usr/local/bin/ \ + 'docker-rootless-extras/vpnkit' \ + ; \ + rm rootless.tgz; \ + \ +# we download/build rootlesskit separately to get a newer release +# rootlesskit --version; \ + vpnkit --version + +# https://github.com/rootless-containers/rootlesskit/releases +ENV ROOTLESSKIT_VERSION 0.6.0 + +RUN set -eux; \ + apk add --no-cache --virtual .rootlesskit-build-deps \ + go \ + libc-dev \ + ; \ + wget -O rootlesskit.tgz "https://github.com/rootless-containers/rootlesskit/archive/v${ROOTLESSKIT_VERSION}.tar.gz"; \ + export GOPATH='/go'; mkdir "$GOPATH"; \ + mkdir -p "$GOPATH/src/github.com/rootless-containers/rootlesskit"; \ + tar --extract --file rootlesskit.tgz --directory "$GOPATH/src/github.com/rootless-containers/rootlesskit" --strip-components 1; \ + rm rootlesskit.tgz; \ + go build -o /usr/local/bin/rootlesskit github.com/rootless-containers/rootlesskit/cmd/rootlesskit; \ + go build -o /usr/local/bin/rootlesskit-docker-proxy github.com/rootless-containers/rootlesskit/cmd/rootlesskit-docker-proxy; \ + rm -rf "$GOPATH"; \ + apk del --no-network .rootlesskit-build-deps; \ + rootlesskit --version + +# pre-create a few useful directories for our rootless user +RUN set -eux; \ + mkdir -p \ + /home/rootless/.local/share/docker \ + /home/rootless/certs/ca \ + /home/rootless/certs/client \ + /home/rootless/certs/server \ + ; \ + chown -R rootless:rootless \ + /home/rootless/.local/share/docker \ + /home/rootless/certs +ENV DOCKER_TLS_CERTDIR=/home/rootless/certs +VOLUME /home/rootless/.local/share/docker +USER rootless diff --git a/19.03-rc/dind/Dockerfile b/19.03-rc/dind/Dockerfile index 0383b9625..768f8c925 100644 --- a/19.03-rc/dind/Dockerfile +++ b/19.03-rc/dind/Dockerfile @@ -8,6 +8,7 @@ RUN set -eux; \ e2fsprogs-extra \ iptables \ openssl \ + shadow-uidmap \ xfsprogs \ xz \ # pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation) diff --git a/19.03-rc/dind/dockerd-entrypoint.sh b/19.03-rc/dind/dockerd-entrypoint.sh index 6e6e61dad..3926eaebd 100755 --- a/19.03-rc/dind/dockerd-entrypoint.sh +++ b/19.03-rc/dind/dockerd-entrypoint.sh @@ -92,6 +92,17 @@ _tls_generate_certs() { # no arguments passed # or first arg is `-f` or `--some-option` if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + # set DOCKER_HOST to the default "--host" value (for both standard or rootless) + uid="$(id -u)" + if [ "$uid" = '0' ]; then + : "${DOCKER_HOST:=unix:///var/run/docker.sock}" + else + # if we're not root, we must be trying to run rootless + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + : "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}" + fi + export DOCKER_HOST + # add our default arguments if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \ && _tls_generate_certs "$DOCKER_TLS_CERTDIR" \ @@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then ; then # generate certs and use TLS if requested/possible (default in 19.03+) set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2376 \ --tlsverify \ --tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \ --tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \ --tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp" else # TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2375 \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp" fi fi @@ -124,7 +137,49 @@ if [ "$1" = 'dockerd' ]; then fi # explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file) - find /run /var/run -iname 'docker*.pid' -delete + find /run /var/run -iname 'docker*.pid' -delete || : + + uid="$(id -u)" + if [ "$uid" != '0' ]; then + # if we're not root, we must be trying to run rootless + if ! command -v rootlesskit > /dev/null; then + echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)" + exit 1 + fi + user="$(id -un 2>/dev/null || :)" + if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then + echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid" + exit 1 + fi + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + export XDG_RUNTIME_DIR + if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then + echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid" + exit 1 + fi + if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1" + exit 1 + fi + if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value" + exit 1 + fi + # TODO overlay support detection? + exec rootlesskit \ + --net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \ + --mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \ + --disable-host-loopback \ + --port-driver=builtin \ + --copy-up=/etc --copy-up=/run \ + ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ + sh -c ' + rm -f /run/docker /run/xtables.lock + exec "$@" --userland-proxy-path=rootlesskit-docker-proxy + ' -- "$@" + fi +elif [ "$1" = 'docker' ]; then + exec docker-entrypoint.sh "$@" fi exec "$@" diff --git a/19.03-rc/docker-entrypoint.sh b/19.03-rc/docker-entrypoint.sh index 9799f4251..d41cef8e3 100755 --- a/19.03-rc/docker-entrypoint.sh +++ b/19.03-rc/docker-entrypoint.sh @@ -19,15 +19,26 @@ _should_tls() { && [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ] } -# if DOCKER_HOST isn't set and we don't have the default unix socket, let's set DOCKER_HOST to a sane remote value -if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then +# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly +if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then + export DOCKER_HOST=unix:///var/run/docker.sock +elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then + export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" +fi + +# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value +if [ -z "${DOCKER_HOST:-}" ]; then if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then export DOCKER_HOST='tcp://docker:2376' else export DOCKER_HOST='tcp://docker:2375' fi fi -if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then +if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \ + && [ -z "${DOCKER_TLS_VERIFY:-}" ] \ + && [ -z "${DOCKER_CERT_PATH:-}" ] \ + && _should_tls \ +; then export DOCKER_TLS_VERIFY=1 export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client" fi diff --git a/19.03/dind-rootless/Dockerfile b/19.03/dind-rootless/Dockerfile new file mode 100644 index 000000000..04abad186 --- /dev/null +++ b/19.03/dind-rootless/Dockerfile @@ -0,0 +1,81 @@ +FROM docker:19.03-dind + +# busybox "ip" is insufficient: +# [rootlesskit:child ] error: executing [[ip tuntap add name tap0 mode tap] [ip link set tap0 address 02:50:00:00:00:01]]: exit status 1 +RUN apk add --no-cache iproute2 + +# "/run/user/UID" will be used by default as the value of XDG_RUNTIME_DIR +RUN mkdir /run/user && chmod 1777 /run/user + +# create a default user preconfigured for running rootless dockerd +RUN set -eux; \ + adduser -h /home/rootless -g 'Rootless' -D -u 1000 rootless; \ + echo 'rootless:100000:65536' >> /etc/subuid; \ + echo 'rootless:100000:65536' >> /etc/subgid + +RUN set -eux; \ + \ +# this "case" statement is generated via "update.sh" + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ +# amd64 + x86_64) dockerArch='x86_64' ;; \ +# arm32v6 + armhf) dockerArch='armel' ;; \ +# arm32v7 + armv7) dockerArch='armhf' ;; \ +# arm64v8 + aarch64) dockerArch='aarch64' ;; \ + *) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\ + esac; \ + \ + if ! wget -O rootless.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-rootless-extras-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-rootless-extras-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + \ + tar --extract \ + --file rootless.tgz \ + --strip-components 1 \ + --directory /usr/local/bin/ \ + 'docker-rootless-extras/vpnkit' \ + ; \ + rm rootless.tgz; \ + \ +# we download/build rootlesskit separately to get a newer release +# rootlesskit --version; \ + vpnkit --version + +# https://github.com/rootless-containers/rootlesskit/releases +ENV ROOTLESSKIT_VERSION 0.6.0 + +RUN set -eux; \ + apk add --no-cache --virtual .rootlesskit-build-deps \ + go \ + libc-dev \ + ; \ + wget -O rootlesskit.tgz "https://github.com/rootless-containers/rootlesskit/archive/v${ROOTLESSKIT_VERSION}.tar.gz"; \ + export GOPATH='/go'; mkdir "$GOPATH"; \ + mkdir -p "$GOPATH/src/github.com/rootless-containers/rootlesskit"; \ + tar --extract --file rootlesskit.tgz --directory "$GOPATH/src/github.com/rootless-containers/rootlesskit" --strip-components 1; \ + rm rootlesskit.tgz; \ + go build -o /usr/local/bin/rootlesskit github.com/rootless-containers/rootlesskit/cmd/rootlesskit; \ + go build -o /usr/local/bin/rootlesskit-docker-proxy github.com/rootless-containers/rootlesskit/cmd/rootlesskit-docker-proxy; \ + rm -rf "$GOPATH"; \ + apk del --no-network .rootlesskit-build-deps; \ + rootlesskit --version + +# pre-create a few useful directories for our rootless user +RUN set -eux; \ + mkdir -p \ + /home/rootless/.local/share/docker \ + /home/rootless/certs/ca \ + /home/rootless/certs/client \ + /home/rootless/certs/server \ + ; \ + chown -R rootless:rootless \ + /home/rootless/.local/share/docker \ + /home/rootless/certs +ENV DOCKER_TLS_CERTDIR=/home/rootless/certs +VOLUME /home/rootless/.local/share/docker +USER rootless diff --git a/19.03/dind/Dockerfile b/19.03/dind/Dockerfile index d85c2df45..eec9871d5 100644 --- a/19.03/dind/Dockerfile +++ b/19.03/dind/Dockerfile @@ -8,6 +8,7 @@ RUN set -eux; \ e2fsprogs-extra \ iptables \ openssl \ + shadow-uidmap \ xfsprogs \ xz \ # pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation) diff --git a/19.03/dind/dockerd-entrypoint.sh b/19.03/dind/dockerd-entrypoint.sh index 6e6e61dad..3926eaebd 100755 --- a/19.03/dind/dockerd-entrypoint.sh +++ b/19.03/dind/dockerd-entrypoint.sh @@ -92,6 +92,17 @@ _tls_generate_certs() { # no arguments passed # or first arg is `-f` or `--some-option` if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + # set DOCKER_HOST to the default "--host" value (for both standard or rootless) + uid="$(id -u)" + if [ "$uid" = '0' ]; then + : "${DOCKER_HOST:=unix:///var/run/docker.sock}" + else + # if we're not root, we must be trying to run rootless + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + : "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}" + fi + export DOCKER_HOST + # add our default arguments if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \ && _tls_generate_certs "$DOCKER_TLS_CERTDIR" \ @@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then ; then # generate certs and use TLS if requested/possible (default in 19.03+) set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2376 \ --tlsverify \ --tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \ --tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \ --tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp" else # TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2375 \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp" fi fi @@ -124,7 +137,49 @@ if [ "$1" = 'dockerd' ]; then fi # explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file) - find /run /var/run -iname 'docker*.pid' -delete + find /run /var/run -iname 'docker*.pid' -delete || : + + uid="$(id -u)" + if [ "$uid" != '0' ]; then + # if we're not root, we must be trying to run rootless + if ! command -v rootlesskit > /dev/null; then + echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)" + exit 1 + fi + user="$(id -un 2>/dev/null || :)" + if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then + echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid" + exit 1 + fi + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + export XDG_RUNTIME_DIR + if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then + echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid" + exit 1 + fi + if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1" + exit 1 + fi + if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value" + exit 1 + fi + # TODO overlay support detection? + exec rootlesskit \ + --net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \ + --mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \ + --disable-host-loopback \ + --port-driver=builtin \ + --copy-up=/etc --copy-up=/run \ + ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ + sh -c ' + rm -f /run/docker /run/xtables.lock + exec "$@" --userland-proxy-path=rootlesskit-docker-proxy + ' -- "$@" + fi +elif [ "$1" = 'docker' ]; then + exec docker-entrypoint.sh "$@" fi exec "$@" diff --git a/19.03/docker-entrypoint.sh b/19.03/docker-entrypoint.sh index 9799f4251..d41cef8e3 100755 --- a/19.03/docker-entrypoint.sh +++ b/19.03/docker-entrypoint.sh @@ -19,15 +19,26 @@ _should_tls() { && [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ] } -# if DOCKER_HOST isn't set and we don't have the default unix socket, let's set DOCKER_HOST to a sane remote value -if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then +# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly +if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then + export DOCKER_HOST=unix:///var/run/docker.sock +elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then + export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" +fi + +# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value +if [ -z "${DOCKER_HOST:-}" ]; then if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then export DOCKER_HOST='tcp://docker:2376' else export DOCKER_HOST='tcp://docker:2375' fi fi -if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then +if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \ + && [ -z "${DOCKER_TLS_VERIFY:-}" ] \ + && [ -z "${DOCKER_CERT_PATH:-}" ] \ + && _should_tls \ +; then export DOCKER_TLS_VERIFY=1 export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client" fi diff --git a/Dockerfile-dind-rootless.template b/Dockerfile-dind-rootless.template new file mode 100644 index 000000000..6eb531e1f --- /dev/null +++ b/Dockerfile-dind-rootless.template @@ -0,0 +1,70 @@ +FROM docker:%%VERSION%%-dind + +# busybox "ip" is insufficient: +# [rootlesskit:child ] error: executing [[ip tuntap add name tap0 mode tap] [ip link set tap0 address 02:50:00:00:00:01]]: exit status 1 +RUN apk add --no-cache iproute2 + +# "/run/user/UID" will be used by default as the value of XDG_RUNTIME_DIR +RUN mkdir /run/user && chmod 1777 /run/user + +# create a default user preconfigured for running rootless dockerd +RUN set -eux; \ + adduser -h /home/rootless -g 'Rootless' -D -u 1000 rootless; \ + echo 'rootless:100000:65536' >> /etc/subuid; \ + echo 'rootless:100000:65536' >> /etc/subgid + +RUN set -eux; \ + \ +# this "case" statement is generated via "update.sh" + %%ARCH-CASE%%; \ + \ + if ! wget -O rootless.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-rootless-extras-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-rootless-extras-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + \ + tar --extract \ + --file rootless.tgz \ + --strip-components 1 \ + --directory /usr/local/bin/ \ + 'docker-rootless-extras/vpnkit' \ + ; \ + rm rootless.tgz; \ + \ +# we download/build rootlesskit separately to get a newer release +# rootlesskit --version; \ + vpnkit --version + +# https://github.com/rootless-containers/rootlesskit/releases +ENV ROOTLESSKIT_VERSION 0.6.0 + +RUN set -eux; \ + apk add --no-cache --virtual .rootlesskit-build-deps \ + go \ + libc-dev \ + ; \ + wget -O rootlesskit.tgz "https://github.com/rootless-containers/rootlesskit/archive/v${ROOTLESSKIT_VERSION}.tar.gz"; \ + export GOPATH='/go'; mkdir "$GOPATH"; \ + mkdir -p "$GOPATH/src/github.com/rootless-containers/rootlesskit"; \ + tar --extract --file rootlesskit.tgz --directory "$GOPATH/src/github.com/rootless-containers/rootlesskit" --strip-components 1; \ + rm rootlesskit.tgz; \ + go build -o /usr/local/bin/rootlesskit github.com/rootless-containers/rootlesskit/cmd/rootlesskit; \ + go build -o /usr/local/bin/rootlesskit-docker-proxy github.com/rootless-containers/rootlesskit/cmd/rootlesskit-docker-proxy; \ + rm -rf "$GOPATH"; \ + apk del --no-network .rootlesskit-build-deps; \ + rootlesskit --version + +# pre-create a few useful directories for our rootless user +RUN set -eux; \ + mkdir -p \ + /home/rootless/.local/share/docker \ + /home/rootless/certs/ca \ + /home/rootless/certs/client \ + /home/rootless/certs/server \ + ; \ + chown -R rootless:rootless \ + /home/rootless/.local/share/docker \ + /home/rootless/certs +ENV DOCKER_TLS_CERTDIR=/home/rootless/certs +VOLUME /home/rootless/.local/share/docker +USER rootless diff --git a/Dockerfile-dind.template b/Dockerfile-dind.template index f7bb3aa36..04f2d31bc 100644 --- a/Dockerfile-dind.template +++ b/Dockerfile-dind.template @@ -8,6 +8,7 @@ RUN set -eux; \ e2fsprogs-extra \ iptables \ openssl \ + shadow-uidmap \ xfsprogs \ xz \ # pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 9799f4251..d41cef8e3 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -19,15 +19,26 @@ _should_tls() { && [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ] } -# if DOCKER_HOST isn't set and we don't have the default unix socket, let's set DOCKER_HOST to a sane remote value -if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then +# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly +if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then + export DOCKER_HOST=unix:///var/run/docker.sock +elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then + export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" +fi + +# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value +if [ -z "${DOCKER_HOST:-}" ]; then if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then export DOCKER_HOST='tcp://docker:2376' else export DOCKER_HOST='tcp://docker:2375' fi fi -if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then +if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \ + && [ -z "${DOCKER_TLS_VERIFY:-}" ] \ + && [ -z "${DOCKER_CERT_PATH:-}" ] \ + && _should_tls \ +; then export DOCKER_TLS_VERIFY=1 export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client" fi diff --git a/dockerd-entrypoint.sh b/dockerd-entrypoint.sh index 6e6e61dad..3926eaebd 100755 --- a/dockerd-entrypoint.sh +++ b/dockerd-entrypoint.sh @@ -92,6 +92,17 @@ _tls_generate_certs() { # no arguments passed # or first arg is `-f` or `--some-option` if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + # set DOCKER_HOST to the default "--host" value (for both standard or rootless) + uid="$(id -u)" + if [ "$uid" = '0' ]; then + : "${DOCKER_HOST:=unix:///var/run/docker.sock}" + else + # if we're not root, we must be trying to run rootless + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + : "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}" + fi + export DOCKER_HOST + # add our default arguments if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \ && _tls_generate_certs "$DOCKER_TLS_CERTDIR" \ @@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then ; then # generate certs and use TLS if requested/possible (default in 19.03+) set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2376 \ --tlsverify \ --tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \ --tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \ --tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp" else # TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs set -- dockerd \ - --host=unix:///var/run/docker.sock \ + --host="$DOCKER_HOST" \ --host=tcp://0.0.0.0:2375 \ "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp" fi fi @@ -124,7 +137,49 @@ if [ "$1" = 'dockerd' ]; then fi # explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file) - find /run /var/run -iname 'docker*.pid' -delete + find /run /var/run -iname 'docker*.pid' -delete || : + + uid="$(id -u)" + if [ "$uid" != '0' ]; then + # if we're not root, we must be trying to run rootless + if ! command -v rootlesskit > /dev/null; then + echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)" + exit 1 + fi + user="$(id -un 2>/dev/null || :)" + if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then + echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid" + exit 1 + fi + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + export XDG_RUNTIME_DIR + if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then + echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid" + exit 1 + fi + if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1" + exit 1 + fi + if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value" + exit 1 + fi + # TODO overlay support detection? + exec rootlesskit \ + --net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \ + --mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \ + --disable-host-loopback \ + --port-driver=builtin \ + --copy-up=/etc --copy-up=/run \ + ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ + sh -c ' + rm -f /run/docker /run/xtables.lock + exec "$@" --userland-proxy-path=rootlesskit-docker-proxy + ' -- "$@" + fi +elif [ "$1" = 'docker' ]; then + exec docker-entrypoint.sh "$@" fi exec "$@" diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index 549ba8958..5a4b153f9 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -146,7 +146,7 @@ for version in "${versions[@]}"; do EOE for v in \ - dind git \ + dind dind-rootless git \ windows/windowsservercore-{ltsc2016,1709} \ ; do dir="$version/$v" diff --git a/update.sh b/update.sh index e5f51f3c1..514658dbc 100755 --- a/update.sh +++ b/update.sh @@ -107,7 +107,7 @@ for version in "${versions[@]}"; do minorVersion="${minorVersion#0}" for variant in \ - '' git dind \ + '' git dind dind-rootless \ windows/windowsservercore-{1709,ltsc2016} \ ; do dir="$version${variant:+/$variant}" From c01ffa41486f70c34f020c769bc5bddf106367ea Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Wed, 31 Jul 2019 14:44:49 -0700 Subject: [PATCH 2/2] Adjust bits per review --- 18.09-rc/Dockerfile | 3 +++ 18.09-rc/dind/dockerd-entrypoint.sh | 10 ++++------ 18.09/Dockerfile | 3 +++ 18.09/dind/dockerd-entrypoint.sh | 10 ++++------ 19.03-rc/Dockerfile | 3 +++ 19.03-rc/dind-rootless/Dockerfile | 14 +++----------- 19.03-rc/dind/dockerd-entrypoint.sh | 10 ++++------ 19.03/Dockerfile | 3 +++ 19.03/dind-rootless/Dockerfile | 14 +++----------- 19.03/dind/dockerd-entrypoint.sh | 10 ++++------ Dockerfile-dind-rootless.template | 14 +++----------- Dockerfile.template | 3 +++ dockerd-entrypoint.sh | 10 ++++------ 13 files changed, 44 insertions(+), 63 deletions(-) diff --git a/18.09-rc/Dockerfile b/18.09-rc/Dockerfile index e62bf15d2..8ac0177f0 100644 --- a/18.09-rc/Dockerfile +++ b/18.09-rc/Dockerfile @@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/ # docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH # (For this to work, at least the "client" subdirectory of this path needs to be shared between the client and server containers via a volume, "docker cp", or other means of data sharing.) ENV DOCKER_TLS_CERTDIR= +# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode +RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client +# (doing both /certs and /certs/client so that if Docker does a "copy-up" into a volume defined on /certs/client, it will "do the right thing" by default in a way that still works for rootless users) ENTRYPOINT ["docker-entrypoint.sh"] CMD ["sh"] diff --git a/18.09-rc/dind/dockerd-entrypoint.sh b/18.09-rc/dind/dockerd-entrypoint.sh index 3926eaebd..47eee5556 100755 --- a/18.09-rc/dind/dockerd-entrypoint.sh +++ b/18.09-rc/dind/dockerd-entrypoint.sh @@ -173,13 +173,11 @@ if [ "$1" = 'dockerd' ]; then --port-driver=builtin \ --copy-up=/etc --copy-up=/run \ ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ - sh -c ' - rm -f /run/docker /run/xtables.lock - exec "$@" --userland-proxy-path=rootlesskit-docker-proxy - ' -- "$@" + "$@" --userland-proxy-path=rootlesskit-docker-proxy fi -elif [ "$1" = 'docker' ]; then - exec docker-entrypoint.sh "$@" +else + # if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too + set -- docker-entrypoint.sh "$@" fi exec "$@" diff --git a/18.09/Dockerfile b/18.09/Dockerfile index 4e49ae068..9ced0da1a 100644 --- a/18.09/Dockerfile +++ b/18.09/Dockerfile @@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/ # docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH # (For this to work, at least the "client" subdirectory of this path needs to be shared between the client and server containers via a volume, "docker cp", or other means of data sharing.) ENV DOCKER_TLS_CERTDIR= +# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode +RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client +# (doing both /certs and /certs/client so that if Docker does a "copy-up" into a volume defined on /certs/client, it will "do the right thing" by default in a way that still works for rootless users) ENTRYPOINT ["docker-entrypoint.sh"] CMD ["sh"] diff --git a/18.09/dind/dockerd-entrypoint.sh b/18.09/dind/dockerd-entrypoint.sh index 3926eaebd..47eee5556 100755 --- a/18.09/dind/dockerd-entrypoint.sh +++ b/18.09/dind/dockerd-entrypoint.sh @@ -173,13 +173,11 @@ if [ "$1" = 'dockerd' ]; then --port-driver=builtin \ --copy-up=/etc --copy-up=/run \ ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ - sh -c ' - rm -f /run/docker /run/xtables.lock - exec "$@" --userland-proxy-path=rootlesskit-docker-proxy - ' -- "$@" + "$@" --userland-proxy-path=rootlesskit-docker-proxy fi -elif [ "$1" = 'docker' ]; then - exec docker-entrypoint.sh "$@" +else + # if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too + set -- docker-entrypoint.sh "$@" fi exec "$@" diff --git a/19.03-rc/Dockerfile b/19.03-rc/Dockerfile index 6414f9fde..b6f49e387 100644 --- a/19.03-rc/Dockerfile +++ b/19.03-rc/Dockerfile @@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/ # docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH # (For this to work, at least the "client" subdirectory of this path needs to be shared between the client and server containers via a volume, "docker cp", or other means of data sharing.) ENV DOCKER_TLS_CERTDIR=/certs +# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode +RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client +# (doing both /certs and /certs/client so that if Docker does a "copy-up" into a volume defined on /certs/client, it will "do the right thing" by default in a way that still works for rootless users) ENTRYPOINT ["docker-entrypoint.sh"] CMD ["sh"] diff --git a/19.03-rc/dind-rootless/Dockerfile b/19.03-rc/dind-rootless/Dockerfile index 9879af618..e65fcb2e0 100644 --- a/19.03-rc/dind-rootless/Dockerfile +++ b/19.03-rc/dind-rootless/Dockerfile @@ -65,17 +65,9 @@ RUN set -eux; \ apk del --no-network .rootlesskit-build-deps; \ rootlesskit --version -# pre-create a few useful directories for our rootless user +# pre-create "/var/lib/docker" for our rootless user RUN set -eux; \ - mkdir -p \ - /home/rootless/.local/share/docker \ - /home/rootless/certs/ca \ - /home/rootless/certs/client \ - /home/rootless/certs/server \ - ; \ - chown -R rootless:rootless \ - /home/rootless/.local/share/docker \ - /home/rootless/certs -ENV DOCKER_TLS_CERTDIR=/home/rootless/certs + mkdir -p /home/rootless/.local/share/docker; \ + chown -R rootless:rootless /home/rootless/.local/share/docker VOLUME /home/rootless/.local/share/docker USER rootless diff --git a/19.03-rc/dind/dockerd-entrypoint.sh b/19.03-rc/dind/dockerd-entrypoint.sh index 3926eaebd..47eee5556 100755 --- a/19.03-rc/dind/dockerd-entrypoint.sh +++ b/19.03-rc/dind/dockerd-entrypoint.sh @@ -173,13 +173,11 @@ if [ "$1" = 'dockerd' ]; then --port-driver=builtin \ --copy-up=/etc --copy-up=/run \ ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ - sh -c ' - rm -f /run/docker /run/xtables.lock - exec "$@" --userland-proxy-path=rootlesskit-docker-proxy - ' -- "$@" + "$@" --userland-proxy-path=rootlesskit-docker-proxy fi -elif [ "$1" = 'docker' ]; then - exec docker-entrypoint.sh "$@" +else + # if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too + set -- docker-entrypoint.sh "$@" fi exec "$@" diff --git a/19.03/Dockerfile b/19.03/Dockerfile index 31074c5bf..1bb4ff237 100644 --- a/19.03/Dockerfile +++ b/19.03/Dockerfile @@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/ # docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH # (For this to work, at least the "client" subdirectory of this path needs to be shared between the client and server containers via a volume, "docker cp", or other means of data sharing.) ENV DOCKER_TLS_CERTDIR=/certs +# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode +RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client +# (doing both /certs and /certs/client so that if Docker does a "copy-up" into a volume defined on /certs/client, it will "do the right thing" by default in a way that still works for rootless users) ENTRYPOINT ["docker-entrypoint.sh"] CMD ["sh"] diff --git a/19.03/dind-rootless/Dockerfile b/19.03/dind-rootless/Dockerfile index 04abad186..51d354f34 100644 --- a/19.03/dind-rootless/Dockerfile +++ b/19.03/dind-rootless/Dockerfile @@ -65,17 +65,9 @@ RUN set -eux; \ apk del --no-network .rootlesskit-build-deps; \ rootlesskit --version -# pre-create a few useful directories for our rootless user +# pre-create "/var/lib/docker" for our rootless user RUN set -eux; \ - mkdir -p \ - /home/rootless/.local/share/docker \ - /home/rootless/certs/ca \ - /home/rootless/certs/client \ - /home/rootless/certs/server \ - ; \ - chown -R rootless:rootless \ - /home/rootless/.local/share/docker \ - /home/rootless/certs -ENV DOCKER_TLS_CERTDIR=/home/rootless/certs + mkdir -p /home/rootless/.local/share/docker; \ + chown -R rootless:rootless /home/rootless/.local/share/docker VOLUME /home/rootless/.local/share/docker USER rootless diff --git a/19.03/dind/dockerd-entrypoint.sh b/19.03/dind/dockerd-entrypoint.sh index 3926eaebd..47eee5556 100755 --- a/19.03/dind/dockerd-entrypoint.sh +++ b/19.03/dind/dockerd-entrypoint.sh @@ -173,13 +173,11 @@ if [ "$1" = 'dockerd' ]; then --port-driver=builtin \ --copy-up=/etc --copy-up=/run \ ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ - sh -c ' - rm -f /run/docker /run/xtables.lock - exec "$@" --userland-proxy-path=rootlesskit-docker-proxy - ' -- "$@" + "$@" --userland-proxy-path=rootlesskit-docker-proxy fi -elif [ "$1" = 'docker' ]; then - exec docker-entrypoint.sh "$@" +else + # if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too + set -- docker-entrypoint.sh "$@" fi exec "$@" diff --git a/Dockerfile-dind-rootless.template b/Dockerfile-dind-rootless.template index 6eb531e1f..b4f6d91d7 100644 --- a/Dockerfile-dind-rootless.template +++ b/Dockerfile-dind-rootless.template @@ -54,17 +54,9 @@ RUN set -eux; \ apk del --no-network .rootlesskit-build-deps; \ rootlesskit --version -# pre-create a few useful directories for our rootless user +# pre-create "/var/lib/docker" for our rootless user RUN set -eux; \ - mkdir -p \ - /home/rootless/.local/share/docker \ - /home/rootless/certs/ca \ - /home/rootless/certs/client \ - /home/rootless/certs/server \ - ; \ - chown -R rootless:rootless \ - /home/rootless/.local/share/docker \ - /home/rootless/certs -ENV DOCKER_TLS_CERTDIR=/home/rootless/certs + mkdir -p /home/rootless/.local/share/docker; \ + chown -R rootless:rootless /home/rootless/.local/share/docker VOLUME /home/rootless/.local/share/docker USER rootless diff --git a/Dockerfile.template b/Dockerfile.template index 8611e96ce..0cd6a29c0 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -43,6 +43,9 @@ COPY docker-entrypoint.sh /usr/local/bin/ # docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH # (For this to work, at least the "client" subdirectory of this path needs to be shared between the client and server containers via a volume, "docker cp", or other means of data sharing.) ENV DOCKER_TLS_CERTDIR=/certs +# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode +RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client +# (doing both /certs and /certs/client so that if Docker does a "copy-up" into a volume defined on /certs/client, it will "do the right thing" by default in a way that still works for rootless users) ENTRYPOINT ["docker-entrypoint.sh"] CMD ["sh"] diff --git a/dockerd-entrypoint.sh b/dockerd-entrypoint.sh index 3926eaebd..47eee5556 100755 --- a/dockerd-entrypoint.sh +++ b/dockerd-entrypoint.sh @@ -173,13 +173,11 @@ if [ "$1" = 'dockerd' ]; then --port-driver=builtin \ --copy-up=/etc --copy-up=/run \ ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ - sh -c ' - rm -f /run/docker /run/xtables.lock - exec "$@" --userland-proxy-path=rootlesskit-docker-proxy - ' -- "$@" + "$@" --userland-proxy-path=rootlesskit-docker-proxy fi -elif [ "$1" = 'docker' ]; then - exec docker-entrypoint.sh "$@" +else + # if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too + set -- docker-entrypoint.sh "$@" fi exec "$@"