Skip to content

Commit 06fc02e

Browse files
committed
Add automatic TLS generation (and enable it by default in 19.03+)
This adds a `DOCKER_TLS_CERTDIR` environment variable that, when present, will auto-enable TLS on `dockerd` by default (and will set the appropriate client flags if the necessary certificates exist). It will attempt to generate a suitable `subjectAltName` extension value based on all available container IP addresses and hostnames, but the default generation can be extended via the `DOCKER_TLS_SAN` environment variable (in the standard OpenSSL format, ala `IP:n.n.n.n,DNS:foobar,...`). For users of 18.09 who wish to enable this behavior, simply set `DOCKER_TLS_CERTDIR` to a path within the container into which you want certificates generated (and share at least the `client` subdirectory of that path with your client containers). The default value in 19.03+ is `/certs` (so to mimic that, something like `-e DOCKER_TLS_CERTDIR=/certs` would be sufficient/appropriate). For users of 19.03+ who wish to *disable* this behavior (not recommended), simply set `DOCKER_TLS_CERTDIR` to the empty string (`-e DOCKER_TLS_CERTDIR=`).
1 parent cbb0ee0 commit 06fc02e

17 files changed

+527
-40
lines changed

18.09-rc/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,8 @@ RUN set -eux; \
4949
COPY modprobe.sh /usr/local/bin/modprobe
5050
COPY docker-entrypoint.sh /usr/local/bin/
5151

52+
# DOCKER_TLS_CERTDIR is where certificates for TLS will be automatically pulled from (and generated into for dind)
53+
ENV DOCKER_TLS_CERTDIR=
54+
5255
ENTRYPOINT ["docker-entrypoint.sh"]
5356
CMD ["sh"]

18.09-rc/dind/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ RUN set -eux; \
77
e2fsprogs \
88
e2fsprogs-extra \
99
iptables \
10+
openssl \
1011
xfsprogs \
1112
xz \
1213
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)
@@ -38,7 +39,7 @@ RUN set -eux; \
3839
COPY dockerd-entrypoint.sh /usr/local/bin/
3940

4041
VOLUME /var/lib/docker
41-
EXPOSE 2375
42+
EXPOSE 2375 2376
4243

4344
ENTRYPOINT ["dockerd-entrypoint.sh"]
4445
CMD []

