Skip to content

Commit b1cda82

Browse files
authored
Merge pull request #6 from kekru/auto-create-certs
create certs on startup and use stream instead of http
2 parents f6f7bd6 + 929e6cd commit b1cda82

File tree

10 files changed

+299
-24
lines changed

10 files changed

+299
-24
lines changed

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
cert-data
2+
certs

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cert-data
2+
certs
3+
temp

Dockerfile

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
FROM nginx:alpine
2-
MAINTAINER Kevin Krummenauer <[email protected]>
3-
COPY resources/nginx-cert.conf /etc/nginx/conf.d/nginx-cert.conf
4-
RUN sed -i 's/user\s*nginx;/user root;/g' /etc/nginx/nginx.conf
1+
FROM nginx:1.15.12-alpine
2+
LABEL MAINTAINER="Kevin Krummenauer <[email protected]>"
3+
RUN apk add --no-cache openssl
4+
5+
COPY resources /script
6+
7+
RUN cp /script/nginx-cert.conf /etc/nginx/nginx.conf \
8+
&& chmod +x /script/create-certs.sh /script/entrypoint.sh
9+
10+
ENV CREATE_CERTS_WITH_PW="" \
11+
CERTS_DIR=/data/certs \
12+
CERT_HOSTNAME="myserver.example.com"
13+
14+
ENTRYPOINT ["/script/entrypoint.sh"]
15+
CMD ["nginx", "-g", "daemon off;"]

README.md

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,67 @@
11
# Docker Remote API with TLS client authentication via container
2+
23
This images makes you publish your Docker Remote API by a container.
34
A client must authenticate with a client-TLS certificate.
4-
This is an alternative way, instead of configuring TLS on Docker directly.
5+
This is an alternative way, instead of [configuring TLS on Docker directly](https://gist.github.com/kekru/974e40bb1cd4b947a53cca5ba4b0bbe5).
6+
7+
## Remote Api with external CA, certificates and key
58

6-
## Create CA, certificates and keys
79
First you need a CA and certs and keys for your Docker server and the client.
10+
811
Create them as shown here [Protect the Docker daemon socket](https://docs.docker.com/engine/security/https/).
912
Or create the files with this script [create-certs.sh](https://github.com/kekru/linux-utils/blob/master/cert-generate/create-certs.sh). Read [Create certificate files](https://gist.github.com/kekru/974e40bb1cd4b947a53cca5ba4b0bbe5#create-certificate-files) for information on how to use the script.
1013

11-
## Start Container
12-
Copy the following files in a directory. The directory will me mounted in the container.
14+
Copy the following files in a directory. The directory will me mounted in the container.
15+
1316
```bash
14-
ca-cert.pem
15-
server-cert.pem
17+
ca-cert.pem
18+
server-cert.pem
1619
server-key.pem
1720
```
1821

1922
The files `cert.pem` and `key.pem` are certificate and key for the client. The client will also need the `ca-cert.pem`.
2023

21-
Now run the container:
22-
`docker run --name remote-api-tls -d -p 2376:443 -v <local cert dir>:/data/certs:ro -v /var/run/docker.sock:/var/run/docker.sock:ro whiledo/docker-remote-api-tls`
24+
Create a docker-compose.yml file:
2325

26+
```yml
27+
version: "3.4"
28+
services:
29+
remote-api:
30+
image: kekru/docker-remote-api-tls:v0.2.0
31+
ports:
32+
- 2376:443
33+
volumes:
34+
- <local cert dir>:/data/certs:ro
35+
- /var/run/docker.sock:/var/run/docker.sock:ro
36+
```
37+
38+
Now run the container with `docker-compose up -d` or `docker stack deploy --compose-file=docker-compose.yml remoteapi`.
2439
Your Docker Remote API is available on port 2376 via https. The client needs to authenticate via `cert.pem` and `key.pem`.
40+
41+
## Remote Api with auto generating CA, certificates and keys
42+
43+
The docker-remote-api image can generate CA, certificates and keys for you automatically.
44+
Create a docker-compose.yml file, specifying a password and the hostname, on which the remote api will be accessible later on. The hostname will be written to the server's certificate.
45+
46+
```yml
47+
version: "3.4"
48+
services:
49+
remote-api:
50+
image: kekru/docker-remote-api-tls:v0.2.0
51+
ports:
52+
- 2376:443
53+
environment:
54+
- CREATE_CERTS_WITH_PW=supersecret
55+
- CERT_HOSTNAME=remote-api.example.com
56+
volumes:
57+
- <local cert dir>:/data/certs
58+
- /var/run/docker.sock:/var/run/docker.sock:ro
59+
```
60+
61+
Now run the container with `docker-compose up -d` or `docker stack deploy --compose-file=docker-compose.yml remoteapi`.
62+
Certificates will be creates in `<local cert dir>`.
63+
You will find the client-certs in `<local cert dir>/client/`. The files are `ca.pem`, `cert.pem` and `key.pem`.
64+
65+
## Setup client
66+
67+
See [Run commands on remote Docker host](https://gist.github.com/kekru/4e6d49b4290a4eebc7b597c07eaf61f2) for instructions how to setup a client to communicate with the remote api.

docker-compose.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
x-sharedConfig: &sharedConfig
2+
logging:
3+
driver: "json-file"
4+
options:
5+
max-size: "500k"
6+
max-file: "10"
7+
8+
x-sharedDeployConfig: &sharedDeployConfig
9+
resources:
10+
limits:
11+
cpus: '0.5'
12+
memory: 150M
13+
reservations:
14+
cpus: '0.1'
15+
memory: 5M
16+
17+
version: "3.4"
18+
19+
services:
20+
remote-api:
21+
<<: *sharedConfig
22+
image: kekru/docker-remote-api-tls:temp
23+
ports:
24+
- 8443:443
25+
environment:
26+
- CREATE_CERTS_WITH_PW=supersecret
27+
- CERT_HOSTNAME=abc.127.0.0.1.nip.io
28+
volumes:
29+
- ./certs:/data/certs
30+
- /var/run/docker.sock:/var/run/docker.sock:ro
31+
deploy:
32+
<<: *sharedDeployConfig
33+
placement:
34+
constraints:
35+
- node.role == manager

resources/create-certs.sh

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/bin/sh
2+
#see https://docs.docker.com/engine/security/https/
3+
4+
EXPIRATIONDAYS=700
5+
CASUBJSTRING="/C=GB/ST=London/L=London/O=ExampleCompany/OU=IT/CN=example.com/[email protected]"
6+
7+
while [[ $# -gt 1 ]]
8+
do
9+
key="$1"
10+
11+
case $key in
12+
-m|--mode)
13+
MODE="$2"
14+
shift
15+
;;
16+
-h|--hostname)
17+
NAME="$2"
18+
shift
19+
;;
20+
-hip|--hostip)
21+
SERVERIP="$2"
22+
shift
23+
;;
24+
-pw|--password)
25+
PASSWORD="$2"
26+
shift
27+
;;
28+
-t|--targetdir)
29+
TARGETDIR="$2"
30+
shift
31+
;;
32+
-e|--expirationdays)
33+
EXPIRATIONDAYS="$2"
34+
shift
35+
;;
36+
--ca-subj)
37+
CASUBJSTRING="$2"
38+
shift
39+
;;
40+
*)
41+
# unknown option
42+
;;
43+
esac
44+
shift
45+
done
46+
47+
echo "Mode $MODE"
48+
echo "Host/Clientname $NAME"
49+
echo "Host IP $SERVERIP"
50+
echo "Targetdir $TARGETDIR"
51+
echo "Expiration $EXPIRATIONDAYS"
52+
53+
programname=$0
54+
55+
function usage {
56+
echo "usage: $programname -m ca -h example.de [-hip 1.2.3.4] -pw my-secret -t /target/dir [-e 365]"
57+
echo " -m|--mode 'ca' to create CA, 'server' to create server cert, 'client' to create client cert"
58+
echo " -h|--hostname|-n|--name DNS hostname for the server or name of client"
59+
echo " -hip|--hostip host's IP - default: none"
60+
echo " -pw|--password Password for CA Key generation"
61+
echo " -t|--targetdir Targetdir for certfiles and keys"
62+
echo " -e|--expirationdays certificate expiration in day - default: 700 days"
63+
echo " --ca-subj subj string for ca cert - default: Example String..."
64+
exit 1
65+
}
66+
67+
function createCA {
68+
openssl genrsa -aes256 -passout pass:$PASSWORD -out $TARGETDIR/ca-key.pem 4096
69+
openssl req -passin pass:$PASSWORD -new -x509 -days $EXPIRATIONDAYS -key $TARGETDIR/ca-key.pem -sha256 -out $TARGETDIR/ca.pem -subj $CASUBJSTRING
70+
71+
chmod 0400 $TARGETDIR/ca-key.pem
72+
chmod 0444 $TARGETDIR/ca.pem
73+
}
74+
75+
function checkCAFilesExist {
76+
if [[ ! -f "$TARGETDIR/ca.pem" || ! -f "$TARGETDIR/ca-key.pem" ]]; then
77+
echo "$TARGETDIR/ca.pem or $TARGETDIR/ca-key.pem not found. Create CA first with '-m ca'"
78+
exit 1
79+
fi
80+
}
81+
82+
function createServerCert {
83+
checkCAFilesExist
84+
85+
if [[ -z $SERVERIP ]]; then
86+
IPSTRING=""
87+
else
88+
IPSTRING=",IP:$SERVERIP"
89+
fi
90+
91+
openssl genrsa -out $TARGETDIR/server-key.pem 4096
92+
openssl req -subj "/CN=$NAME" -new -key $TARGETDIR/server-key.pem -out $TARGETDIR/server.csr
93+
echo "subjectAltName = DNS:$NAME$IPSTRING" > $TARGETDIR/extfile.cnf
94+
openssl x509 -passin pass:$PASSWORD -req -days $EXPIRATIONDAYS -in $TARGETDIR/server.csr -CA $TARGETDIR/ca.pem -CAkey $TARGETDIR/ca-key.pem -CAcreateserial -out $TARGETDIR/server-cert.pem -extfile $TARGETDIR/extfile.cnf
95+
96+
rm $TARGETDIR/server.csr $TARGETDIR/extfile.cnf $TARGETDIR/ca.srl
97+
chmod 0400 $TARGETDIR/server-key.pem
98+
chmod 0444 $TARGETDIR/server-cert.pem
99+
}
100+
101+
function createClientCert {
102+
checkCAFilesExist
103+
104+
openssl genrsa -out $TARGETDIR/client-key.pem 4096
105+
openssl req -subj "/CN=$NAME" -new -key $TARGETDIR/client-key.pem -out $TARGETDIR/client.csr
106+
echo "extendedKeyUsage = clientAuth" > $TARGETDIR/extfile.cnf
107+
openssl x509 -passin pass:$PASSWORD -req -days $EXPIRATIONDAYS -in $TARGETDIR/client.csr -CA $TARGETDIR/ca.pem -CAkey $TARGETDIR/ca-key.pem -CAcreateserial -out $TARGETDIR/client-cert.pem -extfile $TARGETDIR/extfile.cnf
108+
109+
rm $TARGETDIR/client.csr $TARGETDIR/extfile.cnf $TARGETDIR/ca.srl
110+
chmod 0400 $TARGETDIR/client-key.pem
111+
chmod 0444 $TARGETDIR/client-cert.pem
112+
113+
mv $TARGETDIR/client-key.pem $TARGETDIR/client-$NAME-key.pem
114+
mv $TARGETDIR/client-cert.pem $TARGETDIR/client-$NAME-cert.pem
115+
}
116+
117+
118+
if [[ -z $MODE || -z $PASSWORD || -z $TARGETDIR ]]; then
119+
usage
120+
fi
121+
122+
mkdir -p $TARGETDIR
123+
124+
if [[ $MODE = "ca" ]]; then
125+
createCA
126+
elif [[ $MODE = "server" ]]; then
127+
createServerCert
128+
elif [[ $MODE = "client" ]]; then
129+
createClientCert
130+
else
131+
usage
132+
fi

