Skip to content

Commit 2285205

Browse files
authored
Merge pull request #174 from infosiftr/rootless
Add experimental "rootless" dind variant
2 parents 252695b + c01ffa4 commit 2285205

25 files changed

+580
-32
lines changed

18.09-rc/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/
5454
# docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH
5555
# (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.)
5656
ENV DOCKER_TLS_CERTDIR=
57+
# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode
58+
RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client
59+
# (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)
5760

5861
ENTRYPOINT ["docker-entrypoint.sh"]
5962
CMD ["sh"]

18.09-rc/dind/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ RUN set -eux; \
88
e2fsprogs-extra \
99
iptables \
1010
openssl \
11+
shadow-uidmap \
1112
xfsprogs \
1213
xz \
1314
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)

18.09-rc/dind/dockerd-entrypoint.sh

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ _tls_generate_certs() {
9292
# no arguments passed
9393
# or first arg is `-f` or `--some-option`
9494
if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
95+
# set DOCKER_HOST to the default "--host" value (for both standard or rootless)
96+
uid="$(id -u)"
97+
if [ "$uid" = '0' ]; then
98+
: "${DOCKER_HOST:=unix:///var/run/docker.sock}"
99+
else
100+
# if we're not root, we must be trying to run rootless
101+
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
102+
: "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}"
103+
fi
104+
export DOCKER_HOST
105+
95106
# add our default arguments
96107
if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \
97108
&& _tls_generate_certs "$DOCKER_TLS_CERTDIR" \
@@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
101112
; then
102113
# generate certs and use TLS if requested/possible (default in 19.03+)
103114
set -- dockerd \
104-
--host=unix:///var/run/docker.sock \
115+
--host="$DOCKER_HOST" \
105116
--host=tcp://0.0.0.0:2376 \
106117
--tlsverify \
107118
--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
108119
--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
109120
--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
110121
"$@"
122+
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp"
111123
else
112124
# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
113125
set -- dockerd \
114-
--host=unix:///var/run/docker.sock \
126+
--host="$DOCKER_HOST" \
115127
--host=tcp://0.0.0.0:2375 \
116128
"$@"
129+
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp"
117130
fi
118131
fi
119132

@@ -124,7 +137,47 @@ if [ "$1" = 'dockerd' ]; then
124137
fi
125138

126139
# 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)
127-
find /run /var/run -iname 'docker*.pid' -delete
140+
find /run /var/run -iname 'docker*.pid' -delete || :
141+
142+
uid="$(id -u)"
143+
if [ "$uid" != '0' ]; then
144+
# if we're not root, we must be trying to run rootless
145+
if ! command -v rootlesskit > /dev/null; then
146+
echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)"
147+
exit 1
148+
fi
149+
user="$(id -un 2>/dev/null || :)"
150+
if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then
151+
echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid"
152+
exit 1
153+
fi
154+
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
155+
export XDG_RUNTIME_DIR
156+
if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then
157+
echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid"
158+
exit 1
159+
fi
160+
if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then
161+
echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1"
162+
exit 1
163+
fi
164+
if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then
165+
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"
166+
exit 1
167+
fi
168+
# TODO overlay support detection?
169+
exec rootlesskit \
170+
--net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \
171+
--mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \
172+
--disable-host-loopback \
173+
--port-driver=builtin \
174+
--copy-up=/etc --copy-up=/run \
175+
${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \
176+
"$@" --userland-proxy-path=rootlesskit-docker-proxy
177+
fi
178+
else
179+
# if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too
180+
set -- docker-entrypoint.sh "$@"
128181
fi
129182

130183
exec "$@"

18.09-rc/docker-entrypoint.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,26 @@ _should_tls() {
1919
&& [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ]
2020
}
2121

22-
# 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
23-
if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then
22+
# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly
23+
if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then
24+
export DOCKER_HOST=unix:///var/run/docker.sock
25+
elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then
26+
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock"
27+
fi
28+
29+
# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value
30+
if [ -z "${DOCKER_HOST:-}" ]; then
2431
if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then
2532
export DOCKER_HOST='tcp://docker:2376'
2633
else
2734
export DOCKER_HOST='tcp://docker:2375'
2835
fi
2936
fi
30-
if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then
37+
if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \
38+
&& [ -z "${DOCKER_TLS_VERIFY:-}" ] \
39+
&& [ -z "${DOCKER_CERT_PATH:-}" ] \
40+
&& _should_tls \
41+
; then
3142
export DOCKER_TLS_VERIFY=1
3243
export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client"
3344
fi

18.09/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/
5454
# docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH
5555
# (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.)
5656
ENV DOCKER_TLS_CERTDIR=
57+
# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode
58+
RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client
59+
# (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)
5760

5861
ENTRYPOINT ["docker-entrypoint.sh"]
5962
CMD ["sh"]

18.09/dind/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ RUN set -eux; \
88
e2fsprogs-extra \
99
iptables \
1010
openssl \
11+
shadow-uidmap \
1112
xfsprogs \
1213
xz \
1314
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)