18.09-rc/dind/dockerd-entrypoint.sh

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,115 @@
11
#!/bin/sh
2-
set -e
2+
set -eu
3+
4+
_tls_ensure_private() {
5+
local f="$1"; shift
6+
[ -s "$f" ] || openssl genrsa -out "$f" 8192
7+
}
8+
_tls_san() {
9+
{
10+
ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
11+
{
12+
cat /etc/hostname
13+
echo 'localhost'
14+
hostname -f
15+
hostname -s
16+
} | sed 's/^/DNS:/'
17+
[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
18+
} | sort -u | xargs printf '%s,' | sed "s/,\$//"
19+
}
20+
_tls_generate_certs() {
21+
local dir="$1"; shift
22+
23+
# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
24+
# if ca/key.pem, generate server public
25+
# if ca/key.pem, generate client public
26+
# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
27+
28+
if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
29+
# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
30+
mkdir -p "$dir/ca"
31+
_tls_ensure_private "$dir/ca/key.pem"
32+
openssl req -new -key "$dir/ca/key.pem" \
33+
-out "$dir/ca/cert.pem" \
34+
-subj '/CN=docker:dind CA' -x509 -days "$(( 365 * 30 ))"
35+
fi
36+
37+
if [ -s "$dir/ca/key.pem" ]; then
38+
# if we have a CA private key, we should create/manage a server key
39+
mkdir -p "$dir/server"
40+
_tls_ensure_private "$dir/server/key.pem"
41+
openssl req -new -key "$dir/server/key.pem" \
42+
-out "$dir/server/csr.pem" \
43+
-subj '/CN=docker:dind server'
44+
cat > "$dir/server/openssl.cnf" <<-EOF
45+
[ x509_exts ]
46+
subjectAltName = $(_tls_san)
47+
EOF
48+
openssl x509 -req \
49+
-in "$dir/server/csr.pem" \
50+
-CA "$dir/ca/cert.pem" \
51+
-CAkey "$dir/ca/key.pem" \
52+
-CAcreateserial \
53+
-out "$dir/server/cert.pem" \
54+
-days "$(( 365 * 30 ))" \
55+
-extfile "$dir/server/openssl.cnf" \
56+
-extensions x509_exts
57+
cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
58+
openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
59+
fi
60+
61+
if [ -s "$dir/ca/key.pem" ]; then
62+
# if we have a CA private key, we should create/manage a client key
63+
mkdir -p "$dir/client"
64+
_tls_ensure_private "$dir/client/key.pem"
65+
openssl req -new \
66+
-key "$dir/client/key.pem" \
67+
-out "$dir/client/csr.pem" \
68+
-subj '/CN=docker:dind client'
69+
cat > "$dir/client/openssl.cnf" <<-'EOF'
70+
[ x509_exts ]
71+
extendedKeyUsage = clientAuth
72+
EOF
73+
openssl x509 -req \
74+
-in "$dir/client/csr.pem" \
75+
-CA "$dir/ca/cert.pem" \
76+
-CAkey "$dir/ca/key.pem" \
77+
-CAcreateserial \
78+
-out "$dir/client/cert.pem" \
79+
-days "$(( 365 * 30 ))" \
80+
-extfile "$dir/client/openssl.cnf" \
81+
-extensions x509_exts
82+
cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
83+
openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
84+
fi
85+
}
386

487
# no arguments passed
588
# or first arg is `-f` or `--some-option`
689
if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
790
# add our default arguments
8-
set -- dockerd \
9-
--host=unix:///var/run/docker.sock \
10-
--host=tcp://0.0.0.0:2375 \
11-
"$@"
91+
if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \
92+
&& _tls_generate_certs "$DOCKER_TLS_CERTDIR" \
93+
&& [ -s "$DOCKER_TLS_CERTDIR/server/ca.pem" ] \
94+
&& [ -s "$DOCKER_TLS_CERTDIR/server/cert.pem" ] \
95+
&& [ -s "$DOCKER_TLS_CERTDIR/server/key.pem" ] \
96+
; then
97+
# generate certs and use TLS if requested/possible (default in 19.03+)
98+
set -- dockerd \
99+
--host=unix:///var/run/docker.sock \
100+
--host=tcp://0.0.0.0:2376 \
101+
--tlsverify \
102+
--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
103+
--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
104+
--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
105+
"$@"
106+
else
107+
# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
108+
set -- dockerd \
109+
--host=unix:///var/run/docker.sock \
110+
--host=tcp://0.0.0.0:2375 \
111+
"$@"
112+
fi
12113
fi
13114

14115
if [ "$1" = 'dockerd' ]; then

18.09-rc/docker-entrypoint.sh

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/sh
2-
set -e
2+
set -eu
33

44
# first arg is `-f` or `--some-option`
55
if [ "${1#-}" != "$1" ]; then
@@ -12,9 +12,24 @@ if docker help "$1" > /dev/null 2>&1; then
1212
set -- docker "$@"
1313
fi
1414

15-
# if we have "--link some-docker:docker" and not DOCKER_HOST, let's set DOCKER_HOST automatically
16-
if [ -z "$DOCKER_HOST" -a "$DOCKER_PORT_2375_TCP" ]; then
17-
export DOCKER_HOST='tcp://docker:2375'
15+
_should_tls() {
16+
[ -n "${DOCKER_TLS_CERTDIR:-}" ] \
17+
&& [ -s "$DOCKER_TLS_CERTDIR/client/ca.pem" ] \
18+
&& [ -s "$DOCKER_TLS_CERTDIR/client/cert.pem" ] \
19+
&& [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ]
20+
}
21+
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
24+
if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then
25+
export DOCKER_HOST='tcp://docker:2376'
26+
else
27+
export DOCKER_HOST='tcp://docker:2375'
28+
fi
29+
fi
30+
if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then
31+
export DOCKER_TLS_VERIFY=1
32+
export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client"
1833
fi
1934

2035
if [ "$1" = 'dockerd' ]; then

18.09/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,8 @@ RUN set -eux; \
4949
COPY modprobe.sh /usr/local/bin/modprobe
5050
COPY docker-entrypoint.sh /usr/local/bin/
5151

52+
# DOCKER_TLS_CERTDIR is where certificates for TLS will be automatically pulled from (and generated into for dind)
53+
ENV DOCKER_TLS_CERTDIR=
54+
5255
ENTRYPOINT ["docker-entrypoint.sh"]
5356
CMD ["sh"]

18.09/dind/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ RUN set -eux; \
77
e2fsprogs \
88
e2fsprogs-extra \
99
iptables \
10+
openssl \
1011
xfsprogs \
1112
xz \
1213
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)
@@ -38,7 +39,7 @@ RUN set -eux; \
3839
COPY dockerd-entrypoint.sh /usr/local/bin/
3940

