From 55610b8425549863bc04ed80e0797bc8e1e5995f Mon Sep 17 00:00:00 2001 From: cglewis Date: Tue, 31 Oct 2017 18:21:12 -0700 Subject: [PATCH 1/5] MAINTAINER is deprecated, using LABEL now --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index cdf47c5..149fb90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM nginx:1.13 -MAINTAINER Jason Wilder mail@jasonwilder.com +LABEL maintainer="Jason Wilder mail@jasonwilder.com" # Install wget and install/updates certificates RUN apt-get update \ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 1d588d3..fce6aae 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ FROM nginx:1.13-alpine -MAINTAINER Jason Wilder mail@jasonwilder.com +LABEL maintainer="Jason Wilder mail@jasonwilder.com" # Install wget and install/updates certificates RUN apk add --no-cache --virtual .run-deps \ From a312472fb57cbc0e9747898ee96b390232b30340 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 8 Nov 2017 22:30:24 -0500 Subject: [PATCH 2/5] Added custom HSTS support (issue #953) --- README.md | 7 +++++++ nginx.tmpl | 7 +++++-- test/test_ssl/test_hsts.py | 19 +++++++++++++++++++ test/test_ssl/test_hsts.yml | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 test/test_ssl/test_hsts.py create mode 100644 test/test_ssl/test_hsts.yml diff --git a/README.md b/README.md index 91052d4..6d67e6e 100644 --- a/README.md +++ b/README.md @@ -267,6 +267,13 @@ site after changing this setting, your browser has probably cached the HSTS poli redirecting you back to HTTPS. You will need to clear your browser's HSTS cache or use an incognito window / different browser. +By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) +is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable +`HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`. +*WARNING*: HSTS will force your users to visit the HTTPS version of your site for the `max-age` time - +even if they type in `http://` manually. The only way to get to an HTTP site after receiving an HSTS +response is to clear your browser's HSTS cache. + ### Basic Authentication Support In order to be able to secure your virtual host, you have to create a file named as its equivalent VIRTUAL_HOST variable on directory diff --git a/nginx.tmpl b/nginx.tmpl index 28f745a..5147fee 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -158,6 +158,9 @@ upstream {{ $upstream_name }} { {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} +{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} +{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }} + {{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} {{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} @@ -225,8 +228,8 @@ server { ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.crt" $cert }}; {{ end }} - {{ if (ne $https_method "noredirect") }} - add_header Strict-Transport-Security "max-age=31536000"; + {{ if (and (ne $https_method "noredirect") (ne $hsts "off")) }} + add_header Strict-Transport-Security "{{ trim $hsts }}"; {{ end }} {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} diff --git a/test/test_ssl/test_hsts.py b/test/test_ssl/test_hsts.py new file mode 100644 index 0000000..180f274 --- /dev/null +++ b/test/test_ssl/test_hsts.py @@ -0,0 +1,19 @@ +import pytest + + +def test_web1_HSTS_default(docker_compose, nginxproxy): + r = nginxproxy.get("https://web1.nginx-proxy.tld/port", allow_redirects=False) + assert "answer from port 81\n" in r.text + assert "Strict-Transport-Security" in r.headers + assert "max-age=31536000" == r.headers["Strict-Transport-Security"] + +def test_web2_HSTS_off(docker_compose, nginxproxy): + r = nginxproxy.get("https://web2.nginx-proxy.tld/port", allow_redirects=False) + assert "answer from port 81\n" in r.text + assert "Strict-Transport-Security" not in r.headers + +def test_web3_HSTS_custom(docker_compose, nginxproxy): + r = nginxproxy.get("https://web3.nginx-proxy.tld/port", allow_redirects=False) + assert "answer from port 81\n" in r.text + assert "Strict-Transport-Security" in r.headers + assert "max-age=86400; includeSubDomains; preload" == r.headers["Strict-Transport-Security"] diff --git a/test/test_ssl/test_hsts.yml b/test/test_ssl/test_hsts.yml new file mode 100644 index 0000000..5c04cf0 --- /dev/null +++ b/test/test_ssl/test_hsts.yml @@ -0,0 +1,32 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "web1.nginx-proxy.tld" + +web2: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "web2.nginx-proxy.tld" + HSTS: "off" + +web3: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "web3.nginx-proxy.tld" + HSTS: "max-age=86400; includeSubDomains; preload" + +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 From 58a02f107eb0e43a631eb9b99a18c5c13ccec4f0 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 8 Nov 2017 22:42:52 -0500 Subject: [PATCH 3/5] Removed '-verify 0' - to disable verification, exclude -verify entirely --- test/test_ssl/test_dhparam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_ssl/test_dhparam.py b/test/test_ssl/test_dhparam.py index 67b11fa..fd60217 100644 --- a/test/test_ssl/test_dhparam.py +++ b/test/test_ssl/test_dhparam.py @@ -89,5 +89,5 @@ def test_web5_dhparam_is_used(docker_compose): 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) + "echo '' | openssl s_client -connect %s -cipher 'EDH' | grep 'Server Temp Key'" % host, shell=True) assert "Server Temp Key: DH, 2048 bits\n" == r From ebd1485b09ad2ab70bdf4a13d4e39152fc1fda27 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 8 Nov 2017 22:53:44 -0500 Subject: [PATCH 4/5] Catch SSLError instead of CertificateError --- .../wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py index db18809..12b04c7 100644 --- a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py +++ b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py @@ -1,6 +1,5 @@ import pytest -from backports.ssl_match_hostname import CertificateError - +from requests.exceptions import SSLError @pytest.mark.parametrize("subdomain,should_redirect_to_https", [ (1, True), @@ -23,9 +22,10 @@ def test_https_get_served(docker_compose, nginxproxy, subdomain): def test_web3_https_is_500_and_SSL_validation_fails(docker_compose, nginxproxy): - with pytest.raises(CertificateError) as excinfo: + with pytest.raises(SSLError) as excinfo: nginxproxy.get("https://3.web.nginx-proxy.tld/port") assert """hostname '3.web.nginx-proxy.tld' doesn't match 'nginx-proxy.tld'""" in str(excinfo.value) + r = nginxproxy.get("https://3.web.nginx-proxy.tld/port", verify=False) assert r.status_code == 500 From 612bf72ceb3157a0ebad12b2dd09ad2874b24013 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Wed, 8 Nov 2017 23:19:13 -0500 Subject: [PATCH 5/5] Support old and new versions of requests --- .../wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py index 12b04c7..de4b298 100644 --- a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py +++ b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py @@ -1,6 +1,8 @@ import pytest +from backports.ssl_match_hostname import CertificateError from requests.exceptions import SSLError + @pytest.mark.parametrize("subdomain,should_redirect_to_https", [ (1, True), (2, True), @@ -22,10 +24,9 @@ def test_https_get_served(docker_compose, nginxproxy, subdomain): def test_web3_https_is_500_and_SSL_validation_fails(docker_compose, nginxproxy): - with pytest.raises(SSLError) as excinfo: + with pytest.raises( (CertificateError, SSLError) ) as excinfo: nginxproxy.get("https://3.web.nginx-proxy.tld/port") assert """hostname '3.web.nginx-proxy.tld' doesn't match 'nginx-proxy.tld'""" in str(excinfo.value) - r = nginxproxy.get("https://3.web.nginx-proxy.tld/port", verify=False) assert r.status_code == 500