From 0b1e9e56e1f01c60c78688bab37e6d05c3b80b83 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 29 Sep 2016 16:47:47 -0400 Subject: [PATCH 01/23] Issue #535 Added default 2048-bit dhparam.pem file --- Dockerfile | 7 ++++--- README.md | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6d5ce9b..ad9a159 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,9 +9,10 @@ RUN apt-get update \ && apt-get clean \ && rm -r /var/lib/apt/lists/* -# Configure Nginx and apply fix for very long server names -RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ - && sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf +# Generate dhparam.pem, configure Nginx, apply fix for very long server names +RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048 \ + && echo "daemon off;" >> /etc/nginx/nginx.conf \ + && sed -i 's|^http {|&\n server_names_hash_bucket_size 128; ssl_dhparam /etc/nginx/dhparam.pem;|g' /etc/nginx/nginx.conf # Install Forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego diff --git a/README.md b/README.md index 52ab6e4..0a7593d 100644 --- a/README.md +++ b/README.md @@ -142,9 +142,11 @@ hosts in use. The certificate and keys should be named after the virtual host w #### Diffie-Hellman Groups -If you have Diffie-Hellman groups enabled, the files should be named after the virtual host with a +Diffie-Hellman groups are enabled by default, with a pregenerated key in `/etc/nginx/dhparam.pem`. +You can mount a different `dhparam.pem` file at that location to override the default cert. +To use custom `dhparam.pem` files per-virtual-host, the files should be named after the virtual host with a `dhparam` suffix and `.pem` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` -should have a `foo.bar.com.dhparam.pem` file in the certs directory. +should have a `foo.bar.com.dhparam.pem` file in the `/etc/nginx/certs` directory. #### Wildcard Certificates From 6f2b3f1c54b7a4abcd2f89305ede04b183587b6c Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 29 Sep 2016 17:10:17 -0400 Subject: [PATCH 02/23] Issue #586 Removed DES-based SSL ciphers --- nginx.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.tmpl b/nginx.tmpl index 9eb9520..b6629f5 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -144,7 +144,7 @@ server { access_log /var/log/nginx/access.log vhost; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 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'; + 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'; ssl_prefer_server_ciphers on; ssl_session_timeout 5m; From c51c9980cf8f36bac521c061e01a5b3cce984196 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 29 Sep 2016 19:52:20 -0400 Subject: [PATCH 03/23] Removed TLS 1.0 as it is considered unsafe and must be disabled for PCI compliance --- nginx.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.tmpl b/nginx.tmpl index b6629f5..626f508 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -143,7 +143,7 @@ server { listen 443 ssl http2 {{ $default_server }}; access_log /var/log/nginx/access.log vhost; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1.1 TLSv1.2; 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'; ssl_prefer_server_ciphers on; From d3a0da451abf2b73757cd9642c2df5abddc66b4c Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 29 Sep 2016 21:35:37 -0400 Subject: [PATCH 04/23] TLSv1 End-of-life pushed to June 30, 2018, rolled back for compatibility --- nginx.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.tmpl b/nginx.tmpl index 626f508..b6629f5 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -143,7 +143,7 @@ server { listen 443 ssl http2 {{ $default_server }}; access_log /var/log/nginx/access.log vhost; - ssl_protocols TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 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'; ssl_prefer_server_ciphers on; From ebbf7a7b74395e316d0c08267d4bacc2150e6395 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 29 Sep 2016 21:57:28 -0400 Subject: [PATCH 05/23] Expanded documentation in SSL/TLS support --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a7593d..c4a629e 100644 --- a/README.md +++ b/README.md @@ -162,10 +162,13 @@ and `CERT_NAME=shared` will then use this shared cert. #### How SSL Support Works -The SSL cipher configuration is based on [mozilla nginx intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) which +The SSL cipher configuration is based on the [Mozilla nginx intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) which should provide compatibility with clients back to Firefox 1, Chrome 1, IE 7, Opera 5, Safari 1, -Windows XP IE8, Android 2.3, Java 7. The configuration also enables HSTS, and SSL -session caches. +Windows XP IE8, Android 2.3, Java 7. Note that the DES-based TLS ciphers were removed for security. +The configuration also enables HSTS, PFS, and SSL session caches. Currently TLS 1.0, 1.1 and 1.2 +are supported. TLS 1.0 is deprecated but its end of life is not until June 30, 2018. It is being +included because the following browsers will stop working when it is removed: Chrome < 22, Firefox < 27, +IE < 11, Safari < 7, iOS < 5, Android Browser < 5. The default behavior for the proxy when port 80 and 443 are exposed is as follows: From c091d08fee673670218e77b89bc40119ff0be368 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 29 Sep 2016 22:24:06 -0400 Subject: [PATCH 06/23] Updated docs for issue #562 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c4a629e..919d762 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ In this example, the `my-nginx-proxy` container will be connected to `my-network If you would like to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container. +> 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`. + ### uWSGI Backends If you would like to connect to uWSGI backend, set `VIRTUAL_PROTO=uwsgi` on the From 8534185b0cac3e172a2296a1a19bd94da3271307 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Mon, 3 Oct 2016 10:05:55 -0400 Subject: [PATCH 07/23] Added newline to config --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ad9a159..0eb78d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN apt-get update \ # Generate dhparam.pem, configure Nginx, apply fix for very long server names RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048 \ && echo "daemon off;" >> /etc/nginx/nginx.conf \ - && sed -i 's|^http {|&\n server_names_hash_bucket_size 128; ssl_dhparam /etc/nginx/dhparam.pem;|g' /etc/nginx/nginx.conf + && sed -i 's|^http {|&\n server_names_hash_bucket_size 128;\n ssl_dhparam /etc/nginx/dhparam.pem;|g' /etc/nginx/nginx.conf # Install Forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego From b0de80d46b63f353a198e2110aa2b03ad26e691d Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Mon, 3 Oct 2016 10:21:31 -0400 Subject: [PATCH 08/23] Moved config edits from Dockerfile to template --- Dockerfile | 5 ++--- nginx.tmpl | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0eb78d6..0a1df5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,10 +9,9 @@ RUN apt-get update \ && apt-get clean \ && rm -r /var/lib/apt/lists/* -# Generate dhparam.pem, configure Nginx, apply fix for very long server names +# Generate dhparam.pem, configure nginx RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048 \ - && echo "daemon off;" >> /etc/nginx/nginx.conf \ - && sed -i 's|^http {|&\n server_names_hash_bucket_size 128;\n ssl_dhparam /etc/nginx/dhparam.pem;|g' /etc/nginx/nginx.conf + && echo "daemon off;" >> /etc/nginx/nginx.conf # Install Forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego diff --git a/nginx.tmpl b/nginx.tmpl index b6629f5..779d217 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -31,6 +31,12 @@ map $http_upgrade $proxy_connection { '' close; } +# Apply fix for very long server names +server_names_hash_bucket_size 128; + +# Default dhparam +ssl_dhparam /etc/nginx/dhparam.pem; + gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; log_format vhost '$host $remote_addr - $remote_user [$time_local] ' From dfdd67f5a4606d8c9269d71b14d37f04cde947dd Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 11 Jan 2017 22:39:04 -0500 Subject: [PATCH 09/23] Implemented background dhparam generation --- Dockerfile | 7 +++---- dhparam.pem.default | 8 ++++++++ docker-entrypoint.sh | 5 ++++- generate-dhparam.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ nginx.tmpl | 2 +- 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 dhparam.pem.default create mode 100755 generate-dhparam.sh diff --git a/Dockerfile b/Dockerfile index 228254f..786db25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,9 +9,8 @@ RUN apt-get update \ && apt-get clean \ && rm -r /var/lib/apt/lists/* -# Generate dhparam.pem, configure nginx -RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048 \ - && echo "daemon off;" >> /etc/nginx/nginx.conf +# Configure nginx +RUN echo "daemon off;" >> /etc/nginx/nginx.conf # Install Forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego @@ -28,7 +27,7 @@ WORKDIR /app/ ENV DOCKER_HOST unix:///tmp/docker.sock -VOLUME ["/etc/nginx/certs"] +VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"] ENTRYPOINT ["/app/docker-entrypoint.sh"] CMD ["forego", "start", "-r"] diff --git a/dhparam.pem.default b/dhparam.pem.default new file mode 100644 index 0000000..8548c34 --- /dev/null +++ b/dhparam.pem.default @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAzB2nIGzpVq7afJnKBm1X0d64avwOlP2oneiKwxRHdDI/5+6TpH1P +F8ipodGuZBUMmupoB3D34pu2Qq5boNW983sm18ww9LMz2i/pxhSdB+mYAew+A6h6 +ltQ5pNtyn4NaKw1SDFkqvde3GNPhaWoPDbZDJhpHGblR3w1b/ag+lTLZUvVwcD8L +jYS9f9YWAC6T7WxAxh4zvu1Z0I1EKde8KYBxrreZNheXpXHqMNyJYZCaY2Hb/4oI +EL65qZq1GCWezpWMjhk6pOnV5gbvqfhoazCv/4OdRv6RoWOIYBNs9BmGho4AtXqV +FYLdYDhOvN4aVs9Ir+G8ouwiRnix24+UewIBAg== +-----END DH PARAMETERS----- diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 6353314..f43a127 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -2,7 +2,7 @@ set -e # Warn if the DOCKER_HOST socket does not exist -if [[ $DOCKER_HOST == unix://* ]]; then +if [[ $DOCKER_HOST = unix://* ]]; then socket_file=${DOCKER_HOST#unix://} if ! [ -S $socket_file ]; then cat >&2 <<-EOT @@ -14,6 +14,9 @@ if [[ $DOCKER_HOST == unix://* ]]; then fi fi +# Generate dhparam file if required +/app/generate-dhparam.sh + # If the user has run the default command and the socket doesn't exist, fail if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then exit 1 diff --git a/generate-dhparam.sh b/generate-dhparam.sh new file mode 100755 index 0000000..ed010b7 --- /dev/null +++ b/generate-dhparam.sh @@ -0,0 +1,42 @@ +#!/bin/bash -e + +# If a dhparam file is not available, use the pre-generated one and generate a new one in the background. +# Note that /etc/nginx/dhparam is a volume, so this dhparam will persist restarts. +PREGEN_DHPARAM_FILE="/app/dhparam.pem.default" +DHPARAM_FILE="/etc/nginx/dhparam/dhparam.pem" +DHPARAM_BITS="2048" +GEN_LOCKFILE="/tmp/dhparam_generating.lock" + +# The hash of the pregenerated dhparam file is used to check if the pregen dhparam is already in use +PREGEN_HASH=$(md5sum $PREGEN_DHPARAM_FILE | cut -d" " -f1) +if [[ -f $DHPARAM_FILE ]]; then + CURRENT_HASH=$(md5sum $DHPARAM_FILE | cut -d" " -f1) + if [[ $PREGEN_HASH != $CURRENT_HASH ]]; then + # There is already a dhparam, and it's not the default + exit 0 + fi + + if [[ -f $GEN_LOCKFILE ]]; then + # Generation is already in progress + exit 0 + fi +fi + +cat >&2 <<-EOT +WARNING: $DHPARAM_FILE was not found. A pregenerated dhparam.pem will be used for now while a new one +is being generated in the background. Once the new dhparam.pem is in place, nginx will be reloaded. +EOT + +# Put the default dhparam file in place so we can start immediately +cp $PREGEN_DHPARAM_FILE $DHPARAM_FILE +touch $GEN_LOCKFILE + +# Generate a new dhparam in the background in a low priority and reload nginx when finished (grep removes the progress indicator). +( + ( + nice -n +5 openssl dhparam -out $DHPARAM_FILE $DHPARAM_BITS 2>&1 \ + && echo "dhparam generation complete, reloading nginx" \ + && nginx -s reload + ) | grep -vE '^[\.+]+' + rm $GEN_LOCKFILE +) & diff --git a/nginx.tmpl b/nginx.tmpl index b99fa23..4f39ab2 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -42,7 +42,7 @@ map $http_upgrade $proxy_connection { server_names_hash_bucket_size 128; # Default dhparam -ssl_dhparam /etc/nginx/dhparam.pem; +ssl_dhparam /etc/nginx/dhparam/dhparam.pem; # Set appropriate X-Forwarded-Ssl header map $scheme $proxy_x_forwarded_ssl { From ebfe5e9c17b76d671a03ac6ef1b1ab17797c6954 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 11 Jan 2017 22:49:55 -0500 Subject: [PATCH 10/23] Added note about background generation --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b3e1702..e7d3f5f 100644 --- a/README.md +++ b/README.md @@ -151,12 +151,17 @@ By default, Docker is not able to mount directories on the host machine to conta #### Diffie-Hellman Groups -Diffie-Hellman groups are enabled by default, with a pregenerated key in `/etc/nginx/dhparam.pem`. +Diffie-Hellman groups are enabled by default, with a pregenerated key in `/etc/nginx/dhparam/dhparam.pem`. You can mount a different `dhparam.pem` file at that location to override the default cert. To use custom `dhparam.pem` files per-virtual-host, the files should be named after the virtual host with a `dhparam` suffix and `.pem` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` should have a `foo.bar.com.dhparam.pem` file in the `/etc/nginx/certs` directory. +> NOTE: If you don't mount a `dhparam.pem` file at `/etc/nginx/dhparam/dhparam.pem`, one will be generated +at startup. Since it can take minutes to generate a new `dhparam.pem`, it is done at low priority in the +background. Once generation is complete, the `dhparams.pem` is saved on a persistent volume and nginx +is reloaded. This generation process only occurs the first time you start `nginx-proxy`. + #### Wildcard Certificates Wildcard certificates and keys should be named after the domain name with a `.crt` and `.key` extension. From 6242403d33982f927ad72c28e84221864fba823e Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 11 Jan 2017 23:04:24 -0500 Subject: [PATCH 11/23] Added dhparam support for alpine variant --- Dockerfile.alpine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 4ce9561..ef43306 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -25,7 +25,7 @@ WORKDIR /app/ ENV DOCKER_HOST unix:///tmp/docker.sock -VOLUME ["/etc/nginx/certs"] +VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"] ENTRYPOINT ["/app/docker-entrypoint.sh"] CMD ["forego", "start", "-r"] From 7c0f7b944911d81cfe587de8e4ee53cac9ffdb2a Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 00:21:39 -0500 Subject: [PATCH 12/23] Fixed Alpine image, removed dhparams gen from test units --- Dockerfile.alpine | 5 ++--- generate-dhparam.sh | 2 +- test/lib/ssl/dhparam.pem | 8 ++++++++ test/test_helpers.bash | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 test/lib/ssl/dhparam.pem diff --git a/Dockerfile.alpine b/Dockerfile.alpine index ef43306..3d03cf3 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,12 +3,11 @@ MAINTAINER Jason Wilder mail@jasonwilder.com # Install wget and install/updates certificates RUN apk add --no-cache --virtual .run-deps \ - ca-certificates bash wget \ + ca-certificates bash wget openssl \ && update-ca-certificates # Configure Nginx and apply fix for very long server names -RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ - && sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf +RUN echo "daemon off;" >> /etc/nginx/nginx.conf # Install Forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego diff --git a/generate-dhparam.sh b/generate-dhparam.sh index ed010b7..36c4f8b 100755 --- a/generate-dhparam.sh +++ b/generate-dhparam.sh @@ -39,4 +39,4 @@ touch $GEN_LOCKFILE && nginx -s reload ) | grep -vE '^[\.+]+' rm $GEN_LOCKFILE -) & +) &disown diff --git a/test/lib/ssl/dhparam.pem b/test/lib/ssl/dhparam.pem new file mode 100644 index 0000000..eb3218c --- /dev/null +++ b/test/lib/ssl/dhparam.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA1cae6HqPSgicEuAuSCf6Ii3d6qMX9Ta8lnwoX0JQ0CWK7mzaiiIi +dY7oHmc4cq0S3SH+g0tdLP9yqygFS9hdUGINwS2VV6poj2/vdL/dUshegyxpEH58 +nofCPnFDeKkcPDMYAlGS8zjp60TsBkRJKcrxxwnjod1Q5mWuMN5KH3sxs842udKH +0nHFE9kKW/NfXb+EGsjpocGpf786cGuCO2d00THsoItOEcM9/aI8DX1QcyxAHR6D +HaYTFJnyyx8Q44u27M15idI4pbNoKORlotiuOwCTGYCfbN14aOV+Ict7aSF8FWpP +48j9SMNuIu2DlF9pNLo6fsrOjYY3c9X12wIBAg== +-----END DH PARAMETERS----- diff --git a/test/test_helpers.bash b/test/test_helpers.bash index 0fd9532..422df24 100644 --- a/test/test_helpers.bash +++ b/test/test_helpers.bash @@ -35,6 +35,7 @@ function nginxproxy { && docker run -d \ --label bats-type="nginx-proxy" \ --name $container_name \ + -v $DIR/lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro \ "$@" \ $SUT_IMAGE \ && wait_for_nginxproxy_container_to_start $container_name \ From f73a52afaf2fa4b26168827ed082dcf275633564 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 01:45:46 -0500 Subject: [PATCH 13/23] Added BATS tests for dhparam generation --- test/ssl.bats | 9 ++--- test/ssl_dhparam.bats | 85 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 test/ssl_dhparam.bats diff --git a/test/ssl.bats b/test/ssl.bats index e7e0eae..7ae8a78 100644 --- a/test/ssl.bats +++ b/test/ssl.bats @@ -61,13 +61,13 @@ function setup { prepare_web_container bats-ssl-hosts-4 "80 443" \ -e VIRTUAL_HOST=*.nginx-proxy.bats \ -e CERT_NAME=nginx-proxy.bats - dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-1 + dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-4 sleep 1 # THEN assert_301 test.nginx-proxy.bats assert_200_https test.nginx-proxy.bats - assert_output -p "Strict-Transport-Security: max-age=31536000" + assert_output -p "Strict-Transport-Security: max-age=31536000" } @test "[$TEST_FILE] test HTTPS_METHOD=noredirect disables Strict-Transport-Security" { @@ -76,16 +76,15 @@ function setup { -e VIRTUAL_HOST=*.nginx-proxy.bats \ -e CERT_NAME=nginx-proxy.bats \ -e HTTPS_METHOD=noredirect - dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-3 + dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-5 sleep 1 # THEN assert_200 test.nginx-proxy.bats assert_200_https test.nginx-proxy.bats - refute_output -p "Strict-Transport-Security: max-age=31536000" + refute_output -p "Strict-Transport-Security: max-age=31536000" } - @test "[$TEST_FILE] stop all bats containers" { stop_bats_containers } diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats new file mode 100644 index 0000000..0490a29 --- /dev/null +++ b/test/ssl_dhparam.bats @@ -0,0 +1,85 @@ +#!/usr/bin/env bats +load test_helpers + +function setup { + # make sure to stop any web container before each test so we don't + # have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set + stop_bats_containers web +} + +@test "[$TEST_FILE] test dhparam.pem is generated if missing (WARNING: this test is slow!):" { + SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1 + + # WHEN + run docker_clean $SUT_CONTAINER \ + && docker run -d \ + --label bats-type="nginx-proxy" \ + --name $SUT_CONTAINER \ + -v /var/run/docker.sock:/tmp/docker.sock:ro \ + $SUT_IMAGE \ + && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ + && docker logs $SUT_CONTAINER + + assert_success + docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" + + # THEN + run docker exec $SUT_CONTAINER ps aux + assert_output -p "openssl" + + DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem | cut -d" " -f1) + docker_wait_for_log $SUT_CONTAINER 240 "dhparam generation complete, reloading nginx" + + run docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem + refute_output -p $DEFAULT_HASH +} + +@test "[$TEST_FILE] test dhparam.pem is generated if default one is present" { + SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-2 + + # Copy the default dhparams to a volume and mount it in to ensure it's regenerated + TMP_DIR=/tmp/nginx-proxy-bats + if [ ! -d $TMP_DIR ]; then + mkdir $TMP_DIR + fi + cp $DIR/../dhparam.pem.default $TMP_DIR/dhparam.pem + + # WHEN + run docker_clean $SUT_CONTAINER \ + && docker run -d \ + --label bats-type="nginx-proxy" \ + --name $SUT_CONTAINER \ + -v /var/run/docker.sock:/tmp/docker.sock:ro \ + -v $TMP_DIR:/etc/nginx/dhparam \ + $SUT_IMAGE \ + && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ + && docker logs $SUT_CONTAINER + + docker logs $SUT_CONTAINER + + assert_success + docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" + + # THEN + run docker exec $SUT_CONTAINER ps aux + assert_output -p "openssl" + + docker exec $SUT_CONTAINER rm -rf /etc/nginx/dhparam/* +} + +@test "[$TEST_FILE] test dhparam.pem is not generated if custom one is present" { + SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-3 + + # WHEN + run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro + assert_success + docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" + + # THEN + run docker exec $SUT_CONTAINER ps aux + refute_output -p "openssl" +} + +@test "[$TEST_FILE] stop all bats containers" { + stop_bats_containers +} From c219822cd89b1f53d3e62a19b0106ac9ffaedefc Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 01:53:36 -0500 Subject: [PATCH 14/23] Typos --- generate-dhparam.sh | 2 +- test/ssl_dhparam.bats | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/generate-dhparam.sh b/generate-dhparam.sh index 36c4f8b..0097754 100755 --- a/generate-dhparam.sh +++ b/generate-dhparam.sh @@ -23,7 +23,7 @@ if [[ -f $DHPARAM_FILE ]]; then fi cat >&2 <<-EOT -WARNING: $DHPARAM_FILE was not found. A pregenerated dhparam.pem will be used for now while a new one +WARNING: $DHPARAM_FILE was not found. A pre-generated dhparam.pem will be used for now while a new one is being generated in the background. Once the new dhparam.pem is in place, nginx will be reloaded. EOT diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats index 0490a29..67fddb5 100644 --- a/test/ssl_dhparam.bats +++ b/test/ssl_dhparam.bats @@ -7,7 +7,7 @@ function setup { stop_bats_containers web } -@test "[$TEST_FILE] test dhparam.pem is generated if missing (WARNING: this test is slow!):" { +@test "[$TEST_FILE] test dhparam.pem is generated if missing (WARNING: this test is slow)" { SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1 # WHEN From dffc0c47cf15623376e5744e7feca9f7edd2f1d5 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 10:52:56 -0500 Subject: [PATCH 15/23] Tweak test for reliability on Travis-CI --- test/ssl_dhparam.bats | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats index 67fddb5..c5470a6 100644 --- a/test/ssl_dhparam.bats +++ b/test/ssl_dhparam.bats @@ -8,30 +8,28 @@ function setup { } @test "[$TEST_FILE] test dhparam.pem is generated if missing (WARNING: this test is slow)" { - SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1 + SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1 - # WHEN - run docker_clean $SUT_CONTAINER \ - && docker run -d \ - --label bats-type="nginx-proxy" \ - --name $SUT_CONTAINER \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ - $SUT_IMAGE \ - && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ - && docker logs $SUT_CONTAINER + # WHEN + run docker_clean $SUT_CONTAINER \ + && docker run -d \ + --label bats-type="nginx-proxy" \ + --name $SUT_CONTAINER \ + -v /var/run/docker.sock:/tmp/docker.sock:ro \ + $SUT_IMAGE \ + && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ + && docker logs $SUT_CONTAINER - assert_success - docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" + DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem | cut -d" " -f1) - # THEN - run docker exec $SUT_CONTAINER ps aux - assert_output -p "openssl" + assert_success + docker_wait_for_log $SUT_CONTAINER 9 "Generating DH parameters" - DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem | cut -d" " -f1) - docker_wait_for_log $SUT_CONTAINER 240 "dhparam generation complete, reloading nginx" + # THEN + docker_wait_for_log $SUT_CONTAINER 240 "dhparam generation complete, reloading nginx" - run docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem - refute_output -p $DEFAULT_HASH + run docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem + refute_output -p $DEFAULT_HASH } @test "[$TEST_FILE] test dhparam.pem is generated if default one is present" { @@ -42,6 +40,13 @@ function setup { if [ ! -d $TMP_DIR ]; then mkdir $TMP_DIR fi + + # If the previous test crashed, a dhparam is left that only root can delete, so we + # delete it from within a container as root + if [ -f $TMP_DIR/dhparam.pem ]; then + docker run --rm -v $TMP_DIR:/opt busybox rm /opt/dhparam.pem + fi + cp $DIR/../dhparam.pem.default $TMP_DIR/dhparam.pem # WHEN @@ -55,14 +60,9 @@ function setup { && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ && docker logs $SUT_CONTAINER - docker logs $SUT_CONTAINER - - assert_success - docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" - # THEN - run docker exec $SUT_CONTAINER ps aux - assert_output -p "openssl" + assert_success + docker_wait_for_log $SUT_CONTAINER 9 "Generating DH parameters" docker exec $SUT_CONTAINER rm -rf /etc/nginx/dhparam/* } From 7d253dd0f37ca99ef81fcd8bf86fd6b24c02d2c6 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 14:55:25 -0500 Subject: [PATCH 16/23] Allow passing DHPARAM_BITS via env, lower bits to 256 for unit tests --- docker-entrypoint.sh | 3 ++- generate-dhparam.sh | 4 +++- test/ssl_dhparam.bats | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index f43a127..a413877 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -15,7 +15,8 @@ if [[ $DOCKER_HOST = unix://* ]]; then fi # Generate dhparam file if required -/app/generate-dhparam.sh +# Note: if $DHPARAM_BITS is not defined, generate-dhparam.sh will use 2048 as a default +/app/generate-dhparam.sh $DHPARAM_BITS # If the user has run the default command and the socket doesn't exist, fail if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then diff --git a/generate-dhparam.sh b/generate-dhparam.sh index 0097754..9f5d4ff 100755 --- a/generate-dhparam.sh +++ b/generate-dhparam.sh @@ -1,10 +1,12 @@ #!/bin/bash -e +# The first argument is the bit depth of the dhparam, or 2048 if unspecified +DHPARAM_BITS=${1:-2048} + # If a dhparam file is not available, use the pre-generated one and generate a new one in the background. # Note that /etc/nginx/dhparam is a volume, so this dhparam will persist restarts. PREGEN_DHPARAM_FILE="/app/dhparam.pem.default" DHPARAM_FILE="/etc/nginx/dhparam/dhparam.pem" -DHPARAM_BITS="2048" GEN_LOCKFILE="/tmp/dhparam_generating.lock" # The hash of the pregenerated dhparam file is used to check if the pregen dhparam is already in use diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats index c5470a6..c193c5c 100644 --- a/test/ssl_dhparam.bats +++ b/test/ssl_dhparam.bats @@ -7,7 +7,7 @@ function setup { stop_bats_containers web } -@test "[$TEST_FILE] test dhparam.pem is generated if missing (WARNING: this test is slow)" { +@test "[$TEST_FILE] test dhparam.pem is generated if missing" { SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1 # WHEN @@ -16,6 +16,7 @@ function setup { --label bats-type="nginx-proxy" \ --name $SUT_CONTAINER \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ + -e DHPARAM=256 \ $SUT_IMAGE \ && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ && docker logs $SUT_CONTAINER @@ -23,7 +24,7 @@ function setup { DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem | cut -d" " -f1) assert_success - docker_wait_for_log $SUT_CONTAINER 9 "Generating DH parameters" + docker_wait_for_log $SUT_CONTAINER 30 "Generating DH parameters" # THEN docker_wait_for_log $SUT_CONTAINER 240 "dhparam generation complete, reloading nginx" @@ -56,13 +57,14 @@ function setup { --name $SUT_CONTAINER \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ -v $TMP_DIR:/etc/nginx/dhparam \ + -e DHPARAM=256 \ $SUT_IMAGE \ && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ && docker logs $SUT_CONTAINER # THEN assert_success - docker_wait_for_log $SUT_CONTAINER 9 "Generating DH parameters" + docker_wait_for_log $SUT_CONTAINER 30 "Generating DH parameters" docker exec $SUT_CONTAINER rm -rf /etc/nginx/dhparam/* } From b0de1f19d3d551af9304eb86720eb94233406e38 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 17:25:31 -0500 Subject: [PATCH 17/23] Updated to use 256-bit dhparam in tests --- test/ssl_dhparam.bats | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats index c193c5c..2d16595 100644 --- a/test/ssl_dhparam.bats +++ b/test/ssl_dhparam.bats @@ -16,12 +16,12 @@ function setup { --label bats-type="nginx-proxy" \ --name $SUT_CONTAINER \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ - -e DHPARAM=256 \ + -e DHPARAM_BITS=256 \ $SUT_IMAGE \ && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ && docker logs $SUT_CONTAINER - DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem | cut -d" " -f1) + DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /app/dhparam.pem.default | cut -d" " -f1) assert_success docker_wait_for_log $SUT_CONTAINER 30 "Generating DH parameters" @@ -29,7 +29,8 @@ function setup { # THEN docker_wait_for_log $SUT_CONTAINER 240 "dhparam generation complete, reloading nginx" - run docker exec $SUT_CONTAINER md5sum /etc/nginx/dhparam/dhparam.pem + run docker exec $SUT_CONTAINER su -c "md5sum /etc/nginx/dhparam/dhparam.pem" + refute_output -p $DEFAULT_HASH } @@ -57,7 +58,7 @@ function setup { --name $SUT_CONTAINER \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ -v $TMP_DIR:/etc/nginx/dhparam \ - -e DHPARAM=256 \ + -e DHPARAM_BITS=256 \ $SUT_IMAGE \ && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ && docker logs $SUT_CONTAINER From 83a28f47d77ffa90b99116682d10450dc9808e18 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Thu, 12 Jan 2017 17:43:13 -0500 Subject: [PATCH 18/23] Fixed long server name comment, improved dhparam check --- Dockerfile.alpine | 2 +- test/ssl_dhparam.bats | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 3d03cf3..25485d8 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -6,7 +6,7 @@ RUN apk add --no-cache --virtual .run-deps \ ca-certificates bash wget openssl \ && update-ca-certificates -# Configure Nginx and apply fix for very long server names +# Configure Nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf # Install Forego diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats index 2d16595..2ca8a08 100644 --- a/test/ssl_dhparam.bats +++ b/test/ssl_dhparam.bats @@ -78,9 +78,11 @@ function setup { assert_success docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" + sleep 3 + run docker logs $SUT_CONTAINER + # THEN - run docker exec $SUT_CONTAINER ps aux - refute_output -p "openssl" + refute_output -p "Generating DH parameters" } @test "[$TEST_FILE] stop all bats containers" { From 0244b4e71e4ca9bbd70a85d34bd4d0eb8fadffdf Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Tue, 7 Mar 2017 14:04:37 -0500 Subject: [PATCH 19/23] Added dhparams test in new format --- test/test_ssl/test_dhparam.py | 74 +++++++++++++++++++++++ test/test_ssl/test_dhparam.yml | 16 +++++ test/test_ssl/test_dhparam_generation.py | 54 +++++++++++++++++ test/test_ssl/test_dhparam_generation.yml | 7 +++ 4 files changed, 151 insertions(+) create mode 100644 test/test_ssl/test_dhparam.py create mode 100644 test/test_ssl/test_dhparam.yml create mode 100644 test/test_ssl/test_dhparam_generation.py create mode 100644 test/test_ssl/test_dhparam_generation.yml diff --git a/test/test_ssl/test_dhparam.py b/test/test_ssl/test_dhparam.py new file mode 100644 index 0000000..109432a --- /dev/null +++ b/test/test_ssl/test_dhparam.py @@ -0,0 +1,74 @@ +import pytest +import os +import docker +import time +import subprocess +import re + +docker_client = docker.from_env() + +def wait_for_nginxproxy_to_be_ready(): + """ + If one (and only one) container started from image jwilder/nginx-proxy:test is found, + wait for its log to contain substring "Watching docker events" + """ + containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) + if len(containers) != 1: + return + container = containers[0] + for line in container.logs(stream=True): + if "Watching docker events" in line: + break + +def test_dhparam_is_not_generated_if_present(docker_compose, nginxproxy): + wait_for_nginxproxy_to_be_ready() + + containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) + if len(containers) != 1: + assert 0 + return + + sut_container = containers[0] + + docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) + + assert "Custom dhparam.pem file found, generation skipped" in docker_logs + + # Make sure the dhparam in use is not the default, pre-generated one + default_checksum = sut_container.exec_run("md5sum /app/dhparam.pem.default").split() + current_checksum = sut_container.exec_run("md5sum /etc/nginx/dhparam/dhparam.pem").split() + assert default_checksum[0] != current_checksum[0] + +def test_web5_https_works(docker_compose, nginxproxy): + r = nginxproxy.get("https://web5.nginx-proxy.tld/port", allow_redirects=False) + assert r.status_code == 200 + assert "answer from port 85\n" in r.text + +def versiontuple(v): + clean_v = re.sub("[^\d\.]", "", v) + return tuple(map(int, (clean_v.split(".")))) + + +# This code checks that the required version of OpenSSL is present, and skips the test if not +openssl_version_required = "1.0.2" +openssl_version = "0.0.0" + +try: + openssl_version = subprocess.check_output(["openssl", "version"]).split()[1] +except: + pass + +@pytest.mark.skipif(versiontuple(openssl_version) < versiontuple(openssl_version_required), + reason="openssl command is not available in test environment or is less than version %s" % openssl_version_required) + +def test_web5_dhparam_is_used(docker_compose, nginxproxy): + containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) + if len(containers) != 1: + assert 0 + return + + sut_container = containers[0] + + host = "%s:443" % sut_container.attrs["NetworkSettings"]["IPAddress"] + r = subprocess.check_output("echo '' | openssl s_client -verify 0 -connect %s -cipher 'EDH' | grep 'Server Temp Key'" % host, shell=True) + assert "Server Temp Key: DH, 2048 bits" in r diff --git a/test/test_ssl/test_dhparam.yml b/test/test_ssl/test_dhparam.yml new file mode 100644 index 0000000..6e196b0 --- /dev/null +++ b/test/test_ssl/test_dhparam.yml @@ -0,0 +1,16 @@ +web5: + image: web + expose: + - "85" + environment: + WEB_PORTS: "85" + VIRTUAL_HOST: "web5.nginx-proxy.tld" + HTTPS_METHOD: nohttp + + +sut: + image: jwilder/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./certs:/etc/nginx/certs:ro diff --git a/test/test_ssl/test_dhparam_generation.py b/test/test_ssl/test_dhparam_generation.py new file mode 100644 index 0000000..87e71f1 --- /dev/null +++ b/test/test_ssl/test_dhparam_generation.py @@ -0,0 +1,54 @@ +import pytest +import os +import docker +import time + +docker_client = docker.from_env() + +def wait_for_nginxproxy_to_be_ready(): + """ + If one (and only one) container started from image jwilder/nginx-proxy:test is found, + wait for its log to contain substring "Watching docker events" + """ + containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) + if len(containers) != 1: + return + container = containers[0] + for line in container.logs(stream=True): + if "Watching docker events" in line: + break + +def test_dhparam_is_generated_if_missing(docker_compose, nginxproxy): + wait_for_nginxproxy_to_be_ready() + + containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) + if len(containers) != 1: + assert 0 + return + + sut_container = containers[0] + + docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) + + assert "Generating DH parameters" in docker_logs + + expected_line = "dhparam generation complete, reloading nginx" + max_wait = 30 + sleep_interval = 2 + current_wait = 0 + + while current_wait < max_wait: + docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) + if expected_line in docker_logs: + break + + time.sleep(sleep_interval) + current_wait += sleep_interval + + # Re-check the logs to get better assert output on failure + assert expected_line in docker_logs + + # Make sure the dhparam in use is not the default, pre-generated one + default_checksum = sut_container.exec_run("md5sum /app/dhparam.pem.default").split() + generated_checksum = sut_container.exec_run("md5sum /etc/nginx/dhparam/dhparam.pem").split() + assert default_checksum[0] != generated_checksum[0] diff --git a/test/test_ssl/test_dhparam_generation.yml b/test/test_ssl/test_dhparam_generation.yml new file mode 100644 index 0000000..f55cb95 --- /dev/null +++ b/test/test_ssl/test_dhparam_generation.yml @@ -0,0 +1,7 @@ +sut: + image: jwilder/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs:/etc/nginx/certs:ro + environment: + - DHPARAM_BITS=256 From 98b5828f837522e412ee6f2c341d5f31f1e65c35 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Tue, 7 Mar 2017 14:04:44 -0500 Subject: [PATCH 20/23] Modified tests to include dhparams --- .gitignore | 1 + generate-dhparam.sh | 1 + .../Dockerfile-nginx-proxy-tester | 7 +- test/ssl_dhparam.bats | 90 ------------------- test/test_DOCKER_HOST_unix_socket.yml | 6 +- test/test_composev2.yml | 5 +- test/test_custom/test_defaults-location.yml | 9 +- test/test_custom/test_defaults.yml | 7 +- test/test_custom/test_location-per-vhost.yml | 7 +- test/test_custom/test_per-vhost.yml | 7 +- test/test_custom/test_proxy-wide.yml | 7 +- test/test_default-host.yml | 1 + test/test_dockergen/test_dockergen_v2.yml | 3 +- test/test_dockergen/test_dockergen_v3.py | 5 +- test/test_dockergen/test_dockergen_v3.yml | 3 +- test/test_events.yml | 1 + test/test_headers/test_http.yml | 3 +- test/test_headers/test_https.yml | 1 + test/test_ipv6.yml | 5 +- test/test_multiple-hosts.yml | 1 + test/test_multiple-networks.yml | 7 +- .../test_multiple-ports/test_VIRTUAL_PORT.yml | 1 + test/test_multiple-ports/test_default-80.yml | 1 + .../test_single-port-not-80.yml | 3 +- test/test_nominal.yml | 5 +- test/test_ssl/test_nohttp.yml | 1 + test/test_ssl/test_nohttps.yml | 3 +- test/test_ssl/test_noredirect.yml | 1 + test/test_ssl/test_wildcard.yml | 1 + test/test_wildcard_host.yml | 3 +- 30 files changed, 69 insertions(+), 127 deletions(-) delete mode 100644 test/ssl_dhparam.bats diff --git a/.gitignore b/.gitignore index 0b3700d..5daab4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/__pycache__/ **/.cache/ +.idea/ diff --git a/generate-dhparam.sh b/generate-dhparam.sh index 9f5d4ff..3fdc77c 100755 --- a/generate-dhparam.sh +++ b/generate-dhparam.sh @@ -15,6 +15,7 @@ if [[ -f $DHPARAM_FILE ]]; then CURRENT_HASH=$(md5sum $DHPARAM_FILE | cut -d" " -f1) if [[ $PREGEN_HASH != $CURRENT_HASH ]]; then # There is already a dhparam, and it's not the default + echo "Custom dhparam.pem file found, generation skipped" exit 0 fi diff --git a/test/requirements/Dockerfile-nginx-proxy-tester b/test/requirements/Dockerfile-nginx-proxy-tester index b403ed7..27d0538 100644 --- a/test/requirements/Dockerfile-nginx-proxy-tester +++ b/test/requirements/Dockerfile-nginx-proxy-tester @@ -1,5 +1,10 @@ -FROM python:2.7 +FROM python:2.7-alpine + +# Note: we're using alpine because it has openssl 1.0.2, which we need for testing +RUN apk add --update bash openssl curl && rm -rf /var/cache/apk/* + COPY python-requirements.txt /requirements.txt RUN pip install -r /requirements.txt + WORKDIR /test ENTRYPOINT ["pytest"] diff --git a/test/ssl_dhparam.bats b/test/ssl_dhparam.bats deleted file mode 100644 index 2ca8a08..0000000 --- a/test/ssl_dhparam.bats +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bats -load test_helpers - -function setup { - # make sure to stop any web container before each test so we don't - # have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set - stop_bats_containers web -} - -@test "[$TEST_FILE] test dhparam.pem is generated if missing" { - SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1 - - # WHEN - run docker_clean $SUT_CONTAINER \ - && docker run -d \ - --label bats-type="nginx-proxy" \ - --name $SUT_CONTAINER \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ - -e DHPARAM_BITS=256 \ - $SUT_IMAGE \ - && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ - && docker logs $SUT_CONTAINER - - DEFAULT_HASH=$(docker exec $SUT_CONTAINER md5sum /app/dhparam.pem.default | cut -d" " -f1) - - assert_success - docker_wait_for_log $SUT_CONTAINER 30 "Generating DH parameters" - - # THEN - docker_wait_for_log $SUT_CONTAINER 240 "dhparam generation complete, reloading nginx" - - run docker exec $SUT_CONTAINER su -c "md5sum /etc/nginx/dhparam/dhparam.pem" - - refute_output -p $DEFAULT_HASH -} - -@test "[$TEST_FILE] test dhparam.pem is generated if default one is present" { - SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-2 - - # Copy the default dhparams to a volume and mount it in to ensure it's regenerated - TMP_DIR=/tmp/nginx-proxy-bats - if [ ! -d $TMP_DIR ]; then - mkdir $TMP_DIR - fi - - # If the previous test crashed, a dhparam is left that only root can delete, so we - # delete it from within a container as root - if [ -f $TMP_DIR/dhparam.pem ]; then - docker run --rm -v $TMP_DIR:/opt busybox rm /opt/dhparam.pem - fi - - cp $DIR/../dhparam.pem.default $TMP_DIR/dhparam.pem - - # WHEN - run docker_clean $SUT_CONTAINER \ - && docker run -d \ - --label bats-type="nginx-proxy" \ - --name $SUT_CONTAINER \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ - -v $TMP_DIR:/etc/nginx/dhparam \ - -e DHPARAM_BITS=256 \ - $SUT_IMAGE \ - && wait_for_nginxproxy_container_to_start $SUT_CONTAINER \ - && docker logs $SUT_CONTAINER - - # THEN - assert_success - docker_wait_for_log $SUT_CONTAINER 30 "Generating DH parameters" - - docker exec $SUT_CONTAINER rm -rf /etc/nginx/dhparam/* -} - -@test "[$TEST_FILE] test dhparam.pem is not generated if custom one is present" { - SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-3 - - # WHEN - run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro - assert_success - docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" - - sleep 3 - run docker logs $SUT_CONTAINER - - # THEN - refute_output -p "Generating DH parameters" -} - -@test "[$TEST_FILE] stop all bats containers" { - stop_bats_containers -} diff --git a/test/test_DOCKER_HOST_unix_socket.yml b/test/test_DOCKER_HOST_unix_socket.yml index 79b4baf..dff75a8 100644 --- a/test/test_DOCKER_HOST_unix_socket.yml +++ b/test/test_DOCKER_HOST_unix_socket.yml @@ -1,5 +1,5 @@ web1: - image: web + image: web expose: - "81" environment: @@ -8,7 +8,7 @@ web1: web2: image: web - expose: + expose: - "82" environment: WEB_PORTS: 82 @@ -19,6 +19,6 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/f00.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro environment: DOCKER_HOST: unix:///f00.sock - diff --git a/test/test_composev2.yml b/test/test_composev2.yml index 5ffaf57..ef4df8d 100644 --- a/test/test_composev2.yml +++ b/test/test_composev2.yml @@ -4,11 +4,12 @@ services: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro web: - image: web + image: web expose: - "81" environment: WEB_PORTS: 81 - VIRTUAL_HOST: web.nginx-proxy.local \ No newline at end of file + VIRTUAL_HOST: web.nginx-proxy.local diff --git a/test/test_custom/test_defaults-location.yml b/test/test_custom/test_defaults-location.yml index d2ac14a..a5b0c44 100644 --- a/test/test_custom/test_defaults-location.yml +++ b/test/test_custom/test_defaults-location.yml @@ -2,11 +2,12 @@ nginx-proxy: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/default_location:ro - ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.local_location:ro web1: - image: web + image: web expose: - "81" environment: @@ -14,7 +15,7 @@ web1: VIRTUAL_HOST: web1.nginx-proxy.local web2: - image: web + image: web expose: - "82" environment: @@ -22,9 +23,9 @@ web2: VIRTUAL_HOST: web2.nginx-proxy.local web3: - image: web + image: web expose: - "83" environment: WEB_PORTS: 83 - VIRTUAL_HOST: web3.nginx-proxy.local \ No newline at end of file + VIRTUAL_HOST: web3.nginx-proxy.local diff --git a/test/test_custom/test_defaults.yml b/test/test_custom/test_defaults.yml index 2b2f1bb..2cfddf0 100644 --- a/test/test_custom/test_defaults.yml +++ b/test/test_custom/test_defaults.yml @@ -4,10 +4,11 @@ services: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./my_custom_proxy_settings.conf:/etc/nginx/proxy.conf:ro web1: - image: web + image: web expose: - "81" environment: @@ -15,9 +16,9 @@ services: VIRTUAL_HOST: web1.nginx-proxy.local web2: - image: web + image: web expose: - "82" environment: WEB_PORTS: 82 - VIRTUAL_HOST: web2.nginx-proxy.local \ No newline at end of file + VIRTUAL_HOST: web2.nginx-proxy.local diff --git a/test/test_custom/test_location-per-vhost.yml b/test/test_custom/test_location-per-vhost.yml index 7ec9992..988181c 100644 --- a/test/test_custom/test_location-per-vhost.yml +++ b/test/test_custom/test_location-per-vhost.yml @@ -4,10 +4,11 @@ services: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local_location:ro web1: - image: web + image: web expose: - "81" environment: @@ -15,9 +16,9 @@ services: VIRTUAL_HOST: web1.nginx-proxy.local web2: - image: web + image: web expose: - "82" environment: WEB_PORTS: 82 - VIRTUAL_HOST: web2.nginx-proxy.local \ No newline at end of file + VIRTUAL_HOST: web2.nginx-proxy.local diff --git a/test/test_custom/test_per-vhost.yml b/test/test_custom/test_per-vhost.yml index a99da1d..61ae02b 100644 --- a/test/test_custom/test_per-vhost.yml +++ b/test/test_custom/test_per-vhost.yml @@ -4,10 +4,11 @@ services: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local:ro web1: - image: web + image: web expose: - "81" environment: @@ -15,9 +16,9 @@ services: VIRTUAL_HOST: web1.nginx-proxy.local web2: - image: web + image: web expose: - "82" environment: WEB_PORTS: 82 - VIRTUAL_HOST: web2.nginx-proxy.local \ No newline at end of file + VIRTUAL_HOST: web2.nginx-proxy.local diff --git a/test/test_custom/test_proxy-wide.yml b/test/test_custom/test_proxy-wide.yml index 3018131..602f344 100644 --- a/test/test_custom/test_proxy-wide.yml +++ b/test/test_custom/test_proxy-wide.yml @@ -4,10 +4,11 @@ services: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./my_custom_proxy_settings.conf:/etc/nginx/conf.d/my_custom_proxy_settings.conf:ro web1: - image: web + image: web expose: - "81" environment: @@ -15,9 +16,9 @@ services: VIRTUAL_HOST: web1.nginx-proxy.local web2: - image: web + image: web expose: - "82" environment: WEB_PORTS: 82 - VIRTUAL_HOST: web2.nginx-proxy.local \ No newline at end of file + VIRTUAL_HOST: web2.nginx-proxy.local diff --git a/test/test_default-host.yml b/test/test_default-host.yml index 590dcaa..f195f58 100644 --- a/test/test_default-host.yml +++ b/test/test_default-host.yml @@ -13,5 +13,6 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro environment: DEFAULT_HOST: web1.tld diff --git a/test/test_dockergen/test_dockergen_v2.yml b/test/test_dockergen/test_dockergen_v2.yml index 0d2cab0..0fc8af5 100644 --- a/test/test_dockergen/test_dockergen_v2.yml +++ b/test/test_dockergen/test_dockergen_v2.yml @@ -6,6 +6,7 @@ services: container_name: nginx volumes: - /etc/nginx/conf.d + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro dockergen: image: jwilder/docker-gen @@ -23,4 +24,4 @@ services: - "80" environment: WEB_PORTS: 80 - VIRTUAL_HOST: whoami.nginx.container.docker \ No newline at end of file + VIRTUAL_HOST: whoami.nginx.container.docker diff --git a/test/test_dockergen/test_dockergen_v3.py b/test/test_dockergen/test_dockergen_v3.py index 325d6db..7e1f58c 100644 --- a/test/test_dockergen/test_dockergen_v3.py +++ b/test/test_dockergen/test_dockergen_v3.py @@ -2,12 +2,13 @@ import os import docker import logging import pytest - +import re def versiontuple(v): + # Temporary hack to fix version parsing until PR#755 is pulled + v = re.sub("[^\d\.]", "", v) return tuple(map(int, (v.split(".")))) - docker_version = docker.from_env().version()['Version'] pytestmark = pytest.mark.skipif(versiontuple(docker_version) < versiontuple('1.13'), reason="Docker compose syntax v3 requires docker engine v1.13") diff --git a/test/test_dockergen/test_dockergen_v3.yml b/test/test_dockergen/test_dockergen_v3.yml index 643f49b..fad145a 100644 --- a/test/test_dockergen/test_dockergen_v3.yml +++ b/test/test_dockergen/test_dockergen_v3.yml @@ -5,6 +5,7 @@ services: container_name: nginx volumes: - nginx_conf:/etc/nginx/conf.d + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro dockergen: image: jwilder/docker-gen @@ -24,4 +25,4 @@ services: VIRTUAL_HOST: whoami.nginx.container.docker volumes: - nginx_conf: {} \ No newline at end of file + nginx_conf: {} diff --git a/test/test_events.yml b/test/test_events.yml index d534870..87b7c01 100644 --- a/test/test_events.yml +++ b/test/test_events.yml @@ -2,3 +2,4 @@ nginxproxy: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_headers/test_http.yml b/test/test_headers/test_http.yml index e3596be..8cc2e09 100644 --- a/test/test_headers/test_http.yml +++ b/test/test_headers/test_http.yml @@ -10,4 +10,5 @@ web: sut: image: jwilder/nginx-proxy:test volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro \ No newline at end of file + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_headers/test_https.yml b/test/test_headers/test_https.yml index 8dc0744..131f61c 100644 --- a/test/test_headers/test_https.yml +++ b/test/test_headers/test_https.yml @@ -13,3 +13,4 @@ sut: - /var/run/docker.sock:/tmp/docker.sock:ro - ./certs/web.nginx-proxy.tld.crt:/etc/nginx/certs/web.nginx-proxy.tld.crt:ro - ./certs/web.nginx-proxy.tld.key:/etc/nginx/certs/web.nginx-proxy.tld.key:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_ipv6.yml b/test/test_ipv6.yml index c734660..a0b504e 100644 --- a/test/test_ipv6.yml +++ b/test/test_ipv6.yml @@ -1,5 +1,5 @@ web1: - image: web + image: web expose: - "81" environment: @@ -8,7 +8,7 @@ web1: web2: image: web - expose: + expose: - "82" environment: WEB_PORTS: 82 @@ -19,5 +19,6 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro environment: ENABLE_IPV6: "true" diff --git a/test/test_multiple-hosts.yml b/test/test_multiple-hosts.yml index 95dcb4a..70269c8 100644 --- a/test/test_multiple-hosts.yml +++ b/test/test_multiple-hosts.yml @@ -11,3 +11,4 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_multiple-networks.yml b/test/test_multiple-networks.yml index c04a292..da3277b 100644 --- a/test/test_multiple-networks.yml +++ b/test/test_multiple-networks.yml @@ -9,12 +9,13 @@ services: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro networks: - net1 - net2 web1: - image: web + image: web expose: - "81" environment: @@ -24,11 +25,11 @@ services: - net1 web2: - image: web + image: web expose: - "82" environment: WEB_PORTS: 82 VIRTUAL_HOST: web2.nginx-proxy.local networks: - - net2 \ No newline at end of file + - net2 diff --git a/test/test_multiple-ports/test_VIRTUAL_PORT.yml b/test/test_multiple-ports/test_VIRTUAL_PORT.yml index d61ac6f..4eb95ea 100644 --- a/test/test_multiple-ports/test_VIRTUAL_PORT.yml +++ b/test/test_multiple-ports/test_VIRTUAL_PORT.yml @@ -12,3 +12,4 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_multiple-ports/test_default-80.yml b/test/test_multiple-ports/test_default-80.yml index 74916a6..f06ccb8 100644 --- a/test/test_multiple-ports/test_default-80.yml +++ b/test/test_multiple-ports/test_default-80.yml @@ -11,3 +11,4 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_multiple-ports/test_single-port-not-80.yml b/test/test_multiple-ports/test_single-port-not-80.yml index 650dd07..15f230a 100644 --- a/test/test_multiple-ports/test_single-port-not-80.yml +++ b/test/test_multiple-ports/test_single-port-not-80.yml @@ -10,4 +10,5 @@ web: sut: image: jwilder/nginx-proxy:test volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro \ No newline at end of file + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_nominal.yml b/test/test_nominal.yml index 6a582a5..d436499 100644 --- a/test/test_nominal.yml +++ b/test/test_nominal.yml @@ -1,5 +1,5 @@ web1: - image: web + image: web expose: - "81" environment: @@ -8,7 +8,7 @@ web1: web2: image: web - expose: + expose: - "82" environment: WEB_PORTS: 82 @@ -19,3 +19,4 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_ssl/test_nohttp.yml b/test/test_ssl/test_nohttp.yml index 8486c5e..51d63c2 100644 --- a/test/test_ssl/test_nohttp.yml +++ b/test/test_ssl/test_nohttp.yml @@ -12,4 +12,5 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./certs:/etc/nginx/certs:ro diff --git a/test/test_ssl/test_nohttps.yml b/test/test_ssl/test_nohttps.yml index 2e7623a..14140b4 100644 --- a/test/test_ssl/test_nohttps.yml +++ b/test/test_ssl/test_nohttps.yml @@ -11,4 +11,5 @@ web: sut: image: jwilder/nginx-proxy:test volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro \ No newline at end of file + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_ssl/test_noredirect.yml b/test/test_ssl/test_noredirect.yml index 8d0b4b2..9149a87 100644 --- a/test/test_ssl/test_noredirect.yml +++ b/test/test_ssl/test_noredirect.yml @@ -12,4 +12,5 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./certs:/etc/nginx/certs:ro diff --git a/test/test_ssl/test_wildcard.yml b/test/test_ssl/test_wildcard.yml index 27840d3..4c77796 100644 --- a/test/test_ssl/test_wildcard.yml +++ b/test/test_ssl/test_wildcard.yml @@ -10,4 +10,5 @@ sut: image: jwilder/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./certs:/etc/nginx/certs:ro diff --git a/test/test_wildcard_host.yml b/test/test_wildcard_host.yml index a78ee3c..742a8ac 100644 --- a/test/test_wildcard_host.yml +++ b/test/test_wildcard_host.yml @@ -34,4 +34,5 @@ web4: sut: image: jwilder/nginx-proxy:test volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro \ No newline at end of file + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro From abdd5883a1cc5a6db496f6cb8b7016491b0b7e12 Mon Sep 17 00:00:00 2001 From: Thomas LEVEIL Date: Wed, 8 Mar 2017 02:37:12 +0100 Subject: [PATCH 21/23] TESTS: refactor dhparam tests --- test/test_ssl/test_dhparam.py | 123 +++++++++++++--------- test/test_ssl/test_dhparam.yml | 2 +- test/test_ssl/test_dhparam_generation.py | 66 +++++------- test/test_ssl/test_dhparam_generation.yml | 1 + 4 files changed, 101 insertions(+), 91 deletions(-) diff --git a/test/test_ssl/test_dhparam.py b/test/test_ssl/test_dhparam.py index 109432a..67b11fa 100644 --- a/test/test_ssl/test_dhparam.py +++ b/test/test_ssl/test_dhparam.py @@ -1,74 +1,93 @@ -import pytest -import os -import docker -import time -import subprocess import re +import subprocess + +import backoff +import docker +import pytest docker_client = docker.from_env() -def wait_for_nginxproxy_to_be_ready(): + +############################################################################### +# +# Tests helpers +# +############################################################################### + +@backoff.on_exception(backoff.constant, AssertionError, interval=2, max_tries=15, jitter=None) +def assert_log_contains(expected_log_line): """ - If one (and only one) container started from image jwilder/nginx-proxy:test is found, - wait for its log to contain substring "Watching docker events" + Check that the nginx-proxy container log contains a given string. + The backoff decorator will retry the check 15 times with a 2 seconds delay. + + :param expected_log_line: string to search for + :return: None + :raises: AssertError if the expected string is not found in the log """ - containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) - if len(containers) != 1: - return - container = containers[0] - for line in container.logs(stream=True): - if "Watching docker events" in line: - break - -def test_dhparam_is_not_generated_if_present(docker_compose, nginxproxy): - wait_for_nginxproxy_to_be_ready() - - containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) - if len(containers) != 1: - assert 0 - return - - sut_container = containers[0] - + sut_container = docker_client.containers.get("nginxproxy") docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) + assert expected_log_line in docker_logs - assert "Custom dhparam.pem file found, generation skipped" in docker_logs + +def require_openssl(required_version): + """ + This function checks that the required version of OpenSSL is present, and skips the test if not. + Use it as a test function decorator: + + @require_openssl("2.3.4") + def test_something(): + ... + + :param required_version: minimal required version as a string: "1.2.3" + """ + + def versiontuple(v): + clean_v = re.sub("[^\d\.]", "", v) + return tuple(map(int, (clean_v.split(".")))) + + try: + command_output = subprocess.check_output(["openssl", "version"]) + except OSError: + return pytest.mark.skip("openssl command is not available in test environment") + else: + if not command_output: + raise Exception("Could not get openssl version") + openssl_version = command_output.split()[1] + return pytest.mark.skipif( + versiontuple(openssl_version) < versiontuple(required_version), + reason="openssl v%s is less than required version %s" % (openssl_version, required_version)) + + +############################################################################### +# +# Tests +# +############################################################################### + +def test_dhparam_is_not_generated_if_present(docker_compose): + sut_container = docker_client.containers.get("nginxproxy") + assert sut_container.status == "running" + + assert_log_contains("Custom dhparam.pem file found, generation skipped") # Make sure the dhparam in use is not the default, pre-generated one default_checksum = sut_container.exec_run("md5sum /app/dhparam.pem.default").split() current_checksum = sut_container.exec_run("md5sum /etc/nginx/dhparam/dhparam.pem").split() assert default_checksum[0] != current_checksum[0] + def test_web5_https_works(docker_compose, nginxproxy): r = nginxproxy.get("https://web5.nginx-proxy.tld/port", allow_redirects=False) assert r.status_code == 200 assert "answer from port 85\n" in r.text -def versiontuple(v): - clean_v = re.sub("[^\d\.]", "", v) - return tuple(map(int, (clean_v.split(".")))) - -# This code checks that the required version of OpenSSL is present, and skips the test if not -openssl_version_required = "1.0.2" -openssl_version = "0.0.0" - -try: - openssl_version = subprocess.check_output(["openssl", "version"]).split()[1] -except: - pass - -@pytest.mark.skipif(versiontuple(openssl_version) < versiontuple(openssl_version_required), - reason="openssl command is not available in test environment or is less than version %s" % openssl_version_required) - -def test_web5_dhparam_is_used(docker_compose, nginxproxy): - containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) - if len(containers) != 1: - assert 0 - return - - sut_container = containers[0] +@require_openssl("1.0.2") +def test_web5_dhparam_is_used(docker_compose): + sut_container = docker_client.containers.get("nginxproxy") + assert sut_container.status == "running" host = "%s:443" % sut_container.attrs["NetworkSettings"]["IPAddress"] - r = subprocess.check_output("echo '' | openssl s_client -verify 0 -connect %s -cipher 'EDH' | grep 'Server Temp Key'" % host, shell=True) - assert "Server Temp Key: DH, 2048 bits" in r + r = subprocess.check_output( + "echo '' | openssl s_client -verify 0 -connect %s -cipher 'EDH' | grep 'Server Temp Key'" % host, shell=True) + assert "Server Temp Key: DH, 2048 bits\n" == r diff --git a/test/test_ssl/test_dhparam.yml b/test/test_ssl/test_dhparam.yml index 6e196b0..66b1a61 100644 --- a/test/test_ssl/test_dhparam.yml +++ b/test/test_ssl/test_dhparam.yml @@ -5,11 +5,11 @@ web5: environment: WEB_PORTS: "85" VIRTUAL_HOST: "web5.nginx-proxy.tld" - HTTPS_METHOD: nohttp sut: image: jwilder/nginx-proxy:test + container_name: nginxproxy volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro diff --git a/test/test_ssl/test_dhparam_generation.py b/test/test_ssl/test_dhparam_generation.py index 87e71f1..0f5398b 100644 --- a/test/test_ssl/test_dhparam_generation.py +++ b/test/test_ssl/test_dhparam_generation.py @@ -1,52 +1,42 @@ -import pytest -import os +import backoff import docker -import time docker_client = docker.from_env() -def wait_for_nginxproxy_to_be_ready(): + +############################################################################### +# +# Tests helpers +# +############################################################################### + +@backoff.on_exception(backoff.constant, AssertionError, interval=2, max_tries=15, jitter=None) +def assert_log_contains(expected_log_line): """ - If one (and only one) container started from image jwilder/nginx-proxy:test is found, - wait for its log to contain substring "Watching docker events" + Check that the nginx-proxy container log contains a given string. + The backoff decorator will retry the check 15 times with a 2 seconds delay. + + :param expected_log_line: string to search for + :return: None + :raises: AssertError if the expected string is not found in the log """ - containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) - if len(containers) != 1: - return - container = containers[0] - for line in container.logs(stream=True): - if "Watching docker events" in line: - break - -def test_dhparam_is_generated_if_missing(docker_compose, nginxproxy): - wait_for_nginxproxy_to_be_ready() - - containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"}) - if len(containers) != 1: - assert 0 - return - - sut_container = containers[0] - + sut_container = docker_client.containers.get("nginxproxy") docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) + assert expected_log_line in docker_logs - assert "Generating DH parameters" in docker_logs - expected_line = "dhparam generation complete, reloading nginx" - max_wait = 30 - sleep_interval = 2 - current_wait = 0 +############################################################################### +# +# Tests +# +############################################################################### - while current_wait < max_wait: - docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) - if expected_line in docker_logs: - break +def test_dhparam_is_generated_if_missing(docker_compose): + sut_container = docker_client.containers.get("nginxproxy") + assert sut_container.status == "running" - time.sleep(sleep_interval) - current_wait += sleep_interval - - # Re-check the logs to get better assert output on failure - assert expected_line in docker_logs + assert_log_contains("Generating DH parameters") + assert_log_contains("dhparam generation complete, reloading nginx") # Make sure the dhparam in use is not the default, pre-generated one default_checksum = sut_container.exec_run("md5sum /app/dhparam.pem.default").split() diff --git a/test/test_ssl/test_dhparam_generation.yml b/test/test_ssl/test_dhparam_generation.yml index f55cb95..35f3067 100644 --- a/test/test_ssl/test_dhparam_generation.yml +++ b/test/test_ssl/test_dhparam_generation.yml @@ -1,5 +1,6 @@ sut: image: jwilder/nginx-proxy:test + container_name: nginxproxy volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ./certs:/etc/nginx/certs:ro From 761bbf9dbc8ed48a654b1094ce50563efeb1f851 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Sun, 18 Jun 2017 21:21:05 -0400 Subject: [PATCH 22/23] Removed duplicate server_names_hash_bucket_size directive --- Dockerfile | 1 - Dockerfile.alpine | 1 - 2 files changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 656f0ba..19690cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,6 @@ RUN apt-get update \ # Configure Nginx and apply fix for very long server names RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ - && sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf # Install Forego diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 874a236..2a4ebf8 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -9,7 +9,6 @@ RUN apk add --no-cache --virtual .run-deps \ # Configure Nginx and apply fix for very long server names RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ - && sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf # Install Forego From 026ba7cdac0868834ba63116b2d7d0a2793f4439 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Sun, 18 Jun 2017 21:30:59 -0400 Subject: [PATCH 23/23] Added DHParam compatibility note --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3ad39f9..df3e838 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,11 @@ at startup. Since it can take minutes to generate a new `dhparam.pem`, it is do background. Once generation is complete, the `dhparams.pem` is saved on a persistent volume and nginx is reloaded. This generation process only occurs the first time you start `nginx-proxy`. +> COMPATIBILITY WARNING: The default generated `dhparam.pem` key is 2048 bits for A+ security. Some +> older clients (like Java 6 and 7) do not support DH keys with over 1024 bits. In order to support these +> clients, you must either provide your own `dhparam.pem`, or tell `nginx-proxy` to generate a 1024-bit +> key on startup by passing `-e DHPARAM_BITS=1024`. + #### Wildcard Certificates Wildcard certificates and keys should be named after the domain name with a `.crt` and `.key` extension.