4041
VOLUME /var/lib/docker
41-
EXPOSE 2375
42+
EXPOSE 2375 2376
4243

4344
ENTRYPOINT ["dockerd-entrypoint.sh"]
4445
CMD []

18.09/dind/dockerd-entrypoint.sh

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,115 @@
11
#!/bin/sh
2-
set -e
2+
set -eu
3+
4+
_tls_ensure_private() {
5+
local f="$1"; shift
6+
[ -s "$f" ] || openssl genrsa -out "$f" 8192
7+
}
8+
_tls_san() {
9+
{
10+
ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
11+
{
12+
cat /etc/hostname
13+
echo 'localhost'
14+
hostname -f
15+
hostname -s
16+
} | sed 's/^/DNS:/'
17+
[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
18+
} | sort -u | xargs printf '%s,' | sed "s/,\$//"
19+
}
20+
_tls_generate_certs() {
21+
local dir="$1"; shift
22+
23+
# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
24+
# if ca/key.pem, generate server public
25+
# if ca/key.pem, generate client public
26+
# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
27+
28+
if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
29+
# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
30+
mkdir -p "$dir/ca"
31+
_tls_ensure_private "$dir/ca/key.pem"
32+
openssl req -new -key "$dir/ca/key.pem" \
33+
-out "$dir/ca/cert.pem" \
34+
-subj '/CN=docker:dind CA' -x509 -days "$(( 365 * 30 ))"
35+
fi
36+
37+
if [ -s "$dir/ca/key.pem" ]; then
38+
# if we have a CA private key, we should create/manage a server key
39+
mkdir -p "$dir/server"
40+
_tls_ensure_private "$dir/server/key.pem"
41+
openssl req -new -key "$dir/server/key.pem" \
42+
-out "$dir/server/csr.pem" \
43+
-subj '/CN=docker:dind server'
44+
cat > "$dir/server/openssl.cnf" <<-EOF
45+
[ x509_exts ]
46+
subjectAltName = $(_tls_san)
47+
EOF
48+
openssl x509 -req \
49+
-in "$dir/server/csr.pem" \
50+
-CA "$dir/ca/cert.pem" \
51+
-CAkey "$dir/ca/key.pem" \
52+
-CAcreateserial \
53+
-out "$dir/server/cert.pem" \
54+
-days "$(( 365 * 30 ))" \
55+
-extfile "$dir/server/openssl.cnf" \
56+
-extensions x509_exts
57+
cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
58+
openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
59+
fi
60+
61+
if [ -s "$dir/ca/key.pem" ]; then
62+
# if we have a CA private key, we should create/manage a client key
63+
mkdir -p "$dir/client"
64+
_tls_ensure_private "$dir/client/key.pem"
65+
openssl req -new \
66+
-key "$dir/client/key.pem" \
67+
-out "$dir/client/csr.pem" \
68+
-subj '/CN=docker:dind client'
69+
cat > "$dir/client/openssl.cnf" <<-'EOF'
70+
[ x509_exts ]
71+
extendedKeyUsage = clientAuth
72+
EOF
73+
openssl x509 -req \
74+
-in "$dir/client/csr.pem" \
75+
-CA "$dir/ca/cert.pem" \
76+
-CAkey "$dir/ca/key.pem" \
77+
-CAcreateserial \
78+
-out "$dir/client/cert.pem" \
79+
-days "$(( 365 * 30 ))" \
80+
-extfile "$dir/client/openssl.cnf" \
81+
-extensions x509_exts
82+
cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
83+
openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
84+
fi
85+
}
386

487
# no arguments passed
588
# or first arg is `-f` or `--some-option`
689
if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
790
# add our default arguments
8-
set -- dockerd \
9-
--host=unix:///var/run/docker.sock \
10-
--host=tcp://0.0.0.0:2375 \
11-
"$@"
91+
if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \
92+
&& _tls_generate_certs "$DOCKER_TLS_CERTDIR" \
93+
&& [ -s "$DOCKER_TLS_CERTDIR/server/ca.pem" ] \
94+
&& [ -s "$DOCKER_TLS_CERTDIR/server/cert.pem" ] \
95+
&& [ -s "$DOCKER_TLS_CERTDIR/server/key.pem" ] \
96+
; then
97+
# generate certs and use TLS if requested/possible (default in 19.03+)
98+
set -- dockerd \
99+
--host=unix:///var/run/docker.sock \
100+
--host=tcp://0.0.0.0:2376 \
101+
--tlsverify \
102+
--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
103+
--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
104+
--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
105+
"$@"
106+
else
107+
# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
108+
set -- dockerd \
109+
--host=unix:///var/run/docker.sock \
110+
--host=tcp://0.0.0.0:2375 \
111+
"$@"
112+
fi
12113
fi
13114

14115
if [ "$1" = 'dockerd' ]; then

18.09/docker-entrypoint.sh

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/sh
2-
set -e
2+
set -eu
33

44
# first arg is `-f` or `--some-option`
55
if [ "${1#-}" != "$1" ]; then
@@ -12,9 +12,24 @@ if docker help "$1" > /dev/null 2>&1; then
1212
set -- docker "$@"
1313
fi
1414

15-
# if we have "--link some-docker:docker" and not DOCKER_HOST, let's set DOCKER_HOST automatically
16-
if [ -z "$DOCKER_HOST" -a "$DOCKER_PORT_2375_TCP" ]; then
17-
export DOCKER_HOST='tcp://docker:2375'
15+
_should_tls() {
16+
[ -n "${DOCKER_TLS_CERTDIR:-}" ] \
17+
&& [ -s "$DOCKER_TLS_CERTDIR/client/ca.pem" ] \
18+
&& [ -s "$DOCKER_TLS_CERTDIR/client/cert.pem" ] \
19+
&& [ -s "$DOCKER_TLS_CERTDIR/client/key.pem" ]
20+
}
21+
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
24+
if _should_tls || [ -n "${DOCKER_TLS_VERIFY:-}" ]; then
25+
export DOCKER_HOST='tcp://docker:2376'
26+
else
27+
export DOCKER_HOST='tcp://docker:2375'
28+
fi
29+
fi
30+
if [ -n "${DOCKER_HOST:-}" ] && _should_tls; then
31+
export DOCKER_TLS_VERIFY=1
32+
export DOCKER_CERT_PATH="$DOCKER_TLS_CERTDIR/client"
1833
fi
1934

2035
if [ "$1" = 'dockerd' ]; then

19.03-rc/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,8 @@ RUN set -eux; \
4949
COPY modprobe.sh /usr/local/bin/modprobe
5050
COPY docker-entrypoint.sh /usr/local/bin/
5151

52+
# DOCKER_TLS_CERTDIR is where certificates for TLS will be automatically pulled from (and generated into for dind)
53+
ENV DOCKER_TLS_CERTDIR=/certs
54+
5255
ENTRYPOINT ["docker-entrypoint.sh"]
5356
CMD ["sh"]

19.03-rc/dind/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ RUN set -eux; \
77
e2fsprogs \
88
e2fsprogs-extra \
99
iptables \
10+
openssl \
1011
xfsprogs \
1112
xz \
1213
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)
@@ -38,7 +39,7 @@ RUN set -eux; \
3839
COPY dockerd-entrypoint.sh /usr/local/bin/
3940

4041
VOLUME /var/lib/docker
41-
EXPOSE 2375
42+
EXPOSE 2375 2376
4243

4344
ENTRYPOINT ["dockerd-entrypoint.sh"]
4445
CMD []

0 commit comments

Comments
 (0)