From 9711ade7a640249d99c425a10b15ed730a565779 Mon Sep 17 00:00:00 2001 From: Knapoc Date: Mon, 24 Jul 2023 11:36:17 +0200 Subject: [PATCH 01/35] feat: allow nginx / docker-gen network segregation * fix merge conflicts --- docs/README.md | 6 ++++++ nginx.tmpl | 52 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/docs/README.md b/docs/README.md index 3e3938825..d99d8be5b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1157,6 +1157,12 @@ Finally, start your containers with `VIRTUAL_HOST` environment variables. docker run -e VIRTUAL_HOST=foo.bar.com ... ``` +To allow for network segregation of the nginx and docker-gen containers, the label `com.github.nginx-proxy.nginx-proxy.nginx` must be applied to the nginx container, otherwise it is assumed that nginx and docker-gen share the same network: + +```console +docker run -d -p 80:80 --name nginx -l "com.github.nginx-proxy.nginx-proxy.nginx" -v /tmp/nginx:/etc/nginx/conf.d -t nginx +``` + ⬆️ [back to table of contents](#table-of-contents) ## Docker Compose diff --git a/nginx.tmpl b/nginx.tmpl index 14e30ca54..3666625f7 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -11,6 +11,7 @@ {{- $_ := set $globals "Env" $.Env }} {{- $_ := set $globals "Docker" $.Docker }} {{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }} +{{- $_ := set $globals "NginxContainer" (whereLabelExists $globals.containers "com.github.nginx-proxy.nginx-proxy.nginx" | first) }} {{- $config := dict }} {{- $_ := set $config "nginx_proxy_version" $.Env.NGINX_PROXY_VERSION }} @@ -44,14 +45,21 @@ {{- $_ := set $globals "vhosts" (dict) }} {{- $_ := set $globals "networks" (dict) }} -# Networks available to the container running docker-gen (which are assumed to +# Networks available to the container labeled "com.github.nginx-proxy.nginx-proxy.nginx" or the one running docker-gen (which are assumed to # match the networks available to the container running nginx): {{- /* * Note: $globals.CurrentContainer may be nil in some circumstances due to * . For more context * see . */}} -{{- if $globals.CurrentContainer }} +{{- if $globals.NginxContainer }} + {{- range sortObjectsByKeysAsc $globals.NginxContainer.Networks "Name" }} + {{- $_ := set $globals.networks .Name . }} +# {{ .Name }} + {{- else }} +# (none) + {{- end }} +{{- else if $globals.CurrentContainer }} {{- range sortObjectsByKeysAsc $globals.CurrentContainer.Networks "Name" }} {{- $_ := set $globals.networks .Name . }} # {{ .Name }} @@ -97,11 +105,21 @@ {{- $ipv4 = "127.0.0.1" }} {{- continue }} {{- end }} - {{- range sortObjectsByKeysAsc $.globals.CurrentContainer.Networks "Name" }} - {{- if and . .Gateway (not .Internal) }} + {{- if $.globals.NginxContainer }} + {{- range sortObjectsByKeysAsc $.globals.NginxContainer.Networks "Name" }} + {{- if and . .Gateway (not .Internal) }} # container is in host network mode, using {{ .Name }} gateway IP - {{- $ipv4 = .Gateway }} - {{- break }} + {{- $ipv4 = .Gateway }} + {{- break }} + {{- end }} + {{- end }} + {{- else }} + {{- range sortObjectsByKeysAsc $.globals.CurrentContainer.Networks "Name" }} + {{- if and . .Gateway (not .Internal) }} + # container is in host network mode, using {{ .Name }} gateway IP + {{- $ipv4 = .Gateway }} + {{- break }} + {{- end }} {{- end }} {{- end }} {{- if $ipv4 }} @@ -114,7 +132,7 @@ {{- end }} {{- /* * Do not emit multiple `server` directives for this container if it - * is reachable over multiple networks or multiple IP stacks. This avoids + * is reachable over multiple networks or multiple IP stacks. This avoids * accidentally inflating the effective round-robin weight of a server due * to the redundant upstream addresses that nginx sees as belonging to * distinct servers. @@ -397,7 +415,7 @@ upstream {{ $vpath.upstream }} { {{- $debug_vpath := deepCopy $vpath | merge (dict "ports" $tmp_ports) }} {{- $_ := set $debug_paths $path $debug_vpath }} {{- end }} - + {{- $debug_vhost := deepCopy .VHost }} {{- /* If it's a regexp, do not render the Hostname to the response to avoid rendering config breaking characters */}} {{- $_ := set $debug_vhost "hostname" (.VHost.is_regexp | ternary "Hostname is a regexp and unsafe to include in the debug response." .Hostname) }} @@ -606,7 +624,7 @@ proxy_set_header Proxy ""; {{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }} {{- $_ := set $path_ports $port $path_port_containers }} {{- $_ := set $path_data "ports" $path_ports }} - + {{- if (not (hasKey $path_data "dest")) }} {{- $_ := set $path_data "dest" $dest }} {{- end }} @@ -614,7 +632,7 @@ proxy_set_header Proxy ""; {{- if (not (hasKey $path_data "proto")) }} {{- $_ := set $path_data "proto" $proto }} {{- end }} - + {{- $_ := set $paths $path $path_data }} {{- end }} {{- $_ := set $vhost_data "paths" $paths }} @@ -666,7 +684,7 @@ proxy_set_header Proxy ""; {{- if (not (hasKey $path_data "proto")) }} {{- $_ := set $path_data "proto" $proto }} {{- end }} - + {{- $_ := set $paths $path $path_data }} {{- end }} {{- $_ := set $vhost_data "paths" $paths }} @@ -708,7 +726,7 @@ proxy_set_header Proxy ""; {{- end }} {{- $userIdentifiedCert := groupByKeys $vhost_containers "Env.CERT_NAME" | first }} - + {{- $vhostCert := "" }} {{- if exists (printf "/etc/nginx/certs/%s.crt" $hostname) }} {{- $vhostCert = $hostname }} @@ -721,10 +739,10 @@ proxy_set_header Proxy ""; {{- $parentVhostCert = $parentHostname }} {{- end }} {{- end }} - + {{- $trust_default_cert := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.trust-default-cert" | keys | first | default $globals.config.trust_default_cert | parseBool }} {{- $defaultCert := and $trust_default_cert $globals.config.default_cert_ok | ternary "default" "" }} - + {{- $cert := or $userIdentifiedCert $vhostCert $parentVhostCert $defaultCert }} {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} @@ -738,10 +756,10 @@ proxy_set_header Proxy ""; {{- $https_method = "noredirect" }} {{- end }} {{- $non_get_redirect := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.non-get-redirect" | keys | first | default $globals.config.non_get_redirect }} - + {{- $http2_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable" | keys | first | default $globals.config.enable_http2 | parseBool }} {{- $http3_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable" | keys | first | default $globals.config.enable_http3 | parseBool }} - + {{- $acme_http_challenge := groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION" | first | default $globals.config.acme_http_challenge }} {{- $acme_http_challenge_legacy := eq $acme_http_challenge "legacy" }} {{- $acme_http_challenge_enabled := false }} @@ -903,7 +921,7 @@ server { break; } {{- end }} - + {{- if $vhost.enable_debug_endpoint }} {{ template "debug_location" (dict "GlobalConfig" $globals.config "Hostname" $hostname "VHost" $vhost) }} {{- end }} From ded6f89c56ee0737e3eb3f04b3afd8ed4e996aef Mon Sep 17 00:00:00 2001 From: Knapoc <21982227+Knapoc@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:04:04 +0200 Subject: [PATCH 02/35] test: check docker-gen network segregation --- .../test_dockergen_network_segregation_v2.py | 10 +++++ .../test_dockergen_network_segregation_v2.yml | 38 ++++++++++++++++++ .../test_dockergen_network_segregation_v3.py | 27 +++++++++++++ .../test_dockergen_network_segregation_v3.yml | 40 +++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 test/test_dockergen/test_dockergen_network_segregation_v2.py create mode 100644 test/test_dockergen/test_dockergen_network_segregation_v2.yml create mode 100644 test/test_dockergen/test_dockergen_network_segregation_v3.py create mode 100644 test/test_dockergen/test_dockergen_network_segregation_v3.yml diff --git a/test/test_dockergen/test_dockergen_network_segregation_v2.py b/test/test_dockergen/test_dockergen_network_segregation_v2.py new file mode 100644 index 000000000..dbb15d47f --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation_v2.py @@ -0,0 +1,10 @@ +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): + r = nginxproxy.get("http://unknown.nginx.container.docker/") + assert r.status_code == 503 + + +def test_forwards_to_whoami(docker_compose, nginxproxy): + r = nginxproxy.get("http://whoami.nginx.container.docker/") + assert r.status_code == 200 + whoami_container = docker_compose.containers.get("whoami") + assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation_v2.yml b/test/test_dockergen/test_dockergen_network_segregation_v2.yml new file mode 100644 index 000000000..949e282b8 --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation_v2.yml @@ -0,0 +1,38 @@ +version: '2' + +services: + nginx: + image: nginx + container_name: nginx + volumes: + - "/etc/nginx/conf.d" + labels: + - "com.github.nginx-proxy.nginx-proxy.nginx" + networks: + - proxy + + dockergen: + image: nginxproxy/docker-gen + command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + volumes_from: + - nginx + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + networks: + - internal + + web: + image: web + container_name: whoami + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: "whoami.nginx.container.docker" + networks: + - proxy + +networks: + proxy: + internal: diff --git a/test/test_dockergen/test_dockergen_network_segregation_v3.py b/test/test_dockergen/test_dockergen_network_segregation_v3.py new file mode 100644 index 000000000..b696e6c62 --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation_v3.py @@ -0,0 +1,27 @@ +import docker +import pytest +from distutils.version import LooseVersion + + +raw_version = docker.from_env().version()["Version"] +pytestmark = pytest.mark.skipif( + LooseVersion(raw_version) < LooseVersion("1.13"), + reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" +) + + +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): + r = nginxproxy.get("http://unknown.nginx.container.docker/") + assert r.status_code == 503 + + +def test_forwards_to_whoami(docker_compose, nginxproxy): + r = nginxproxy.get("http://whoami.nginx.container.docker/") + assert r.status_code == 200 + whoami_container = docker_compose.containers.get("whoami") + assert r.text == f"I'm {whoami_container.id[:12]}\n" + + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/test/test_dockergen/test_dockergen_network_segregation_v3.yml b/test/test_dockergen/test_dockergen_network_segregation_v3.yml new file mode 100644 index 000000000..c873c318c --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation_v3.yml @@ -0,0 +1,40 @@ +version: '3' + +services: + nginx: + image: nginx + container_name: nginx + volumes: + - "nginx_conf:/etc/nginx/conf.d" + labels: + - "com.github.nginx-proxy.nginx-proxy.nginx" + networks: + - proxy + + dockergen: + image: nginxproxy/docker-gen + command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + volumes: + - "/var/run/docker.sock:/tmp/docker.sock:ro" + - "../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl" + - "nginx_conf:/etc/nginx/conf.d" + networks: + - internal + + web: + image: web + container_name: whoami + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: "whoami.nginx.container.docker" + networks: + - proxy + +networks: + proxy: + internal: + +volumes: + nginx_conf: {} From c338e1bcdfdf56d939005dde1b2cecc7f9c7ac68 Mon Sep 17 00:00:00 2001 From: Knapoc <21982227+Knapoc@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:31:09 +0200 Subject: [PATCH 03/35] test: docker-gen network segregation * fix tests * remove obsolete compose version --- ...st_dockergen_network_segregation.base.yml} | 25 +++++++----- ... => test_dockergen_network_segregation.py} | 8 ++-- .../test_dockergen_network_segregation_v2.py | 10 ----- .../test_dockergen_network_segregation_v3.yml | 40 ------------------- 4 files changed, 18 insertions(+), 65 deletions(-) rename test/test_dockergen/{test_dockergen_network_segregation_v2.yml => test_dockergen_network_segregation.base.yml} (69%) rename test/test_dockergen/{test_dockergen_network_segregation_v3.py => test_dockergen_network_segregation.py} (72%) delete mode 100644 test/test_dockergen/test_dockergen_network_segregation_v2.py delete mode 100644 test/test_dockergen/test_dockergen_network_segregation_v3.yml diff --git a/test/test_dockergen/test_dockergen_network_segregation_v2.yml b/test/test_dockergen/test_dockergen_network_segregation.base.yml similarity index 69% rename from test/test_dockergen/test_dockergen_network_segregation_v2.yml rename to test/test_dockergen/test_dockergen_network_segregation.base.yml index 949e282b8..8040b479c 100644 --- a/test/test_dockergen/test_dockergen_network_segregation_v2.yml +++ b/test/test_dockergen/test_dockergen_network_segregation.base.yml @@ -1,38 +1,41 @@ -version: '2' - services: - nginx: + nginx-proxy-nginx: image: nginx container_name: nginx volumes: - - "/etc/nginx/conf.d" - labels: - - "com.github.nginx-proxy.nginx-proxy.nginx" + - nginx_conf:/etc/nginx/conf.d:ro + ports: + - "80:80" + - "443:443" networks: - proxy + labels: + - "com.github.nginx-proxy.nginx-proxy.nginx" - dockergen: + nginx-proxy-dockergen: image: nginxproxy/docker-gen command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf - volumes_from: - - nginx volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - nginx_conf:/etc/nginx/conf.d networks: - internal web: image: web - container_name: whoami + container_name: whoami2 expose: - "80" environment: WEB_PORTS: "80" - VIRTUAL_HOST: "whoami.nginx.container.docker" + VIRTUAL_HOST: whoami2.nginx.container.docker networks: - proxy networks: proxy: internal: + +volumes: + nginx_conf: diff --git a/test/test_dockergen/test_dockergen_network_segregation_v3.py b/test/test_dockergen/test_dockergen_network_segregation.py similarity index 72% rename from test/test_dockergen/test_dockergen_network_segregation_v3.py rename to test/test_dockergen/test_dockergen_network_segregation.py index b696e6c62..ad487d99f 100644 --- a/test/test_dockergen/test_dockergen_network_segregation_v3.py +++ b/test/test_dockergen/test_dockergen_network_segregation.py @@ -1,11 +1,11 @@ import docker import pytest -from distutils.version import LooseVersion +from packaging.version import Version raw_version = docker.from_env().version()["Version"] pytestmark = pytest.mark.skipif( - LooseVersion(raw_version) < LooseVersion("1.13"), + Version(raw_version) < Version("1.13"), reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" ) @@ -16,9 +16,9 @@ def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami.nginx.container.docker/") + r = nginxproxy.get("http://whoami2.nginx.container.docker/") assert r.status_code == 200 - whoami_container = docker_compose.containers.get("whoami") + whoami_container = docker_compose.containers.get("whoami2") assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation_v2.py b/test/test_dockergen/test_dockergen_network_segregation_v2.py deleted file mode 100644 index dbb15d47f..000000000 --- a/test/test_dockergen/test_dockergen_network_segregation_v2.py +++ /dev/null @@ -1,10 +0,0 @@ -def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): - r = nginxproxy.get("http://unknown.nginx.container.docker/") - assert r.status_code == 503 - - -def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami.nginx.container.docker/") - assert r.status_code == 200 - whoami_container = docker_compose.containers.get("whoami") - assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation_v3.yml b/test/test_dockergen/test_dockergen_network_segregation_v3.yml deleted file mode 100644 index c873c318c..000000000 --- a/test/test_dockergen/test_dockergen_network_segregation_v3.yml +++ /dev/null @@ -1,40 +0,0 @@ -version: '3' - -services: - nginx: - image: nginx - container_name: nginx - volumes: - - "nginx_conf:/etc/nginx/conf.d" - labels: - - "com.github.nginx-proxy.nginx-proxy.nginx" - networks: - - proxy - - dockergen: - image: nginxproxy/docker-gen - command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf - volumes: - - "/var/run/docker.sock:/tmp/docker.sock:ro" - - "../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl" - - "nginx_conf:/etc/nginx/conf.d" - networks: - - internal - - web: - image: web - container_name: whoami - expose: - - "80" - environment: - WEB_PORTS: "80" - VIRTUAL_HOST: "whoami.nginx.container.docker" - networks: - - proxy - -networks: - proxy: - internal: - -volumes: - nginx_conf: {} From 67aa2aafd20717d53b5eea8fc929851307ecc435 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 11 May 2025 11:38:13 +0200 Subject: [PATCH 04/35] chore: update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..66d881f3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,44 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +# ⚠️ PLEASE READ ⚠️ + +## Questions or Features + +If you have a question or want to request a feature, please **DO NOT SUBMIT** a new issue. + +Instead please use the relevant Discussions section's category: +- 🙏 [Ask a question](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/q-a) +- 💡 [Request a feature](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/ideas) + +## Bugs + +If you are logging a bug, please search the current open issues first to see if there is already a bug opened. + +For bugs, the easier you make it to reproduce the issue you see and the more initial information you provide, the easier and faster the bug can be identified and can get fixed. + +Please at least provide: +- the exact nginx-proxy version you're using (if using `latest` please make sure it is up to date and provide the version number printed at container startup). +- complete configuration (compose file, command line, etc) of both your nginx-proxy container(s) and proxied containers. You should redact sensitive info if needed but please provide **full** configurations. +- generated nginx configuration obtained with `docker exec nameofyournginxproxycontainer nginx -T` + +If you can provide a script or docker-compose file that reproduces the problems, that is very helpful. + +## General advice about `latest` + +Do not use the `latest` tag for production setups. + +`latest` is nothing more than a convenient default used by Docker if no specific tag is provided, there isn't any strict convention on what goes into this tag over different projects, and it does not carry any promise of stability. + +Using `latest` will most certainly put you at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes) and makes it harder for maintainers to track which exact version of the container you are experiencing an issue with. + +This recommendation stands for pretty much every Docker image in existence, not just nginx-proxy's ones. + +Thanks, +Nicolas From be1fd947787c80b3f27b300e962775797aa0b577 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 11 May 2025 12:01:38 +0200 Subject: [PATCH 05/35] chore: configure the issue template chooser --- .github/ISSUE_TEMPLATE.md | 23 ----------------------- .github/ISSUE_TEMPLATE/config.yml | 5 +++++ 2 files changed, 5 insertions(+), 23 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9824d632a..65b80f03b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -8,28 +8,5 @@ Instead please use the relevant Discussions section's category: - 🙏 [Ask a question](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/q-a) - 💡 [Request a feature](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/ideas) -## Bugs - -If you are logging a bug, please search the current open issues first to see if there is already a bug opened. - -For bugs, the easier you make it to reproduce the issue you see and the more initial information you provide, the easier and faster the bug can be identified and can get fixed. - -Please at least provide: -- the exact nginx-proxy version you're using (if using `latest` please make sure it is up to date and provide the version number printed at container startup). -- complete configuration (compose file, command line, etc) of both your nginx-proxy container(s) and proxied containers. You should redact sensitive info if needed but please provide **full** configurations. -- generated nginx configuration obtained with `docker exec nameofyournginxproxycontainer nginx -T` - -If you can provide a script or docker-compose file that reproduces the problems, that is very helpful. - -## General advice about `latest` - -Do not use the `latest` tag for production setups. - -`latest` is nothing more than a convenient default used by Docker if no specific tag is provided, there isn't any strict convention on what goes into this tag over different projects, and it does not carry any promise of stability. - -Using `latest` will most certainly put you at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes) and makes it harder for maintainers to track which exact version of the container you are experiencing an issue with. - -This recommendation stands for pretty much every Docker image in existence, not just nginx-proxy's ones. - Thanks, Nicolas diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..7c19e5e3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: GitHub Discussions + url: https://github.com/orgs/community/discussions + about: Please ask and answer questions here. \ No newline at end of file From 40744f6f413399d5aedb081a3e39ef9d4875d8d9 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 11 May 2025 12:26:24 +0200 Subject: [PATCH 06/35] refactor: deduplicate code --- nginx.tmpl | 55 ++++++++++++++++++++---------------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 3666625f7..dd1c4448d 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -10,11 +10,10 @@ {{- $_ := set $globals "containers" $ }} {{- $_ := set $globals "Env" $.Env }} {{- $_ := set $globals "Docker" $.Docker }} -{{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }} -{{- $_ := set $globals "NginxContainer" (whereLabelExists $globals.containers "com.github.nginx-proxy.nginx-proxy.nginx" | first) }} {{- $config := dict }} {{- $_ := set $config "nginx_proxy_version" $.Env.NGINX_PROXY_VERSION }} +{{- $_ := set $config "nginx_container_label" ($.Env.NGINX_CONTAINER_LABEL | default "com.github.nginx-proxy.nginx-proxy.nginx") }} {{- $_ := set $config "default_cert_ok" (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} {{- $_ := set $config "external_http_port" ($globals.Env.HTTP_PORT | default "80") }} {{- $_ := set $config "external_https_port" ($globals.Env.HTTPS_PORT | default "443") }} @@ -45,33 +44,29 @@ {{- $_ := set $globals "vhosts" (dict) }} {{- $_ := set $globals "networks" (dict) }} -# Networks available to the container labeled "com.github.nginx-proxy.nginx-proxy.nginx" or the one running docker-gen (which are assumed to -# match the networks available to the container running nginx): + +{{- $currentContainer := where $globals.containers "ID" $globals.Docker.CurrentContainerID | first }} +{{- $labeledContainer := whereLabelExists $globals.containers $globals.config.nginx_container_label | first }} +{{- $_ := set $globals "NetworkContainer" ($labeledContainer | default $currentContainer) }} +# Networks available to the container labeled "{{ $globals.config.nginx_container_label }}" or the one running docker-gen +# (which are assumed to match the networks available to the container running nginx): {{- /* - * Note: $globals.CurrentContainer may be nil in some circumstances due to - * . For more context - * see . + * Note: + * $globals.NetworkContainer may be nil in some circumstances due to https://github.com/nginx-proxy/docker-gen/issues/458. + * For more context see https://github.com/nginx-proxy/nginx-proxy/issues/2189. */}} -{{- if $globals.NginxContainer }} - {{- range sortObjectsByKeysAsc $globals.NginxContainer.Networks "Name" }} - {{- $_ := set $globals.networks .Name . }} -# {{ .Name }} - {{- else }} -# (none) - {{- end }} -{{- else if $globals.CurrentContainer }} - {{- range sortObjectsByKeysAsc $globals.CurrentContainer.Networks "Name" }} +{{- if $globals.NetworkContainer }} + {{- range sortObjectsByKeysAsc $globals.NetworkContainer.Networks "Name" }} {{- $_ := set $globals.networks .Name . }} # {{ .Name }} {{- else }} # (none) {{- end }} {{- else }} -# /!\ WARNING: Failed to find the Docker container running docker-gen. All -# upstream (backend) application containers will appear to be -# unreachable. Try removing the -only-exposed and -only-published -# arguments to docker-gen if you pass either of those. See -# . +# /!\ WARNING: Failed to find the Docker container labeled "{{ $globals.config.nginx_container_label }}" or the one running docker-gen. +# All upstream (backend) application containers will appear to be unreachable. +# Try removing the -only-exposed and -only-published arguments to docker-gen if you pass either of those. +# See https://github.com/nginx-proxy/docker-gen/issues/458. {{- end }} {{- /* @@ -105,21 +100,11 @@ {{- $ipv4 = "127.0.0.1" }} {{- continue }} {{- end }} - {{- if $.globals.NginxContainer }} - {{- range sortObjectsByKeysAsc $.globals.NginxContainer.Networks "Name" }} - {{- if and . .Gateway (not .Internal) }} - # container is in host network mode, using {{ .Name }} gateway IP - {{- $ipv4 = .Gateway }} - {{- break }} - {{- end }} - {{- end }} - {{- else }} - {{- range sortObjectsByKeysAsc $.globals.CurrentContainer.Networks "Name" }} - {{- if and . .Gateway (not .Internal) }} + {{- range sortObjectsByKeysAsc $.globals.NetworkContainer.Networks "Name" }} + {{- if and . .Gateway (not .Internal) }} # container is in host network mode, using {{ .Name }} gateway IP - {{- $ipv4 = .Gateway }} - {{- break }} - {{- end }} + {{- $ipv4 = .Gateway }} + {{- break }} {{- end }} {{- end }} {{- if $ipv4 }} From bfabd460548816244da650d29f76ff80bc069a16 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 11 May 2025 12:40:11 +0200 Subject: [PATCH 07/35] test: network segregation w/ internal Docker network --- ...test_dockergen_network_segregation.base.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/test_dockergen/test_dockergen_network_segregation.base.yml b/test/test_dockergen/test_dockergen_network_segregation.base.yml index 8040b479c..c0e85ac0b 100644 --- a/test/test_dockergen/test_dockergen_network_segregation.base.yml +++ b/test/test_dockergen/test_dockergen_network_segregation.base.yml @@ -1,3 +1,12 @@ +networks: + proxy: + private: + internal: true + +volumes: + nginx_conf: + + services: nginx-proxy-nginx: image: nginx @@ -20,7 +29,7 @@ services: - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl - nginx_conf:/etc/nginx/conf.d networks: - - internal + - private web: image: web @@ -32,10 +41,3 @@ services: VIRTUAL_HOST: whoami2.nginx.container.docker networks: - proxy - -networks: - proxy: - internal: - -volumes: - nginx_conf: From db51154175d7f3b3b8d603634f81446ea46abca7 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 11 May 2025 12:46:45 +0200 Subject: [PATCH 08/35] test: custom nginx network segregation label --- ..._network_segregation-custom-label.base.yml | 45 +++++++++++++++++++ ...kergen_network_segregation-custom-label.py | 27 +++++++++++ 2 files changed, 72 insertions(+) create mode 100644 test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml create mode 100644 test/test_dockergen/test_dockergen_network_segregation-custom-label.py diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml b/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml new file mode 100644 index 000000000..1429e9f96 --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml @@ -0,0 +1,45 @@ +networks: + proxy: + private: + internal: true + +volumes: + nginx_conf: + + +services: + nginx-proxy-nginx: + image: nginx + container_name: nginx + volumes: + - nginx_conf:/etc/nginx/conf.d:ro + ports: + - "80:80" + - "443:443" + networks: + - proxy + labels: + - "com.github.nginx-proxy.nginx-proxy.foobarbuzz" + + nginx-proxy-dockergen: + image: nginxproxy/docker-gen + command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - nginx_conf:/etc/nginx/conf.d + environment: + NGINX_CONTAINER_LABEL: "com.github.nginx-proxy.nginx-proxy.foobarbuzz" + networks: + - private + + web: + image: web + container_name: whoami2 + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: whoami2.nginx.container.docker + networks: + - proxy diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.py b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py new file mode 100644 index 000000000..ad487d99f --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py @@ -0,0 +1,27 @@ +import docker +import pytest +from packaging.version import Version + + +raw_version = docker.from_env().version()["Version"] +pytestmark = pytest.mark.skipif( + Version(raw_version) < Version("1.13"), + reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" +) + + +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): + r = nginxproxy.get("http://unknown.nginx.container.docker/") + assert r.status_code == 503 + + +def test_forwards_to_whoami(docker_compose, nginxproxy): + r = nginxproxy.get("http://whoami2.nginx.container.docker/") + assert r.status_code == 200 + whoami_container = docker_compose.containers.get("whoami2") + assert r.text == f"I'm {whoami_container.id[:12]}\n" + + +if __name__ == "__main__": + import doctest + doctest.testmod() From eb9f0f31d702381919bec10d39dbc7f6a3f7b80e Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 11 May 2025 13:05:12 +0200 Subject: [PATCH 09/35] docs: add NGINX_CONTAINER_LABEL to docs --- docs/README.md | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index d99d8be5b..95d9b2756 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1136,31 +1136,65 @@ I'm 5b129ab83266 To run nginx proxy as a separate container you'll need to have [nginx.tmpl](https://github.com/nginx-proxy/nginx-proxy/blob/main/nginx.tmpl) on your host system. -First start nginx with a volume: +First start nginx with a volume mounted to `/etc/nginx/conf.d`: ```console -docker run -d -p 80:80 --name nginx -v /tmp/nginx:/etc/nginx/conf.d -t nginx +docker run --detach \ + --name nginx \ + --publish 80:80 \ + --volume /tmp/nginx:/etc/nginx/conf.d \ + nginx ``` Then start the docker-gen container with the shared volume and template: ```console -docker run --volumes-from nginx \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ - -v $(pwd):/etc/docker-gen/templates \ - -t nginxproxy/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf +docker run --detach \ + --name docker-gen \ + --volumes-from nginx \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume $(pwd):/etc/docker-gen/templates \ + nginxproxy/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf ``` Finally, start your containers with `VIRTUAL_HOST` environment variables. ```console -docker run -e VIRTUAL_HOST=foo.bar.com ... +docker run --env VIRTUAL_HOST=foo.bar.com ... ``` +### Network segregation + To allow for network segregation of the nginx and docker-gen containers, the label `com.github.nginx-proxy.nginx-proxy.nginx` must be applied to the nginx container, otherwise it is assumed that nginx and docker-gen share the same network: ```console -docker run -d -p 80:80 --name nginx -l "com.github.nginx-proxy.nginx-proxy.nginx" -v /tmp/nginx:/etc/nginx/conf.d -t nginx +docker run --detach \ + --name nginx \ + --publish 80:80 \ + --label "com.github.nginx-proxy.nginx-proxy.nginx" \ + --volume /tmp/nginx:/etc/nginx/conf.d \ + nginx +``` + +Network segregation make it possible to run the docker-gen container in an [internal network](https://docs.docker.com/reference/cli/docker/network/create/#internal), unreachable from the outside. + +You can also customise the label being used by docker-gen to find the nginx container with the `NGINX_CONTAINER_LABEL`environment variable (on the docker-gen container): + +```console +docker run --detach \ + --name docker-gen \ + --volumes-from nginx \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume $(pwd):/etc/docker-gen/templates \ + --env "NGINX_CONTAINER_LABEL=com.github.foobarbuzz" \ + nginxproxy/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + +docker run --detach \ + --name nginx \ + --publish 80:80 \ + --label "com.github.foobarbuzz" \ + --volume "/tmp/nginx:/etc/nginx/conf.d" \ + nginx ``` ⬆️ [back to table of contents](#table-of-contents) From 7dd39e69033d8cf83232b25c2b29adce6e37e747 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 18 May 2025 20:32:29 +0200 Subject: [PATCH 10/35] fix(build): correctly grab docker-gen version from dockerfile (#2622) --- .github/workflows/build-publish-dispatch.yml | 2 +- .github/workflows/build-publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-publish-dispatch.yml b/.github/workflows/build-publish-dispatch.yml index 01a7b379b..145e4dc1e 100644 --- a/.github/workflows/build-publish-dispatch.yml +++ b/.github/workflows/build-publish-dispatch.yml @@ -27,7 +27,7 @@ jobs: - name: Retrieve docker-gen version id: docker-gen_version - run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" + run: sed -n -e 's;^FROM docker.io/nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" - name: Get Docker tags id: docker_meta diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 70798e13f..defbaf936 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -38,7 +38,7 @@ jobs: - name: Retrieve docker-gen version id: docker-gen_version - run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" + run: sed -n -e 's;^FROM docker.io/nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" - name: Get Docker tags id: docker_meta From a1372d2a9b33e7f55e09f62b23f2db21a3d6e12f Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 18 May 2025 20:39:59 +0200 Subject: [PATCH 11/35] docs: standadize docker cli invocation style --- docs/README.md | 153 ++++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 59 deletions(-) diff --git a/docs/README.md b/docs/README.md index 9993ecab0..ca1d56c71 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,13 +33,19 @@ You can also use wildcards at the beginning and the end of host name, like `*.ba To set the default host for nginx use the env var `DEFAULT_HOST=foo.bar.com` for example ```console -docker run -d -p 80:80 -e DEFAULT_HOST=foo.bar.com -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 80:80 \ + --env DEFAULT_HOST=foo.bar.com \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` nginx-proxy will then redirect all requests to a container where `VIRTUAL_HOST` is set to `DEFAULT_HOST`, if they don't match any (other) `VIRTUAL_HOST`. Using the example above requests without matching `VIRTUAL_HOST` will be redirected to a plain nginx instance after running the following command: ```console -docker run -d -e VIRTUAL_HOST=foo.bar.com nginx +docker run --detach \ + --env VIRTUAL_HOST=foo.bar.com \ + nginx ``` ### Virtual Ports @@ -179,7 +185,12 @@ If the application runs natively on this sub-path or has a setting to do so, `VI If the requests are expected to not contain a sub-path and the generated links contain the sub-path, `VIRTUAL_DEST=/` should be used. ```console -$ docker run -d -e VIRTUAL_HOST=example.tld -e VIRTUAL_PATH=/app1/ -e VIRTUAL_DEST=/ --name app1 app +docker run --detach \ + --name app1 \ + --env VIRTUAL_HOST=example.tld \ + --env VIRTUAL_PATH=/app1/ \ + --env VIRTUAL_DEST=/ \ + app ``` In this example, the incoming request `http://example.tld/app1/foo` will be proxied as `http://app1/foo` instead of `http://app1/app1/foo`. @@ -221,7 +232,13 @@ Nginx variables such as `$scheme`, `$host`, and `$request_uri` can be used. Howe If you want to use `nginx-proxy` with different external ports that the default ones of `80` for `HTTP` traffic and `443` for `HTTPS` traffic, you'll have to use the environment variable(s) `HTTP_PORT` and/or `HTTPS_PORT` in addition to the changes to the Docker port mapping. If you change the `HTTPS` port, the redirect for `HTTPS` traffic will also be configured to redirect to the custom port. Typical usage, here with the custom ports `1080` and `10443`: ```console -docker run -d -p 1080:1080 -p 10443:10443 -e HTTP_PORT=1080 -e HTTPS_PORT=10443 -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 1080:1080 \ + --publish 10443:10443 \ + --env HTTP_PORT=1080 \ + --env HTTPS_PORT=10443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` ### Multiple Networks @@ -231,8 +248,12 @@ With the addition of [overlay networking](https://docs.docker.com/engine/usergui If you want your `nginx-proxy` container to be attached to a different network, you must pass the `--net=my-network` option in your `docker create` or `docker run` command. At the time of this writing, only a single network can be specified at container creation time. To attach to other networks, you can use the `docker network connect` command after your container is created: ```console -docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro \ - --name my-nginx-proxy --net my-network nginxproxy/nginx-proxy +docker run --detach \ + --name my-nginx-proxy \ + --publish 80:80 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --net my-network \ + nginxproxy/nginx-proxy docker network connect my-other-network my-nginx-proxy ``` @@ -336,10 +357,12 @@ In order to be able to secure your virtual host, you have to create a file named `/etc/nginx/htpasswd/`. Example: `/etc/nginx/htpasswd/app.example.com`. ```console -docker run -d -p 80:80 -p 443:443 \ - -v /path/to/htpasswd:/etc/nginx/htpasswd \ - -v /path/to/certs:/etc/nginx/certs \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ +docker run --detach \ + --publish 80:80 \ + --publish 443:443 \ + --volume /path/to/htpasswd:/etc/nginx/htpasswd \ + --volume /path/to/certs:/etc/nginx/certs \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ nginxproxy/nginx-proxy ``` @@ -399,10 +422,10 @@ To remove colors from the container log output, set the [`NO_COLOR` environment ```console docker run --detach \ - --publish 80:80 \ - --env NO_COLOR=1 \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - nginxproxy/nginx-proxy + --publish 80:80 \ + --env NO_COLOR=1 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` ⬆️ [back to table of contents](#table-of-contents) @@ -414,7 +437,12 @@ SSL is supported using single host, wildcard and SAN certificates using naming c To enable SSL: ```console -docker run -d -p 80:80 -p 443:443 -v /path/to/certs:/etc/nginx/certs -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 80:80 \ + --publish 443:443 \ + --volume /path/to/certs:/etc/nginx/certs \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` The contents of `/path/to/certs` should contain the certificates and private keys for any virtual hosts in use. The certificate and keys should be named after the virtual host with a `.crt` and `.key` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` should have a `foo.bar.com.crt` and `foo.bar.com.key` file in the certs directory. @@ -445,7 +473,7 @@ In the separate container setup, no pre-generated key will be available and neit Set `DHPARAM_SKIP` environment variable to `true` to disable using default Diffie-Hellman parameters. The default value is `false`. ```console -docker run -e DHPARAM_SKIP=true .... +docker run --env DHPARAM_SKIP=true .... ``` ### Wildcard Certificates @@ -661,7 +689,11 @@ IPv4 and IPv6 are never both used at the same time on containers that use both I By default the nginx-proxy container will only listen on IPv4. To enable listening on IPv6 too, set the `ENABLE_IPV6` environment variable to `true`: ```console -docker run -d -p 80:80 -e ENABLE_IPV6=true -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 80:80 \ + --env ENABLE_IPV6=true \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` ### Scoped IPv6 Resolvers @@ -694,8 +726,11 @@ More reading on the potential TCP head-of-line blocking issue with HTTP/2: [HTTP 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 \ +docker run --detach \ + --publish 80:80 \ + --publish 443:443/tcp \ + --publish 443:443/udp \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ nginxproxy/nginx-proxy ``` @@ -788,12 +823,12 @@ client_max_body_size 100m; ```console docker run --detach \ - --name nginx-proxy \ - --publish 80:80 \ - --publish 443:443 \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - --volume /path/to/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro \ - nginxproxy/nginx-proxy + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro \ + nginxproxy/nginx-proxy ``` @@ -842,12 +877,12 @@ client_max_body_size 100m; ```console docker run --detach \ - --name nginx-proxy \ - --publish 80:80 \ - --publish 443:443 \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/app.example.com:ro \ - nginxproxy/nginx-proxy + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/app.example.com:ro \ + nginxproxy/nginx-proxy ``` @@ -877,13 +912,13 @@ If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=e ```console docker run --detach \ - --name nginx-proxy \ - --publish 80:80 \ - --publish 443:443 \ - --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/example.com:ro \ - --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/www.example.com:ro \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - nginxproxy/nginx-proxy + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/example.com:ro \ + --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/www.example.com:ro \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` @@ -933,12 +968,12 @@ proxy_cache_valid 404 1m; ```console docker run --detach \ - --name nginx-proxy \ - --publish 80:80 \ - --publish 443:443 \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/app.example.com_location:ro \ - nginxproxy/nginx-proxy + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/app.example.com_location:ro \ + nginxproxy/nginx-proxy ``` @@ -968,13 +1003,13 @@ If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=e ```console docker run --detach \ - --name nginx-proxy \ - --publish 80:80 \ - --publish 443:443 \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/example.com_location:ro \ - --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/www.example.com_location:ro \ - nginxproxy/nginx-proxy + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/example.com_location:ro \ + --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/www.example.com_location:ro \ + nginxproxy/nginx-proxy ``` @@ -1245,14 +1280,14 @@ Pay attention to the `upstream` definition blocks, which should look like this: ```nginx # foo.example.com upstream foo.example.com { - ## Can be connected with "my_network" network - # Exposed ports: [{ tcp } { tcp } ...] - # Default virtual port: - # VIRTUAL_PORT: - # foo - server 172.18.0.9:; - # Fallback entry - server 127.0.0.1 down; + ## Can be connected with "my_network" network + # Exposed ports: [{ tcp } { tcp } ...] + # Default virtual port: + # VIRTUAL_PORT: + # foo + server 172.18.0.9:; + # Fallback entry + server 127.0.0.1 down; } ``` From 72210064f0b89d40980f0d5d89b87910552ea349 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Fri, 14 Mar 2025 01:28:11 +0200 Subject: [PATCH 12/35] test: add test for unknown domain responses to acme challenge --- .../test_acme-http-challenge-location-disabled.py | 7 +++++++ ...test_acme-http-challenge-location-enabled-is-default.py | 7 +++++++ .../test_acme-http-challenge-location-legacy.py | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py index ae12fa64a..5588167b4 100644 --- a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py @@ -25,3 +25,10 @@ def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, allow_redirects=False ) assert r.status_code == 200 + +def test_unknown_domain_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 503 diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py index 88cb07d7e..80eea97e8 100644 --- a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py @@ -25,3 +25,10 @@ def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, allow_redirects=False ) assert r.status_code == 404 + +def test_unknown_domain_acme_challenge_location_default_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 503 diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py index ed9f25a63..495ad834a 100644 --- a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py @@ -11,3 +11,10 @@ def test_noredirect_acme_challenge_location_legacy(docker_compose, nginxproxy, a allow_redirects=False ) assert r.status_code == 404 + +def test_unknown_domain_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 503 From 4c8f22ebcc4c3bb7a2d192991da2fc1d2abb40fe Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Fri, 14 Mar 2025 01:28:12 +0200 Subject: [PATCH 13/35] feat: support ACME challenges for unknown virtual hosts Currently any ACME challenge for unknown virtual host returns 503. This is inconvenient because if the user does not use wildcard certificates, then the user must match the configuration of certificate renewal script to what virtual hosts are enabled at the time. This must be done automatically, because due to short certificate lifetime the renewal script runs automatically. Additionally, enabling a previously disabled virtual host forces certificate renewal. Accordingly, it's worthwhile supporting unknown virtual hosts for the purposes of passing ACME challenges. This is done by introducing a global ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST variable to control this. --- docs/README.md | 2 + nginx.tmpl | 11 +++++ ...-challenge-location-accept-unknown-host.py | 34 ++++++++++++++++ ...challenge-location-accept-unknown-host.yml | 40 +++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py create mode 100644 test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml diff --git a/docs/README.md b/docs/README.md index ca1d56c71..4e6788dc2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -459,6 +459,8 @@ By default nginx-proxy generates location blocks to handle ACME HTTP Challenge. - `false`: do not handle ACME HTTP Challenge at all. - `legacy`: legacy behavior for compatibility with older (<= `2.3`) versions of acme-companion, only handle ACME HTTP challenge when there is a certificate for the domain and `HTTPS_METHOD=redirect`. +By default, nginx-proxy does not handle ACME HTTP Challenges for unknown virtual hosts. This may happen in cases when a container is not running at the time of the renewal. To enable handling of unknown virtual hosts, set `ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST` environment variable to `true` on the nginx-proxy container. + ### Diffie-Hellman Groups [RFC7919 groups](https://datatracker.ietf.org/doc/html/rfc7919#appendix-A) with key lengths of 2048, 3072, and 4096 bits are [provided by `nginx-proxy`](https://github.com/nginx-proxy/nginx-proxy/dhparam). The ENV `DHPARAM_BITS` can be set to `2048` or `3072` to change from the default 4096-bit key. The DH key file will be located in the container at `/etc/nginx/dhparam/dhparam.pem`. Mounting a different `dhparam.pem` file at that location will override the RFC7919 key. diff --git a/nginx.tmpl b/nginx.tmpl index dd1c4448d..afc9f166c 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -28,6 +28,7 @@ {{- $_ := set $config "enable_debug_endpoint" ($globals.Env.DEBUG_ENDPOINT | default "false") }} {{- $_ := set $config "hsts" ($globals.Env.HSTS | default "max-age=31536000") }} {{- $_ := set $config "acme_http_challenge" ($globals.Env.ACME_HTTP_CHALLENGE_LOCATION | default "true") }} +{{- $_ := set $config "acme_http_challenge_accept_unknown_host" ($globals.Env.ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST | default "false" | parseBool) }} {{- $_ := set $config "enable_http2" ($globals.Env.ENABLE_HTTP2 | default "true") }} {{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }} {{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }} @@ -861,6 +862,16 @@ server { ssl_reject_handshake on; {{- end }} + {{- if $globals.config.acme_http_challenge_accept_unknown_host }} + location ^~ /.well-known/acme-challenge/ { + auth_basic off; + allow all; + root /usr/share/nginx/html; + try_files $uri =404; + break; + } + {{- end }} + {{- if (exists "/usr/share/nginx/html/errors/50x.html") }} error_page 500 502 503 504 /50x.html; location /50x.html { diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py new file mode 100644 index 000000000..8643b4bf9 --- /dev/null +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py @@ -0,0 +1,34 @@ +def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web1.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 200 + +def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web2.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 301 + +def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web3.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 200 + +def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web4.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 404 + +def test_unknown_domain_acme_challenge_location_default_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 200 diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml new file mode 100644 index 000000000..25965e493 --- /dev/null +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml @@ -0,0 +1,40 @@ +services: + nginx-proxy: + environment: + ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST: "true" + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "web1.nginx-proxy.tld" + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "web2.nginx-proxy.tld" + ACME_HTTP_CHALLENGE_LOCATION: "false" + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "web3.nginx-proxy.tld" + HTTPS_METHOD: noredirect + + web4: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: "web4.nginx-proxy.tld" + HTTPS_METHOD: noredirect + ACME_HTTP_CHALLENGE_LOCATION: "false" From 4af3be397af689aba32cd03e3f6e37fefb5dc0a9 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 23 May 2025 20:55:29 +0200 Subject: [PATCH 14/35] test: wait for docker-gen container to be ready too --- test/conftest.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 15d18bc32..83f5865e3 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -348,16 +348,26 @@ def docker_compose_down(compose_files: List[str], project_name: str): def wait_for_nginxproxy_to_be_ready(): """ - If one (and only one) container started from image nginxproxy/nginx-proxy:test is found, - wait for its log to contain substring "Watching docker events" + If one (and only one) container started from image nginxproxy/nginx-proxy:test + or nginxproxy/docker-gen:latest is found, wait for its log to contain the substring "Watching docker events" """ - containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"}) - if len(containers) != 1: + nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"}) + docker_gen_containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/docker-gen:latest"}) + + container_name = "nginx-proxy" + + if len(nginx_proxy_containers) == 1: + container = nginx_proxy_containers.pop() + elif len(docker_gen_containers) == 1: + container = docker_gen_containers.pop() + container_name = "docker-gen" + else: + logging.debug("Either more than one or no nginx-proxy or docker-gen container found, skipping container readiness check") return - container = containers[0] + for line in container.logs(stream=True): if b"Watching docker events" in line: - logging.debug("nginx-proxy ready") + logging.debug(f"{container_name} ready") break From 0479cb02a2d6fec892ebd56b67706aace7b1c0e4 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 23 May 2025 21:33:33 +0200 Subject: [PATCH 15/35] test: fix docker-gen flaky tests --- test/conftest.py | 34 +++++++++++++++---- test/test_dockergen/test_dockergen.base.yml | 2 +- test/test_dockergen/test_dockergen.py | 4 +-- ..._network_segregation-custom-label.base.yml | 2 +- ...kergen_network_segregation-custom-label.py | 4 +-- ...est_dockergen_network_segregation.base.yml | 2 +- .../test_dockergen_network_segregation.py | 4 +-- 7 files changed, 37 insertions(+), 15 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 83f5865e3..c0a060b43 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -199,26 +199,48 @@ def container_ipv6(container: Container) -> str: def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]: """ if "nginx-proxy" if found in host, return the ip address of the docker container - issued from the docker image nginxproxy/nginx-proxy:test. + issued from the docker image nginxproxy/nginx-proxy:test or nginx:latest. :return: IP or None """ log = logging.getLogger('DNS') log.debug(f"nginx_proxy_dns_resolver({domain_name!r})") + if 'nginx-proxy' in domain_name: nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"}) - if len(nginxproxy_containers) == 0: - log.warning(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}") + nginx_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginx:latest"}) + + if len(nginxproxy_containers) == 0 and len(nginx_containers) == 0: + log.warning(f"no runninf container found from image nginxproxy/nginx-proxy:test or nginx:latest while resolving {domain_name!r}") + exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"}) + exited_nginx_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginx:latest"}) + if len(exited_nginxproxy_containers) > 0: exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs() log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode()) + if len(exited_nginx_containers) > 0: + exited_nginx_container_logs = exited_nginx_containers[0].logs() + log.warning(f"nginx:latest container might have exited unexpectedly. Container logs: " + "\n" + exited_nginx_container_logs.decode()) + return None - nginxproxy_container = nginxproxy_containers[0] - ip = container_ip(nginxproxy_container) - log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}") + + container = None + container_type = "nginx-proxy" + + if len(nginxproxy_containers) >= 1: + container = nginxproxy_containers[0] + if len(nginx_containers) >= 1: + container = nginx_containers[0] + container_type = "nginx" + + ip = container_ip(container) + log.info(f"resolving domain name {domain_name!r} as IP address {ip} of {container_type} container {container.name}") return ip + return None + + def docker_container_dns_resolver(domain_name: str) -> Optional[str]: """ if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", diff --git a/test/test_dockergen/test_dockergen.base.yml b/test/test_dockergen/test_dockergen.base.yml index 69f6c85a2..3012abefa 100644 --- a/test/test_dockergen/test_dockergen.base.yml +++ b/test/test_dockergen/test_dockergen.base.yml @@ -27,4 +27,4 @@ services: - "80" environment: WEB_PORTS: "80" - VIRTUAL_HOST: whoami.nginx.container.docker + VIRTUAL_HOST: whoami.nginx-proxy.tld diff --git a/test/test_dockergen/test_dockergen.py b/test/test_dockergen/test_dockergen.py index 6d419cd37..8784ed5f7 100644 --- a/test/test_dockergen/test_dockergen.py +++ b/test/test_dockergen/test_dockergen.py @@ -11,12 +11,12 @@ def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): - r = nginxproxy.get("http://unknown.nginx.container.docker/") + r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami.nginx.container.docker/") + r = nginxproxy.get("http://whoami.nginx-proxy.tld/") assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami") assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml b/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml index 1429e9f96..bc79d2dba 100644 --- a/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml @@ -40,6 +40,6 @@ services: - "80" environment: WEB_PORTS: "80" - VIRTUAL_HOST: whoami2.nginx.container.docker + VIRTUAL_HOST: whoami2.nginx-proxy.tld networks: - proxy diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.py b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py index ad487d99f..ed5c20bd6 100644 --- a/test/test_dockergen/test_dockergen_network_segregation-custom-label.py +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py @@ -11,12 +11,12 @@ def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): - r = nginxproxy.get("http://unknown.nginx.container.docker/") + r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami2.nginx.container.docker/") + r = nginxproxy.get("http://whoami2.nginx-proxy.tld/") assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami2") assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation.base.yml b/test/test_dockergen/test_dockergen_network_segregation.base.yml index c0e85ac0b..10f61a76b 100644 --- a/test/test_dockergen/test_dockergen_network_segregation.base.yml +++ b/test/test_dockergen/test_dockergen_network_segregation.base.yml @@ -38,6 +38,6 @@ services: - "80" environment: WEB_PORTS: "80" - VIRTUAL_HOST: whoami2.nginx.container.docker + VIRTUAL_HOST: whoami2.nginx-proxy.tld networks: - proxy diff --git a/test/test_dockergen/test_dockergen_network_segregation.py b/test/test_dockergen/test_dockergen_network_segregation.py index ad487d99f..ed5c20bd6 100644 --- a/test/test_dockergen/test_dockergen_network_segregation.py +++ b/test/test_dockergen/test_dockergen_network_segregation.py @@ -11,12 +11,12 @@ def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): - r = nginxproxy.get("http://unknown.nginx.container.docker/") + r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami2.nginx.container.docker/") + r = nginxproxy.get("http://whoami2.nginx-proxy.tld/") assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami2") assert r.text == f"I'm {whoami_container.id[:12]}\n" From f4c297995d102e7444f803b3d67d40eb90932524 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 18 May 2025 21:53:32 +0200 Subject: [PATCH 16/35] docs: config summary --- docs/README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/docs/README.md b/docs/README.md index 4e6788dc2..f14bc2be7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ - [Unhashed vs SHA1 upstream names](#unhashed-vs-sha1-upstream-names) - [Separate Containers](#separate-containers) - [Docker Compose](#docker-compose) +- [Configuration Summary](#configuration-summary) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) @@ -1269,6 +1270,74 @@ I'm 5b129ab83266 ⬆️ [back to table of contents](#table-of-contents) +## Configuration summary + +This section summarize the configurations available on the proxy and proxied container. + +### Proxy container + +Configuration available either on the nginx-proxy container, or the docker-gen container in a [separate containers setup](#separate-containers): + +| Environment Variable | Default Value | +|---------------------|---------------| +| [`ACME_HTTP_CHALLENGE_LOCATION`](#ssl-support-using-an-acme-ca) | `true` | +| [`ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST`](#ssl-support-using-an-acme-ca) | `false` | +| [`DEBUG_ENDPOINT`](#debug-endpoint) | `false` | +| [`DEFAULT_HOST`](#default-host) | no default value | +| [`DEFAULT_ROOT`](#default_root) | `404` | +| [`DHPARAM_SKIP`](#diffie-hellman-groups) | `false` | +| [`DHPARAM_BITS`](#diffie-hellman-groups) | `4096` | +| [`DISABLE_ACCESS_LOGS`](#disable-access-logs) | `false` | +| [`ENABLE_HTTP_ON_MISSING_CERT`](#default-and-missing-certificate) | `true` | +| [`ENABLE_HTTP2`](#http2-support) | `true` | +| [`ENABLE_HTTP3`](#http3-support) | `false` | +| [`ENABLE_IPV6`](#listening-on-ipv6) | `false` | +| [`HTTP_PORT`](#custom-external-httphttps-ports) | `80` | +| [`HTTPS_PORT`](#custom-external-httphttps-ports) | `443` | +| [`HTTPS_METHOD`](#how-ssl-support-works) | `redirect` | +| [`HSTS`](#how-ssl-support-works) | `max-age=31536000` | +| [`LOG_FORMAT`](#custom-log-format) | no default value | +| [`LOG_FORMAT_ESCAPE`](#log-format-escaping) | no default value | +| [`LOG_JSON`](#json-log-format) | `false` | +| [`NGINX_CONTAINER_LABEL`](#network-segregation) | `com.github.nginx-proxy.nginx-proxy.nginx` | +| [`NON_GET_REDIRECT`](#how-ssl-support-works) | `301` | +| [`PREFER_IPV6_NETWORK`](#ipv6-docker-networks) | `false` | +| `RESOLVERS` | no default value | +| [`SHA1_UPSTREAM_NAME`](#unhashed-vs-sha1-upstream-names) | `false` | +| [`SSL_POLICY`](#how-ssl-support-works) | `Mozilla-Intermediate` | +| [`TRUST_DEFAULT_CERT`](#default-and-missing-certificate) | `true` | +| [`TRUST_DOWNSTREAM_PROXY`](#trusting-downstream-proxy-headers) | `true` | + +### Proxyied container + +Configuration available on each proxied container, either by environment variable or by label: + +| Environment Variable | Label | Default Value | +|---------------------|---------------|---------------| +| [`ACME_HTTP_CHALLENGE_LOCATION`](#ssl-support-using-an-acme-ca) | n/a | global (proxy) value | +| [`CERT_NAME`](#san-certificates) | n/a | no default value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.debug-endpoint`](#debug-endpoint) | global (proxy) value | +| [`ENABLE_HTTP_ON_MISSING_CERT`](#default-and-missing-certificate) | n/a | global (proxy) value | +| [`HSTS`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.http2.enable`](#http2-support) | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.http3.enable`](#http3-support) | global (proxy) value | +| [`HTTPS_METHOD`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.keepalive`](#upstream-server-http-keep-alive-support) | `auto` | +| n/a | [`com.github.nginx-proxy.nginx-proxy.loadbalance`](#upstream-server-http-load-balancing-support) | no default value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.non-get-redirect`](#how-ssl-support-works) | global (proxy) value | +| [`SERVER_TOKENS`](#per-virtual_host-server_tokens-configuration) | n/a | no default value | +| [`SSL_POLICY`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.trust-default-cert`](#default-and-missing-certificate) | global (proxy) value | +| [`VIRTUAL_DEST`](#virtual_dest) | n/a | `empty string` | +| [`VIRTUAL_HOST`](#virtual-hosts-and-ports) | n/a | no default value | +| [`VIRTUAL_HOST_MULTIPORTS`](#multiple-ports) | n/a | no default value | +| [`VIRTUAL_PATH`](#path-based-routing) | n/a | `/` | +| [`VIRTUAL_PORT`](#virtual-ports) | n/a | no default value | +| [`VIRTUAL_PROTO`](#upstream-backend-features) | n/a | `http` | +| [`VIRTUAL_ROOT`](#fastcgi-file-root-directory) | n/a | `/var/www/public` | + +⬆️ [back to table of contents](#table-of-contents) + ## Troubleshooting If you can't access your `VIRTUAL_HOST`, inspect the generated nginx configuration: From 0bb98ef41560543a071d3dd8757c1e6dd1589f90 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 24 May 2025 14:43:35 +0200 Subject: [PATCH 17/35] test: simplify container readiness check --- test/conftest.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index c0a060b43..ccc46ed8a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -370,27 +370,20 @@ def docker_compose_down(compose_files: List[str], project_name: str): def wait_for_nginxproxy_to_be_ready(): """ - If one (and only one) container started from image nginxproxy/nginx-proxy:test - or nginxproxy/docker-gen:latest is found, wait for its log to contain the substring "Watching docker events" + Wait for logs of running containers started from image nginxproxy/nginx-proxy:test and/or + nginxproxy/docker-gen:latest to contain the substring "Watching docker events" """ - nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"}) - docker_gen_containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/docker-gen:latest"}) + nginx_proxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"}) + docker_gen_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/docker-gen:latest"}) - container_name = "nginx-proxy" + containers = nginx_proxy_containers + docker_gen_containers - if len(nginx_proxy_containers) == 1: - container = nginx_proxy_containers.pop() - elif len(docker_gen_containers) == 1: - container = docker_gen_containers.pop() - container_name = "docker-gen" - else: - logging.debug("Either more than one or no nginx-proxy or docker-gen container found, skipping container readiness check") - return - - for line in container.logs(stream=True): - if b"Watching docker events" in line: - logging.debug(f"{container_name} ready") - break + for container in containers: + logging.debug(f"waiting for container {container.name} to be ready") + for line in container.logs(stream=True): + if b"Watching docker events" in line: + logging.debug(f"{container.name} ready") + break @pytest.fixture From def3131239408d0ff6f1039f70c36c8b6150a132 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 24 May 2025 14:57:12 +0200 Subject: [PATCH 18/35] ci: disable fail-fast on test workflow --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 60e833115..390db4f79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,7 @@ jobs: strategy: matrix: base_docker_image: [alpine, debian] + fail-fast: false steps: - uses: actions/checkout@v4 From 3f17dd29497e5ece8529d710ab97c7b231a50b55 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 24 May 2025 15:00:06 +0200 Subject: [PATCH 19/35] test: typo --- test/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/conftest.py b/test/conftest.py index ccc46ed8a..c95e42ca6 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -211,7 +211,7 @@ def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]: nginx_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginx:latest"}) if len(nginxproxy_containers) == 0 and len(nginx_containers) == 0: - log.warning(f"no runninf container found from image nginxproxy/nginx-proxy:test or nginx:latest while resolving {domain_name!r}") + log.warning(f"no running container found from image nginxproxy/nginx-proxy:test or nginx:latest while resolving {domain_name!r}") exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"}) exited_nginx_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginx:latest"}) From d3a3d32e183706ac9cc761d1d0069a3a57632691 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 26 May 2025 22:56:28 +0200 Subject: [PATCH 20/35] test: drop old checks in docker-gen tests docker engine 1.13 has been out for more than 8 years now --- test/test_dockergen/test_dockergen.py | 17 ----------------- ...ockergen_network_segregation-custom-label.py | 17 ----------------- .../test_dockergen_network_segregation.py | 17 ----------------- 3 files changed, 51 deletions(-) diff --git a/test/test_dockergen/test_dockergen.py b/test/test_dockergen/test_dockergen.py index 8784ed5f7..8fd4be910 100644 --- a/test/test_dockergen/test_dockergen.py +++ b/test/test_dockergen/test_dockergen.py @@ -1,15 +1,3 @@ -import docker -import pytest -from packaging.version import Version - - -raw_version = docker.from_env().version()["Version"] -pytestmark = pytest.mark.skipif( - Version(raw_version) < Version("1.13"), - reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" -) - - def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 @@ -20,8 +8,3 @@ def test_forwards_to_whoami(docker_compose, nginxproxy): assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami") assert r.text == f"I'm {whoami_container.id[:12]}\n" - - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.py b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py index ed5c20bd6..7efd79162 100644 --- a/test/test_dockergen/test_dockergen_network_segregation-custom-label.py +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py @@ -1,15 +1,3 @@ -import docker -import pytest -from packaging.version import Version - - -raw_version = docker.from_env().version()["Version"] -pytestmark = pytest.mark.skipif( - Version(raw_version) < Version("1.13"), - reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" -) - - def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 @@ -20,8 +8,3 @@ def test_forwards_to_whoami(docker_compose, nginxproxy): assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami2") assert r.text == f"I'm {whoami_container.id[:12]}\n" - - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/test/test_dockergen/test_dockergen_network_segregation.py b/test/test_dockergen/test_dockergen_network_segregation.py index ed5c20bd6..7efd79162 100644 --- a/test/test_dockergen/test_dockergen_network_segregation.py +++ b/test/test_dockergen/test_dockergen_network_segregation.py @@ -1,15 +1,3 @@ -import docker -import pytest -from packaging.version import Version - - -raw_version = docker.from_env().version()["Version"] -pytestmark = pytest.mark.skipif( - Version(raw_version) < Version("1.13"), - reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" -) - - def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 @@ -20,8 +8,3 @@ def test_forwards_to_whoami(docker_compose, nginxproxy): assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami2") assert r.text == f"I'm {whoami_container.id[:12]}\n" - - -if __name__ == "__main__": - import doctest - doctest.testmod() From 70659362445faa3daf53d1189594325c678d3ab6 Mon Sep 17 00:00:00 2001 From: Shane St Savage Date: Thu, 29 May 2025 18:45:25 +0000 Subject: [PATCH 21/35] Set auth_request off for all acme challenge locations Adds missing `auth_request off;` to a few `.well-known/acme-challenge` location blocks. This is needed to allow unrestricted access to `.well-known/acme-challenge` files on servers where `auth_request` is otherwise globally applied. See #1409, nginx-proxy/acme-companion#570 --- nginx.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nginx.tmpl b/nginx.tmpl index afc9f166c..fa23bd47b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -865,6 +865,7 @@ server { {{- if $globals.config.acme_http_challenge_accept_unknown_host }} location ^~ /.well-known/acme-challenge/ { auth_basic off; + auth_request off; allow all; root /usr/share/nginx/html; try_files $uri =404; @@ -970,6 +971,7 @@ server { {{- if (and (eq $vhost.https_method "noredirect") $vhost.acme_http_challenge_enabled) }} location /.well-known/acme-challenge/ { auth_basic off; + auth_request off; allow all; root /usr/share/nginx/html; try_files $uri =404; From 409b0e6fbb682a28ed49165ec5b085d45f0f81cd Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Thu, 27 Feb 2025 13:20:45 +0100 Subject: [PATCH 22/35] feat: SSL client certificate validation --- nginx.tmpl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/nginx.tmpl b/nginx.tmpl index fa23bd47b..6dbea625b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -759,6 +759,9 @@ proxy_set_header Proxy ""; {{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default). */}} {{- $ssl_policy := groupByKeys $vhost_containers "Env.SSL_POLICY" | first | default "" }} + {{- /* Get ssl_verify_client defined by containers w/ the same vhost, falling back to "on" */}} + {{- $ssl_verify_client := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.ssl_verify_client" | keys | first | default "on" }} + {{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000". */}} {{- $hsts := groupByKeys $vhost_containers "Env.HSTS" | first | default $globals.config.hsts }} @@ -780,6 +783,7 @@ proxy_set_header Proxy ""; "acme_http_challenge_enabled" $acme_http_challenge_enabled "server_tokens" $server_tokens "ssl_policy" $ssl_policy + "ssl_verify_client" $ssl_verify_client "trust_default_cert" $trust_default_cert "upstream_name" $upstream_name "vhost_root" $vhost_root @@ -1038,6 +1042,25 @@ server { include /etc/nginx/vhost.d/default; {{- end }} + {{/* SSL Client Certificate Validation */}} + {{/* If vhost(hash).ca.crt exists, include CA */}} + {{- if (exists (printf "/etc/nginx/certs/%s.ca.crt" $vhostFileName)) }} + ssl_client_certificate {{ printf "/etc/nginx/certs/%s.ca.crt" $vhostFileName }}; + ssl_verify_client {{ $vhost.ssl_verify_client }}; + {{/* If vhost(hash).crl.pem exists, include CRL */}} + {{- if (exists (printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName)) }} + ssl_crl {{ printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName }}; + {{ end }} + {{/* If no vhost CA file exists, but a global ca.crt exists include it */}} + {{ else if (exists "/etc/nginx/certs/ca.crt") }} + ssl_client_certificate /etc/nginx/certs/ca.crt; + ssl_verify_client {{ $vhost.ssl_verify_client }}; + {{/* If no vhost CA file exists, but a global ca.crl.pem exists include it */}} + {{ if (exists "/etc/nginx/certs/ca.crl.pem")}} + ssl_crl /etc/nginx/certs/ca.crl.pem; + {{ end }} + {{ end }} + {{- if $vhost.enable_debug_endpoint }} {{ template "debug_location" (dict "GlobalConfig" $globals.config "Hostname" $hostname "VHost" $vhost) }} {{- end }} From 5e77e2910b93b248eedb842690008300210ffd99 Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Thu, 27 Feb 2025 13:22:03 +0100 Subject: [PATCH 23/35] tests: SSL client certificate validation mTLS --- ...1b655182b052fed458ec701f9ae1524e1c2.ca.crt | 21 +++++ ...b655182b052fed458ec701f9ae1524e1c2.crl.pem | 13 +++ test/test_ssl/certs_mtls/ca.crl.pem | 13 +++ test/test_ssl/certs_mtls/ca.crt | 21 +++++ .../mtls-enabled.nginx-proxy.tld.ca.crt | 21 +++++ .../mtls-enabled.nginx-proxy.tld.crl.pem | 13 +++ .../certs_mtls/mtls-optional-foo-bar_location | 4 + .../mtls-optional.nginx-proxy.tld.ca.crt | 21 +++++ test/test_ssl/certs_mtls/nginx-proxy.tld.crt | 70 +++++++++++++++ test/test_ssl/certs_mtls/nginx-proxy.tld.key | 27 ++++++ test/test_ssl/clientcerts/Revoked.crt | 85 +++++++++++++++++++ test/test_ssl/clientcerts/Revoked.key | 28 ++++++ test/test_ssl/clientcerts/Valid.crt | 85 +++++++++++++++++++ test/test_ssl/clientcerts/Valid.key | 28 ++++++ test/test_ssl/test_mtls-client-certificate.py | 59 +++++++++++++ .../test_ssl/test_mtls-client-certificate.yml | 69 +++++++++++++++ 16 files changed, 578 insertions(+) create mode 100644 test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt create mode 100644 test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem create mode 100644 test/test_ssl/certs_mtls/ca.crl.pem create mode 100644 test/test_ssl/certs_mtls/ca.crt create mode 100644 test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt create mode 100644 test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem create mode 100644 test/test_ssl/certs_mtls/mtls-optional-foo-bar_location create mode 100644 test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt create mode 100644 test/test_ssl/certs_mtls/nginx-proxy.tld.crt create mode 100644 test/test_ssl/certs_mtls/nginx-proxy.tld.key create mode 100644 test/test_ssl/clientcerts/Revoked.crt create mode 100644 test/test_ssl/clientcerts/Revoked.key create mode 100644 test/test_ssl/clientcerts/Valid.crt create mode 100644 test/test_ssl/clientcerts/Valid.key create mode 100644 test/test_ssl/test_mtls-client-certificate.py create mode 100644 test/test_ssl/test_mtls-client-certificate.yml diff --git a/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem new file mode 100644 index 000000000..a4bbd9562 --- /dev/null +++ b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem @@ -0,0 +1,13 @@ +-----BEGIN X509 CRL----- +MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 +eS10ZXN0LXN1aXRlFw0yNTAxMDMxMjMwNTBaFw0yNTA3MDIxMjMwNTBaMDIwMAIR +AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg +MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV +BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t +MA0GCSqGSIb3DQEBCwUAA4IBAQCGaKW8kJy1Mznc3T2OHkCx8GudvOo0ZBsZ+pTm +sAnlxDQTIqm8e4gU19WF/SISlfr7qEERqif8+SlUgS9CWtJa70gk+9oobuWfBNIT +VXD4ujO/47nqt2MdRUSSGX+K+9Ox2gyU6kHO1ZrT8VmsL22Bhfa2Pw/3OBL/QHMU +b1hAZyed0CoPCnMqjG0X5zMo3ByGW3TkxG2GhzKCWLGXVbzdHFpS98hpkpaxvIlE +juSYuPItwEftHdB8JHAHL18uDJapZ5mOCuUn/HoZBWOudFjtFQUUzq4eTsB56My4 +qDGb1/ReAoGyheuV0fEtg9MJkGEuGrb38JN6hcdfpW5u0Hwb +-----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/ca.crl.pem b/test/test_ssl/certs_mtls/ca.crl.pem new file mode 100644 index 000000000..a4bbd9562 --- /dev/null +++ b/test/test_ssl/certs_mtls/ca.crl.pem @@ -0,0 +1,13 @@ +-----BEGIN X509 CRL----- +MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 +eS10ZXN0LXN1aXRlFw0yNTAxMDMxMjMwNTBaFw0yNTA3MDIxMjMwNTBaMDIwMAIR +AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg +MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV +BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t +MA0GCSqGSIb3DQEBCwUAA4IBAQCGaKW8kJy1Mznc3T2OHkCx8GudvOo0ZBsZ+pTm +sAnlxDQTIqm8e4gU19WF/SISlfr7qEERqif8+SlUgS9CWtJa70gk+9oobuWfBNIT +VXD4ujO/47nqt2MdRUSSGX+K+9Ox2gyU6kHO1ZrT8VmsL22Bhfa2Pw/3OBL/QHMU +b1hAZyed0CoPCnMqjG0X5zMo3ByGW3TkxG2GhzKCWLGXVbzdHFpS98hpkpaxvIlE +juSYuPItwEftHdB8JHAHL18uDJapZ5mOCuUn/HoZBWOudFjtFQUUzq4eTsB56My4 +qDGb1/ReAoGyheuV0fEtg9MJkGEuGrb38JN6hcdfpW5u0Hwb +-----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/ca.crt b/test/test_ssl/certs_mtls/ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem new file mode 100644 index 000000000..a4bbd9562 --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem @@ -0,0 +1,13 @@ +-----BEGIN X509 CRL----- +MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 +eS10ZXN0LXN1aXRlFw0yNTAxMDMxMjMwNTBaFw0yNTA3MDIxMjMwNTBaMDIwMAIR +AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg +MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV +BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t +MA0GCSqGSIb3DQEBCwUAA4IBAQCGaKW8kJy1Mznc3T2OHkCx8GudvOo0ZBsZ+pTm +sAnlxDQTIqm8e4gU19WF/SISlfr7qEERqif8+SlUgS9CWtJa70gk+9oobuWfBNIT +VXD4ujO/47nqt2MdRUSSGX+K+9Ox2gyU6kHO1ZrT8VmsL22Bhfa2Pw/3OBL/QHMU +b1hAZyed0CoPCnMqjG0X5zMo3ByGW3TkxG2GhzKCWLGXVbzdHFpS98hpkpaxvIlE +juSYuPItwEftHdB8JHAHL18uDJapZ5mOCuUn/HoZBWOudFjtFQUUzq4eTsB56My4 +qDGb1/ReAoGyheuV0fEtg9MJkGEuGrb38JN6hcdfpW5u0Hwb +-----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/mtls-optional-foo-bar_location b/test/test_ssl/certs_mtls/mtls-optional-foo-bar_location new file mode 100644 index 000000000..285a3bd36 --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-optional-foo-bar_location @@ -0,0 +1,4 @@ +if ($ssl_client_verify = SUCCESS) { + add_header Content-Type text/plain; + return 418 'ssl_client_verify is SUCCESS'; +} diff --git a/test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt b/test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/nginx-proxy.tld.crt b/test/test_ssl/certs_mtls/nginx-proxy.tld.crt new file mode 100644 index 000000000..cd7284b06 --- /dev/null +++ b/test/test_ssl/certs_mtls/nginx-proxy.tld.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Jan 10 00:08:52 2017 GMT + Not After : May 28 00:08:52 2044 GMT + Subject: CN=*.nginx-proxy.tld + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:cb:45:f4:14:9b:fe:64:85:79:4a:36:8d:3d:d1: + 27:d0:7c:36:28:30:e6:73:80:6f:7c:49:23:d0:6c: + 17:e4:44:c0:77:4d:9a:c2:bc:24:84:e3:a5:4d:ba: + d2:da:51:7b:a1:2a:12:d4:c0:19:55:69:2c:22:27: + 2d:1a:f6:fc:4b:7f:e9:cb:a8:3c:e8:69:b8:d2:4f: + de:4e:50:e2:d0:74:30:7c:42:5a:ae:aa:85:a5:b1: + 71:4d:c9:7e:86:8b:62:8c:3e:0d:e3:3b:c3:f5:81: + 0b:8c:68:79:fe:bf:10:fb:ae:ec:11:49:6d:64:5e: + 1a:7d:b3:92:93:4e:96:19:3a:98:04:a7:66:b2:74: + 61:2d:41:13:0c:a4:54:0d:2c:78:fd:b4:a3:e8:37: + 78:9a:de:fa:bc:2e:a8:0f:67:14:58:ce:c3:87:d5: + 14:0e:8b:29:7d:48:19:b2:a9:f5:b4:e8:af:32:21: + 67:15:7e:43:52:8b:20:cf:9f:38:43:bf:fd:c8:24: + 7f:52:a3:88:f2:f1:4a:14:91:2a:6e:91:6f:fb:7d: + 6a:78:c6:6d:2e:dd:1e:4c:2b:63:bb:3a:43:9c:91: + f9:df:d3:08:13:63:86:7d:ce:e8:46:cf:f1:6c:1f: + ca:f7:4c:de:d8:4b:e0:da:bc:06:d9:87:0f:ff:96: + 45:85 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:*.nginx-proxy.tld + Signature Algorithm: sha256WithRSAEncryption + 6e:a5:0e:e4:d3:cc:d5:b7:fc:34:75:89:4e:98:8c:e7:08:06: + a8:5b:ec:13:7d:83:99:a2:61:b8:d5:12:6e:c5:b4:53:4e:9a: + 22:cd:ad:14:30:6a:7d:58:d7:23:d9:a4:2a:96:a0:40:9e:50: + 9f:ce:f2:fe:8c:dd:9a:ac:99:39:5b:89:2d:ca:e5:3e:c3:bc: + 03:04:1c:12:d9:6e:b8:9f:f0:3a:be:12:44:7e:a4:21:86:73: + af:d5:00:51:3f:2c:56:70:34:8f:26:b0:7f:b0:cf:cf:7f:f9: + 40:6f:00:29:c4:cf:c3:b7:c2:49:3d:3f:b0:26:78:87:b9:c7: + 6c:1b:aa:6a:1a:dd:c5:eb:f2:69:ba:6d:46:0b:92:49:b5:11: + 3c:eb:48:c7:2f:fb:33:a6:6a:82:a2:ab:f8:1e:5f:7d:e3:b7: + f2:fd:f5:88:a5:09:4d:a0:bc:f4:3b:cd:d2:8b:d7:57:1f:86: + 3b:d2:3e:a4:92:21:b0:02:0b:e9:e0:c4:1c:f1:78:e2:58:a7: + 26:5f:4c:29:c8:23:f0:6e:12:3f:bd:ad:44:7b:0b:bd:db:ba: + 63:8d:07:c6:9d:dc:46:cc:63:40:ba:5e:45:82:dd:9a:e5:50: + e8:e7:d7:27:88:fc:6f:1d:8a:e7:5c:49:28:aa:10:29:75:28: + c7:52:de:f9 +-----BEGIN CERTIFICATE----- +MIIC9zCCAd+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAeFw0xNzAxMTAwMDA4NTJaFw00NDA1MjgwMDA4NTJaMBwxGjAYBgNVBAMMESou +bmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +y0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02awrwkhOOlTbrS2lF7 +oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJarqqFpbFxTcl+hoti +jD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdmsnRhLUETDKRUDSx4 +/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFnFX5DUosgz584Q7/9 +yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MIE2OGfc7oRs/xbB/K +90ze2Evg2rwG2YcP/5ZFhQIDAQABoyAwHjAcBgNVHREEFTATghEqLm5naW54LXBy +b3h5LnRsZDANBgkqhkiG9w0BAQsFAAOCAQEAbqUO5NPM1bf8NHWJTpiM5wgGqFvs +E32DmaJhuNUSbsW0U06aIs2tFDBqfVjXI9mkKpagQJ5Qn87y/ozdmqyZOVuJLcrl +PsO8AwQcEtluuJ/wOr4SRH6kIYZzr9UAUT8sVnA0jyawf7DPz3/5QG8AKcTPw7fC +ST0/sCZ4h7nHbBuqahrdxevyabptRguSSbURPOtIxy/7M6ZqgqKr+B5ffeO38v31 +iKUJTaC89DvN0ovXVx+GO9I+pJIhsAIL6eDEHPF44linJl9MKcgj8G4SP72tRHsL +vdu6Y40Hxp3cRsxjQLpeRYLdmuVQ6OfXJ4j8bx2K51xJKKoQKXUox1Le+Q== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/nginx-proxy.tld.key b/test/test_ssl/certs_mtls/nginx-proxy.tld.key new file mode 100644 index 000000000..91adb14e1 --- /dev/null +++ b/test/test_ssl/certs_mtls/nginx-proxy.tld.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAy0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02a +wrwkhOOlTbrS2lF7oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJa +rqqFpbFxTcl+hotijD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdm +snRhLUETDKRUDSx4/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFn +FX5DUosgz584Q7/9yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MI +E2OGfc7oRs/xbB/K90ze2Evg2rwG2YcP/5ZFhQIDAQABAoIBAQCjAro2PNLJMfCO +fyjNRgmzu6iCmpR0U68T8GN0JPsT576g7e8J828l0pkhuIyW33lRSThIvLSUNf9a +dChL032H3lBTLduKVh4NKleQXnVFzaeEPoISSFVdButiAhAhPW4OIUVp0OfY3V+x +fac3j2nDLAfL5SKAtqZv363Py9m66EBYm5BmGTQqT/frQWeCEBvlErQef5RIaU8p +e2zMWgSNNojVai8U3nKNRvYHWeWXM6Ck7lCvkHhMF+RpbmCZuqhbEARVnehU/Jdn +QHJ3nxeA2OWpoWKXvAHtSnno49yxq1UIstiQvY+ng5C5i56UlB60UiU2NJ6doZkB +uQ7/1MaBAoGBAORdcFtgdgRALjXngFWhpCp0CseyUehn1KhxDCG+D1pJ142/ymcf +oJOzKJPMRNDdDUBMnR1GBfy7rmwvYevI/SMNy2Qs7ofcXPbdtwwvTCToZ1V9/54k +VfuPBFT+3QzWRvG1tjTV3E4L2VV3nrl2qNPhE5DlfIaU3nQq5Fl0HprJAoGBAOPf +MWOTGev61CdODO5KN3pLAoamiPs5lEUlz3kM3L1Q52YLITxNDjRj9hWBUATJZOS2 +pLOoYRwmhD7vrnimMc41+NuuFX+4T7hWPc8uSuOxX0VijYtULyNRK57mncG1Fq9M +RMLbOJ7FD+8jdXNsSMqpQ+pxLJRX/A10O2fOQnbdAoGAL5hV4YWSM0KZHvz332EI +ER0MXiCJN7HkPZMKH0I4eu3m8hEmAyYxVndBnsQ1F37q0xrkqAQ/HTSUntGlS/og +4Bxw5pkCwegoq/77tpto+ExDtSrEitYx4XMmSPyxX4qNULU5m3tzJgUML+b1etwD +Rd2kMU/TC02dq4KBAy/TbRkCgYAl1xN5iJz+XenLGR/2liZ+TWR+/bqzlU006mF4 +pZUmbv/uJxz+yYD5XDwqOA4UrWjuvhG9r9FoflDprp2XdWnB556KxG7XhcDfSJr9 +A5/2DadXe1Ur9O/a+oi2228JEsxQkea9QPA3FVxfBtFjOHEiDlez39VaUP4PMeUH +iO3qlQKBgFQhdTb7HeYnApYIDHLmd1PvjRvp8XKR1CpEN0nkw8HpHcT1q1MUjQCr +iT6FQupULEvGmO3frQsgVeRIQDbEdZK3C5xCtn6qOw70sYATVf361BbTtidmU9yV +THFxwDSVLiVZgFryoY/NtAc27sVdJnGsPRjjaeVgALAsLbmZ1K/H +-----END RSA PRIVATE KEY----- diff --git a/test/test_ssl/clientcerts/Revoked.crt b/test/test_ssl/clientcerts/Revoked.crt new file mode 100644 index 000000000..2fcd03ec9 --- /dev/null +++ b/test/test_ssl/clientcerts/Revoked.crt @@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + af:1a:d4:09:9e:cc:6e:7a:bd:3a:79:5a:ab:69:9d:27 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=nginx-proxy-test-suite + Validity + Not Before: Jan 3 12:28:03 2025 GMT + Not After : Jan 1 12:28:03 2035 GMT + Subject: CN=Revoked + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c0:ba:0d:13:f3:c9:8e:1a:e8:53:03:0e:fd:1e: + c1:a9:08:b5:7a:0a:64:47:9f:a2:1c:d1:da:4a:cb: + 9b:b5:ab:32:6e:34:03:ee:0d:54:cf:0d:1b:5b:0a: + b3:0c:f0:16:38:81:b2:d9:f9:7d:49:d4:b8:b6:d0: + bc:ce:25:ad:e2:8b:24:58:d6:77:3f:50:4e:32:0e: + 77:84:65:39:6d:41:4c:98:f6:dc:f4:01:e4:f1:b4: + 0d:43:da:54:ed:46:a7:eb:b7:fb:8e:51:dd:f8:77: + 60:be:76:be:43:14:7d:e7:2c:e3:46:0c:40:cc:b1: + 39:cc:7d:ee:67:18:c6:02:79:5e:4e:f3:25:4a:19: + c8:81:9f:d2:11:f0:84:14:ab:f6:e7:4a:b6:c6:e1: + a0:d1:4d:62:03:50:49:41:87:c2:79:32:b0:66:be: + a2:fa:08:31:f4:79:4c:cc:4a:4d:ad:81:3c:a6:6f: + e5:a1:41:d9:8c:f0:ba:68:50:f6:11:23:f7:27:8e: + 0d:2f:27:06:43:d0:b8:78:cd:ef:7d:6b:36:28:7e: + 7c:96:17:3b:6e:34:b7:4f:14:3c:b2:a6:79:98:9b: + 97:08:9b:19:ab:06:21:a9:fa:7d:55:c2:7b:7f:f4: + c1:e4:c7:5a:b8:0e:70:ef:1a:59:cd:05:bd:29:4f: + 43:67 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 13:7E:71:8D:FC:AD:51:DE:05:9F:5F:48:A0:BB:BB:C6:D9:26:1B:61 + X509v3 Authority Key Identifier: + keyid:F5:7E:4F:D6:61:7D:64:12:18:39:28:A4:A8:7E:34:6D:49:A9:81:16 + DirName:/CN=nginx-proxy-test-suite + serial:36:F4:8A:C0:24:42:9C:E0:13:B1:76:A5:B8:A4:84:DD:12:3A:7F:2D + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 50:ae:d0:c6:32:da:28:c4:5b:d9:35:9b:46:f1:f1:22:eb:40: + 88:e9:4b:3e:a6:f8:06:60:8e:e0:03:15:07:26:9d:51:04:db: + a8:5b:72:3e:55:cf:87:89:a9:c1:4d:94:07:cc:4a:ab:bf:a6: + e2:54:19:ee:ee:f8:cb:22:60:cb:fc:e6:25:1d:a0:b2:7f:61: + ed:de:2a:62:24:f2:14:eb:b3:e8:04:51:85:b9:c7:26:2e:42: + ef:94:bf:dd:67:b3:8d:44:78:14:4e:83:93:4e:c3:f3:75:82: + 00:9c:bc:aa:45:c2:45:80:30:43:28:ea:57:c8:a3:1b:fc:d7: + f7:9f:29:46:c1:05:8e:09:ff:e3:70:0b:fb:35:e7:94:15:dd: + 72:b9:e6:18:f7:68:65:57:cb:c7:31:c2:22:cc:94:a4:ee:3b: + b1:8b:90:80:56:da:6e:f7:01:84:74:e8:8b:65:ac:a2:1c:c4: + bd:e5:b6:d8:03:c2:97:6b:f0:da:24:fa:df:a0:1c:8b:f4:4f: + 47:f4:58:34:4f:de:40:99:7d:37:94:20:9f:0e:51:b1:49:c8: + 53:ef:39:b6:ea:25:ab:f4:4c:23:c2:a1:ae:40:8f:b0:bc:1e: + 6e:fa:4f:d9:2b:b3:4c:d0:55:cf:19:c5:7a:ed:52:4f:da:4a: + 24:03:7c:1f +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIRAK8a1AmezG56vTp5WqtpnScwDQYJKoZIhvcNAQELBQAw +ITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMxMjI4 +MDNaFw0zNTAxMDExMjI4MDNaMBIxEDAOBgNVBAMMB1Jldm9rZWQwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAug0T88mOGuhTAw79HsGpCLV6CmRHn6Ic +0dpKy5u1qzJuNAPuDVTPDRtbCrMM8BY4gbLZ+X1J1Li20LzOJa3iiyRY1nc/UE4y +DneEZTltQUyY9tz0AeTxtA1D2lTtRqfrt/uOUd34d2C+dr5DFH3nLONGDEDMsTnM +fe5nGMYCeV5O8yVKGciBn9IR8IQUq/bnSrbG4aDRTWIDUElBh8J5MrBmvqL6CDH0 +eUzMSk2tgTymb+WhQdmM8LpoUPYRI/cnjg0vJwZD0Lh4ze99azYofnyWFztuNLdP +FDyypnmYm5cImxmrBiGp+n1Vwnt/9MHkx1q4DnDvGlnNBb0pT0NnAgMBAAGjga0w +gaowCQYDVR0TBAIwADAdBgNVHQ4EFgQUE35xjfytUd4Fn19IoLu7xtkmG2EwXAYD +VR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNVBAMMFm5n +aW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8tMBMGA1Ud +JQQMMAoGCCsGAQUFBwMCMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEA +UK7QxjLaKMRb2TWbRvHxIutAiOlLPqb4BmCO4AMVByadUQTbqFtyPlXPh4mpwU2U +B8xKq7+m4lQZ7u74yyJgy/zmJR2gsn9h7d4qYiTyFOuz6ARRhbnHJi5C75S/3Wez +jUR4FE6Dk07D83WCAJy8qkXCRYAwQyjqV8ijG/zX958pRsEFjgn/43AL+zXnlBXd +crnmGPdoZVfLxzHCIsyUpO47sYuQgFbabvcBhHToi2WsohzEveW22APCl2vw2iT6 +36Aci/RPR/RYNE/eQJl9N5Qgnw5RsUnIU+85tuolq/RMI8KhrkCPsLwebvpP2Suz +TNBVzxnFeu1ST9pKJAN8Hw== +-----END CERTIFICATE----- diff --git a/test/test_ssl/clientcerts/Revoked.key b/test/test_ssl/clientcerts/Revoked.key new file mode 100644 index 000000000..42f0dc8cb --- /dev/null +++ b/test/test_ssl/clientcerts/Revoked.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAug0T88mOGuhT +Aw79HsGpCLV6CmRHn6Ic0dpKy5u1qzJuNAPuDVTPDRtbCrMM8BY4gbLZ+X1J1Li2 +0LzOJa3iiyRY1nc/UE4yDneEZTltQUyY9tz0AeTxtA1D2lTtRqfrt/uOUd34d2C+ +dr5DFH3nLONGDEDMsTnMfe5nGMYCeV5O8yVKGciBn9IR8IQUq/bnSrbG4aDRTWID +UElBh8J5MrBmvqL6CDH0eUzMSk2tgTymb+WhQdmM8LpoUPYRI/cnjg0vJwZD0Lh4 +ze99azYofnyWFztuNLdPFDyypnmYm5cImxmrBiGp+n1Vwnt/9MHkx1q4DnDvGlnN +Bb0pT0NnAgMBAAECggEALzFf4nLf+Bw+p5UoJnNRmMK5LZk91QwR9lysx4P0LRgu +0S2LiM9a5RigijqkfZaM2mloElg1hc7BLIMQuKohWkgYLmjV6nsPqtJAEft3hHlo ++Ev67wVHuqgMV4EvKqsSk3YJ81+4qw8QcZNCI8rwyZsETDLT60u6i4iKyFQYqKIC +ivkWxI1WUTe1VYfsL3peTlCfT3oYUR7vk6+gJ6vLgCA+V8jjoAh++MxkPCTEU2vL +BRx0jJYy5C7hyNTzHz0Xq+vu9m+DzLmP0/maUMlsKACF5RsGfO5Jdp499LL//sC0 +/5Z5rC55sJs8/bHGt8nA5N8qNLpTNDSTwuAT5fMqgQKBgQDv4tY2NWND+p9+RIdS +Uc4XsXbvj3Se3Rsx4FB8ApX89KdKUO74dBJ8/0lGttNApYoWL44TfmJDFemmC5eT ++ya7BwQLPmOKnhzc9U8DKziFEQhWrF2FndUm3prAlWolqe/5ddoTel51fEgXooEP +2M4kwBv6Y7DWA+4X72Fvk8tL0wKBgQDNrD8bh4C+vmsqf5u0EV/ST14nLtpg+jS6 +r+a6PfzzhmYos249ZVoHog4f+0mcmO/F+K1X/XAPBnDoFWgHJ9ooVIykO1D5ias8 +CBEzHEiw0E3ayMkYNR1bRbo9ykTuUjNXH7ctdCl+QEAXM2cf83VXvc8fsXQxyRpD +iJZpkExRnQKBgA3zD+fZFOuoEExEaeYUkbp4/GST4AE49FLjK2r6r4QlKfE9YZgb +D9Qq+DTffstclPoTS9zAVbB2/r5EIE1fpnHrx4Vr3Ff8N8t1jGGvyrqaMfTwUwPp +GLMI8NDQH3sPNcA45TSKwiFs17hgH8cvIVWrwjS+RVM8qUTFC9J0Mrc3AoGACeq8 +QD+QXaIg2LO+djhAPovFJm6D6RknYbkJjwFeKP/Z/SxprFwusx+FPtWG/x4AsbMD +6LI3rQHKf+ZIVc/+HOO2xFR32xBgSUy6R5SdjKj+mAYGbDxjZfs+t6wBFtyvzQui +cXagaY3/iR7ZYhkDF2/3hLexupTPx2HWntBuXaECgYBLXINRHusQaJLN1Jjxe22h +9Tx6mPNdnM0KzNeg16F+ix1O5hArNUXaHEwPj6XX6mOq1HbM6crKeGXEzaTw7vTa +o82m4nYjxa+Yh4nDiH0/4bxjZxLRl83+Y13PSxv87lqWXNdzsGdpzkWRasrrs+0J +85NwFxyxcH1wiylc86g0BQ== +-----END PRIVATE KEY----- diff --git a/test/test_ssl/clientcerts/Valid.crt b/test/test_ssl/clientcerts/Valid.crt new file mode 100644 index 000000000..03558ff9c --- /dev/null +++ b/test/test_ssl/clientcerts/Valid.crt @@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + e8:b3:fd:e8:a7:be:5e:a0:ea:3f:23:33:bc:2d:72:da + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=nginx-proxy-test-suite + Validity + Not Before: Jan 3 12:21:59 2025 GMT + Not After : Jan 1 12:21:59 2035 GMT + Subject: CN=Valid + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d8:40:8d:5d:2e:a1:19:de:c5:20:01:fe:dc:9a: + ee:15:fe:9e:e1:30:a0:c7:a2:5a:22:dd:99:6e:dd: + 34:4d:6e:ad:ec:32:17:a6:e4:37:99:11:ea:67:b1: + db:1f:52:7c:17:c8:02:46:04:7f:86:b3:54:6e:55: + 18:26:af:e8:ce:84:c4:52:7c:42:fd:37:ca:ce:bf: + 88:65:09:30:4d:91:55:6f:33:0d:66:bd:e9:49:08: + 4f:65:a3:bd:41:17:67:78:57:41:94:55:6f:da:91: + cb:48:da:a1:df:fb:7d:35:16:c8:1c:ef:9a:d7:fe: + c9:19:4b:c5:89:e1:29:da:5d:01:8f:99:d4:45:da: + 9b:a8:a2:d6:f1:c2:83:36:f5:8f:bd:4b:6a:54:03: + 37:6b:d8:62:e9:15:31:97:34:43:15:a5:6a:df:2f: + b6:df:19:90:4e:53:c5:42:2a:95:b0:94:43:03:02: + 73:a4:f0:20:10:77:71:5d:55:9c:f8:d4:08:11:07: + 94:c6:bb:45:62:93:c5:bd:61:95:75:49:27:ea:b5: + e4:0b:be:27:52:61:9d:cd:11:c8:12:b3:29:d9:3a: + e9:80:6c:65:44:b0:84:cd:7f:8d:e6:ce:07:86:1c: + c1:a2:75:2f:bf:e4:66:b1:b2:b2:2f:5a:fc:44:82: + e4:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + ED:86:4A:75:0A:B2:ED:D1:B8:D9:88:3C:BE:42:DF:8B:18:C3:99:E8 + X509v3 Authority Key Identifier: + keyid:F5:7E:4F:D6:61:7D:64:12:18:39:28:A4:A8:7E:34:6D:49:A9:81:16 + DirName:/CN=nginx-proxy-test-suite + serial:36:F4:8A:C0:24:42:9C:E0:13:B1:76:A5:B8:A4:84:DD:12:3A:7F:2D + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 25:aa:b5:6e:07:0a:3e:d2:ed:61:33:6b:48:ef:81:41:3b:7f: + 02:1c:58:74:13:25:7f:29:d2:5e:14:31:10:7a:12:81:9b:9f: + 6f:3b:b2:3e:07:32:d8:f8:3b:15:5a:18:ec:00:41:15:b1:49: + ea:c1:38:d1:7f:c9:55:bd:dd:99:a0:b9:a8:80:76:f1:6c:db: + 75:bd:23:23:2b:e4:71:9b:d7:fd:74:42:51:31:29:cd:28:32: + a9:d9:09:c5:1f:48:93:e4:6d:e6:45:97:4e:a8:4e:bd:48:d1: + e7:45:e5:34:88:74:aa:98:a3:8d:03:af:c1:10:5b:1d:ec:cb: + 55:90:7c:b4:4c:d0:33:92:63:81:eb:39:50:2b:6e:39:e3:1b: + 74:9d:67:1b:55:76:95:af:8e:83:24:47:c2:fc:aa:c5:7d:f1: + 10:e2:d1:8b:7a:b4:da:5e:40:c4:ce:32:e1:93:d9:17:76:fa: + a2:5d:a8:48:73:ff:0a:b4:b3:ac:97:e4:8b:6e:18:56:09:e0: + 05:be:42:3e:c6:8e:30:cf:29:da:0f:a1:05:c0:7d:3d:8f:8f: + 52:27:e0:dd:06:f2:06:5b:8a:40:e5:0f:5c:a5:3b:16:10:32: + 49:9f:9c:ca:d8:02:9f:f2:5c:b1:20:96:b9:b0:ff:87:7a:20: + 15:1c:4b:88 +-----BEGIN CERTIFICATE----- +MIIDajCCAlKgAwIBAgIRAOiz/einvl6g6j8jM7wtctowDQYJKoZIhvcNAQELBQAw +ITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMxMjIx +NTlaFw0zNTAxMDExMjIxNTlaMBAxDjAMBgNVBAMMBVZhbGlkMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ECNXS6hGd7FIAH+3JruFf6e4TCgx6JaIt2Z +bt00TW6t7DIXpuQ3mRHqZ7HbH1J8F8gCRgR/hrNUblUYJq/ozoTEUnxC/TfKzr+I +ZQkwTZFVbzMNZr3pSQhPZaO9QRdneFdBlFVv2pHLSNqh3/t9NRbIHO+a1/7JGUvF +ieEp2l0Bj5nURdqbqKLW8cKDNvWPvUtqVAM3a9hi6RUxlzRDFaVq3y+23xmQTlPF +QiqVsJRDAwJzpPAgEHdxXVWc+NQIEQeUxrtFYpPFvWGVdUkn6rXkC74nUmGdzRHI +ErMp2TrpgGxlRLCEzX+N5s4HhhzBonUvv+RmsbKyL1r8RILk8QIDAQABo4GtMIGq +MAkGA1UdEwQCMAAwHQYDVR0OBBYEFO2GSnUKsu3RuNmIPL5C34sYw5noMFwGA1Ud +IwRVMFOAFPV+T9ZhfWQSGDkopKh+NG1JqYEWoSWkIzAhMR8wHQYDVQQDDBZuZ2lu +eC1wcm94eS10ZXN0LXN1aXRlghQ29IrAJEKc4BOxdqW4pITdEjp/LTATBgNVHSUE +DDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBACWq +tW4HCj7S7WEza0jvgUE7fwIcWHQTJX8p0l4UMRB6EoGbn287sj4HMtj4OxVaGOwA +QRWxSerBONF/yVW93ZmguaiAdvFs23W9IyMr5HGb1/10QlExKc0oMqnZCcUfSJPk +beZFl06oTr1I0edF5TSIdKqYo40Dr8EQWx3sy1WQfLRM0DOSY4HrOVArbjnjG3Sd +ZxtVdpWvjoMkR8L8qsV98RDi0Yt6tNpeQMTOMuGT2Rd2+qJdqEhz/wq0s6yX5Itu +GFYJ4AW+Qj7GjjDPKdoPoQXAfT2Pj1In4N0G8gZbikDlD1ylOxYQMkmfnMrYAp/y +XLEglrmw/4d6IBUcS4g= +-----END CERTIFICATE----- diff --git a/test/test_ssl/clientcerts/Valid.key b/test/test_ssl/clientcerts/Valid.key new file mode 100644 index 000000000..ff63056fb --- /dev/null +++ b/test/test_ssl/clientcerts/Valid.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYQI1dLqEZ3sUg +Af7cmu4V/p7hMKDHoloi3Zlu3TRNbq3sMhem5DeZEepnsdsfUnwXyAJGBH+Gs1Ru +VRgmr+jOhMRSfEL9N8rOv4hlCTBNkVVvMw1mvelJCE9lo71BF2d4V0GUVW/akctI +2qHf+301Fsgc75rX/skZS8WJ4SnaXQGPmdRF2puootbxwoM29Y+9S2pUAzdr2GLp +FTGXNEMVpWrfL7bfGZBOU8VCKpWwlEMDAnOk8CAQd3FdVZz41AgRB5TGu0Vik8W9 +YZV1SSfqteQLvidSYZ3NEcgSsynZOumAbGVEsITNf43mzgeGHMGidS+/5GaxsrIv +WvxEguTxAgMBAAECggEAA9DrzbOncyHrv5tZaIIxZmD2Y8nsZGl4hrn7Xan4pM8p +sYvWwAKx+nkG9mp8j+pwNyk35Q1qRLHAcSv+P5yGErLkFgKMWhSUdx6JJSNK32uL +ouk3ONNsHPZRFF2V1uK3WDfN9/SkAyrkd/YnuiChfoDW6i/OPzaHTPN52muHRguq +z5Iaw9BTTJXFhzdLqd+U6JfdXpn6brmoySXVANYwi/Ug470pR8wyulTIXIhyu8gc +9O9U/QTfpV3uRIxCdci72w9UICVHljPG1ARW1JAZsmTAgxpioGjXcwn5soSNlQ4g +dO6MJ9M7Y/amkvLfAh0Cabj4vRrEJ83TGKaYhlAquQKBgQDwKtQdftvxrQtQiU0Y +7zglGtjBRKtVjb3rcgRs2cvU1GITjIEAuMyMHnyJmGDGRVPdI2us7EZwtpsOmlk1 +nZppLOe478GBA7twaS6vONxBbegZTBtSClyktY8RG5g/siIE6y3m+JcJnZO7XELA +p1r1JUeVLWXJkO2dmgpDjVJIPQKBgQDmgh71G4iLdIxSR/VGmrHX8kwnBRV3rn2+ +O0XcY0XQZPk4XL4zx3Qw5QUC7QMxikaLDQOXKZXylyjyGGyFAjJI8HrLr7Sm7xb4 +g91EjlfQWmTjrvI/6RBndwsExiEzgkASDxm6iOMXFx658tDK6dIsZWhh/59thUVE +u+ZSBp1mxQKBgQDfQVDxIk5fONc9xISw2x+8DlrUPntvClY0GkdW0JeUfuG0/nWl +MCSlVGm8lrPPW/77oMOlefZ5LKazSnQHTTyO7LlzxxyAS/HgK0bEh/znrb2GVqNG +/m7khgo6gwZin7rUC7Md9JSi0aLVFozO/kOlg0QpvovSdjEMwncsGKEWmQKBgQDS +TiuSc1Fr8qTHuVFF3oOdwznJa/D/JZshwZBml8gtbsKWsr7yHOqcZYbh+X4tZ7we +x3vcIZvmHhXEc5Ym8C8Srx1J0wAeQgsSJ7TsBHaH6MEdnhL1Tl2iGFFcRKwsA40T +LOXLc3LFMVneS3RFfXk8+jR3HLLHSI0/PbPQaKqZBQKBgHggyUNuWtSwWTByicnU +MmGv+fb+0NEFN/c+lf2HGb5GFifhFTppAi2L+FT/D5cW+vFpC0f/O7m+ymIoa5YC +fjLvUE6L83D17H/TN34pwxpUxZg/cWl3HkVHRVLnpm4L5HKnt2Qj7q1n4q2FTjhR +vuihVh5K2G3n+Sysf1rViP5I +-----END PRIVATE KEY----- diff --git a/test/test_ssl/test_mtls-client-certificate.py b/test/test_ssl/test_mtls-client-certificate.py new file mode 100644 index 000000000..c7c03aea3 --- /dev/null +++ b/test/test_ssl/test_mtls-client-certificate.py @@ -0,0 +1,59 @@ +import pathlib + +import pytest +from requests.exceptions import SSLError + + +@pytest.fixture(scope="session") +def clientcerts(): + """ + Pytest fixture to provide paths to client certificates and keys. + """ + current_file_path = pathlib.Path(__file__) + clientcerts_path = current_file_path.parent.joinpath("clientcerts") + + return { + "valid_client_cert": clientcerts_path.joinpath("Valid.crt"), + "valid_client_key": clientcerts_path.joinpath("Valid.key"), + "revoked_client_cert": clientcerts_path.joinpath("Revoked.crt"), + "revoked_client_key": clientcerts_path.joinpath("Revoked.key"), + } + +@pytest.mark.parametrize("description, url, cert, expected_code, expected_text", [ + #Enforced: Test connection to a website with mTLS enabled without providing a client certificate. + ("Enforced: No client certificate, virtual_host", "https://mtls-enabled.nginx-proxy.tld/port", None, 400, "400 No required SSL certificate was sent"), + ("Enforced: No client certificate, virtual_path", "https://mtls-enabled.nginx-proxy.tld/bar/port", None, 400, "400 No required SSL certificate was sent"), + ("Enforced: No client certificate, regex", "https://regex.nginx-proxy.tld/port", None, 400, "400 No required SSL certificate was sent"), + ("Enforced: No client certificate, global CA", "https://global-mtls-enabled.nginx-proxy.tld/port", None, 400, "400 No required SSL certificate was sent"), + #Authenticated: Test connection to a website with mTLS enabled providing a valid client certificate. + ("Authenticated: Valid client certificate, virtual_host", "https://mtls-enabled.nginx-proxy.tld/port", "valid", 200, "answer from port 81\n"), + ("Authenticated: Valid client certificate, virtual_path", "https://mtls-enabled.nginx-proxy.tld/bar/port", "valid", 200, "answer from port 83\n"), + ("Authenticated: Valid client certificate, regex", "https://regex.nginx-proxy.tld/port", "valid", 200, "answer from port 85\n"), + ("Authenticated: Valid client certificate, global CA", "https://global-mtls-enabled.nginx-proxy.tld/port", "valid", 200, "answer from port 81\n"), + #Revoked: Test connection to a website with mTLS enabled providing a revoked client certificate on the CRL. + ("Revoked: Invalid client certificate, virtual_host", "https://mtls-enabled.nginx-proxy.tld/port", "revoked", 400, "400 The SSL certificate error"), + ("Revoked: Invalid client certificate, virtual_path", "https://mtls-enabled.nginx-proxy.tld/bar/port", "revoked", 400, "400 The SSL certificate error"), + ("Revoked: Invalid client certificate, regex", "https://regex.nginx-proxy.tld/port", "revoked", 400, "400 The SSL certificate error"), + ("Revoked: Invalid client certificate, global CA", "https://global-mtls-enabled.nginx-proxy.tld/port", "revoked", 400, "400 The SSL certificate error"), + #Optional: Test connection to a website with optional mTLS. Access is not blocked but can be controlled with "$ssl_client_verify" directive. We assert on /foo if $ssl_client_verify = SUCCESS response with status code 418. + ("Optional, Not enforced: No client certificate", "https://mtls-optional.nginx-proxy.tld/port", None, 200, "answer from port 82\n"), + ("Optional: Enforced, Valid client certificate", "https://mtls-optional.nginx-proxy.tld/foo/port", "valid", 418, "ssl_client_verify is SUCCESS"), + ("Optional, Not enforced: No client certificate", "https://mtls-optional.nginx-proxy.tld/bar/port", None, 200, "answer from port 84\n"), + ("Optional: Enforced, Valid client certificate", "https://mtls-optional.nginx-proxy.tld/foo/bar/port", "valid", 418, "ssl_client_verify is SUCCESS"), + ("Optional, Not enforced: No client certificate, global CA", "https://global-mtls-optional.nginx-proxy.tld/port", None, 200, "answer from port 82\n"), + ("Optional: Enforced, Valid client certificate, global CA", "https://global-mtls-optional.nginx-proxy.tld/foo/port", "valid", 418, "ssl_client_verify is SUCCESS"), +]) +def test_mtls_client_certificates(docker_compose, nginxproxy, clientcerts, description, url, cert, expected_code, expected_text): + """ + Parameterized test for mTLS client certificate scenarios. + """ + if cert == "valid": + client_cert = (clientcerts["valid_client_cert"], clientcerts["valid_client_key"]) + elif cert == "revoked": + client_cert = (clientcerts["revoked_client_cert"], clientcerts["revoked_client_key"]) + else: + client_cert = None + + r = nginxproxy.get(url, cert=client_cert if client_cert else None) + assert r.status_code == expected_code + assert expected_text in r.text diff --git a/test/test_ssl/test_mtls-client-certificate.yml b/test/test_ssl/test_mtls-client-certificate.yml new file mode 100644 index 000000000..99edc83fd --- /dev/null +++ b/test/test_ssl/test_mtls-client-certificate.yml @@ -0,0 +1,69 @@ +services: + mtls-vhost-enabled: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "mtls-enabled.nginx-proxy.tld,global-mtls-enabled.nginx-proxy.tld" + + mtls-vhost-optional: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST_MULTIPORTS: |- + mtls-optional.nginx-proxy.tld: + "/": + dest: "/" + "/foo": + dest: "/" + global-mtls-optional.nginx-proxy.tld: + "/": + dest: "/" + "/foo": + dest: "/" + labels: + com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional" + + mtls-vpath-enabled: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "mtls-enabled.nginx-proxy.tld" + VIRTUAL_PATH: /bar/ + VIRTUAL_DEST: / + + mtls-vpath-optional: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST_MULTIPORTS: |- + mtls-optional.nginx-proxy.tld: + "/bar": + dest: "/" + "/foo/bar": + dest: "/" + labels: + com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional" + + mtls-regex-enabled: + image: web + expose: + - "85" + environment: + WEB_PORTS: "85" + VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.tld$ + CERT_NAME: nginx-proxy.tld + + nginx-proxy: + volumes: + - ${PYTEST_MODULE_PATH}/certs_mtls:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/certs_mtls/mtls-optional-foo-bar_location:/etc/nginx/vhost.d/mtls-optional.nginx-proxy.tld_6dbd548cc03e44b8b44b6e68e56255ce4273ae49_location:ro #/foo + - ${PYTEST_MODULE_PATH}/certs_mtls/mtls-optional-foo-bar_location:/etc/nginx/vhost.d/mtls-optional.nginx-proxy.tld_a82cce35fd860de6f63f97e6c482dc6a14d002e8_location:ro #/bar + - ${PYTEST_MODULE_PATH}/certs_mtls/mtls-optional-foo-bar_location:/etc/nginx/vhost.d/global-mtls-optional.nginx-proxy.tld_6dbd548cc03e44b8b44b6e68e56255ce4273ae49_location:ro #/foo From 7edf50a9e5deb3f8935264a92e4b58b0383c03a5 Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:42:25 +0200 Subject: [PATCH 24/35] docs: SSL client certificate validation mTLS --- docs/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/README.md b/docs/README.md index f14bc2be7..d5f714b79 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,6 +5,7 @@ - [Docker Networking](#docker-networking) - [Upstream (Backend) features](#upstream-backend-features) - [Basic Authentication Support](#basic-authentication-support) +- [mTLS client side certificate authentication](#mtls-client-side-certificate-authentication) - [Logging](#logging) - [SSL Support](#ssl-support) - [IPv6 Support](#ipv6-nat) @@ -374,6 +375,36 @@ You'll need apache2-utils on the machine where you plan to create the htpasswd f ⬆️ [back to table of contents](#table-of-contents) +## mTLS client side certificate authentication +In mTLS, both the client and server have a certificate, and both sides authenticate using their public/private key pair. +A "root" TLS certificate is necessary for mTLS; this enables an organization to be their own certificate authority. The certificates used by authorized clients and servers have to correspond to this root certificate. The root certificate is self-signed, meaning that the organization creates it themselves. +Make sure you have a root certificate (CA) and client public/private key pair. There is a [howto in the wiki](https://github.com/nginx-proxy/nginx-proxy/wiki/mTLS-client-side-certificate-authentication). + +### Certificate Authority (CA) +#### Per-VIRTUAL_HOST CA +In order to secure a virtual host, you have to copy your CA certificate file (ca.crt) named as its equivalent `VIRTUAL_HOST` variable or if `VIRTUAL_HOST` is a regex, after the sha1 hash of the regex with the suffix `.ca.crt` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/app.example.com.ca.crt`. +Or if your `VIRTUAL_HOST` is a regex: `/etc/nginx/certs/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt`. + +#### Global CA +If you want to secure everything globally you can copy your CA certificate file (ca.crt) named as `ca.crt` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/ca.crt`. + +### Certificate Revocation List (CRL) +#### Per-VIRTUAL_HOST CRL +In order to use a certificate revocation list, you have to copy your CRL file named as its equivalent `VIRTUAL_HOST` variable or if `VIRTUAL_HOST` is a regex, after the sha1 hash of the regex with the suffix `.crl.pem` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/app.example.com.crl.pem`. +Or if your `VIRTUAL_HOST` is a regex: `/etc/nginx/certs/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem`. + +#### Global CRL +If you want to use a global CRL file you have to copy your CRL file named as `ca.crl.pem` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/ca.crl.pem`. + +### optional ssl_verify_client +Optional [`ssl_verify_client`](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client) can be activated by using the `com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional"` label on a proxied container. If this label is set on a proxied container access is not blocked but the result of the mTLS verify is stored in the [$ssl_client_verify](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify) variable which you can use this in the [Per-VIRTUAL_HOST location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_host-location-configuration) and [Per-VIRTUAL_PATH location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_path-location-configuration) configurations. + +⬆️ [back to table of contents](#table-of-contents) + ## Logging The default nginx access log format is @@ -1327,6 +1358,7 @@ Configuration available on each proxied container, either by environment variabl | n/a | [`com.github.nginx-proxy.nginx-proxy.non-get-redirect`](#how-ssl-support-works) | global (proxy) value | | [`SERVER_TOKENS`](#per-virtual_host-server_tokens-configuration) | n/a | no default value | | [`SSL_POLICY`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.ssl_verify_client`](#optional-ssl_verify_client) | `on` | | n/a | [`com.github.nginx-proxy.nginx-proxy.trust-default-cert`](#default-and-missing-certificate) | global (proxy) value | | [`VIRTUAL_DEST`](#virtual_dest) | n/a | `empty string` | | [`VIRTUAL_HOST`](#virtual-hosts-and-ports) | n/a | no default value | From 6a92955a16dc74977355017c8bd30ccf9cc5502c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 05:02:04 +0000 Subject: [PATCH 25/35] ci: bump pytest from 8.3.5 to 8.4.0 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.5 to 8.4.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.5...8.4.0) --- updated-dependencies: - dependency-name: pytest dependency-version: 8.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- test/requirements/python-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/requirements/python-requirements.txt b/test/requirements/python-requirements.txt index 7a5eead5c..b3cb14b83 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,6 +1,6 @@ backoff==2.2.1 docker==7.1.0 packaging==25.0 -pytest==8.3.5 +pytest==8.4.0 requests==2.32.3 urllib3==2.4.0 From 03403dedd679b28863eddae37fb33593753efb4f Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Tue, 10 Jun 2025 10:40:00 +0200 Subject: [PATCH 26/35] chore: styling --- nginx.tmpl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 6dbea625b..939aa2e9c 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1047,18 +1047,18 @@ server { {{- if (exists (printf "/etc/nginx/certs/%s.ca.crt" $vhostFileName)) }} ssl_client_certificate {{ printf "/etc/nginx/certs/%s.ca.crt" $vhostFileName }}; ssl_verify_client {{ $vhost.ssl_verify_client }}; - {{/* If vhost(hash).crl.pem exists, include CRL */}} - {{- if (exists (printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName)) }} + {{/* If vhost(hash).crl.pem exists, include CRL */}} + {{- if (exists (printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName)) }} ssl_crl {{ printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName }}; - {{ end }} - {{/* If no vhost CA file exists, but a global ca.crt exists include it */}} + {{ end }} + {{/* Else if no vhost CA file exists, but a global ca.crt exists include it */}} {{ else if (exists "/etc/nginx/certs/ca.crt") }} ssl_client_certificate /etc/nginx/certs/ca.crt; ssl_verify_client {{ $vhost.ssl_verify_client }}; - {{/* If no vhost CA file exists, but a global ca.crl.pem exists include it */}} - {{ if (exists "/etc/nginx/certs/ca.crl.pem")}} + {{/* If no vhost CA file exists, but a global ca.crl.pem exists include it */}} + {{ if (exists "/etc/nginx/certs/ca.crl.pem")}} ssl_crl /etc/nginx/certs/ca.crl.pem; - {{ end }} + {{ end }} {{ end }} {{- if $vhost.enable_debug_endpoint }} From df85d14f6c388c65457eb4c9f2df593f0eeeb8ac Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Tue, 10 Jun 2025 10:40:54 +0200 Subject: [PATCH 27/35] docs: note about global and per-vhost CRL --- docs/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/README.md b/docs/README.md index d5f714b79..feeb5f1d0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -400,6 +400,9 @@ Or if your `VIRTUAL_HOST` is a regex: `/etc/nginx/certs/9ae5d1b655182b052fed458e If you want to use a global CRL file you have to copy your CRL file named as `ca.crl.pem` in directory `/etc/nginx/certs/`. Example: `/etc/nginx/certs/ca.crl.pem`. +> [!NOTE] +> Use Per-VIRTUAL_HOST CRL if you configured the [Per-VIRTUAL_HOST CA](#per-virtual_host-ca) or Global CRL if you configured the [Global CA](#global-ca) + ### optional ssl_verify_client Optional [`ssl_verify_client`](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client) can be activated by using the `com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional"` label on a proxied container. If this label is set on a proxied container access is not blocked but the result of the mTLS verify is stored in the [$ssl_client_verify](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify) variable which you can use this in the [Per-VIRTUAL_HOST location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_host-location-configuration) and [Per-VIRTUAL_PATH location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_path-location-configuration) configurations. From 0cf0e80aa9eca5b89fd3cca1c2324476f67334ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 04:13:45 +0000 Subject: [PATCH 28/35] ci: bump requests from 2.32.3 to 2.32.4 in /test/requirements Bumps [requests](https://github.com/psf/requests) from 2.32.3 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- test/requirements/python-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/requirements/python-requirements.txt b/test/requirements/python-requirements.txt index b3cb14b83..899f52a87 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -2,5 +2,5 @@ backoff==2.2.1 docker==7.1.0 packaging==25.0 pytest==8.4.0 -requests==2.32.3 +requests==2.32.4 urllib3==2.4.0 From f7d3c59ffc0ea44cf3333c1799a296c433f63df4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 05:12:55 +0000 Subject: [PATCH 29/35] ci: bump urllib3 from 2.4.0 to 2.5.0 in /test/requirements Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.4.0 to 2.5.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.4.0...2.5.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- test/requirements/python-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/requirements/python-requirements.txt b/test/requirements/python-requirements.txt index 899f52a87..64b143606 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -3,4 +3,4 @@ docker==7.1.0 packaging==25.0 pytest==8.4.0 requests==2.32.4 -urllib3==2.4.0 +urllib3==2.5.0 From a212d80509ffc702853b8c31c961307e35aceedd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 06:27:47 +0000 Subject: [PATCH 30/35] ci: bump pytest from 8.4.0 to 8.4.1 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.0 to 8.4.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.4.0...8.4.1) --- updated-dependencies: - dependency-name: pytest dependency-version: 8.4.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- test/requirements/python-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/requirements/python-requirements.txt b/test/requirements/python-requirements.txt index 64b143606..ca89fd32c 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,6 +1,6 @@ backoff==2.2.1 docker==7.1.0 packaging==25.0 -pytest==8.4.0 +pytest==8.4.1 requests==2.32.4 urllib3==2.5.0 From a13372d0259e4368a271fd24ef61e6697ca6e942 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 04:30:01 +0000 Subject: [PATCH 31/35] build: bump library/nginx from 1.27.5 to 1.29.0 Bumps library/nginx from 1.27.5 to 1.29.0. --- updated-dependencies: - dependency-name: library/nginx dependency-version: 1.29.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Dockerfile.alpine | 2 +- Dockerfile.debian | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 36d6867c7..7996af553 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -4,7 +4,7 @@ FROM docker.io/nginxproxy/docker-gen:0.14.7 AS docker-gen FROM docker.io/nginxproxy/forego:0.18.3 AS forego # Build the final image -FROM docker.io/library/nginx:1.27.5-alpine +FROM docker.io/library/nginx:1.29.0-alpine ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable because diff --git a/Dockerfile.debian b/Dockerfile.debian index 5b612a8d3..ac1dc084d 100644 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -4,7 +4,7 @@ FROM docker.io/nginxproxy/docker-gen:0.14.7-debian AS docker-gen FROM docker.io/nginxproxy/forego:0.18.3-debian AS forego # Build the final image -FROM docker.io/library/nginx:1.27.5 +FROM docker.io/library/nginx:1.29.0 ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable because From f185cd43b7a8c94aed511c646f81cad83cbde4b6 Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Wed, 9 Jul 2025 12:25:46 +0200 Subject: [PATCH 32/35] ci: recreate CRL with 3650 days expiration Recreate CRL files with 3650 days (10 years) expire date. --- ...ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem | 14 +++++++------- test/test_ssl/certs_mtls/ca.crl.pem | 14 +++++++------- .../mtls-enabled.nginx-proxy.tld.crl.pem | 14 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem index a4bbd9562..de292c16d 100644 --- a/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem +++ b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem @@ -1,13 +1,13 @@ -----BEGIN X509 CRL----- MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 -eS10ZXN0LXN1aXRlFw0yNTAxMDMxMjMwNTBaFw0yNTA3MDIxMjMwNTBaMDIwMAIR +eS10ZXN0LXN1aXRlFw0yNTA3MDkxMDEyNDFaFw0zNTA3MDcxMDEyNDFaMDIwMAIR AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t -MA0GCSqGSIb3DQEBCwUAA4IBAQCGaKW8kJy1Mznc3T2OHkCx8GudvOo0ZBsZ+pTm -sAnlxDQTIqm8e4gU19WF/SISlfr7qEERqif8+SlUgS9CWtJa70gk+9oobuWfBNIT -VXD4ujO/47nqt2MdRUSSGX+K+9Ox2gyU6kHO1ZrT8VmsL22Bhfa2Pw/3OBL/QHMU -b1hAZyed0CoPCnMqjG0X5zMo3ByGW3TkxG2GhzKCWLGXVbzdHFpS98hpkpaxvIlE -juSYuPItwEftHdB8JHAHL18uDJapZ5mOCuUn/HoZBWOudFjtFQUUzq4eTsB56My4 -qDGb1/ReAoGyheuV0fEtg9MJkGEuGrb38JN6hcdfpW5u0Hwb +MA0GCSqGSIb3DQEBCwUAA4IBAQAji33L7enDzhw8qNYLtMxrJuuLAMJeRDO4qYeI +pIJu38K+9RTKG2U/BPPKmdtos/M1NEVJrLqZ/eKHoEU/+u0f1pod3Vh2tAlyB+qp +aGwsg5o07hdB85VDAJ7zwPLFjHtChhhVTS5qOqidaSdVBE0/IFifWBEyHyC7yJDl +dlNY7jmarlmFnpDWmXqAdgMqNlS/t9KN8RtCjiHlF8lF+qjimCWAcfecMmdbAUFC +RFHmo6ENxmcDXQDRVqKAXMzmk/YAe0SCqdT0EsWSvUmRBKdtXSBHAQRz8hl2xI2Z +6CtJXYw6Oy4eA+Ge2JMSRUuEKYwpVSLGdxCoHAkZkz+2rU2X -----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/ca.crl.pem b/test/test_ssl/certs_mtls/ca.crl.pem index a4bbd9562..de292c16d 100644 --- a/test/test_ssl/certs_mtls/ca.crl.pem +++ b/test/test_ssl/certs_mtls/ca.crl.pem @@ -1,13 +1,13 @@ -----BEGIN X509 CRL----- MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 -eS10ZXN0LXN1aXRlFw0yNTAxMDMxMjMwNTBaFw0yNTA3MDIxMjMwNTBaMDIwMAIR +eS10ZXN0LXN1aXRlFw0yNTA3MDkxMDEyNDFaFw0zNTA3MDcxMDEyNDFaMDIwMAIR AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t -MA0GCSqGSIb3DQEBCwUAA4IBAQCGaKW8kJy1Mznc3T2OHkCx8GudvOo0ZBsZ+pTm -sAnlxDQTIqm8e4gU19WF/SISlfr7qEERqif8+SlUgS9CWtJa70gk+9oobuWfBNIT -VXD4ujO/47nqt2MdRUSSGX+K+9Ox2gyU6kHO1ZrT8VmsL22Bhfa2Pw/3OBL/QHMU -b1hAZyed0CoPCnMqjG0X5zMo3ByGW3TkxG2GhzKCWLGXVbzdHFpS98hpkpaxvIlE -juSYuPItwEftHdB8JHAHL18uDJapZ5mOCuUn/HoZBWOudFjtFQUUzq4eTsB56My4 -qDGb1/ReAoGyheuV0fEtg9MJkGEuGrb38JN6hcdfpW5u0Hwb +MA0GCSqGSIb3DQEBCwUAA4IBAQAji33L7enDzhw8qNYLtMxrJuuLAMJeRDO4qYeI +pIJu38K+9RTKG2U/BPPKmdtos/M1NEVJrLqZ/eKHoEU/+u0f1pod3Vh2tAlyB+qp +aGwsg5o07hdB85VDAJ7zwPLFjHtChhhVTS5qOqidaSdVBE0/IFifWBEyHyC7yJDl +dlNY7jmarlmFnpDWmXqAdgMqNlS/t9KN8RtCjiHlF8lF+qjimCWAcfecMmdbAUFC +RFHmo6ENxmcDXQDRVqKAXMzmk/YAe0SCqdT0EsWSvUmRBKdtXSBHAQRz8hl2xI2Z +6CtJXYw6Oy4eA+Ge2JMSRUuEKYwpVSLGdxCoHAkZkz+2rU2X -----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem index a4bbd9562..de292c16d 100644 --- a/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem +++ b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem @@ -1,13 +1,13 @@ -----BEGIN X509 CRL----- MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 -eS10ZXN0LXN1aXRlFw0yNTAxMDMxMjMwNTBaFw0yNTA3MDIxMjMwNTBaMDIwMAIR +eS10ZXN0LXN1aXRlFw0yNTA3MDkxMDEyNDFaFw0zNTA3MDcxMDEyNDFaMDIwMAIR AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t -MA0GCSqGSIb3DQEBCwUAA4IBAQCGaKW8kJy1Mznc3T2OHkCx8GudvOo0ZBsZ+pTm -sAnlxDQTIqm8e4gU19WF/SISlfr7qEERqif8+SlUgS9CWtJa70gk+9oobuWfBNIT -VXD4ujO/47nqt2MdRUSSGX+K+9Ox2gyU6kHO1ZrT8VmsL22Bhfa2Pw/3OBL/QHMU -b1hAZyed0CoPCnMqjG0X5zMo3ByGW3TkxG2GhzKCWLGXVbzdHFpS98hpkpaxvIlE -juSYuPItwEftHdB8JHAHL18uDJapZ5mOCuUn/HoZBWOudFjtFQUUzq4eTsB56My4 -qDGb1/ReAoGyheuV0fEtg9MJkGEuGrb38JN6hcdfpW5u0Hwb +MA0GCSqGSIb3DQEBCwUAA4IBAQAji33L7enDzhw8qNYLtMxrJuuLAMJeRDO4qYeI +pIJu38K+9RTKG2U/BPPKmdtos/M1NEVJrLqZ/eKHoEU/+u0f1pod3Vh2tAlyB+qp +aGwsg5o07hdB85VDAJ7zwPLFjHtChhhVTS5qOqidaSdVBE0/IFifWBEyHyC7yJDl +dlNY7jmarlmFnpDWmXqAdgMqNlS/t9KN8RtCjiHlF8lF+qjimCWAcfecMmdbAUFC +RFHmo6ENxmcDXQDRVqKAXMzmk/YAe0SCqdT0EsWSvUmRBKdtXSBHAQRz8hl2xI2Z +6CtJXYw6Oy4eA+Ge2JMSRUuEKYwpVSLGdxCoHAkZkz+2rU2X -----END X509 CRL----- From 76fc9bbdabc371d1978402ebe1e8b8522a39fdf7 Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Wed, 9 Jul 2025 13:00:26 +0200 Subject: [PATCH 33/35] docs: note about CRL expiration --- docs/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/README.md b/docs/README.md index feeb5f1d0..7ab167538 100644 --- a/docs/README.md +++ b/docs/README.md @@ -403,6 +403,9 @@ If you want to use a global CRL file you have to copy your CRL file named as `ca > [!NOTE] > Use Per-VIRTUAL_HOST CRL if you configured the [Per-VIRTUAL_HOST CA](#per-virtual_host-ca) or Global CRL if you configured the [Global CA](#global-ca) +> [!IMPORTANT] +> Make sure you rotate the CRL before it's expiration date, even if nothing has changed. An expired CRL will make Nginx unable to validate the certificates that were issued. + ### optional ssl_verify_client Optional [`ssl_verify_client`](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client) can be activated by using the `com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional"` label on a proxied container. If this label is set on a proxied container access is not blocked but the result of the mTLS verify is stored in the [$ssl_client_verify](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify) variable which you can use this in the [Per-VIRTUAL_HOST location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_host-location-configuration) and [Per-VIRTUAL_PATH location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_path-location-configuration) configurations. From ed3f93adde5004e10242e6efc19dd04c62c955fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 04:56:17 +0000 Subject: [PATCH 34/35] build: bump nginxproxy/docker-gen from 0.14.7-debian to 0.15.0-debian Bumps nginxproxy/docker-gen from 0.14.7-debian to 0.15.0-debian. --- updated-dependencies: - dependency-name: nginxproxy/docker-gen dependency-version: 0.15.0-debian dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Dockerfile.alpine | 2 +- Dockerfile.debian | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 7996af553..11a010cea 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM docker.io/nginxproxy/docker-gen:0.14.7 AS docker-gen +FROM docker.io/nginxproxy/docker-gen:0.15.0 AS docker-gen FROM docker.io/nginxproxy/forego:0.18.3 AS forego diff --git a/Dockerfile.debian b/Dockerfile.debian index ac1dc084d..a51d5cccb 100644 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM docker.io/nginxproxy/docker-gen:0.14.7-debian AS docker-gen +FROM docker.io/nginxproxy/docker-gen:0.15.0-debian AS docker-gen FROM docker.io/nginxproxy/forego:0.18.3-debian AS forego From e6d78e747466cc6821b761ce50ea66e22393fbd7 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 26 Jul 2025 18:27:18 +0200 Subject: [PATCH 35/35] docs: update nginx version badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0d028d62..d691e670f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Test](https://github.com/nginx-proxy/nginx-proxy/actions/workflows/test.yml/badge.svg)](https://github.com/nginx-proxy/nginx-proxy/actions/workflows/test.yml) [![GitHub release](https://img.shields.io/github/v/release/nginx-proxy/nginx-proxy)](https://github.com/nginx-proxy/nginx-proxy/releases) -[![nginx 1.27.5](https://img.shields.io/badge/nginx-1.27.5-brightgreen.svg?logo=nginx)](https://nginx.org/en/CHANGES) +[![nginx 1.29.0](https://img.shields.io/badge/nginx-1.29.0-brightgreen.svg?logo=nginx)](https://nginx.org/en/CHANGES) [![Docker Image Size](https://img.shields.io/docker/image-size/nginxproxy/nginx-proxy?sort=semver)](https://hub.docker.com/r/nginxproxy/nginx-proxy "Click to view the image on Docker Hub") [![Docker stars](https://img.shields.io/docker/stars/nginxproxy/nginx-proxy.svg)](https://hub.docker.com/r/nginxproxy/nginx-proxy "DockerHub") [![Docker pulls](https://img.shields.io/docker/pulls/nginxproxy/nginx-proxy.svg)](https://hub.docker.com/r/nginxproxy/nginx-proxy "DockerHub")