From 5af973b193e5b67b64e247e9996c475310c5f5db Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 18 Jul 2023 22:32:50 +0200 Subject: [PATCH 1/5] feat: optionally disable HTTP/2 --- nginx.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nginx.tmpl b/nginx.tmpl index fb0766b..1a8f82c 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -471,6 +471,7 @@ server { {{- $containers := $vhost.containers }} {{- $default_server := when $vhost.default "default_server" "" }} {{- $https_method := $vhost.https_method }} + {{- $http2 := parseBool (or (first (keys (groupByLabel $containers "com.github.nginx-proxy.nginx-proxy.http2.enable"))) $globals.Env.ENABLE_HTTP2 "true")}} {{- $is_regexp := hasPrefix "~" $host }} {{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }} @@ -549,7 +550,9 @@ server { {{- if $server_tokens }} server_tokens {{ $server_tokens }}; {{- end }} + {{- if $http2 }} http2 on; + {{- end }} {{ $globals.access_log }} {{- if or (eq $https_method "nohttps") (not $cert_ok) (eq $https_method "noredirect") }} listen {{ $globals.external_http_port }} {{ $default_server }}; From b5cc9b1aa2582749b5e556e3e15316a7f4effcad Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 18 Jul 2023 22:44:36 +0200 Subject: [PATCH 2/5] feat: experimental http3 support Co-authored-by: Nicolas Duchon Co-authored-by: Knapoc --- nginx.tmpl | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 1a8f82c..7e5a52e 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -203,6 +203,10 @@ include /etc/nginx/network_internal.conf; {{- end }} + {{- if .http3 }} + add_header alt-svc 'h3=":{{ $.globals.external_https_port }}"; ma=86400;'; + {{- end }} + {{- if eq .Proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; @@ -333,7 +337,7 @@ map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl { 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 '{{ or $globals.Env.LOG_FORMAT "$host $remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$upstream_addr\"" }}'; +log_format vhost '{{ or $globals.Env.LOG_FORMAT "$host $remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$upstream_addr\" $http3" }}'; access_log off; @@ -350,7 +354,7 @@ include /etc/nginx/proxy.conf; # HTTP 1.1 support proxy_http_version 1.1; proxy_buffering off; -proxy_set_header Host $http_host; +proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $proxy_connection; proxy_set_header X-Real-IP $remote_addr; @@ -384,7 +388,15 @@ proxy_set_header Proxy ""; {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} {{- $default := eq $globals.Env.DEFAULT_HOST $vhost }} {{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) $globals.Env.HTTPS_METHOD "redirect" }} - {{- $_ := set $globals.vhosts $vhost (dict "cert" $cert "cert_ok" $cert_ok "containers" $containers "default" $default "https_method" $https_method) }} + {{- $http3 := parseBool (or (first (keys (groupByLabel $containers "com.github.nginx-proxy.nginx-proxy.http3.enable"))) $globals.Env.ENABLE_HTTP3 "false")}} + {{- $_ := set $globals.vhosts $vhost (dict + "cert" $cert + "cert_ok" $cert_ok + "containers" $containers + "default" $default + "https_method" $https_method + "http3" $http3 + ) }} {{- end }} {{- /* @@ -406,6 +418,7 @@ proxy_set_header Proxy ""; {{- $https_exists := false }} {{- $default_http_exists := false }} {{- $default_https_exists := false }} + {{- $http3 := false }} {{- range $vhost := $globals.vhosts }} {{- $http := or (ne $vhost.https_method "nohttp") (not $vhost.cert_ok) }} {{- $https := ne $vhost.https_method "nohttps" }} @@ -413,6 +426,7 @@ proxy_set_header Proxy ""; {{- $https_exists = or $https_exists $https }} {{- $default_http_exists = or $default_http_exists (and $http $vhost.default) }} {{- $default_https_exists = or $default_https_exists (and $https $vhost.default) }} + {{- $http3 = or $http3 $vhost.http3 }} {{- end }} {{- $fallback_http := and $http_exists (not $default_http_exists) }} {{- $fallback_https := and $https_exists (not $default_https_exists) }} @@ -438,8 +452,14 @@ server { {{- end }} {{- if $fallback_https }} listen {{ $globals.external_https_port }} ssl; {{- /* Do not add `default_server` (see comment above). */}} + {{- if $http3 }} + listen {{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} + {{- end }} {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl; {{- /* Do not add `default_server` (see comment above). */}} + {{- if $http3 }} + listen [::]:{{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} + {{- end }} {{- end }} ssl_session_cache shared:SSL:50m; ssl_session_tickets off; @@ -472,6 +492,7 @@ server { {{- $default_server := when $vhost.default "default_server" "" }} {{- $https_method := $vhost.https_method }} {{- $http2 := parseBool (or (first (keys (groupByLabel $containers "com.github.nginx-proxy.nginx-proxy.http2.enable"))) $globals.Env.ENABLE_HTTP2 "true")}} + {{- $http3 := parseBool (or (first (keys (groupByLabel $containers "com.github.nginx-proxy.nginx-proxy.http3.enable"))) $globals.Env.ENABLE_HTTP3 "false")}} {{- $is_regexp := hasPrefix "~" $host }} {{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }} @@ -562,8 +583,14 @@ server { {{- end }} {{- if ne $https_method "nohttps" }} listen {{ $globals.external_https_port }} ssl {{ $default_server }}; + {{- if $http3 }} + listen {{ $globals.external_https_port }} quic {{ $default_server }}; + {{- end }} {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl {{ $default_server }}; + {{- if $http3 }} + listen [::]:{{ $globals.external_https_port }} quic {{ $default_server }}; + {{- end }} {{- end }} {{- if $cert_ok }} @@ -648,7 +675,18 @@ server { {{- $upstream = printf "%s-%s" $upstream $sum }} {{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} {{- end }} - {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag "Containers" $containers) }} + {{- template "location" (dict + "globals" $globals + "Path" $path + "http3" $http3 + "Proto" $proto + "Upstream" $upstream + "Host" $host + "VhostRoot" $vhost_root + "Dest" $dest + "NetworkTag" $network_tag + "Containers" $containers + ) }} {{- end }} {{- if and (not (contains $paths "/")) (ne $globals.default_root_response "none")}} location / { From 018db70367b2c4c6900c02d909681e18ff82e23c Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 22 Jul 2023 10:42:17 +0200 Subject: [PATCH 3/5] refactor: re-organise template for HTTP/3 feature Co-authored-by: Nicolas Duchon Co-authored-by: Niek <100143256+SchoNie@users.noreply.github.com> --- nginx.tmpl | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 7e5a52e..db2d54b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -203,10 +203,6 @@ include /etc/nginx/network_internal.conf; {{- end }} - {{- if .http3 }} - add_header alt-svc 'h3=":{{ $.globals.external_https_port }}"; ma=86400;'; - {{- end }} - {{- if eq .Proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; @@ -281,8 +277,8 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto { } map $http_x_forwarded_host $proxy_x_forwarded_host { - default {{ if $globals.trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }}; - '' $http_host; + default {{ if $globals.trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$host{{ end }}; + '' $host; } # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the @@ -337,7 +333,7 @@ map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl { 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 '{{ or $globals.Env.LOG_FORMAT "$host $remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$upstream_addr\" $http3" }}'; +log_format vhost '{{ or $globals.Env.LOG_FORMAT "$host $remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$upstream_addr\"" }}'; access_log off; @@ -443,6 +439,7 @@ proxy_set_header Proxy ""; server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; + {{ $globals.access_log }} http2 on; {{- if $fallback_http }} listen {{ $globals.external_http_port }}; {{- /* Do not add `default_server` (see comment above). */}} @@ -452,19 +449,19 @@ server { {{- end }} {{- if $fallback_https }} listen {{ $globals.external_https_port }} ssl; {{- /* Do not add `default_server` (see comment above). */}} - {{- if $http3 }} - listen {{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} - {{- end }} {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl; {{- /* Do not add `default_server` (see comment above). */}} - {{- if $http3 }} + {{- end }} + {{- if $http3 }} + http3 on; + listen {{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} + {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} {{- end }} ssl_session_cache shared:SSL:50m; ssl_session_tickets off; {{- end }} - {{ $globals.access_log }} {{- if $globals.default_cert_ok }} ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; @@ -540,11 +537,11 @@ server { {{- if $server_tokens }} server_tokens {{ $server_tokens }}; {{- end }} + {{ $globals.access_log }} listen {{ $globals.external_http_port }} {{ $default_server }}; {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; {{- end }} - {{ $globals.access_log }} # Do not HTTPS redirect Let's Encrypt ACME challenge location ^~ /.well-known/acme-challenge/ { @@ -571,10 +568,10 @@ server { {{- if $server_tokens }} server_tokens {{ $server_tokens }}; {{- end }} + {{ $globals.access_log }} {{- if $http2 }} http2 on; {{- end }} - {{ $globals.access_log }} {{- if or (eq $https_method "nohttps") (not $cert_ok) (eq $https_method "noredirect") }} listen {{ $globals.external_http_port }} {{ $default_server }}; {{- if $globals.enable_ipv6 }} @@ -583,12 +580,15 @@ server { {{- end }} {{- if ne $https_method "nohttps" }} listen {{ $globals.external_https_port }} ssl {{ $default_server }}; - {{- if $http3 }} - listen {{ $globals.external_https_port }} quic {{ $default_server }}; - {{- end }} {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl {{ $default_server }}; - {{- if $http3 }} + {{- end }} + + {{- if $http3 }} + http3 on; + add_header alt-svc 'h3=":{{ $globals.external_https_port }}"; ma=86400;'; + listen {{ $globals.external_https_port }} quic {{ $default_server }}; + {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} quic {{ $default_server }}; {{- end }} {{- end }} @@ -676,9 +676,7 @@ server { {{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} {{- end }} {{- template "location" (dict - "globals" $globals "Path" $path - "http3" $http3 "Proto" $proto "Upstream" $upstream "Host" $host From ae5beca0fee13edfde5436db2ff48f3432bb8daa Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 27 Jul 2023 21:47:06 +0200 Subject: [PATCH 4/5] docs: documentation for HTTP/2 and HTTP/3 support Co-authored-by: Nicolas Duchon Co-authored-by: Patrick Domack --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 6d918e0..30bf591 100644 --- a/README.md +++ b/README.md @@ -388,6 +388,35 @@ If the default certificate is also missing, nginx-proxy will configure nginx to > > Error code: `SSL_ERROR_INTERNAL_ERROR_ALERT` "TLS error". +### HTTP/2 support + +HTTP/2 is enabled by default and can be disabled if necessary either per-proxied container or globally: + +To disable HTTP/2 for a single proxied container, set the `com.github.nginx-proxy.nginx-proxy.http2.enable` label to `false` on this container. + +To disable HTTP/2 globally set the environment variable `ENABLE_HTTP2` to `false` on the nginx-proxy container. + +More reading on the potential TCP head-of-line blocking issue with HTTP/2: [HTTP/2 Issues](https://www.twilio.com/blog/2017/10/http2-issues.html), [Comparing HTTP/3 vs HTTP/2](https://blog.cloudflare.com/http-3-vs-http-2/) + +### HTTP/3 support + +> **Warning** +> HTTP/3 support [is still considered experimental in nginx](https://www.nginx.com/blog/binary-packages-for-preview-nginx-quic-http3-implementation/) and as such is considered experimental in nginx-proxy too and is disabled by default. [Feedbacks for the HTTP/3 support are welcome in #2271.](https://github.com/nginx-proxy/nginx-proxy/discussions/2271) + +HTTP/3 use the QUIC protocol over UDP (unlike HTTP/1.1 and HTTP/2 which work over TCP), so if you want to use HTTP/3 you'll have to explicitely publish the 443/udp port of the proxy in addition to the 443/tcp port: + +```console +docker run -d -p 80:80 -p 443:443/tcp -p 443:443/udp \ + -v /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy +``` + +HTTP/3 can be enabled either per-proxied container or globally: + +To enable HTTP/3 for a single proxied container, set the `com.github.nginx-proxy.nginx-proxy.http3.enable` label to `true` on this container. + +To enable HTTP/3 globally set the environment variable `ENABLE_HTTP3` to `true` on the nginx-proxy container. + ### 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 From c4cb1c3797a5fc2648bb93ea743680432f98be58 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 8 Dec 2023 00:46:21 +0100 Subject: [PATCH 5/5] test: tests for HTTP/3 Co-authored-by: Nicolas Duchon Co-authored-by: Niek <100143256+SchoNie@users.noreply.github.com> --- test/test_http2/test_http2_global_disabled.py | 8 +++ .../test_http2/test_http2_global_disabled.yml | 15 ++++++ test/test_http3/test_http3_global_disabled.py | 19 +++++++ .../test_http3/test_http3_global_disabled.yml | 15 ++++++ test/test_http3/test_http3_global_enabled.py | 21 ++++++++ test/test_http3/test_http3_global_enabled.yml | 15 ++++++ test/test_http3/test_http3_vhost.py | 49 +++++++++++++++++++ test/test_http3/test_http3_vhost.yml | 33 +++++++++++++ test/test_ssl/test_hsts.py | 8 +++ test/test_ssl/test_hsts.yml | 10 ++++ 10 files changed, 193 insertions(+) create mode 100644 test/test_http2/test_http2_global_disabled.py create mode 100644 test/test_http2/test_http2_global_disabled.yml create mode 100644 test/test_http3/test_http3_global_disabled.py create mode 100644 test/test_http3/test_http3_global_disabled.yml create mode 100644 test/test_http3/test_http3_global_enabled.py create mode 100644 test/test_http3/test_http3_global_enabled.yml create mode 100644 test/test_http3/test_http3_vhost.py create mode 100644 test/test_http3/test_http3_vhost.yml diff --git a/test/test_http2/test_http2_global_disabled.py b/test/test_http2/test_http2_global_disabled.py new file mode 100644 index 0000000..42e102d --- /dev/null +++ b/test/test_http2/test_http2_global_disabled.py @@ -0,0 +1,8 @@ +import pytest +import re + +def test_http2_global_disabled_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r = nginxproxy.get("http://http2-global-disabled.nginx-proxy.tld") + assert r.status_code == 200 + assert not re.search(r"(?s)http2-global-disabled\.nginx-proxy\.tld.*http2 on", conf) diff --git a/test/test_http2/test_http2_global_disabled.yml b/test/test_http2/test_http2_global_disabled.yml new file mode 100644 index 0000000..5dffa19 --- /dev/null +++ b/test/test_http2/test_http2_global_disabled.yml @@ -0,0 +1,15 @@ +services: + http2-global-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: http2-global-disabled.nginx-proxy.tld + + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + ENABLE_HTTP2: "false" diff --git a/test/test_http3/test_http3_global_disabled.py b/test/test_http3/test_http3_global_disabled.py new file mode 100644 index 0000000..508823e --- /dev/null +++ b/test/test_http3/test_http3_global_disabled.py @@ -0,0 +1,19 @@ +import pytest +import re + + #Python Requests is not able to do native http3 requests. + #We only check for directives which should enable http3. + +def test_http3_global_disabled_ALTSVC_header(docker_compose, nginxproxy): + r = nginxproxy.get("http://http3-global-disabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "Host: http3-global-disabled.nginx-proxy.tld" in r.text + assert not "alt-svc" in r.headers + +def test_http3_global_disabled_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r = nginxproxy.get("http://http3-global-disabled.nginx-proxy.tld") + assert r.status_code == 200 + assert not re.search(r"(?s)listen 443 quic", conf) + assert not re.search(r"(?s)http3 on", conf) + assert not re.search(r"(?s)add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf) diff --git a/test/test_http3/test_http3_global_disabled.yml b/test/test_http3/test_http3_global_disabled.yml new file mode 100644 index 0000000..66530a4 --- /dev/null +++ b/test/test_http3/test_http3_global_disabled.yml @@ -0,0 +1,15 @@ +services: + http3-global-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: http3-global-disabled.nginx-proxy.tld + + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + #environment: + #ENABLE_HTTP3: "false" #Disabled by default diff --git a/test/test_http3/test_http3_global_enabled.py b/test/test_http3/test_http3_global_enabled.py new file mode 100644 index 0000000..c678ab6 --- /dev/null +++ b/test/test_http3/test_http3_global_enabled.py @@ -0,0 +1,21 @@ +import pytest +import re + + #Python Requests is not able to do native http3 requests. + #We only check for directives which should enable http3. + +def test_http3_global_enabled_ALTSVC_header(docker_compose, nginxproxy): + r = nginxproxy.get("http://http3-global-enabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "Host: http3-global-enabled.nginx-proxy.tld" in r.text + assert "alt-svc" in r.headers + assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;' + +def test_http3_global_enabled_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r = nginxproxy.get("http://http3-global-enabled.nginx-proxy.tld") + assert r.status_code == 200 + assert re.search(r"listen 443 quic reuseport\;", conf) + assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*listen 443 quic", conf) + assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*http3 on\;", conf) + assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf) diff --git a/test/test_http3/test_http3_global_enabled.yml b/test/test_http3/test_http3_global_enabled.yml new file mode 100644 index 0000000..0825469 --- /dev/null +++ b/test/test_http3/test_http3_global_enabled.yml @@ -0,0 +1,15 @@ +services: + http3-global-enabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: http3-global-enabled.nginx-proxy.tld + + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + ENABLE_HTTP3: "true" diff --git a/test/test_http3/test_http3_vhost.py b/test/test_http3/test_http3_vhost.py new file mode 100644 index 0000000..93a217c --- /dev/null +++ b/test/test_http3/test_http3_vhost.py @@ -0,0 +1,49 @@ +import pytest +import re + + #Python Requests is not able to do native http3 requests. + #We only check for directives which should enable http3. + +def test_http3_vhost_enabled_ALTSVC_header(docker_compose, nginxproxy): + r = nginxproxy.get("http://http3-vhost-enabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "Host: http3-vhost-enabled.nginx-proxy.tld" in r.text + assert "alt-svc" in r.headers + assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;' + +def test_http3_vhost_enabled_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r = nginxproxy.get("http://http3-vhost-enabled.nginx-proxy.tld") + assert r.status_code == 200 + assert re.search(r"listen 443 quic reuseport\;", conf) + assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*listen 443 quic", conf) + assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*http3 on\;", conf) + assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf) + +def test_http3_vhost_disabled_ALTSVC_header(docker_compose, nginxproxy): + r = nginxproxy.get("http://http3-vhost-disabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "Host: http3-vhost-disabled.nginx-proxy.tld" in r.text + assert not "alt-svc" in r.headers + +def test_http3_vhost_disabled_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r = nginxproxy.get("http://http3-vhost-disabled.nginx-proxy.tld") + assert r.status_code == 200 + assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld.*listen 443 quic.*\# http3-vhost-enabled\.nginx-proxy\.tld", conf) + assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld.*http3 on.*\# http3-vhost-enabled\.nginx-proxy\.tld", conf) + assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'.*\# http3-vhost-enabled\.nginx-proxy\.tld", conf) + +def test_http3_vhost_disabledbydefault_ALTSVC_header(docker_compose, nginxproxy): + r = nginxproxy.get("http://http3-vhost-default-disabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "Host: http3-vhost-default-disabled.nginx-proxy.tld" in r.text + assert not "alt-svc" in r.headers + +def test_http3_vhost_disabledbydefault_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r = nginxproxy.get("http://http3-vhost-default-disabled.nginx-proxy.tld") + assert r.status_code == 200 + assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld.*listen 443 quic.*\# http3-vhost-disabled\.nginx-proxy\.tld", conf) + assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld.*http3 on.*\# http3-vhost-disabled\.nginx-proxy\.tld", conf) + assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'.*\# http3-vhost-disabled\.nginx-proxy\.tld", conf) diff --git a/test/test_http3/test_http3_vhost.yml b/test/test_http3/test_http3_vhost.yml new file mode 100644 index 0000000..1d5cdf2 --- /dev/null +++ b/test/test_http3/test_http3_vhost.yml @@ -0,0 +1,33 @@ +services: + http3-vhost-enabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: http3-vhost-enabled.nginx-proxy.tld + labels: + com.github.nginx-proxy.nginx-proxy.http3.enable: "true" + + http3-vhost-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: http3-vhost-disabled.nginx-proxy.tld + labels: + com.github.nginx-proxy.nginx-proxy.http3.enable: "false" + + http3-vhost-default-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: http3-vhost-default-disabled.nginx-proxy.tld + + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_ssl/test_hsts.py b/test/test_ssl/test_hsts.py index 16dffd2..890c4ad 100644 --- a/test/test_ssl/test_hsts.py +++ b/test/test_ssl/test_hsts.py @@ -31,3 +31,11 @@ def test_web4_HSTS_off_noredirect(docker_compose, nginxproxy): r = nginxproxy.get("https://web4.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_http3_vhost_enabled_HSTS_default(docker_compose, nginxproxy): + r = nginxproxy.get("https://http3-vhost-enabled.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"] + assert "alt-svc" in r.headers + assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;' diff --git a/test/test_ssl/test_hsts.yml b/test/test_ssl/test_hsts.yml index b4af3b6..da3b629 100644 --- a/test/test_ssl/test_hsts.yml +++ b/test/test_ssl/test_hsts.yml @@ -34,6 +34,16 @@ web4: HSTS: "off" HTTPS_METHOD: "noredirect" +web5: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: http3-vhost-enabled.nginx-proxy.tld + labels: + com.github.nginx-proxy.nginx-proxy.http3.enable: "true" + sut: image: nginxproxy/nginx-proxy:test volumes: