Skip to content

Commit cad4d26

Browse files
authored
Merge pull request #166 from infosiftr/auto-tls
Add automatic TLS generation (and enable it by default in 19.03+)
2 parents 651c075 + 6438624 commit cad4d26

17 files changed

+553
-40
lines changed

18.09-rc/Dockerfile

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

52+
# https://github.com/docker-library/docker/pull/166
53+
# dockerd-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-generating TLS certificates
54+
# docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH
55+
# (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.)
56+
ENV DOCKER_TLS_CERTDIR=
57+
5258
ENTRYPOINT ["docker-entrypoint.sh"]
5359
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: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,119 @@
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" 4196
7+
}
8+
_tls_san() {
9+
{
10+
ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
11+
{
12+
cat /etc/hostname
13+
echo 'docker'
14+
echo 'localhost'
15+
hostname -f
16+
hostname -s
17+
} | sed 's/^/DNS:/'
18+
[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
19+
} | sort -u | xargs printf '%s,' | sed "s/,\$//"
20+
}
21+
_tls_generate_certs() {
22+
local dir="$1"; shift
23+
24+
# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
25+
# if ca/key.pem, generate server public
26+
# if ca/key.pem, generate client public
27+
# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
28+
29+
# https://github.com/FiloSottile/mkcert/issues/174
30+
local certValidDays='825'
31+
32+
if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
33+
# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
34+
mkdir -p "$dir/ca"
35+
_tls_ensure_private "$dir/ca/key.pem"
36+
openssl req -new -key "$dir/ca/key.pem" \
37+
-out "$dir/ca/cert.pem" \
38+
-subj '/CN=docker:dind CA' -x509 -days "$certValidDays"
39+
fi
40+
41+
if [ -s "$dir/ca/key.pem" ]; then
42+
# if we have a CA private key, we should create/manage a server key
43+
mkdir -p "$dir/server"
44+
_tls_ensure_private "$dir/server/key.pem"
45+
openssl req -new -key "$dir/server/key.pem" \
46+
-out "$dir/server/csr.pem" \
47+
-subj '/CN=docker:dind server'
48+
cat > "$dir/server/openssl.cnf" <<-EOF
49+
[ x509_exts ]
50+
subjectAltName = $(_tls_san)
51+
EOF
52+
openssl x509 -req \
53+
-in "$dir/server/csr.pem" \
54+
-CA "$dir/ca/cert.pem" \
55+
-CAkey "$dir/ca/key.pem" \
56+
-CAcreateserial \
57+
-out "$dir/server/cert.pem" \
58+
-days "$certValidDays" \
59+
-extfile "$dir/server/openssl.cnf" \
60+
-extensions x509_exts
61+
cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
62+
openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
63+
fi
64+
65+
if [ -s "$dir/ca/key.pem" ]; then
66+
# if we have a CA private key, we should create/manage a client key
67+
mkdir -p "$dir/client"
68+
_tls_ensure_private "$dir/client/key.pem"
69+
openssl req -new \
70+
-key "$dir/client/key.pem" \
71+
-out "$dir/client/csr.pem" \
72+
-subj '/CN=docker:dind client'
73+
cat > "$dir/client/openssl.cnf" <<-'EOF'
74+
[ x509_exts ]
75+
extendedKeyUsage = clientAuth
76+
EOF
77+
openssl x509 -req \
78+
-in "$dir/client/csr.pem" \
79+
-CA "$dir/ca/cert.pem" \
80+
-CAkey "$dir/ca/key.pem" \
81+
-CAcreateserial \
82+
-out "$dir/client/cert.pem" \
83+
-days "$certValidDays" \
84+
-extfile "$dir/client/openssl.cnf" \
85+
-extensions x509_exts
86+
cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
87+
openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
88+
fi
89+
}
390

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

14119
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,11 @@ RUN set -eux; \
4949
COPY modprobe.sh /usr/local/bin/modprobe
5050
COPY docker-entrypoint.sh /usr/local/bin/
5151

52+
# https://github.com/docker-library/docker/pull/166
53+
# dockerd-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-generating TLS certificates
54+
# docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH
55+
# (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.)
56+
ENV DOCKER_TLS_CERTDIR=
57+
5258
ENTRYPOINT ["docker-entrypoint.sh"]
5359
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: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,119 @@
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" 4196
7+
}
8+
_tls_san() {
9+
{
10+
ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
11+
{
12+
cat /etc/hostname
13+
echo 'docker'
14+
echo 'localhost'
15+
hostname -f
16+
hostname -s
17+
} | sed 's/^/DNS:/'
18+
[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
19+
} | sort -u | xargs printf '%s,' | sed "s/,\$//"
20+
}
21+
_tls_generate_certs() {
22+
local dir="$1"; shift
23+
24+
# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
25+
# if ca/key.pem, generate server public
26+
# if ca/key.pem, generate client public
27+
# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
28+
29+
# https://github.com/FiloSottile/mkcert/issues/174
30+
local certValidDays='825'
31+
32+
if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
33+
# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
34+
mkdir -p "$dir/ca"
35+
_tls_ensure_private "$dir/ca/key.pem"
36+
openssl req -new -key "$dir/ca/key.pem" \
37+
-out "$dir/ca/cert.pem" \
38+
-subj '/CN=docker:dind CA' -x509 -days "$certValidDays"
39+
fi
40+
41+
if [ -s "$dir/ca/key.pem" ]; then
42+
# if we have a CA private key, we should create/manage a server key
43+
mkdir -p "$dir/server"
44+
_tls_ensure_private "$dir/server/key.pem"
45+
openssl req -new -key "$dir/server/key.pem" \
46+
-out "$dir/server/csr.pem" \
47+
-subj '/CN=docker:dind server'
48+
cat > "$dir/server/openssl.cnf" <<-EOF
49+
[ x509_exts ]
50+
subjectAltName = $(_tls_san)
51+
EOF
52+
openssl x509 -req \
53+
-in "$dir/server/csr.pem" \
54+
-CA "$dir/ca/cert.pem" \
55+
-CAkey "$dir/ca/key.pem" \
56+
-CAcreateserial \
57+
-out "$dir/server/cert.pem" \
58+
-days "$certValidDays" \
59+
-extfile "$dir/server/openssl.cnf" \
60+
-extensions x509_exts
61+
cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
62+
openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
63+
fi
64+
65+
if [ -s "$dir/ca/key.pem" ]; then
66+
# if we have a CA private key, we should create/manage a client key
67+
mkdir -p "$dir/client"
68+
_tls_ensure_private "$dir/client/key.pem"
69+
openssl req -new \
70+
-key "$dir/client/key.pem" \
71+
-out "$dir/client/csr.pem" \
72+
-subj '/CN=docker:dind client'
73+
cat > "$dir/client/openssl.cnf" <<-'EOF'
74+
[ x509_exts ]
75+
extendedKeyUsage = clientAuth
76+
EOF
77+
openssl x509 -req \
78+
-in "$dir/client/csr.pem" \
79+
-CA "$dir/ca/cert.pem" \
80+
-CAkey "$dir/ca/key.pem" \
81+
-CAcreateserial \
82+
-out "$dir/client/cert.pem" \
83+
-days "$certValidDays" \
84+
-extfile "$dir/client/openssl.cnf" \
85+
-extensions x509_exts
86+
cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
87+
openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
88+
fi
89+
}
390

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

14119
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,11 @@ RUN set -eux; \
4949
COPY modprobe.sh /usr/local/bin/modprobe
5050
COPY docker-entrypoint.sh /usr/local/bin/
5151

52+
# https://github.com/docker-library/docker/pull/166
53+
# dockerd-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-generating TLS certificates
54+
# docker-entrypoint.sh uses DOCKER_TLS_CERTDIR for auto-setting DOCKER_TLS_VERIFY and DOCKER_CERT_PATH
55+
# (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.)
56+
ENV DOCKER_TLS_CERTDIR=/certs
57+
5258
ENTRYPOINT ["docker-entrypoint.sh"]
5359
CMD ["sh"]

0 commit comments

Comments
 (0)