From b6e9cdc065a19ce7e7cb0dcf2c11cd7c9fe1d063 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 4 May 2021 11:03:27 +0200 Subject: [PATCH 01/43] ci: use docker-gen main on dev branch tests --- .github/workflows/test.yml | 20 ++++++++++++++++---- Makefile | 6 ++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6be93bd..8c4a173 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,13 +3,16 @@ name: Tests on: workflow_dispatch: push: + branches: + - main + - dev paths-ignore: - - 'LICENSE' - - '**.md' + - 'LICENSE' + - '**.md' pull_request: paths-ignore: - - 'LICENSE' - - '**.md' + - 'LICENSE' + - '**.md' jobs: unit: @@ -39,6 +42,15 @@ jobs: - name: Build Docker nginx proxy test image run: make build-nginx-proxy-test-${{ matrix.base_docker_image }} + if: | + ( github.event_name == 'push' && github.ref != 'refs/heads/dev' ) || + ( github.event_name == 'pull_request' && github.base_ref != 'dev' ) + + - name: Build Docker nginx proxy dev test image + run: make build-nginx-proxy-test-${{ matrix.base_docker_image }}-dev + if: | + ( github.event_name == 'push' && github.ref == 'refs/heads/dev' ) || + ( github.event_name == 'pull_request' && github.base_ref == 'dev' ) - name: Run tests run: pytest diff --git a/Makefile b/Makefile index ab44880..60406b6 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,12 @@ build-nginx-proxy-test-debian: build-nginx-proxy-test-alpine: docker build --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test . +build-nginx-proxy-test-debian-dev: + docker build --build-arg DOCKER_GEN_VERSION=main -t nginxproxy/nginx-proxy:test . + +build-nginx-proxy-test-alpine-dev: + docker build -f Dockerfile.alpine --build-arg DOCKER_GEN_VERSION=main -t nginxproxy/nginx-proxy:test . + test-debian: build-webserver build-nginx-proxy-test-debian test/pytest.sh From 2901b917a0cee641b58e66338d8855366b60d1c2 Mon Sep 17 00:00:00 2001 From: Greg Symons Date: Sun, 23 May 2021 22:52:57 +0200 Subject: [PATCH 02/43] feat: support for path-based routing Co-authored-by: Josh Trow Co-authored-by: Adrian Co-authored-by: Rodrigo Aguilera Co-authored-by: Alexander Lieret --- nginx.tmpl | 212 +++++++++++++++++++++++++++++------------------------ 1 file changed, 115 insertions(+), 97 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 2414633..fc6f540 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -49,6 +49,92 @@ {{ end }} {{ end }} +{{ define "location" }} +location {{ .Path }} { + {{ if eq .Proto "uwsgi" }} + include uwsgi_params; + uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; + {{ else if eq .Proto "fastcgi" }} + root {{ trim .Vhostroot }}; + include fastcgi.conf; + fastcgi_pass {{ trim .Upstream }}; + {{ else if eq .Proto "grpc" }} + grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; + {{ else }} + proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}/; + {{ end }} + + {{ if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} + auth_basic "Restricted {{ .Host }}"; + auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }}; + {{ end }} + + {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} + include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; + {{ else if (exists "/etc/nginx/vhost.d/default_location") }} + include /etc/nginx/vhost.d/default_location; + {{ end }} +} +{{ end }} + +{{ define "upstream-definition" }} + {{ $networks := .Networks }} + {{ $debug_all := .Debug }} + upstream {{ .Upstream }} { + {{ $server_found := "false" }} + {{ range $container := .Containers }} + {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} + {{/* If only 1 port exposed, use that as a default, else 80 */}} + {{ $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }} + {{ $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} + {{ $address := where $container.Addresses "Port" $port | first }} + {{ if $debug }} + # Exposed ports: {{ $container.Addresses }} + # Default virtual port: {{ $defaultPort }} + # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} + {{ if not $address }} + # /!\ Virtual port not exposed + {{ end }} + {{ end }} + {{ range $knownNetwork := $networks }} + {{ range $containerNetwork := $container.Networks }} + {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} + ## Can be connected with "{{ $containerNetwork.Name }}" network + {{ if $address }} + {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} + {{ if and $container.Node.ID $address.HostPort }} + {{ $server_found = "true" }} + # {{ $container.Node.Name }}/{{ $container.Name }} + server {{ $container.Node.Address.IP }}:{{ $address.HostPort }}; + {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} + {{ else if $containerNetwork }} + {{ $server_found = "true" }} + # {{ $container.Name }} + server {{ $containerNetwork.IP }}:{{ $address.Port }}; + {{ end }} + {{ else if $containerNetwork }} + # {{ $container.Name }} + {{ if $containerNetwork.IP }} + {{ $server_found = "true" }} + server {{ $containerNetwork.IP }}:{{ $port }}; + {{ else }} + # /!\ No IP for this network! + {{ end }} + {{ end }} + {{ else }} + # Cannot connect to network '{{ $containerNetwork.Name }}' of this container + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{/* nginx-proxy/nginx-proxy#1105 */}} + {{ if (eq $server_found "false") }} + # Fallback entry + server 127.0.0.1 down; + {{ end }} + } +{{ end }} + {{ if ne $nginx_proxy_version "" }} # nginx-proxy version : {{ $nginx_proxy_version }} {{ end }} @@ -100,6 +186,7 @@ access_log off; {{/* Get the SSL_POLICY defined by this container, falling back to "Mozilla-Intermediate" */}} {{ $ssl_policy := or ($.Env.SSL_POLICY) "Mozilla-Intermediate" }} {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} +error_log /dev/stderr; {{ if $.Env.RESOLVERS }} resolver {{ $.Env.RESOLVERS }}; @@ -119,6 +206,7 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; +proxy_set_header X-Original-URI $request_uri; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; @@ -162,61 +250,20 @@ server { {{ $is_regexp := hasPrefix "~" $host }} {{ $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} -# {{ $host }} -upstream {{ $upstream_name }} { +{{ $paths := groupBy $containers "Env.VIRTUAL_PATH" }} +{{ $nPaths := len $paths }} -{{ $server_found := "false" }} -{{ range $container := $containers }} - {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} - {{/* If only 1 port exposed, use that as a default, else 80 */}} - {{ $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }} - {{ $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} - {{ $address := where $container.Addresses "Port" $port | first }} - {{ if $debug }} - # Exposed ports: {{ $container.Addresses }} - # Default virtual port: {{ $defaultPort }} - # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} - {{ if not $address }} - # /!\ Virtual port not exposed - {{ end }} - {{ end }} - {{ range $knownNetwork := $CurrentContainer.Networks }} - {{ range $containerNetwork := $container.Networks }} - {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} - ## Can be connected with "{{ $containerNetwork.Name }}" network - {{ if $address }} - {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} - {{ if and $container.Node.ID $address.HostPort }} - {{ $server_found = "true" }} - # {{ $container.Node.Name }}/{{ $container.Name }} - server {{ $container.Node.Address.IP }}:{{ $address.HostPort }}; - {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} - {{ else if $containerNetwork }} - {{ $server_found = "true" }} - # {{ $container.Name }} - server {{ $containerNetwork.IP }}:{{ $address.Port }}; - {{ end }} - {{ else if $containerNetwork }} - # {{ $container.Name }} - {{ if $containerNetwork.IP }} - {{ $server_found = "true" }} - server {{ $containerNetwork.IP }}:{{ $port }}; - {{ else }} - # /!\ No IP for this network! - {{ end }} - {{ end }} - {{ else }} - # Cannot connect to network '{{ $containerNetwork.Name }}' of this container - {{ end }} - {{ end }} +{{ if eq $nPaths 0 }} + # {{ $host }} + {{ template "upstream-definition" (dict "Upstream" $upstream_name "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} +{{ else }} + {{ range $path, $containers := $paths }} + {{ $sum := sha1 $path }} + {{ $upstream := printf "%s-%s" $upstream_name $sum }} + # {{ $host }}{{ $path }} + {{ template "upstream-definition" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} {{ end }} {{ end }} -{{/* nginx-proxy/nginx-proxy#1105 */}} -{{ if (eq $server_found "false") }} - # Fallback entry - server 127.0.0.1 down; -{{ end }} -} {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} {{ $default_server := index (dict $host "" $default_host "default_server") $host }} @@ -337,30 +384,15 @@ server { include /etc/nginx/vhost.d/default; {{ end }} - location / { - {{ if eq $proto "uwsgi" }} - include uwsgi_params; - uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else if eq $proto "fastcgi" }} - root {{ trim $vhost_root }}; - include fastcgi_params; - fastcgi_pass {{ trim $upstream_name }}; - {{ else if eq $proto "grpc" }} - grpc_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else }} - proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; + {{ if eq $nPaths 0 }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root) }} + {{ else }} + {{ range $path, $container := $paths }} + {{ $sum := sha1 $path }} + {{ $upstream := printf "%s-%s" $host $sum }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} {{ end }} - - {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} - auth_basic "Restricted {{ $host }}"; - auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; - {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} - include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; - {{ else if (exists "/etc/nginx/vhost.d/default_location") }} - include /etc/nginx/vhost.d/default_location; - {{ end }} - } + {{ end }} } {{ end }} @@ -389,29 +421,15 @@ server { include /etc/nginx/vhost.d/default; {{ end }} - location / { - {{ if eq $proto "uwsgi" }} - include uwsgi_params; - uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else if eq $proto "fastcgi" }} - root {{ trim $vhost_root }}; - include fastcgi_params; - fastcgi_pass {{ trim $upstream_name }}; - {{ else if eq $proto "grpc" }} - grpc_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else }} - proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; + {{ if eq $nPaths 0 }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root) }} + {{ else }} + {{ range $path, $container := $paths }} + {{ $sum := sha1 $path }} + {{ $upstream := printf "%s-%s" $upstream_name $sum }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} {{ end }} - {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} - auth_basic "Restricted {{ $host }}"; - auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; - {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} - include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; - {{ else if (exists "/etc/nginx/vhost.d/default_location") }} - include /etc/nginx/vhost.d/default_location; - {{ end }} - } + {{ end }} } {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} From fc4c4e17cab24f84f6b2b9691a43cd4bb90d688b Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 6 Jul 2021 14:40:21 +0200 Subject: [PATCH 03/43] ci: Add tests for the virtual-path routing @gregsymons test cases were too outdated to be ported easily. The new tests should include the coverage of the old ones. --- test/test_events.py | 38 +++++++++++- test/test_virtual-path/test_virtual_paths.py | 59 +++++++++++++++++++ test/test_virtual-path/test_virtual_paths.yml | 42 +++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 test/test_virtual-path/test_virtual_paths.py create mode 100644 test/test_virtual-path/test_virtual_paths.yml diff --git a/test/test_events.py b/test/test_events.py index 201917f..b5da3dd 100644 --- a/test/test_events.py +++ b/test/test_events.py @@ -29,13 +29,36 @@ def web1(docker_compose): except NotFound: pass +@pytest.fixture() +def web2(docker_compose): + """ + pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy`, `VIRTUAL_PATH=/web2/` and `VIRTUAL_DEST=/` listening on port 82. + """ + container = docker_compose.containers.run( + name="web2", + image="web", + detach=True, + environment={ + "WEB_PORTS": "82", + "VIRTUAL_HOST": "nginx-proxy", + "VIRTUAL_PATH": "/web2/", + "VIRTUAL_DEST": "/", + }, + ports={"82/tcp": None} + ) + sleep(2) # give it some time to initialize and for docker-gen to detect it + yield container + try: + docker_compose.containers.get("web2").remove(force=True) + except NotFound: + pass def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 -def test_new_container_is_detected(web1, nginxproxy): +def test_new_container_is_detected_vhost(web1, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy/port") assert r.status_code == 200 assert "answer from port 81\n" == r.text @@ -44,3 +67,16 @@ def test_new_container_is_detected(web1, nginxproxy): sleep(2) r = nginxproxy.get("http://web1.nginx-proxy/port") assert r.status_code == 503 + +def test_new_container_is_detected_vpath(web2, nginxproxy): + r = nginxproxy.get("http://nginx-proxy/web2/port") + assert r.status_code == 200 + assert "answer from port 82\n" == r.text + r = nginxproxy.get("http://nginx-proxy/port") + assert r.status_code in [404, 503] + + web2.remove(force=True) + sleep(2) + r = nginxproxy.get("http://nginx-proxy/web2/port") + assert r.status_code == 503 + diff --git a/test/test_virtual-path/test_virtual_paths.py b/test/test_virtual-path/test_virtual_paths.py new file mode 100644 index 0000000..115d47f --- /dev/null +++ b/test/test_virtual-path/test_virtual_paths.py @@ -0,0 +1,59 @@ +from time import sleep + +import pytest +from docker.errors import NotFound + +@pytest.mark.parametrize("stub,expected_port", [ + ("nginx-proxy.test/web1", 81), + ("nginx-proxy.test/web2", 82), + ("nginx-proxy.test", 83), + ("foo.nginx-proxy.test", 42), +]) +def test_valid_path(docker_compose, nginxproxy, stub, expected_port): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert r.text == f"answer from port {expected_port}\n" + +@pytest.mark.parametrize("stub", [ + "nginx-proxy.test/foo", + "bar.nginx-proxy.test", +]) +def test_invalid_path(docker_compose, nginxproxy, stub): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code in [404, 503] + +@pytest.fixture() +def web4(docker_compose): + """ + pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy.test`, `VIRTUAL_PATH=/web4/` and `VIRTUAL_DEST=/` listening on port 84. + """ + container = docker_compose.containers.run( + name="web4", + image="web", + detach=True, + environment={ + "WEB_PORTS": "84", + "VIRTUAL_HOST": "nginx-proxy.test", + "VIRTUAL_PATH": "/web4/", + "VIRTUAL_DEST": "/", + }, + ports={"84/tcp": None} + ) + sleep(2) # give it some time to initialize and for docker-gen to detect it + yield container + try: + docker_compose.containers.get("web4").remove(force=True) + except NotFound: + pass + +""" +Test if we can add and remove a single virtual_path from multiple ones on the same subdomain. +""" +def test_container_hotplug(web4, nginxproxy): + r = nginxproxy.get(f"http://nginx-proxy.test/web4/port") + assert r.status_code == 200 + assert r.text == f"answer from port 84\n" + web4.remove(force=True) + sleep(2) + r = nginxproxy.get(f"http://nginx-proxy.test/web4/port") + assert r.status_code == 404 diff --git a/test/test_virtual-path/test_virtual_paths.yml b/test/test_virtual-path/test_virtual_paths.yml new file mode 100644 index 0000000..ca688eb --- /dev/null +++ b/test/test_virtual-path/test_virtual_paths.yml @@ -0,0 +1,42 @@ + +foo: + image: web + expose: + - "42" + environment: + WEB_PORTS: "42" + VIRTUAL_HOST: "foo.nginx-proxy.test" + +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web1/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web2/" + +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + From e0e1732842b7c4f90847a4a9f7ca1747a8e17558 Mon Sep 17 00:00:00 2001 From: Greg Symons Date: Tue, 6 Jul 2021 15:02:09 +0200 Subject: [PATCH 04/43] docs: Add documentation for path-based routing Co-authored-by: Josh Trow Co-authored-by: Adrian Co-authored-by: Rodrigo Aguilera Co-authored-by: Alexander Lieret --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 556cf5c..9e248fa 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,12 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie You can also use wildcards at the beginning and the end of host name, like `*.bar.com` or `foo.bar.*`. Or even a regular expression, which can be very useful in conjunction with a wildcard DNS service like [nip.io](https://nip.io) or [sslip.io](https://sslip.io), using `~^foo\.bar\..*\.nip\.io` will match `foo.bar.127.0.0.1.nip.io`, `foo.bar.10.0.2.2.nip.io` and all other given IPs. More information about this topic can be found in the nginx documentation about [`server_names`](http://nginx.org/en/docs/http/server_names.html). +### Path-based Routing + +You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a `VIRTUAL_PATH` environment variable containing the absolute path to where the container should be mounted. For example with `VIRTUAL_HOST=foo.example.com` and `VIRTUAL_PATH=/api/v2/service`, then requests to http://foo.example.com/api/v2/service will be routed to the container. If you wish to have a container serve the root while other containers serve other paths, make give the root container a `VIRTUAL_PATH` of `/`. Unmatched paths will be served by the container at `/` or will return the default nginx error page if no container has been assigned `/`. + +The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. + ### Multiple Networks With the addition of [overlay networking](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) in Docker 1.9, your `nginx-proxy` container may need to connect to backend containers on multiple networks. By default, if you don't pass the `--net` flag when your `nginx-proxy` container is created, it will only be attached to the default `bridge` network. This means that it will not be able to connect to containers on networks other than `bridge`. @@ -337,6 +343,7 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; +proxy_set_header X-Forwarded-Path $request_uri; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; From 9cd85f61d5165f5307cae1dfad5c165bc9821c75 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 15 Jul 2021 21:47:03 +0200 Subject: [PATCH 05/43] build: build and push the dev branch to Dockerhub --- .github/workflows/dockerhub.yml | 43 +++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index cb3635e..74283b7 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -7,6 +7,7 @@ on: push: branches: - main + - dev tags: - '*.*.*' paths-ignore: @@ -42,7 +43,8 @@ jobs: tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} @@ -60,6 +62,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push the Debian based image + if: github.ref == 'refs/heads/main' id: docker_build_debian uses: docker/build-push-action@v2 with: @@ -72,8 +75,25 @@ jobs: labels: ${{ steps.docker_meta_debian.outputs.labels }} - name: Images digests + if: github.ref == 'refs/heads/main' run: echo ${{ steps.docker_build_debian.outputs.digest }} + - name: Build and push the Debian based dev image + if: github.ref == 'refs/heads/dev' + id: docker_build_debian_dev + uses: docker/build-push-action@v2 + with: + file: Dockerfile + build-args: DOCKER_GEN_VERSION=main + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: ${{ steps.docker_meta_debian.outputs.tags }} + labels: ${{ steps.docker_meta_debian.outputs.labels }} + + - name: Images digests + if: github.ref == 'refs/heads/dev' + run: echo ${{ steps.docker_build_debian_dev.outputs.digest }} + multiarch-build-alpine: runs-on: ubuntu-latest steps: @@ -96,7 +116,8 @@ jobs: tags: | type=semver,suffix=-alpine,pattern={{version}} type=semver,suffix=-alpine,pattern={{major}}.{{minor}} - type=raw,value=alpine,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=dev-alpine,enable=${{ github.ref == 'refs/heads/dev' }} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} @@ -115,6 +136,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push the Alpine based image + if: github.ref == 'refs/heads/main' id: docker_build_alpine uses: docker/build-push-action@v2 with: @@ -127,4 +149,21 @@ jobs: labels: ${{ steps.docker_meta_alpine.outputs.labels }} - name: Images digests + if: github.ref == 'refs/heads/main' run: echo ${{ steps.docker_build_alpine.outputs.digest }} + + - name: Build and push the Alpine based dev image + if: github.ref == 'refs/heads/dev' + id: docker_build_alpine_dev + uses: docker/build-push-action@v2 + with: + file: Dockerfile.alpine + build-args: DOCKER_GEN_VERSION=main + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: ${{ steps.docker_meta_alpine.outputs.tags }} + labels: ${{ steps.docker_meta_alpine.outputs.labels }} + + - name: Images digests + if: github.ref == 'refs/heads/dev' + run: echo ${{ steps.docker_build_alpine_dev.outputs.digest }} From dad4a2d7bfcf71d517b35a4fe1f7f46c74b4fea8 Mon Sep 17 00:00:00 2001 From: Rafael Kraut Date: Tue, 20 Jul 2021 10:28:39 +0200 Subject: [PATCH 06/43] docs: remove unnecessary word --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e248fa..262147c 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ You can also use wildcards at the beginning and the end of host name, like `*.ba ### Path-based Routing -You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a `VIRTUAL_PATH` environment variable containing the absolute path to where the container should be mounted. For example with `VIRTUAL_HOST=foo.example.com` and `VIRTUAL_PATH=/api/v2/service`, then requests to http://foo.example.com/api/v2/service will be routed to the container. If you wish to have a container serve the root while other containers serve other paths, make give the root container a `VIRTUAL_PATH` of `/`. Unmatched paths will be served by the container at `/` or will return the default nginx error page if no container has been assigned `/`. +You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a `VIRTUAL_PATH` environment variable containing the absolute path to where the container should be mounted. For example with `VIRTUAL_HOST=foo.example.com` and `VIRTUAL_PATH=/api/v2/service`, then requests to http://foo.example.com/api/v2/service will be routed to the container. If you wish to have a container serve the root while other containers serve other paths, give the root container a `VIRTUAL_PATH` of `/`. Unmatched paths will be served by the container at `/` or will return the default nginx error page if no container has been assigned `/`. The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. From 28c73e5b52d281964812e411c0a411dabf1a387e Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Wed, 11 Aug 2021 18:04:53 +0200 Subject: [PATCH 07/43] fix: non working https with virtual path --- nginx.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.tmpl b/nginx.tmpl index fc6f540..73fba26 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -389,7 +389,7 @@ server { {{ else }} {{ range $path, $container := $paths }} {{ $sum := sha1 $path }} - {{ $upstream := printf "%s-%s" $host $sum }} + {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} {{ end }} {{ end }} From 9df330e51ebb405966c942a4f5241faec16a462e Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 6 Jul 2021 15:26:02 +0200 Subject: [PATCH 08/43] feat: Add user customizable default root response --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- README.md | 10 +++++++++ nginx.tmpl | 11 ++++++++++ test/test_virtual-path/test_custom_conf.py | 6 ++++++ test/test_virtual-path/test_custom_conf.yml | 24 +++++++++++++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/test_virtual-path/test_custom_conf.py create mode 100644 test/test_virtual-path/test_custom_conf.yml diff --git a/Dockerfile b/Dockerfile index d5c71bc..bc4093d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.7.7 +ARG DOCKER_GEN_VERSION=main ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 2552615..98e9bc5 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.7.7 +ARG DOCKER_GEN_VERSION=main ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries diff --git a/README.md b/README.md index 262147c..aa1b79b 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,16 @@ You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. +**NOTE**: Your application needs to be able to generate links starting with `VIRTUAL_PATH`. This can be achieved by it being natively on this path or havin an option to prepend this path. The application does not need to expect this path in the request. + +#### DEFAULT_ROOT + +This environment variable of the nginx proxy container can be used to customize the return error page if no matching path is found. Furthermore it is possible to use anything which is compatible with the `return` statement of nginx. + +For example `DEFAUL_ROOT=418` will return a 418 error page instead of the normal 404 one. +Another example is `DEFAULT_ROOT="301 https://github.com/nginx-proxy/nginx-proxy/blob/main/README.md"` which would redirect an invalid request to this documentation. + + ### Multiple Networks With the addition of [overlay networking](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) in Docker 1.9, your `nginx-proxy` container may need to connect to backend containers on multiple networks. By default, if you don't pass the `--net` flag when your `nginx-proxy` container is created, it will only be attached to the default `bridge` network. This means that it will not be able to connect to containers on networks other than `bridge`. diff --git a/nginx.tmpl b/nginx.tmpl index 73fba26..85ccbda 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -5,6 +5,7 @@ {{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }} {{ $debug_all := $.Env.DEBUG }} {{ $sha1_upstream_name := parseBool (coalesce $.Env.SHA1_UPSTREAM_NAME "false") }} +{{ $default_root_response := coalesce $.Env.DEFAULT_ROOT "404" }} {{ define "ssl_policy" }} {{ if eq .ssl_policy "Mozilla-Modern" }} @@ -392,6 +393,11 @@ server { {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} {{ end }} + {{ if (not (contains $paths "/")) }} + location / { + return {{ $default_root_response }}; + } + {{ end }} {{ end }} } @@ -429,6 +435,11 @@ server { {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} {{ end }} + {{ if (not (contains $paths "/")) }} + location / { + return {{ $default_root_response }}; + } + {{ end }} {{ end }} } diff --git a/test/test_virtual-path/test_custom_conf.py b/test/test_virtual-path/test_custom_conf.py new file mode 100644 index 0000000..68ecd3a --- /dev/null +++ b/test/test_virtual-path/test_custom_conf.py @@ -0,0 +1,6 @@ +import pytest + +def test_default_root_response(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.test/") + assert r.status_code == 418 + diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom_conf.yml new file mode 100644 index 0000000..2fffd65 --- /dev/null +++ b/test/test_virtual-path/test_custom_conf.yml @@ -0,0 +1,24 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web1/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web2/" +sut: + image: nginxproxy/nginx-proxy:test + environment: + DEFAULT_ROOT: 418 + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro From 4b85e9582450ff07dcf205ad8ab5f5b1131599ef Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 6 Jul 2021 15:36:06 +0200 Subject: [PATCH 09/43] feat: Replace path stripping with variable This commit removes the automatic path stripping and replaces it with a user configurable environment variable. This can be set individually for each container. --- README.md | 14 ++++++++++++++ nginx.tmpl | 12 +++++++----- test/test_virtual-path/test_custom_conf.yml | 3 +++ test/test_virtual-path/test_virtual_paths.yml | 2 ++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aa1b79b..33ec073 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,20 @@ The full request URI will be forwarded to the serving container in the `X-Forwar **NOTE**: Your application needs to be able to generate links starting with `VIRTUAL_PATH`. This can be achieved by it being natively on this path or havin an option to prepend this path. The application does not need to expect this path in the request. +#### VIRTUAL_DEST + +This environment variable can be used to rewrite the `VIRTUAL_PATH` part of the requested URL to proxied application. The default value is empty (off). +Make sure that your settings won't result in the slash missing or being doubled. Both these versions can cause troubles. + +If the application runs natively on this sub-path or has a setting to do so, `VIRTUAL_DEST` should not be set or empty. +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 +``` + +In this example, the incoming request `http://example.tld/app1/foo` will be proxied as `http://app1/foo` instead of `http://app1/app1/foo`. + #### DEFAULT_ROOT This environment variable of the nginx proxy container can be used to customize the return error page if no matching path is found. Furthermore it is possible to use anything which is compatible with the `return` statement of nginx. diff --git a/nginx.tmpl b/nginx.tmpl index 85ccbda..b1e6249 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -62,7 +62,7 @@ location {{ .Path }} { {{ else if eq .Proto "grpc" }} grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; {{ else }} - proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}/; + proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }}; {{ end }} {{ if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} @@ -386,12 +386,13 @@ server { {{ end }} {{ if eq $nPaths 0 }} - {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root) }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "") }} {{ else }} {{ range $path, $container := $paths }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} + {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest) }} {{ end }} {{ if (not (contains $paths "/")) }} location / { @@ -428,12 +429,13 @@ server { {{ end }} {{ if eq $nPaths 0 }} - {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root) }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "") }} {{ else }} {{ range $path, $container := $paths }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root) }} + {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest) }} {{ end }} {{ if (not (contains $paths "/")) }} location / { diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom_conf.yml index 2fffd65..2369bac 100644 --- a/test/test_virtual-path/test_custom_conf.yml +++ b/test/test_virtual-path/test_custom_conf.yml @@ -6,6 +6,7 @@ web1: WEB_PORTS: "81" VIRTUAL_HOST: "nginx-proxy.test" VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" web2: image: web @@ -15,6 +16,8 @@ web2: WEB_PORTS: "82" VIRTUAL_HOST: "nginx-proxy.test" VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + sut: image: nginxproxy/nginx-proxy:test environment: diff --git a/test/test_virtual-path/test_virtual_paths.yml b/test/test_virtual-path/test_virtual_paths.yml index ca688eb..b335c3f 100644 --- a/test/test_virtual-path/test_virtual_paths.yml +++ b/test/test_virtual-path/test_virtual_paths.yml @@ -15,6 +15,7 @@ web1: WEB_PORTS: "81" VIRTUAL_HOST: "nginx-proxy.test" VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" web2: image: web @@ -24,6 +25,7 @@ web2: WEB_PORTS: "82" VIRTUAL_HOST: "nginx-proxy.test" VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" web3: image: web From 33eab70d321b727b245f5d59056b8a990048eb68 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 6 Jul 2021 15:41:14 +0200 Subject: [PATCH 10/43] feat: Add custom location block to virtual paths This features allows the custom location blocks to be added to the virtual path based routing. The custom config can be specified for each container individually. --- README.md | 9 ++++++ nginx.tmpl | 2 ++ test/test_virtual-path/alternate.conf | 1 + test/test_virtual-path/bar.conf | 1 + test/test_virtual-path/foo.conf | 1 + test/test_virtual-path/test_custom_conf.py | 32 +++++++++++++++++++++ test/test_virtual-path/test_custom_conf.yml | 22 ++++++++++++++ 7 files changed, 68 insertions(+) create mode 100644 test/test_virtual-path/alternate.conf create mode 100644 test/test_virtual-path/bar.conf create mode 100644 test/test_virtual-path/foo.conf diff --git a/README.md b/README.md index 33ec073..2f00d1c 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ You can also use wildcards at the beginning and the end of host name, like `*.ba ### Path-based Routing You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a `VIRTUAL_PATH` environment variable containing the absolute path to where the container should be mounted. For example with `VIRTUAL_HOST=foo.example.com` and `VIRTUAL_PATH=/api/v2/service`, then requests to http://foo.example.com/api/v2/service will be routed to the container. If you wish to have a container serve the root while other containers serve other paths, give the root container a `VIRTUAL_PATH` of `/`. Unmatched paths will be served by the container at `/` or will return the default nginx error page if no container has been assigned `/`. +It is also possible to specify multiple paths with regex locations like `VIRTUAL_PATH=~^/(app1|alternative1)/`. For further details see the nginx documentation on location blocks. This is not compatible with `VIRTUAL_DEST`. The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. @@ -139,6 +140,14 @@ $ docker run -d -e VIRTUAL_HOST=example.tld -e VIRTUAL_PATH=/app1/ -e VIRTUAL_DE In this example, the incoming request `http://example.tld/app1/foo` will be proxied as `http://app1/foo` instead of `http://app1/app1/foo`. +#### Per-VIRTUAL_PATH location configuration + +The same options as from [Per-VIRTUAL_HOST location configuration](#Per-VIRTUAL_HOST-location-configuration) are available on a `VIRTUAL_PATH` basis. +The only difference is that the filename gets an additional block `HASH=$(echo -n $VIRTUAL_PATH | sha1sum | awk '{ print $1 }')`. This is the sha1-hash of the `VIRTUAL_PATH` (no newline). This is done filename sanitization purposes. +The used filename is `${VIRTUAL_HOST}_${HASH}_location` + +The filename of the previous example would be `example.tld_8610f6c344b4096614eab6e09d58885349f42faf_location`. + #### DEFAULT_ROOT This environment variable of the nginx proxy container can be used to customize the return error page if no matching path is found. Furthermore it is possible to use anything which is compatible with the `return` statement of nginx. diff --git a/nginx.tmpl b/nginx.tmpl index b1e6249..dd48d4b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -72,6 +72,8 @@ location {{ .Path }} { {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; + {{ else if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }} + include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }}; {{ else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; {{ end }} diff --git a/test/test_virtual-path/alternate.conf b/test/test_virtual-path/alternate.conf new file mode 100644 index 0000000..541332e --- /dev/null +++ b/test/test_virtual-path/alternate.conf @@ -0,0 +1 @@ +rewrite ^/(web3|alt)/(.*) /$2 break; diff --git a/test/test_virtual-path/bar.conf b/test/test_virtual-path/bar.conf new file mode 100644 index 0000000..e8b0827 --- /dev/null +++ b/test/test_virtual-path/bar.conf @@ -0,0 +1 @@ +add_header X-test bar; diff --git a/test/test_virtual-path/foo.conf b/test/test_virtual-path/foo.conf new file mode 100644 index 0000000..8d8502d --- /dev/null +++ b/test/test_virtual-path/foo.conf @@ -0,0 +1 @@ +add_header X-test f00; \ No newline at end of file diff --git a/test/test_virtual-path/test_custom_conf.py b/test/test_virtual-path/test_custom_conf.py index 68ecd3a..eec149f 100644 --- a/test/test_virtual-path/test_custom_conf.py +++ b/test/test_virtual-path/test_custom_conf.py @@ -4,3 +4,35 @@ def test_default_root_response(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy.test/") assert r.status_code == 418 +@pytest.mark.parametrize("stub,header", [ + ("nginx-proxy.test/web1", "bar"), + ("foo.nginx-proxy.test", "f00"), +]) +def test_custom_applies(docker_compose, nginxproxy, stub, header): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert "X-test" in r.headers + assert header == r.headers["X-test"] + +@pytest.mark.parametrize("stub,code", [ + ("nginx-proxy.test/foo", 418), + ("nginx-proxy.test/web2", 200), + ("nginx-proxy.test/web3", 200), + ("bar.nginx-proxy.test", 503), +]) +def test_custom_does_not_apply(docker_compose, nginxproxy, stub, code): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == code + assert "X-test" not in r.headers + +@pytest.mark.parametrize("stub,port", [ + ("nginx-proxy.test/web1", 81), + ("nginx-proxy.test/web2", 82), + ("nginx-proxy.test/web3", 83), + ("nginx-proxy.test/alt", 83), +]) +def test_alternate(docker_compose, nginxproxy, stub, port): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert r.text == f"answer from port {port}\n" + diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom_conf.yml index 2369bac..abd9d0c 100644 --- a/test/test_virtual-path/test_custom_conf.yml +++ b/test/test_virtual-path/test_custom_conf.yml @@ -1,3 +1,12 @@ + +foo: + image: web + expose: + - "42" + environment: + WEB_PORTS: "42" + VIRTUAL_HOST: "foo.nginx-proxy.test" + web1: image: web expose: @@ -18,6 +27,15 @@ web2: VIRTUAL_PATH: "/web2/" VIRTUAL_DEST: "/" +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "~ ^/(web3|alt)/" + sut: image: nginxproxy/nginx-proxy:test environment: @@ -25,3 +43,7 @@ sut: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro + - ./bar.conf:/etc/nginx/vhost.d/nginx-proxy.test_918d687a929083edd0c7224ee2293e0e7c062ab4_location:ro + - ./alternate.conf:/etc/nginx/vhost.d/nginx-proxy.test_7fb22b74bbdf907425dbbad18e4462565cada230_location:ro + From c75622db87abf0aee6eb97affa95474ea0b2573b Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 13 Aug 2021 10:54:12 +0200 Subject: [PATCH 11/43] docs: fix typo in README.md Co-authored-by: Jonathan Underwood --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f00d1c..9a69d1c 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ It is also possible to specify multiple paths with regex locations like `VIRTUAL The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. -**NOTE**: Your application needs to be able to generate links starting with `VIRTUAL_PATH`. This can be achieved by it being natively on this path or havin an option to prepend this path. The application does not need to expect this path in the request. +**NOTE**: Your application needs to be able to generate links starting with `VIRTUAL_PATH`. This can be achieved by it being natively on this path or having an option to prepend this path. The application does not need to expect this path in the request. #### VIRTUAL_DEST From efb250da0120854e4f399d58e4a4c2e2db82333e Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 14 Aug 2021 21:38:13 +0200 Subject: [PATCH 12/43] fix: use most specific custom location config first Co-authored-by: Jonathan Underwood --- nginx.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index dd48d4b..c0f6db5 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -70,10 +70,10 @@ location {{ .Path }} { auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }}; {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} - include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; - {{ else if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }} + {{ if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }} include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }}; + {{ else if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} + include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; {{ else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; {{ end }} From 12887a977b3570e27775f19c13563d68ded4314e Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 14 Aug 2021 21:40:25 +0200 Subject: [PATCH 13/43] docs: update DEFAULT_ROOT documentation Co-authored-by: Jonathan Underwood --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 9a69d1c..880d0e8 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,11 @@ This environment variable of the nginx proxy container can be used to customize For example `DEFAUL_ROOT=418` will return a 418 error page instead of the normal 404 one. Another example is `DEFAULT_ROOT="301 https://github.com/nginx-proxy/nginx-proxy/blob/main/README.md"` which would redirect an invalid request to this documentation. +Nginx variables such as $scheme, $host, and $request_uri can be used. However, care must be taken to make sure the $ signs are escaped properly. +If you want to use `301 $scheme://$host/myapp1$request_uri` you should use: + +* Bash: `DEFAULT_ROOT='301 $scheme://$host/myapp1$request_uri'` +* Docker Compose yaml: `- DEFAULT_ROOT: 301 $$scheme://$$host/myapp1$$request_uri` ### Multiple Networks From e08b3487c95b38cd3f094e86f9638a39c12bbf84 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 17 Aug 2021 10:51:32 +0200 Subject: [PATCH 14/43] test: Add test to cover SSL of path-based routing --- test/test_ssl/test_virtual_path.py | 15 +++++++++++++++ test/test_ssl/test_virtual_path.yml | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 test/test_ssl/test_virtual_path.py create mode 100644 test/test_ssl/test_virtual_path.yml diff --git a/test/test_ssl/test_virtual_path.py b/test/test_ssl/test_virtual_path.py new file mode 100644 index 0000000..508653f --- /dev/null +++ b/test/test_ssl/test_virtual_path.py @@ -0,0 +1,15 @@ +import pytest + +@pytest.mark.parametrize("path", ["web1", "web2"]) +def test_web1_http_redirects_to_https(docker_compose, nginxproxy, path): + r = nginxproxy.get("http://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) + assert r.status_code == 301 + assert "Location" in r.headers + assert "https://www.nginx-proxy.tld/%s/port" % path == r.headers['Location'] + +@pytest.mark.parametrize("path,port", [("web1", 81), ("web2", 82)]) +def test_web1_https_is_forwarded(docker_compose, nginxproxy, path, port): + r = nginxproxy.get("https://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) + assert r.status_code == 200 + assert "answer from port %d\n" % port in r.text + diff --git a/test/test_ssl/test_virtual_path.yml b/test/test_ssl/test_virtual_path.yml new file mode 100644 index 0000000..07175ac --- /dev/null +++ b/test/test_ssl/test_virtual_path.yml @@ -0,0 +1,27 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./certs:/etc/nginx/certs:ro + From 4099fcd61872fe4093323771090ccad570a80e77 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 17 Aug 2021 11:08:56 +0200 Subject: [PATCH 15/43] test: Add test case for default app redirect Co-authored-by: Jonathan Underwood --- test/test_virtual-path/test_forwarding.py | 18 ++++++++++++++++++ test/test_virtual-path/test_forwarding.yml | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/test_virtual-path/test_forwarding.py create mode 100644 test/test_virtual-path/test_forwarding.yml diff --git a/test/test_virtual-path/test_forwarding.py b/test/test_virtual-path/test_forwarding.py new file mode 100644 index 0000000..062dd6c --- /dev/null +++ b/test/test_virtual-path/test_forwarding.py @@ -0,0 +1,18 @@ +import pytest + +def test_root_redirects_to_web1(docker_compose, nginxproxy): + r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=False) + assert r.status_code == 301 + assert "Location" in r.headers + assert "http://www.nginx-proxy.tld/web1/port" == r.headers['Location'] + +def test_direct_access(docker_compose, nginxproxy): + r = nginxproxy.get("http://www.nginx-proxy.tld/web1/port", allow_redirects=False) + assert r.status_code == 200 + assert "answer from port 81\n" in r.text + +def test_root_is_forwarded(docker_compose, nginxproxy): + r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=True) + assert r.status_code == 200 + assert "answer from port 81\n" in r.text + diff --git a/test/test_virtual-path/test_forwarding.yml b/test/test_virtual-path/test_forwarding.yml new file mode 100644 index 0000000..78662d8 --- /dev/null +++ b/test/test_virtual-path/test_forwarding.yml @@ -0,0 +1,18 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./certs:/etc/nginx/certs:ro + environment: + - DEFAULT_ROOT=301 http://$$host/web1$$request_uri From 6a580ad66435b7d2b81a3df0cfb857636db46ef3 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Wed, 18 Aug 2021 15:34:30 +0200 Subject: [PATCH 16/43] test: Add test case for location config priority --- test/test_virtual-path/default.conf | 1 + test/test_virtual-path/host.conf | 1 + test/test_virtual-path/path.conf | 1 + .../test_location_precedence.py | 32 ++++++++++++++++ .../test_location_precedence.yml | 38 +++++++++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 test/test_virtual-path/default.conf create mode 100644 test/test_virtual-path/host.conf create mode 100644 test/test_virtual-path/path.conf create mode 100644 test/test_virtual-path/test_location_precedence.py create mode 100644 test/test_virtual-path/test_location_precedence.yml diff --git a/test/test_virtual-path/default.conf b/test/test_virtual-path/default.conf new file mode 100644 index 0000000..087e66c --- /dev/null +++ b/test/test_virtual-path/default.conf @@ -0,0 +1 @@ +add_header X-test-default true; diff --git a/test/test_virtual-path/host.conf b/test/test_virtual-path/host.conf new file mode 100644 index 0000000..fe05265 --- /dev/null +++ b/test/test_virtual-path/host.conf @@ -0,0 +1 @@ +add_header X-test-host true; diff --git a/test/test_virtual-path/path.conf b/test/test_virtual-path/path.conf new file mode 100644 index 0000000..6c23b9a --- /dev/null +++ b/test/test_virtual-path/path.conf @@ -0,0 +1 @@ +add_header X-test-path true; diff --git a/test/test_virtual-path/test_location_precedence.py b/test/test_virtual-path/test_location_precedence.py new file mode 100644 index 0000000..415c6c1 --- /dev/null +++ b/test/test_virtual-path/test_location_precedence.py @@ -0,0 +1,32 @@ +import pytest + +def test_location_precedence_case1(docker_compose, nginxproxy): + r = nginxproxy.get(f"http://foo.nginx-proxy.test/web1/port") + assert r.status_code == 200 + + assert "X-test-default" in r.headers + assert "X-test-host" not in r.headers + assert "X-test-path" not in r.headers + + assert r.headers["X-test-default"] == "true" + +def test_location_precedence_case2(docker_compose, nginxproxy): + r = nginxproxy.get(f"http://bar.nginx-proxy.test/web2/port") + assert r.status_code == 200 + + assert "X-test-default" not in r.headers + assert "X-test-host" in r.headers + assert "X-test-path" not in r.headers + + assert r.headers["X-test-host"] == "true" + +def test_location_precedence_case3(docker_compose, nginxproxy): + r = nginxproxy.get(f"http://bar.nginx-proxy.test/web3/port") + assert r.status_code == 200 + + assert "X-test-default" not in r.headers + assert "X-test-host" not in r.headers + assert "X-test-path" in r.headers + + assert r.headers["X-test-path"] == "true" + diff --git a/test/test_virtual-path/test_location_precedence.yml b/test/test_virtual-path/test_location_precedence.yml new file mode 100644 index 0000000..be93b58 --- /dev/null +++ b/test/test_virtual-path/test_location_precedence.yml @@ -0,0 +1,38 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "foo.nginx-proxy.test" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "bar.nginx-proxy.test" + VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "bar.nginx-proxy.test" + VIRTUAL_PATH: "/web3/" + VIRTUAL_DEST: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./default.conf:/etc/nginx/vhost.d/default_location:ro + - ./host.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_location:ro + - ./path.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_99f2db0ed8aa95dbb5b87fca79c7eff2ff6bb8bd_location:ro From 28c74e8daeb0eb7f5bcace7db4c5b570af6b5436 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 24 Aug 2021 15:51:39 +0200 Subject: [PATCH 17/43] fix: Move NETWORK_ACCESS to location block --- network_internal.conf | 1 + nginx.tmpl | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/network_internal.conf b/network_internal.conf index cdf3c9c..bacceb1 100644 --- a/network_internal.conf +++ b/network_internal.conf @@ -3,4 +3,5 @@ allow 127.0.0.0/8; allow 10.0.0.0/8; allow 192.168.0.0/16; allow 172.16.0.0/12; +allow fc00::/7; # IPv6 local address range deny all; diff --git a/nginx.tmpl b/nginx.tmpl index c0f6db5..13ca7a4 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -52,6 +52,11 @@ {{ define "location" }} location {{ .Path }} { + {{ if eq .NetworkTag "internal" }} + # Only allow traffic from internal clients + include /etc/nginx/network_internal.conf; + {{ end }} + {{ if eq .Proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; @@ -277,8 +282,6 @@ server { {{/* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}} {{ $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }} -{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} -{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $.Env.HTTPS_METHOD "redirect") }} @@ -353,11 +356,6 @@ server { {{ end }} {{ $access_log }} - {{ if eq $network_tag "internal" }} - # Only allow traffic from internal clients - include /etc/nginx/network_internal.conf; - {{ end }} - {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} ssl_session_timeout 5m; @@ -388,13 +386,17 @@ server { {{ end }} {{ if eq $nPaths 0 }} - {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "") }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} {{ else }} {{ range $path, $container := $paths }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest) }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} {{ if (not (contains $paths "/")) }} location / { @@ -419,11 +421,6 @@ server { {{ end }} {{ $access_log }} - {{ if eq $network_tag "internal" }} - # Only allow traffic from internal clients - include /etc/nginx/network_internal.conf; - {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s" $host }}; {{ else if (exists "/etc/nginx/vhost.d/default") }} @@ -431,13 +428,17 @@ server { {{ end }} {{ if eq $nPaths 0 }} - {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "") }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} {{ else }} {{ range $path, $container := $paths }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest) }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} {{ if (not (contains $paths "/")) }} location / { From 2509fc1076d50832e124955a829c7a94634be98e Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Mon, 30 Aug 2021 12:19:10 +0200 Subject: [PATCH 18/43] test: Add test cases for NETWORK_ACCESS=internal --- test/test_internal/network_internal.conf | 11 ++++++++++ test/test_internal/test_per-vhost.py | 14 ++++++++++++ test/test_internal/test_per-vhost.yml | 24 ++++++++++++++++++++ test/test_internal/test_per-vpath.py | 14 ++++++++++++ test/test_internal/test_per-vpath.yml | 28 ++++++++++++++++++++++++ 5 files changed, 91 insertions(+) create mode 100644 test/test_internal/network_internal.conf create mode 100644 test/test_internal/test_per-vhost.py create mode 100644 test/test_internal/test_per-vhost.yml create mode 100644 test/test_internal/test_per-vpath.py create mode 100644 test/test_internal/test_per-vpath.yml diff --git a/test/test_internal/network_internal.conf b/test/test_internal/network_internal.conf new file mode 100644 index 0000000..496e569 --- /dev/null +++ b/test/test_internal/network_internal.conf @@ -0,0 +1,11 @@ +# Only allow traffic from internal clients +allow 127.0.0.0/8; +allow 10.0.0.0/8; +allow 192.168.0.0/16; +allow 172.16.0.0/12; +allow fc00::/7; # IPv6 local address range +deny all; + +# Dummy header for testing +add_header X-network internal; + diff --git a/test/test_internal/test_per-vhost.py b/test/test_internal/test_per-vhost.py new file mode 100644 index 0000000..4586cc0 --- /dev/null +++ b/test/test_internal/test_per-vhost.py @@ -0,0 +1,14 @@ +import pytest + +def test_network_web1(docker_compose, nginxproxy): + r = nginxproxy.get("http://web1.nginx-proxy.local/port") + assert r.status_code == 200 + assert r.text == "answer from port 81\n" + assert "X-network" in r.headers + assert "internal" == r.headers["X-network"] + +def test_network_web2(docker_compose, nginxproxy): + r = nginxproxy.get("http://web2.nginx-proxy.local/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + assert "X-network" not in r.headers diff --git a/test/test_internal/test_per-vhost.yml b/test/test_internal/test_per-vhost.yml new file mode 100644 index 0000000..a935e53 --- /dev/null +++ b/test/test_internal/test_per-vhost.yml @@ -0,0 +1,24 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: web1.nginx-proxy.local + NETWORK_ACCESS: internal + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: web2.nginx-proxy.local + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./network_internal.conf:/etc/nginx/network_internal.conf:ro + diff --git a/test/test_internal/test_per-vpath.py b/test/test_internal/test_per-vpath.py new file mode 100644 index 0000000..e95fe00 --- /dev/null +++ b/test/test_internal/test_per-vpath.py @@ -0,0 +1,14 @@ +import pytest + +def test_network_web1(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.local/web1/port") + assert r.status_code == 200 + assert r.text == "answer from port 81\n" + assert "X-network" in r.headers + assert "internal" == r.headers["X-network"] + +def test_network_web2(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.local/web2/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + assert "X-network" not in r.headers diff --git a/test/test_internal/test_per-vpath.yml b/test/test_internal/test_per-vpath.yml new file mode 100644 index 0000000..1ea470f --- /dev/null +++ b/test/test_internal/test_per-vpath.yml @@ -0,0 +1,28 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: nginx-proxy.local + VIRTUAL_PATH: /web1/ + VIRTUAL_DEST: / + NETWORK_ACCESS: internal + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: nginx-proxy.local + VIRTUAL_PATH: /web2/ + VIRTUAL_DEST: / + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./network_internal.conf:/etc/nginx/network_internal.conf:ro + From 7ede0fa4b97840c02aeac5498c43d616c74b6221 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Mon, 30 Aug 2021 14:37:28 +0200 Subject: [PATCH 19/43] test: fix: Rename new test files --- .../{test_per-vhost.py => test_internal-per-vhost.py} | 0 .../{test_per-vhost.yml => test_internal-per-vhost.yml} | 0 .../{test_per-vpath.py => test_internal-per-vpath.py} | 0 .../{test_per-vpath.yml => test_internal-per-vpath.yml} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename test/test_internal/{test_per-vhost.py => test_internal-per-vhost.py} (100%) rename test/test_internal/{test_per-vhost.yml => test_internal-per-vhost.yml} (100%) rename test/test_internal/{test_per-vpath.py => test_internal-per-vpath.py} (100%) rename test/test_internal/{test_per-vpath.yml => test_internal-per-vpath.yml} (100%) diff --git a/test/test_internal/test_per-vhost.py b/test/test_internal/test_internal-per-vhost.py similarity index 100% rename from test/test_internal/test_per-vhost.py rename to test/test_internal/test_internal-per-vhost.py diff --git a/test/test_internal/test_per-vhost.yml b/test/test_internal/test_internal-per-vhost.yml similarity index 100% rename from test/test_internal/test_per-vhost.yml rename to test/test_internal/test_internal-per-vhost.yml diff --git a/test/test_internal/test_per-vpath.py b/test/test_internal/test_internal-per-vpath.py similarity index 100% rename from test/test_internal/test_per-vpath.py rename to test/test_internal/test_internal-per-vpath.py diff --git a/test/test_internal/test_per-vpath.yml b/test/test_internal/test_internal-per-vpath.yml similarity index 100% rename from test/test_internal/test_per-vpath.yml rename to test/test_internal/test_internal-per-vpath.yml From 08c9586346703dfa7035404ca7ab724afd3a0e54 Mon Sep 17 00:00:00 2001 From: Alexander Lieret Date: Tue, 21 Sep 2021 14:07:41 +0200 Subject: [PATCH 20/43] fix: Handle VIRTUAL_PROTO on virtual path basis --- nginx.tmpl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 13ca7a4..0eeeac9 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -276,9 +276,6 @@ server { {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} {{ $default_server := index (dict $host "" $default_host "default_server") $host }} -{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} -{{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} - {{/* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}} {{ $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }} @@ -386,11 +383,17 @@ server { {{ end }} {{ if eq $nPaths 0 }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} {{ else }} {{ range $path, $container := $paths }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $container "Env.VIRTUAL_PROTO")) "http") }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }} {{ $sum := sha1 $path }} @@ -428,11 +431,17 @@ server { {{ end }} {{ if eq $nPaths 0 }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} {{ else }} {{ range $path, $container := $paths }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $container "Env.VIRTUAL_PROTO")) "http") }} + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }} {{ $sum := sha1 $path }} From 01446472dddb3c7ebbe7f69c7269b3461daa9aec Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 24 Feb 2022 15:08:45 +0100 Subject: [PATCH 21/43] Revert "ci: use docker-gen main on dev branch tests" This reverts commit b6e9cdc065a19ce7e7cb0dcf2c11cd7c9fe1d063. --- .github/workflows/test.yml | 20 ++++---------------- Makefile | 6 ------ 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c4a173..6be93bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,16 +3,13 @@ name: Tests on: workflow_dispatch: push: - branches: - - main - - dev paths-ignore: - - 'LICENSE' - - '**.md' + - 'LICENSE' + - '**.md' pull_request: paths-ignore: - - 'LICENSE' - - '**.md' + - 'LICENSE' + - '**.md' jobs: unit: @@ -42,15 +39,6 @@ jobs: - name: Build Docker nginx proxy test image run: make build-nginx-proxy-test-${{ matrix.base_docker_image }} - if: | - ( github.event_name == 'push' && github.ref != 'refs/heads/dev' ) || - ( github.event_name == 'pull_request' && github.base_ref != 'dev' ) - - - name: Build Docker nginx proxy dev test image - run: make build-nginx-proxy-test-${{ matrix.base_docker_image }}-dev - if: | - ( github.event_name == 'push' && github.ref == 'refs/heads/dev' ) || - ( github.event_name == 'pull_request' && github.base_ref == 'dev' ) - name: Run tests run: pytest diff --git a/Makefile b/Makefile index 60406b6..ab44880 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,6 @@ build-nginx-proxy-test-debian: build-nginx-proxy-test-alpine: docker build --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test . -build-nginx-proxy-test-debian-dev: - docker build --build-arg DOCKER_GEN_VERSION=main -t nginxproxy/nginx-proxy:test . - -build-nginx-proxy-test-alpine-dev: - docker build -f Dockerfile.alpine --build-arg DOCKER_GEN_VERSION=main -t nginxproxy/nginx-proxy:test . - test-debian: build-webserver build-nginx-proxy-test-debian test/pytest.sh From b6b7133a2eba412749a6e90856b98292ce09c3af Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 24 Feb 2022 15:17:47 +0100 Subject: [PATCH 22/43] fix: minor fixes on nginx template --- nginx.tmpl | 70 +++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 0eeeac9..351cf88 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -61,8 +61,8 @@ location {{ .Path }} { include uwsgi_params; uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; {{ else if eq .Proto "fastcgi" }} - root {{ trim .Vhostroot }}; - include fastcgi.conf; + root {{ trim .VhostRoot }}; + include fastcgi_params; fastcgi_pass {{ trim .Upstream }}; {{ else if eq .Proto "grpc" }} grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; @@ -85,12 +85,12 @@ location {{ .Path }} { } {{ end }} -{{ define "upstream-definition" }} +{{ define "upstream" }} {{ $networks := .Networks }} {{ $debug_all := .Debug }} upstream {{ .Upstream }} { - {{ $server_found := "false" }} - {{ range $container := .Containers }} + {{ $server_found := "false" }} + {{ range $container := .Containers }} {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} {{/* If only 1 port exposed, use that as a default, else 80 */}} {{ $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }} @@ -100,46 +100,46 @@ location {{ .Path }} { # Exposed ports: {{ $container.Addresses }} # Default virtual port: {{ $defaultPort }} # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} - {{ if not $address }} + {{ if not $address }} # /!\ Virtual port not exposed - {{ end }} + {{ end }} {{ end }} - {{ range $knownNetwork := $networks }} - {{ range $containerNetwork := $container.Networks }} - {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} + {{ range $knownNetwork := $networks }} + {{ range $containerNetwork := $container.Networks }} + {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} ## Can be connected with "{{ $containerNetwork.Name }}" network - {{ if $address }} - {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} - {{ if and $container.Node.ID $address.HostPort }} - {{ $server_found = "true" }} + {{ if $address }} + {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} + {{ if and $container.Node.ID $address.HostPort }} + {{ $server_found = "true" }} # {{ $container.Node.Name }}/{{ $container.Name }} server {{ $container.Node.Address.IP }}:{{ $address.HostPort }}; - {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} - {{ else if $containerNetwork }} - {{ $server_found = "true" }} + {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} + {{ else if $containerNetwork }} + {{ $server_found = "true" }} # {{ $container.Name }} server {{ $containerNetwork.IP }}:{{ $address.Port }}; - {{ end }} - {{ else if $containerNetwork }} + {{ end }} + {{ else if $containerNetwork }} # {{ $container.Name }} - {{ if $containerNetwork.IP }} - {{ $server_found = "true" }} + {{ if $containerNetwork.IP }} + {{ $server_found = "true" }} server {{ $containerNetwork.IP }}:{{ $port }}; - {{ else }} + {{ else }} # /!\ No IP for this network! - {{ end }} - {{ end }} - {{ else }} - # Cannot connect to network '{{ $containerNetwork.Name }}' of this container + {{ end }} {{ end }} + {{ else }} + # Cannot connect to network '{{ $containerNetwork.Name }}' of this container {{ end }} {{ end }} {{ end }} - {{/* nginx-proxy/nginx-proxy#1105 */}} - {{ if (eq $server_found "false") }} + {{ end }} + {{/* nginx-proxy/nginx-proxy#1105 */}} + {{ if (eq $server_found "false") }} # Fallback entry server 127.0.0.1 down; - {{ end }} + {{ end }} } {{ end }} @@ -263,13 +263,13 @@ server { {{ if eq $nPaths 0 }} # {{ $host }} - {{ template "upstream-definition" (dict "Upstream" $upstream_name "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} + {{ template "upstream" (dict "Upstream" $upstream_name "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} {{ else }} {{ range $path, $containers := $paths }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} # {{ $host }}{{ $path }} - {{ template "upstream-definition" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} + {{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} {{ end }} {{ end }} @@ -388,7 +388,7 @@ server { {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} - {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} {{ else }} {{ range $path, $container := $paths }} {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} @@ -399,7 +399,7 @@ server { {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} {{ if (not (contains $paths "/")) }} location / { @@ -436,7 +436,7 @@ server { {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} - {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "Vhostroot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} {{ else }} {{ range $path, $container := $paths }} {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} @@ -447,7 +447,7 @@ server { {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "Vhostroot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} {{ if (not (contains $paths "/")) }} location / { From 0185a2971c6538afa72021acefd9bfdc1552034d Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 24 Feb 2022 15:21:14 +0100 Subject: [PATCH 23/43] tests: fix virtual path tests for new dhparam --- test/test_internal/test_internal-per-vhost.yml | 1 - test/test_internal/test_internal-per-vpath.yml | 1 - test/test_ssl/test_virtual_path.yml | 1 - test/test_virtual-path/test_custom_conf.yml | 1 - test/test_virtual-path/test_forwarding.yml | 1 - test/test_virtual-path/test_location_precedence.yml | 1 - test/test_virtual-path/test_virtual_paths.yml | 2 -- 7 files changed, 8 deletions(-) diff --git a/test/test_internal/test_internal-per-vhost.yml b/test/test_internal/test_internal-per-vhost.yml index a935e53..5c732ee 100644 --- a/test/test_internal/test_internal-per-vhost.yml +++ b/test/test_internal/test_internal-per-vhost.yml @@ -19,6 +19,5 @@ sut: image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./network_internal.conf:/etc/nginx/network_internal.conf:ro diff --git a/test/test_internal/test_internal-per-vpath.yml b/test/test_internal/test_internal-per-vpath.yml index 1ea470f..f5bac55 100644 --- a/test/test_internal/test_internal-per-vpath.yml +++ b/test/test_internal/test_internal-per-vpath.yml @@ -23,6 +23,5 @@ sut: image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./network_internal.conf:/etc/nginx/network_internal.conf:ro diff --git a/test/test_ssl/test_virtual_path.yml b/test/test_ssl/test_virtual_path.yml index 07175ac..2260321 100644 --- a/test/test_ssl/test_virtual_path.yml +++ b/test/test_ssl/test_virtual_path.yml @@ -22,6 +22,5 @@ sut: image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./certs:/etc/nginx/certs:ro diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom_conf.yml index abd9d0c..40ab512 100644 --- a/test/test_virtual-path/test_custom_conf.yml +++ b/test/test_virtual-path/test_custom_conf.yml @@ -42,7 +42,6 @@ sut: DEFAULT_ROOT: 418 volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro - ./bar.conf:/etc/nginx/vhost.d/nginx-proxy.test_918d687a929083edd0c7224ee2293e0e7c062ab4_location:ro - ./alternate.conf:/etc/nginx/vhost.d/nginx-proxy.test_7fb22b74bbdf907425dbbad18e4462565cada230_location:ro diff --git a/test/test_virtual-path/test_forwarding.yml b/test/test_virtual-path/test_forwarding.yml index 78662d8..ee87e8d 100644 --- a/test/test_virtual-path/test_forwarding.yml +++ b/test/test_virtual-path/test_forwarding.yml @@ -12,7 +12,6 @@ sut: image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./certs:/etc/nginx/certs:ro environment: - DEFAULT_ROOT=301 http://$$host/web1$$request_uri diff --git a/test/test_virtual-path/test_location_precedence.yml b/test/test_virtual-path/test_location_precedence.yml index be93b58..be3248c 100644 --- a/test/test_virtual-path/test_location_precedence.yml +++ b/test/test_virtual-path/test_location_precedence.yml @@ -32,7 +32,6 @@ sut: image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - ./default.conf:/etc/nginx/vhost.d/default_location:ro - ./host.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_location:ro - ./path.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_99f2db0ed8aa95dbb5b87fca79c7eff2ff6bb8bd_location:ro diff --git a/test/test_virtual-path/test_virtual_paths.yml b/test/test_virtual-path/test_virtual_paths.yml index b335c3f..9f6a54f 100644 --- a/test/test_virtual-path/test_virtual_paths.yml +++ b/test/test_virtual-path/test_virtual_paths.yml @@ -40,5 +40,3 @@ sut: image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro - From 621e703fad4c6ab58fc1248211fe2a674b688e5f Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 24 Feb 2022 16:21:10 +0100 Subject: [PATCH 24/43] build: docker-gen main -> 0.8.2 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index bc4093d..9857927 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=main +ARG DOCKER_GEN_VERSION=0.8.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 98e9bc5..f8e5b56 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=main +ARG DOCKER_GEN_VERSION=0.8.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 061d129b1b38af335aad70ef7cd613254927adb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Feb 2022 15:39:59 +0000 Subject: [PATCH 25/43] chore(deps): bump golang from 1.16.7 to 1.17.7 Bumps golang from 1.16.7 to 1.17.7. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9857927..7aa1a77 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.8.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.16.7 as gobuilder +FROM golang:1.17.7 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index f8e5b56..ec330a8 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.8.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.16.7-alpine as gobuilder +FROM golang:1.17.7-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From fee27ea712eec20e0642043fad6fddf8edad1159 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 24 Feb 2022 16:43:45 +0100 Subject: [PATCH 26/43] docs: nginx badge 1.21.5 -> 1.21.6 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 880d0e8..dc8414b 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.21.5](https://img.shields.io/badge/nginx-1.21.5-brightgreen.svg) +![nginx 1.21.6](https://img.shields.io/badge/nginx-1.21.6-brightgreen.svg) [![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') From 5ab320d82aa4338e702b6f7dfa635cbe990b6503 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 4 Mar 2022 10:59:50 +0100 Subject: [PATCH 27/43] ci: publish Docker images to ghcr.io --- .github/workflows/dockerhub.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 74283b7..1630f8a 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -38,6 +38,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | + ghcr.io/nginx-proxy/nginx-proxy nginxproxy/nginx-proxy jwilder/nginx-proxy tags: | @@ -60,6 +61,13 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push the Debian based image if: github.ref == 'refs/heads/main' From 993e5f82824d016366615ec7fc6235ddd620da09 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 4 Mar 2022 11:10:14 +0100 Subject: [PATCH 28/43] ci: publish the alpine image to ghcr.io too --- .github/workflows/dockerhub.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 1630f8a..dfb4286 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -119,6 +119,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | + ghcr.io/nginx-proxy/nginx-proxy nginxproxy/nginx-proxy jwilder/nginx-proxy tags: | @@ -142,6 +143,13 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push the Alpine based image if: github.ref == 'refs/heads/main' From 0ea8a087612807971ebd21ae05a06868120ca5fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Mar 2022 04:11:51 +0000 Subject: [PATCH 29/43] chore(deps): bump golang from 1.17.7 to 1.17.8 Bumps golang from 1.17.7 to 1.17.8. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7aa1a77..16de38c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.8.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.17.7 as gobuilder +FROM golang:1.17.8 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index ec330a8..0208de6 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.8.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.17.7-alpine as gobuilder +FROM golang:1.17.8-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 5ee6db5e3e798588edd9433495f16617d688d148 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 7 Mar 2022 14:12:26 +0100 Subject: [PATCH 30/43] ci: remove dev branch build, fix build on tags --- .github/workflows/dockerhub.yml | 40 --------------------------------- 1 file changed, 40 deletions(-) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index dfb4286..706d298 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -7,13 +7,11 @@ on: push: branches: - main - - dev tags: - '*.*.*' paths-ignore: - 'test/*' - '.gitignore' - - '.travis.yml' - 'docker-compose-separate-containers.yml' - 'docker-compose.yml' - 'LICENSE' @@ -45,7 +43,6 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} - type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} @@ -70,7 +67,6 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push the Debian based image - if: github.ref == 'refs/heads/main' id: docker_build_debian uses: docker/build-push-action@v2 with: @@ -83,25 +79,8 @@ jobs: labels: ${{ steps.docker_meta_debian.outputs.labels }} - name: Images digests - if: github.ref == 'refs/heads/main' run: echo ${{ steps.docker_build_debian.outputs.digest }} - - name: Build and push the Debian based dev image - if: github.ref == 'refs/heads/dev' - id: docker_build_debian_dev - uses: docker/build-push-action@v2 - with: - file: Dockerfile - build-args: DOCKER_GEN_VERSION=main - platforms: linux/amd64,linux/arm64,linux/arm/v7 - push: true - tags: ${{ steps.docker_meta_debian.outputs.tags }} - labels: ${{ steps.docker_meta_debian.outputs.labels }} - - - name: Images digests - if: github.ref == 'refs/heads/dev' - run: echo ${{ steps.docker_build_debian_dev.outputs.digest }} - multiarch-build-alpine: runs-on: ubuntu-latest steps: @@ -126,7 +105,6 @@ jobs: type=semver,suffix=-alpine,pattern={{version}} type=semver,suffix=-alpine,pattern={{major}}.{{minor}} type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' }} - type=raw,value=dev-alpine,enable=${{ github.ref == 'refs/heads/dev' }} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} @@ -152,7 +130,6 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push the Alpine based image - if: github.ref == 'refs/heads/main' id: docker_build_alpine uses: docker/build-push-action@v2 with: @@ -165,21 +142,4 @@ jobs: labels: ${{ steps.docker_meta_alpine.outputs.labels }} - name: Images digests - if: github.ref == 'refs/heads/main' run: echo ${{ steps.docker_build_alpine.outputs.digest }} - - - name: Build and push the Alpine based dev image - if: github.ref == 'refs/heads/dev' - id: docker_build_alpine_dev - uses: docker/build-push-action@v2 - with: - file: Dockerfile.alpine - build-args: DOCKER_GEN_VERSION=main - platforms: linux/amd64,linux/arm64,linux/arm/v7 - push: true - tags: ${{ steps.docker_meta_alpine.outputs.tags }} - labels: ${{ steps.docker_meta_alpine.outputs.labels }} - - - name: Images digests - if: github.ref == 'refs/heads/dev' - run: echo ${{ steps.docker_build_alpine_dev.outputs.digest }} From 3257177d80cfcc27d3ae556471a9b87a7af5f92a Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 7 Mar 2022 14:14:16 +0100 Subject: [PATCH 31/43] fix: remove outdated comment from Dockerfiles --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 16de38c..851baa1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ RUN apt-get update \ && rm -r /var/lib/apt/lists/* -# Configure Nginx and apply fix for very long server names +# Configure Nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf \ && sed -i 's/worker_connections 1024/worker_connections 10240/' /etc/nginx/nginx.conf \ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 0208de6..7779572 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -52,7 +52,7 @@ RUN apk add --no-cache --virtual .run-deps \ ca-certificates bash wget openssl \ && update-ca-certificates -# Configure Nginx and apply fix for very long server names +# Configure Nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf \ && sed -i 's/worker_connections 1024/worker_connections 10240/' /etc/nginx/nginx.conf \ From 5aba125fb7b359c44b419a746c052a3806d08d09 Mon Sep 17 00:00:00 2001 From: Gilles Filippini Date: Mon, 1 Jun 2020 07:15:00 +0000 Subject: [PATCH 32/43] chore: do not copy useless files into the image Move required files but 'nginx.tmpl' into a local 'app' folder and copy the folder content into the image. 'nginx.tmpl' should be moved as well, but this is a breaking change for configuration with a separate 'docker-gen' container. --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- Procfile => app/Procfile | 0 {dhparam => app/dhparam}/ffdhe2048.pem | 0 {dhparam => app/dhparam}/ffdhe3072.pem | 0 {dhparam => app/dhparam}/ffdhe4096.pem | 0 docker-entrypoint.sh => app/docker-entrypoint.sh | 0 test/test_ssl/test_dhparam.yml | 2 +- 8 files changed, 3 insertions(+), 3 deletions(-) rename Procfile => app/Procfile (100%) rename {dhparam => app/dhparam}/ffdhe2048.pem (100%) rename {dhparam => app/dhparam}/ffdhe3072.pem (100%) rename {dhparam => app/dhparam}/ffdhe4096.pem (100%) rename docker-entrypoint.sh => app/docker-entrypoint.sh (100%) diff --git a/Dockerfile b/Dockerfile index 851baa1..35a2dbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,7 +67,7 @@ COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ -COPY . /app/ +COPY app nginx.tmpl /app/ WORKDIR /app/ ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 7779572..602c5f9 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -64,7 +64,7 @@ COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ -COPY . /app/ +COPY app nginx.tmpl /app/ WORKDIR /app/ ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/Procfile b/app/Procfile similarity index 100% rename from Procfile rename to app/Procfile diff --git a/dhparam/ffdhe2048.pem b/app/dhparam/ffdhe2048.pem similarity index 100% rename from dhparam/ffdhe2048.pem rename to app/dhparam/ffdhe2048.pem diff --git a/dhparam/ffdhe3072.pem b/app/dhparam/ffdhe3072.pem similarity index 100% rename from dhparam/ffdhe3072.pem rename to app/dhparam/ffdhe3072.pem diff --git a/dhparam/ffdhe4096.pem b/app/dhparam/ffdhe4096.pem similarity index 100% rename from dhparam/ffdhe4096.pem rename to app/dhparam/ffdhe4096.pem diff --git a/docker-entrypoint.sh b/app/docker-entrypoint.sh similarity index 100% rename from docker-entrypoint.sh rename to app/docker-entrypoint.sh diff --git a/test/test_ssl/test_dhparam.yml b/test/test_ssl/test_dhparam.yml index fa4fe1e..505ac4c 100644 --- a/test/test_ssl/test_dhparam.yml +++ b/test/test_ssl/test_dhparam.yml @@ -54,7 +54,7 @@ with_custom_file: volumes: - *docker-sock - *nginx-certs - - ../../dhparam/ffdhe3072.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ../../app/dhparam/ffdhe3072.pem:/etc/nginx/dhparam/dhparam.pem:ro with_skip: container_name: dh-skip From fea6cc7537ae3e60d94903be2e0928c0c8941008 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 7 Mar 2022 15:35:49 +0100 Subject: [PATCH 33/43] chore: update .dockerignore --- .dockerignore | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index 8fafbb0..6849862 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,10 @@ .git +.github +test .dockerignore -circle.yml +.gitignore +*.yml +Dockerfile* +LICENSE Makefile README.md -test From 4ea3437dfab423b6710b32b2aa9d50f9f750bed9 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Wed, 9 Mar 2022 12:05:56 +0100 Subject: [PATCH 34/43] chore: include license into the Docker images --- .dockerignore | 1 - Dockerfile | 2 +- Dockerfile.alpine | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.dockerignore b/.dockerignore index 6849862..aad7a31 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,6 +5,5 @@ test .gitignore *.yml Dockerfile* -LICENSE Makefile README.md diff --git a/Dockerfile b/Dockerfile index 35a2dbb..8a46d97 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,7 +67,7 @@ COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ -COPY app nginx.tmpl /app/ +COPY app nginx.tmpl LICENSE /app/ WORKDIR /app/ ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 602c5f9..cc1247e 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -64,7 +64,7 @@ COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ -COPY app nginx.tmpl /app/ +COPY app nginx.tmpl LICENSE /app/ WORKDIR /app/ ENTRYPOINT ["/app/docker-entrypoint.sh"] From 1cc3bbf5cea53a78dd9a830d7948f3e9ebd5b3e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Mar 2022 04:16:58 +0000 Subject: [PATCH 35/43] chore(deps): bump pytest from 7.0.1 to 7.1.1 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.0.1 to 7.1.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/7.0.1...7.1.1) --- updated-dependencies: - dependency-name: pytest 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 3747455..865bb27 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==1.11.1 docker-compose==1.29.2 docker==5.0.3 -pytest==7.0.1 +pytest==7.1.1 requests==2.27.1 From 55d913255d82f78f62a41c490d82b00454701b40 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 20 Mar 2022 18:54:07 -0400 Subject: [PATCH 36/43] Fix IPv6 HTTP listen port --- nginx.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.tmpl b/nginx.tmpl index 351cf88..610edf1 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -420,7 +420,7 @@ server { {{ end }} listen {{ $external_http_port }} {{ $default_server }}; {{ if $enable_ipv6 }} - listen [::]:80 {{ $default_server }}; + listen [::]:{{ $external_http_port }} {{ $default_server }}; {{ end }} {{ $access_log }} From 6a0a1f67825dfb5beb7fe58aaaa611773a212a30 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 25 Mar 2022 11:05:17 +0100 Subject: [PATCH 37/43] build: bump docker-gen from 0.8.2 to 0.8.4 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8a46d97..46524b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.8.2 +ARG DOCKER_GEN_VERSION=0.8.4 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries diff --git a/Dockerfile.alpine b/Dockerfile.alpine index cc1247e..14f558e 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.8.2 +ARG DOCKER_GEN_VERSION=0.8.4 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 2e1da37f9af3dbd4a70a8095fab1d55bb972ef9b Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 10 Apr 2022 13:15:02 +0200 Subject: [PATCH 38/43] build: bump docker-gen from 0.8.4 to 0.9.0 --- Dockerfile | 4 ++-- Dockerfile.alpine | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 46524b8..168858b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.8.4 +ARG DOCKER_GEN_VERSION=0.9.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.17.8 as gobuilder +FROM golang:1.18.0 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 14f558e..f98c4b8 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,9 +1,9 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.8.4 +ARG DOCKER_GEN_VERSION=0.9.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.17.8-alpine as gobuilder +FROM golang:1.18.0-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From ae0faa43cd3b452bc576a4f440fff22b19589301 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 04:10:10 +0000 Subject: [PATCH 39/43] chore(deps): bump golang from 1.18.0 to 1.18.1 Bumps golang from 1.18.0 to 1.18.1. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 168858b..dcd0285 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.18.0 as gobuilder +FROM golang:1.18.1 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index f98c4b8..51cafd9 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.18.0-alpine as gobuilder +FROM golang:1.18.1-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 998d56c473079901b788c6ddc297307016362419 Mon Sep 17 00:00:00 2001 From: Nitin Jain Date: Wed, 6 Apr 2022 16:24:27 +0530 Subject: [PATCH 40/43] chore: indent location, upstream in template --- nginx.tmpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 610edf1..e8a555d 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -51,10 +51,10 @@ {{ end }} {{ define "location" }} -location {{ .Path }} { + location {{ .Path }} { {{ if eq .NetworkTag "internal" }} - # Only allow traffic from internal clients - include /etc/nginx/network_internal.conf; + # Only allow traffic from internal clients + include /etc/nginx/network_internal.conf; {{ end }} {{ if eq .Proto "uwsgi" }} @@ -88,7 +88,7 @@ location {{ .Path }} { {{ define "upstream" }} {{ $networks := .Networks }} {{ $debug_all := .Debug }} - upstream {{ .Upstream }} { +upstream {{ .Upstream }} { {{ $server_found := "false" }} {{ range $container := .Containers }} {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} @@ -140,7 +140,7 @@ location {{ .Path }} { # Fallback entry server 127.0.0.1 down; {{ end }} - } +} {{ end }} {{ if ne $nginx_proxy_version "" }} From 20e76ac7a68f89060bb234c58dfb99f3d3d3fb9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 04:22:00 +0000 Subject: [PATCH 41/43] chore(deps): bump pytest from 7.1.1 to 7.1.2 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.1 to 7.1.2. - [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/7.1.1...7.1.2) --- updated-dependencies: - dependency-name: pytest 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 865bb27..7aa8731 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==1.11.1 docker-compose==1.29.2 docker==5.0.3 -pytest==7.1.1 +pytest==7.1.2 requests==2.27.1 From 77c672f57661c1b41ea5d2859a9db23ae0fbbc05 Mon Sep 17 00:00:00 2001 From: Robin Windey Date: Fri, 7 Oct 2022 10:17:18 +0000 Subject: [PATCH 42/43] Merge Upstream --- .dockerignore | 7 +- .github/workflows/dockerhub.yml | 21 +- Dockerfile | 6 +- Dockerfile.alpine | 6 +- README.md | 49 +++- Procfile => app/Procfile | 0 {dhparam => app/dhparam}/ffdhe2048.pem | 0 {dhparam => app/dhparam}/ffdhe3072.pem | 0 {dhparam => app/dhparam}/ffdhe4096.pem | 0 .../docker-entrypoint.sh | 0 network_internal.conf | 1 + nginx.tmpl | 267 ++++++++++-------- test/test_events.py | 38 ++- test/test_internal/network_internal.conf | 11 + test/test_internal/test_internal-per-vhost.py | 14 + .../test_internal/test_internal-per-vhost.yml | 23 ++ test/test_internal/test_internal-per-vpath.py | 14 + .../test_internal/test_internal-per-vpath.yml | 27 ++ test/test_ssl/test_dhparam.yml | 2 +- test/test_ssl/test_virtual_path.py | 15 + test/test_ssl/test_virtual_path.yml | 26 ++ test/test_virtual-path/alternate.conf | 1 + test/test_virtual-path/bar.conf | 1 + test/test_virtual-path/default.conf | 1 + test/test_virtual-path/foo.conf | 1 + test/test_virtual-path/host.conf | 1 + test/test_virtual-path/path.conf | 1 + test/test_virtual-path/test_custom_conf.py | 38 +++ test/test_virtual-path/test_custom_conf.yml | 48 ++++ test/test_virtual-path/test_forwarding.py | 18 ++ test/test_virtual-path/test_forwarding.yml | 17 ++ .../test_location_precedence.py | 32 +++ .../test_location_precedence.yml | 37 +++ test/test_virtual-path/test_virtual_paths.py | 59 ++++ test/test_virtual-path/test_virtual_paths.yml | 42 +++ 35 files changed, 700 insertions(+), 124 deletions(-) rename Procfile => app/Procfile (100%) rename {dhparam => app/dhparam}/ffdhe2048.pem (100%) rename {dhparam => app/dhparam}/ffdhe3072.pem (100%) rename {dhparam => app/dhparam}/ffdhe4096.pem (100%) rename docker-entrypoint.sh => app/docker-entrypoint.sh (100%) create mode 100644 test/test_internal/network_internal.conf create mode 100644 test/test_internal/test_internal-per-vhost.py create mode 100644 test/test_internal/test_internal-per-vhost.yml create mode 100644 test/test_internal/test_internal-per-vpath.py create mode 100644 test/test_internal/test_internal-per-vpath.yml create mode 100644 test/test_ssl/test_virtual_path.py create mode 100644 test/test_ssl/test_virtual_path.yml create mode 100644 test/test_virtual-path/alternate.conf create mode 100644 test/test_virtual-path/bar.conf create mode 100644 test/test_virtual-path/default.conf create mode 100644 test/test_virtual-path/foo.conf create mode 100644 test/test_virtual-path/host.conf create mode 100644 test/test_virtual-path/path.conf create mode 100644 test/test_virtual-path/test_custom_conf.py create mode 100644 test/test_virtual-path/test_custom_conf.yml create mode 100644 test/test_virtual-path/test_forwarding.py create mode 100644 test/test_virtual-path/test_forwarding.yml create mode 100644 test/test_virtual-path/test_location_precedence.py create mode 100644 test/test_virtual-path/test_location_precedence.yml create mode 100644 test/test_virtual-path/test_virtual_paths.py create mode 100644 test/test_virtual-path/test_virtual_paths.yml diff --git a/.dockerignore b/.dockerignore index 8fafbb0..aad7a31 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,9 @@ .git +.github +test .dockerignore -circle.yml +.gitignore +*.yml +Dockerfile* Makefile README.md -test diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index cb3635e..706d298 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -12,7 +12,6 @@ on: paths-ignore: - 'test/*' - '.gitignore' - - '.travis.yml' - 'docker-compose-separate-containers.yml' - 'docker-compose.yml' - 'LICENSE' @@ -37,12 +36,13 @@ jobs: uses: docker/metadata-action@v3 with: images: | + ghcr.io/nginx-proxy/nginx-proxy nginxproxy/nginx-proxy jwilder/nginx-proxy tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} @@ -58,6 +58,13 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push the Debian based image id: docker_build_debian @@ -91,12 +98,13 @@ jobs: uses: docker/metadata-action@v3 with: images: | + ghcr.io/nginx-proxy/nginx-proxy nginxproxy/nginx-proxy jwilder/nginx-proxy tags: | type=semver,suffix=-alpine,pattern={{version}} type=semver,suffix=-alpine,pattern={{major}}.{{minor}} - type=raw,value=alpine,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' }} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} @@ -113,6 +121,13 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push the Alpine based image id: docker_build_alpine diff --git a/Dockerfile b/Dockerfile index 86b7722..bdc50f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.7.7 +ARG DOCKER_GEN_VERSION=0.9.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries @@ -55,7 +55,7 @@ RUN apt-get update \ && rm -r /var/lib/apt/lists/* -# Configure Nginx and apply fix for very long server names +# Configure Nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf \ && sed -i 's/worker_connections 1024/worker_connections 10240/' /etc/nginx/nginx.conf \ @@ -67,7 +67,7 @@ COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ -COPY . /app/ +COPY app nginx.tmpl LICENSE /app/ WORKDIR /app/ ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 9839eaa..aab6c1f 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.7.7 +ARG DOCKER_GEN_VERSION=0.9.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries @@ -52,7 +52,7 @@ RUN apk add --no-cache --virtual .run-deps \ ca-certificates bash wget openssl \ && update-ca-certificates -# Configure Nginx and apply fix for very long server names +# Configure Nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf \ && sed -i 's/worker_connections 1024/worker_connections 10240/' /etc/nginx/nginx.conf \ @@ -64,7 +64,7 @@ COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ -COPY . /app/ +COPY app nginx.tmpl LICENSE /app/ WORKDIR /app/ ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/README.md b/README.md index bc2b1e5..bfad473 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ To push a new version to docker hub just create a new tag like `v1.0.1`. Github it to the docker hub automatically. See `.github/workflows/docker_push.yml` for details. # Original Readme -![nginx 1.21.5](https://img.shields.io/badge/nginx-1.21.5-brightgreen.svg) +[![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.21.6](https://img.shields.io/badge/nginx-1.21.6-brightgreen.svg) [![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') @@ -123,6 +125,50 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie You can also use wildcards at the beginning and the end of host name, like `*.bar.com` or `foo.bar.*`. Or even a regular expression, which can be very useful in conjunction with a wildcard DNS service like [nip.io](https://nip.io) or [sslip.io](https://sslip.io), using `~^foo\.bar\..*\.nip\.io` will match `foo.bar.127.0.0.1.nip.io`, `foo.bar.10.0.2.2.nip.io` and all other given IPs. More information about this topic can be found in the nginx documentation about [`server_names`](http://nginx.org/en/docs/http/server_names.html). +### Path-based Routing + +You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a `VIRTUAL_PATH` environment variable containing the absolute path to where the container should be mounted. For example with `VIRTUAL_HOST=foo.example.com` and `VIRTUAL_PATH=/api/v2/service`, then requests to http://foo.example.com/api/v2/service will be routed to the container. If you wish to have a container serve the root while other containers serve other paths, give the root container a `VIRTUAL_PATH` of `/`. Unmatched paths will be served by the container at `/` or will return the default nginx error page if no container has been assigned `/`. +It is also possible to specify multiple paths with regex locations like `VIRTUAL_PATH=~^/(app1|alternative1)/`. For further details see the nginx documentation on location blocks. This is not compatible with `VIRTUAL_DEST`. + +The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. + +**NOTE**: Your application needs to be able to generate links starting with `VIRTUAL_PATH`. This can be achieved by it being natively on this path or having an option to prepend this path. The application does not need to expect this path in the request. + +#### VIRTUAL_DEST + +This environment variable can be used to rewrite the `VIRTUAL_PATH` part of the requested URL to proxied application. The default value is empty (off). +Make sure that your settings won't result in the slash missing or being doubled. Both these versions can cause troubles. + +If the application runs natively on this sub-path or has a setting to do so, `VIRTUAL_DEST` should not be set or empty. +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 +``` + +In this example, the incoming request `http://example.tld/app1/foo` will be proxied as `http://app1/foo` instead of `http://app1/app1/foo`. + +#### Per-VIRTUAL_PATH location configuration + +The same options as from [Per-VIRTUAL_HOST location configuration](#Per-VIRTUAL_HOST-location-configuration) are available on a `VIRTUAL_PATH` basis. +The only difference is that the filename gets an additional block `HASH=$(echo -n $VIRTUAL_PATH | sha1sum | awk '{ print $1 }')`. This is the sha1-hash of the `VIRTUAL_PATH` (no newline). This is done filename sanitization purposes. +The used filename is `${VIRTUAL_HOST}_${HASH}_location` + +The filename of the previous example would be `example.tld_8610f6c344b4096614eab6e09d58885349f42faf_location`. + +#### DEFAULT_ROOT + +This environment variable of the nginx proxy container can be used to customize the return error page if no matching path is found. Furthermore it is possible to use anything which is compatible with the `return` statement of nginx. + +For example `DEFAUL_ROOT=418` will return a 418 error page instead of the normal 404 one. +Another example is `DEFAULT_ROOT="301 https://github.com/nginx-proxy/nginx-proxy/blob/main/README.md"` which would redirect an invalid request to this documentation. +Nginx variables such as $scheme, $host, and $request_uri can be used. However, care must be taken to make sure the $ signs are escaped properly. +If you want to use `301 $scheme://$host/myapp1$request_uri` you should use: + +* Bash: `DEFAULT_ROOT='301 $scheme://$host/myapp1$request_uri'` +* Docker Compose yaml: `- DEFAULT_ROOT: 301 $$scheme://$$host/myapp1$$request_uri` + + ### Multiple Networks With the addition of [overlay networking](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) in Docker 1.9, your `nginx-proxy` container may need to connect to backend containers on multiple networks. By default, if you don't pass the `--net` flag when your `nginx-proxy` container is created, it will only be attached to the default `bridge` network. This means that it will not be able to connect to containers on networks other than `bridge`. @@ -343,6 +389,7 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; +proxy_set_header X-Forwarded-Path $request_uri; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; diff --git a/Procfile b/app/Procfile similarity index 100% rename from Procfile rename to app/Procfile diff --git a/dhparam/ffdhe2048.pem b/app/dhparam/ffdhe2048.pem similarity index 100% rename from dhparam/ffdhe2048.pem rename to app/dhparam/ffdhe2048.pem diff --git a/dhparam/ffdhe3072.pem b/app/dhparam/ffdhe3072.pem similarity index 100% rename from dhparam/ffdhe3072.pem rename to app/dhparam/ffdhe3072.pem diff --git a/dhparam/ffdhe4096.pem b/app/dhparam/ffdhe4096.pem similarity index 100% rename from dhparam/ffdhe4096.pem rename to app/dhparam/ffdhe4096.pem diff --git a/docker-entrypoint.sh b/app/docker-entrypoint.sh similarity index 100% rename from docker-entrypoint.sh rename to app/docker-entrypoint.sh diff --git a/network_internal.conf b/network_internal.conf index cdf3c9c..bacceb1 100644 --- a/network_internal.conf +++ b/network_internal.conf @@ -3,4 +3,5 @@ allow 127.0.0.0/8; allow 10.0.0.0/8; allow 192.168.0.0/16; allow 172.16.0.0/12; +allow fc00::/7; # IPv6 local address range deny all; diff --git a/nginx.tmpl b/nginx.tmpl index e8e36ac..9162bc4 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -5,6 +5,7 @@ {{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }} {{ $debug_all := $.Env.DEBUG }} {{ $sha1_upstream_name := parseBool (coalesce $.Env.SHA1_UPSTREAM_NAME "false") }} +{{ $default_root_response := coalesce $.Env.DEFAULT_ROOT "404" }} {{ define "ssl_policy" }} {{ if eq .ssl_policy "Mozilla-Modern" }} @@ -49,6 +50,99 @@ {{ end }} {{ end }} +{{ define "location" }} + location {{ .Path }} { + {{ if eq .NetworkTag "internal" }} + # Only allow traffic from internal clients + include /etc/nginx/network_internal.conf; + {{ end }} + + {{ if eq .Proto "uwsgi" }} + include uwsgi_params; + uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; + {{ else if eq .Proto "fastcgi" }} + root {{ trim .VhostRoot }}; + include fastcgi_params; + fastcgi_pass {{ trim .Upstream }}; + {{ else if eq .Proto "grpc" }} + grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; + {{ else }} + proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }}; + {{ end }} + + {{ if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} + auth_basic "Restricted {{ .Host }}"; + auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }}; + {{ end }} + + {{ if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }} + include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }}; + {{ else if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} + include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; + {{ else if (exists "/etc/nginx/vhost.d/default_location") }} + include /etc/nginx/vhost.d/default_location; + {{ end }} +} +{{ end }} + +{{ define "upstream" }} + {{ $networks := .Networks }} + {{ $debug_all := .Debug }} +upstream {{ .Upstream }} { + {{ $server_found := "false" }} + {{ range $container := .Containers }} + {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} + {{/* If only 1 port exposed, use that as a default, else 80 */}} + {{ $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }} + {{ $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} + {{ $address := where $container.Addresses "Port" $port | first }} + {{ if $debug }} + # Exposed ports: {{ $container.Addresses }} + # Default virtual port: {{ $defaultPort }} + # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} + {{ if not $address }} + # /!\ Virtual port not exposed + {{ end }} + {{ end }} + {{ range $knownNetwork := $networks }} + {{ range $containerNetwork := $container.Networks }} + {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} + ## Can be connected with "{{ $containerNetwork.Name }}" network + {{ if $address }} + {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} + {{ if and $container.Node.ID $address.HostPort }} + {{ $server_found = "true" }} + # {{ $container.Node.Name }}/{{ $container.Name }} + server {{ $container.Node.Address.IP }}:{{ $address.HostPort }}; + {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} + {{ else if $containerNetwork }} + {{ $server_found = "true" }} + # {{ $container.Name }} + server {{ $containerNetwork.IP }}:{{ $address.Port }}; + {{ end }} + {{ else if $containerNetwork }} + # {{ $container.Name }} + {{ if $containerNetwork.IP }} + {{ $server_found = "true" }} + server {{ $containerNetwork.IP }}:{{ $port }}; + {{ else }} + # /!\ No IP for this network! + {{ end }} + {{ end }} + {{ else }} + # Cannot connect to network '{{ $containerNetwork.Name }}' of this container + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{/* nginx-proxy/nginx-proxy#1105 */}} + {{ if (eq $server_found "false") }} + # Fallback entry + server 127.0.0.1 down; + {{ end }} +} +{{ end }} + {{ if ne $nginx_proxy_version "" }} # nginx-proxy version : {{ $nginx_proxy_version }} {{ end }} @@ -100,6 +194,7 @@ access_log off; {{/* Get the SSL_POLICY defined by this container, falling back to "Mozilla-Intermediate" */}} {{ $ssl_policy := or ($.Env.SSL_POLICY) "Mozilla-Intermediate" }} {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} +error_log /dev/stderr; {{ if $.Env.RESOLVERS }} resolver {{ $.Env.RESOLVERS }}; @@ -119,6 +214,7 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; +proxy_set_header X-Original-URI $request_uri; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; @@ -162,73 +258,27 @@ server { {{ $is_regexp := hasPrefix "~" $host }} {{ $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} -# {{ $host }} -upstream {{ $upstream_name }} { +{{ $paths := groupBy $containers "Env.VIRTUAL_PATH" }} +{{ $nPaths := len $paths }} -{{ $server_found := "false" }} -{{ range $container := $containers }} - {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} - {{/* If only 1 port exposed, use that as a default, else 80 */}} - {{ $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }} - {{ $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} - {{ $address := where $container.Addresses "Port" $port | first }} - {{ if $debug }} - # Exposed ports: {{ $container.Addresses }} - # Default virtual port: {{ $defaultPort }} - # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} - {{ if not $address }} - # /!\ Virtual port not exposed - {{ end }} - {{ end }} - {{ range $knownNetwork := $CurrentContainer.Networks }} - {{ range $containerNetwork := $container.Networks }} - {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} - ## Can be connected with "{{ $containerNetwork.Name }}" network - {{ if $address }} - {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} - {{ if and $container.Node.ID $address.HostPort }} - {{ $server_found = "true" }} - # {{ $container.Node.Name }}/{{ $container.Name }} - server {{ $container.Node.Address.IP }}:{{ $address.HostPort }}; - {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} - {{ else if $containerNetwork }} - {{ $server_found = "true" }} - # {{ $container.Name }} - server {{ $containerNetwork.IP }}:{{ $address.Port }}; - {{ end }} - {{ else if $containerNetwork }} - # {{ $container.Name }} - {{ if $containerNetwork.IP }} - {{ $server_found = "true" }} - server {{ $containerNetwork.IP }}:{{ $port }}; - {{ else }} - # /!\ No IP for this network! - {{ end }} - {{ end }} - {{ else }} - # Cannot connect to network '{{ $containerNetwork.Name }}' of this container - {{ end }} - {{ end }} +{{ if eq $nPaths 0 }} + # {{ $host }} + {{ template "upstream" (dict "Upstream" $upstream_name "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} +{{ else }} + {{ range $path, $containers := $paths }} + {{ $sum := sha1 $path }} + {{ $upstream := printf "%s-%s" $upstream_name $sum }} + # {{ $host }}{{ $path }} + {{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} {{ end }} {{ end }} -{{/* nginx-proxy/nginx-proxy#1105 */}} -{{ if (eq $server_found "false") }} - # Fallback entry - server 127.0.0.1 down; -{{ end }} -} {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} {{ $default_server := index (dict $host "" $default_host "default_server") $host }} -{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} -{{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} - {{/* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}} {{ $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }} -{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} -{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $.Env.HTTPS_METHOD "redirect") }} @@ -312,11 +362,6 @@ server { {{ end }} {{ $access_log }} - {{ if eq $network_tag "internal" }} - # Only allow traffic from internal clients - include /etc/nginx/network_internal.conf; - {{ end }} - {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} ssl_session_timeout 5m; @@ -349,30 +394,33 @@ server { {{ if (hasPrefix "www." $host) }} return 301 https://{{ trimPrefix "www." $host }}$request_uri; {{ else }} - location / { - {{ if eq $proto "uwsgi" }} - include uwsgi_params; - uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else if eq $proto "fastcgi" }} - root {{ trim $vhost_root }}; - include fastcgi_params; - fastcgi_pass {{ trim $upstream_name }}; - {{ else if eq $proto "grpc" }} - grpc_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else }} - proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ end }} - {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} - auth_basic "Restricted {{ $host }}"; - auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; + {{ if eq $nPaths 0 }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} + + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} + {{ else }} + {{ range $path, $container := $paths }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $container "Env.VIRTUAL_PROTO")) "http") }} + + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }} + {{ $sum := sha1 $path }} + {{ $upstream := printf "%s-%s" $upstream_name $sum }} + {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} - include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; - {{ else if (exists "/etc/nginx/vhost.d/default_location") }} - include /etc/nginx/vhost.d/default_location; + {{ if (not (contains $paths "/")) }} + location / { + return {{ $default_root_response }}; + } {{ end }} - } + {{ end }} + {{ end }} } @@ -387,15 +435,10 @@ server { {{ end }} listen {{ $external_http_port }} {{ $default_server }}; {{ if $enable_ipv6 }} - listen [::]:80 {{ $default_server }}; + listen [::]:{{ $external_http_port }} {{ $default_server }}; {{ end }} {{ $access_log }} - {{ if eq $network_tag "internal" }} - # Only allow traffic from internal clients - include /etc/nginx/network_internal.conf; - {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s" $host }}; {{ else if (exists "/etc/nginx/vhost.d/default") }} @@ -405,29 +448,33 @@ server { {{ if (hasPrefix "www." $host) }} return 301 http://{{ trimPrefix "www." $host }}$request_uri; {{ else }} - location / { - {{ if eq $proto "uwsgi" }} - include uwsgi_params; - uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else if eq $proto "fastcgi" }} - root {{ trim $vhost_root }}; - include fastcgi_params; - fastcgi_pass {{ trim $upstream_name }}; - {{ else if eq $proto "grpc" }} - grpc_pass {{ trim $proto }}://{{ trim $upstream_name }}; - {{ else }} - proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; + + {{ if eq $nPaths 0 }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} + + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} + {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }} + {{ else }} + {{ range $path, $container := $paths }} + {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} + {{ $proto := trim (or (first (groupByKeys $container "Env.VIRTUAL_PROTO")) "http") }} + + {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} + {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }} + {{ $sum := sha1 $path }} + {{ $upstream := printf "%s-%s" $upstream_name $sum }} + {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} + {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} - {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} - auth_basic "Restricted {{ $host }}"; - auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; + {{ if (not (contains $paths "/")) }} + location / { + return {{ $default_root_response }}; + } {{ end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} - include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; - {{ else if (exists "/etc/nginx/vhost.d/default_location") }} - include /etc/nginx/vhost.d/default_location; - {{ end }} - } + {{ end }} + {{ end }} } diff --git a/test/test_events.py b/test/test_events.py index 201917f..b5da3dd 100644 --- a/test/test_events.py +++ b/test/test_events.py @@ -29,13 +29,36 @@ def web1(docker_compose): except NotFound: pass +@pytest.fixture() +def web2(docker_compose): + """ + pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy`, `VIRTUAL_PATH=/web2/` and `VIRTUAL_DEST=/` listening on port 82. + """ + container = docker_compose.containers.run( + name="web2", + image="web", + detach=True, + environment={ + "WEB_PORTS": "82", + "VIRTUAL_HOST": "nginx-proxy", + "VIRTUAL_PATH": "/web2/", + "VIRTUAL_DEST": "/", + }, + ports={"82/tcp": None} + ) + sleep(2) # give it some time to initialize and for docker-gen to detect it + yield container + try: + docker_compose.containers.get("web2").remove(force=True) + except NotFound: + pass def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 -def test_new_container_is_detected(web1, nginxproxy): +def test_new_container_is_detected_vhost(web1, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy/port") assert r.status_code == 200 assert "answer from port 81\n" == r.text @@ -44,3 +67,16 @@ def test_new_container_is_detected(web1, nginxproxy): sleep(2) r = nginxproxy.get("http://web1.nginx-proxy/port") assert r.status_code == 503 + +def test_new_container_is_detected_vpath(web2, nginxproxy): + r = nginxproxy.get("http://nginx-proxy/web2/port") + assert r.status_code == 200 + assert "answer from port 82\n" == r.text + r = nginxproxy.get("http://nginx-proxy/port") + assert r.status_code in [404, 503] + + web2.remove(force=True) + sleep(2) + r = nginxproxy.get("http://nginx-proxy/web2/port") + assert r.status_code == 503 + diff --git a/test/test_internal/network_internal.conf b/test/test_internal/network_internal.conf new file mode 100644 index 0000000..496e569 --- /dev/null +++ b/test/test_internal/network_internal.conf @@ -0,0 +1,11 @@ +# Only allow traffic from internal clients +allow 127.0.0.0/8; +allow 10.0.0.0/8; +allow 192.168.0.0/16; +allow 172.16.0.0/12; +allow fc00::/7; # IPv6 local address range +deny all; + +# Dummy header for testing +add_header X-network internal; + diff --git a/test/test_internal/test_internal-per-vhost.py b/test/test_internal/test_internal-per-vhost.py new file mode 100644 index 0000000..4586cc0 --- /dev/null +++ b/test/test_internal/test_internal-per-vhost.py @@ -0,0 +1,14 @@ +import pytest + +def test_network_web1(docker_compose, nginxproxy): + r = nginxproxy.get("http://web1.nginx-proxy.local/port") + assert r.status_code == 200 + assert r.text == "answer from port 81\n" + assert "X-network" in r.headers + assert "internal" == r.headers["X-network"] + +def test_network_web2(docker_compose, nginxproxy): + r = nginxproxy.get("http://web2.nginx-proxy.local/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + assert "X-network" not in r.headers diff --git a/test/test_internal/test_internal-per-vhost.yml b/test/test_internal/test_internal-per-vhost.yml new file mode 100644 index 0000000..5c732ee --- /dev/null +++ b/test/test_internal/test_internal-per-vhost.yml @@ -0,0 +1,23 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: web1.nginx-proxy.local + NETWORK_ACCESS: internal + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: web2.nginx-proxy.local + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./network_internal.conf:/etc/nginx/network_internal.conf:ro + diff --git a/test/test_internal/test_internal-per-vpath.py b/test/test_internal/test_internal-per-vpath.py new file mode 100644 index 0000000..e95fe00 --- /dev/null +++ b/test/test_internal/test_internal-per-vpath.py @@ -0,0 +1,14 @@ +import pytest + +def test_network_web1(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.local/web1/port") + assert r.status_code == 200 + assert r.text == "answer from port 81\n" + assert "X-network" in r.headers + assert "internal" == r.headers["X-network"] + +def test_network_web2(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.local/web2/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + assert "X-network" not in r.headers diff --git a/test/test_internal/test_internal-per-vpath.yml b/test/test_internal/test_internal-per-vpath.yml new file mode 100644 index 0000000..f5bac55 --- /dev/null +++ b/test/test_internal/test_internal-per-vpath.yml @@ -0,0 +1,27 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: nginx-proxy.local + VIRTUAL_PATH: /web1/ + VIRTUAL_DEST: / + NETWORK_ACCESS: internal + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: nginx-proxy.local + VIRTUAL_PATH: /web2/ + VIRTUAL_DEST: / + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./network_internal.conf:/etc/nginx/network_internal.conf:ro + diff --git a/test/test_ssl/test_dhparam.yml b/test/test_ssl/test_dhparam.yml index fa4fe1e..505ac4c 100644 --- a/test/test_ssl/test_dhparam.yml +++ b/test/test_ssl/test_dhparam.yml @@ -54,7 +54,7 @@ with_custom_file: volumes: - *docker-sock - *nginx-certs - - ../../dhparam/ffdhe3072.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ../../app/dhparam/ffdhe3072.pem:/etc/nginx/dhparam/dhparam.pem:ro with_skip: container_name: dh-skip diff --git a/test/test_ssl/test_virtual_path.py b/test/test_ssl/test_virtual_path.py new file mode 100644 index 0000000..508653f --- /dev/null +++ b/test/test_ssl/test_virtual_path.py @@ -0,0 +1,15 @@ +import pytest + +@pytest.mark.parametrize("path", ["web1", "web2"]) +def test_web1_http_redirects_to_https(docker_compose, nginxproxy, path): + r = nginxproxy.get("http://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) + assert r.status_code == 301 + assert "Location" in r.headers + assert "https://www.nginx-proxy.tld/%s/port" % path == r.headers['Location'] + +@pytest.mark.parametrize("path,port", [("web1", 81), ("web2", 82)]) +def test_web1_https_is_forwarded(docker_compose, nginxproxy, path, port): + r = nginxproxy.get("https://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) + assert r.status_code == 200 + assert "answer from port %d\n" % port in r.text + diff --git a/test/test_ssl/test_virtual_path.yml b/test/test_ssl/test_virtual_path.yml new file mode 100644 index 0000000..2260321 --- /dev/null +++ b/test/test_ssl/test_virtual_path.yml @@ -0,0 +1,26 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs:/etc/nginx/certs:ro + diff --git a/test/test_virtual-path/alternate.conf b/test/test_virtual-path/alternate.conf new file mode 100644 index 0000000..541332e --- /dev/null +++ b/test/test_virtual-path/alternate.conf @@ -0,0 +1 @@ +rewrite ^/(web3|alt)/(.*) /$2 break; diff --git a/test/test_virtual-path/bar.conf b/test/test_virtual-path/bar.conf new file mode 100644 index 0000000..e8b0827 --- /dev/null +++ b/test/test_virtual-path/bar.conf @@ -0,0 +1 @@ +add_header X-test bar; diff --git a/test/test_virtual-path/default.conf b/test/test_virtual-path/default.conf new file mode 100644 index 0000000..087e66c --- /dev/null +++ b/test/test_virtual-path/default.conf @@ -0,0 +1 @@ +add_header X-test-default true; diff --git a/test/test_virtual-path/foo.conf b/test/test_virtual-path/foo.conf new file mode 100644 index 0000000..8d8502d --- /dev/null +++ b/test/test_virtual-path/foo.conf @@ -0,0 +1 @@ +add_header X-test f00; \ No newline at end of file diff --git a/test/test_virtual-path/host.conf b/test/test_virtual-path/host.conf new file mode 100644 index 0000000..fe05265 --- /dev/null +++ b/test/test_virtual-path/host.conf @@ -0,0 +1 @@ +add_header X-test-host true; diff --git a/test/test_virtual-path/path.conf b/test/test_virtual-path/path.conf new file mode 100644 index 0000000..6c23b9a --- /dev/null +++ b/test/test_virtual-path/path.conf @@ -0,0 +1 @@ +add_header X-test-path true; diff --git a/test/test_virtual-path/test_custom_conf.py b/test/test_virtual-path/test_custom_conf.py new file mode 100644 index 0000000..eec149f --- /dev/null +++ b/test/test_virtual-path/test_custom_conf.py @@ -0,0 +1,38 @@ +import pytest + +def test_default_root_response(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.test/") + assert r.status_code == 418 + +@pytest.mark.parametrize("stub,header", [ + ("nginx-proxy.test/web1", "bar"), + ("foo.nginx-proxy.test", "f00"), +]) +def test_custom_applies(docker_compose, nginxproxy, stub, header): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert "X-test" in r.headers + assert header == r.headers["X-test"] + +@pytest.mark.parametrize("stub,code", [ + ("nginx-proxy.test/foo", 418), + ("nginx-proxy.test/web2", 200), + ("nginx-proxy.test/web3", 200), + ("bar.nginx-proxy.test", 503), +]) +def test_custom_does_not_apply(docker_compose, nginxproxy, stub, code): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == code + assert "X-test" not in r.headers + +@pytest.mark.parametrize("stub,port", [ + ("nginx-proxy.test/web1", 81), + ("nginx-proxy.test/web2", 82), + ("nginx-proxy.test/web3", 83), + ("nginx-proxy.test/alt", 83), +]) +def test_alternate(docker_compose, nginxproxy, stub, port): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert r.text == f"answer from port {port}\n" + diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom_conf.yml new file mode 100644 index 0000000..40ab512 --- /dev/null +++ b/test/test_virtual-path/test_custom_conf.yml @@ -0,0 +1,48 @@ + +foo: + image: web + expose: + - "42" + environment: + WEB_PORTS: "42" + VIRTUAL_HOST: "foo.nginx-proxy.test" + +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "~ ^/(web3|alt)/" + +sut: + image: nginxproxy/nginx-proxy:test + environment: + DEFAULT_ROOT: 418 + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro + - ./bar.conf:/etc/nginx/vhost.d/nginx-proxy.test_918d687a929083edd0c7224ee2293e0e7c062ab4_location:ro + - ./alternate.conf:/etc/nginx/vhost.d/nginx-proxy.test_7fb22b74bbdf907425dbbad18e4462565cada230_location:ro + diff --git a/test/test_virtual-path/test_forwarding.py b/test/test_virtual-path/test_forwarding.py new file mode 100644 index 0000000..062dd6c --- /dev/null +++ b/test/test_virtual-path/test_forwarding.py @@ -0,0 +1,18 @@ +import pytest + +def test_root_redirects_to_web1(docker_compose, nginxproxy): + r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=False) + assert r.status_code == 301 + assert "Location" in r.headers + assert "http://www.nginx-proxy.tld/web1/port" == r.headers['Location'] + +def test_direct_access(docker_compose, nginxproxy): + r = nginxproxy.get("http://www.nginx-proxy.tld/web1/port", allow_redirects=False) + assert r.status_code == 200 + assert "answer from port 81\n" in r.text + +def test_root_is_forwarded(docker_compose, nginxproxy): + r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=True) + assert r.status_code == 200 + assert "answer from port 81\n" in r.text + diff --git a/test/test_virtual-path/test_forwarding.yml b/test/test_virtual-path/test_forwarding.yml new file mode 100644 index 0000000..ee87e8d --- /dev/null +++ b/test/test_virtual-path/test_forwarding.yml @@ -0,0 +1,17 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs:/etc/nginx/certs:ro + environment: + - DEFAULT_ROOT=301 http://$$host/web1$$request_uri diff --git a/test/test_virtual-path/test_location_precedence.py b/test/test_virtual-path/test_location_precedence.py new file mode 100644 index 0000000..415c6c1 --- /dev/null +++ b/test/test_virtual-path/test_location_precedence.py @@ -0,0 +1,32 @@ +import pytest + +def test_location_precedence_case1(docker_compose, nginxproxy): + r = nginxproxy.get(f"http://foo.nginx-proxy.test/web1/port") + assert r.status_code == 200 + + assert "X-test-default" in r.headers + assert "X-test-host" not in r.headers + assert "X-test-path" not in r.headers + + assert r.headers["X-test-default"] == "true" + +def test_location_precedence_case2(docker_compose, nginxproxy): + r = nginxproxy.get(f"http://bar.nginx-proxy.test/web2/port") + assert r.status_code == 200 + + assert "X-test-default" not in r.headers + assert "X-test-host" in r.headers + assert "X-test-path" not in r.headers + + assert r.headers["X-test-host"] == "true" + +def test_location_precedence_case3(docker_compose, nginxproxy): + r = nginxproxy.get(f"http://bar.nginx-proxy.test/web3/port") + assert r.status_code == 200 + + assert "X-test-default" not in r.headers + assert "X-test-host" not in r.headers + assert "X-test-path" in r.headers + + assert r.headers["X-test-path"] == "true" + diff --git a/test/test_virtual-path/test_location_precedence.yml b/test/test_virtual-path/test_location_precedence.yml new file mode 100644 index 0000000..be3248c --- /dev/null +++ b/test/test_virtual-path/test_location_precedence.yml @@ -0,0 +1,37 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "foo.nginx-proxy.test" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "bar.nginx-proxy.test" + VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "bar.nginx-proxy.test" + VIRTUAL_PATH: "/web3/" + VIRTUAL_DEST: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./default.conf:/etc/nginx/vhost.d/default_location:ro + - ./host.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_location:ro + - ./path.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_99f2db0ed8aa95dbb5b87fca79c7eff2ff6bb8bd_location:ro diff --git a/test/test_virtual-path/test_virtual_paths.py b/test/test_virtual-path/test_virtual_paths.py new file mode 100644 index 0000000..115d47f --- /dev/null +++ b/test/test_virtual-path/test_virtual_paths.py @@ -0,0 +1,59 @@ +from time import sleep + +import pytest +from docker.errors import NotFound + +@pytest.mark.parametrize("stub,expected_port", [ + ("nginx-proxy.test/web1", 81), + ("nginx-proxy.test/web2", 82), + ("nginx-proxy.test", 83), + ("foo.nginx-proxy.test", 42), +]) +def test_valid_path(docker_compose, nginxproxy, stub, expected_port): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert r.text == f"answer from port {expected_port}\n" + +@pytest.mark.parametrize("stub", [ + "nginx-proxy.test/foo", + "bar.nginx-proxy.test", +]) +def test_invalid_path(docker_compose, nginxproxy, stub): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code in [404, 503] + +@pytest.fixture() +def web4(docker_compose): + """ + pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy.test`, `VIRTUAL_PATH=/web4/` and `VIRTUAL_DEST=/` listening on port 84. + """ + container = docker_compose.containers.run( + name="web4", + image="web", + detach=True, + environment={ + "WEB_PORTS": "84", + "VIRTUAL_HOST": "nginx-proxy.test", + "VIRTUAL_PATH": "/web4/", + "VIRTUAL_DEST": "/", + }, + ports={"84/tcp": None} + ) + sleep(2) # give it some time to initialize and for docker-gen to detect it + yield container + try: + docker_compose.containers.get("web4").remove(force=True) + except NotFound: + pass + +""" +Test if we can add and remove a single virtual_path from multiple ones on the same subdomain. +""" +def test_container_hotplug(web4, nginxproxy): + r = nginxproxy.get(f"http://nginx-proxy.test/web4/port") + assert r.status_code == 200 + assert r.text == f"answer from port 84\n" + web4.remove(force=True) + sleep(2) + r = nginxproxy.get(f"http://nginx-proxy.test/web4/port") + assert r.status_code == 404 diff --git a/test/test_virtual-path/test_virtual_paths.yml b/test/test_virtual-path/test_virtual_paths.yml new file mode 100644 index 0000000..9f6a54f --- /dev/null +++ b/test/test_virtual-path/test_virtual_paths.yml @@ -0,0 +1,42 @@ + +foo: + image: web + expose: + - "42" + environment: + WEB_PORTS: "42" + VIRTUAL_HOST: "foo.nginx-proxy.test" + +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web1/" + VIRTUAL_DEST: "/" + +web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/web2/" + VIRTUAL_DEST: "/" + +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "/" + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro From 727264995857bd959fa785d31255903cb61b997c Mon Sep 17 00:00:00 2001 From: Robin Windey Date: Fri, 7 Oct 2022 15:40:44 +0200 Subject: [PATCH 43/43] Fix tests after merge with upstream * Use "sub." instead of "www." because the latter will be redirected to non-www --- test/test_ssl/test_virtual_path.py | 6 +++--- test/test_ssl/test_virtual_path.yml | 4 ++-- test/test_virtual-path/test_forwarding.py | 8 ++++---- test/test_virtual-path/test_forwarding.yml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/test_ssl/test_virtual_path.py b/test/test_ssl/test_virtual_path.py index 508653f..8f9fb44 100644 --- a/test/test_ssl/test_virtual_path.py +++ b/test/test_ssl/test_virtual_path.py @@ -2,14 +2,14 @@ import pytest @pytest.mark.parametrize("path", ["web1", "web2"]) def test_web1_http_redirects_to_https(docker_compose, nginxproxy, path): - r = nginxproxy.get("http://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) + r = nginxproxy.get("http://sub.nginx-proxy.tld/%s/port" % path, allow_redirects=False) assert r.status_code == 301 assert "Location" in r.headers - assert "https://www.nginx-proxy.tld/%s/port" % path == r.headers['Location'] + assert "https://sub.nginx-proxy.tld/%s/port" % path == r.headers['Location'] @pytest.mark.parametrize("path,port", [("web1", 81), ("web2", 82)]) def test_web1_https_is_forwarded(docker_compose, nginxproxy, path, port): - r = nginxproxy.get("https://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) + r = nginxproxy.get("https://sub.nginx-proxy.tld/%s/port" % path, allow_redirects=False) assert r.status_code == 200 assert "answer from port %d\n" % port in r.text diff --git a/test/test_ssl/test_virtual_path.yml b/test/test_ssl/test_virtual_path.yml index 2260321..bfb32d3 100644 --- a/test/test_ssl/test_virtual_path.yml +++ b/test/test_ssl/test_virtual_path.yml @@ -4,7 +4,7 @@ web1: - "81" environment: WEB_PORTS: "81" - VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_HOST: "sub.nginx-proxy.tld" VIRTUAL_PATH: "/web1/" VIRTUAL_DEST: "/" @@ -14,7 +14,7 @@ web2: - "82" environment: WEB_PORTS: "82" - VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_HOST: "sub.nginx-proxy.tld" VIRTUAL_PATH: "/web2/" VIRTUAL_DEST: "/" diff --git a/test/test_virtual-path/test_forwarding.py b/test/test_virtual-path/test_forwarding.py index 062dd6c..310cfa9 100644 --- a/test/test_virtual-path/test_forwarding.py +++ b/test/test_virtual-path/test_forwarding.py @@ -1,18 +1,18 @@ import pytest def test_root_redirects_to_web1(docker_compose, nginxproxy): - r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=False) + r = nginxproxy.get("http://sub.nginx-proxy.tld/port", allow_redirects=False) assert r.status_code == 301 assert "Location" in r.headers - assert "http://www.nginx-proxy.tld/web1/port" == r.headers['Location'] + assert "http://sub.nginx-proxy.tld/web1/port" == r.headers['Location'] def test_direct_access(docker_compose, nginxproxy): - r = nginxproxy.get("http://www.nginx-proxy.tld/web1/port", allow_redirects=False) + r = nginxproxy.get("http://sub.nginx-proxy.tld/web1/port", allow_redirects=False) assert r.status_code == 200 assert "answer from port 81\n" in r.text def test_root_is_forwarded(docker_compose, nginxproxy): - r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=True) + r = nginxproxy.get("http://sub.nginx-proxy.tld/port", allow_redirects=True) assert r.status_code == 200 assert "answer from port 81\n" in r.text diff --git a/test/test_virtual-path/test_forwarding.yml b/test/test_virtual-path/test_forwarding.yml index ee87e8d..da3112a 100644 --- a/test/test_virtual-path/test_forwarding.yml +++ b/test/test_virtual-path/test_forwarding.yml @@ -4,7 +4,7 @@ web1: - "81" environment: WEB_PORTS: "81" - VIRTUAL_HOST: "www.nginx-proxy.tld" + VIRTUAL_HOST: "sub.nginx-proxy.tld" VIRTUAL_PATH: "/web1/" VIRTUAL_DEST: "/"