Skip to content

Commit 02121df

Browse files
authored
Merge pull request nginx-proxy#589 from kamermans/feature_ssl_improvement
SSL security enhancement
2 parents 14779ec + 026ba7c commit 02121df

40 files changed

+327
-48
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
**/__pycache__/
22
**/.cache/
3+
.idea/

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ RUN apt-get update \
99
&& apt-get clean \
1010
&& rm -r /var/lib/apt/lists/*
1111

12+
1213
# Configure Nginx and apply fix for very long server names
1314
RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
14-
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \
1515
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
1616

1717
# Install Forego
@@ -29,7 +29,7 @@ WORKDIR /app/
2929

3030
ENV DOCKER_HOST unix:///tmp/docker.sock
3131

32-
VOLUME ["/etc/nginx/certs"]
32+
VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"]
3333

3434
ENTRYPOINT ["/app/docker-entrypoint.sh"]
3535
CMD ["forego", "start", "-r"]

Dockerfile.alpine

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ MAINTAINER Jason Wilder [email protected]
33

44
# Install wget and install/updates certificates
55
RUN apk add --no-cache --virtual .run-deps \
6-
ca-certificates bash wget \
6+
ca-certificates bash wget openssl \
77
&& update-ca-certificates
88

9+
910
# Configure Nginx and apply fix for very long server names
1011
RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
11-
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \
1212
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
1313

1414
# Install Forego
@@ -26,7 +26,7 @@ WORKDIR /app/
2626

2727
ENV DOCKER_HOST unix:///tmp/docker.sock
2828

29-
VOLUME ["/etc/nginx/certs"]
29+
VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"]
3030

3131
ENTRYPOINT ["/app/docker-entrypoint.sh"]
3232
CMD ["forego", "start", "-r"]

README.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ In this example, the `my-nginx-proxy` container will be connected to `my-network
100100

101101
If you would like the reverse proxy to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container.
102102

103+
> Note: If you use `VIRTUAL_PROTO=https` and your backend container exposes port 80 and 443, `nginx-proxy` will use HTTPS on port 80. This is almost certainly not what you want, so you should also include `VIRTUAL_PORT=443`.
104+
103105
### uWSGI Backends
104106

105107
If you would like to connect to uWSGI backend, set `VIRTUAL_PROTO=uwsgi` on the
@@ -171,9 +173,21 @@ By default, Docker is not able to mount directories on the host machine to conta
171173

172174
#### Diffie-Hellman Groups
173175

174-
If you have Diffie-Hellman groups enabled, the files should be named after the virtual host with a
176+
Diffie-Hellman groups are enabled by default, with a pregenerated key in `/etc/nginx/dhparam/dhparam.pem`.
177+
You can mount a different `dhparam.pem` file at that location to override the default cert.
178+
To use custom `dhparam.pem` files per-virtual-host, the files should be named after the virtual host with a
175179
`dhparam` suffix and `.pem` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com`
176-
should have a `foo.bar.com.dhparam.pem` file in the certs directory.
180+
should have a `foo.bar.com.dhparam.pem` file in the `/etc/nginx/certs` directory.
181+
182+
> NOTE: If you don't mount a `dhparam.pem` file at `/etc/nginx/dhparam/dhparam.pem`, one will be generated
183+
at startup. Since it can take minutes to generate a new `dhparam.pem`, it is done at low priority in the
184+
background. Once generation is complete, the `dhparams.pem` is saved on a persistent volume and nginx
185+
is reloaded. This generation process only occurs the first time you start `nginx-proxy`.
186+
187+
> COMPATIBILITY WARNING: The default generated `dhparam.pem` key is 2048 bits for A+ security. Some
188+
> older clients (like Java 6 and 7) do not support DH keys with over 1024 bits. In order to support these
189+
> clients, you must either provide your own `dhparam.pem`, or tell `nginx-proxy` to generate a 1024-bit
190+
> key on startup by passing `-e DHPARAM_BITS=1024`.
177191
178192
#### Wildcard Certificates
179193

@@ -189,10 +203,13 @@ and `CERT_NAME=shared` will then use this shared cert.
189203

190204
#### How SSL Support Works
191205

192-
The SSL cipher configuration is based on [mozilla nginx intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) which
206+
The SSL cipher configuration is based on the [Mozilla nginx intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) which
193207
should provide compatibility with clients back to Firefox 1, Chrome 1, IE 7, Opera 5, Safari 1,
194-
Windows XP IE8, Android 2.3, Java 7. The configuration also enables HSTS, and SSL
195-
session caches.
208+
Windows XP IE8, Android 2.3, Java 7. Note that the DES-based TLS ciphers were removed for security.
209+
The configuration also enables HSTS, PFS, and SSL session caches. Currently TLS 1.0, 1.1 and 1.2
210+
are supported. TLS 1.0 is deprecated but its end of life is not until June 30, 2018. It is being
211+
included because the following browsers will stop working when it is removed: Chrome < 22, Firefox < 27,
212+
IE < 11, Safari < 7, iOS < 5, Android Browser < 5.
196213

197214
The default behavior for the proxy when port 80 and 443 are exposed is as follows:
198215

dhparam.pem.default

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN DH PARAMETERS-----
2+
MIIBCAKCAQEAzB2nIGzpVq7afJnKBm1X0d64avwOlP2oneiKwxRHdDI/5+6TpH1P
3+
F8ipodGuZBUMmupoB3D34pu2Qq5boNW983sm18ww9LMz2i/pxhSdB+mYAew+A6h6
4+
ltQ5pNtyn4NaKw1SDFkqvde3GNPhaWoPDbZDJhpHGblR3w1b/ag+lTLZUvVwcD8L
5+
jYS9f9YWAC6T7WxAxh4zvu1Z0I1EKde8KYBxrreZNheXpXHqMNyJYZCaY2Hb/4oI
6+
EL65qZq1GCWezpWMjhk6pOnV5gbvqfhoazCv/4OdRv6RoWOIYBNs9BmGho4AtXqV
7+
FYLdYDhOvN4aVs9Ir+G8ouwiRnix24+UewIBAg==
8+
-----END DH PARAMETERS-----

docker-entrypoint.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -e
33

44
# Warn if the DOCKER_HOST socket does not exist
5-
if [[ $DOCKER_HOST == unix://* ]]; then
5+
if [[ $DOCKER_HOST = unix://* ]]; then
66
socket_file=${DOCKER_HOST#unix://}
77
if ! [ -S $socket_file ]; then
88
cat >&2 <<-EOT
@@ -14,6 +14,10 @@ if [[ $DOCKER_HOST == unix://* ]]; then
1414
fi
1515
fi
1616

17+
# Generate dhparam file if required
18+
# Note: if $DHPARAM_BITS is not defined, generate-dhparam.sh will use 2048 as a default
19+
/app/generate-dhparam.sh $DHPARAM_BITS
20+
1721
# If the user has run the default command and the socket doesn't exist, fail
1822
if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then
1923
exit 1

generate-dhparam.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/bash -e
2+
3+
# The first argument is the bit depth of the dhparam, or 2048 if unspecified
4+
DHPARAM_BITS=${1:-2048}
5+
6+
# If a dhparam file is not available, use the pre-generated one and generate a new one in the background.
7+
# Note that /etc/nginx/dhparam is a volume, so this dhparam will persist restarts.
8+
PREGEN_DHPARAM_FILE="/app/dhparam.pem.default"
9+
DHPARAM_FILE="/etc/nginx/dhparam/dhparam.pem"
10+
GEN_LOCKFILE="/tmp/dhparam_generating.lock"
11+
12+
# The hash of the pregenerated dhparam file is used to check if the pregen dhparam is already in use
13+
PREGEN_HASH=$(md5sum $PREGEN_DHPARAM_FILE | cut -d" " -f1)
14+
if [[ -f $DHPARAM_FILE ]]; then
15+
CURRENT_HASH=$(md5sum $DHPARAM_FILE | cut -d" " -f1)
16+
if [[ $PREGEN_HASH != $CURRENT_HASH ]]; then
17+
# There is already a dhparam, and it's not the default
18+
echo "Custom dhparam.pem file found, generation skipped"
19+
exit 0
20+
fi
21+
22+
if [[ -f $GEN_LOCKFILE ]]; then
23+
# Generation is already in progress
24+
exit 0
25+
fi
26+
fi
27+
28+
cat >&2 <<-EOT
29+
WARNING: $DHPARAM_FILE was not found. A pre-generated dhparam.pem will be used for now while a new one
30+
is being generated in the background. Once the new dhparam.pem is in place, nginx will be reloaded.
31+
EOT
32+
33+
# Put the default dhparam file in place so we can start immediately
34+
cp $PREGEN_DHPARAM_FILE $DHPARAM_FILE
35+
touch $GEN_LOCKFILE
36+
37+
# Generate a new dhparam in the background in a low priority and reload nginx when finished (grep removes the progress indicator).
38+
(
39+
(
40+
nice -n +5 openssl dhparam -out $DHPARAM_FILE $DHPARAM_BITS 2>&1 \
41+
&& echo "dhparam generation complete, reloading nginx" \
42+
&& nginx -s reload
43+
) | grep -vE '^[\.+]+'
44+
rm $GEN_LOCKFILE
45+
) &disown

nginx.tmpl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ map $http_upgrade $proxy_connection {
3838
'' close;
3939
}
4040

41+
# Apply fix for very long server names
42+
server_names_hash_bucket_size 128;
43+
44+
# Default dhparam
45+
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
46+
4147
# Set appropriate X-Forwarded-Ssl header
4248
map $scheme $proxy_x_forwarded_ssl {
4349
default off;
@@ -178,7 +184,7 @@ server {
178184
access_log /var/log/nginx/access.log vhost;
179185

180186
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
181-
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
187+
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:!DSS';
182188

183189
ssl_prefer_server_ciphers on;
184190
ssl_session_timeout 5m;

test/lib/ssl/dhparam.pem

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN DH PARAMETERS-----
2+
MIIBCAKCAQEA1cae6HqPSgicEuAuSCf6Ii3d6qMX9Ta8lnwoX0JQ0CWK7mzaiiIi
3+
dY7oHmc4cq0S3SH+g0tdLP9yqygFS9hdUGINwS2VV6poj2/vdL/dUshegyxpEH58
4+
nofCPnFDeKkcPDMYAlGS8zjp60TsBkRJKcrxxwnjod1Q5mWuMN5KH3sxs842udKH
5+
0nHFE9kKW/NfXb+EGsjpocGpf786cGuCO2d00THsoItOEcM9/aI8DX1QcyxAHR6D
6+
HaYTFJnyyx8Q44u27M15idI4pbNoKORlotiuOwCTGYCfbN14aOV+Ict7aSF8FWpP
7+
48j9SMNuIu2DlF9pNLo6fsrOjYY3c9X12wIBAg==
8+
-----END DH PARAMETERS-----
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
FROM python:2.7
1+
FROM python:2.7-alpine
2+
3+
# Note: we're using alpine because it has openssl 1.0.2, which we need for testing
4+
RUN apk add --update bash openssl curl && rm -rf /var/cache/apk/*
5+
26
COPY python-requirements.txt /requirements.txt
37
RUN pip install -r /requirements.txt
8+
49
WORKDIR /test
510
ENTRYPOINT ["pytest"]

test/test_DOCKER_HOST_unix_socket.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
web1:
2-
image: web
2+
image: web
33
expose:
44
- "81"
55
environment:
@@ -8,7 +8,7 @@ web1:
88

99
web2:
1010
image: web
11-
expose:
11+
expose:
1212
- "82"
1313
environment:
1414
WEB_PORTS: 82
@@ -19,6 +19,6 @@ sut:
1919
image: jwilder/nginx-proxy:test
2020
volumes:
2121
- /var/run/docker.sock:/f00.sock:ro
22+
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
2223
environment:
2324
DOCKER_HOST: unix:///f00.sock
24-

test/test_composev2.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ services:
44
image: jwilder/nginx-proxy:test
55
volumes:
66
- /var/run/docker.sock:/tmp/docker.sock:ro
7+
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
78

89
web:
9-
image: web
10+
image: web
1011
expose:
1112
- "81"
1213
environment:
1314
WEB_PORTS: 81
14-
VIRTUAL_HOST: web.nginx-proxy.local
15+
VIRTUAL_HOST: web.nginx-proxy.local

test/test_custom/test_defaults-location.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,30 @@ nginx-proxy:
22
image: jwilder/nginx-proxy:test
33
volumes:
44
- /var/run/docker.sock:/tmp/docker.sock:ro
5+
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
56
- ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/default_location:ro
67
- ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.local_location:ro
78

89
web1:
9-
image: web
10+
image: web
1011
expose:
1112
- "81"
1213
environment:
1314
WEB_PORTS: 81
1415
VIRTUAL_HOST: web1.nginx-proxy.local
1516

1617
web2:
17-
image: web
18+
image: web
1819
expose:
1920
- "82"
2021
environment:
2122
WEB_PORTS: 82
2223
VIRTUAL_HOST: web2.nginx-proxy.local
2324

2425
web3:
25-
image: web
26+
image: web
2627
expose:
2728
- "83"
2829
environment:
2930
WEB_PORTS: 83
30-
VIRTUAL_HOST: web3.nginx-proxy.local
31+
VIRTUAL_HOST: web3.nginx-proxy.local

test/test_custom/test_defaults.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ services:
44
image: jwilder/nginx-proxy:test
55
volumes:
66
- /var/run/docker.sock:/tmp/docker.sock:ro
7+
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
78
- ./my_custom_proxy_settings.conf:/etc/nginx/proxy.conf:ro
89

910
web1:
10-
image: web
11+
image: web
1112
expose:
1213
- "81"
1314
environment:
1415
WEB_PORTS: 81
1516
VIRTUAL_HOST: web1.nginx-proxy.local
1617

1718
web2:
18-
image: web
19+
image: web
1920
expose:
2021
- "82"
2122
environment:
2223
WEB_PORTS: 82
23-
VIRTUAL_HOST: web2.nginx-proxy.local
24+
VIRTUAL_HOST: web2.nginx-proxy.local

test/test_custom/test_location-per-vhost.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ services:
44
image: jwilder/nginx-proxy:test
55
volumes:
66
- /var/run/docker.sock:/tmp/docker.sock:ro
7+
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
78
- ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local_location:ro
89

910
web1:
10-
image: web
11+
image: web
1112
expose:
1213
- "81"
1314
environment:
1415
WEB_PORTS: 81
1516
VIRTUAL_HOST: web1.nginx-proxy.local
1617

1718
web2:
18-
image: web
19+
image: web
1920
expose:
2021
- "82"
2122
environment:
2223
WEB_PORTS: 82
23-
VIRTUAL_HOST: web2.nginx-proxy.local
24+
VIRTUAL_HOST: web2.nginx-proxy.local

test/test_custom/test_per-vhost.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ services:
44
image: jwilder/nginx-proxy:test
55
volumes:
66
- /var/run/docker.sock:/tmp/docker.sock:ro
7+
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
78
- ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local:ro
89

910
web1:
10-
image: web
11+
image: web
1112
expose:
1213
- "81"
1314
environment:
1415
WEB_PORTS: 81
1516
VIRTUAL_HOST: web1.nginx-proxy.local
1617

1718
web2:
18-
image: web
19+
image: web
1920
expose:
2021
- "82"
2122
environment:
2223
WEB_PORTS: 82
23-
VIRTUAL_HOST: web2.nginx-proxy.local
24+
VIRTUAL_HOST: web2.nginx-proxy.local

test/test_custom/test_proxy-wide.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ services:
44
image: jwilder/nginx-proxy:test
55
volumes:
66
- /var/run/docker.sock:/tmp/docker.sock:ro
7+
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
78
- ./my_custom_proxy_settings.conf:/etc/nginx/conf.d/my_custom_proxy_settings.conf:ro
89

910
web1:
10-
image: web
11+
image: web
1112
expose:
1213
- "81"
1314
environment:
1415
WEB_PORTS: 81
1516
VIRTUAL_HOST: web1.nginx-proxy.local
1617

1718
web2:
18-
image: web
19+
image: web
1920
expose:
2021
- "82"
2122
environment:
2223
WEB_PORTS: 82
23-
VIRTUAL_HOST: web2.nginx-proxy.local
24+
VIRTUAL_HOST: web2.nginx-proxy.local

0 commit comments

Comments
 (0)