resources/entrypoint.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/sh
2+
3+
if [ -n $CREATE_CERTS_WITH_PW ]; then
4+
if [ -z "$(ls -A $CERTS_DIR)" ]; then
5+
6+
echo "Create CA cert"
7+
/script/create-certs.sh -m ca -pw $CREATE_CERTS_WITH_PW -t $CERTS_DIR -e 900
8+
echo "Create server cert"
9+
/script/create-certs.sh -m server -h $CERT_HOSTNAME -pw $CREATE_CERTS_WITH_PW -t $CERTS_DIR -e 365
10+
echo "Create client cert"
11+
/script/create-certs.sh -m client -h testClient -pw $CREATE_CERTS_WITH_PW -t $CERTS_DIR -e 365
12+
13+
mkdir $CERTS_DIR/client
14+
mv $CERTS_DIR/ca.pem $CERTS_DIR/ca-cert.pem
15+
cp $CERTS_DIR/ca-cert.pem $CERTS_DIR/client/ca.pem
16+
mv $CERTS_DIR/client-testClient-cert.pem $CERTS_DIR/client/cert.pem
17+
mv $CERTS_DIR/client-testClient-key.pem $CERTS_DIR/client/key.pem
18+
chmod 444 $CERTS_DIR/client/key.pem
19+
20+
else
21+
22+
echo "$CERTS_DIR is not empty. Not creating certs."
23+
fi
24+
fi
25+
26+
exec "$@"

resources/nginx-cert.conf

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
server {
2-
client_max_body_size 0;
3-
listen 443;
4-
ssl on;
5-
server_name localhost;
6-
7-
ssl_certificate /data/certs/server-cert.pem;
8-
ssl_certificate_key /data/certs/server-key.pem;
1+
user root;
2+
worker_processes 1;
3+
4+
error_log /var/log/nginx/error.log warn;
5+
pid /var/run/nginx.pid;
6+
7+
8+
events {
9+
worker_connections 1024;
10+
}
11+
12+
13+
stream {
14+
server {
15+
listen 443 ssl;
16+
17+
ssl_certificate /data/certs/server-cert.pem;
18+
ssl_certificate_key /data/certs/server-key.pem;
919
ssl_client_certificate /data/certs/ca-cert.pem;
1020
ssl_verify_client on;
11-
12-
location / {
13-
proxy_pass http://unix:/var/run/docker.sock:/;
14-
}
21+
22+
proxy_pass unix:/var/run/docker.sock;
23+
}
1524
}

test/localConnect.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/sh
2+
3+
# call this file with source localConnect.sh
4+
5+
unset DOCKER_HOST
6+
unset DOCKER_TLS_VERIFY
7+
unset DOCKER_CERT_PATH

test/remoteConnect.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/sh
2+
3+
# call this file with source remoteConnect.sh
4+
5+
export DOCKER_HOST=abc.127.0.0.1.nip.io:8443
6+
export DOCKER_TLS_VERIFY=1
7+
export DOCKER_CERT_PATH=$(pwd)/certs/client

0 commit comments

Comments
 (0)