18.09/dind/dockerd-entrypoint.sh

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ _tls_generate_certs() {
9292
# no arguments passed
9393
# or first arg is `-f` or `--some-option`
9494
if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
95+
# set DOCKER_HOST to the default "--host" value (for both standard or rootless)
96+
uid="$(id -u)"
97+
if [ "$uid" = '0' ]; then
98+
: "${DOCKER_HOST:=unix:///var/run/docker.sock}"
99+
else
100+
# if we're not root, we must be trying to run rootless
101+
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
102+
: "${DOCKER_HOST:=unix://$XDG_RUNTIME_DIR/docker.sock}"
103+
fi
104+
export DOCKER_HOST
105+
95106
# add our default arguments
96107
if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \
97108
&& _tls_generate_certs "$DOCKER_TLS_CERTDIR" \
@@ -101,19 +112,21 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
101112
; then
102113
# generate certs and use TLS if requested/possible (default in 19.03+)
103114
set -- dockerd \
104-
--host=unix:///var/run/docker.sock \
115+
--host="$DOCKER_HOST" \
105116
--host=tcp://0.0.0.0:2376 \
106117
--tlsverify \
107118
--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
108119
--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
109120
--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
110121
"$@"
122+
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp"
111123
else
112124
# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
113125
set -- dockerd \
114-
--host=unix:///var/run/docker.sock \
126+
--host="$DOCKER_HOST" \
115127
--host=tcp://0.0.0.0:2375 \
116128
"$@"
129+
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp"
117130
fi
118131
fi
119132

@@ -124,7 +137,47 @@ if [ "$1" = 'dockerd' ]; then
124137
fi
125138

126139
# 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)
127-
find /run /var/run -iname 'docker*.pid' -delete
140+
find /run /var/run -iname 'docker*.pid' -delete || :
141+
142+
uid="$(id -u)"
143+
if [ "$uid" != '0' ]; then
144+
# if we're not root, we must be trying to run rootless
145+
if ! command -v rootlesskit > /dev/null; then
146+
echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)"
147+
exit 1
148+
fi
149+
user="$(id -un 2>/dev/null || :)"
150+
if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then
151+
echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid"
152+
exit 1
153+
fi
154+
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
155+
export XDG_RUNTIME_DIR
156+
if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then
157+
echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid"
158+
exit 1
159+
fi
160+
if ! unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone || :)" || [ "$unprivClone" != '1' ]; then
161+
echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1"
162+
exit 1
163+
fi
164+
if ! maxUserns="$(cat /proc/sys/user/max_user_namespaces || :)" || [ "$maxUserns" = '0' ]; then
165+
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"
166+
exit 1
167+
fi
168+
# TODO overlay support detection?
169+
exec rootlesskit \
170+
--net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \
171+
--mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \
172+
--disable-host-loopback \
173+
--port-driver=builtin \
174+
--copy-up=/etc --copy-up=/run \
175+
${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \
176+
"$@" --userland-proxy-path=rootlesskit-docker-proxy
177+
fi
178+
else
179+
# if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too
180+
set -- docker-entrypoint.sh "$@"
128181
fi
129182

130183
exec "$@"

18.09/docker-entrypoint.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,26 @@ _should_tls() {
1919
&& [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ]
2020
}
2121

22-
# 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
23-
if [ -z "${DOCKER_HOST:-}" ] && [ ! -S /var/run/docker.sock ]; then
22+
# if we have no DOCKER_HOST but we do have the default Unix socket (standard or rootless), use it explicitly
23+
if [ -z "${DOCKER_HOST:-}" ] && [ -S /var/run/docker.sock ]; then
24+
export DOCKER_HOST=unix:///var/run/docker.sock
25+
elif [ -z "${DOCKER_HOST:-}" ] && XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" && [ -S "$XDG_RUNTIME_DIR/docker.sock" ]; then
26+
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock"
27+
fi
28+
29+
# if DOCKER_HOST isn't set (no custom setting, no default socket), let's set it to a sane remote value
30+
if [ -z "${DOCKER_HOST:-}" ]; then
2431
if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then
2532
export DOCKER_HOST='tcp://docker:2376'
2633
else
2734
export DOCKER_HOST='tcp://docker:2375'
2835
fi
2936
fi
30-
if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then
37+
if [ "${DOCKER_HOST#tcp:}" != "$DOCKER_HOST" ] \
38+
&& [ -z "${DOCKER_TLS_VERIFY:-}" ] \
39+
&& [ -z "${DOCKER_CERT_PATH:-}" ] \
40+
&& _should_tls \
41+
; then
3142
export DOCKER_TLS_VERIFY=1
3243
export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client"
3344
fi

19.03-rc/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ COPY docker-entrypoint.sh /usr/local/bin/
5454
# docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH
5555
# (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.)
5656
ENV DOCKER_TLS_CERTDIR=/certs
57+
# also, ensure the directory pre-exists and has wide enough permissions for "dockerd-entrypoint.sh" to create subdirectories, even when run in "rootless" mode
58+
RUN mkdir /certs /certs/client && chmod 1777 /certs /certs/client
59+
# (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)
5760

5861
ENTRYPOINT ["docker-entrypoint.sh"]
5962
CMD ["sh"]

19.03-rc/dind-rootless/Dockerfile

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
FROM docker:19.03-rc-dind
2+
3+
# busybox "ip" is insufficient:
4+
# [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
5+
RUN apk add --no-cache iproute2
6+
7+
# "/run/user/UID" will be used by default as the value of XDG_RUNTIME_DIR
8+
RUN mkdir /run/user && chmod 1777 /run/user
9+
10+
# create a default user preconfigured for running rootless dockerd
11+
RUN set -eux; \
12+
adduser -h /home/rootless -g 'Rootless' -D -u 1000 rootless; \
13+
echo 'rootless:100000:65536' >> /etc/subuid; \
14+
echo 'rootless:100000:65536' >> /etc/subgid
15+
16+
RUN set -eux; \
17+
\
18+
# this "case" statement is generated via "update.sh"
19+
apkArch="$(apk --print-arch)"; \
20+
case "$apkArch" in \
21+
# amd64
22+
x86_64) dockerArch='x86_64' ;; \
23+
# arm32v6
24+
armhf) dockerArch='armel' ;; \
25+
# arm32v7
26+
armv7) dockerArch='armhf' ;; \
27+
# arm64v8
28+
aarch64) dockerArch='aarch64' ;; \
29+
*) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\
30+
esac; \
31+
\
32+
if ! wget -O rootless.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-rootless-extras-${DOCKER_VERSION}.tgz"; then \
33+
echo >&2 "error: failed to download 'docker-rootless-extras-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \
34+
exit 1; \
35+
fi; \
36+
\
37+
tar --extract \
38+
--file rootless.tgz \
39+
--strip-components 1 \
40+
--directory /usr/local/bin/ \
41+
'docker-rootless-extras/vpnkit' \
42+
; \
43+
rm rootless.tgz; \
44+
\
45+
# we download/build rootlesskit separately to get a newer release
46+
# rootlesskit --version; \
47+
vpnkit --version
48+
49+
# https://github.com/rootless-containers/rootlesskit/releases
50+
ENV ROOTLESSKIT_VERSION 0.6.0
51+
52+
RUN set -eux; \
53+
apk add --no-cache --virtual .rootlesskit-build-deps \
54+
go \
55+
libc-dev \
56+
; \
57+
wget -O rootlesskit.tgz "https://github.com/rootless-containers/rootlesskit/archive/v${ROOTLESSKIT_VERSION}.tar.gz"; \
58+
export GOPATH='/go'; mkdir "$GOPATH"; \
59+
mkdir -p "$GOPATH/src/github.com/rootless-containers/rootlesskit"; \
60+
tar --extract --file rootlesskit.tgz --directory "$GOPATH/src/github.com/rootless-containers/rootlesskit" --strip-components 1; \
61+
rm rootlesskit.tgz; \
62+
go build -o /usr/local/bin/rootlesskit github.com/rootless-containers/rootlesskit/cmd/rootlesskit; \
63+
go build -o /usr/local/bin/rootlesskit-docker-proxy github.com/rootless-containers/rootlesskit/cmd/rootlesskit-docker-proxy; \
64+
rm -rf "$GOPATH"; \
65+
apk del --no-network .rootlesskit-build-deps; \
66+
rootlesskit --version
67+
68+
# pre-create "/var/lib/docker" for our rootless user
69+
RUN set -eux; \
70+
mkdir -p /home/rootless/.local/share/docker; \
71+
chown -R rootless:rootless /home/rootless/.local/share/docker
72+
VOLUME /home/rootless/.local/share/docker
73+
USER rootless

0 commit comments

Comments
 (0)