From 9bad218f2344b3d8a8591a84b5547891e646b35c Mon Sep 17 00:00:00 2001 From: Dakota Hawkins Date: Wed, 5 May 2021 17:09:25 -0400 Subject: [PATCH] Add vhost.d includes to nginx.tmpl's https redirect server block Adds customization points to https redirect responses that are analogous to other response handling. This gives you the opportunity to add response headers or etc. before returning the 301 redirect. If `$host` or `default` exist in `/etc/nginx/vhost.d/`, we rely on nginx-proxy/acme-companion to add the Let's Encrypt acme challenge location block there, so it's only included here if those files don't exist. New tests in `test_ssl` are similar to tests in `test_custom`, except they expect 301 responses along with custom configs. Fixes #1613 --- nginx.tmpl | 16 +++++++++-- .../test_redirect_custom_defaults-location.py | 27 ++++++++++++++++++ ...test_redirect_custom_defaults-location.yml | 24 ++++++++++++++++ .../test_ssl/test_redirect_custom_defaults.py | 26 +++++++++++++++++ .../test_redirect_custom_defaults.yml | 25 +++++++++++++++++ ...test_redirect_custom_location-per-vhost.py | 28 +++++++++++++++++++ ...est_redirect_custom_location-per-vhost.yml | 25 +++++++++++++++++ .../test_redirect_custom_per-vhost.py | 25 +++++++++++++++++ .../test_redirect_custom_per-vhost.yml | 25 +++++++++++++++++ 9 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 test/test_ssl/test_redirect_custom_defaults-location.py create mode 100644 test/test_ssl/test_redirect_custom_defaults-location.yml create mode 100644 test/test_ssl/test_redirect_custom_defaults.py create mode 100644 test/test_ssl/test_redirect_custom_defaults.yml create mode 100644 test/test_ssl/test_redirect_custom_location-per-vhost.py create mode 100644 test/test_ssl/test_redirect_custom_location-per-vhost.yml create mode 100644 test/test_ssl/test_redirect_custom_per-vhost.py create mode 100644 test/test_ssl/test_redirect_custom_per-vhost.yml diff --git a/nginx.tmpl b/nginx.tmpl index 7c36e70..f7b161f 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -265,7 +265,12 @@ server { listen [::]:{{ $external_http_port }} {{ $default_server }}; {{ end }} {{ $access_log }} - + + {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} + include {{ printf "/etc/nginx/vhost.d/%s" $host }}; + {{ else if (exists "/etc/nginx/vhost.d/default") }} + include /etc/nginx/vhost.d/default; + {{ else }} # Do not HTTPS redirect Let'sEncrypt ACME challenge location ^~ /.well-known/acme-challenge/ { auth_basic off; @@ -275,8 +280,15 @@ server { try_files $uri =404; break; } - + {{ end }} + location / { + {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} + include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; + {{ else if (exists "/etc/nginx/vhost.d/default_location") }} + include /etc/nginx/vhost.d/default_location; + {{ end }} + {{ if eq $external_https_port "443" }} return 301 https://$host$request_uri; {{ else }} diff --git a/test/test_ssl/test_redirect_custom_defaults-location.py b/test/test_ssl/test_redirect_custom_defaults-location.py new file mode 100644 index 0000000..c060fe7 --- /dev/null +++ b/test/test_ssl/test_redirect_custom_defaults-location.py @@ -0,0 +1,27 @@ +import pytest + +def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy/", allow_redirects=False) + assert r.status_code == 503 + assert "X-test" not in r.headers + +def test_custom_default_conf_applies_to_web2(docker_compose, nginxproxy): + url = "web2.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] + + +def test_custom_default_conf_is_overriden_for_web3(docker_compose, nginxproxy): + url = "web3.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" in r.headers + assert "bar" == r.headers["X-test"] + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] diff --git a/test/test_ssl/test_redirect_custom_defaults-location.yml b/test/test_ssl/test_redirect_custom_defaults-location.yml new file mode 100644 index 0000000..129b3e3 --- /dev/null +++ b/test/test_ssl/test_redirect_custom_defaults-location.yml @@ -0,0 +1,24 @@ +nginx-proxy: + image: nginxproxy/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 + - ../test_custom/my_custom_proxy_settings.conf:/etc/nginx/vhost.d/default_location:ro + - ../test_custom/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.tld_location:ro + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: web2.nginx-proxy.tld + +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: 83 + VIRTUAL_HOST: web3.nginx-proxy.tld diff --git a/test/test_ssl/test_redirect_custom_defaults.py b/test/test_ssl/test_redirect_custom_defaults.py new file mode 100644 index 0000000..dcaa575 --- /dev/null +++ b/test/test_ssl/test_redirect_custom_defaults.py @@ -0,0 +1,26 @@ +import pytest + +def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy/", allow_redirects=False) + assert r.status_code == 503 + assert "X-test" not in r.headers + +def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): + url = "web2.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] + +def test_custom_conf_applies_to_web3(docker_compose, nginxproxy): + url = "web3.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] diff --git a/test/test_ssl/test_redirect_custom_defaults.yml b/test/test_ssl/test_redirect_custom_defaults.yml new file mode 100644 index 0000000..3fc8187 --- /dev/null +++ b/test/test_ssl/test_redirect_custom_defaults.yml @@ -0,0 +1,25 @@ +version: '2' +services: + nginx-proxy: + image: nginxproxy/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 + - ../test_custom/my_custom_proxy_settings.conf:/etc/nginx/proxy.conf:ro + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: web2.nginx-proxy.tld + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: 83 + VIRTUAL_HOST: web3.nginx-proxy.tld diff --git a/test/test_ssl/test_redirect_custom_location-per-vhost.py b/test/test_ssl/test_redirect_custom_location-per-vhost.py new file mode 100644 index 0000000..c61c24c --- /dev/null +++ b/test/test_ssl/test_redirect_custom_location-per-vhost.py @@ -0,0 +1,28 @@ +import pytest + +def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy/", allow_redirects=False) + assert r.status_code == 503 + assert "X-test" not in r.headers + +def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): + url = "web2.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] + +def test_custom_conf_does_not_apply_to_web3(docker_compose, nginxproxy): + url = "web3.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" not in r.headers + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] + +def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): + assert b"include /etc/nginx/vhost.d/web2.nginx-proxy.tld_location;" in nginxproxy.get_conf() diff --git a/test/test_ssl/test_redirect_custom_location-per-vhost.yml b/test/test_ssl/test_redirect_custom_location-per-vhost.yml new file mode 100644 index 0000000..e5dca4e --- /dev/null +++ b/test/test_ssl/test_redirect_custom_location-per-vhost.yml @@ -0,0 +1,25 @@ +version: '2' +services: + nginx-proxy: + image: nginxproxy/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 + - ../test_custom/my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web2.nginx-proxy.tld_location:ro + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: web2.nginx-proxy.tld + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: 83 + VIRTUAL_HOST: web3.nginx-proxy.tld diff --git a/test/test_ssl/test_redirect_custom_per-vhost.py b/test/test_ssl/test_redirect_custom_per-vhost.py new file mode 100644 index 0000000..936cbec --- /dev/null +++ b/test/test_ssl/test_redirect_custom_per-vhost.py @@ -0,0 +1,25 @@ +import pytest + +def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy/", allow_redirects=False) + assert r.status_code == 503 + assert "X-test" not in r.headers + +def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): + url = "web2.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] + +def test_custom_conf_does_not_apply_to_web3(docker_compose, nginxproxy): + url = "web3.nginx-proxy.tld/port" + r = nginxproxy.get(f"http://{url}", allow_redirects=False) + assert r.status_code == 301 + assert "301 Moved Permanently" in r.text + assert "X-test" not in r.headers + assert "Location" in r.headers + assert f"https://{url}" == r.headers["Location"] diff --git a/test/test_ssl/test_redirect_custom_per-vhost.yml b/test/test_ssl/test_redirect_custom_per-vhost.yml new file mode 100644 index 0000000..d4e33e4 --- /dev/null +++ b/test/test_ssl/test_redirect_custom_per-vhost.yml @@ -0,0 +1,25 @@ +version: '2' +services: + nginx-proxy: + image: nginxproxy/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 + - ../test_custom/my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web2.nginx-proxy.tld:ro + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: web2.nginx-proxy.tld + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: 83 + VIRTUAL_HOST: web3.nginx-proxy.tld