From 510d376f00f3c747c3ccd3221656499c6b390ebe Mon Sep 17 00:00:00 2001 From: SilverFire - Dmitry Naumenko Date: Wed, 11 May 2022 12:54:51 +0000 Subject: [PATCH 001/105] Make sure networks order is the same --- nginx.tmpl | 2 +- test/test_vhost-in-multiple-networks.py | 29 ++++++++++++++++++++++++ test/test_vhost-in-multiple-networks.yml | 26 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 test/test_vhost-in-multiple-networks.py create mode 100644 test/test_vhost-in-multiple-networks.yml diff --git a/nginx.tmpl b/nginx.tmpl index e8a555d..e96f0e2 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -105,7 +105,7 @@ upstream {{ .Upstream }} { {{ end }} {{ end }} {{ range $knownNetwork := $networks }} - {{ range $containerNetwork := $container.Networks }} + {{ range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} {{ 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 }} diff --git a/test/test_vhost-in-multiple-networks.py b/test/test_vhost-in-multiple-networks.py new file mode 100644 index 0000000..97a48fc --- /dev/null +++ b/test/test_vhost-in-multiple-networks.py @@ -0,0 +1,29 @@ +import pytest +import logging +import time + +def test_forwards_to_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" + +def test_nginx_config_remains_the_same_after_restart(docker_compose, nginxproxy): + """ + Restarts the Web container and returns nginx-proxy config after restart + """ + def get_conf_after_web_container_restart(): + web_containers = docker_compose.containers.list(filters={"ancestor": "web:latest"}) + assert len(web_containers) == 1 + web_containers[0].restart() + time.sleep(3) + + return nginxproxy.get_conf() + + config_before_restart = nginxproxy.get_conf() + + for i in range(1, 8): + logging.info(f"Checking for the {i}-st time that config is the same") + config_after_restart = get_conf_after_web_container_restart() + if config_before_restart != config_after_restart: + logging.debug(f"{config_before_restart!r} \n\n {config_after_restart!r}") + pytest.fail("nginx-proxy config before and after restart of a web container does not match", pytrace=False) diff --git a/test/test_vhost-in-multiple-networks.yml b/test/test_vhost-in-multiple-networks.yml new file mode 100644 index 0000000..bd01b7e --- /dev/null +++ b/test/test_vhost-in-multiple-networks.yml @@ -0,0 +1,26 @@ +version: '2' + +networks: + net1: {} + net2: {} + net3: {} + +services: + nginx-proxy: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + networks: + - net1 + + web: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: web1.nginx-proxy.local + networks: + - net1 + - net2 + - net3 From 9218caef710e47ebb1cfc21177528b5ad57ca258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Val=C3=A8re=20BRON?= Date: Tue, 23 Aug 2022 12:45:45 +0200 Subject: [PATCH 002/105] Simple mistake in DEFAULT_ROOT variable name `DEFAUL_ROOT` should be `DEFAULT_ROOT` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc8414b..fe1dc40 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ The filename of the previous example would be `example.tld_8610f6c344b4096614eab 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. +For example `DEFAULT_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: From 0fbd71362b7e4be0cffa9308c184428df975af50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 04:07:57 +0000 Subject: [PATCH 003/105] chore(deps): bump nginx from 1.21.6 to 1.23.2 Bumps nginx from 1.21.6 to 1.23.2. --- updated-dependencies: - dependency-name: nginx 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 dcd0285..eaf4f8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ && rm -rf /go/forego # Build the final image -FROM nginx:1.21.6 +FROM nginx:1.23.2 ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 51cafd9..55a7c6c 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -37,7 +37,7 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ && rm -rf /go/forego # Build the final image -FROM nginx:1.21.6-alpine +FROM nginx:1.23.2-alpine ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable From d23a746833f155be1e00f0a02d272182a594b46d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 04:21:21 +0000 Subject: [PATCH 004/105] chore(deps): bump pytest from 7.1.2 to 7.2.0 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.2 to 7.2.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.1.2...7.2.0) --- 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 7aa8731..5597fd2 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.2 +pytest==7.2.0 requests==2.27.1 From 302ecfff518bbb6cd8924e96618a1d9d92b6f3c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 12:00:48 +0000 Subject: [PATCH 005/105] chore(deps): bump requests from 2.27.1 to 2.28.1 in /test/requirements Bumps [requests](https://github.com/psf/requests) from 2.27.1 to 2.28.1. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.27.1...v2.28.1) --- updated-dependencies: - dependency-name: requests 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 5597fd2..ec5c918 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -2,4 +2,4 @@ backoff==1.11.1 docker-compose==1.29.2 docker==5.0.3 pytest==7.2.0 -requests==2.27.1 +requests==2.28.1 From b53e09373a5b0a7529b3db1194a462bc86d7460f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 13:02:13 +0000 Subject: [PATCH 006/105] chore(deps): bump backoff from 1.11.1 to 2.2.1 in /test/requirements Bumps [backoff](https://github.com/litl/backoff) from 1.11.1 to 2.2.1. - [Release notes](https://github.com/litl/backoff/releases) - [Changelog](https://github.com/litl/backoff/blob/master/CHANGELOG.md) - [Commits](https://github.com/litl/backoff/compare/v1.11.1...v2.2.1) --- updated-dependencies: - dependency-name: backoff dependency-type: direct:production update-type: version-update:semver-major ... 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 ec5c918..b9b5a8e 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,4 +1,4 @@ -backoff==1.11.1 +backoff==2.2.1 docker-compose==1.29.2 docker==5.0.3 pytest==7.2.0 From e5b340cb6f4b3f7dc259e008d0e770346422ad08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 14:30:16 +0000 Subject: [PATCH 007/105] chore(deps): bump docker from 5.0.3 to 6.0.1 in /test/requirements Bumps [docker](https://github.com/docker/docker-py) from 5.0.3 to 6.0.1. - [Release notes](https://github.com/docker/docker-py/releases) - [Commits](https://github.com/docker/docker-py/compare/5.0.3...6.0.1) --- updated-dependencies: - dependency-name: docker dependency-type: direct:production update-type: version-update:semver-major ... 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 b9b5a8e..c75a674 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==2.2.1 docker-compose==1.29.2 -docker==5.0.3 +docker==6.0.1 pytest==7.2.0 requests==2.28.1 From b4dd1a4ba881877260ab7fcc0f1da57eeca5861b Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 1 Dec 2022 23:22:02 +0100 Subject: [PATCH 008/105] build: dockergen 0.9.0 -> 0.9.1 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index eaf4f8d..9dff82f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.9.0 +ARG DOCKER_GEN_VERSION=0.9.1 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 55a7c6c..9f4c2ae 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.9.0 +ARG DOCKER_GEN_VERSION=0.9.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 050d9da7bd21881f8584db5485deac27f1786d96 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 1 Dec 2022 23:24:53 +0100 Subject: [PATCH 009/105] docs: nginx badge 1.21.6 -> 1.23.2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe1dc40..239a905 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.6](https://img.shields.io/badge/nginx-1.21.6-brightgreen.svg) +![nginx 1.23.2](https://img.shields.io/badge/nginx-1.23.2-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 75c7b1399bfe57707550adcafe1dae1f567aa80d Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 2 Dec 2022 00:21:56 +0100 Subject: [PATCH 010/105] build: golang 1.18.1 -> 1.18.8 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9dff82f..cf33847 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.18.1 as gobuilder +FROM golang:1.18.8 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 9f4c2ae..43a57fd 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.18.1-alpine as gobuilder +FROM golang:1.18.8-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 02d3a37cfbf55dfce154d35eb9859eb63ef46d21 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 3 Dec 2022 14:30:40 +0100 Subject: [PATCH 011/105] style: linting on CI yaml files --- .github/dependabot.yml | 1 - .github/workflows/dockerhub.yml | 32 +++++++++++++++----------------- .github/workflows/test.yml | 8 ++++---- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9056ae1..81ddf73 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,5 @@ version: 2 updates: - # Maintain dependencies for Docker - package-ecosystem: "docker" directory: "/" diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 706d298..72c769e 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -3,31 +3,30 @@ name: DockerHub on: workflow_dispatch: schedule: - - cron: '0 0 * * 1' + - cron: "0 0 * * 1" push: branches: - main tags: - - '*.*.*' + - "*.*.*" paths-ignore: - - 'test/*' - - '.gitignore' - - 'docker-compose-separate-containers.yml' - - 'docker-compose.yml' - - 'LICENSE' - - 'Makefile' - - '*.md' + - "test/*" + - ".gitignore" + - "docker-compose-separate-containers.yml" + - "docker-compose.yml" + - "LICENSE" + - "Makefile" + - "*.md" jobs: multiarch-build-debian: runs-on: ubuntu-latest steps: - - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - + - name: Retrieve version run: echo "GIT_DESCRIBE=$(git describe --tags)" >> $GITHUB_ENV @@ -54,11 +53,11 @@ jobs: uses: docker/setup-buildx-action@v1 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - + - name: Log in to GitHub Container Registry uses: docker/login-action@v1 with: @@ -84,12 +83,11 @@ jobs: multiarch-build-alpine: runs-on: ubuntu-latest steps: - - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - + - name: Retrieve version run: echo "GIT_DESCRIBE=$(git describe --tags)" >> $GITHUB_ENV @@ -117,11 +115,11 @@ jobs: uses: docker/setup-buildx-action@v1 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - + - name: Log in to GitHub Container Registry uses: docker/login-action@v1 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6be93bd..3f088d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,12 +4,12 @@ on: workflow_dispatch: push: paths-ignore: - - 'LICENSE' - - '**.md' + - "LICENSE" + - "**.md" pull_request: paths-ignore: - - 'LICENSE' - - '**.md' + - "LICENSE" + - "**.md" jobs: unit: From 9f9e5b8cd41da7b86fba0295e3d98c4f9861df20 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 3 Dec 2022 14:34:15 +0100 Subject: [PATCH 012/105] ci: update Actions versions --- .github/workflows/dockerhub.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 72c769e..e628b2f 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -32,7 +32,7 @@ jobs: - name: Get Docker tags for Debian based image id: docker_meta_debian - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/nginx-proxy/nginx-proxy @@ -41,25 +41,25 @@ jobs: tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=latest,enable={{is_default_branch}} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Log in to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} @@ -67,7 +67,7 @@ jobs: - name: Build and push the Debian based image id: docker_build_debian - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: Dockerfile @@ -93,7 +93,7 @@ jobs: - name: Get Docker tags for Alpine based image id: docker_meta_alpine - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/nginx-proxy/nginx-proxy @@ -102,26 +102,26 @@ jobs: tags: | 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=alpine,enable={{is_default_branch}} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} flavor: latest=false - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Log in to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} @@ -129,7 +129,7 @@ jobs: - name: Build and push the Alpine based image id: docker_build_alpine - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: Dockerfile.alpine From 9c2b2cec38d82e60e6abdd56431d8d435a7b3daa Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sat, 3 Dec 2022 19:36:31 +0100 Subject: [PATCH 013/105] ci: use actions/checkout@v3 --- .github/workflows/dockerhub.yml | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index e628b2f..8b33fb2 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 @@ -84,7 +84,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3f088d9..3dd2674 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: base_docker_image: [alpine, debian] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.9 uses: actions/setup-python@v2 From f1fb85865dd36f5620b3383c625ab55e4488d6ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Dec 2022 04:02:52 +0000 Subject: [PATCH 014/105] chore(deps): bump golang from 1.18.8 to 1.19.4 Bumps golang from 1.18.8 to 1.19.4. --- 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 cf33847..e599c8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.18.8 as gobuilder +FROM golang:1.19.4 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 43a57fd..ecdfe2c 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.18.8-alpine as gobuilder +FROM golang:1.19.4-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 1aadd9ba8c691282468a077057404c042bc1d3e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 04:02:52 +0000 Subject: [PATCH 015/105] chore(deps): bump nginx from 1.23.2 to 1.23.3 Bumps nginx from 1.23.2 to 1.23.3. --- updated-dependencies: - dependency-name: nginx 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 cf33847..9b04e48 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ && rm -rf /go/forego # Build the final image -FROM nginx:1.23.2 +FROM nginx:1.23.3 ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 43a57fd..5aec1d6 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -37,7 +37,7 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ && rm -rf /go/forego # Build the final image -FROM nginx:1.23.2-alpine +FROM nginx:1.23.3-alpine ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable From 9cb21132a461461c2a60d4293042edc6b8a54f77 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 16 Mar 2022 01:10:56 -0400 Subject: [PATCH 016/105] docs: Sync README.md with default proxy.conf settings --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 239a905..1b1b529 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ You can also use wildcards at the beginning and the end of host name, like `*.ba 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. +The full request URI will be forwarded to the serving container in the `X-Original-URI` 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. @@ -381,7 +381,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; +proxy_set_header X-Original-URI $request_uri; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; From 5f15f045564da8d91332a4e9e3cdccce2be171d5 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 26 Mar 2022 21:10:16 -0400 Subject: [PATCH 017/105] docs: Document the request headers sent to the backend server --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b1b529..564f72e 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,19 @@ docker run -d -p 80:80 -p 443:443 \ You'll need apache2-utils on the machine where you plan to create the htpasswd file. Follow these [instructions](http://httpd.apache.org/docs/2.2/programs/htpasswd.html) +### Headers + +By default, `nginx-proxy` forwards all incoming request headers from the client to the backend server unmodified, with the following exceptions: + + * `Connection`: Set to `upgrade` if the client sets the `Upgrade` header, otherwise set to `close`. (Keep-alive between `nginx-proxy` and the backend server is not supported.) + * `Proxy`: Always removed if present. This prevents attackers from using the so-called [httpoxy attack](http://httpoxy.org). There is no legitimate reason for a client to send this header, and there are many vulnerable languages / platforms (`CVE-2016-5385`, `CVE-2016-5386`, `CVE-2016-5387`, `CVE-2016-5388`, `CVE-2016-1000109`, `CVE-2016-1000110`, `CERT-VU#797896`). + * `X-Real-IP`: Set to the client's IP address. + * `X-Forwarded-For`: The client's IP address is appended to the value provided by the client. (If the client did not provide this header, it is set to the client's IP address.) + * `X-Forwarded-Proto`: If the client did not provide this header, this is set to `http` for plain HTTP connections and `https` for TLS connections. Otherwise, the header is forwarded to the backend server unmodified. + * `X-Forwarded-Ssl`: Set to `on` if the `X-Forwarded-Proto` header sent to the backend server is `https`, otherwise set to `off`. + * `X-Forwarded-Port`: If the client did not provide this header, this is set to the port of the server that accepted the client's request. Otherwise, the header is forwarded to the backend server unmodified. + * `X-Original-URI`: Set to the original request URI. + ### Custom Nginx Configuration If you need to configure Nginx beyond what is possible using environment variables, you can provide custom configuration files on either a proxy-wide or per-`VIRTUAL_HOST` basis. @@ -389,8 +402,6 @@ proxy_set_header Proxy ""; ***NOTE***: If you provide this file it will replace the defaults; you may want to check the .tmpl file to make sure you have all of the needed options. -***NOTE***: The default configuration blocks the `Proxy` HTTP request header from being sent to downstream servers. This prevents attackers from using the so-called [httpoxy attack](http://httpoxy.org). There is no legitimate reason for a client to send this header, and there are many vulnerable languages / platforms (`CVE-2016-5385`, `CVE-2016-5386`, `CVE-2016-5387`, `CVE-2016-5388`, `CVE-2016-1000109`, `CVE-2016-1000110`, `CERT-VU#797896`). - #### Proxy-wide To add settings on a proxy-wide basis, add your configuration file under `/etc/nginx/conf.d` using a name ending in `.conf`. From 8aa00fcea2588eb39565b030c706068a21f9ee33 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 16 Mar 2022 00:59:03 -0400 Subject: [PATCH 018/105] feat: Option to not trust `X-Forwarded-*` headers from clients If header values from a malicious client are passed to the backend server unchecked and unchanged, the client may be able to subvert security checks done by the backend server. --- README.md | 13 +++- app/docker-entrypoint.sh | 7 ++ nginx.tmpl | 5 +- .../certs/web.nginx-proxy.tld.crt | 70 +++++++++++++++++++ .../certs/web.nginx-proxy.tld.key | 27 +++++++ .../test_default.py | 20 ++++++ .../test_default.yml | 16 +++++ .../test_disabled.py | 20 ++++++ .../test_disabled.yml | 18 +++++ .../test_enabled.py | 20 ++++++ .../test_enabled.yml | 18 +++++ 11 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.crt create mode 100644 test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.key create mode 100644 test/test_trust-downstream-proxy/test_default.py create mode 100644 test/test_trust-downstream-proxy/test_default.yml create mode 100644 test/test_trust-downstream-proxy/test_disabled.py create mode 100644 test/test_trust-downstream-proxy/test_disabled.yml create mode 100644 test/test_trust-downstream-proxy/test_enabled.py create mode 100644 test/test_trust-downstream-proxy/test_enabled.yml diff --git a/README.md b/README.md index 564f72e..367be6b 100644 --- a/README.md +++ b/README.md @@ -369,11 +369,20 @@ By default, `nginx-proxy` forwards all incoming request headers from the client * `Proxy`: Always removed if present. This prevents attackers from using the so-called [httpoxy attack](http://httpoxy.org). There is no legitimate reason for a client to send this header, and there are many vulnerable languages / platforms (`CVE-2016-5385`, `CVE-2016-5386`, `CVE-2016-5387`, `CVE-2016-5388`, `CVE-2016-1000109`, `CVE-2016-1000110`, `CERT-VU#797896`). * `X-Real-IP`: Set to the client's IP address. * `X-Forwarded-For`: The client's IP address is appended to the value provided by the client. (If the client did not provide this header, it is set to the client's IP address.) - * `X-Forwarded-Proto`: If the client did not provide this header, this is set to `http` for plain HTTP connections and `https` for TLS connections. Otherwise, the header is forwarded to the backend server unmodified. + * `X-Forwarded-Proto`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to `http` for plain HTTP connections and `https` for TLS connections. Otherwise, the header is forwarded to the backend server unmodified. * `X-Forwarded-Ssl`: Set to `on` if the `X-Forwarded-Proto` header sent to the backend server is `https`, otherwise set to `off`. - * `X-Forwarded-Port`: If the client did not provide this header, this is set to the port of the server that accepted the client's request. Otherwise, the header is forwarded to the backend server unmodified. + * `X-Forwarded-Port`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to the port of the server that accepted the client's request. Otherwise, the header is forwarded to the backend server unmodified. * `X-Original-URI`: Set to the original request URI. +#### Trusting Downstream Proxy Headers + +For legacy compatibility reasons, `nginx-proxy` forwards any client-supplied `X-Forwarded-Proto` (which affects the value of `X-Forwarded-Ssl`) and `X-Forwarded-Port` headers unchecked and unmodified. To prevent malicious clients from spoofing the protocol or port that is perceived by your backend server, you are encouraged to set the `TRUST_DOWNSTREAM_PROXY` value to `false` if: + + * you do not operate a second reverse proxy downstream of `nginx-proxy`, or + * you do operate a second reverse proxy downstream of `nginx-proxy` but that proxy forwards those headers unchecked from untrusted clients. + +The default for `TRUST_DOWNSTREAM_PROXY` may change to `false` in a future version of `nginx-proxy`. If you require it to be enabled, you are encouraged to explicitly set it to `true` to avoid compatibility problems when upgrading. + ### Custom Nginx Configuration If you need to configure Nginx beyond what is possible using environment variables, you can provide custom configuration files on either a proxy-wide or per-`VIRTUAL_HOST` basis. diff --git a/app/docker-entrypoint.sh b/app/docker-entrypoint.sh index 8f4ed7a..0477dd2 100755 --- a/app/docker-entrypoint.sh +++ b/app/docker-entrypoint.sh @@ -109,6 +109,13 @@ if [[ $* == 'forego start -r' ]]; then _resolvers _setup_dhparam + + if [ -z "${TRUST_DOWNSTREAM_PROXY}" ]; then + cat >&2 <<-EOT + Warning: TRUST_DOWNSTREAM_PROXY is not set; defaulting to "true". For security, you should explicitly set TRUST_DOWNSTREAM_PROXY to "false" if there is not a trusted reverse proxy in front of this proxy. + Warning: The default value of TRUST_DOWNSTREAM_PROXY might change to "false" in a future version of nginx-proxy. If you require TRUST_DOWNSTREAM_PROXY to be enabled, explicitly set it to "true". + EOT + fi fi exec "$@" diff --git a/nginx.tmpl b/nginx.tmpl index e8a555d..295af0c 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -6,6 +6,7 @@ {{ $debug_all := $.Env.DEBUG }} {{ $sha1_upstream_name := parseBool (coalesce $.Env.SHA1_UPSTREAM_NAME "false") }} {{ $default_root_response := coalesce $.Env.DEFAULT_ROOT "404" }} +{{ $trust_downstream_proxy := parseBool (coalesce $.Env.TRUST_DOWNSTREAM_PROXY "true") }} {{ define "ssl_policy" }} {{ if eq .ssl_policy "Mozilla-Modern" }} @@ -150,14 +151,14 @@ upstream {{ .Upstream }} { # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { - default $http_x_forwarded_proto; + default {{ if $trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; '' $scheme; } # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the # server port the client connected to map $http_x_forwarded_port $proxy_x_forwarded_port { - default $http_x_forwarded_port; + default {{ if $trust_downstream_proxy }}$http_x_forwarded_port{{ else }}$server_port{{ end }}; '' $server_port; } diff --git a/test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.crt b/test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.crt new file mode 100644 index 0000000..aed9349 --- /dev/null +++ b/test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Jan 13 03:06:39 2017 GMT + Not After : May 31 03:06:39 2044 GMT + Subject: CN=web.nginx-proxy.tld + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:95:56:c7:0d:48:a5:2b:3c:65:49:3f:26:e1:38: + 2b:61:30:56:e4:92:d7:63:e0:eb:ad:ac:f9:33:9b: + b2:31:f1:39:13:0b:e5:43:7b:c5:bd:8a:85:c8:d9: + 3d:d8:ac:71:ba:16:e7:81:96:b2:ab:ae:c6:c0:bd: + be:a7:d1:96:8f:b2:9b:df:ba:f9:4d:a1:3b:7e:21: + 4a:cd:b6:45:f9:6d:79:50:bf:24:8f:c1:6b:c1:09: + 19:5b:62:cb:96:e8:04:14:20:e8:d4:16:62:6a:f2: + 37:c1:96:e2:9d:53:05:0b:52:1d:e7:68:92:db:8b: + 36:68:cd:8d:5b:02:ff:12:f0:ac:5d:0c:c4:e0:7a: + 55:a2:49:60:9f:ff:47:1f:52:73:55:4d:d4:f2:d1: + 62:a2:f4:50:9d:c9:f6:f1:43:b3:dc:57:e1:31:76: + b4:e0:a4:69:7e:f2:6d:34:ae:b9:8d:74:26:7b:d9: + f6:07:00:ef:4b:36:61:b3:ef:7a:a1:36:3a:b6:d0: + 9e:f8:b8:a9:0d:4c:30:a2:ed:eb:ab:6b:eb:2e:e2: + 0b:28:be:f7:04:b1:e9:e0:84:d6:5d:31:77:7c:dc: + d2:1f:d4:1d:71:6f:6f:6c:6d:1b:bf:31:e2:5b:c3: + 52:d0:14:fc:8b:fb:45:ea:41:ec:ca:c7:3b:67:12: + c4:df + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:web.nginx-proxy.tld + Signature Algorithm: sha256WithRSAEncryption + 4e:48:7d:81:66:ba:2f:50:3d:24:42:61:3f:1f:de:cf:ec:1b: + 1b:bd:0a:67:b6:62:c8:79:9d:31:a0:fd:a9:61:ce:ff:69:bf: + 0e:f4:f7:e6:15:2b:b0:f0:e4:f2:f4:d2:8f:74:02:b1:1e:4a: + a8:6f:26:0a:77:32:29:cf:dc:b5:61:82:3e:58:47:61:92:f0: + 0c:20:25:f8:41:4d:34:09:44:bc:39:9e:aa:82:06:83:13:8b: + 1e:2c:3d:cf:cd:1a:f7:77:39:38:e0:a3:a7:f3:09:da:02:8d: + 73:75:38:b4:dd:24:a7:f9:03:db:98:c6:88:54:87:dc:e0:65: + 4c:95:c5:39:9c:00:30:dc:f0:d3:2c:19:ca:f1:f4:6c:c6:d9: + b5:c4:4a:c7:bc:a1:2e:88:7b:b5:33:d0:ff:fb:48:5e:3e:29: + fa:58:e5:03:de:d8:17:de:ed:96:fc:7e:1f:fe:98:f6:be:99: + 38:87:51:c0:d3:b7:9a:0f:26:92:e5:53:1b:d6:25:4c:ac:48: + f3:29:fc:74:64:9d:07:6a:25:57:24:aa:a7:70:fa:8f:6c:a7: + 2b:b7:9d:81:46:10:32:93:b9:45:6d:0f:16:18:b2:21:1f:f3: + 30:24:62:3f:e1:6c:07:1d:71:28:cb:4c:bb:f5:39:05:f9:b2: + 5b:a0:05:1b +-----BEGIN CERTIFICATE----- +MIIC+zCCAeOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAeFw0xNzAxMTMwMzA2MzlaFw00NDA1MzEwMzA2MzlaMB4xHDAaBgNVBAMME3dl +Yi5uZ2lueC1wcm94eS50bGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCVVscNSKUrPGVJPybhOCthMFbkktdj4OutrPkzm7Ix8TkTC+VDe8W9ioXI2T3Y +rHG6FueBlrKrrsbAvb6n0ZaPspvfuvlNoTt+IUrNtkX5bXlQvySPwWvBCRlbYsuW +6AQUIOjUFmJq8jfBluKdUwULUh3naJLbizZozY1bAv8S8KxdDMTgelWiSWCf/0cf +UnNVTdTy0WKi9FCdyfbxQ7PcV+ExdrTgpGl+8m00rrmNdCZ72fYHAO9LNmGz73qh +Njq20J74uKkNTDCi7eura+su4gsovvcEsenghNZdMXd83NIf1B1xb29sbRu/MeJb +w1LQFPyL+0XqQezKxztnEsTfAgMBAAGjIjAgMB4GA1UdEQQXMBWCE3dlYi5uZ2lu +eC1wcm94eS50bGQwDQYJKoZIhvcNAQELBQADggEBAE5IfYFmui9QPSRCYT8f3s/s +Gxu9Cme2Ysh5nTGg/alhzv9pvw709+YVK7Dw5PL00o90ArEeSqhvJgp3MinP3LVh +gj5YR2GS8AwgJfhBTTQJRLw5nqqCBoMTix4sPc/NGvd3OTjgo6fzCdoCjXN1OLTd +JKf5A9uYxohUh9zgZUyVxTmcADDc8NMsGcrx9GzG2bXESse8oS6Ie7Uz0P/7SF4+ +KfpY5QPe2Bfe7Zb8fh/+mPa+mTiHUcDTt5oPJpLlUxvWJUysSPMp/HRknQdqJVck +qqdw+o9spyu3nYFGEDKTuUVtDxYYsiEf8zAkYj/hbAcdcSjLTLv1OQX5slugBRs= +-----END CERTIFICATE----- diff --git a/test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.key b/test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.key new file mode 100644 index 0000000..8365ecf --- /dev/null +++ b/test/test_trust-downstream-proxy/certs/web.nginx-proxy.tld.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAlVbHDUilKzxlST8m4TgrYTBW5JLXY+Drraz5M5uyMfE5Ewvl +Q3vFvYqFyNk92KxxuhbngZayq67GwL2+p9GWj7Kb37r5TaE7fiFKzbZF+W15UL8k +j8FrwQkZW2LLlugEFCDo1BZiavI3wZbinVMFC1Id52iS24s2aM2NWwL/EvCsXQzE +4HpVoklgn/9HH1JzVU3U8tFiovRQncn28UOz3FfhMXa04KRpfvJtNK65jXQme9n2 +BwDvSzZhs+96oTY6ttCe+LipDUwwou3rq2vrLuILKL73BLHp4ITWXTF3fNzSH9Qd +cW9vbG0bvzHiW8NS0BT8i/tF6kHsysc7ZxLE3wIDAQABAoIBAEmK7IecKMq7+V0y +3mC3GpXICmKR9cRX9XgX4LkLiZuSoXrBtuuevmhzGSMp6I0VjwQHV4a3wdFORs6Q +Ip3eVvj5Ck4Jc9BJAFVC6+WWR6tnwACFwOmSZRAw/O3GH2B3bdrDwiT/yQPFuLN7 +LKoxQiCrFdLp6rh3PBosb9pMBXU7k/HUazIdgmSKg6/JIoo/4Gwyid04TF/4MI2l +RscxtP5/ANtS8VgwBEqhgdafRJ4KnLEpgvswgIQvUKmduVhZQlzd0LMY8FbhKVqz +Utg8gsXaTyH6df/nmgUIInxLMz/MKPnMkv99fS6Sp/hvYlGpLZFWBJ6unMq3lKEr +LMbHfIECgYEAxB+5QWdVqG2r9loJlf8eeuNeMPml4P8Jmi5RKyJC7Cww6DMlMxOS +78ZJfl4b3ZrWuyvhjOfX/aTq7kQaF1BI9o3KJBH8k6EtO4gI8KeNmDONyQk9zsrn +ru8Zwr7hVbAo8fCXxCnmPzhDLsYg6f3BVOsQWoX2SFYKZ1GvkPfIReECgYEAwu6G +qtgFb57Vim10ecfWGM6vrPxvyfqP+zlH/p4nR+aQ+2sFbt27D0B1byWBRZe4KQyw +Vq6XiQ09Fk6MJr8E8iAr9GXPPHcqlYI6bbNc6YOP3jVSKut0tQdTUOHll4kYIY+h +RS3VA3+BA//ADpWpywu+7RZRbaIECA+U2a224r8CgYB5PCMIixgoRaNHZeEHF+1/ +iY1wOOKRcxY8eOU0BLnZxHd3EiasrCzoi2pi80nGczDKAxYqRCcAZDHVl8OJJdf0 +kTGjmnrHx5pucmkUWn7s1vGOlGfgrQ0K1kLWX6hrj7m/1Tn7yOrLqbvd7hvqiTI5 +jBVP3/+eN5G2zIf61TC4AQKBgCX2Q92jojNhsF58AHHy+/vqzIWYx8CC/mVDe4TX +kfjLqzJ7XhyAK/zFZdlWaX1/FYtRAEpxR+uV226rr1mgW7s3jrfS1/ADmRRyvyQ8 +CP0k9PCmW7EmF51lptEanRbMyRlIGnUZfuFmhF6eAO4WMXHsgKs1bHg4VCapuihG +T1aLAoGACRGn1UxFuBGqtsh2zhhsBZE7GvXKJSk/eP7QJeEXUNpNjCpgm8kIZM5K +GorpL7PSB8mwVlDl18TpMm3P7nz6YkJYte+HdjO7pg59H39Uvtg3tZnIrFxNxVNb +YF62/yHfk2AyTgjQZQUSmDS84jq1zUK4oS90lxr+u8qwELTniMs= +-----END RSA PRIVATE KEY----- diff --git a/test/test_trust-downstream-proxy/test_default.py b/test/test_trust-downstream-proxy/test_default.py new file mode 100644 index 0000000..456d07a --- /dev/null +++ b/test/test_trust-downstream-proxy/test_default.py @@ -0,0 +1,20 @@ +import pytest +import re + + +@pytest.mark.parametrize('url,header,input,want', [ + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'http'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'), + + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'), +]) +def test_downstream_proxy_header(docker_compose, nginxproxy, url, header, input, want): + kwargs = {} if input is None else {'headers': {header: input}} + r = nginxproxy.get(url, **kwargs) + assert r.status_code == 200 + assert re.search(fr'(?m)^(?i:{re.escape(header)}): {re.escape(want)}$', r.text) diff --git a/test/test_trust-downstream-proxy/test_default.yml b/test/test_trust-downstream-proxy/test_default.yml new file mode 100644 index 0000000..c420d80 --- /dev/null +++ b/test/test_trust-downstream-proxy/test_default.yml @@ -0,0 +1,16 @@ +web: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: web.nginx-proxy.tld + HTTPS_METHOD: noredirect + + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs/web.nginx-proxy.tld.crt:/etc/nginx/certs/web.nginx-proxy.tld.crt:ro + - ./certs/web.nginx-proxy.tld.key:/etc/nginx/certs/web.nginx-proxy.tld.key:ro diff --git a/test/test_trust-downstream-proxy/test_disabled.py b/test/test_trust-downstream-proxy/test_disabled.py new file mode 100644 index 0000000..bc9684f --- /dev/null +++ b/test/test_trust-downstream-proxy/test_disabled.py @@ -0,0 +1,20 @@ +import pytest +import re + + +@pytest.mark.parametrize('url,header,input,want', [ + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'http'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'http'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'https'), + + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '80'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '443'), +]) +def test_downstream_proxy_header(docker_compose, nginxproxy, url, header, input, want): + kwargs = {} if input is None else {'headers': {header: input}} + r = nginxproxy.get(url, **kwargs) + assert r.status_code == 200 + assert re.search(fr'(?m)^(?i:{re.escape(header)}): {re.escape(want)}$', r.text) diff --git a/test/test_trust-downstream-proxy/test_disabled.yml b/test/test_trust-downstream-proxy/test_disabled.yml new file mode 100644 index 0000000..860e5f9 --- /dev/null +++ b/test/test_trust-downstream-proxy/test_disabled.yml @@ -0,0 +1,18 @@ +web: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: web.nginx-proxy.tld + HTTPS_METHOD: noredirect + + +sut: + image: nginxproxy/nginx-proxy:test + environment: + TRUST_DOWNSTREAM_PROXY: "false" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs/web.nginx-proxy.tld.crt:/etc/nginx/certs/web.nginx-proxy.tld.crt:ro + - ./certs/web.nginx-proxy.tld.key:/etc/nginx/certs/web.nginx-proxy.tld.key:ro diff --git a/test/test_trust-downstream-proxy/test_enabled.py b/test/test_trust-downstream-proxy/test_enabled.py new file mode 100644 index 0000000..456d07a --- /dev/null +++ b/test/test_trust-downstream-proxy/test_enabled.py @@ -0,0 +1,20 @@ +import pytest +import re + + +@pytest.mark.parametrize('url,header,input,want', [ + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'http'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'), + + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'), +]) +def test_downstream_proxy_header(docker_compose, nginxproxy, url, header, input, want): + kwargs = {} if input is None else {'headers': {header: input}} + r = nginxproxy.get(url, **kwargs) + assert r.status_code == 200 + assert re.search(fr'(?m)^(?i:{re.escape(header)}): {re.escape(want)}$', r.text) diff --git a/test/test_trust-downstream-proxy/test_enabled.yml b/test/test_trust-downstream-proxy/test_enabled.yml new file mode 100644 index 0000000..7b2a8de --- /dev/null +++ b/test/test_trust-downstream-proxy/test_enabled.yml @@ -0,0 +1,18 @@ +web: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: web.nginx-proxy.tld + HTTPS_METHOD: noredirect + + +sut: + image: nginxproxy/nginx-proxy:test + environment: + TRUST_DOWNSTREAM_PROXY: "true" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./certs/web.nginx-proxy.tld.crt:/etc/nginx/certs/web.nginx-proxy.tld.crt:ro + - ./certs/web.nginx-proxy.tld.key:/etc/nginx/certs/web.nginx-proxy.tld.key:ro From ee0d68c34e8817ce31da7315505fd885d5e47354 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 23 Dec 2022 19:42:48 +0100 Subject: [PATCH 019/105] docs: nginx badge 1.23.2 -> 1.23.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 239a905..e79331d 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.23.2](https://img.shields.io/badge/nginx-1.23.2-brightgreen.svg) +![nginx 1.23.3](https://img.shields.io/badge/nginx-1.23.3-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 ba8f5a4eb84904251617d77b8d618b1513756fdc Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Fri, 23 Dec 2022 19:45:04 +0100 Subject: [PATCH 020/105] build: dockergen 0.9.1 -> 0.9.2 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a2332ce..1319579 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.9.1 +ARG DOCKER_GEN_VERSION=0.9.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 cb8597a..4e70c11 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.9.1 +ARG DOCKER_GEN_VERSION=0.9.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From af877cf784829ab22eb43aa9f77083baba3eb755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=8A=E9=85=92?= Date: Fri, 10 Dec 2021 13:08:18 +0800 Subject: [PATCH 021/105] feat: Add proxy header `X-Forwarded-Host` Co-authored-by: Richard Hansen --- README.md | 4 +++- nginx.tmpl | 6 ++++++ test/test_headers/test_http.py | 13 +++++++++++++ test/test_headers/test_https.py | 13 +++++++++++++ test/test_trust-downstream-proxy/test_default.py | 5 +++++ test/test_trust-downstream-proxy/test_disabled.py | 5 +++++ test/test_trust-downstream-proxy/test_enabled.py | 5 +++++ 7 files changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b6229e..0ca08d7 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,7 @@ By default, `nginx-proxy` forwards all incoming request headers from the client * `Proxy`: Always removed if present. This prevents attackers from using the so-called [httpoxy attack](http://httpoxy.org). There is no legitimate reason for a client to send this header, and there are many vulnerable languages / platforms (`CVE-2016-5385`, `CVE-2016-5386`, `CVE-2016-5387`, `CVE-2016-5388`, `CVE-2016-1000109`, `CVE-2016-1000110`, `CERT-VU#797896`). * `X-Real-IP`: Set to the client's IP address. * `X-Forwarded-For`: The client's IP address is appended to the value provided by the client. (If the client did not provide this header, it is set to the client's IP address.) + * `X-Forwarded-Host`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to the value of the `Host` header provided by the client. Otherwise, the header is forwarded to the backend server unmodified. * `X-Forwarded-Proto`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to `http` for plain HTTP connections and `https` for TLS connections. Otherwise, the header is forwarded to the backend server unmodified. * `X-Forwarded-Ssl`: Set to `on` if the `X-Forwarded-Proto` header sent to the backend server is `https`, otherwise set to `off`. * `X-Forwarded-Port`: If the client did not provide this header or if the `TRUST_DOWNSTREAM_PROXY` environment variable is set to `false` (see below), this is set to the port of the server that accepted the client's request. Otherwise, the header is forwarded to the backend server unmodified. @@ -376,7 +377,7 @@ By default, `nginx-proxy` forwards all incoming request headers from the client #### Trusting Downstream Proxy Headers -For legacy compatibility reasons, `nginx-proxy` forwards any client-supplied `X-Forwarded-Proto` (which affects the value of `X-Forwarded-Ssl`) and `X-Forwarded-Port` headers unchecked and unmodified. To prevent malicious clients from spoofing the protocol or port that is perceived by your backend server, you are encouraged to set the `TRUST_DOWNSTREAM_PROXY` value to `false` if: +For legacy compatibility reasons, `nginx-proxy` forwards any client-supplied `X-Forwarded-Proto` (which affects the value of `X-Forwarded-Ssl`), `X-Forwarded-Host`, and `X-Forwarded-Port` headers unchecked and unmodified. To prevent malicious clients from spoofing the protocol, hostname, or port that is perceived by your backend server, you are encouraged to set the `TRUST_DOWNSTREAM_PROXY` value to `false` if: * you do not operate a second reverse proxy downstream of `nginx-proxy`, or * you do operate a second reverse proxy downstream of `nginx-proxy` but that proxy forwards those headers unchecked from untrusted clients. @@ -400,6 +401,7 @@ proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $proxy_connection; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host; 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; diff --git a/nginx.tmpl b/nginx.tmpl index eaf4121..e7d77c9 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -155,6 +155,11 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto { '' $scheme; } +map $http_x_forwarded_host $proxy_x_forwarded_host { + default {{ if $trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }}; + '' $http_host; +} + # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the # server port the client connected to map $http_x_forwarded_port $proxy_x_forwarded_port { @@ -212,6 +217,7 @@ proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $proxy_connection; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host; 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; diff --git a/test/test_headers/test_http.py b/test/test_headers/test_http.py index 5983a10..739b455 100644 --- a/test/test_headers/test_http.py +++ b/test/test_headers/test_http.py @@ -30,6 +30,19 @@ def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy): assert "X-Forwarded-Proto: f00\n" in r.text +##### Testing the handling of X-Forwarded-Host ##### + +def test_X_Forwarded_Host_is_generated(docker_compose, nginxproxy): + r = nginxproxy.get("http://web.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text + +def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy): + r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'}) + assert r.status_code == 200 + assert "X-Forwarded-Host: example.com\n" in r.text + + ##### Testing the handling of X-Forwarded-Port ##### def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy): diff --git a/test/test_headers/test_https.py b/test/test_headers/test_https.py index c5457c4..7a428ae 100644 --- a/test/test_headers/test_https.py +++ b/test/test_headers/test_https.py @@ -33,6 +33,19 @@ def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy): assert "X-Forwarded-Proto: f00\n" in r.text +##### Testing the handling of X-Forwarded-Host ##### + +def test_X_Forwarded_Host_is_generated(docker_compose, nginxproxy): + r = nginxproxy.get("https://web.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text + +def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy): + r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'}) + assert r.status_code == 200 + assert "X-Forwarded-Host: example.com\n" in r.text + + ##### Testing the handling of X-Forwarded-Port ##### def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy): diff --git a/test/test_trust-downstream-proxy/test_default.py b/test/test_trust-downstream-proxy/test_default.py index 456d07a..f56c406 100644 --- a/test/test_trust-downstream-proxy/test_default.py +++ b/test/test_trust-downstream-proxy/test_default.py @@ -8,6 +8,11 @@ import re ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'), ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'), ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'), ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'), diff --git a/test/test_trust-downstream-proxy/test_disabled.py b/test/test_trust-downstream-proxy/test_disabled.py index bc9684f..88c8054 100644 --- a/test/test_trust-downstream-proxy/test_disabled.py +++ b/test/test_trust-downstream-proxy/test_disabled.py @@ -8,6 +8,11 @@ import re ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'), ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'https'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'web.nginx-proxy.tld'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'web.nginx-proxy.tld'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'), ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '80'), ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'), diff --git a/test/test_trust-downstream-proxy/test_enabled.py b/test/test_trust-downstream-proxy/test_enabled.py index 456d07a..f56c406 100644 --- a/test/test_trust-downstream-proxy/test_enabled.py +++ b/test/test_trust-downstream-proxy/test_enabled.py @@ -8,6 +8,11 @@ import re ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'https'), ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', 'f00', 'f00'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', None, 'web.nginx-proxy.tld'), + ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Host', 'example.com', 'example.com'), + ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '80'), ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', '1234', '1234'), ('https://web.nginx-proxy.tld/headers', 'X-Forwarded-Port', None, '443'), From 146b7933a955811bd15597195397c58f0a47f4f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 04:03:07 +0000 Subject: [PATCH 022/105] chore(deps): bump golang from 1.19.4 to 1.19.5 Bumps golang from 1.19.4 to 1.19.5. --- 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 1319579..fa227cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.19.4 as gobuilder +FROM golang:1.19.5 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 4e70c11..5473e53 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.19.4-alpine as gobuilder +FROM golang:1.19.5-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 831615fdd4cf2267257462691882105dde83f759 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Jan 2023 04:04:20 +0000 Subject: [PATCH 023/105] chore(deps): bump requests from 2.28.1 to 2.28.2 in /test/requirements Bumps [requests](https://github.com/psf/requests) from 2.28.1 to 2.28.2. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.28.1...v2.28.2) --- updated-dependencies: - dependency-name: requests 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 c75a674..995cd1e 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -2,4 +2,4 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.0.1 pytest==7.2.0 -requests==2.28.1 +requests==2.28.2 From 9c9545bf7f936bb9c96e426baf560b56f8c4db5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jan 2023 04:12:49 +0000 Subject: [PATCH 024/105] chore(deps): bump pytest from 7.2.0 to 7.2.1 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.2.0 to 7.2.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.2.0...7.2.1) --- 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 995cd1e..6d7d495 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.0.1 -pytest==7.2.0 +pytest==7.2.1 requests==2.28.2 From c117ae8fd8cb9b9e2b082ac70ab9d92c63b7b340 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 28 Mar 2022 01:03:04 -0400 Subject: [PATCH 025/105] chore: Use boolean for `$server_found` variable --- nginx.tmpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index e7d77c9..33df00a 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -90,7 +90,7 @@ {{ $networks := .Networks }} {{ $debug_all := .Debug }} upstream {{ .Upstream }} { - {{ $server_found := "false" }} + {{ $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 */}} @@ -112,19 +112,19 @@ upstream {{ .Upstream }} { {{ 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" }} + {{ $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" }} + {{ $server_found = true }} # {{ $container.Name }} server {{ $containerNetwork.IP }}:{{ $address.Port }}; {{ end }} {{ else if $containerNetwork }} # {{ $container.Name }} {{ if $containerNetwork.IP }} - {{ $server_found = "true" }} + {{ $server_found = true }} server {{ $containerNetwork.IP }}:{{ $port }}; {{ else }} # /!\ No IP for this network! @@ -137,7 +137,7 @@ upstream {{ .Upstream }} { {{ end }} {{ end }} {{/* nginx-proxy/nginx-proxy#1105 */}} - {{ if (eq $server_found "false") }} + {{ if not $server_found }} # Fallback entry server 127.0.0.1 down; {{ end }} From 05423c681af6e50e958f46e056dc126941ed6784 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 28 Mar 2022 01:04:20 -0400 Subject: [PATCH 026/105] fix: Use `parseBool` to parse boolean strings --- nginx.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 33df00a..14616d4 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -92,7 +92,7 @@ upstream {{ .Upstream }} { {{ $server_found := false }} {{ range $container := .Containers }} - {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }} + {{ $debug := parseBool (coalesce $container.Env.DEBUG $debug_all "false") }} {{/* 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) }} @@ -229,7 +229,7 @@ proxy_set_header Proxy ""; {{ $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} -{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }} +{{ $enable_ipv6 := parseBool (coalesce $.Env.ENABLE_IPV6 "false") }} server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; From 14d0f3f222b0d6e627c23bebc80878d79b475d58 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 28 Mar 2022 02:13:16 -0400 Subject: [PATCH 027/105] chore: Rename `$container` to `$containers` The value is actually a slice/array of containers so it should be pluralized. --- nginx.tmpl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 14616d4..10dd720 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -397,15 +397,15 @@ server { {{ $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 }} + {{ range $path, $containers := $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") }} + {{ $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 $container "Env.NETWORK_ACCESS")) "external" }} + {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} - {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} + {{ $dest := (or (first (groupByKeys $containers "Env.VIRTUAL_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 "/")) }} @@ -445,15 +445,15 @@ server { {{ $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 }} + {{ range $path, $containers := $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") }} + {{ $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 $container "Env.NETWORK_ACCESS")) "external" }} + {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{ $sum := sha1 $path }} {{ $upstream := printf "%s-%s" $upstream_name $sum }} - {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }} + {{ $dest := (or (first (groupByKeys $containers "Env.VIRTUAL_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 "/")) }} From 491642b1e92cb1c058ebd72d6bc0074eec6af387 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 5 Apr 2022 04:59:54 -0400 Subject: [PATCH 028/105] chore: Factor out duplicate virtual path code --- nginx.tmpl | 52 ++++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 10dd720..c2409e1 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -269,15 +269,17 @@ server { {{ $nPaths := len $paths }} {{ if eq $nPaths 0 }} - # {{ $host }} - {{ template "upstream" (dict "Upstream" $upstream_name "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} -{{ else }} - {{ range $path, $containers := $paths }} + {{ $paths = dict "/" $containers }} +{{ end }} + +{{ range $path, $containers := $paths }} + {{ $upstream := $upstream_name }} + {{ if gt $nPaths 0 }} {{ $sum := sha1 $path }} - {{ $upstream := printf "%s-%s" $upstream_name $sum }} + {{ $upstream = printf "%s-%s" $upstream $sum }} + {{ end }} # {{ $host }}{{ $path }} {{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} - {{ end }} {{ end }} {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} @@ -389,23 +391,19 @@ server { include /etc/nginx/vhost.d/default; {{ 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, $containers := $paths }} {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, 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" }} - {{ $sum := sha1 $path }} - {{ $upstream := printf "%s-%s" $upstream_name $sum }} - {{ $dest := (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} + {{ $upstream := $upstream_name }} + {{ $dest := "" }} + {{ if gt $nPaths 0 }} + {{ $sum := sha1 $path }} + {{ $upstream = printf "%s-%s" $upstream $sum }} + {{ $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} + {{ end }} {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} {{ if (not (contains $paths "/")) }} @@ -413,7 +411,6 @@ server { return {{ $default_root_response }}; } {{ end }} - {{ end }} } {{ end }} @@ -437,23 +434,19 @@ server { include /etc/nginx/vhost.d/default; {{ 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, $containers := $paths }} {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, 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" }} - {{ $sum := sha1 $path }} - {{ $upstream := printf "%s-%s" $upstream_name $sum }} - {{ $dest := (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} + {{ $upstream := $upstream_name }} + {{ $dest := "" }} + {{ if gt $nPaths 0 }} + {{ $sum := sha1 $path }} + {{ $upstream = printf "%s-%s" $upstream $sum }} + {{ $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} + {{ end }} {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{ end }} {{ if (not (contains $paths "/")) }} @@ -461,7 +454,6 @@ server { return {{ $default_root_response }}; } {{ end }} - {{ end }} } {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} From 744bd82c5491e46f1ab2bfc7ad803edd1bb37b9b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Apr 2022 15:37:53 -0400 Subject: [PATCH 029/105] chore: Combine identical HTTP and HTTPS servers --- nginx.tmpl | 72 ++++++++++++------------------------------------------ 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index c2409e1..a262a96 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -239,25 +239,19 @@ server { {{ end }} {{ $access_log }} return 503; -} {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} -server { - server_name _; # This is just an invalid value which will never trigger on a real hostname. - server_tokens off; listen {{ $external_https_port }} ssl http2; {{ if $enable_ipv6 }} listen [::]:{{ $external_https_port }} ssl http2; {{ end }} - {{ $access_log }} - return 503; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; -} {{ end }} +} {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} @@ -317,9 +311,7 @@ server { {{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} -{{ if $is_https }} - -{{ if eq $https_method "redirect" }} +{{ if and $is_https (eq $https_method "redirect") }} server { server_name {{ $host }}; {{ if $server_tokens }} @@ -356,11 +348,18 @@ server { {{ if $server_tokens }} server_tokens {{ $server_tokens }}; {{ end }} + {{ $access_log }} + {{- if or (not $is_https) (eq $https_method "noredirect") }} + listen {{ $external_http_port }} {{ $default_server }}; + {{ if $enable_ipv6 }} + listen [::]:{{ $external_http_port }} {{ $default_server }}; + {{ end }} + {{- end }} + {{- if $is_https }} listen {{ $external_https_port }} ssl http2 {{ $default_server }}; {{ if $enable_ipv6 }} listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; {{ end }} - {{ $access_log }} {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} @@ -382,51 +381,13 @@ server { {{ end }} {{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} - add_header Strict-Transport-Security "{{ trim $hsts }}" always; + set $sts_header ""; + if ($https) { + set $sts_header "{{ trim $hsts }}"; + } + add_header Strict-Transport-Security $sts_header always; {{ 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") }} - include /etc/nginx/vhost.d/default; - {{ end }} - - {{ range $path, $containers := $paths }} - {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, 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" }} - {{ $upstream := $upstream_name }} - {{ $dest := "" }} - {{ if gt $nPaths 0 }} - {{ $sum := sha1 $path }} - {{ $upstream = printf "%s-%s" $upstream $sum }} - {{ $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} - {{ end }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} - {{ end }} - {{ if (not (contains $paths "/")) }} - location / { - return {{ $default_root_response }}; - } - {{ end }} -} - -{{ end }} - -{{ if or (not $is_https) (eq $https_method "noredirect") }} - -server { - server_name {{ $host }}; - {{ if $server_tokens }} - server_tokens {{ $server_tokens }}; - {{ end }} - listen {{ $external_http_port }} {{ $default_server }}; - {{ if $enable_ipv6 }} - listen [::]:{{ $external_http_port }} {{ $default_server }}; - {{ end }} - {{ $access_log }} + {{- end }} {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s" $host }}; @@ -475,4 +436,3 @@ server { {{ end }} {{ end }} -{{ end }} From 4651bf411d05b196f75b6af448ef4b33742d0b6e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 28 Mar 2022 02:49:58 -0400 Subject: [PATCH 030/105] chore: Fix comment for `$proxy_connection` variable --- nginx.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index a262a96..68fd3bb 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -167,8 +167,8 @@ map $http_x_forwarded_port $proxy_x_forwarded_port { '' $server_port; } -# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any -# Connection header that may have been passed to this server +# If we receive Upgrade, set Connection to "upgrade"; otherwise, preserve +# NGINX's default behavior ("Connection: close"). map $http_upgrade $proxy_connection { default upgrade; '' close; From d6d8b2205fcc5e87bf21581cf6a466ce9ba62b1d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 17 May 2022 00:47:40 -0400 Subject: [PATCH 031/105] chore: Fix comment terminators --- nginx.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 68fd3bb..cf2fd2c 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -11,8 +11,8 @@ {{ define "ssl_policy" }} {{ if eq .ssl_policy "Mozilla-Modern" }} ssl_protocols TLSv1.3; - {{/* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 /*}} - {{/* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) /*}} + {{/* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 */}} + {{/* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) */}} {{/* explicitly set ngnix default value in order to allow single servers to override the global http value */}} ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers off; From f20662eeaa877c577bd5712190234ce819dfdef0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 17 May 2022 00:56:06 -0400 Subject: [PATCH 032/105] chore: Use `{{-` instead of `{{` to clean up whitespace --- nginx.tmpl | 368 ++++++++++++++++++++++++++--------------------------- 1 file changed, 184 insertions(+), 184 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index cf2fd2c..a320def 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1,152 +1,152 @@ -{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} +{{- $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} -{{ $nginx_proxy_version := coalesce $.Env.NGINX_PROXY_VERSION "" }} -{{ $external_http_port := coalesce $.Env.HTTP_PORT "80" }} -{{ $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" }} -{{ $trust_downstream_proxy := parseBool (coalesce $.Env.TRUST_DOWNSTREAM_PROXY "true") }} +{{- $nginx_proxy_version := coalesce $.Env.NGINX_PROXY_VERSION "" }} +{{- $external_http_port := coalesce $.Env.HTTP_PORT "80" }} +{{- $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" }} +{{- $trust_downstream_proxy := parseBool (coalesce $.Env.TRUST_DOWNSTREAM_PROXY "true") }} -{{ define "ssl_policy" }} - {{ if eq .ssl_policy "Mozilla-Modern" }} +{{- define "ssl_policy" }} + {{- if eq .ssl_policy "Mozilla-Modern" }} ssl_protocols TLSv1.3; - {{/* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 */}} - {{/* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) */}} - {{/* explicitly set ngnix default value in order to allow single servers to override the global http value */}} + {{- /* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 */}} + {{- /* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) */}} + {{- /* explicitly set ngnix default value in order to allow single servers to override the global http value */}} ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers off; - {{ else if eq .ssl_policy "Mozilla-Intermediate" }} + {{- else if eq .ssl_policy "Mozilla-Intermediate" }} ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_prefer_server_ciphers off; - {{ else if eq .ssl_policy "Mozilla-Old" }} + {{- else if eq .ssl_policy "Mozilla-Old" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA'; ssl_prefer_server_ciphers on; - {{ else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }} + {{- else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }} ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256'; ssl_prefer_server_ciphers on; - {{ else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }} + {{- else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }} ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; ssl_prefer_server_ciphers on; - {{ else if eq .ssl_policy "AWS-2016-08" }} + {{- else if eq .ssl_policy "AWS-2016-08" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; ssl_prefer_server_ciphers on; - {{ else if eq .ssl_policy "AWS-2015-05" }} + {{- else if eq .ssl_policy "AWS-2015-05" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA'; ssl_prefer_server_ciphers on; - {{ else if eq .ssl_policy "AWS-2015-03" }} + {{- else if eq .ssl_policy "AWS-2015-03" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA'; ssl_prefer_server_ciphers on; - {{ else if eq .ssl_policy "AWS-2015-02" }} + {{- else if eq .ssl_policy "AWS-2015-02" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA'; ssl_prefer_server_ciphers on; - {{ end }} -{{ end }} + {{- end }} +{{- end }} -{{ define "location" }} +{{- define "location" }} location {{ .Path }} { - {{ if eq .NetworkTag "internal" }} + {{- if eq .NetworkTag "internal" }} # Only allow traffic from internal clients include /etc/nginx/network_internal.conf; - {{ end }} + {{- end }} - {{ if eq .Proto "uwsgi" }} + {{- if eq .Proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; - {{ else if eq .Proto "fastcgi" }} + {{- else if eq .Proto "fastcgi" }} root {{ trim .VhostRoot }}; include fastcgi_params; fastcgi_pass {{ trim .Upstream }}; - {{ else if eq .Proto "grpc" }} + {{- else if eq .Proto "grpc" }} grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; - {{ else }} + {{- else }} proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }}; - {{ end }} + {{- end }} - {{ if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} + {{- if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} auth_basic "Restricted {{ .Host }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }}; - {{ end }} + {{- end }} - {{ 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)) }} + {{- 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") }} + {{- else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; - {{ end }} + {{- end }} } -{{ end }} +{{- end }} -{{ define "upstream" }} - {{ $networks := .Networks }} - {{ $debug_all := .Debug }} +{{- define "upstream" }} + {{- $networks := .Networks }} + {{- $debug_all := .Debug }} upstream {{ .Upstream }} { - {{ $server_found := false }} - {{ range $container := .Containers }} - {{ $debug := parseBool (coalesce $container.Env.DEBUG $debug_all "false") }} - {{/* 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 }} + {{- $server_found := false }} + {{- range $container := .Containers }} + {{- $debug := parseBool (coalesce $container.Env.DEBUG $debug_all "false") }} + {{- /* 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 }} + {{- if not $address }} # /!\ Virtual port not exposed - {{ end }} - {{ end }} - {{ range $knownNetwork := $networks }} - {{ range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} - {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} + {{- end }} + {{- end }} + {{- range $knownNetwork := $networks }} + {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} + {{- 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 }} + {{- end }} + {{- end }} + {{- else }} # Cannot connect to network '{{ $containerNetwork.Name }}' of this container - {{ end }} - {{ end }} - {{ end }} - {{ end }} - {{/* nginx-proxy/nginx-proxy#1105 */}} - {{ if not $server_found }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- /* nginx-proxy/nginx-proxy#1105 */}} + {{- if not $server_found }} # Fallback entry server 127.0.0.1 down; - {{ end }} + {{- end }} } -{{ end }} +{{- end }} -{{ if ne $nginx_proxy_version "" }} +{{- if ne $nginx_proxy_version "" }} # nginx-proxy version : {{ $nginx_proxy_version }} -{{ end }} +{{- end }} # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server @@ -178,9 +178,9 @@ map $http_upgrade $proxy_connection { server_names_hash_bucket_size 128; # Default dhparam -{{ if (exists "/etc/nginx/dhparam/dhparam.pem") }} +{{- if (exists "/etc/nginx/dhparam/dhparam.pem") }} ssl_dhparam /etc/nginx/dhparam/dhparam.pem; -{{ end }} +{{- end }} # Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl { @@ -197,18 +197,18 @@ log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 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) }} +{{- /* 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 }} +{{- if $.Env.RESOLVERS }} resolver {{ $.Env.RESOLVERS }}; -{{ end }} +{{- end }} -{{ if (exists "/etc/nginx/proxy.conf") }} +{{- if (exists "/etc/nginx/proxy.conf") }} include /etc/nginx/proxy.conf; -{{ else }} +{{- else }} # HTTP 1.1 support proxy_http_version 1.1; proxy_buffering off; @@ -225,102 +225,102 @@ proxy_set_header X-Original-URI $request_uri; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; -{{ end }} +{{- end }} -{{ $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} +{{- $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} -{{ $enable_ipv6 := parseBool (coalesce $.Env.ENABLE_IPV6 "false") }} +{{- $enable_ipv6 := parseBool (coalesce $.Env.ENABLE_IPV6 "false") }} server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; listen {{ $external_http_port }}; - {{ if $enable_ipv6 }} + {{- if $enable_ipv6 }} listen [::]:{{ $external_http_port }}; - {{ end }} + {{- end }} {{ $access_log }} return 503; -{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +{{- if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} listen {{ $external_https_port }} ssl http2; - {{ if $enable_ipv6 }} + {{- if $enable_ipv6 }} listen [::]:{{ $external_https_port }} ssl http2; - {{ end }} + {{- end }} ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; -{{ end }} +{{- end }} } -{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} +{{- range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} -{{ $host := trim $host }} -{{ $is_regexp := hasPrefix "~" $host }} -{{ $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} +{{- $host := trim $host }} +{{- $is_regexp := hasPrefix "~" $host }} +{{- $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} -{{ $paths := groupBy $containers "Env.VIRTUAL_PATH" }} -{{ $nPaths := len $paths }} +{{- $paths := groupBy $containers "Env.VIRTUAL_PATH" }} +{{- $nPaths := len $paths }} -{{ if eq $nPaths 0 }} - {{ $paths = dict "/" $containers }} -{{ end }} +{{- if eq $nPaths 0 }} + {{- $paths = dict "/" $containers }} +{{- end }} -{{ range $path, $containers := $paths }} - {{ $upstream := $upstream_name }} - {{ if gt $nPaths 0 }} - {{ $sum := sha1 $path }} - {{ $upstream = printf "%s-%s" $upstream $sum }} - {{ end }} +{{- range $path, $containers := $paths }} + {{- $upstream := $upstream_name }} + {{- if gt $nPaths 0 }} + {{- $sum := sha1 $path }} + {{- $upstream = printf "%s-%s" $upstream $sum }} + {{- end }} # {{ $host }}{{ $path }} {{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} -{{ end }} +{{- end }} -{{ $default_host := or ($.Env.DEFAULT_HOST) "" }} -{{ $default_server := index (dict $host "" $default_host "default_server") $host }} +{{- $default_host := or ($.Env.DEFAULT_HOST) "" }} +{{- $default_server := index (dict $host "" $default_host "default_server") $host }} -{{/* 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 SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}} +{{- $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }} -{{/* 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") }} +{{- /* 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") }} -{{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}} -{{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} +{{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}} +{{- $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} -{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} -{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }} +{{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} +{{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }} -{{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} -{{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} +{{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} +{{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} -{{/* Get the first cert name defined by containers w/ the same vhost */}} -{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} +{{- /* Get the first cert name defined by containers w/ the same vhost */}} +{{- $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} -{{/* Get the best matching cert by name for the vhost. */}} -{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} +{{- /* Get the best matching cert by name for the vhost. */}} +{{- $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} -{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} -{{ $vhostCert := trimSuffix ".crt" $vhostCert }} -{{ $vhostCert := trimSuffix ".key" $vhostCert }} +{{- /* vhostCert is actually a filename so remove any suffixes since they are added later */}} +{{- $vhostCert := trimSuffix ".crt" $vhostCert }} +{{- $vhostCert := trimSuffix ".key" $vhostCert }} -{{/* Use the cert specified on the container or fallback to the best vhost match */}} -{{ $cert := (coalesce $certName $vhostCert) }} +{{- /* Use the cert specified on the container or fallback to the best vhost match */}} +{{- $cert := (coalesce $certName $vhostCert) }} -{{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} +{{- $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} -{{ if and $is_https (eq $https_method "redirect") }} +{{- if and $is_https (eq $https_method "redirect") }} server { server_name {{ $host }}; - {{ if $server_tokens }} + {{- if $server_tokens }} server_tokens {{ $server_tokens }}; - {{ end }} + {{- end }} listen {{ $external_http_port }} {{ $default_server }}; - {{ if $enable_ipv6 }} + {{- if $enable_ipv6 }} listen [::]:{{ $external_http_port }} {{ $default_server }}; - {{ end }} + {{- end }} {{ $access_log }} # Do not HTTPS redirect Let'sEncrypt ACME challenge @@ -334,34 +334,34 @@ server { } location / { - {{ if eq $external_https_port "443" }} + {{- if eq $external_https_port "443" }} return 301 https://$host$request_uri; - {{ else }} + {{- else }} return 301 https://$host:{{ $external_https_port }}$request_uri; - {{ end }} + {{- end }} } } -{{ end }} +{{- end }} server { server_name {{ $host }}; - {{ if $server_tokens }} + {{- if $server_tokens }} server_tokens {{ $server_tokens }}; - {{ end }} + {{- end }} {{ $access_log }} {{- if or (not $is_https) (eq $https_method "noredirect") }} listen {{ $external_http_port }} {{ $default_server }}; - {{ if $enable_ipv6 }} + {{- if $enable_ipv6 }} listen [::]:{{ $external_http_port }} {{ $default_server }}; - {{ end }} + {{- end }} {{- end }} {{- if $is_https }} listen {{ $external_https_port }} ssl http2 {{ $default_server }}; - {{ if $enable_ipv6 }} + {{- if $enable_ipv6 }} listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; - {{ end }} + {{- end }} - {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} + {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; @@ -370,69 +370,69 @@ server { ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; - {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} + {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; - {{ end }} + {{- end }} - {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} + {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; - {{ end }} + {{- end }} - {{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} + {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} set $sts_header ""; if ($https) { set $sts_header "{{ trim $hsts }}"; } add_header Strict-Transport-Security $sts_header always; - {{ end }} + {{- end }} {{- end }} - {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} + {{- 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") }} + {{- else if (exists "/etc/nginx/vhost.d/default") }} include /etc/nginx/vhost.d/default; - {{ end }} + {{- end }} - {{ range $path, $containers := $paths }} - {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} - {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} + {{- range $path, $containers := $paths }} + {{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, 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" }} - {{ $upstream := $upstream_name }} - {{ $dest := "" }} - {{ if gt $nPaths 0 }} - {{ $sum := sha1 $path }} - {{ $upstream = printf "%s-%s" $upstream $sum }} - {{ $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} - {{ end }} - {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} - {{ end }} - {{ if (not (contains $paths "/")) }} + {{- /* 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" }} + {{- $upstream := $upstream_name }} + {{- $dest := "" }} + {{- if gt $nPaths 0 }} + {{- $sum := sha1 $path }} + {{- $upstream = printf "%s-%s" $upstream $sum }} + {{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} + {{- end }} + {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} + {{- end }} + {{- if (not (contains $paths "/")) }} location / { return {{ $default_root_response }}; } - {{ end }} + {{- end }} } -{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +{{- if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} server { server_name {{ $host }}; - {{ if $server_tokens }} + {{- if $server_tokens }} server_tokens {{ $server_tokens }}; - {{ end }} + {{- end }} listen {{ $external_https_port }} ssl http2 {{ $default_server }}; - {{ if $enable_ipv6 }} + {{- if $enable_ipv6 }} listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; - {{ end }} + {{- end }} {{ $access_log }} return 500; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; } -{{ end }} +{{- end }} -{{ end }} +{{- end }} From 0da38122bd17c42d826a714cc05a4160a1a9f63e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 17 May 2022 01:19:32 -0400 Subject: [PATCH 033/105] chore: Consistent indentation --- nginx.tmpl | 533 ++++++++++++++++++++++++++--------------------------- 1 file changed, 266 insertions(+), 267 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index a320def..2cb0fe4 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -9,138 +9,138 @@ {{- $trust_downstream_proxy := parseBool (coalesce $.Env.TRUST_DOWNSTREAM_PROXY "true") }} {{- define "ssl_policy" }} - {{- if eq .ssl_policy "Mozilla-Modern" }} - ssl_protocols TLSv1.3; - {{- /* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 */}} - {{- /* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) */}} - {{- /* explicitly set ngnix default value in order to allow single servers to override the global http value */}} - ssl_ciphers HIGH:!aNULL:!MD5; - ssl_prefer_server_ciphers off; - {{- else if eq .ssl_policy "Mozilla-Intermediate" }} - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers off; - {{- else if eq .ssl_policy "Mozilla-Old" }} - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA'; - ssl_prefer_server_ciphers on; - {{- else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }} - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256'; - ssl_prefer_server_ciphers on; - {{- else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }} - ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; - ssl_prefer_server_ciphers on; - {{- else if eq .ssl_policy "AWS-2016-08" }} - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; - ssl_prefer_server_ciphers on; - {{- else if eq .ssl_policy "AWS-2015-05" }} - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA'; - ssl_prefer_server_ciphers on; - {{- else if eq .ssl_policy "AWS-2015-03" }} - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA'; - ssl_prefer_server_ciphers on; - {{- else if eq .ssl_policy "AWS-2015-02" }} - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA'; - ssl_prefer_server_ciphers on; - {{- end }} + {{- if eq .ssl_policy "Mozilla-Modern" }} + ssl_protocols TLSv1.3; + {{- /* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 */}} + {{- /* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) */}} + {{- /* explicitly set ngnix default value in order to allow single servers to override the global http value */}} + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers off; + {{- else if eq .ssl_policy "Mozilla-Intermediate" }} + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; + ssl_prefer_server_ciphers off; + {{- else if eq .ssl_policy "Mozilla-Old" }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA'; + ssl_prefer_server_ciphers on; + {{- else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }} + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256'; + ssl_prefer_server_ciphers on; + {{- else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }} + ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; + ssl_prefer_server_ciphers on; + {{- else if eq .ssl_policy "AWS-2016-08" }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; + ssl_prefer_server_ciphers on; + {{- else if eq .ssl_policy "AWS-2015-05" }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA'; + ssl_prefer_server_ciphers on; + {{- else if eq .ssl_policy "AWS-2015-03" }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA'; + ssl_prefer_server_ciphers on; + {{- else if eq .ssl_policy "AWS-2015-02" }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA'; + ssl_prefer_server_ciphers on; + {{- end }} {{- end }} {{- define "location" }} - location {{ .Path }} { - {{- if eq .NetworkTag "internal" }} - # Only allow traffic from internal clients - include /etc/nginx/network_internal.conf; - {{- end }} + 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 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/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 }} -} + {{- 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 }} + {{- $networks := .Networks }} + {{- $debug_all := .Debug }} upstream {{ .Upstream }} { - {{- $server_found := false }} - {{- range $container := .Containers }} + {{- $server_found := false }} + {{- range $container := .Containers }} {{- $debug := parseBool (coalesce $container.Env.DEBUG $debug_all "false") }} {{- /* 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 }} + # Exposed ports: {{ $container.Addresses }} + # Default virtual port: {{ $defaultPort }} + # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} {{- if not $address }} - # /!\ Virtual port not exposed + # /!\ Virtual port not exposed {{- end }} {{- end }} - {{- range $knownNetwork := $networks }} - {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} - {{- if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} - ## Can be connected with "{{ $containerNetwork.Name }}" network + {{- range $knownNetwork := $networks }} + {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} + {{- 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 */}} + # {{ $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 }}; + # {{ $container.Name }} + server {{ $containerNetwork.IP }}:{{ $address.Port }}; {{- end }} {{- else if $containerNetwork }} - # {{ $container.Name }} + # {{ $container.Name }} {{- if $containerNetwork.IP }} {{- $server_found = true }} - server {{ $containerNetwork.IP }}:{{ $port }}; + 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 not $server_found }} - # Fallback entry - server 127.0.0.1 down; - {{- end }} + # /!\ 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 not $server_found }} + # Fallback entry + server 127.0.0.1 down; + {{- end }} } {{- end }} @@ -151,27 +151,27 @@ upstream {{ .Upstream }} { # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { - default {{ if $trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; - '' $scheme; + default {{ if $trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; + '' $scheme; } map $http_x_forwarded_host $proxy_x_forwarded_host { - default {{ if $trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }}; - '' $http_host; + default {{ if $trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }}; + '' $http_host; } # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the # server port the client connected to map $http_x_forwarded_port $proxy_x_forwarded_port { - default {{ if $trust_downstream_proxy }}$http_x_forwarded_port{{ else }}$server_port{{ end }}; - '' $server_port; + default {{ if $trust_downstream_proxy }}$http_x_forwarded_port{{ else }}$server_port{{ end }}; + '' $server_port; } # If we receive Upgrade, set Connection to "upgrade"; otherwise, preserve # NGINX's default behavior ("Connection: close"). map $http_upgrade $proxy_connection { - default upgrade; - '' close; + default upgrade; + '' close; } # Apply fix for very long server names @@ -184,8 +184,8 @@ ssl_dhparam /etc/nginx/dhparam/dhparam.pem; # Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl { - default off; - https on; + default off; + https on; } gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; @@ -231,208 +231,207 @@ proxy_set_header Proxy ""; {{- $enable_ipv6 := parseBool (coalesce $.Env.ENABLE_IPV6 "false") }} server { - server_name _; # This is just an invalid value which will never trigger on a real hostname. - server_tokens off; - listen {{ $external_http_port }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_http_port }}; - {{- end }} - {{ $access_log }} - return 503; + server_name _; # This is just an invalid value which will never trigger on a real hostname. + server_tokens off; + listen {{ $external_http_port }}; +{{- if $enable_ipv6 }} + listen [::]:{{ $external_http_port }}; +{{- end }} + {{ $access_log }} + return 503; {{- if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} - listen {{ $external_https_port }} ssl http2; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_https_port }} ssl http2; - {{- end }} + listen {{ $external_https_port }} ssl http2; + {{- if $enable_ipv6 }} + listen [::]:{{ $external_https_port }} ssl http2; + {{- end }} - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; - ssl_certificate /etc/nginx/certs/default.crt; - ssl_certificate_key /etc/nginx/certs/default.key; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; + ssl_certificate /etc/nginx/certs/default.crt; + ssl_certificate_key /etc/nginx/certs/default.key; {{- end }} } {{- range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} -{{- $host := trim $host }} -{{- $is_regexp := hasPrefix "~" $host }} -{{- $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} + {{- $host := trim $host }} + {{- $is_regexp := hasPrefix "~" $host }} + {{- $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} -{{- $paths := groupBy $containers "Env.VIRTUAL_PATH" }} -{{- $nPaths := len $paths }} + {{- $paths := groupBy $containers "Env.VIRTUAL_PATH" }} + {{- $nPaths := len $paths }} + {{- if eq $nPaths 0 }} + {{- $paths = dict "/" $containers }} + {{- end }} -{{- if eq $nPaths 0 }} - {{- $paths = dict "/" $containers }} -{{- end }} + {{- range $path, $containers := $paths }} + {{- $upstream := $upstream_name }} + {{- if gt $nPaths 0 }} + {{- $sum := sha1 $path }} + {{- $upstream = printf "%s-%s" $upstream $sum }} + {{- end }} +# {{ $host }}{{ $path }} +{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} + {{- end }} -{{- range $path, $containers := $paths }} - {{- $upstream := $upstream_name }} - {{- if gt $nPaths 0 }} - {{- $sum := sha1 $path }} - {{- $upstream = printf "%s-%s" $upstream $sum }} - {{- end }} - # {{ $host }}{{ $path }} - {{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} -{{- end }} + {{- $default_host := or ($.Env.DEFAULT_HOST) "" }} + {{- $default_server := index (dict $host "" $default_host "default_server") $host }} -{{- $default_host := or ($.Env.DEFAULT_HOST) "" }} -{{- $default_server := index (dict $host "" $default_host "default_server") $host }} - -{{- /* 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 SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}} + {{- $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }} -{{- /* 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") }} + {{- /* 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") }} -{{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}} -{{- $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} + {{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}} + {{- $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} -{{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} -{{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }} + {{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} + {{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }} -{{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} -{{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} + {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} + {{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} -{{- /* Get the first cert name defined by containers w/ the same vhost */}} -{{- $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} + {{- /* Get the first cert name defined by containers w/ the same vhost */}} + {{- $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} -{{- /* Get the best matching cert by name for the vhost. */}} -{{- $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} + {{- /* Get the best matching cert by name for the vhost. */}} + {{- $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} -{{- /* vhostCert is actually a filename so remove any suffixes since they are added later */}} -{{- $vhostCert := trimSuffix ".crt" $vhostCert }} -{{- $vhostCert := trimSuffix ".key" $vhostCert }} + {{- /* vhostCert is actually a filename so remove any suffixes since they are added later */}} + {{- $vhostCert := trimSuffix ".crt" $vhostCert }} + {{- $vhostCert := trimSuffix ".key" $vhostCert }} -{{- /* Use the cert specified on the container or fallback to the best vhost match */}} -{{- $cert := (coalesce $certName $vhostCert) }} + {{- /* Use the cert specified on the container or fallback to the best vhost match */}} + {{- $cert := (coalesce $certName $vhostCert) }} -{{- $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} + {{- $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} -{{- if and $is_https (eq $https_method "redirect") }} + {{- if and $is_https (eq $https_method "redirect") }} server { - server_name {{ $host }}; - {{- if $server_tokens }} - server_tokens {{ $server_tokens }}; - {{- end }} - listen {{ $external_http_port }} {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_http_port }} {{ $default_server }}; - {{- end }} - {{ $access_log }} + server_name {{ $host }}; + {{- if $server_tokens }} + server_tokens {{ $server_tokens }}; + {{- end }} + listen {{ $external_http_port }} {{ $default_server }}; + {{- if $enable_ipv6 }} + listen [::]:{{ $external_http_port }} {{ $default_server }}; + {{- end }} + {{ $access_log }} - # Do not HTTPS redirect Let'sEncrypt ACME challenge - location ^~ /.well-known/acme-challenge/ { - auth_basic off; - auth_request off; - allow all; - root /usr/share/nginx/html; - try_files $uri =404; - break; - } + # Do not HTTPS redirect Let's Encrypt ACME challenge + location ^~ /.well-known/acme-challenge/ { + auth_basic off; + auth_request off; + allow all; + root /usr/share/nginx/html; + try_files $uri =404; + break; + } - location / { - {{- if eq $external_https_port "443" }} - return 301 https://$host$request_uri; - {{- else }} - return 301 https://$host:{{ $external_https_port }}$request_uri; - {{- end }} - } + location / { + {{- if eq $external_https_port "443" }} + return 301 https://$host$request_uri; + {{- else }} + return 301 https://$host:{{ $external_https_port }}$request_uri; + {{- end }} + } } -{{- end }} + {{- end }} server { - server_name {{ $host }}; - {{- if $server_tokens }} - server_tokens {{ $server_tokens }}; - {{- end }} - {{ $access_log }} + server_name {{ $host }}; + {{- if $server_tokens }} + server_tokens {{ $server_tokens }}; + {{- end }} + {{ $access_log }} {{- if or (not $is_https) (eq $https_method "noredirect") }} - listen {{ $external_http_port }} {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_http_port }} {{ $default_server }}; - {{- end }} + listen {{ $external_http_port }} {{ $default_server }}; + {{- if $enable_ipv6 }} + listen [::]:{{ $external_http_port }} {{ $default_server }}; + {{- end }} {{- end }} {{- if $is_https }} - listen {{ $external_https_port }} ssl http2 {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; - {{- end }} + listen {{ $external_https_port }} ssl http2 {{ $default_server }}; + {{- if $enable_ipv6 }} + listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; + {{- end }} - {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} + {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} - ssl_session_timeout 5m; - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; + ssl_session_timeout 5m; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; - ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; - ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; + ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; + ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; - {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} - ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; - {{- end }} + {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} + ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; + {{- end }} - {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} - ssl_stapling on; - ssl_stapling_verify on; - ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; - {{- end }} + {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; + {{- end }} - {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} - set $sts_header ""; - if ($https) { - set $sts_header "{{ trim $hsts }}"; - } - add_header Strict-Transport-Security $sts_header always; - {{- end }} + {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} + set $sts_header ""; + if ($https) { + set $sts_header "{{ trim $hsts }}"; + } + add_header Strict-Transport-Security $sts_header always; + {{- end }} {{- 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") }} - include /etc/nginx/vhost.d/default; - {{- 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") }} + include /etc/nginx/vhost.d/default; + {{- end }} - {{- range $path, $containers := $paths }} - {{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} - {{- $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} + {{- range $path, $containers := $paths }} + {{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, 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" }} - {{- $upstream := $upstream_name }} - {{- $dest := "" }} - {{- if gt $nPaths 0 }} - {{- $sum := sha1 $path }} - {{- $upstream = printf "%s-%s" $upstream $sum }} - {{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} - {{- end }} - {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} - {{- end }} - {{- if (not (contains $paths "/")) }} - location / { - return {{ $default_root_response }}; - } - {{- end }} + {{- /* 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" }} + {{- $upstream := $upstream_name }} + {{- $dest := "" }} + {{- if gt $nPaths 0 }} + {{- $sum := sha1 $path }} + {{- $upstream = printf "%s-%s" $upstream $sum }} + {{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} + {{- end }} + {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} + {{- end }} + {{- if (not (contains $paths "/")) }} + location / { + return {{ $default_root_response }}; + } + {{- end }} } -{{- if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} + {{- if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} server { - server_name {{ $host }}; - {{- if $server_tokens }} - server_tokens {{ $server_tokens }}; - {{- end }} - listen {{ $external_https_port }} ssl http2 {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; - {{- end }} - {{ $access_log }} - return 500; + server_name {{ $host }}; + {{- if $server_tokens }} + server_tokens {{ $server_tokens }}; + {{- end }} + listen {{ $external_https_port }} ssl http2 {{ $default_server }}; + {{- if $enable_ipv6 }} + listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; + {{- end }} + {{ $access_log }} + return 500; - ssl_certificate /etc/nginx/certs/default.crt; - ssl_certificate_key /etc/nginx/certs/default.key; + ssl_certificate /etc/nginx/certs/default.crt; + ssl_certificate_key /etc/nginx/certs/default.key; } -{{- end }} + {{- end }} {{- end }} From 1b253cd9086133fa0f9caa0e5b9a00a9aead4681 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 9 Jan 2023 17:21:35 -0500 Subject: [PATCH 034/105] chore: Wrap long comments --- nginx.tmpl | 68 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 2cb0fe4..4f90880 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -11,9 +11,15 @@ {{- define "ssl_policy" }} {{- if eq .ssl_policy "Mozilla-Modern" }} ssl_protocols TLSv1.3; - {{- /* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 */}} - {{- /* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) */}} - {{- /* explicitly set ngnix default value in order to allow single servers to override the global http value */}} + {{- /* + * nginx currently lacks ability to choose ciphers in TLS 1.3 in + * configuration; see https://trac.nginx.org/nginx/ticket/1529. A + * possible workaround can be modify /etc/ssl/openssl.cnf to change + * it globally (see + * https://trac.nginx.org/nginx/ticket/1529#comment:12). Explicitly + * set ngnix default value in order to allow single servers to + * override the global http value. + */}} ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers off; {{- else if eq .ssl_policy "Mozilla-Intermediate" }} @@ -110,12 +116,19 @@ upstream {{ .Upstream }} { {{- 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 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 */}} + {{- /* + * 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 }} @@ -197,7 +210,10 @@ log_format vhost '$host $remote_addr - $remote_user [$time_local] ' access_log off; -{{- /* Get the SSL_POLICY defined by this container, falling back to "Mozilla-Intermediate" */}} +{{- /* + * 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; @@ -278,17 +294,29 @@ server { {{- $default_host := or ($.Env.DEFAULT_HOST) "" }} {{- $default_server := index (dict $host "" $default_host "default_server") $host }} - {{- /* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}} + {{- /* + * 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 HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} + {{- /* + * 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") }} - {{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}} + {{- /* + * Get the SSL_POLICY defined by containers w/ the same vhost, falling + * back to empty string (use default). + */}} {{- $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} - {{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} + {{- /* + * Get the HSTS defined by containers w/ the same vhost, falling back to + * "max-age=31536000". + */}} {{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }} {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} @@ -301,11 +329,17 @@ server { {{- /* Get the best matching cert by name for the vhost. */}} {{- $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} - {{- /* vhostCert is actually a filename so remove any suffixes since they are added later */}} + {{- /* + * vhostCert is actually a filename so remove any suffixes since they + * are added later. + */}} {{- $vhostCert := trimSuffix ".crt" $vhostCert }} {{- $vhostCert := trimSuffix ".key" $vhostCert }} - {{- /* Use the cert specified on the container or fallback to the best vhost match */}} + {{- /* + * Use the cert specified on the container or fallback to the best vhost + * match. + */}} {{- $cert := (coalesce $certName $vhostCert) }} {{- $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} @@ -395,10 +429,16 @@ server { {{- end }} {{- range $path, $containers := $paths }} - {{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}} + {{- /* + * Get the VIRTUAL_PROTO defined by containers w/ the same + * vhost-vpath, 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" */}} + {{- /* + * 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" }} {{- $upstream := $upstream_name }} {{- $dest := "" }} From 2427b383b5beedc5a04bb2e8fb91c66795539fcc Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 14 Jan 2023 22:59:33 -0500 Subject: [PATCH 035/105] chore: Move global variables to a `$globals` dict Planned future changes will introduce more embedded templates, and the ability to pass the globals to the templates will be useful. --- nginx.tmpl | 116 +++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 4f90880..fb4f76f 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1,12 +1,24 @@ -{{- $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} - -{{- $nginx_proxy_version := coalesce $.Env.NGINX_PROXY_VERSION "" }} -{{- $external_http_port := coalesce $.Env.HTTP_PORT "80" }} -{{- $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" }} -{{- $trust_downstream_proxy := parseBool (coalesce $.Env.TRUST_DOWNSTREAM_PROXY "true") }} +{{- /* + * Global values. Values are stored in this map rather than in individual + * global variables so that the values can be easily passed to embedded + * templates. (Go templates cannot access variables outside of their own + * scope.) + */}} +{{- $globals := dict }} +{{- $_ := set $globals "containers" $ }} +{{- $_ := set $globals "Env" $.Env }} +{{- $_ := set $globals "Docker" $.Docker }} +{{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }} +{{- $_ := set $globals "nginx_proxy_version" (coalesce $globals.Env.NGINX_PROXY_VERSION "") }} +{{- $_ := set $globals "external_http_port" (coalesce $globals.Env.HTTP_PORT "80") }} +{{- $_ := set $globals "external_https_port" (coalesce $globals.Env.HTTPS_PORT "443") }} +{{- $_ := set $globals "debug_all" $globals.Env.DEBUG }} +{{- $_ := set $globals "sha1_upstream_name" (parseBool (coalesce $globals.Env.SHA1_UPSTREAM_NAME "false")) }} +{{- $_ := set $globals "default_root_response" (coalesce $globals.Env.DEFAULT_ROOT "404") }} +{{- $_ := set $globals "trust_downstream_proxy" (parseBool (coalesce $globals.Env.TRUST_DOWNSTREAM_PROXY "true")) }} +{{- $_ := set $globals "access_log" (or (and (not $globals.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} +{{- $_ := set $globals "enable_ipv6" (parseBool (coalesce $globals.Env.ENABLE_IPV6 "false")) }} +{{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }} {{- define "ssl_policy" }} {{- if eq .ssl_policy "Mozilla-Modern" }} @@ -157,26 +169,26 @@ upstream {{ .Upstream }} { } {{- end }} -{{- if ne $nginx_proxy_version "" }} -# nginx-proxy version : {{ $nginx_proxy_version }} +{{- if ne $globals.nginx_proxy_version "" }} +# nginx-proxy version : {{ $globals.nginx_proxy_version }} {{- end }} # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { - default {{ if $trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; + default {{ if $globals.trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; '' $scheme; } map $http_x_forwarded_host $proxy_x_forwarded_host { - default {{ if $trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }}; + default {{ if $globals.trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$http_host{{ end }}; '' $http_host; } # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the # server port the client connected to map $http_x_forwarded_port $proxy_x_forwarded_port { - default {{ if $trust_downstream_proxy }}$http_x_forwarded_port{{ else }}$server_port{{ end }}; + default {{ if $globals.trust_downstream_proxy }}$http_x_forwarded_port{{ else }}$server_port{{ end }}; '' $server_port; } @@ -210,16 +222,11 @@ log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 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) }} +{{- template "ssl_policy" (dict "ssl_policy" $globals.ssl_policy) }} error_log /dev/stderr; -{{- if $.Env.RESOLVERS }} -resolver {{ $.Env.RESOLVERS }}; +{{- if $globals.Env.RESOLVERS }} +resolver {{ $globals.Env.RESOLVERS }}; {{- end }} {{- if (exists "/etc/nginx/proxy.conf") }} @@ -243,23 +250,20 @@ proxy_set_header X-Original-URI $request_uri; proxy_set_header Proxy ""; {{- end }} -{{- $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} - -{{- $enable_ipv6 := parseBool (coalesce $.Env.ENABLE_IPV6 "false") }} server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; - listen {{ $external_http_port }}; -{{- if $enable_ipv6 }} - listen [::]:{{ $external_http_port }}; + listen {{ $globals.external_http_port }}; +{{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_http_port }}; {{- end }} - {{ $access_log }} + {{ $globals.access_log }} return 503; {{- if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} - listen {{ $external_https_port }} ssl http2; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_https_port }} ssl http2; + listen {{ $globals.external_https_port }} ssl http2; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_https_port }} ssl http2; {{- end }} ssl_session_cache shared:SSL:50m; @@ -269,11 +273,11 @@ server { {{- end }} } -{{- range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} +{{- range $host, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }} {{- $host := trim $host }} {{- $is_regexp := hasPrefix "~" $host }} - {{- $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }} + {{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }} {{- $paths := groupBy $containers "Env.VIRTUAL_PATH" }} {{- $nPaths := len $paths }} @@ -288,10 +292,10 @@ server { {{- $upstream = printf "%s-%s" $upstream $sum }} {{- end }} # {{ $host }}{{ $path }} -{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }} +{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $globals.CurrentContainer.Networks "Debug" $globals.debug_all) }} {{- end }} - {{- $default_host := or ($.Env.DEFAULT_HOST) "" }} + {{- $default_host := or ($globals.Env.DEFAULT_HOST) "" }} {{- $default_server := index (dict $host "" $default_host "default_server") $host }} {{- /* @@ -305,7 +309,7 @@ server { * 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") }} + {{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $globals.Env.HTTPS_METHOD "redirect") }} {{- /* * Get the SSL_POLICY defined by containers w/ the same vhost, falling @@ -317,7 +321,7 @@ server { * Get the HSTS defined by containers w/ the same vhost, falling back to * "max-age=31536000". */}} - {{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }} + {{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $globals.Env.HSTS "max-age=31536000") }} {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} {{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} @@ -350,11 +354,11 @@ server { {{- if $server_tokens }} server_tokens {{ $server_tokens }}; {{- end }} - listen {{ $external_http_port }} {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_http_port }} {{ $default_server }}; + listen {{ $globals.external_http_port }} {{ $default_server }}; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; {{- end }} - {{ $access_log }} + {{ $globals.access_log }} # Do not HTTPS redirect Let's Encrypt ACME challenge location ^~ /.well-known/acme-challenge/ { @@ -367,10 +371,10 @@ server { } location / { - {{- if eq $external_https_port "443" }} + {{- if eq $globals.external_https_port "443" }} return 301 https://$host$request_uri; {{- else }} - return 301 https://$host:{{ $external_https_port }}$request_uri; + return 301 https://$host:{{ $globals.external_https_port }}$request_uri; {{- end }} } } @@ -381,17 +385,17 @@ server { {{- if $server_tokens }} server_tokens {{ $server_tokens }}; {{- end }} - {{ $access_log }} + {{ $globals.access_log }} {{- if or (not $is_https) (eq $https_method "noredirect") }} - listen {{ $external_http_port }} {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_http_port }} {{ $default_server }}; + listen {{ $globals.external_http_port }} {{ $default_server }}; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; {{- end }} {{- end }} {{- if $is_https }} - listen {{ $external_https_port }} ssl http2 {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; + listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }}; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }}; {{- end }} {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} @@ -451,7 +455,7 @@ server { {{- end }} {{- if (not (contains $paths "/")) }} location / { - return {{ $default_root_response }}; + return {{ $globals.default_root_response }}; } {{- end }} } @@ -462,11 +466,11 @@ server { {{- if $server_tokens }} server_tokens {{ $server_tokens }}; {{- end }} - listen {{ $external_https_port }} ssl http2 {{ $default_server }}; - {{- if $enable_ipv6 }} - listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; + listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }}; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }}; {{- end }} - {{ $access_log }} + {{ $globals.access_log }} return 500; ssl_certificate /etc/nginx/certs/default.crt; From b16ad278780bc011aaff71c6233520aa95cf8619 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 17 Jan 2023 08:17:02 +0100 Subject: [PATCH 036/105] build: dockergen 0.9.2 -> 0.9.3 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index fa227cf..54e3a67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.9.2 +ARG DOCKER_GEN_VERSION=0.9.3 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 5473e53..da8f06e 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.9.2 +ARG DOCKER_GEN_VERSION=0.9.3 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 26b0b05f730cd6dfdb8c77f9ee18fabce3a82019 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 16 Jan 2023 19:22:20 -0500 Subject: [PATCH 037/105] tests: Fix `test_debug/*` tests when IPv6 is enabled --- test/test_debug/test_proxy-debug-flag.py | 8 ++++---- test/test_debug/test_server-debug-flag.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_debug/test_proxy-debug-flag.py b/test/test_debug/test_proxy-debug-flag.py index af7f73a..6430d84 100644 --- a/test/test_debug/test_proxy-debug-flag.py +++ b/test/test_debug/test_proxy-debug-flag.py @@ -3,10 +3,10 @@ import re def test_debug_info_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): conf = nginxproxy.get_conf().decode('ASCII') - assert re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \}\]", conf) or \ - re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \}\]", conf) - assert re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+82\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+83\s+tcp \}\]", conf) or \ - re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+83\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+82\s+tcp \}\]", conf) + assert re.search(r"# Exposed ports: \[\{[^}]+\s+80\s+tcp \} \{[^}]+\s+81\s+tcp \}\]", conf) or \ + re.search(r"# Exposed ports: \[\{[^}]+\s+81\s+tcp \} \{[^}]+\s+80\s+tcp \}\]", conf) + assert re.search(r"# Exposed ports: \[\{[^}]+\s+82\s+tcp \} \{[^}]+\s+83\s+tcp \}\]", conf) or \ + re.search(r"# Exposed ports: \[\{[^}]+\s+83\s+tcp \} \{[^}]+\s+82\s+tcp \}\]", conf) assert "# Default virtual port: 80" in conf assert "# VIRTUAL_PORT: 82" in conf assert conf.count("# /!\\ Virtual port not exposed") == 1 diff --git a/test/test_debug/test_server-debug-flag.py b/test/test_debug/test_server-debug-flag.py index 50ae737..c635175 100644 --- a/test/test_debug/test_server-debug-flag.py +++ b/test/test_debug/test_server-debug-flag.py @@ -3,6 +3,6 @@ import re def test_debug_info_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): conf = nginxproxy.get_conf().decode('ASCII') - assert re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \}\]", conf) or \ - re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \}\]", conf) + assert re.search(r"# Exposed ports: \[\{[^}]+\s+80\s+tcp \} \{[^}]+\s+81\s+tcp \}\]", conf) or \ + re.search(r"# Exposed ports: \[\{[^}]+\s+81\s+tcp \} \{[^}]+\s+80\s+tcp \}\]", conf) assert conf.count("# Exposed ports: [{") == 1 From d56b5b370d8aef801ec2c1d7c13dfcf2a6f23c03 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 16 Jan 2023 23:37:48 -0500 Subject: [PATCH 038/105] tests: Whitespace fixes --- test/pytest.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pytest.sh b/test/pytest.sh index 28275e5..beac0af 100755 --- a/test/pytest.sh +++ b/test/pytest.sh @@ -16,10 +16,10 @@ ARGS=("$@") echo "> Building nginx-proxy-tester image..." docker build -t nginx-proxy-tester -f "${DIR}/requirements/Dockerfile-nginx-proxy-tester" "${DIR}/requirements" -# run the nginx-proxy-tester container setting the correct value for the working dir in order for +# run the nginx-proxy-tester container setting the correct value for the working dir in order for # docker-compose to work properly when run from within that container. exec docker run --rm -it --name "nginx-proxy-pytest" \ --volume "/var/run/docker.sock:/var/run/docker.sock" \ --volume "${DIR}:${DIR}" \ --workdir "${DIR}" \ - nginx-proxy-tester "${ARGS[@]}" \ No newline at end of file + nginx-proxy-tester "${ARGS[@]}" From 55cfae963689f3b583c17ca22831361e5b8c901d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 16 Jan 2023 23:39:20 -0500 Subject: [PATCH 039/105] tests: Avoid unnecessary bashisms --- test/pytest.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/pytest.sh b/test/pytest.sh index beac0af..2ac281d 100755 --- a/test/pytest.sh +++ b/test/pytest.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh ############################################################################### # # # This script is meant to run the test suite from a Docker container. # @@ -9,8 +9,7 @@ ############################################################################### # Returns the absolute directory path to this script -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ARGS=("$@") +DIR=$(cd "${0%/*}" && pwd) # check requirements echo "> Building nginx-proxy-tester image..." @@ -22,4 +21,4 @@ exec docker run --rm -it --name "nginx-proxy-pytest" \ --volume "/var/run/docker.sock:/var/run/docker.sock" \ --volume "${DIR}:${DIR}" \ --workdir "${DIR}" \ - nginx-proxy-tester "${ARGS[@]}" + nginx-proxy-tester "$@" From 569953521aa5eca5261d97f08785b5a9edf03880 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 16 Jan 2023 23:49:32 -0500 Subject: [PATCH 040/105] tests: Exit non-zero if creation of nginx-proxy-tester image fails --- test/pytest.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/pytest.sh b/test/pytest.sh index 2ac281d..8e46ce0 100755 --- a/test/pytest.sh +++ b/test/pytest.sh @@ -9,11 +9,14 @@ ############################################################################### # Returns the absolute directory path to this script -DIR=$(cd "${0%/*}" && pwd) +DIR=$(cd "${0%/*}" && pwd) || exit 1 # check requirements echo "> Building nginx-proxy-tester image..." -docker build -t nginx-proxy-tester -f "${DIR}/requirements/Dockerfile-nginx-proxy-tester" "${DIR}/requirements" +docker build -t nginx-proxy-tester \ + -f "${DIR}/requirements/Dockerfile-nginx-proxy-tester" \ + "${DIR}/requirements" \ + || exit 1 # run the nginx-proxy-tester container setting the correct value for the working dir in order for # docker-compose to work properly when run from within that container. From 486addd14403f016ebd829e4611d81e6b830c9f8 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 16 Jan 2023 23:53:51 -0500 Subject: [PATCH 041/105] tests: Bind-mount the entire nginx-proxy directory in the container --- test/pytest.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/pytest.sh b/test/pytest.sh index 8e46ce0..9088d10 100755 --- a/test/pytest.sh +++ b/test/pytest.sh @@ -9,13 +9,14 @@ ############################################################################### # Returns the absolute directory path to this script -DIR=$(cd "${0%/*}" && pwd) || exit 1 +TESTDIR=$(cd "${0%/*}" && pwd) || exit 1 +DIR=$(cd "${TESTDIR}/.." && pwd) || exit 1 # check requirements echo "> Building nginx-proxy-tester image..." docker build -t nginx-proxy-tester \ - -f "${DIR}/requirements/Dockerfile-nginx-proxy-tester" \ - "${DIR}/requirements" \ + -f "${TESTDIR}/requirements/Dockerfile-nginx-proxy-tester" \ + "${TESTDIR}/requirements" \ || exit 1 # run the nginx-proxy-tester container setting the correct value for the working dir in order for @@ -23,5 +24,5 @@ docker build -t nginx-proxy-tester \ exec docker run --rm -it --name "nginx-proxy-pytest" \ --volume "/var/run/docker.sock:/var/run/docker.sock" \ --volume "${DIR}:${DIR}" \ - --workdir "${DIR}" \ + --workdir "${TESTDIR}" \ nginx-proxy-tester "$@" From 92e1a6567ead9e2cdb7dea17774b0a20d187af6a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 17 Jan 2023 00:02:02 -0500 Subject: [PATCH 042/105] tests: Remove extraction of `nginx.tmpl` (now unnecessary) --- test/test_dockergen/test_dockergen_v2.py | 36 ++--------------------- test/test_dockergen/test_dockergen_v2.yml | 2 +- test/test_dockergen/test_dockergen_v3.py | 33 ++------------------- test/test_dockergen/test_dockergen_v3.yml | 2 +- 4 files changed, 6 insertions(+), 67 deletions(-) diff --git a/test/test_dockergen/test_dockergen_v2.py b/test/test_dockergen/test_dockergen_v2.py index 43b1431..dbb15d4 100644 --- a/test/test_dockergen/test_dockergen_v2.py +++ b/test/test_dockergen/test_dockergen_v2.py @@ -1,41 +1,9 @@ -import os -import docker -import logging -import pytest - - -@pytest.fixture(scope="module") -def nginx_tmpl(): - """ - pytest fixture which extracts the the nginx config template from - the nginxproxy/nginx-proxy:test image - """ - script_dir = os.path.dirname(__file__) - logging.info("extracting nginx.tmpl from nginxproxy/nginx-proxy:test") - docker_client = docker.from_env() - print( - docker_client.containers.run( - image="nginxproxy/nginx-proxy:test", - remove=True, - volumes=["{current_dir}:{current_dir}".format(current_dir=script_dir)], - entrypoint="sh", - command='-xc "cp /app/nginx.tmpl {current_dir} && chmod 777 {current_dir}/nginx.tmpl"'.format( - current_dir=script_dir - ), - stderr=True, - ) - ) - yield - logging.info("removing nginx.tmpl") - os.remove(os.path.join(script_dir, "nginx.tmpl")) - - -def test_unknown_virtual_host_is_503(nginx_tmpl, docker_compose, nginxproxy): +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx.container.docker/") assert r.status_code == 503 -def test_forwards_to_whoami(nginx_tmpl, docker_compose, nginxproxy): +def test_forwards_to_whoami(docker_compose, nginxproxy): r = nginxproxy.get("http://whoami.nginx.container.docker/") assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami") diff --git a/test/test_dockergen/test_dockergen_v2.yml b/test/test_dockergen/test_dockergen_v2.yml index b1f443c..36ee1c1 100644 --- a/test/test_dockergen/test_dockergen_v2.yml +++ b/test/test_dockergen/test_dockergen_v2.yml @@ -14,7 +14,7 @@ services: - nginx volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl web: image: web diff --git a/test/test_dockergen/test_dockergen_v3.py b/test/test_dockergen/test_dockergen_v3.py index 67561bf..b696e6c 100644 --- a/test/test_dockergen/test_dockergen_v3.py +++ b/test/test_dockergen/test_dockergen_v3.py @@ -1,8 +1,5 @@ -import os import docker -import logging import pytest -import re from distutils.version import LooseVersion @@ -13,38 +10,12 @@ pytestmark = pytest.mark.skipif( ) -@pytest.fixture(scope="module") -def nginx_tmpl(): - """ - pytest fixture which extracts the the nginx config template from - the nginxproxy/nginx-proxy:test image - """ - script_dir = os.path.dirname(__file__) - logging.info("extracting nginx.tmpl from nginxproxy/nginx-proxy:test") - docker_client = docker.from_env() - print( - docker_client.containers.run( - image="nginxproxy/nginx-proxy:test", - remove=True, - volumes=["{current_dir}:{current_dir}".format(current_dir=script_dir)], - entrypoint="sh", - command='-xc "cp /app/nginx.tmpl {current_dir} && chmod 777 {current_dir}/nginx.tmpl"'.format( - current_dir=script_dir - ), - stderr=True, - ) - ) - yield - logging.info("removing nginx.tmpl") - os.remove(os.path.join(script_dir, "nginx.tmpl")) - - -def test_unknown_virtual_host_is_503(nginx_tmpl, docker_compose, nginxproxy): +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx.container.docker/") assert r.status_code == 503 -def test_forwards_to_whoami(nginx_tmpl, docker_compose, nginxproxy): +def test_forwards_to_whoami(docker_compose, nginxproxy): r = nginxproxy.get("http://whoami.nginx.container.docker/") assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami") diff --git a/test/test_dockergen/test_dockergen_v3.yml b/test/test_dockergen/test_dockergen_v3.yml index 8339273..8b0411c 100644 --- a/test/test_dockergen/test_dockergen_v3.yml +++ b/test/test_dockergen/test_dockergen_v3.yml @@ -11,7 +11,7 @@ services: command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl - nginx_conf:/etc/nginx/conf.d web: From 8fbc8514ef3cd4a464c0ed7315590f8dee8b972c Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 17 Jan 2023 21:23:08 -0500 Subject: [PATCH 043/105] feat: Unconditionally produce debug comments Rationale for eliminating the check to see if the `DEBUG` environment variable holds a true value: * The `DEBUG` environment variable might be set on a container (for purposes specific to that container, not `nginx-proxy`) to a value that cannot be parsed as a bool, which would break `nginx-proxy`. * It simplifies the template. * It eliminates a cold code path. * It avoids heisenbugs. * It makes debugging easier for users. Also delete the debug info tests, as they are fragile and they provide limited value. Alternatively, we could avoid collision with the container's use of the `DEBUG` environment variable by using a container label [1] such as `com.google.nginx-proxy.nginx-proxy.debug`. I think doing so has dubious value, especially if we want to attempt backwards compatibility with the `DEBUG` environment variable. Fixes #2139 [1] https://docs.docker.com/engine/reference/commandline/run/#-set-metadata-on-container--l---label---label-file Co-authored-by: Nicolas Duchon --- README.md | 7 ++--- nginx.tmpl | 9 ++----- .../test_deleted_cert/docker-compose.yml | 4 +-- test/test_debug/test_proxy-debug-flag.py | 12 --------- test/test_debug/test_proxy-debug-flag.yml | 26 ------------------- test/test_debug/test_server-debug-flag.py | 8 ------ test/test_debug/test_server-debug-flag.yml | 25 ------------------ 7 files changed, 7 insertions(+), 84 deletions(-) delete mode 100644 test/test_debug/test_proxy-debug-flag.py delete mode 100644 test/test_debug/test_proxy-debug-flag.yml delete mode 100644 test/test_debug/test_server-debug-flag.py delete mode 100644 test/test_debug/test_server-debug-flag.yml diff --git a/README.md b/README.md index 0ca08d7..82b2dcf 100644 --- a/README.md +++ b/README.md @@ -490,12 +490,13 @@ Please note that using regular expressions in `VIRTUAL_HOST` will always result ### Troubleshooting -In case you can't access your VIRTUAL_HOST, set `DEBUG=true` in the client container's environment and have a look at the generated nginx configuration file `/etc/nginx/conf.d/default.conf`: +If you can't access your `VIRTUAL_HOST`, inspect the generated nginx configuration: ```console -docker exec cat /etc/nginx/conf.d/default.conf +docker exec nginx -T ``` -Especially at `upstream` definition blocks which should look like: + +Pay attention to the `upstream` definition blocks, which should look like this: ```Nginx # foo.example.com diff --git a/nginx.tmpl b/nginx.tmpl index fb4f76f..2abfb7c 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -12,7 +12,6 @@ {{- $_ := set $globals "nginx_proxy_version" (coalesce $globals.Env.NGINX_PROXY_VERSION "") }} {{- $_ := set $globals "external_http_port" (coalesce $globals.Env.HTTP_PORT "80") }} {{- $_ := set $globals "external_https_port" (coalesce $globals.Env.HTTPS_PORT "443") }} -{{- $_ := set $globals "debug_all" $globals.Env.DEBUG }} {{- $_ := set $globals "sha1_upstream_name" (parseBool (coalesce $globals.Env.SHA1_UPSTREAM_NAME "false")) }} {{- $_ := set $globals "default_root_response" (coalesce $globals.Env.DEFAULT_ROOT "404") }} {{- $_ := set $globals "trust_downstream_proxy" (parseBool (coalesce $globals.Env.TRUST_DOWNSTREAM_PROXY "true")) }} @@ -106,22 +105,18 @@ {{- define "upstream" }} {{- $networks := .Networks }} - {{- $debug_all := .Debug }} upstream {{ .Upstream }} { {{- $server_found := false }} {{- range $container := .Containers }} - {{- $debug := parseBool (coalesce $container.Env.DEBUG $debug_all "false") }} {{- /* 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 }} + {{- if not $address }} # /!\ Virtual port not exposed - {{- end }} {{- end }} {{- range $knownNetwork := $networks }} {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} @@ -292,7 +287,7 @@ server { {{- $upstream = printf "%s-%s" $upstream $sum }} {{- end }} # {{ $host }}{{ $path }} -{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $globals.CurrentContainer.Networks "Debug" $globals.debug_all) }} +{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $globals.CurrentContainer.Networks) }} {{- end }} {{- $default_host := or ($globals.Env.DEFAULT_HOST) "" }} diff --git a/test/stress_tests/test_deleted_cert/docker-compose.yml b/test/stress_tests/test_deleted_cert/docker-compose.yml index 33c92a7..a42abac 100644 --- a/test/stress_tests/test_deleted_cert/docker-compose.yml +++ b/test/stress_tests/test_deleted_cert/docker-compose.yml @@ -10,8 +10,6 @@ web: reverseproxy: image: nginxproxy/nginx-proxy:test container_name: reverseproxy - environment: - DEBUG: "true" volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./tmp_certs:/etc/nginx/certs:ro \ No newline at end of file + - ./tmp_certs:/etc/nginx/certs:ro diff --git a/test/test_debug/test_proxy-debug-flag.py b/test/test_debug/test_proxy-debug-flag.py deleted file mode 100644 index 6430d84..0000000 --- a/test/test_debug/test_proxy-debug-flag.py +++ /dev/null @@ -1,12 +0,0 @@ -import pytest -import re - -def test_debug_info_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): - conf = nginxproxy.get_conf().decode('ASCII') - assert re.search(r"# Exposed ports: \[\{[^}]+\s+80\s+tcp \} \{[^}]+\s+81\s+tcp \}\]", conf) or \ - re.search(r"# Exposed ports: \[\{[^}]+\s+81\s+tcp \} \{[^}]+\s+80\s+tcp \}\]", conf) - assert re.search(r"# Exposed ports: \[\{[^}]+\s+82\s+tcp \} \{[^}]+\s+83\s+tcp \}\]", conf) or \ - re.search(r"# Exposed ports: \[\{[^}]+\s+83\s+tcp \} \{[^}]+\s+82\s+tcp \}\]", conf) - assert "# Default virtual port: 80" in conf - assert "# VIRTUAL_PORT: 82" in conf - assert conf.count("# /!\\ Virtual port not exposed") == 1 diff --git a/test/test_debug/test_proxy-debug-flag.yml b/test/test_debug/test_proxy-debug-flag.yml deleted file mode 100644 index f930da3..0000000 --- a/test/test_debug/test_proxy-debug-flag.yml +++ /dev/null @@ -1,26 +0,0 @@ -web1: - image: web - expose: - - "80" - - "81" - environment: - WEB_PORTS: "80 81" - VIRTUAL_HOST: "web1.nginx-proxy.tld" - VIRTUAL_PORT: "82" - -web2: - image: web - expose: - - "82" - - "83" - environment: - WEB_PORTS: "82 83" - VIRTUAL_HOST: "web2.nginx-proxy.tld" - VIRTUAL_PORT: "82" - -sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - DEBUG: "true" diff --git a/test/test_debug/test_server-debug-flag.py b/test/test_debug/test_server-debug-flag.py deleted file mode 100644 index c635175..0000000 --- a/test/test_debug/test_server-debug-flag.py +++ /dev/null @@ -1,8 +0,0 @@ -import pytest -import re - -def test_debug_info_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): - conf = nginxproxy.get_conf().decode('ASCII') - assert re.search(r"# Exposed ports: \[\{[^}]+\s+80\s+tcp \} \{[^}]+\s+81\s+tcp \}\]", conf) or \ - re.search(r"# Exposed ports: \[\{[^}]+\s+81\s+tcp \} \{[^}]+\s+80\s+tcp \}\]", conf) - assert conf.count("# Exposed ports: [{") == 1 diff --git a/test/test_debug/test_server-debug-flag.yml b/test/test_debug/test_server-debug-flag.yml deleted file mode 100644 index 89bb6b5..0000000 --- a/test/test_debug/test_server-debug-flag.yml +++ /dev/null @@ -1,25 +0,0 @@ -web1: - image: web - expose: - - "80" - - "81" - environment: - WEB_PORTS: "80 81" - VIRTUAL_HOST: "web1.nginx-proxy.tld" - VIRTUAL_PORT: "82" - DEBUG: "true" - -web2: - image: web - expose: - - "82" - - "83" - environment: - WEB_PORTS: "82 83" - VIRTUAL_HOST: "web2.nginx-proxy.tld" - VIRTUAL_PORT: "82" - -sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro From 8df67cdde8cc3216a2f07ab1b35446655d63607d Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 23 Jan 2023 23:14:42 +0100 Subject: [PATCH 044/105] build: dockergen 0.9.3 -> 0.9.4 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 54e3a67..548e2e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.9.3 +ARG DOCKER_GEN_VERSION=0.9.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 da8f06e..5dc34a5 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.9.3 +ARG DOCKER_GEN_VERSION=0.9.4 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From fa52426d5449766b3ffd6187536a65d465ebfaa3 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 23 Jan 2023 23:24:58 +0100 Subject: [PATCH 045/105] ci: set Dependabot commit prefixs --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 81ddf73..eb6a749 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,8 @@ updates: directory: "/" schedule: interval: "daily" + commit-message: + prefix: "build" labels: - "type/build" - "scope/dockerfile" @@ -14,5 +16,7 @@ updates: directory: "/test/requirements" schedule: interval: "daily" + commit-message: + prefix: "ci" labels: - "type/ci" From f8ae0a4b0020e3e59a3ba02ad889206d00d85857 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 18 Jan 2023 18:27:35 -0500 Subject: [PATCH 046/105] feat: `DEFAULT_ROOT=none` disables the default `location /` block --- README.md | 15 +++++++++++---- nginx.tmpl | 2 +- test/test_default-root-none.py | 8 ++++++++ test/test_default-root-none.yml | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 test/test_default-root-none.py create mode 100644 test/test_default-root-none.yml diff --git a/README.md b/README.md index 82b2dcf..951e2f6 100644 --- a/README.md +++ b/README.md @@ -152,10 +152,17 @@ The filename of the previous example would be `example.tld_8610f6c344b4096614eab 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 `DEFAULT_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: +Exception: If this is set to the string `none`, no default `location /` directive will be generated. This makes it possible for you to provide your own `location /` directive in your [`/etc/nginx/vhost.d/VIRTUAL_HOST`](#per-virtual_host) or [`/etc/nginx/vhost.d/default`](#per-virtual_host-default-configuration) files. + +If unspecified, `DEFAULT_ROOT` defaults to `404`. + +Examples (YAML syntax): + + * `DEFAULT_ROOT: "none"` prevents `nginx-proxy` from generating a default `location /` directive. + * `DEFAULT_ROOT: "418"` returns a 418 error page instead of the normal 404 one. + * `DEFAULT_ROOT: "301 https://github.com/nginx-proxy/nginx-proxy/blob/main/README.md"` redirects the client 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. For example, 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` diff --git a/nginx.tmpl b/nginx.tmpl index 2abfb7c..18a2d23 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -448,7 +448,7 @@ server { {{- end }} {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} {{- end }} - {{- if (not (contains $paths "/")) }} + {{- if and (not (contains $paths "/")) (ne $globals.default_root_response "none")}} location / { return {{ $globals.default_root_response }}; } diff --git a/test/test_default-root-none.py b/test/test_default-root-none.py new file mode 100644 index 0000000..742d87b --- /dev/null +++ b/test/test_default-root-none.py @@ -0,0 +1,8 @@ +import re + + +def test_default_root_none(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode() + assert re.search(r"(?m)^\s*location\s+/path\s+\{", conf) + assert not re.search(r"(?m)^\s*location\s+/\s+\{", conf) + diff --git a/test/test_default-root-none.yml b/test/test_default-root-none.yml new file mode 100644 index 0000000..309d2ab --- /dev/null +++ b/test/test_default-root-none.yml @@ -0,0 +1,15 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + DEFAULT_ROOT: none + web: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: web.nginx-proxy.test + VIRTUAL_PATH: /path From 8346b68a288ed20eab50a8dbeb959ac6e2ef5f15 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 21 Jan 2023 19:11:28 -0500 Subject: [PATCH 047/105] fix: Ignore `VIRTUAL_HOST` set to the empty string Fixes #2144 --- nginx.tmpl | 4 ++++ test/test_vhost-empty-string.py | 10 +++++++++ test/test_vhost-empty-string.yml | 37 ++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 test/test_vhost-empty-string.py create mode 100644 test/test_vhost-empty-string.yml diff --git a/nginx.tmpl b/nginx.tmpl index 18a2d23..f2e2bcf 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -271,6 +271,10 @@ server { {{- range $host, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }} {{- $host := trim $host }} + {{- if not $host }} + {{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}} + {{- continue }} + {{- end }} {{- $is_regexp := hasPrefix "~" $host }} {{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }} diff --git a/test/test_vhost-empty-string.py b/test/test_vhost-empty-string.py new file mode 100644 index 0000000..707becb --- /dev/null +++ b/test/test_vhost-empty-string.py @@ -0,0 +1,10 @@ +import re + + +def test_vhost_empty_string(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode() + assert re.search(r"(?m)^\s*server_name\s+web2\.nginx-proxy\.test\s*;", conf) + assert re.search(r"(?m)^\s*server_name\s+web3\.nginx-proxy\.test\s*;", conf) + assert re.search(r"(?m)^\s*server_name\s+web4a\.nginx-proxy\.test\s*;", conf) + assert re.search(r"(?m)^\s*server_name\s+web4b\.nginx-proxy\.test\s*;", conf) + assert not re.search(r"(?m)^\s*server_name\s*;", conf) diff --git a/test/test_vhost-empty-string.yml b/test/test_vhost-empty-string.yml new file mode 100644 index 0000000..83dd554 --- /dev/null +++ b/test/test_vhost-empty-string.yml @@ -0,0 +1,37 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + # The space is intentional (should be trimmed). + VIRTUAL_HOST: " " + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + # The space is intentional (should be trimmed). + VIRTUAL_HOST: "web2.nginx-proxy.test ," + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + # The space is intentional (should be trimmed). + VIRTUAL_HOST: " ,web3.nginx-proxy.test" + web4: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + # The spaces are intentional (should be trimmed). + VIRTUAL_HOST: "web4a.nginx-proxy.test, , web4b.nginx-proxy.test" From 07cc80ac6bb2d390b4ba2b8e0332101a19df79e3 Mon Sep 17 00:00:00 2001 From: Vincent Herlemont Date: Fri, 27 Jan 2023 18:28:40 +0100 Subject: [PATCH 048/105] feat: Support LOG_FORMAT env variable (#2151) --- nginx.tmpl | 5 +---- test/test_log_format.py | 11 +++++++++++ test/test_log_format.yml | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 test/test_log_format.py create mode 100644 test/test_log_format.yml diff --git a/nginx.tmpl b/nginx.tmpl index f2e2bcf..54872e4 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -210,10 +210,7 @@ map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl { gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; -log_format vhost '$host $remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" ' - '"$upstream_addr"'; +log_format vhost '{{ or $globals.Env.LOG_FORMAT "$host $remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$upstream_addr\"" }}'; access_log off; diff --git a/test/test_log_format.py b/test/test_log_format.py new file mode 100644 index 0000000..589f0c7 --- /dev/null +++ b/test/test_log_format.py @@ -0,0 +1,11 @@ +import pytest + +def test_log_format(docker_compose, nginxproxy): + r = nginxproxy.get("http://nginx-proxy.test/port") + assert r.status_code == 200 + assert r.text == "answer from port 81\n" + sut_container = docker_compose.containers.get("sut") + docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False) + docker_logs = docker_logs.decode("utf-8").splitlines() + docker_logs = [line for line in docker_logs if "GET /port" in line] + assert "request_time=" in docker_logs[0] diff --git a/test/test_log_format.yml b/test/test_log_format.yml new file mode 100644 index 0000000..ef3bbf6 --- /dev/null +++ b/test/test_log_format.yml @@ -0,0 +1,15 @@ +web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: nginx-proxy.test + +sut: + container_name: sut + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + LOG_FORMAT: "$$remote_addr - $$remote_user [$$time_local] \"$$request\" $$status $$body_bytes_sent \"$$http_referer\" \"$$http_user_agent\" request_time=$$request_time $$upstream_response_time" From aa50116272e3aa45ae85101757fa99bdd113712d Mon Sep 17 00:00:00 2001 From: Vincent Herlemont Date: Fri, 27 Jan 2023 23:16:49 +0100 Subject: [PATCH 049/105] Documentation custom log format. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 951e2f6..51d7b80 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,11 @@ If you would like to connect to FastCGI backend, set `VIRTUAL_PROTO=fastcgi` on If you use fastcgi,you can set `VIRTUAL_ROOT=xxx` for your root directory +### Custom log format + +If you want to use a custom log format, you can set `LOG_FORMAT=xxx` on the proxy container. + +With docker compose take care to escape the `$` character with `$$` to avoid variable interpolation. Example: `$remote_addr` becomes `$$remote_addr`. ### Default Host From e97bf606c8c45e708f5617f1f9a6e1dd9e99a243 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 25 Jan 2023 17:49:23 -0500 Subject: [PATCH 050/105] chore: Move version comment to the top of the template to ensure that the version is always the first output line. Also, always output `# nginx-proxy`, even if the version isn't known. This makes it easier to find the start of the generated config in the output of `nginx -T`. --- nginx.tmpl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 54872e4..b44f4d5 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1,3 +1,5 @@ +# nginx-proxy{{ if $.Env.NGINX_PROXY_VERSION }} version : {{ $.Env.NGINX_PROXY_VERSION }}{{ end }} + {{- /* * Global values. Values are stored in this map rather than in individual * global variables so that the values can be easily passed to embedded @@ -9,7 +11,6 @@ {{- $_ := set $globals "Env" $.Env }} {{- $_ := set $globals "Docker" $.Docker }} {{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }} -{{- $_ := set $globals "nginx_proxy_version" (coalesce $globals.Env.NGINX_PROXY_VERSION "") }} {{- $_ := set $globals "external_http_port" (coalesce $globals.Env.HTTP_PORT "80") }} {{- $_ := set $globals "external_https_port" (coalesce $globals.Env.HTTPS_PORT "443") }} {{- $_ := set $globals "sha1_upstream_name" (parseBool (coalesce $globals.Env.SHA1_UPSTREAM_NAME "false")) }} @@ -164,10 +165,6 @@ upstream {{ .Upstream }} { } {{- end }} -{{- if ne $globals.nginx_proxy_version "" }} -# nginx-proxy version : {{ $globals.nginx_proxy_version }} -{{- end }} - # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { From 2760ead49041fa6dafaab6fd55120e41c3c1ec28 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 26 Jan 2023 16:39:13 -0500 Subject: [PATCH 051/105] chore: Remove warning comment when port is not exposed Exposing ports is largely deprecated because it doesn't actually do anything in Docker. --- nginx.tmpl | 3 --- 1 file changed, 3 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index b44f4d5..0982aaa 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -116,9 +116,6 @@ upstream {{ .Upstream }} { # Exposed ports: {{ $container.Addresses }} # Default virtual port: {{ $defaultPort }} # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} - {{- if not $address }} - # /!\ Virtual port not exposed - {{- end }} {{- range $knownNetwork := $networks }} {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} {{- if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} From 5a8a6ceae2cee36b3f60168bc496fa9ec568f3de Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 26 Jan 2023 16:53:52 -0500 Subject: [PATCH 052/105] chore: Improve debug comments in `upstream` template --- nginx.tmpl | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 0982aaa..9251948 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -109,17 +109,26 @@ upstream {{ .Upstream }} { {{- $server_found := false }} {{- range $container := .Containers }} + # Container: {{ $container.Name }} {{- /* 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 }} - # Exposed ports: {{ $container.Addresses }} - # Default virtual port: {{ $defaultPort }} - # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} + # Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} + # Default virtual port: {{ $defaultPort }} + # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} + {{- if $container.Node.ID }} + # Swarm node name: {{ $container.Node.Name }} + {{- end }} {{- range $knownNetwork := $networks }} + # Container network reachability from {{ $knownNetwork.Name }}: {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} - {{- if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} - ## Can be connected with "{{ $containerNetwork.Name }}" network + {{- if eq $containerNetwork.Name "ingress" }} + # {{ $containerNetwork.Name }} (ignored) + {{- else if and (ne $knownNetwork.Name $containerNetwork.Name) (ne $knownNetwork.Name "host") }} + # {{ $containerNetwork.Name }} (unreachable) + {{- else }} + # {{ $containerNetwork.Name }} (reachable) {{- if $address }} {{- /* * If we got the containers from swarm and this @@ -128,7 +137,6 @@ upstream {{ .Upstream }} { */}} {{- 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 @@ -136,21 +144,19 @@ upstream {{ .Upstream }} { */}} {{- 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! + # /!\ No IP for this network! {{- end }} {{- end }} - {{- else }} - # Cannot connect to network '{{ $containerNetwork.Name }}' of this container {{- end }} + {{- else }} + # (none) {{- end }} {{- end }} {{- end }} From daeed502cb639e0ffdeca25522af1538e125ccb5 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 26 Jan 2023 18:53:50 -0500 Subject: [PATCH 053/105] feat: Add a warning comment if the container port is published --- nginx.tmpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nginx.tmpl b/nginx.tmpl index 9251948..f236000 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -117,6 +117,11 @@ upstream {{ .Upstream }} { # Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} # Default virtual port: {{ $defaultPort }} # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} + {{- if and $address $address.HostPort }} + # /!\ WARNING: Virtual port published on host. Clients might be able to + # bypass nginx-proxy and access the container's server + # directly. + {{- end }} {{- if $container.Node.ID }} # Swarm node name: {{ $container.Node.Name }} {{- end }} From bcec2d9075c053b9930de52a32a712d69961a213 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 26 Jan 2023 19:13:36 -0500 Subject: [PATCH 054/105] chore: Refactor `upstream` template for readability In particular, reduce the nesting depth to make it easier to understand what the code is doing by: * converting an $O(nm)$ nested loop into two serial $O(n)+O(m)$ loops, and * consolidating similar nested `if` cases. --- nginx.tmpl | 72 +++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index f236000..310b60e 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -19,6 +19,12 @@ {{- $_ := set $globals "access_log" (or (and (not $globals.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} {{- $_ := set $globals "enable_ipv6" (parseBool (coalesce $globals.Env.ENABLE_IPV6 "false")) }} {{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }} +{{- $_ := set $globals "networks" (dict) }} +# networks available to nginx-proxy: +{{- range $globals.CurrentContainer.Networks }} + {{- $_ := set $globals.networks .Name . }} +# {{ .Name }} +{{- end }} {{- define "ssl_policy" }} {{- if eq .ssl_policy "Mozilla-Modern" }} @@ -113,11 +119,11 @@ upstream {{ .Upstream }} { {{- /* 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 }} + {{- $addr_obj := where $container.Addresses "Port" $port | first }} # Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} # Default virtual port: {{ $defaultPort }} # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} - {{- if and $address $address.HostPort }} + {{- if and $addr_obj $addr_obj.HostPort }} # /!\ WARNING: Virtual port published on host. Clients might be able to # bypass nginx-proxy and access the container's server # directly. @@ -125,44 +131,32 @@ upstream {{ .Upstream }} { {{- if $container.Node.ID }} # Swarm node name: {{ $container.Node.Name }} {{- end }} - {{- range $knownNetwork := $networks }} - # Container network reachability from {{ $knownNetwork.Name }}: - {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} - {{- if eq $containerNetwork.Name "ingress" }} + # Container networks: + {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} + {{- if eq $containerNetwork.Name "ingress" }} # {{ $containerNetwork.Name }} (ignored) - {{- else if and (ne $knownNetwork.Name $containerNetwork.Name) (ne $knownNetwork.Name "host") }} - # {{ $containerNetwork.Name }} (unreachable) - {{- else }} - # {{ $containerNetwork.Name }} (reachable) - {{- 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 }} - 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 }} - server {{ $containerNetwork.IP }}:{{ $address.Port }}; - {{- end }} - {{- else if $containerNetwork }} - {{- if $containerNetwork.IP }} - {{- $server_found = true }} - server {{ $containerNetwork.IP }}:{{ $port }}; - {{- else }} - # /!\ No IP for this network! - {{- end }} - {{- end }} - {{- end }} - {{- else }} - # (none) + {{- continue }} {{- end }} + {{- if and (not (index $networks $containerNetwork.Name)) (not $networks.host) }} + # {{ $containerNetwork.Name }} (unreachable) + {{- continue }} + {{- end }} + # {{ $containerNetwork.Name }} (reachable) + {{- /* + * 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 $addr_obj $addr_obj.HostPort }} + {{- $server_found = true }} + server {{ $container.Node.Address.IP }}:{{ $addr_obj.HostPort }}; + {{- else if and $containerNetwork $containerNetwork.IP }} + {{- $server_found = true }} + server {{ $containerNetwork.IP }}:{{ $port }}; + {{- else }} + # /!\ No IP for this network! + {{- end }} + {{- else }} + # (none) {{- end }} {{- end }} {{- /* nginx-proxy/nginx-proxy#1105 */}} @@ -293,7 +287,7 @@ server { {{- $upstream = printf "%s-%s" $upstream $sum }} {{- end }} # {{ $host }}{{ $path }} -{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $globals.CurrentContainer.Networks) }} +{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $globals.networks) }} {{- end }} {{- $default_host := or ($globals.Env.DEFAULT_HOST) "" }} From 6162427c4533a7c48881a5e666e206ba9b87084c Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 28 Jan 2023 00:19:21 -0500 Subject: [PATCH 055/105] fix: Generate at most one `server` directive per container --- nginx.tmpl | 23 +++++++++++++++++++---- test/test_multiple-networks.py | 19 ++++++++++++++++--- test/test_multiple-networks.yml | 15 +++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 310b60e..83f9222 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -118,6 +118,7 @@ upstream {{ .Upstream }} { # Container: {{ $container.Name }} {{- /* 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 }} + {{- $ip := "" }} {{- $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} {{- $addr_obj := where $container.Addresses "Port" $port | first }} # Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} @@ -141,23 +142,37 @@ upstream {{ .Upstream }} { # {{ $containerNetwork.Name }} (unreachable) {{- continue }} {{- end }} + {{- /* + * Do not emit multiple `server` directives for this container + * if it is reachable over multiple networks. This avoids + * accidentally inflating the effective round-robin weight of + * this container due to the redundant upstreams that nginx sees + * as belonging to distinct servers. + */}} + {{- if $ip }} + # {{ $containerNetwork.Name }} (ignored; reachable but redundant) + {{- continue }} + {{- end }} # {{ $containerNetwork.Name }} (reachable) {{- /* * 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 $addr_obj $addr_obj.HostPort }} - {{- $server_found = true }} - server {{ $container.Node.Address.IP }}:{{ $addr_obj.HostPort }}; + {{- $ip = $container.Node.Address.IP }} + {{- $port = $addr_obj.HostPort }} {{- else if and $containerNetwork $containerNetwork.IP }} - {{- $server_found = true }} - server {{ $containerNetwork.IP }}:{{ $port }}; + {{- $ip = $containerNetwork.IP }} {{- else }} # /!\ No IP for this network! {{- end }} {{- else }} # (none) {{- end }} + {{- if $ip }} + {{- $server_found = true }} + server {{ $ip }}:{{ $port }}; + {{- end }} {{- end }} {{- /* nginx-proxy/nginx-proxy#1105 */}} {{- if not $server_found }} diff --git a/test/test_multiple-networks.py b/test/test_multiple-networks.py index b9fa4c5..550d0a3 100644 --- a/test/test_multiple-networks.py +++ b/test/test_multiple-networks.py @@ -1,15 +1,28 @@ +import re + import pytest + def test_unknown_virtual_host(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 def test_forwards_to_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy.local/port") - assert r.status_code == 200 + assert r.status_code == 200 assert r.text == "answer from port 81\n" def test_forwards_to_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" \ No newline at end of file + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + +def test_multipath(docker_compose, nginxproxy): + r = nginxproxy.get("http://web3.nginx-proxy.test/port") + assert r.status_code == 200 + assert r.text == "answer from port 83\n" + cfg = nginxproxy.get_conf().decode() + lines = cfg.splitlines() + web3_server_lines = [l for l in lines + if re.search(r'(?m)^\s*server\s+[^\s]*:83;\s*$', l)] + assert len(web3_server_lines) == 1 diff --git a/test/test_multiple-networks.yml b/test/test_multiple-networks.yml index e4548b5..7e79174 100644 --- a/test/test_multiple-networks.yml +++ b/test/test_multiple-networks.yml @@ -3,6 +3,8 @@ version: '2' networks: net1: {} net2: {} + net3a: {} + net3b: {} services: nginx-proxy: @@ -12,6 +14,8 @@ services: networks: - net1 - net2 + - net3a + - net3b web1: image: web @@ -32,3 +36,14 @@ services: VIRTUAL_HOST: web2.nginx-proxy.local networks: - net2 + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: 83 + VIRTUAL_HOST: web3.nginx-proxy.test + networks: + - net3a + - net3b From 912a0654069097cc725eca74f7bae85df0e9c920 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 28 Jan 2023 18:14:59 -0500 Subject: [PATCH 056/105] chore: Pass `--pull` to `docker build` to get fresh images This is a no-op if the images are already up to date, and it prevents puzzling problems when the images are old. --- Makefile | 6 +++--- test/pytest.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ab44880..5e53e36 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,13 @@ build-webserver: - docker build -t web test/requirements/web + docker build --pull -t web test/requirements/web build-nginx-proxy-test-debian: - docker build --build-arg NGINX_PROXY_VERSION="test" -t nginxproxy/nginx-proxy:test . + docker build --pull --build-arg NGINX_PROXY_VERSION="test" -t nginxproxy/nginx-proxy:test . build-nginx-proxy-test-alpine: - docker build --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test . + docker build --pull --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test . test-debian: build-webserver build-nginx-proxy-test-debian test/pytest.sh diff --git a/test/pytest.sh b/test/pytest.sh index 9088d10..19a8188 100755 --- a/test/pytest.sh +++ b/test/pytest.sh @@ -14,7 +14,7 @@ DIR=$(cd "${TESTDIR}/.." && pwd) || exit 1 # check requirements echo "> Building nginx-proxy-tester image..." -docker build -t nginx-proxy-tester \ +docker build --pull -t nginx-proxy-tester \ -f "${TESTDIR}/requirements/Dockerfile-nginx-proxy-tester" \ "${TESTDIR}/requirements" \ || exit 1 From 2115974e939d685f21b9e65041b5a9984451cd4a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 28 Jan 2023 02:27:18 -0500 Subject: [PATCH 057/105] feat: Add ability to completely override location blocks Co-authored-by: Trent Harvey --- README.md | 26 +++++++++++ nginx.tmpl | 34 ++++++++------ test/test_location-override.py | 39 ++++++++++++++++ ...8b1b0f03eb30a1afd00e5696_location_override | 3 ++ ...d8fd4e056c2568d7c2e3ffa8_location_override | 4 ++ ...-nohash.nginx-proxy.test_location_override | 4 ++ ...d8fd4e056c2568d7c2e3ffa8_location_override | 3 ++ ...-nohash.nginx-proxy.test_location_override | 3 ++ ...d8fd4e056c2568d7c2e3ffa8_location_override | 4 ++ ...-nohash.nginx-proxy.test_location_override | 4 ++ ...d8fd4e056c2568d7c2e3ffa8_location_override | 3 ++ ...-nohash.nginx-proxy.test_location_override | 3 ++ test/test_location-override.yml | 44 +++++++++++++++++++ 13 files changed, 161 insertions(+), 13 deletions(-) create mode 100644 test/test_location-override.py create mode 100644 test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override create mode 100644 test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override create mode 100644 test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override create mode 100644 test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override create mode 100644 test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override create mode 100644 test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override create mode 100644 test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override create mode 100644 test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override create mode 100644 test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override create mode 100644 test/test_location-override.yml diff --git a/README.md b/README.md index 51d7b80..1728467 100644 --- a/README.md +++ b/README.md @@ -491,6 +491,32 @@ ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com If you want most of your virtual hosts to use a default single `location` block configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default_location` file. This file will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}_location` file associated with it. +#### Overriding `location` blocks + +The `${VIRTUAL_HOST}_${PATH_HASH}_location`, `${VIRTUAL_HOST}_location`, and `default_location` files documented above make it possible to *augment* the generated [`location` block(s)](https://nginx.org/en/docs/http/ngx_http_core_module.html#location) in a virtual host. In some circumstances, you may need to *completely override* the `location` block for a particular combination of virtual host and path. To do this, create a file whose name follows this pattern: + +``` +/etc/nginx/vhost.d/${VIRTUAL_HOST}_${PATH_HASH}_location_override +``` + +where `${VIRTUAL_HOST}` is the name of the virtual host (the `VIRTUAL_HOST` environment variable) and `${PATH_HASH}` is the SHA-1 hash of the path, as [described above](#per-virtual_path-location-configuration). + +For convenience, the `_${PATH_HASH}` part can be omitted if the path is `/`: + +``` +/etc/nginx/vhost.d/${VIRTUAL_HOST}_location_override +``` + +When an override file exists, the `location` block that is normally created by `nginx-proxy` is not generated. Instead, the override file is included via the [nginx `include` directive](https://nginx.org/en/docs/ngx_core_module.html#include). + +You are responsible for providing a suitable `location` block in your override file as required for your service. By default, `nginx-proxy` uses the `VIRTUAL_HOST` name as the upstream name for your application's Docker container; see [here](#unhashed-vs-sha1-upstream-names) for details. As an example, if your container has a `VIRTUAL_HOST` value of `app.example.com`, then to override the location block for `/` you would create a file named `/etc/nginx/vhost.d/app.example.com_location_override` that contains something like this: + +``` +location / { + proxy_pass http://app.example.com; +} +``` + #### Per-VIRTUAL_HOST `server_tokens` configuration Per virtual-host `servers_tokens` directive can be configured by passing appropriate value to the `SERVER_TOKENS` environment variable. Please see the [nginx http_core module configuration](https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens) for more details. diff --git a/nginx.tmpl b/nginx.tmpl index 83f9222..f61cd20 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -76,38 +76,46 @@ {{- end }} {{- define "location" }} + {{- $override := printf "/etc/nginx/vhost.d/%s_%s_location_override" .Host (sha1 .Path) }} + {{- if and (eq .Path "/") (not (exists $override)) }} + {{- $override = printf "/etc/nginx/vhost.d/%s_location_override" .Host }} + {{- end }} + {{- if exists $override }} + include {{ $override }}; + {{- else }} location {{ .Path }} { - {{- if eq .NetworkTag "internal" }} + {{- if eq .NetworkTag "internal" }} # Only allow traffic from internal clients include /etc/nginx/network_internal.conf; - {{- end }} + {{- end }} - {{- if eq .Proto "uwsgi" }} + {{- if eq .Proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }}; - {{- else if eq .Proto "fastcgi" }} + {{- else if eq .Proto "fastcgi" }} root {{ trim .VhostRoot }}; include fastcgi_params; fastcgi_pass {{ trim .Upstream }}; - {{- else if eq .Proto "grpc" }} + {{- else if eq .Proto "grpc" }} grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; - {{- else }} + {{- else }} proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }}; - {{- end }} + {{- end }} - {{- if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} + {{- if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} auth_basic "Restricted {{ .Host }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }}; - {{- end }} + {{- end }} - {{- 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)) }} + {{- 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") }} + {{- else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; - {{- end }} + {{- end }} } + {{- end }} {{- end }} {{- define "upstream" }} diff --git a/test/test_location-override.py b/test/test_location-override.py new file mode 100644 index 0000000..cbccbd9 --- /dev/null +++ b/test/test_location-override.py @@ -0,0 +1,39 @@ +def test_explicit_root_nohash(docker_compose, nginxproxy): + r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/port") + assert r.status_code == 418 + r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/foo/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + +def test_explicit_root_hash(docker_compose, nginxproxy): + r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/port") + assert r.status_code == 418 + r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/foo/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + +def test_explicit_root_hash_and_nohash(docker_compose, nginxproxy): + r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/port") + assert r.status_code == 418 + r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/foo/port") + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + +def test_explicit_nonroot(docker_compose, nginxproxy): + r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/port") + assert r.status_code == 200 + assert r.text == "answer from port 81\n" + r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/foo/port") + assert r.status_code == 418 + +def test_implicit_root_nohash(docker_compose, nginxproxy): + r = nginxproxy.get("http://implicit-root-nohash.nginx-proxy.test/port") + assert r.status_code == 418 + +def test_implicit_root_hash(docker_compose, nginxproxy): + r = nginxproxy.get("http://implicit-root-hash.nginx-proxy.test/port") + assert r.status_code == 418 + +def test_implicit_root_hash_and_nohash(docker_compose, nginxproxy): + r = nginxproxy.get("http://implicit-root-hash-and-nohash.nginx-proxy.test/port") + assert r.status_code == 418 diff --git a/test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override b/test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override new file mode 100644 index 0000000..f955c57 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override @@ -0,0 +1,3 @@ +location /foo/ { + return 418; +} diff --git a/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..f289d30 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override @@ -0,0 +1,4 @@ +# This file should trump the file without the hash. +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..4993313 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override @@ -0,0 +1,4 @@ +# The file with the hash should trump this file. +location / { + return 503; +} diff --git a/test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..f289d30 --- /dev/null +++ b/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override @@ -0,0 +1,4 @@ +# This file should trump the file without the hash. +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..4993313 --- /dev/null +++ b/test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override @@ -0,0 +1,4 @@ +# The file with the hash should trump this file. +location / { + return 503; +} diff --git a/test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override b/test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override b/test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override new file mode 100644 index 0000000..cbbf1e1 --- /dev/null +++ b/test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override @@ -0,0 +1,3 @@ +location / { + return 418; +} diff --git a/test/test_location-override.yml b/test/test_location-override.yml new file mode 100644 index 0000000..f36b206 --- /dev/null +++ b/test/test_location-override.yml @@ -0,0 +1,44 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./test_location-override.vhost.d:/etc/nginx/vhost.d:ro + + explicit-root: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: >- + explicit-root-nohash.nginx-proxy.test, + explicit-root-hash.nginx-proxy.test, + explicit-root-hash-and-nohash.nginx-proxy.test, + explicit-nonroot.nginx-proxy.test + VIRTUAL_PATH: / + explicit-foo: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: >- + explicit-root-nohash.nginx-proxy.test, + explicit-root-hash.nginx-proxy.test, + explicit-root-hash-and-nohash.nginx-proxy.test, + explicit-nonroot.nginx-proxy.test + VIRTUAL_PATH: /foo/ + VIRTUAL_DEST: / + + # Same as explicit-root except VIRTUAL_PATH is left unset. + implicit-root: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: >- + implicit-root-nohash.nginx-proxy.test, + implicit-root-hash.nginx-proxy.test, + implicit-root-hash-and-nohash.nginx-proxy.test, From 2494e207843c92a715da3e8e65ed763fd0d2d624 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 26 Jan 2023 19:27:21 -0500 Subject: [PATCH 058/105] chore: Remove support for legacy swarm It doesn't work with the newer Docker Swarm mode so it doesn't have much value anymore. --- nginx.tmpl | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index f61cd20..6f3bf91 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -137,15 +137,8 @@ upstream {{ .Upstream }} { # bypass nginx-proxy and access the container's server # directly. {{- end }} - {{- if $container.Node.ID }} - # Swarm node name: {{ $container.Node.Name }} - {{- end }} # Container networks: {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} - {{- if eq $containerNetwork.Name "ingress" }} - # {{ $containerNetwork.Name }} (ignored) - {{- continue }} - {{- end }} {{- if and (not (index $networks $containerNetwork.Name)) (not $networks.host) }} # {{ $containerNetwork.Name }} (unreachable) {{- continue }} @@ -162,14 +155,7 @@ upstream {{ .Upstream }} { {{- continue }} {{- end }} # {{ $containerNetwork.Name }} (reachable) - {{- /* - * 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 $addr_obj $addr_obj.HostPort }} - {{- $ip = $container.Node.Address.IP }} - {{- $port = $addr_obj.HostPort }} - {{- else if and $containerNetwork $containerNetwork.IP }} + {{- if and $containerNetwork $containerNetwork.IP }} {{- $ip = $containerNetwork.IP }} {{- else }} # /!\ No IP for this network! From 11a46f728c476854ddb42f78f468c8fca6c5d505 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 31 Jan 2023 15:07:59 -0500 Subject: [PATCH 059/105] chore: Factor out container IP:port lookup This will make planned future changes easier. --- nginx.tmpl | 117 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 6f3bf91..2ec7b43 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -26,6 +26,75 @@ # {{ .Name }} {{- end }} +{{- /* + * Template used as a function to get a container's IP address. This + * template only outputs debug comments; the IP address is "returned" by + * storing the value in the provided dot dict. + * + * The provided dot dict is expected to have the following entries: + * - "globals": Global values. + * - "container": The container's RuntimeContainer struct. + * + * The return value will be added to the dot dict with key "ip". + */}} +{{- define "container_ip" }} + {{- $ip := "" }} + # networks: + {{- range sortObjectsByKeysAsc $.container.Networks "Name" }} + {{- if and (not (index $.globals.networks .Name)) (not $.globals.networks.host) }} + # {{ .Name }} (unreachable) + {{- continue }} + {{- end }} + {{- /* + * Do not emit multiple `server` directives for this container if it + * is reachable over multiple networks. This avoids accidentally + * inflating the effective round-robin weight of a server due to the + * redundant upstream addresses that nginx sees as belonging to + * distinct servers. + */}} + {{- if $ip }} + # {{ .Name }} (ignored; reachable but redundant) + {{- continue }} + {{- end }} + # {{ .Name }} (reachable) + {{- if and . .IP }} + {{- $ip = .IP }} + {{- else }} + # /!\ No IP for this network! + {{- end }} + {{- else }} + # (none) + {{- end }} + # IP address: {{ if $ip }}{{ $ip }}{{ else }}(none usable){{ end }} + {{- $_ := set $ "ip" $ip }} +{{- end }} + +{{- /* + * Template used as a function to get the port of the server in the given + * container. This template only outputs debug comments; the port is + * "returned" by storing the value in the provided dot dict. + * + * The provided dot dict is expected to have the following entries: + * - "container": The container's RuntimeContainer struct. + * + * The return value will be added to the dot dict with key "port". + */}} +{{- define "container_port" }} + {{- /* If only 1 port exposed, use that as a default, else 80. */}} + # exposed ports:{{ range $.container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} + {{- $default_port := when (eq (len $.container.Addresses) 1) (first $.container.Addresses).Port "80" }} + # default port: {{ $default_port }} + {{- $port := or $.container.Env.VIRTUAL_PORT $default_port }} + # using port: {{ $port }} + {{- $addr_obj := where $.container.Addresses "Port" $port | first }} + {{- if and $addr_obj $addr_obj.HostPort }} + # /!\ WARNING: Virtual port published on host. Clients + # might be able to bypass nginx-proxy and + # access the container's server directly. + {{- end }} + {{- $_ := set $ "port" $port }} +{{- end }} + {{- define "ssl_policy" }} {{- if eq .ssl_policy "Mozilla-Modern" }} ssl_protocols TLSv1.3; @@ -119,50 +188,16 @@ {{- end }} {{- define "upstream" }} - {{- $networks := .Networks }} upstream {{ .Upstream }} { {{- $server_found := false }} {{- range $container := .Containers }} # Container: {{ $container.Name }} - {{- /* 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 }} - {{- $ip := "" }} - {{- $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} - {{- $addr_obj := where $container.Addresses "Port" $port | first }} - # Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} - # Default virtual port: {{ $defaultPort }} - # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }} - {{- if and $addr_obj $addr_obj.HostPort }} - # /!\ WARNING: Virtual port published on host. Clients might be able to - # bypass nginx-proxy and access the container's server - # directly. - {{- end }} - # Container networks: - {{- range $containerNetwork := sortObjectsByKeysAsc $container.Networks "Name" }} - {{- if and (not (index $networks $containerNetwork.Name)) (not $networks.host) }} - # {{ $containerNetwork.Name }} (unreachable) - {{- continue }} - {{- end }} - {{- /* - * Do not emit multiple `server` directives for this container - * if it is reachable over multiple networks. This avoids - * accidentally inflating the effective round-robin weight of - * this container due to the redundant upstreams that nginx sees - * as belonging to distinct servers. - */}} - {{- if $ip }} - # {{ $containerNetwork.Name }} (ignored; reachable but redundant) - {{- continue }} - {{- end }} - # {{ $containerNetwork.Name }} (reachable) - {{- if and $containerNetwork $containerNetwork.IP }} - {{- $ip = $containerNetwork.IP }} - {{- else }} - # /!\ No IP for this network! - {{- end }} - {{- else }} - # (none) - {{- end }} + {{- $args := dict "globals" $.globals "container" $container }} + {{- template "container_ip" $args }} + {{- $ip := $args.ip }} + {{- $args := dict "container" $container }} + {{- template "container_port" $args }} + {{- $port := $args.port }} {{- if $ip }} {{- $server_found = true }} server {{ $ip }}:{{ $port }}; @@ -296,7 +331,7 @@ server { {{- $upstream = printf "%s-%s" $upstream $sum }} {{- end }} # {{ $host }}{{ $path }} -{{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $globals.networks) }} +{{ template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers) }} {{- end }} {{- $default_host := or ($globals.Env.DEFAULT_HOST) "" }} From 7a2b1f8833d33fa99d20c34e104e4041f857422d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 1 Feb 2023 18:17:43 -0500 Subject: [PATCH 060/105] chore: Split `$is_https` variable into two separate checks for improved readability. --- nginx.tmpl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 98ab38e..5e26ce3 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -444,10 +444,9 @@ server { * match. */}} {{- $cert := (coalesce $certName $vhostCert) }} + {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} - {{- $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} - - {{- if and $is_https (eq $https_method "redirect") }} + {{- if and $cert_ok (eq $https_method "redirect") }} server { server_name {{ $host }}; {{- if $server_tokens }} @@ -485,13 +484,13 @@ server { server_tokens {{ $server_tokens }}; {{- end }} {{ $globals.access_log }} - {{- if or (not $is_https) (eq $https_method "noredirect") }} + {{- if or (eq $https_method "nohttps") (not $cert_ok) (eq $https_method "noredirect") }} listen {{ $globals.external_http_port }} {{ $default_server }}; {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; {{- end }} {{- end }} - {{- if $is_https }} + {{- if and (ne $https_method "nohttps") $cert_ok }} listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }}; {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }}; @@ -559,7 +558,7 @@ server { {{- end }} } - {{- if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} + {{- if and (or (eq $https_method "nohttps") (not $cert_ok)) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key") }} server { server_name {{ $host }}; {{- if $server_tokens }} From 18d0671312fa81245b3bbbe35495c61760b7c46a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 1 Feb 2023 18:56:16 -0500 Subject: [PATCH 061/105] chore: Factor out duplicate checks for `default.crt` for improved readability. --- nginx.tmpl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 5e26ce3..bc7e12b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -11,6 +11,7 @@ {{- $_ := set $globals "Env" $.Env }} {{- $_ := set $globals "Docker" $.Docker }} {{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }} +{{- $_ := set $globals "default_cert_ok" (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} {{- $_ := set $globals "external_http_port" (coalesce $globals.Env.HTTP_PORT "80") }} {{- $_ := set $globals "external_https_port" (coalesce $globals.Env.HTTPS_PORT "443") }} {{- $_ := set $globals "sha1_upstream_name" (parseBool (coalesce $globals.Env.SHA1_UPSTREAM_NAME "false")) }} @@ -355,7 +356,7 @@ server { {{ $globals.access_log }} return 503; -{{- if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +{{- if $globals.default_cert_ok }} listen {{ $globals.external_https_port }} ssl http2; {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl http2; @@ -558,7 +559,7 @@ server { {{- end }} } - {{- if and (or (eq $https_method "nohttps") (not $cert_ok)) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key") }} + {{- if and (or (eq $https_method "nohttps") (not $cert_ok)) $globals.default_cert_ok }} server { server_name {{ $host }}; {{- if $server_tokens }} From 7b6b2f773d47b10c41085694e836b29e8be4c90b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 04:01:39 +0000 Subject: [PATCH 062/105] build: bump golang from 1.19.5 to 1.20.0 Bumps golang from 1.19.5 to 1.20.0. --- 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 548e2e3..7669314 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.4 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.19.5 as gobuilder +FROM golang:1.20.0 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 5dc34a5..6902b41 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.9.4 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.19.5-alpine as gobuilder +FROM golang:1.20.0-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 16066cab61aa10547f1b87c740ce2b34ec8fb0c0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 2 Feb 2023 17:17:00 -0500 Subject: [PATCH 063/105] fix: Don't create cert error https server if https is not enabled --- nginx.tmpl | 2 +- .../withdefault.certs/default.crt | 70 ++++++++++++++++++ .../withdefault.certs/default.key | 27 +++++++ .../http-only.nginx-proxy.test.crt | 71 +++++++++++++++++++ .../http-only.nginx-proxy.test.key | 27 +++++++ .../https-and-http.nginx-proxy.test.crt | 71 +++++++++++++++++++ .../https-and-http.nginx-proxy.test.key | 27 +++++++ .../https-only.nginx-proxy.test.crt | 71 +++++++++++++++++++ .../https-only.nginx-proxy.test.key | 27 +++++++ test/test_fallback.data/withdefault.yml | 36 ++++++++++ test/test_fallback.py | 53 ++++++++++++++ .../test_wildcard_cert_nohttps.py | 4 +- 12 files changed, 483 insertions(+), 3 deletions(-) create mode 100644 test/test_fallback.data/withdefault.certs/default.crt create mode 100644 test/test_fallback.data/withdefault.certs/default.key create mode 100644 test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt create mode 100644 test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key create mode 100644 test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt create mode 100644 test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key create mode 100644 test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt create mode 100644 test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key create mode 100644 test/test_fallback.data/withdefault.yml create mode 100644 test/test_fallback.py diff --git a/nginx.tmpl b/nginx.tmpl index bc7e12b..c8b704d 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -559,7 +559,7 @@ server { {{- end }} } - {{- if and (or (eq $https_method "nohttps") (not $cert_ok)) $globals.default_cert_ok }} + {{- if and (ne $https_method "nohttps") (not $cert_ok) $globals.default_cert_ok }} server { server_name {{ $host }}; {{- if $server_tokens }} diff --git a/test/test_fallback.data/withdefault.certs/default.crt b/test/test_fallback.data/withdefault.certs/default.crt new file mode 100644 index 0000000..f855457 --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/default.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 9 04:02:23 2023 GMT + Not After : Jun 27 04:02:23 2050 GMT + Subject: CN=*.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:db:bd:54:de:01:7e:82:4e:c0:f1:5d:12:fd:3a: + fb:21:19:4d:44:25:47:ea:ad:d8:11:5c:d1:65:88: + af:49:fc:8e:4b:c3:01:c1:0d:6d:22:67:bd:31:66: + 9f:4a:50:17:9e:47:b3:3b:b3:21:73:1f:81:55:73: + 52:47:9b:fb:85:6b:e8:d8:09:cc:e1:7d:1c:14:03: + 1c:ae:84:b4:5b:e5:e5:c7:71:fc:1f:74:33:4f:ae: + f7:8d:21:1f:55:8d:93:c7:84:4d:93:01:a1:1c:37: + ae:85:5c:70:2c:21:ec:87:35:c3:86:d3:b3:0f:9a: + b0:9d:8a:cd:0e:49:e8:99:c5:4c:50:bd:a8:6e:a7: + 01:3e:a7:dc:cf:c3:48:37:8e:c6:8a:89:b0:41:01: + 58:ee:45:94:fa:90:eb:df:c8:0e:b7:dd:79:75:13: + 1e:07:69:ee:54:47:92:18:9d:e0:a9:ee:4e:22:d1: + f4:a2:4d:a1:47:ed:9b:35:2a:70:cc:66:fb:3e:f0: + 49:f7:ee:62:2a:27:a1:d3:52:7b:ff:e9:12:d9:5b: + 6b:f6:18:bf:9c:9d:5f:00:29:d2:54:b5:f8:a4:a2: + 9b:3f:fe:a6:ed:14:ae:a0:fe:13:33:18:33:17:a9: + 8b:fe:fc:75:65:0c:fb:c2:d1:1e:81:ca:43:89:bd: + 78:dd + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:*.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 43:a7:1f:4b:ec:ff:1d:70:c7:f8:6e:eb:fd:15:25:27:b2:54: + c7:92:cf:ed:51:31:28:56:76:5c:da:8b:17:31:55:8c:a1:c2: + 37:95:27:7b:b6:58:e5:92:ef:1e:fe:35:f1:44:ca:c7:1b:7b: + 75:bf:e1:91:61:6d:8a:6f:35:8b:73:f4:d9:08:60:25:07:7a: + 3e:c2:79:e7:ae:b4:70:cc:8a:30:cb:80:aa:47:1a:40:82:00: + a0:5e:01:67:d1:95:21:3c:b1:52:7d:f5:87:b6:43:41:df:b2: + a7:ee:3b:73:17:c4:19:2c:6b:7b:3c:26:9e:4c:00:e3:e8:07: + f2:e1:a1:31:79:57:be:b6:b1:a7:93:70:4e:e1:7d:bf:08:c5: + e7:a0:de:7d:82:20:24:f7:b0:3f:c2:94:36:88:ef:7b:7d:c0: + 7f:8a:78:a1:8e:56:42:82:ce:82:e6:8e:3d:1b:b7:ca:dd:a9: + a8:e6:f9:a3:f4:4a:a4:a0:9c:15:6f:44:8c:48:20:e5:85:ed: + 6f:85:22:41:1d:1f:fe:58:e5:43:ad:f2:c4:10:5a:10:ed:36: + 10:98:ad:73:97:6a:e0:19:18:d6:32:26:03:3d:dd:84:5c:2e: + 97:ca:a2:f5:63:f2:7a:16:f1:55:ca:d2:a1:54:09:8a:bb:23: + f0:53:36:51 +-----BEGIN CERTIFICATE----- +MIIC+zCCAeOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDkwNDAyMjNaGA8yMDUwMDYyNzA0MDIyM1owHTEbMBkGA1UEAwwS +Ki5uZ2lueC1wcm94eS50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA271U3gF+gk7A8V0S/Tr7IRlNRCVH6q3YEVzRZYivSfyOS8MBwQ1tIme9MWaf +SlAXnkezO7Mhcx+BVXNSR5v7hWvo2AnM4X0cFAMcroS0W+Xlx3H8H3QzT673jSEf +VY2Tx4RNkwGhHDeuhVxwLCHshzXDhtOzD5qwnYrNDknomcVMUL2obqcBPqfcz8NI +N47GiomwQQFY7kWU+pDr38gOt915dRMeB2nuVEeSGJ3gqe5OItH0ok2hR+2bNSpw +zGb7PvBJ9+5iKieh01J7/+kS2Vtr9hi/nJ1fACnSVLX4pKKbP/6m7RSuoP4TMxgz +F6mL/vx1ZQz7wtEegcpDib143QIDAQABoyEwHzAdBgNVHREEFjAUghIqLm5naW54 +LXByb3h5LnRlc3QwDQYJKoZIhvcNAQELBQADggEBAEOnH0vs/x1wx/hu6/0VJSey +VMeSz+1RMShWdlzaixcxVYyhwjeVJ3u2WOWS7x7+NfFEyscbe3W/4ZFhbYpvNYtz +9NkIYCUHej7CeeeutHDMijDLgKpHGkCCAKBeAWfRlSE8sVJ99Ye2Q0HfsqfuO3MX +xBksa3s8Jp5MAOPoB/LhoTF5V762saeTcE7hfb8Ixeeg3n2CICT3sD/ClDaI73t9 +wH+KeKGOVkKCzoLmjj0bt8rdqajm+aP0SqSgnBVvRIxIIOWF7W+FIkEdH/5Y5UOt +8sQQWhDtNhCYrXOXauAZGNYyJgM93YRcLpfKovVj8noW8VXK0qFUCYq7I/BTNlE= +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/withdefault.certs/default.key b/test/test_fallback.data/withdefault.certs/default.key new file mode 100644 index 0000000..79ccb0b --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/default.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA271U3gF+gk7A8V0S/Tr7IRlNRCVH6q3YEVzRZYivSfyOS8MB +wQ1tIme9MWafSlAXnkezO7Mhcx+BVXNSR5v7hWvo2AnM4X0cFAMcroS0W+Xlx3H8 +H3QzT673jSEfVY2Tx4RNkwGhHDeuhVxwLCHshzXDhtOzD5qwnYrNDknomcVMUL2o +bqcBPqfcz8NIN47GiomwQQFY7kWU+pDr38gOt915dRMeB2nuVEeSGJ3gqe5OItH0 +ok2hR+2bNSpwzGb7PvBJ9+5iKieh01J7/+kS2Vtr9hi/nJ1fACnSVLX4pKKbP/6m +7RSuoP4TMxgzF6mL/vx1ZQz7wtEegcpDib143QIDAQABAoIBAGUd9QXMTjkMoIDx +QaHCGHocuI+ZUETQBtPGkJ1WjsNPMvPuIsqBsSzZ7Bflj3uU66lseTAJuGTPpKZ7 +0Ose/llhVN7Fc8B34AndfL9aVdzMKDblXw3iXRJYA5awHUkzQ0PWwBPb9hWUEf1Q +klXcrolx1i4fEREnMArvKnlezWikpXcqDYRcmUfEvVozaq75heavHpOcOq2dg7vo +N/gcJmfG4aDOhrZC1f22u5cNePvbVj+DdXOUHMEEfXOFbxk97VhmcIaH75ugvVh4 +EMMg87mcGLZiqPO5k6SYcuGyquc32Tf5sK80mpt/+SAEHCvSmUt9c1ynQrS9IhNp +OGfZhQkCgYEA+LonwScVGVEgHg1A9E7BKhIrgUOlwWNM43s+o8Uuz1T72VUVZ6N/ +aO0+2Panw1qjsb0CUC2zft3zZTiZd81gWRmBYQ0R9dHWyWHbJlOv8rAmJ+60Gr2a +UVTLHEdZKx6svSDNhL0HfxxfWwePgHB4NVa2RUA3KQ5y5C96EXb8WbsCgYEA4iow +nIIbRZ9ILDz1oThxE+dFifKWXWFOwa58EFBY+/y34itL7kXRu2+4ZIltwL0L8m5j +GUALUabuoOASKg4CFBhCvoAAlWZRr6L6EOecrElUnrefUrKuCWPCVo3MnCMuLXDp +p1mEGIwEZBCY+jrSBMrRCawsMRkcymLJhEBFYkcCgYB6xIey0vObF2ve6XPSIr09 +YtKObzF1jun4rnBwrXc5Zx0YXOK/0PemdtO6i6SqzCZYKI7nvGcIi80DfThi5cBU +uj4eBTGEQBrgM6jT9iK2izOKKkxDlqqA0nWec6kTm4Rvpa1Lg3Ibz4lRiR3Pq7Pp +v+8fp16SqUsUTkrWLADK2QKBgCRIhHf3X4yx2xBNz1JIDcwVpFBXPMxKWio0Ze7w +FPaIOq/sJkhZpyYc7EYkzhjHu2zvTLK2VZqJ32qrx/47NRYoNjz9qBpPyfcVfGzN +25LASPUVnFfWFpmnCXx9T0AVXMkpfjK857ZQcDvldcVfPmZKa3LTzlsqHjZR1uaC +sR7tAoGBANBfInPZVZRJfqqkPN1K8j4P7uCGjTIBmys/vxoilh1d0scTgZrdqt92 +EKi/3UsJW2ndqQNDLbvi5kcW8a6UU3UB1LLvpyQ5zuS81x3+kKfv+5cDM8rt/M4A +MXnJA5eDZZ4SlHFzdblUv/MZdT+1x0tivMn3zFKNNj2SmaSGkQ0m +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt b/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt new file mode 100644 index 0000000..33fa2f7 --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt @@ -0,0 +1,71 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 7 21:54:16 2023 GMT + Not After : Jun 25 21:54:16 2050 GMT + Subject: CN=http-only.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:b4:62:61:07:54:2e:6d:55:83:2d:24:b7:e2:15: + 34:13:bd:79:21:e9:10:75:3f:4c:f8:ba:60:29:87: + e5:8e:2a:1e:fd:33:51:5a:8a:3a:6f:60:ff:24:f1: + 1b:27:30:8c:ac:43:04:b7:79:cb:7a:ec:c6:08:a4: + a0:15:b0:0f:ee:6b:15:84:24:11:bc:85:2b:48:06: + 04:0a:58:bb:8c:e8:4d:48:f5:06:c5:91:fe:5d:99: + 0a:29:31:8a:f1:9b:0c:e0:39:75:a1:06:9b:d4:f5: + 06:74:8f:46:5e:64:ba:2f:d0:3d:7c:3d:30:03:e9: + 7c:35:17:69:04:f6:2e:29:d4:93:d6:d6:d2:6c:04: + 38:06:21:06:05:30:8a:b9:9d:05:8d:12:6e:48:39: + bb:f6:93:4f:ba:a5:84:c7:96:2f:be:92:25:e9:d0: + 95:2a:d9:23:8a:b3:28:0b:b6:19:1c:3b:be:a2:91: + 70:44:a8:77:18:94:4b:df:61:f4:5c:c9:78:76:34: + b5:87:0f:c0:92:04:26:b6:ca:62:cd:9b:5d:eb:bf: + 10:ac:df:af:72:5f:af:09:38:b1:dc:e1:3d:13:db: + a0:ac:b7:2e:ca:39:5c:4c:f1:1e:81:a8:b4:44:a2: + 72:d5:3b:c0:71:cc:dc:16:0d:fa:38:96:44:b3:00: + d6:65 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:http-only.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 3b:54:95:48:4d:f6:93:38:42:40:02:ab:b7:17:3b:50:3b:ca: + c7:12:69:b0:da:cb:d7:3e:0e:1f:bf:a2:59:c7:fe:c2:5c:43: + 84:92:b9:3a:be:8f:7e:2e:81:3c:ed:f3:a9:77:21:c2:35:f1: + da:cf:3a:1e:e2:ee:a2:ce:72:55:97:87:0e:ad:59:61:f7:75: + 46:c0:2b:d4:88:b7:36:97:11:fb:5e:28:89:e9:2a:92:f1:15: + f1:43:8e:c1:38:85:8d:3a:26:7d:25:72:93:17:96:8d:5a:ed: + e8:73:3a:d5:8d:80:f2:af:38:84:ff:85:2e:d1:36:7d:2e:e1: + f0:2c:d8:15:5f:fc:c5:70:5d:25:6a:22:f3:2a:cd:0f:25:ad: + d4:93:d3:9a:3e:50:bc:da:a5:6c:86:ea:1d:d9:b9:c5:90:db: + f5:02:c8:c9:77:5c:ef:77:fe:74:60:41:33:d9:3c:a2:e1:73: + aa:14:18:5d:36:58:c8:41:63:4c:59:0e:4b:3d:c5:65:5a:01: + b0:16:50:0f:d0:4f:0d:ca:97:f6:11:47:06:6b:b1:ae:bb:26: + 30:34:8b:7a:91:5d:8a:22:c7:f9:05:0d:bb:a5:b7:60:c0:20: + ce:d0:0e:c0:66:b3:e7:c4:61:ec:c5:40:e6:52:11:41:c3:11: + 18:04:c7:1e +-----BEGIN CERTIFICATE----- +MIIDCzCCAfOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDcyMTU0MTZaGA8yMDUwMDYyNTIxNTQxNlowJTEjMCEGA1UEAwwa +aHR0cC1vbmx5Lm5naW54LXByb3h5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC0YmEHVC5tVYMtJLfiFTQTvXkh6RB1P0z4umAph+WOKh79M1Fa +ijpvYP8k8RsnMIysQwS3ect67MYIpKAVsA/uaxWEJBG8hStIBgQKWLuM6E1I9QbF +kf5dmQopMYrxmwzgOXWhBpvU9QZ0j0ZeZLov0D18PTAD6Xw1F2kE9i4p1JPW1tJs +BDgGIQYFMIq5nQWNEm5IObv2k0+6pYTHli++kiXp0JUq2SOKsygLthkcO76ikXBE +qHcYlEvfYfRcyXh2NLWHD8CSBCa2ymLNm13rvxCs369yX68JOLHc4T0T26Csty7K +OVxM8R6BqLREonLVO8BxzNwWDfo4lkSzANZlAgMBAAGjKTAnMCUGA1UdEQQeMByC +Gmh0dHAtb25seS5uZ2lueC1wcm94eS50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQA7 +VJVITfaTOEJAAqu3FztQO8rHEmmw2svXPg4fv6JZx/7CXEOEkrk6vo9+LoE87fOp +dyHCNfHazzoe4u6iznJVl4cOrVlh93VGwCvUiLc2lxH7XiiJ6SqS8RXxQ47BOIWN +OiZ9JXKTF5aNWu3oczrVjYDyrziE/4Uu0TZ9LuHwLNgVX/zFcF0laiLzKs0PJa3U +k9OaPlC82qVshuod2bnFkNv1AsjJd1zvd/50YEEz2Tyi4XOqFBhdNljIQWNMWQ5L +PcVlWgGwFlAP0E8Nypf2EUcGa7GuuyYwNIt6kV2KIsf5BQ27pbdgwCDO0A7AZrPn +xGHsxUDmUhFBwxEYBMce +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key b/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key new file mode 100644 index 0000000..3834584 --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtGJhB1QubVWDLSS34hU0E715IekQdT9M+LpgKYfljioe/TNR +Woo6b2D/JPEbJzCMrEMEt3nLeuzGCKSgFbAP7msVhCQRvIUrSAYECli7jOhNSPUG +xZH+XZkKKTGK8ZsM4Dl1oQab1PUGdI9GXmS6L9A9fD0wA+l8NRdpBPYuKdST1tbS +bAQ4BiEGBTCKuZ0FjRJuSDm79pNPuqWEx5YvvpIl6dCVKtkjirMoC7YZHDu+opFw +RKh3GJRL32H0XMl4djS1hw/AkgQmtspizZtd678QrN+vcl+vCTix3OE9E9ugrLcu +yjlcTPEegai0RKJy1TvAcczcFg36OJZEswDWZQIDAQABAoIBAAfDA/HQyX6i41YZ +8l+kEe2XhZLT+IVTB/jb7C9dTZ9kaJj0kFeZAxKv1cq9JTH2gNcYuyc58muDrLHK +g6jrPoQ/z1k0RB8ci9Q5jgrz7n4NsOWmxXfS5GMaprlHDHeA+HjdgBZBtorfUDvL +vndpVimgiETETUCd115hd39jKHFcRcdV6yCix7ObywK3dMgLVpagCcnlyCWffS/r +nhhMfJ+VstW0nUtfZ7JEYwT6Cg7lLAVtDkqPX8zGjJiRwUKH808bUyqEw1y5Cc8U +U5hbmMgPWfXsKxsEC6FSVHBG9ZX2jymOMQXijLFcBSuWvADHmyU+ZxXcbtd1rv4E +cGFj3wECgYEA5cNrr5WjrpEin6MYYVWxiQ+xEWPU2R17eApagrDRLM41JJpv7a5m +TYuZRfIxb59CBPi718Gi168P3T2KMvo2/BTh9Lq5ZBYHx3aDqW2QvMFn7/tgamj8 +0DBxccd2QWfGIBrT1rAF7lD8TC86wtDDVKrvhucRSEXVKF/jWFFRGfUCgYEAyPt6 +48khr7sfNMVdkDLjQjZVV6H7ZUMoSn0FGybgKWxW+b0XCBPObUQWIpyCNTRr1+4A +1TAUS+F/OVVfwnLNgemeE2wd6CaduxwiK1U4pHbyXCElH1ifonHWV3MoXOefYsiY +q5z2jfJzUi0JZVUKsveu9rQsFLsc//1s/I5T1LECgYEAldY6fNg2VVp63OZsuNU8 +oSiljbSwEyMh6Oe/nOkYkIKtr4AzrCoGt11piG7ohGW0lS9suMijnMqiquI+JP5+ +KyinLoUy761aR17nf+9e62mpkZw6hUqQTGi7Irs0SHUXhMpaCfDi/Ua9MiW+yVuB +ds6+xBgeciZwWxMlXOwy2p0CgYEAm+YWiSK3Mq0fo7uEvBn9Fps2z+ciLoZNdppL +n6gkMX2MaeQ3PVi/wxoRYX+tsL+c973yf2vwEnw0R7Dlutt6dc9VgxNWj4GE0GMe +Tiao7Uom7Tf4p7wC9+r9rI/zOz2f8OxRIK18wtbShWfR5fx1dCWUXmGb3+jUse1O +4Qk2FcECgYAvSvGFoJb8tuHFEYYHBbjficmvTUsrTE+EhxPqWKFhKfF19fFFIupy +XBCrN6nwrh+/YMxZXeIRbbTTf814cOO7PjLeNhnfhJZkaJq1HzbYe3bOurna3qrm +Ra3xiM8Ld2PyGnZPXf8+AWhMhuPkLX1KFVTCAxwCpmTZCHtiGCmXMA== +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt b/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt new file mode 100644 index 0000000..8b04cb9 --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt @@ -0,0 +1,71 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 7 21:53:19 2023 GMT + Not After : Jun 25 21:53:19 2050 GMT + Subject: CN=https-and-http.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:b7:97:85:d1:7f:6b:50:29:f3:87:b7:4e:f5:25: + 40:6a:d8:fa:a1:63:3c:4a:2e:68:4a:c6:8b:38:df: + 07:81:d0:08:9d:fc:17:f5:37:28:7f:31:e6:f3:81: + 28:4e:22:b6:bd:a2:4e:f2:2f:e5:0f:dd:55:3c:e1: + 04:84:4c:45:1b:1a:ae:b7:f0:2a:da:43:05:71:91: + 92:b8:d1:49:fe:80:0a:53:b9:66:da:54:60:9a:fc: + e1:b2:e8:28:48:7f:96:94:3c:92:a3:b2:37:f6:7a: + c2:de:0b:12:f0:ae:4e:92:fe:2d:c1:b2:95:28:1f: + 88:8d:79:99:81:19:ae:22:a4:95:f5:9f:db:25:8e: + 1d:cf:43:cd:6f:85:93:5f:79:ee:f8:f3:d4:82:e1: + e9:4d:c9:ad:ae:5b:92:43:3a:3c:71:51:70:f7:3e: + bd:1b:24:52:6a:a3:cf:54:72:57:ed:fe:72:ea:96: + 9b:5a:02:02:a7:df:85:b7:68:ae:1e:07:77:9f:59: + a5:a0:8b:28:c2:c8:b7:bb:8a:42:50:df:05:73:bf: + 9c:55:13:b5:82:79:77:40:57:a4:8f:88:a5:71:50: + d7:70:b0:4d:0c:d9:86:b3:9b:db:8a:20:bd:19:68: + 10:52:2d:53:ba:0e:2e:1c:ad:80:54:bb:b6:c9:ab: + 11:39 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:https-and-http.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 2c:f3:e5:47:3f:8e:5a:28:b1:df:e5:95:50:85:6f:27:2f:a6: + 8d:f1:5e:cf:df:e2:52:66:97:61:36:59:81:26:25:19:99:c9: + 93:e5:85:cb:ca:69:af:4b:21:a3:d2:7a:bf:b5:5e:2d:42:fb: + 99:f8:22:58:e5:bf:79:b8:8a:74:7e:c6:94:14:d9:f2:27:63: + b6:e5:74:21:5b:59:fb:f6:c8:a9:28:fb:60:f7:5e:bd:c2:e6: + 74:24:14:96:61:95:6c:c2:66:b4:52:25:a1:85:5a:97:e5:68: + 5c:62:cf:69:3b:b0:a9:56:d8:e3:5f:74:dc:84:18:d5:3e:4f: + c9:35:39:26:88:dc:9b:80:d9:40:e1:4f:09:27:8d:d2:89:55: + 30:91:02:86:35:04:95:1e:1d:58:14:5b:c6:e0:2e:a7:bf:a8: + f6:2b:76:8a:4e:71:79:bc:c0:04:cd:db:81:73:46:ce:68:ed: + 25:b0:0e:42:8d:96:64:77:3b:f4:9d:1a:c9:f6:78:4c:56:4f: + 92:17:29:3d:80:50:71:77:4b:a8:29:c2:12:fc:ad:0a:37:81: + 38:4c:fb:54:99:4d:12:5f:98:dc:d1:a9:7b:08:45:c4:6f:7e: + fe:00:e0:db:79:fe:d1:28:e3:8e:82:d1:fb:bc:0a:c4:42:93: + c9:5e:eb:ba +-----BEGIN CERTIFICATE----- +MIIDFTCCAf2gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDcyMTUzMTlaGA8yMDUwMDYyNTIxNTMxOVowKjEoMCYGA1UEAwwf +aHR0cHMtYW5kLWh0dHAubmdpbngtcHJveHkudGVzdDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBALeXhdF/a1Ap84e3TvUlQGrY+qFjPEouaErGizjfB4HQ +CJ38F/U3KH8x5vOBKE4itr2iTvIv5Q/dVTzhBIRMRRsarrfwKtpDBXGRkrjRSf6A +ClO5ZtpUYJr84bLoKEh/lpQ8kqOyN/Z6wt4LEvCuTpL+LcGylSgfiI15mYEZriKk +lfWf2yWOHc9DzW+Fk1957vjz1ILh6U3Jra5bkkM6PHFRcPc+vRskUmqjz1RyV+3+ +cuqWm1oCAqffhbdorh4Hd59ZpaCLKMLIt7uKQlDfBXO/nFUTtYJ5d0BXpI+IpXFQ +13CwTQzZhrOb24ogvRloEFItU7oOLhytgFS7tsmrETkCAwEAAaMuMCwwKgYDVR0R +BCMwIYIfaHR0cHMtYW5kLWh0dHAubmdpbngtcHJveHkudGVzdDANBgkqhkiG9w0B +AQsFAAOCAQEALPPlRz+OWiix3+WVUIVvJy+mjfFez9/iUmaXYTZZgSYlGZnJk+WF +y8ppr0sho9J6v7VeLUL7mfgiWOW/ebiKdH7GlBTZ8idjtuV0IVtZ+/bIqSj7YPde +vcLmdCQUlmGVbMJmtFIloYVal+VoXGLPaTuwqVbY41903IQY1T5PyTU5Jojcm4DZ +QOFPCSeN0olVMJEChjUElR4dWBRbxuAup7+o9it2ik5xebzABM3bgXNGzmjtJbAO +Qo2WZHc79J0ayfZ4TFZPkhcpPYBQcXdLqCnCEvytCjeBOEz7VJlNEl+Y3NGpewhF +xG9+/gDg23n+0SjjjoLR+7wKxEKTyV7rug== +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key b/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key new file mode 100644 index 0000000..11f5210 --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAt5eF0X9rUCnzh7dO9SVAatj6oWM8Si5oSsaLON8HgdAInfwX +9TcofzHm84EoTiK2vaJO8i/lD91VPOEEhExFGxqut/Aq2kMFcZGSuNFJ/oAKU7lm +2lRgmvzhsugoSH+WlDySo7I39nrC3gsS8K5Okv4twbKVKB+IjXmZgRmuIqSV9Z/b +JY4dz0PNb4WTX3nu+PPUguHpTcmtrluSQzo8cVFw9z69GyRSaqPPVHJX7f5y6pab +WgICp9+Ft2iuHgd3n1mloIsowsi3u4pCUN8Fc7+cVRO1gnl3QFekj4ilcVDXcLBN +DNmGs5vbiiC9GWgQUi1Tug4uHK2AVLu2yasROQIDAQABAoIBACT4KSVHoEdzOyvw +GME6sB8T9Fw9TG2vrKaqFmzsVGmqh6Gwmu5xHgGG/fe44XHigaPsJDOWu2yXaEur +ECrH5P6RP++gODDdYCI/ayk2U80g4XN8mR6L8Swkkhphr4Lx1lOhYvH9uFE05Tqr +RjQbFY16C6K+oFSFDQ1YGDYsAqnM3RD7PH+lHpo8UN1TO/vogdSQEpMYZDwLAYnW +uD5G3c0u2PsGu9YLuz2p8hcs3chh+cqKJWXOeW0JLrNGx1bqeQWkn6nXRDdRYi9V +cJlTgDqGuF54bieSyq9ABDZQP4Ol+moYKDoIz5PwurNjcYSklrT1tw0gqHZoQK1L +fDjw3QECgYEA7QMRU1AFKTvO7/8WLHLN5BT63n31wm0e9PYpz/XVLWEfxBcp9Xmf +xAIhXZ/U9P4dfNqxTjN9mVGzCHh5KfDJnUFqOXFy/zvfMeRzJf6dJo6/4OX9Bijr +Tgd454vyGXYQP2t+F14UAwl6vlGOAjttiP5qY5Ef1gllBEeIPe9Ts9kCgYEAxkzZ +pq4HJ/5/iDquMEHXNXzpNPavSvgxQdl1ILvJ49LJImmQFBCP9PqiOTIfePz1OqUI +C4baFuc0FEDJ3x9CUNmMY1lEi2ZUq2agPSXaQNsMcKtEJH8SoJlJIRpkQA7unX09 +zb4dam6g79OaGmb8scePuezXMLv1Ee6WWtXbzGECgYEA6PYn9Gzl9cacu9dOUzgw +2ewpPcIvawDY+cxwAsHO3MDneVWPX4JBoGa7pwvwRTL1hwBqYMRJwwbD5CKObcQI +V/KxV28Eqo2N77tt1z2x9/E99u/4yTI1P0gm9ejfeVlL1RpyIMPPBcEujZ0Z6WXC +X3I63k0KLtajHRa2erIf4tkCgYAfunAgwTuX5JqXO3xfcEl033WY6deGUUvgU2Dw +Sdu1viY8gVNyQmwmMGwAZsquWxsJtRoibgM7IucsTml+b8v2j7hstP3IqCjn+9Wr +swDG28WTyXNvu31JgP04dLaRoVIAlOdsofym6OiLNvozO0M3VsziXMjZnVlK8zfP +dORkQQKBgQDXAJEJPygxVA+bF104dzCMWGmU7K8ShEWC5eOdKK4KWf9bNDpY6M6c +i6zga/xBbj7e3Bxqprpp8Wy2gIsnYiVo4V9EQethbLdomPxOpBMNMARw81rL1CpO +jbHB7bIDcKs2tQoZEXUW86ZxC8sdaDaWTJTfUO0RpJow6ZO3yvxVIQ== +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt b/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt new file mode 100644 index 0000000..a93e728 --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt @@ -0,0 +1,71 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 7 21:53:49 2023 GMT + Not After : Jun 25 21:53:49 2050 GMT + Subject: CN=https-only.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:d9:87:48:02:85:f4:5f:0d:90:7e:4c:4f:13:89: + 41:ca:41:15:c2:6f:fd:a8:c7:17:83:c6:dd:8c:fe: + 19:a4:b2:6b:0b:35:4f:b4:3d:7c:40:0a:04:33:2a: + fd:10:72:f7:63:63:99:5b:3d:ec:78:ee:c6:4d:c8: + 0e:4c:be:f2:3f:e3:02:74:57:9a:c1:fe:15:95:63: + 4e:e7:2c:eb:70:f2:6b:c8:ba:01:a2:ca:a1:c7:76: + ff:38:e4:c2:b0:66:fc:85:d2:af:0f:22:81:d4:82: + eb:d5:b0:e6:69:14:37:dd:8d:ad:29:ce:93:68:5a: + ce:f4:77:76:6f:78:13:b6:c8:2f:fe:e0:b6:7e:fb: + 29:16:be:e2:f5:45:3b:39:5b:52:dc:26:b7:ca:0c: + b6:1c:fc:a8:38:0b:dd:c1:f4:04:9b:2d:38:c9:a5: + 2d:3e:f1:42:88:53:a2:3b:17:cf:d5:3c:2b:d6:6a: + 7f:6f:05:8d:c5:b7:5d:64:1e:83:1b:e7:ec:80:3d: + 6d:34:c1:66:b2:e6:5d:d9:a7:6e:46:75:14:bf:10: + 16:c5:fc:47:8e:63:fa:e5:b4:bd:f2:b9:e0:cb:ea: + 75:f9:68:ee:7d:8f:ea:8f:1a:9f:34:27:7a:4a:9f: + 85:fd:3e:17:a7:96:c3:d0:4e:50:a2:a2:e0:45:92: + d0:b5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:https-only.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 8a:52:46:42:a9:74:18:6a:52:90:ef:a4:e9:c5:54:d5:97:3a: + ff:8b:c2:76:4f:9e:47:aa:e1:ea:e5:b9:af:9d:33:e3:85:17: + 54:7d:32:bd:ac:90:3f:5c:d2:a1:42:17:52:2b:b1:83:e5:c3: + bf:81:f0:e7:38:e2:88:67:7b:d8:59:fe:f9:94:99:ba:be:f4: + 3c:24:b2:c7:9e:f0:98:21:c6:2d:c2:e8:f3:67:bd:62:00:aa: + ce:34:fa:b4:53:6d:c1:09:5e:55:bd:43:aa:86:c6:f8:c5:83: + 46:3a:49:12:a2:ec:30:36:0c:99:44:74:09:9d:cc:4b:98:1f: + 7e:c9:9b:68:a0:f8:1e:00:14:d0:da:2a:bf:c8:ca:a8:1c:10: + b5:68:a2:f1:41:93:0c:f3:3f:c0:c6:53:3c:8d:a7:dd:a5:7b: + 35:cc:44:e0:5b:6d:c5:cb:33:6f:c1:43:7e:06:df:21:99:11: + b3:91:41:b4:5e:f0:37:1e:8e:e5:73:85:dc:4a:21:d5:41:f9: + 4e:b8:f5:ed:21:93:09:91:c2:8c:6b:04:a4:84:ab:3a:fe:35: + 64:fa:6b:a7:8d:40:a6:64:89:30:84:ac:28:99:5a:01:79:77: + c0:df:88:da:a9:75:5f:c4:51:ae:a8:45:7b:d2:e1:a2:81:29: + 60:cd:7b:cd +-----BEGIN CERTIFICATE----- +MIIDDTCCAfWgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDcyMTUzNDlaGA8yMDUwMDYyNTIxNTM0OVowJjEkMCIGA1UEAwwb +aHR0cHMtb25seS5uZ2lueC1wcm94eS50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA2YdIAoX0Xw2QfkxPE4lBykEVwm/9qMcXg8bdjP4ZpLJrCzVP +tD18QAoEMyr9EHL3Y2OZWz3seO7GTcgOTL7yP+MCdFeawf4VlWNO5yzrcPJryLoB +osqhx3b/OOTCsGb8hdKvDyKB1ILr1bDmaRQ33Y2tKc6TaFrO9Hd2b3gTtsgv/uC2 +fvspFr7i9UU7OVtS3Ca3ygy2HPyoOAvdwfQEmy04yaUtPvFCiFOiOxfP1Twr1mp/ +bwWNxbddZB6DG+fsgD1tNMFmsuZd2aduRnUUvxAWxfxHjmP65bS98rngy+p1+Wju +fY/qjxqfNCd6Sp+F/T4Xp5bD0E5QoqLgRZLQtQIDAQABoyowKDAmBgNVHREEHzAd +ghtodHRwcy1vbmx5Lm5naW54LXByb3h5LnRlc3QwDQYJKoZIhvcNAQELBQADggEB +AIpSRkKpdBhqUpDvpOnFVNWXOv+LwnZPnkeq4erlua+dM+OFF1R9Mr2skD9c0qFC +F1IrsYPlw7+B8Oc44ohne9hZ/vmUmbq+9Dwkssee8Jghxi3C6PNnvWIAqs40+rRT +bcEJXlW9Q6qGxvjFg0Y6SRKi7DA2DJlEdAmdzEuYH37Jm2ig+B4AFNDaKr/Iyqgc +ELVoovFBkwzzP8DGUzyNp92lezXMROBbbcXLM2/BQ34G3yGZEbORQbRe8DcejuVz +hdxKIdVB+U649e0hkwmRwoxrBKSEqzr+NWT6a6eNQKZkiTCErCiZWgF5d8DfiNqp +dV/EUa6oRXvS4aKBKWDNe80= +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key b/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key new file mode 100644 index 0000000..17976ce --- /dev/null +++ b/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2YdIAoX0Xw2QfkxPE4lBykEVwm/9qMcXg8bdjP4ZpLJrCzVP +tD18QAoEMyr9EHL3Y2OZWz3seO7GTcgOTL7yP+MCdFeawf4VlWNO5yzrcPJryLoB +osqhx3b/OOTCsGb8hdKvDyKB1ILr1bDmaRQ33Y2tKc6TaFrO9Hd2b3gTtsgv/uC2 +fvspFr7i9UU7OVtS3Ca3ygy2HPyoOAvdwfQEmy04yaUtPvFCiFOiOxfP1Twr1mp/ +bwWNxbddZB6DG+fsgD1tNMFmsuZd2aduRnUUvxAWxfxHjmP65bS98rngy+p1+Wju +fY/qjxqfNCd6Sp+F/T4Xp5bD0E5QoqLgRZLQtQIDAQABAoIBAAWs//YA5MVuJy0E +dLO/yxWp6RVvsqCqwTRRBgrdvnGLrjtWosPDLvDE0iM7peq99TKEsMWusfLd2BLD +e4wJF20PUUsT1hflt050juR9SY9i4+kS4WQMAXig5DvpzCKqLUCYpLSyY8zVta2X +tgtb2bFQNwp2N2ZrqCa8zzxNV8ZXGoW+ZlvBJEDtBwt1DCDhY39/pqHfIhFl4Vwk +YhhbVjID145D1j/fP6vLceM2YA4uRmF1itj1iQ6YNNpXRspUGE4DXdqR6HcbduiX +trZjmdtKXY8mJg6jyLZxYbjFlKV/LvqKRYF3Jb9K0vdd4juBdZoy7DQzoLhcnzui +pEnPLakCgYEA9tN6KdQGKGBXGuF+ZqhXfB/XSkKUf8o/5j62cbu11ZIJ+iEBx+d6 +lQAxTz5hHUL6a3c5qiM+AWBxYuFD6oqptIlTlBfIXI978neDNvEWWffivPvQLbt9 +o9ohOirfK1iGPvtrpAwjv5ylE5SiTmJ/6wDvQWjNGAnJ3aaxkesJUSMCgYEA4Z0K +UHZVtnKLtzzIY7KfLbuKF/fJEDfMNr4Wgl6ny21vqO9kJGmA7SaoNdhx8RDcKmeV +/Vey4ug6YlOG48eapKLTthdRz5mx+jIkUfdOhj81m28xm/OPTqCrviTHCNOHeYDy +NKAIlJMo2z0vTKJn5eP6CsYmDWLpHQNyXY5qcEcCgYAzDBWt5O3JF/Or2Yr8zEAb +qbIq544yx69jfQDakMnQe72Yf48Quuz9N+b6zpnjJWEJLMU+TL+cJUgN/SzAqyDh +96zTaf/ENOCbiuAWUtIelUfNcf7iFm6rnodUsl0pZ8uL5w+iA+i4zjrNy+WtdG2k +OrNAwd345L1dHAaJeSSaJQKBgQCUnF3r7Fa/TCpt87LHwSQK+sqWyRf+/9IbiRDI +pVL/s8FmVPHw7jIHhHwuo7lCImnz4LGy5C6oOnIizIRAy/04Ty0Hd8ri5YmPlbHI +8A8gbMiB7zeNU1zlXP5jzFPyo2tMhLyGH5gnTdwOtfnPD/dCPe45ZJYyISIOg3O0 +3peMBwKBgH20cskAOCNclfoG+Nis52h8FqmDlflJ8waUarvk26JhO1e009kOytw8 +x/qSuttpGtTG+4fdc2wJvFNczr4h9ZlftBdgZXj8PKgRpcIe8q97Xg8PUj+Xfu/t +vD/QV+tVcGoAMsQq4NeFxiTbPfwVyXdYFT1XVCu6JEdLL+gpWh5W +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/withdefault.yml b/test/test_fallback.data/withdefault.yml new file mode 100644 index 0000000..00f7ee7 --- /dev/null +++ b/test/test_fallback.data/withdefault.yml @@ -0,0 +1,36 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./withdefault.certs:/etc/nginx/certs:ro + https-and-http: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: https-and-http.nginx-proxy.test + https-only: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: https-only.nginx-proxy.test + HTTPS_METHOD: nohttp + http-only: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: http-only.nginx-proxy.test + HTTPS_METHOD: nohttps + missing-cert: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: missing-cert.nginx-proxy.test diff --git a/test/test_fallback.py b/test/test_fallback.py new file mode 100644 index 0000000..1e687a3 --- /dev/null +++ b/test/test_fallback.py @@ -0,0 +1,53 @@ +import os.path + +import backoff +import pytest +import requests + + +@pytest.fixture +def data_dir(): + return f"{os.path.splitext(__file__)[0]}.data" + + +@pytest.fixture +def docker_compose_file(data_dir, compose_file): + return os.path.join(data_dir, compose_file) + + +@pytest.fixture +def get(docker_compose, nginxproxy, want_err_re): + + @backoff.on_exception( + backoff.constant, + requests.exceptions.RequestException, + giveup=lambda e: want_err_re and want_err_re.search(str(e)), + interval=.3, + max_tries=30, + jitter=None) + def _get(url): + return nginxproxy.get(url, allow_redirects=False) + + return _get + + +@pytest.mark.parametrize("compose_file,url,want_code,want_err_re", [ + # Has default.crt. + ("withdefault.yml", "http://https-and-http.nginx-proxy.test/", 301, None), + ("withdefault.yml", "https://https-and-http.nginx-proxy.test/", 200, None), + ("withdefault.yml", "http://https-only.nginx-proxy.test/", 503, None), + ("withdefault.yml", "https://https-only.nginx-proxy.test/", 200, None), + ("withdefault.yml", "http://http-only.nginx-proxy.test/", 200, None), + ("withdefault.yml", "https://http-only.nginx-proxy.test/", 503, None), + ("withdefault.yml", "http://missing-cert.nginx-proxy.test/", 200, None), + ("withdefault.yml", "https://missing-cert.nginx-proxy.test/", 500, None), + ("withdefault.yml", "http://unknown.nginx-proxy.test/", 503, None), + ("withdefault.yml", "https://unknown.nginx-proxy.test/", 503, None), +]) +def test_fallback(get, url, want_code, want_err_re): + if want_err_re is None: + r = get(url) + assert r.status_code == want_code + else: + with pytest.raises(requests.exceptions.RequestException, match=want_err_re): + get(url) diff --git a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py index 68b0329..603d281 100644 --- a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py +++ b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py @@ -24,10 +24,10 @@ def test_https_get_served(docker_compose, nginxproxy, subdomain): assert f"answer from port 8{subdomain}\n" == r.text @pytest.mark.filterwarnings('ignore::urllib3.exceptions.InsecureRequestWarning') -def test_web3_https_is_500_and_SSL_validation_fails(docker_compose, nginxproxy): +def test_https_request_to_nohttps_vhost_goes_to_fallback_server(docker_compose, nginxproxy): with pytest.raises( (CertificateError, SSLError) ) as excinfo: nginxproxy.get("https://3.web.nginx-proxy.tld/port") assert """hostname '3.web.nginx-proxy.tld' doesn't match 'nginx-proxy.tld'""" in str(excinfo.value) r = nginxproxy.get("https://3.web.nginx-proxy.tld/port", verify=False) - assert r.status_code == 500 + assert r.status_code == 503 From 9297e9438901df2898c2daf2145a28cb761f880d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 2 Feb 2023 22:02:06 -0500 Subject: [PATCH 064/105] fix: Emit TLS error if there are no certs available Before, if neither the vhost-specific cert nor `default.crt` existed, nginx-proxy would not create the https vhost. This resulted in nginx either refusing the connection or serving the wrong vhost depending on whether there was another https vhost with a certificate. Now nginx-proxy always creates an https server for a vhost, even if the vhost-specific certificate and the default certificate are both missing. When both certs are missing, nginx is given empty certificate data to make it possible for it to start up without an error. The empty certificate data causes the user to see a TLS error, which is much easier to troubleshoot than a connection refused error or serving the wrong vhost. --- README.md | 32 ++++++- nginx.tmpl | 83 +++++++++++-------- .../http-only.nginx-proxy.test.crt | 71 ++++++++++++++++ .../http-only.nginx-proxy.test.key | 27 ++++++ .../https-and-http.nginx-proxy.test.crt | 71 ++++++++++++++++ .../https-and-http.nginx-proxy.test.key | 27 ++++++ .../https-only.nginx-proxy.test.crt | 71 ++++++++++++++++ .../https-only.nginx-proxy.test.key | 27 ++++++ test/test_fallback.data/nodefault.yml | 36 ++++++++ test/test_fallback.py | 15 ++++ 10 files changed, 423 insertions(+), 37 deletions(-) create mode 100644 test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt create mode 100644 test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key create mode 100644 test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt create mode 100644 test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key create mode 100644 test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt create mode 100644 test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key create mode 100644 test/test_fallback.data/nodefault.yml diff --git a/README.md b/README.md index 4e0d9d4..2757c33 100644 --- a/README.md +++ b/README.md @@ -347,10 +347,9 @@ Note that the `Mozilla-Old` policy should use a 1024 bits DH key for compatibili The default behavior for the proxy when port 80 and 443 are exposed is as follows: -* If a container has a usable cert, port 80 will redirect to 443 for that container so that HTTPS is always preferred when available. -* If the container does not have a usable cert, a 503 will be returned. - -Note that in the latter case, a browser may get an connection error as no certificate is available to establish a connection. A self-signed or generic cert named `default.crt` and `default.key` will allow a client browser to make a SSL connection (likely w/ a warning) and subsequently receive a 500. +* If a virtual host has a usable cert, port 80 will redirect to 443 for that virtual host so that HTTPS is always preferred when available. +* If the virtual host does not have a usable cert, but `default.crt` and `default.key` exist, those will be used as the virtual host's certificate and the client browser will receive a 500 error. +* If the virtual host does not have a usable cert, and `default.crt` and `default.key` do not exist, TLS negotiation will fail (see [Missing Certificate](#missing-certificate) below). To serve traffic in both SSL and non-SSL modes without redirecting to SSL, you can include the environment variable `HTTPS_METHOD=noredirect` (the default is `HTTPS_METHOD=redirect`). You can also disable the non-SSL site entirely with `HTTPS_METHOD=nohttp`, or disable the HTTPS site with `HTTPS_METHOD=nohttps`. `HTTPS_METHOD` can be specified on each container for which you want to override the default behavior or on the proxy container to set it globally. If `HTTPS_METHOD=noredirect` is used, Strict Transport Security (HSTS) is disabled to prevent HTTPS users from being redirected by the client. If you cannot get to the HTTP site after changing this setting, your browser has probably cached the HSTS policy and is automatically redirecting you back to HTTPS. You will need to clear your browser's HSTS cache or use an incognito window / different browser. @@ -358,6 +357,31 @@ By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.or *WARNING*: HSTS will force your users to visit the HTTPS version of your site for the `max-age` time - even if they type in `http://` manually. The only way to get to an HTTP site after receiving an HSTS response is to clear your browser's HSTS cache. +#### Missing Certificate + +If HTTPS is enabled for a virtual host but its certificate is missing, nginx-proxy will configure nginx to use the default certificate (`default.crt` with `default.key`) and return a 500 error. + +If the default certificate is also missing, nginx-proxy will configure nginx to accept HTTPS connections but fail the TLS negotiation. Client browsers will render a TLS error page. As of March 2023, web browsers display the following error messages: + + * Chrome: + + > This site can't provide a secure connection + > + > example.test sent an invalid response. + > + > Try running Connectivity Diagnostics. + > + > `ERR_SSL_PROTOCOL_ERROR` + + * Firefox: + + > Secure Connection Failed + > + > An error occurred during a connection to example.test. + > Peer reports it experienced an internal error. + > + > Error code: `SSL_ERROR_INTERNAL_ERROR_ALERT` "TLS error". + ### Basic Authentication Support In order to be able to secure your virtual host, you have to create a file named as its equivalent VIRTUAL_HOST variable on directory diff --git a/nginx.tmpl b/nginx.tmpl index c8b704d..d2ccd8f 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -350,23 +350,30 @@ server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; listen {{ $globals.external_http_port }}; + listen {{ $globals.external_https_port }} ssl http2; {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_http_port }}; + listen [::]:{{ $globals.external_https_port }} ssl http2; {{- end }} {{ $globals.access_log }} - return 503; - {{- if $globals.default_cert_ok }} - listen {{ $globals.external_https_port }} ssl http2; - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} ssl http2; - {{- end }} - ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; +{{- else }} + # No default.crt certificate found for this vhost, so force nginx to emit a + # TLS error if the client connects via https. + {{- /* See the comment in the main `server` directive for rationale. */}} + ssl_ciphers aNULL; + set $empty ""; + ssl_certificate data:$empty; + ssl_certificate_key data:$empty; + if ($https) { + return 444; + } {{- end }} + return 503; } {{- range $host, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }} @@ -491,13 +498,14 @@ server { listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; {{- end }} {{- end }} - {{- if and (ne $https_method "nohttps") $cert_ok }} + {{- if ne $https_method "nohttps" }} listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }}; {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }}; {{- end }} - {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} + {{- if $cert_ok }} + {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; @@ -506,22 +514,50 @@ server { ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; - {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} + {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; - {{- end }} + {{- end }} - {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} + {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; - {{- end }} + {{- end }} - {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} + {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} set $sts_header ""; if ($https) { set $sts_header "{{ trim $hsts }}"; } add_header Strict-Transport-Security $sts_header always; + {{- end }} + {{- else if $globals.default_cert_ok }} + # No certificate found for this vhost, so use the default certificate and + # return an error code if the user connects via https. + ssl_certificate /etc/nginx/certs/default.crt; + ssl_certificate_key /etc/nginx/certs/default.key; + if ($https) { + return 500; + } + {{- else }} + # No certificate found for this vhost, so force nginx to emit a TLS error if + # the client connects via https. + {{- /* + * The alternative is to not provide an https server for this + * vhost, which would either cause the user to see the wrong + * vhost (if there is another vhost with a certificate) or a + * connection refused error (if there is no other vhost with a + * certificate). A TLS error is easier to troubleshoot, and is + * safer than serving the wrong vhost. Also see + * . + */}} + ssl_ciphers aNULL; + set $empty ""; + ssl_certificate data:$empty; + ssl_certificate_key data:$empty; + if ($https) { + return 444; + } {{- end }} {{- end }} @@ -558,23 +594,4 @@ server { } {{- end }} } - - {{- if and (ne $https_method "nohttps") (not $cert_ok) $globals.default_cert_ok }} -server { - server_name {{ $host }}; - {{- if $server_tokens }} - server_tokens {{ $server_tokens }}; - {{- end }} - listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }}; - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }}; - {{- end }} - {{ $globals.access_log }} - return 500; - - ssl_certificate /etc/nginx/certs/default.crt; - ssl_certificate_key /etc/nginx/certs/default.key; -} - {{- end }} - {{- end }} diff --git a/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt b/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt new file mode 100644 index 0000000..33fa2f7 --- /dev/null +++ b/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt @@ -0,0 +1,71 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 7 21:54:16 2023 GMT + Not After : Jun 25 21:54:16 2050 GMT + Subject: CN=http-only.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:b4:62:61:07:54:2e:6d:55:83:2d:24:b7:e2:15: + 34:13:bd:79:21:e9:10:75:3f:4c:f8:ba:60:29:87: + e5:8e:2a:1e:fd:33:51:5a:8a:3a:6f:60:ff:24:f1: + 1b:27:30:8c:ac:43:04:b7:79:cb:7a:ec:c6:08:a4: + a0:15:b0:0f:ee:6b:15:84:24:11:bc:85:2b:48:06: + 04:0a:58:bb:8c:e8:4d:48:f5:06:c5:91:fe:5d:99: + 0a:29:31:8a:f1:9b:0c:e0:39:75:a1:06:9b:d4:f5: + 06:74:8f:46:5e:64:ba:2f:d0:3d:7c:3d:30:03:e9: + 7c:35:17:69:04:f6:2e:29:d4:93:d6:d6:d2:6c:04: + 38:06:21:06:05:30:8a:b9:9d:05:8d:12:6e:48:39: + bb:f6:93:4f:ba:a5:84:c7:96:2f:be:92:25:e9:d0: + 95:2a:d9:23:8a:b3:28:0b:b6:19:1c:3b:be:a2:91: + 70:44:a8:77:18:94:4b:df:61:f4:5c:c9:78:76:34: + b5:87:0f:c0:92:04:26:b6:ca:62:cd:9b:5d:eb:bf: + 10:ac:df:af:72:5f:af:09:38:b1:dc:e1:3d:13:db: + a0:ac:b7:2e:ca:39:5c:4c:f1:1e:81:a8:b4:44:a2: + 72:d5:3b:c0:71:cc:dc:16:0d:fa:38:96:44:b3:00: + d6:65 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:http-only.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 3b:54:95:48:4d:f6:93:38:42:40:02:ab:b7:17:3b:50:3b:ca: + c7:12:69:b0:da:cb:d7:3e:0e:1f:bf:a2:59:c7:fe:c2:5c:43: + 84:92:b9:3a:be:8f:7e:2e:81:3c:ed:f3:a9:77:21:c2:35:f1: + da:cf:3a:1e:e2:ee:a2:ce:72:55:97:87:0e:ad:59:61:f7:75: + 46:c0:2b:d4:88:b7:36:97:11:fb:5e:28:89:e9:2a:92:f1:15: + f1:43:8e:c1:38:85:8d:3a:26:7d:25:72:93:17:96:8d:5a:ed: + e8:73:3a:d5:8d:80:f2:af:38:84:ff:85:2e:d1:36:7d:2e:e1: + f0:2c:d8:15:5f:fc:c5:70:5d:25:6a:22:f3:2a:cd:0f:25:ad: + d4:93:d3:9a:3e:50:bc:da:a5:6c:86:ea:1d:d9:b9:c5:90:db: + f5:02:c8:c9:77:5c:ef:77:fe:74:60:41:33:d9:3c:a2:e1:73: + aa:14:18:5d:36:58:c8:41:63:4c:59:0e:4b:3d:c5:65:5a:01: + b0:16:50:0f:d0:4f:0d:ca:97:f6:11:47:06:6b:b1:ae:bb:26: + 30:34:8b:7a:91:5d:8a:22:c7:f9:05:0d:bb:a5:b7:60:c0:20: + ce:d0:0e:c0:66:b3:e7:c4:61:ec:c5:40:e6:52:11:41:c3:11: + 18:04:c7:1e +-----BEGIN CERTIFICATE----- +MIIDCzCCAfOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDcyMTU0MTZaGA8yMDUwMDYyNTIxNTQxNlowJTEjMCEGA1UEAwwa +aHR0cC1vbmx5Lm5naW54LXByb3h5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC0YmEHVC5tVYMtJLfiFTQTvXkh6RB1P0z4umAph+WOKh79M1Fa +ijpvYP8k8RsnMIysQwS3ect67MYIpKAVsA/uaxWEJBG8hStIBgQKWLuM6E1I9QbF +kf5dmQopMYrxmwzgOXWhBpvU9QZ0j0ZeZLov0D18PTAD6Xw1F2kE9i4p1JPW1tJs +BDgGIQYFMIq5nQWNEm5IObv2k0+6pYTHli++kiXp0JUq2SOKsygLthkcO76ikXBE +qHcYlEvfYfRcyXh2NLWHD8CSBCa2ymLNm13rvxCs369yX68JOLHc4T0T26Csty7K +OVxM8R6BqLREonLVO8BxzNwWDfo4lkSzANZlAgMBAAGjKTAnMCUGA1UdEQQeMByC +Gmh0dHAtb25seS5uZ2lueC1wcm94eS50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQA7 +VJVITfaTOEJAAqu3FztQO8rHEmmw2svXPg4fv6JZx/7CXEOEkrk6vo9+LoE87fOp +dyHCNfHazzoe4u6iznJVl4cOrVlh93VGwCvUiLc2lxH7XiiJ6SqS8RXxQ47BOIWN +OiZ9JXKTF5aNWu3oczrVjYDyrziE/4Uu0TZ9LuHwLNgVX/zFcF0laiLzKs0PJa3U +k9OaPlC82qVshuod2bnFkNv1AsjJd1zvd/50YEEz2Tyi4XOqFBhdNljIQWNMWQ5L +PcVlWgGwFlAP0E8Nypf2EUcGa7GuuyYwNIt6kV2KIsf5BQ27pbdgwCDO0A7AZrPn +xGHsxUDmUhFBwxEYBMce +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key b/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key new file mode 100644 index 0000000..3834584 --- /dev/null +++ b/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtGJhB1QubVWDLSS34hU0E715IekQdT9M+LpgKYfljioe/TNR +Woo6b2D/JPEbJzCMrEMEt3nLeuzGCKSgFbAP7msVhCQRvIUrSAYECli7jOhNSPUG +xZH+XZkKKTGK8ZsM4Dl1oQab1PUGdI9GXmS6L9A9fD0wA+l8NRdpBPYuKdST1tbS +bAQ4BiEGBTCKuZ0FjRJuSDm79pNPuqWEx5YvvpIl6dCVKtkjirMoC7YZHDu+opFw +RKh3GJRL32H0XMl4djS1hw/AkgQmtspizZtd678QrN+vcl+vCTix3OE9E9ugrLcu +yjlcTPEegai0RKJy1TvAcczcFg36OJZEswDWZQIDAQABAoIBAAfDA/HQyX6i41YZ +8l+kEe2XhZLT+IVTB/jb7C9dTZ9kaJj0kFeZAxKv1cq9JTH2gNcYuyc58muDrLHK +g6jrPoQ/z1k0RB8ci9Q5jgrz7n4NsOWmxXfS5GMaprlHDHeA+HjdgBZBtorfUDvL +vndpVimgiETETUCd115hd39jKHFcRcdV6yCix7ObywK3dMgLVpagCcnlyCWffS/r +nhhMfJ+VstW0nUtfZ7JEYwT6Cg7lLAVtDkqPX8zGjJiRwUKH808bUyqEw1y5Cc8U +U5hbmMgPWfXsKxsEC6FSVHBG9ZX2jymOMQXijLFcBSuWvADHmyU+ZxXcbtd1rv4E +cGFj3wECgYEA5cNrr5WjrpEin6MYYVWxiQ+xEWPU2R17eApagrDRLM41JJpv7a5m +TYuZRfIxb59CBPi718Gi168P3T2KMvo2/BTh9Lq5ZBYHx3aDqW2QvMFn7/tgamj8 +0DBxccd2QWfGIBrT1rAF7lD8TC86wtDDVKrvhucRSEXVKF/jWFFRGfUCgYEAyPt6 +48khr7sfNMVdkDLjQjZVV6H7ZUMoSn0FGybgKWxW+b0XCBPObUQWIpyCNTRr1+4A +1TAUS+F/OVVfwnLNgemeE2wd6CaduxwiK1U4pHbyXCElH1ifonHWV3MoXOefYsiY +q5z2jfJzUi0JZVUKsveu9rQsFLsc//1s/I5T1LECgYEAldY6fNg2VVp63OZsuNU8 +oSiljbSwEyMh6Oe/nOkYkIKtr4AzrCoGt11piG7ohGW0lS9suMijnMqiquI+JP5+ +KyinLoUy761aR17nf+9e62mpkZw6hUqQTGi7Irs0SHUXhMpaCfDi/Ua9MiW+yVuB +ds6+xBgeciZwWxMlXOwy2p0CgYEAm+YWiSK3Mq0fo7uEvBn9Fps2z+ciLoZNdppL +n6gkMX2MaeQ3PVi/wxoRYX+tsL+c973yf2vwEnw0R7Dlutt6dc9VgxNWj4GE0GMe +Tiao7Uom7Tf4p7wC9+r9rI/zOz2f8OxRIK18wtbShWfR5fx1dCWUXmGb3+jUse1O +4Qk2FcECgYAvSvGFoJb8tuHFEYYHBbjficmvTUsrTE+EhxPqWKFhKfF19fFFIupy +XBCrN6nwrh+/YMxZXeIRbbTTf814cOO7PjLeNhnfhJZkaJq1HzbYe3bOurna3qrm +Ra3xiM8Ld2PyGnZPXf8+AWhMhuPkLX1KFVTCAxwCpmTZCHtiGCmXMA== +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt b/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt new file mode 100644 index 0000000..8b04cb9 --- /dev/null +++ b/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt @@ -0,0 +1,71 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 7 21:53:19 2023 GMT + Not After : Jun 25 21:53:19 2050 GMT + Subject: CN=https-and-http.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:b7:97:85:d1:7f:6b:50:29:f3:87:b7:4e:f5:25: + 40:6a:d8:fa:a1:63:3c:4a:2e:68:4a:c6:8b:38:df: + 07:81:d0:08:9d:fc:17:f5:37:28:7f:31:e6:f3:81: + 28:4e:22:b6:bd:a2:4e:f2:2f:e5:0f:dd:55:3c:e1: + 04:84:4c:45:1b:1a:ae:b7:f0:2a:da:43:05:71:91: + 92:b8:d1:49:fe:80:0a:53:b9:66:da:54:60:9a:fc: + e1:b2:e8:28:48:7f:96:94:3c:92:a3:b2:37:f6:7a: + c2:de:0b:12:f0:ae:4e:92:fe:2d:c1:b2:95:28:1f: + 88:8d:79:99:81:19:ae:22:a4:95:f5:9f:db:25:8e: + 1d:cf:43:cd:6f:85:93:5f:79:ee:f8:f3:d4:82:e1: + e9:4d:c9:ad:ae:5b:92:43:3a:3c:71:51:70:f7:3e: + bd:1b:24:52:6a:a3:cf:54:72:57:ed:fe:72:ea:96: + 9b:5a:02:02:a7:df:85:b7:68:ae:1e:07:77:9f:59: + a5:a0:8b:28:c2:c8:b7:bb:8a:42:50:df:05:73:bf: + 9c:55:13:b5:82:79:77:40:57:a4:8f:88:a5:71:50: + d7:70:b0:4d:0c:d9:86:b3:9b:db:8a:20:bd:19:68: + 10:52:2d:53:ba:0e:2e:1c:ad:80:54:bb:b6:c9:ab: + 11:39 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:https-and-http.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 2c:f3:e5:47:3f:8e:5a:28:b1:df:e5:95:50:85:6f:27:2f:a6: + 8d:f1:5e:cf:df:e2:52:66:97:61:36:59:81:26:25:19:99:c9: + 93:e5:85:cb:ca:69:af:4b:21:a3:d2:7a:bf:b5:5e:2d:42:fb: + 99:f8:22:58:e5:bf:79:b8:8a:74:7e:c6:94:14:d9:f2:27:63: + b6:e5:74:21:5b:59:fb:f6:c8:a9:28:fb:60:f7:5e:bd:c2:e6: + 74:24:14:96:61:95:6c:c2:66:b4:52:25:a1:85:5a:97:e5:68: + 5c:62:cf:69:3b:b0:a9:56:d8:e3:5f:74:dc:84:18:d5:3e:4f: + c9:35:39:26:88:dc:9b:80:d9:40:e1:4f:09:27:8d:d2:89:55: + 30:91:02:86:35:04:95:1e:1d:58:14:5b:c6:e0:2e:a7:bf:a8: + f6:2b:76:8a:4e:71:79:bc:c0:04:cd:db:81:73:46:ce:68:ed: + 25:b0:0e:42:8d:96:64:77:3b:f4:9d:1a:c9:f6:78:4c:56:4f: + 92:17:29:3d:80:50:71:77:4b:a8:29:c2:12:fc:ad:0a:37:81: + 38:4c:fb:54:99:4d:12:5f:98:dc:d1:a9:7b:08:45:c4:6f:7e: + fe:00:e0:db:79:fe:d1:28:e3:8e:82:d1:fb:bc:0a:c4:42:93: + c9:5e:eb:ba +-----BEGIN CERTIFICATE----- +MIIDFTCCAf2gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDcyMTUzMTlaGA8yMDUwMDYyNTIxNTMxOVowKjEoMCYGA1UEAwwf +aHR0cHMtYW5kLWh0dHAubmdpbngtcHJveHkudGVzdDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBALeXhdF/a1Ap84e3TvUlQGrY+qFjPEouaErGizjfB4HQ +CJ38F/U3KH8x5vOBKE4itr2iTvIv5Q/dVTzhBIRMRRsarrfwKtpDBXGRkrjRSf6A +ClO5ZtpUYJr84bLoKEh/lpQ8kqOyN/Z6wt4LEvCuTpL+LcGylSgfiI15mYEZriKk +lfWf2yWOHc9DzW+Fk1957vjz1ILh6U3Jra5bkkM6PHFRcPc+vRskUmqjz1RyV+3+ +cuqWm1oCAqffhbdorh4Hd59ZpaCLKMLIt7uKQlDfBXO/nFUTtYJ5d0BXpI+IpXFQ +13CwTQzZhrOb24ogvRloEFItU7oOLhytgFS7tsmrETkCAwEAAaMuMCwwKgYDVR0R +BCMwIYIfaHR0cHMtYW5kLWh0dHAubmdpbngtcHJveHkudGVzdDANBgkqhkiG9w0B +AQsFAAOCAQEALPPlRz+OWiix3+WVUIVvJy+mjfFez9/iUmaXYTZZgSYlGZnJk+WF +y8ppr0sho9J6v7VeLUL7mfgiWOW/ebiKdH7GlBTZ8idjtuV0IVtZ+/bIqSj7YPde +vcLmdCQUlmGVbMJmtFIloYVal+VoXGLPaTuwqVbY41903IQY1T5PyTU5Jojcm4DZ +QOFPCSeN0olVMJEChjUElR4dWBRbxuAup7+o9it2ik5xebzABM3bgXNGzmjtJbAO +Qo2WZHc79J0ayfZ4TFZPkhcpPYBQcXdLqCnCEvytCjeBOEz7VJlNEl+Y3NGpewhF +xG9+/gDg23n+0SjjjoLR+7wKxEKTyV7rug== +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key b/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key new file mode 100644 index 0000000..11f5210 --- /dev/null +++ b/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAt5eF0X9rUCnzh7dO9SVAatj6oWM8Si5oSsaLON8HgdAInfwX +9TcofzHm84EoTiK2vaJO8i/lD91VPOEEhExFGxqut/Aq2kMFcZGSuNFJ/oAKU7lm +2lRgmvzhsugoSH+WlDySo7I39nrC3gsS8K5Okv4twbKVKB+IjXmZgRmuIqSV9Z/b +JY4dz0PNb4WTX3nu+PPUguHpTcmtrluSQzo8cVFw9z69GyRSaqPPVHJX7f5y6pab +WgICp9+Ft2iuHgd3n1mloIsowsi3u4pCUN8Fc7+cVRO1gnl3QFekj4ilcVDXcLBN +DNmGs5vbiiC9GWgQUi1Tug4uHK2AVLu2yasROQIDAQABAoIBACT4KSVHoEdzOyvw +GME6sB8T9Fw9TG2vrKaqFmzsVGmqh6Gwmu5xHgGG/fe44XHigaPsJDOWu2yXaEur +ECrH5P6RP++gODDdYCI/ayk2U80g4XN8mR6L8Swkkhphr4Lx1lOhYvH9uFE05Tqr +RjQbFY16C6K+oFSFDQ1YGDYsAqnM3RD7PH+lHpo8UN1TO/vogdSQEpMYZDwLAYnW +uD5G3c0u2PsGu9YLuz2p8hcs3chh+cqKJWXOeW0JLrNGx1bqeQWkn6nXRDdRYi9V +cJlTgDqGuF54bieSyq9ABDZQP4Ol+moYKDoIz5PwurNjcYSklrT1tw0gqHZoQK1L +fDjw3QECgYEA7QMRU1AFKTvO7/8WLHLN5BT63n31wm0e9PYpz/XVLWEfxBcp9Xmf +xAIhXZ/U9P4dfNqxTjN9mVGzCHh5KfDJnUFqOXFy/zvfMeRzJf6dJo6/4OX9Bijr +Tgd454vyGXYQP2t+F14UAwl6vlGOAjttiP5qY5Ef1gllBEeIPe9Ts9kCgYEAxkzZ +pq4HJ/5/iDquMEHXNXzpNPavSvgxQdl1ILvJ49LJImmQFBCP9PqiOTIfePz1OqUI +C4baFuc0FEDJ3x9CUNmMY1lEi2ZUq2agPSXaQNsMcKtEJH8SoJlJIRpkQA7unX09 +zb4dam6g79OaGmb8scePuezXMLv1Ee6WWtXbzGECgYEA6PYn9Gzl9cacu9dOUzgw +2ewpPcIvawDY+cxwAsHO3MDneVWPX4JBoGa7pwvwRTL1hwBqYMRJwwbD5CKObcQI +V/KxV28Eqo2N77tt1z2x9/E99u/4yTI1P0gm9ejfeVlL1RpyIMPPBcEujZ0Z6WXC +X3I63k0KLtajHRa2erIf4tkCgYAfunAgwTuX5JqXO3xfcEl033WY6deGUUvgU2Dw +Sdu1viY8gVNyQmwmMGwAZsquWxsJtRoibgM7IucsTml+b8v2j7hstP3IqCjn+9Wr +swDG28WTyXNvu31JgP04dLaRoVIAlOdsofym6OiLNvozO0M3VsziXMjZnVlK8zfP +dORkQQKBgQDXAJEJPygxVA+bF104dzCMWGmU7K8ShEWC5eOdKK4KWf9bNDpY6M6c +i6zga/xBbj7e3Bxqprpp8Wy2gIsnYiVo4V9EQethbLdomPxOpBMNMARw81rL1CpO +jbHB7bIDcKs2tQoZEXUW86ZxC8sdaDaWTJTfUO0RpJow6ZO3yvxVIQ== +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt b/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt new file mode 100644 index 0000000..a93e728 --- /dev/null +++ b/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt @@ -0,0 +1,71 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Feb 7 21:53:49 2023 GMT + Not After : Jun 25 21:53:49 2050 GMT + Subject: CN=https-only.nginx-proxy.test + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:d9:87:48:02:85:f4:5f:0d:90:7e:4c:4f:13:89: + 41:ca:41:15:c2:6f:fd:a8:c7:17:83:c6:dd:8c:fe: + 19:a4:b2:6b:0b:35:4f:b4:3d:7c:40:0a:04:33:2a: + fd:10:72:f7:63:63:99:5b:3d:ec:78:ee:c6:4d:c8: + 0e:4c:be:f2:3f:e3:02:74:57:9a:c1:fe:15:95:63: + 4e:e7:2c:eb:70:f2:6b:c8:ba:01:a2:ca:a1:c7:76: + ff:38:e4:c2:b0:66:fc:85:d2:af:0f:22:81:d4:82: + eb:d5:b0:e6:69:14:37:dd:8d:ad:29:ce:93:68:5a: + ce:f4:77:76:6f:78:13:b6:c8:2f:fe:e0:b6:7e:fb: + 29:16:be:e2:f5:45:3b:39:5b:52:dc:26:b7:ca:0c: + b6:1c:fc:a8:38:0b:dd:c1:f4:04:9b:2d:38:c9:a5: + 2d:3e:f1:42:88:53:a2:3b:17:cf:d5:3c:2b:d6:6a: + 7f:6f:05:8d:c5:b7:5d:64:1e:83:1b:e7:ec:80:3d: + 6d:34:c1:66:b2:e6:5d:d9:a7:6e:46:75:14:bf:10: + 16:c5:fc:47:8e:63:fa:e5:b4:bd:f2:b9:e0:cb:ea: + 75:f9:68:ee:7d:8f:ea:8f:1a:9f:34:27:7a:4a:9f: + 85:fd:3e:17:a7:96:c3:d0:4e:50:a2:a2:e0:45:92: + d0:b5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:https-only.nginx-proxy.test + Signature Algorithm: sha256WithRSAEncryption + 8a:52:46:42:a9:74:18:6a:52:90:ef:a4:e9:c5:54:d5:97:3a: + ff:8b:c2:76:4f:9e:47:aa:e1:ea:e5:b9:af:9d:33:e3:85:17: + 54:7d:32:bd:ac:90:3f:5c:d2:a1:42:17:52:2b:b1:83:e5:c3: + bf:81:f0:e7:38:e2:88:67:7b:d8:59:fe:f9:94:99:ba:be:f4: + 3c:24:b2:c7:9e:f0:98:21:c6:2d:c2:e8:f3:67:bd:62:00:aa: + ce:34:fa:b4:53:6d:c1:09:5e:55:bd:43:aa:86:c6:f8:c5:83: + 46:3a:49:12:a2:ec:30:36:0c:99:44:74:09:9d:cc:4b:98:1f: + 7e:c9:9b:68:a0:f8:1e:00:14:d0:da:2a:bf:c8:ca:a8:1c:10: + b5:68:a2:f1:41:93:0c:f3:3f:c0:c6:53:3c:8d:a7:dd:a5:7b: + 35:cc:44:e0:5b:6d:c5:cb:33:6f:c1:43:7e:06:df:21:99:11: + b3:91:41:b4:5e:f0:37:1e:8e:e5:73:85:dc:4a:21:d5:41:f9: + 4e:b8:f5:ed:21:93:09:91:c2:8c:6b:04:a4:84:ab:3a:fe:35: + 64:fa:6b:a7:8d:40:a6:64:89:30:84:ac:28:99:5a:01:79:77: + c0:df:88:da:a9:75:5f:c4:51:ae:a8:45:7b:d2:e1:a2:81:29: + 60:cd:7b:cd +-----BEGIN CERTIFICATE----- +MIIDDTCCAfWgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yMzAyMDcyMTUzNDlaGA8yMDUwMDYyNTIxNTM0OVowJjEkMCIGA1UEAwwb +aHR0cHMtb25seS5uZ2lueC1wcm94eS50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA2YdIAoX0Xw2QfkxPE4lBykEVwm/9qMcXg8bdjP4ZpLJrCzVP +tD18QAoEMyr9EHL3Y2OZWz3seO7GTcgOTL7yP+MCdFeawf4VlWNO5yzrcPJryLoB +osqhx3b/OOTCsGb8hdKvDyKB1ILr1bDmaRQ33Y2tKc6TaFrO9Hd2b3gTtsgv/uC2 +fvspFr7i9UU7OVtS3Ca3ygy2HPyoOAvdwfQEmy04yaUtPvFCiFOiOxfP1Twr1mp/ +bwWNxbddZB6DG+fsgD1tNMFmsuZd2aduRnUUvxAWxfxHjmP65bS98rngy+p1+Wju +fY/qjxqfNCd6Sp+F/T4Xp5bD0E5QoqLgRZLQtQIDAQABoyowKDAmBgNVHREEHzAd +ghtodHRwcy1vbmx5Lm5naW54LXByb3h5LnRlc3QwDQYJKoZIhvcNAQELBQADggEB +AIpSRkKpdBhqUpDvpOnFVNWXOv+LwnZPnkeq4erlua+dM+OFF1R9Mr2skD9c0qFC +F1IrsYPlw7+B8Oc44ohne9hZ/vmUmbq+9Dwkssee8Jghxi3C6PNnvWIAqs40+rRT +bcEJXlW9Q6qGxvjFg0Y6SRKi7DA2DJlEdAmdzEuYH37Jm2ig+B4AFNDaKr/Iyqgc +ELVoovFBkwzzP8DGUzyNp92lezXMROBbbcXLM2/BQ34G3yGZEbORQbRe8DcejuVz +hdxKIdVB+U649e0hkwmRwoxrBKSEqzr+NWT6a6eNQKZkiTCErCiZWgF5d8DfiNqp +dV/EUa6oRXvS4aKBKWDNe80= +-----END CERTIFICATE----- diff --git a/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key b/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key new file mode 100644 index 0000000..17976ce --- /dev/null +++ b/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2YdIAoX0Xw2QfkxPE4lBykEVwm/9qMcXg8bdjP4ZpLJrCzVP +tD18QAoEMyr9EHL3Y2OZWz3seO7GTcgOTL7yP+MCdFeawf4VlWNO5yzrcPJryLoB +osqhx3b/OOTCsGb8hdKvDyKB1ILr1bDmaRQ33Y2tKc6TaFrO9Hd2b3gTtsgv/uC2 +fvspFr7i9UU7OVtS3Ca3ygy2HPyoOAvdwfQEmy04yaUtPvFCiFOiOxfP1Twr1mp/ +bwWNxbddZB6DG+fsgD1tNMFmsuZd2aduRnUUvxAWxfxHjmP65bS98rngy+p1+Wju +fY/qjxqfNCd6Sp+F/T4Xp5bD0E5QoqLgRZLQtQIDAQABAoIBAAWs//YA5MVuJy0E +dLO/yxWp6RVvsqCqwTRRBgrdvnGLrjtWosPDLvDE0iM7peq99TKEsMWusfLd2BLD +e4wJF20PUUsT1hflt050juR9SY9i4+kS4WQMAXig5DvpzCKqLUCYpLSyY8zVta2X +tgtb2bFQNwp2N2ZrqCa8zzxNV8ZXGoW+ZlvBJEDtBwt1DCDhY39/pqHfIhFl4Vwk +YhhbVjID145D1j/fP6vLceM2YA4uRmF1itj1iQ6YNNpXRspUGE4DXdqR6HcbduiX +trZjmdtKXY8mJg6jyLZxYbjFlKV/LvqKRYF3Jb9K0vdd4juBdZoy7DQzoLhcnzui +pEnPLakCgYEA9tN6KdQGKGBXGuF+ZqhXfB/XSkKUf8o/5j62cbu11ZIJ+iEBx+d6 +lQAxTz5hHUL6a3c5qiM+AWBxYuFD6oqptIlTlBfIXI978neDNvEWWffivPvQLbt9 +o9ohOirfK1iGPvtrpAwjv5ylE5SiTmJ/6wDvQWjNGAnJ3aaxkesJUSMCgYEA4Z0K +UHZVtnKLtzzIY7KfLbuKF/fJEDfMNr4Wgl6ny21vqO9kJGmA7SaoNdhx8RDcKmeV +/Vey4ug6YlOG48eapKLTthdRz5mx+jIkUfdOhj81m28xm/OPTqCrviTHCNOHeYDy +NKAIlJMo2z0vTKJn5eP6CsYmDWLpHQNyXY5qcEcCgYAzDBWt5O3JF/Or2Yr8zEAb +qbIq544yx69jfQDakMnQe72Yf48Quuz9N+b6zpnjJWEJLMU+TL+cJUgN/SzAqyDh +96zTaf/ENOCbiuAWUtIelUfNcf7iFm6rnodUsl0pZ8uL5w+iA+i4zjrNy+WtdG2k +OrNAwd345L1dHAaJeSSaJQKBgQCUnF3r7Fa/TCpt87LHwSQK+sqWyRf+/9IbiRDI +pVL/s8FmVPHw7jIHhHwuo7lCImnz4LGy5C6oOnIizIRAy/04Ty0Hd8ri5YmPlbHI +8A8gbMiB7zeNU1zlXP5jzFPyo2tMhLyGH5gnTdwOtfnPD/dCPe45ZJYyISIOg3O0 +3peMBwKBgH20cskAOCNclfoG+Nis52h8FqmDlflJ8waUarvk26JhO1e009kOytw8 +x/qSuttpGtTG+4fdc2wJvFNczr4h9ZlftBdgZXj8PKgRpcIe8q97Xg8PUj+Xfu/t +vD/QV+tVcGoAMsQq4NeFxiTbPfwVyXdYFT1XVCu6JEdLL+gpWh5W +-----END RSA PRIVATE KEY----- diff --git a/test/test_fallback.data/nodefault.yml b/test/test_fallback.data/nodefault.yml new file mode 100644 index 0000000..ecd4359 --- /dev/null +++ b/test/test_fallback.data/nodefault.yml @@ -0,0 +1,36 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./nodefault.certs:/etc/nginx/certs:ro + https-and-http: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: https-and-http.nginx-proxy.test + https-only: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: https-only.nginx-proxy.test + HTTPS_METHOD: nohttp + http-only: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: http-only.nginx-proxy.test + HTTPS_METHOD: nohttps + missing-cert: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: missing-cert.nginx-proxy.test diff --git a/test/test_fallback.py b/test/test_fallback.py index 1e687a3..cdaeef3 100644 --- a/test/test_fallback.py +++ b/test/test_fallback.py @@ -1,4 +1,5 @@ import os.path +import re import backoff import pytest @@ -31,6 +32,9 @@ def get(docker_compose, nginxproxy, want_err_re): return _get +INTERNAL_ERR_RE = re.compile("TLSV1_ALERT_INTERNAL_ERROR") + + @pytest.mark.parametrize("compose_file,url,want_code,want_err_re", [ # Has default.crt. ("withdefault.yml", "http://https-and-http.nginx-proxy.test/", 301, None), @@ -43,6 +47,17 @@ def get(docker_compose, nginxproxy, want_err_re): ("withdefault.yml", "https://missing-cert.nginx-proxy.test/", 500, None), ("withdefault.yml", "http://unknown.nginx-proxy.test/", 503, None), ("withdefault.yml", "https://unknown.nginx-proxy.test/", 503, None), + # Same as withdefault.yml, except there is no default.crt. + ("nodefault.yml", "http://https-and-http.nginx-proxy.test/", 301, None), + ("nodefault.yml", "https://https-and-http.nginx-proxy.test/", 200, None), + ("nodefault.yml", "http://https-only.nginx-proxy.test/", 503, None), + ("nodefault.yml", "https://https-only.nginx-proxy.test/", 200, None), + ("nodefault.yml", "http://http-only.nginx-proxy.test/", 200, None), + ("nodefault.yml", "https://http-only.nginx-proxy.test/", None, INTERNAL_ERR_RE), + ("nodefault.yml", "http://missing-cert.nginx-proxy.test/", 200, None), + ("nodefault.yml", "https://missing-cert.nginx-proxy.test/", None, INTERNAL_ERR_RE), + ("nodefault.yml", "http://unknown.nginx-proxy.test/", 503, None), + ("nodefault.yml", "https://unknown.nginx-proxy.test/", None, INTERNAL_ERR_RE), ]) def test_fallback(get, url, want_code, want_err_re): if want_err_re is None: From 9b4bb07b348dc5a428b94416517291adb30794c3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 4 Feb 2023 18:59:38 -0500 Subject: [PATCH 065/105] fix: Don't create fallback http(s) server when http(s) disabled Before, a fallback http server was created to handle requests for unknown virtual hosts even when `HTTPS_METHOD=nohttp`. (In this case, all http vhosts would be unknown.) Likewise, a catch-all fallback https server was still created even if `HTTPS_METHOD=nohttps`. Now the fallback servers are created only if needed. This brings the behavior in line with the documentation and user expectation. It will also make it easier to implement a planned feature: different servers on different ports. --- nginx.tmpl | 124 +++++++++++------- test/test_fallback.data/nohttp-on-app.yml | 16 +++ .../nohttp-with-missing-cert.yml | 22 ++++ test/test_fallback.data/nohttp.yml | 15 +++ test/test_fallback.data/nohttps-on-app.yml | 15 +++ test/test_fallback.data/nohttps.yml | 14 ++ test/test_fallback.py | 31 +++++ test/test_ssl/test_nohttp.py | 7 +- 8 files changed, 194 insertions(+), 50 deletions(-) create mode 100644 test/test_fallback.data/nohttp-on-app.yml create mode 100644 test/test_fallback.data/nohttp-with-missing-cert.yml create mode 100644 test/test_fallback.data/nohttp.yml create mode 100644 test/test_fallback.data/nohttps-on-app.yml create mode 100644 test/test_fallback.data/nohttps.yml diff --git a/nginx.tmpl b/nginx.tmpl index d2ccd8f..5733351 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -20,6 +20,7 @@ {{- $_ := set $globals "access_log" (or (and (not $globals.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }} {{- $_ := set $globals "enable_ipv6" (parseBool (coalesce $globals.Env.ENABLE_IPV6 "false")) }} {{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }} +{{- $_ := set $globals "vhosts" (dict) }} {{- $_ := set $globals "networks" (dict) }} # Networks available to the container running docker-gen (which are assumed to # match the networks available to the container running nginx): @@ -346,22 +347,80 @@ proxy_set_header X-Original-URI $request_uri; proxy_set_header Proxy ""; {{- end }} +{{- /* + * Precompute some information about each vhost. This is done early because + * the creation of fallback servers depends on DEFAULT_HOST, HTTPS_METHOD, + * and whether there are any missing certs. + */}} +{{- range $vhost, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }} + {{- $vhost := trim $vhost }} + {{- if not $vhost }} + {{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}} + {{- continue }} + {{- end }} + {{- $certName := first (groupByKeys $containers "Env.CERT_NAME") }} + {{- $vhostCert := closest (dir "/etc/nginx/certs") (printf "%s.crt" $vhost) }} + {{- $vhostCert = trimSuffix ".crt" $vhostCert }} + {{- $vhostCert = trimSuffix ".key" $vhostCert }} + {{- $cert := or $certName $vhostCert }} + {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} + {{- $default := eq $globals.Env.DEFAULT_HOST $vhost }} + {{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) $globals.Env.HTTPS_METHOD "redirect" }} + {{- $_ := set $globals.vhosts $vhost (dict "cert" $cert "cert_ok" $cert_ok "containers" $containers "default" $default "https_method" $https_method) }} +{{- end }} + +{{- /* + * If needed, create a catch-all fallback server to send an error code to + * clients that request something from an unknown vhost. + */}} +{{- block "fallback_server" $globals }} + {{- $globals := . }} + {{- $http_exists := false }} + {{- $https_exists := false }} + {{- $default_http_exists := false }} + {{- $default_https_exists := false }} + {{- range $vhost := $globals.vhosts }} + {{- $http := or (ne $vhost.https_method "nohttp") (not $vhost.cert_ok) }} + {{- $https := ne $vhost.https_method "nohttps" }} + {{- $http_exists = or $http_exists $http }} + {{- $https_exists = or $https_exists $https }} + {{- $default_http_exists = or $default_http_exists (and $http $vhost.default) }} + {{- $default_https_exists = or $default_https_exists (and $https $vhost.default) }} + {{- end }} + {{- $fallback_http := and $http_exists (not $default_http_exists) }} + {{- $fallback_https := and $https_exists (not $default_https_exists) }} + {{- /* + * If there are no vhosts at all, create fallbacks for both plain http + * and https so that clients get something more useful than a connection + * refused error. + */}} + {{- if and (not $http_exists) (not $https_exists) }} + {{- $fallback_http = true }} + {{- $fallback_https = true }} + {{- end }} + {{- if or $fallback_http $fallback_https }} server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; - listen {{ $globals.external_http_port }}; - listen {{ $globals.external_https_port }} ssl http2; -{{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_http_port }}; - listen [::]:{{ $globals.external_https_port }} ssl http2; -{{- end }} + {{- if $fallback_http }} + listen {{ $globals.external_http_port }} default_server; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_http_port }} default_server; + {{- end }} + {{- end }} + {{- if $fallback_https }} + listen {{ $globals.external_https_port }} ssl http2 default_server; + {{- if $globals.enable_ipv6 }} + listen [::]:{{ $globals.external_https_port }} ssl http2 default_server; + {{- end }} + {{- end }} {{ $globals.access_log }} -{{- if $globals.default_cert_ok }} + {{- if $globals.default_cert_ok }} ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; -{{- else }} + {{- else }} # No default.crt certificate found for this vhost, so force nginx to emit a # TLS error if the client connects via https. {{- /* See the comment in the main `server` directive for rationale. */}} @@ -372,17 +431,19 @@ server { if ($https) { return 444; } -{{- end }} + {{- end }} return 503; } - -{{- range $host, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }} - - {{- $host := trim $host }} - {{- if not $host }} - {{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}} - {{- continue }} {{- end }} +{{- end }} + +{{- range $host, $vhost := $globals.vhosts }} + {{- $cert := $vhost.cert }} + {{- $cert_ok := $vhost.cert_ok }} + {{- $containers := $vhost.containers }} + {{- $default_server := when $vhost.default "default_server" "" }} + {{- $https_method := $vhost.https_method }} + {{- $is_regexp := hasPrefix "~" $host }} {{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }} @@ -402,22 +463,12 @@ server { {{ template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers) }} {{- end }} - {{- $default_host := or ($globals.Env.DEFAULT_HOST) "" }} - {{- $default_server := index (dict $host "" $default_host "default_server") $host }} - {{- /* * 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 HTTPS_METHOD defined by containers w/ the same vhost, falling - * back to "redirect". - */}} - {{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $globals.Env.HTTPS_METHOD "redirect") }} - {{- /* * Get the SSL_POLICY defined by containers w/ the same vhost, falling * back to empty string (use default). @@ -433,27 +484,6 @@ server { {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} {{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} - - {{- /* Get the first cert name defined by containers w/ the same vhost */}} - {{- $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} - - {{- /* Get the best matching cert by name for the vhost. */}} - {{- $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} - - {{- /* - * vhostCert is actually a filename so remove any suffixes since they - * are added later. - */}} - {{- $vhostCert := trimSuffix ".crt" $vhostCert }} - {{- $vhostCert := trimSuffix ".key" $vhostCert }} - - {{- /* - * Use the cert specified on the container or fallback to the best vhost - * match. - */}} - {{- $cert := (coalesce $certName $vhostCert) }} - {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} - {{- if and $cert_ok (eq $https_method "redirect") }} server { server_name {{ $host }}; diff --git a/test/test_fallback.data/nohttp-on-app.yml b/test/test_fallback.data/nohttp-on-app.yml new file mode 100644 index 0000000..d81c9ca --- /dev/null +++ b/test/test_fallback.data/nohttp-on-app.yml @@ -0,0 +1,16 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./withdefault.certs:/etc/nginx/certs:ro + environment: + HTTPS_METHOD: redirect + https-only: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + HTTPS_METHOD: nohttp + VIRTUAL_HOST: https-only.nginx-proxy.test diff --git a/test/test_fallback.data/nohttp-with-missing-cert.yml b/test/test_fallback.data/nohttp-with-missing-cert.yml new file mode 100644 index 0000000..3593a32 --- /dev/null +++ b/test/test_fallback.data/nohttp-with-missing-cert.yml @@ -0,0 +1,22 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./withdefault.certs:/etc/nginx/certs:ro + environment: + HTTPS_METHOD: nohttp + https-only: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: https-only.nginx-proxy.test + missing-cert: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: missing-cert.nginx-proxy.test diff --git a/test/test_fallback.data/nohttp.yml b/test/test_fallback.data/nohttp.yml new file mode 100644 index 0000000..3ed0c0e --- /dev/null +++ b/test/test_fallback.data/nohttp.yml @@ -0,0 +1,15 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./withdefault.certs:/etc/nginx/certs:ro + environment: + HTTPS_METHOD: nohttp + https-only: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: https-only.nginx-proxy.test diff --git a/test/test_fallback.data/nohttps-on-app.yml b/test/test_fallback.data/nohttps-on-app.yml new file mode 100644 index 0000000..690d656 --- /dev/null +++ b/test/test_fallback.data/nohttps-on-app.yml @@ -0,0 +1,15 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + HTTPS_METHOD: redirect + http-only: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + HTTPS_METHOD: nohttps + VIRTUAL_HOST: http-only.nginx-proxy.test diff --git a/test/test_fallback.data/nohttps.yml b/test/test_fallback.data/nohttps.yml new file mode 100644 index 0000000..f07ddf9 --- /dev/null +++ b/test/test_fallback.data/nohttps.yml @@ -0,0 +1,14 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + HTTPS_METHOD: nohttps + http-only: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: http-only.nginx-proxy.test diff --git a/test/test_fallback.py b/test/test_fallback.py index cdaeef3..ce3d68f 100644 --- a/test/test_fallback.py +++ b/test/test_fallback.py @@ -33,6 +33,7 @@ def get(docker_compose, nginxproxy, want_err_re): INTERNAL_ERR_RE = re.compile("TLSV1_ALERT_INTERNAL_ERROR") +CONNECTION_REFUSED_RE = re.compile("Connection refused") @pytest.mark.parametrize("compose_file,url,want_code,want_err_re", [ @@ -58,6 +59,36 @@ INTERNAL_ERR_RE = re.compile("TLSV1_ALERT_INTERNAL_ERROR") ("nodefault.yml", "https://missing-cert.nginx-proxy.test/", None, INTERNAL_ERR_RE), ("nodefault.yml", "http://unknown.nginx-proxy.test/", 503, None), ("nodefault.yml", "https://unknown.nginx-proxy.test/", None, INTERNAL_ERR_RE), + # HTTPS_METHOD=nohttp on nginx-proxy, HTTPS_METHOD unset on the app container. + ("nohttp.yml", "http://https-only.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttp.yml", "https://https-only.nginx-proxy.test/", 200, None), + ("nohttp.yml", "http://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttp.yml", "https://unknown.nginx-proxy.test/", 503, None), + # HTTPS_METHOD=redirect on nginx-proxy, HTTPS_METHOD=nohttp on the app container. + ("nohttp-on-app.yml", "http://https-only.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttp-on-app.yml", "https://https-only.nginx-proxy.test/", 200, None), + ("nohttp-on-app.yml", "http://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttp-on-app.yml", "https://unknown.nginx-proxy.test/", 503, None), + # Same as nohttp.yml, except there is a vhost with a missing cert. This causes its + # HTTPS_METHOD=nohttp setting to effectively become HTTPS_METHOD=noredirect. This means that + # there will be a plain http server solely to support that vhost, so http requests to other + # vhosts get a 503, not a connection refused error. + ("nohttp-with-missing-cert.yml", "http://https-only.nginx-proxy.test/", 503, None), + ("nohttp-with-missing-cert.yml", "https://https-only.nginx-proxy.test/", 200, None), + ("nohttp-with-missing-cert.yml", "http://missing-cert.nginx-proxy.test/", 200, None), + ("nohttp-with-missing-cert.yml", "https://missing-cert.nginx-proxy.test/", 500, None), + ("nohttp-with-missing-cert.yml", "http://unknown.nginx-proxy.test/", 503, None), + ("nohttp-with-missing-cert.yml", "https://unknown.nginx-proxy.test/", 503, None), + # HTTPS_METHOD=nohttps on nginx-proxy, HTTPS_METHOD unset on the app container. + ("nohttps.yml", "http://http-only.nginx-proxy.test/", 200, None), + ("nohttps.yml", "https://http-only.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttps.yml", "http://unknown.nginx-proxy.test/", 503, None), + ("nohttps.yml", "https://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + # HTTPS_METHOD=redirect on nginx-proxy, HTTPS_METHOD=nohttps on the app container. + ("nohttps-on-app.yml", "http://http-only.nginx-proxy.test/", 200, None), + ("nohttps-on-app.yml", "https://http-only.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttps-on-app.yml", "http://unknown.nginx-proxy.test/", 503, None), + ("nohttps-on-app.yml", "https://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), ]) def test_fallback(get, url, want_code, want_err_re): if want_err_re is None: diff --git a/test/test_ssl/test_nohttp.py b/test/test_ssl/test_nohttp.py index d7f0d92..5b650db 100644 --- a/test/test_ssl/test_nohttp.py +++ b/test/test_ssl/test_nohttp.py @@ -1,9 +1,10 @@ import pytest +import requests -def test_web2_http_is_not_forwarded(docker_compose, nginxproxy): - r = nginxproxy.get("http://web2.nginx-proxy.tld/", allow_redirects=False) - assert r.status_code == 503 +def test_web2_http_is_connection_refused(docker_compose, nginxproxy): + with pytest.raises(requests.exceptions.RequestException, match="Connection refused"): + nginxproxy.get("http://web2.nginx-proxy.tld/") def test_web2_https_is_forwarded(docker_compose, nginxproxy): From c10c7bcbe93cd2060c5fbfabbde6b07e29131627 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 9 Feb 2023 08:10:43 +0100 Subject: [PATCH 066/105] build: dockergen 0.9.4 -> 0.10.0 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7669314..e178013 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.9.4 +ARG DOCKER_GEN_VERSION=0.10.0 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 6902b41..0d2f560 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.9.4 +ARG DOCKER_GEN_VERSION=0.10.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From de4386e44070ce3a35a360e533d99c448bf10b6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 04:57:57 +0000 Subject: [PATCH 067/105] build: bump golang from 1.20.0 to 1.20.1 Bumps golang from 1.20.0 to 1.20.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 e178013..0f42c8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.10.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.20.0 as gobuilder +FROM golang:1.20.1 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 0d2f560..724bdf1 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.10.0 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.20.0-alpine as gobuilder +FROM golang:1.20.1-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 6207be5f8ff62ecd9a446c92506ab06b51e4ba01 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 17 Feb 2023 01:52:05 -0500 Subject: [PATCH 068/105] fix: Partially revert "chore: Remove support for legacy swarm" This partially reverts commit 2494e207843c92a715da3e8e65ed763fd0d2d624 by ignoring any network named "ingress" when searching for a container's IP address. That commit was technically a backwards-incompatible change: Some users use nginx-proxy with Swarm mode even though it is not fully supported. In such cases nginx-proxy should ignore the `ingress` network, otherwise nginx will not be able to reach the server (container-to-container traffic apparently doesn't work over the Swarm `ingress` network). The parts of that commit that examine the `SwarmNode` structure are not reverted here because docker-gen does not currently populate that structure -- not even when both docker-gen and the service task container are running on the same manager node. --- nginx.tmpl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nginx.tmpl b/nginx.tmpl index 2ec7b43..1388636 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -41,6 +41,14 @@ {{- $ip := "" }} # networks: {{- range sortObjectsByKeysAsc $.container.Networks "Name" }} + {{- /* + * TODO: Only ignore the "ingress" network for Swarm tasks (in case + * the user is not using Swarm mode and names a network "ingress"). + */}} + {{- if eq .Name "ingress" }} + # {{ .Name }} (ignored) + {{- continue }} + {{- end }} {{- if and (not (index $.globals.networks .Name)) (not $.globals.networks.host) }} # {{ .Name }} (unreachable) {{- continue }} From 01745a836fc6d5687c26efff26c5713b8e4b5d2f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 02:44:12 -0500 Subject: [PATCH 069/105] tests: Fix path to `ca-root.crt` `os.getcwd()` is not guaranteed to always return the `test/` directory. --- test/conftest.py | 6 ++++++ test/test_ssl/test_dhparam.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 1121e96..128f57d 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -425,6 +425,12 @@ def connect_to_all_networks(): # ############################################################################### + +@pytest.fixture +def ca_root_certificate(): + return CA_ROOT_CERTIFICATE + + @pytest.fixture(scope="module") def docker_compose(request): """ diff --git a/test/test_ssl/test_dhparam.py b/test/test_ssl/test_dhparam.py index 2f69ad3..d4d64a3 100644 --- a/test/test_ssl/test_dhparam.py +++ b/test/test_ssl/test_dhparam.py @@ -1,6 +1,5 @@ import re import subprocess -import os import backoff import docker @@ -219,7 +218,7 @@ def test_custom_dhparam_is_supported(docker_compose): # Only `web2` has a site-specific DH param file (which overrides all other DH config) # Other tests here use `web5` explicitly, or implicitly (via ENV `DEFAULT_HOST`, otherwise first HTTPS server) -def test_custom_dhparam_is_supported_per_site(docker_compose): +def test_custom_dhparam_is_supported_per_site(docker_compose, ca_root_certificate): container_name="dh-file" sut_container = docker_client.containers.get(container_name) assert sut_container.status == "running" @@ -242,7 +241,7 @@ def test_custom_dhparam_is_supported_per_site(docker_compose): # - `web2` has it's own cert provisioned at `/etc/nginx/certs/web2.nginx-proxy.tld.crt`. can_verify_chain_of_trust( sut_container, - ca_cert = f"{os.getcwd()}/certs/ca-root.crt", + ca_cert = ca_root_certificate, fqdn = 'web2.nginx-proxy.tld' ) From 09a2f40633a9c77179b3e26523fda04aab86c9ea Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 6 Jan 2023 01:25:35 -0500 Subject: [PATCH 070/105] tests: Turn helper function into `docker_compose_file` fixture This makes it easier for tests to override the filename. --- test/conftest.py | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 128f57d..3cffe9b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -322,31 +322,28 @@ def wait_for_nginxproxy_to_be_ready(): logging.debug("nginx-proxy ready") break -def find_docker_compose_file(request): - """ - helper for fixture functions to figure out the name of the docker-compose file to consider. - - if the test module provides a `docker_compose_file` variable, take that - - else, if a yaml file exists with the same name as the test module (but for the `.yml` extension), use that - - otherwise use `docker-compose.yml`. +@pytest.fixture(scope="module") +def docker_compose_file(request): + """Fixture naming the docker-compose file to consider. + + If a YAML file exists with the same name as the test module (with the `.py` extension replaced + with `.yml` or `.yaml`), use that. Otherwise, use `docker-compose.yml` in the same directory + as the test module. + + Tests can override this fixture to specify a custom location. """ test_module_dir = os.path.dirname(request.module.__file__) yml_file = os.path.join(test_module_dir, request.module.__name__ + '.yml') yaml_file = os.path.join(test_module_dir, request.module.__name__ + '.yaml') default_file = os.path.join(test_module_dir, 'docker-compose.yml') - docker_compose_file_module_variable = getattr(request.module, "docker_compose_file", None) - if docker_compose_file_module_variable is not None: - docker_compose_file = os.path.join( test_module_dir, docker_compose_file_module_variable) - if not os.path.isfile(docker_compose_file): - raise ValueError(f"docker compose file {docker_compose_file!r} could not be found. Check your test module `docker_compose_file` variable value.") + if os.path.isfile(yml_file): + docker_compose_file = yml_file + elif os.path.isfile(yaml_file): + docker_compose_file = yaml_file else: - if os.path.isfile(yml_file): - docker_compose_file = yml_file - elif os.path.isfile(yaml_file): - docker_compose_file = yaml_file - else: - docker_compose_file = default_file + docker_compose_file = default_file if not os.path.isfile(docker_compose_file): logging.error("Could not find any docker-compose file named either '{0}.yml', '{0}.yaml' or 'docker-compose.yml'".format(request.module.__name__)) @@ -432,16 +429,15 @@ def ca_root_certificate(): @pytest.fixture(scope="module") -def docker_compose(request): - """ - pytest fixture providing containers described in a docker compose file. After the tests, remove the created containers +def docker_compose(docker_compose_file): + """Ensures containers described in a docker compose file are started. - A custom docker compose file name can be defined in a variable named `docker_compose_file`. + A custom docker compose file name can be specified by overriding the `docker_compose_file` + fixture. Also, in the case where pytest is running from a docker container, this fixture makes sure our container will be attached to all the docker networks. """ - docker_compose_file = find_docker_compose_file(request) original_dns_resolver = monkey_patch_urllib_dns_resolver() remove_all_containers() docker_compose_up(docker_compose_file) From f5a3492926985b45a6e1f2e5d477d3fc007fc012 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 19:28:30 -0500 Subject: [PATCH 071/105] tests: Factor out DNS monkey patching to its own fixture --- test/conftest.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 3cffe9b..8e80bf4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -429,7 +429,14 @@ def ca_root_certificate(): @pytest.fixture(scope="module") -def docker_compose(docker_compose_file): +def monkey_patched_dns(): + original_dns_resolver = monkey_patch_urllib_dns_resolver() + yield + restore_urllib_dns_resolver(original_dns_resolver) + + +@pytest.fixture(scope="module") +def docker_compose(monkey_patched_dns, docker_compose_file): """Ensures containers described in a docker compose file are started. A custom docker compose file name can be specified by overriding the `docker_compose_file` @@ -438,7 +445,6 @@ def docker_compose(docker_compose_file): Also, in the case where pytest is running from a docker container, this fixture makes sure our container will be attached to all the docker networks. """ - original_dns_resolver = monkey_patch_urllib_dns_resolver() remove_all_containers() docker_compose_up(docker_compose_file) networks = connect_to_all_networks() @@ -448,7 +454,6 @@ def docker_compose(docker_compose_file): for network in networks: disconnect_from_network(network) docker_compose_down(docker_compose_file) - restore_urllib_dns_resolver(original_dns_resolver) @pytest.fixture() From 4d8f878ba7e25c25f0f781bf2f89291b2a2e599e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 19:38:55 -0500 Subject: [PATCH 072/105] tests: Fixture that simplifies Docker compose file changes --- test/conftest.py | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 8e80bf4..13ff689 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -416,6 +416,35 @@ def connect_to_all_networks(): return [connect_to_network(network) for network in networks] +class DockerComposer(contextlib.AbstractContextManager): + def __init__(self): + self._docker_compose_file = None + + def __exit__(self, *exc_info): + self._down() + + def _down(self): + if self._docker_compose_file is None: + return + for network in self._networks: + disconnect_from_network(network) + docker_compose_down(self._docker_compose_file) + self._docker_compose_file = None + + def compose(self, docker_compose_file): + if docker_compose_file == self._docker_compose_file: + return + self._down() + if docker_compose_file is None: + return + remove_all_containers() + docker_compose_up(docker_compose_file) + self._networks = connect_to_all_networks() + wait_for_nginxproxy_to_be_ready() + time.sleep(3) # give time to containers to be ready + self._docker_compose_file = docker_compose_file + + ############################################################################### # # Py.test fixtures @@ -423,6 +452,12 @@ def connect_to_all_networks(): ############################################################################### +@pytest.fixture(scope="module") +def docker_composer(): + with DockerComposer() as d: + yield d + + @pytest.fixture def ca_root_certificate(): return CA_ROOT_CERTIFICATE @@ -436,7 +471,7 @@ def monkey_patched_dns(): @pytest.fixture(scope="module") -def docker_compose(monkey_patched_dns, docker_compose_file): +def docker_compose(monkey_patched_dns, docker_composer, docker_compose_file): """Ensures containers described in a docker compose file are started. A custom docker compose file name can be specified by overriding the `docker_compose_file` @@ -445,15 +480,8 @@ def docker_compose(monkey_patched_dns, docker_compose_file): Also, in the case where pytest is running from a docker container, this fixture makes sure our container will be attached to all the docker networks. """ - remove_all_containers() - docker_compose_up(docker_compose_file) - networks = connect_to_all_networks() - wait_for_nginxproxy_to_be_ready() - time.sleep(3) # give time to containers to be ready + docker_composer.compose(docker_compose_file) yield docker_client - for network in networks: - disconnect_from_network(network) - docker_compose_down(docker_compose_file) @pytest.fixture() From b5a54ac2191041dfa06c151fea6f5a832862c0cb Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 19:56:14 -0500 Subject: [PATCH 073/105] tests: Reduce scope of `docker_compose` fixture (and friends) This makes it possible to bring up different compose files for different tests in the same test module. This change does not negatively affect performance because the fixture is a no-op if the docker compose filename is unchanged between tests. --- test/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 13ff689..e7133e7 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -323,7 +323,7 @@ def wait_for_nginxproxy_to_be_ready(): break -@pytest.fixture(scope="module") +@pytest.fixture def docker_compose_file(request): """Fixture naming the docker-compose file to consider. @@ -463,14 +463,14 @@ def ca_root_certificate(): return CA_ROOT_CERTIFICATE -@pytest.fixture(scope="module") +@pytest.fixture def monkey_patched_dns(): original_dns_resolver = monkey_patch_urllib_dns_resolver() yield restore_urllib_dns_resolver(original_dns_resolver) -@pytest.fixture(scope="module") +@pytest.fixture def docker_compose(monkey_patched_dns, docker_composer, docker_compose_file): """Ensures containers described in a docker compose file are started. From 37134c44d7fbf96bd7f8402c54180a8c05ac50e2 Mon Sep 17 00:00:00 2001 From: Jan Malte Gerth Date: Mon, 20 Feb 2023 22:46:24 +0100 Subject: [PATCH 074/105] fix: Sort networks and ports before iterating This avoids unnecessary nginx restarts caused by config file churn. --- nginx.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 2ec7b43..6ba7954 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -21,7 +21,7 @@ {{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }} {{- $_ := set $globals "networks" (dict) }} # networks available to nginx-proxy: -{{- range $globals.CurrentContainer.Networks }} +{{- range sortObjectsByKeysAsc $globals.CurrentContainer.Networks "Name" }} {{- $_ := set $globals.networks .Name . }} # {{ .Name }} {{- end }} @@ -81,7 +81,7 @@ */}} {{- define "container_port" }} {{- /* If only 1 port exposed, use that as a default, else 80. */}} - # exposed ports:{{ range $.container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} + # exposed ports:{{ range sortObjectsByKeysAsc $.container.Addresses "Port" }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} {{- $default_port := when (eq (len $.container.Addresses) 1) (first $.container.Addresses).Port "80" }} # default port: {{ $default_port }} {{- $port := or $.container.Env.VIRTUAL_PORT $default_port }} From 16b8cde8e4a931eabdb3745675ca904d09acc741 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 28 Feb 2023 08:36:27 +0100 Subject: [PATCH 075/105] build: dockergen 0.10.0 -> 0.10.1 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0f42c8c..43568c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.10.0 +ARG DOCKER_GEN_VERSION=0.10.1 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 724bdf1..b98682a 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.10.0 +ARG DOCKER_GEN_VERSION=0.10.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 2fc3e6c28cbecdd11dc96726171a7f0ffaec7ecd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 05:04:42 +0000 Subject: [PATCH 076/105] ci: bump pytest from 7.2.1 to 7.2.2 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.2.1 to 7.2.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.2.1...7.2.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 6d7d495..422f2b1 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.0.1 -pytest==7.2.1 +pytest==7.2.2 requests==2.28.2 From 9906ccda425e2150beac11adbd2d786d9764c5b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Mar 2023 04:58:06 +0000 Subject: [PATCH 077/105] build: bump golang from 1.20.1-alpine to 1.20.2-alpine Bumps golang from 1.20.1-alpine to 1.20.2-alpine. --- 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 43568c1..249fa6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.10.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.20.1 as gobuilder +FROM golang:1.20.2 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index b98682a..006b045 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.10.1 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.20.1-alpine as gobuilder +FROM golang:1.20.2-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 364beed773e7f70274341a2c5a5e2e58e8fe08fe Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 10 Mar 2023 05:05:21 -0500 Subject: [PATCH 078/105] fix: Don't error if `$globals.CurrentContainer` is `nil` Also: * Note when there are no networks. * Fix "networks available" comment. --- nginx.tmpl | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 902f82d..6f84eb7 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -20,10 +20,26 @@ {{- $_ := set $globals "enable_ipv6" (parseBool (coalesce $globals.Env.ENABLE_IPV6 "false")) }} {{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }} {{- $_ := set $globals "networks" (dict) }} -# networks available to nginx-proxy: -{{- range sortObjectsByKeysAsc $globals.CurrentContainer.Networks "Name" }} - {{- $_ := set $globals.networks .Name . }} +# Networks available to the container running docker-gen (which are assumed to +# match the networks available to the container running nginx): +{{- /* + * Note: $globals.CurrentContainer may be nil in some circumstances due to + * . For more context + * see . + */}} +{{- if $globals.CurrentContainer }} + {{- range sortObjectsByKeysAsc $globals.CurrentContainer.Networks "Name" }} + {{- $_ := set $globals.networks .Name . }} # {{ .Name }} + {{- else }} +# (none) + {{- end }} +{{- else }} +# /!\ WARNING: Failed to find the Docker container running docker-gen. All +# upstream (backend) application containers will appear to be +# unreachable. Try removing the -only-exposed and -only-published +# arguments to docker-gen if you pass either of those. See +# . {{- end }} {{- /* From 49bb37dfdb3e4222f1f303d8e2eca748d0edfcbd Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 28 Mar 2022 02:56:36 -0400 Subject: [PATCH 079/105] feat: Add support for HTTP keep-alive between the proxy and upstream --- README.md | 7 +++++++ nginx.tmpl | 39 +++++++++++++++++++++++++++++++++++---- test/test_keepalive.py | 31 +++++++++++++++++++++++++++++++ test/test_keepalive.yml | 25 +++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 test/test_keepalive.py create mode 100644 test/test_keepalive.yml diff --git a/README.md b/README.md index 1728467..1ff960b 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,13 @@ docker run -d -p 80:80 -p 443:443 \ You'll need apache2-utils on the machine where you plan to create the htpasswd file. Follow these [instructions](http://httpd.apache.org/docs/2.2/programs/htpasswd.html) +### Upstream (Backend) Server HTTP Keep-Alive Support + +> **Warning** +> This feature is experimental. The behavior may change (or the feature may be removed entirely) without warning in a future release, even if the release is not a new major version. If you use this feature, or if you would like to use this feature but you require changes to it first, please [provide feedback in #2194](https://github.com/nginx-proxy/nginx-proxy/discussions/2194). Once we have collected enough feedback we will promote this feature to officially supported. + +To enable HTTP keep-alive between `nginx-proxy` and a backend server, set the `com.github.nginx-proxy.nginx-proxy.keepalive` label on the server's container to the desired maximum number of idle connections. See the [nginx keepalive documentation](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive) and the [Docker label documentation](https://docs.docker.com/config/labels-custom-metadata/) for details. + ### Headers By default, `nginx-proxy` forwards all incoming request headers from the client to the backend server unmodified, with the following exceptions: diff --git a/nginx.tmpl b/nginx.tmpl index 6f84eb7..e35258f 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -176,6 +176,7 @@ {{- if exists $override }} include {{ $override }}; {{- else }} + {{- $keepalive := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.keepalive")) }} location {{ .Path }} { {{- if eq .NetworkTag "internal" }} # Only allow traffic from internal clients @@ -189,10 +190,14 @@ root {{ trim .VhostRoot }}; include fastcgi_params; fastcgi_pass {{ trim .Upstream }}; + {{- if $keepalive }} + fastcgi_keep_conn on; + {{- end }} {{- else if eq .Proto "grpc" }} grpc_pass {{ trim .Proto }}://{{ trim .Upstream }}; {{- else }} proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }}; + set $upstream_keepalive {{ if $keepalive }}true{{ else }}false{{ end }}; {{- end }} {{- if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} @@ -232,6 +237,10 @@ upstream {{ .Upstream }} { # Fallback entry server 127.0.0.1 down; {{- end }} + {{- $keepalive := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.keepalive")) }} + {{- if $keepalive }} + keepalive {{ $keepalive }}; + {{- end }} } {{- end }} @@ -254,11 +263,33 @@ map $http_x_forwarded_port $proxy_x_forwarded_port { '' $server_port; } -# If we receive Upgrade, set Connection to "upgrade"; otherwise, preserve -# NGINX's default behavior ("Connection: close"). +# If the request from the downstream client has an "Upgrade:" header (set to any +# non-empty value), pass "Connection: upgrade" to the upstream (backend) server. +# Otherwise, the value for the "Connection" header depends on whether the user +# has enabled keepalive to the upstream server. map $http_upgrade $proxy_connection { default upgrade; - '' close; + '' $proxy_connection_noupgrade; +} +map $upstream_keepalive $proxy_connection_noupgrade { + # Preserve nginx's default behavior (send "Connection: close"). + default close; + # Use an empty string to cancel nginx's default behavior. + true ''; +} +# Abuse the map directive (see ) to ensure +# that $upstream_keepalive is always defined. This is necessary because: +# - The $proxy_connection variable is indirectly derived from +# $upstream_keepalive, so $upstream_keepalive must be defined whenever +# $proxy_connection is resolved. +# - The $proxy_connection variable is used in a proxy_set_header directive in +# the http block, so it is always fully resolved for every request -- even +# those where proxy_pass is not used (e.g., unknown virtual host). +map "" $upstream_keepalive { + # The value here should not matter because it should always be overridden in + # a location block (see the "location" template) for all requests where the + # value actually matters. + default false; } # Apply fix for very long server names @@ -514,7 +545,7 @@ server { {{- $upstream = printf "%s-%s" $upstream $sum }} {{- $dest = (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }} {{- end }} - {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }} + {{- template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag "Containers" $containers) }} {{- end }} {{- if and (not (contains $paths "/")) (ne $globals.default_root_response "none")}} location / { diff --git a/test/test_keepalive.py b/test/test_keepalive.py new file mode 100644 index 0000000..b5b8353 --- /dev/null +++ b/test/test_keepalive.py @@ -0,0 +1,31 @@ +import re + + +def test_keepalive_disabled(docker_compose, nginxproxy): + r = nginxproxy.get("http://keepalive-disabled.nginx-proxy.test/headers") + assert r.status_code == 200 + assert re.search(fr'(?m)^(?i:Connection): close$', r.text) + +def test_keepalive_disabled_other_headers_ok(docker_compose, nginxproxy): + """Make sure the other proxy_set_header headers are still set. + + According to the nginx docs [1], any proxy_set_header directive in a block + disables inheritance of proxy_set_header directives in a parent block. Make + sure that doesn't happen. + + [1] https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header + """ + r = nginxproxy.get("http://keepalive-disabled.nginx-proxy.test/headers") + assert r.status_code == 200 + assert re.search(fr'(?m)^(?i:X-Real-IP): ', r.text) + +def test_keepalive_enabled(docker_compose, nginxproxy): + r = nginxproxy.get("http://keepalive-enabled.nginx-proxy.test/headers") + assert r.status_code == 200 + assert not re.search(fr'(?m)^(?i:Connection):', r.text) + +def test_keepalive_enabled_other_headers_ok(docker_compose, nginxproxy): + """See the docstring for the disabled case above.""" + r = nginxproxy.get("http://keepalive-enabled.nginx-proxy.test/headers") + assert r.status_code == 200 + assert re.search(fr'(?m)^(?i:X-Real-IP): ', r.text) diff --git a/test/test_keepalive.yml b/test/test_keepalive.yml new file mode 100644 index 0000000..541b69d --- /dev/null +++ b/test/test_keepalive.yml @@ -0,0 +1,25 @@ +keepalive-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: keepalive-disabled.nginx-proxy.test + +keepalive-enabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: 80 + VIRTUAL_HOST: keepalive-enabled.nginx-proxy.test + labels: + com.github.nginx-proxy.nginx-proxy.keepalive: "64" + + +sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + HTTPS_METHOD: nohttps From 4696944245570f5d21901885a45ed17e04f9bf51 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 21 Mar 2023 07:15:34 +0100 Subject: [PATCH 080/105] build: dockergen 0.10.1 -> 0.10.2 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 249fa6c..9775f0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.10.1 +ARG DOCKER_GEN_VERSION=0.10.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 006b045..bd5b81d 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.10.1 +ARG DOCKER_GEN_VERSION=0.10.2 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 7ca1da8358879fe54b3861f590c1c43af7abde5e Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Tue, 21 Mar 2023 07:49:27 +0100 Subject: [PATCH 081/105] feat: Add support for HTTP load balancing between the proxy and upstream server groups (#2173) Add initial tests Newlines Remove unused variable Co-authored-by: Richard Hansen Change comment value Co-authored-by: Richard Hansen add missing services line Co-authored-by: Richard Hansen Use deploy.replicas Remove details about choosing a load balancing method Feedback note Co-authored-by: Nicolas Duchon --- README.md | 36 ++++++++++++++++++++++++++++++++++++ nginx.tmpl | 5 +++++ test/test_loadbalancing.py | 16 ++++++++++++++++ test/test_loadbalancing.yml | 27 +++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 test/test_loadbalancing.py create mode 100644 test/test_loadbalancing.yml diff --git a/README.md b/README.md index 1ff960b..4e0d9d4 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,42 @@ docker run -d -p 80:80 -p 443:443 \ You'll need apache2-utils on the machine where you plan to create the htpasswd file. Follow these [instructions](http://httpd.apache.org/docs/2.2/programs/htpasswd.html) +### Upstream (Backend) Server HTTP Load Balancing Support + +> **Warning** +> This feature is experimental. The behavior may change (or the feature may be removed entirely) without warning in a future release, even if the release is not a new major version. If you use this feature, or if you would like to use this feature but you require changes to it first, please [provide feedback in #2195](https://github.com/nginx-proxy/nginx-proxy/discussions/2195). Once we have collected enough feedback we will promote this feature to officially supported. + +If you have multiple containers with the same `VIRTUAL_HOST` and `VIRTUAL_PATH` settings, nginx will spread the load across all of them. To change the load balancing algorithm from nginx's default (round-robin), set the `com.github.nginx-proxy.nginx-proxy.loadbalance` label on one or more of your application containers to the desired load balancing directive. See the [`ngx_http_upstream_module` documentation](https://nginx.org/en/docs/http/ngx_http_upstream_module.html) for available directives. + +> **Note** +> * Don't forget the terminating semicolon (`;`). +> * If you are using Docker Compose, remember to escape any dollar sign (`$`) characters (`$` becomes `$$`). + +Docker Compose example: + +```yaml +services: + nginx-proxy: + image: nginxproxy/nginx-proxy + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + HTTPS_METHOD: nohttps + myapp: + image: jwilder/whoami + expose: + - "8000" + environment: + VIRTUAL_HOST: myapp.example + VIRTUAL_PORT: "8000" + labels: + com.github.nginx-proxy.nginx-proxy.loadbalance: "hash $$remote_addr;" + deploy: + replicas: 4 +``` + ### Upstream (Backend) Server HTTP Keep-Alive Support > **Warning** diff --git a/nginx.tmpl b/nginx.tmpl index e35258f..98ab38e 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -219,6 +219,11 @@ {{- define "upstream" }} upstream {{ .Upstream }} { {{- $server_found := false }} + {{- $loadbalance := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.loadbalance")) }} + {{- if $loadbalance }} + # From the container's loadbalance label: + {{ $loadbalance }} + {{- end }} {{- range $container := .Containers }} # Container: {{ $container.Name }} {{- $args := dict "globals" $.globals "container" $container }} diff --git a/test/test_loadbalancing.py b/test/test_loadbalancing.py new file mode 100644 index 0000000..4b43aa5 --- /dev/null +++ b/test/test_loadbalancing.py @@ -0,0 +1,16 @@ +import pytest +import re + +def test_loadbalance_hash(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r1 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld") + r2 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld") + assert re.search(r"hash \$remote_addr\;", conf) + assert r1.status_code == 200 + assert r2.text == r1.text + +def test_loadbalance_roundrobin(docker_compose, nginxproxy): + r1 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld") + r2 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld") + assert r1.status_code == 200 + assert r2.text != r1.text diff --git a/test/test_loadbalancing.yml b/test/test_loadbalancing.yml new file mode 100644 index 0000000..b8f42eb --- /dev/null +++ b/test/test_loadbalancing.yml @@ -0,0 +1,27 @@ +services: + loadbalance-hash: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: loadbalance-enabled.nginx-proxy.tld + labels: + com.github.nginx-proxy.nginx-proxy.loadbalance: "hash $$remote_addr;" + deploy: + replicas: 2 + + loadbalance-roundrobin: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: loadbalance-disabled.nginx-proxy.tld + deploy: + replicas: 2 + + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro From 87ffa7a5a8842f16668085f6e4f1b98de0f33fa5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Mar 2023 04:58:26 +0000 Subject: [PATCH 082/105] build: bump nginx from 1.23.3-alpine to 1.23.4-alpine Bumps nginx from 1.23.3-alpine to 1.23.4-alpine. --- updated-dependencies: - dependency-name: nginx 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 9775f0a..e067408 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ && rm -rf /go/forego # Build the final image -FROM nginx:1.23.3 +FROM nginx:1.23.4 ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable diff --git a/Dockerfile.alpine b/Dockerfile.alpine index bd5b81d..b2da6fd 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -37,7 +37,7 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ && rm -rf /go/forego # Build the final image -FROM nginx:1.23.3-alpine +FROM nginx:1.23.4-alpine ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable From 2056dc4429b936c22c2d05a2d2bbbfbb6b36329d Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 30 Mar 2023 01:10:05 +0200 Subject: [PATCH 083/105] build: dockergen 0.10.2 -> 0.10.3 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9775f0a..db02a1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.10.2 +ARG DOCKER_GEN_VERSION=0.10.3 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 bd5b81d..85100ee 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.10.2 +ARG DOCKER_GEN_VERSION=0.10.3 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 130fd908fbd0a72ffbef862ee7da576fc2a8ec8c Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Thu, 30 Mar 2023 20:58:56 +0200 Subject: [PATCH 084/105] docs: update nginx version badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2757c33..106a2df 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.23.3](https://img.shields.io/badge/nginx-1.23.3-brightgreen.svg) +![nginx 1.23.4](https://img.shields.io/badge/nginx-1.23.4-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 a3d3baf2599f26b905d999b55c11e402b686d92a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 04:58:03 +0000 Subject: [PATCH 085/105] build: bump golang from 1.20.2-alpine to 1.20.3-alpine Bumps golang from 1.20.2-alpine to 1.20.3-alpine. --- 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 2da6832..ccf62fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.10.3 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.20.2 as gobuilder +FROM golang:1.20.3 as gobuilder # Build docker-gen from scratch FROM gobuilder as dockergen diff --git a/Dockerfile.alpine b/Dockerfile.alpine index cc31704..68de02a 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -3,7 +3,7 @@ ARG DOCKER_GEN_VERSION=0.10.3 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries -FROM golang:1.20.2-alpine as gobuilder +FROM golang:1.20.3-alpine as gobuilder RUN apk add --no-cache git musl-dev # Build docker-gen from scratch From 695ad54dcf637e47458c1328b13071daceeeac61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 05:00:55 +0000 Subject: [PATCH 086/105] ci: bump pytest from 7.2.2 to 7.3.0 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.2.2 to 7.3.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.2.2...7.3.0) --- 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 422f2b1..0357d65 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.0.1 -pytest==7.2.2 +pytest==7.3.0 requests==2.28.2 From 035bd2b5ac9f0f7ee3ce7c5e2690a2b71b6b78ff Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 7 Apr 2023 18:05:48 -0400 Subject: [PATCH 087/105] fix: Remove `default_server` listen option from fallback server This fixes a bug introduced in commit 9b4bb07b348dc5a428b94416517291adb30794c3. --- nginx.tmpl | 17 +++++++++++++---- test/test_fallback.data/custom-fallback.conf | 5 +++++ test/test_fallback.data/custom-fallback.yml | 14 ++++++++++++++ test/test_fallback.py | 5 +++++ 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 test/test_fallback.data/custom-fallback.conf create mode 100644 test/test_fallback.data/custom-fallback.yml diff --git a/nginx.tmpl b/nginx.tmpl index 5733351..2556acf 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -372,6 +372,15 @@ proxy_set_header Proxy ""; {{- /* * If needed, create a catch-all fallback server to send an error code to * clients that request something from an unknown vhost. + * + * This server must appear first in the generated config because nginx uses + * the first `server` directive to handle requests that don't match any of + * the other `server` directives. An alternative approach would be to add + * the `default_server` option to the `listen` directives inside this + * `server`, but some users inject a custom `server` directive that uses + * `default_server`. Using `default_server` here would cause nginx to fail + * to start for those users. See + * . */}} {{- block "fallback_server" $globals }} {{- $globals := . }} @@ -403,15 +412,15 @@ server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; {{- if $fallback_http }} - listen {{ $globals.external_http_port }} default_server; + listen {{ $globals.external_http_port }}; {{- /* Do not add `default_server` (see comment above). */}} {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_http_port }} default_server; + listen [::]:{{ $globals.external_http_port }}; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} {{- end }} {{- if $fallback_https }} - listen {{ $globals.external_https_port }} ssl http2 default_server; + listen {{ $globals.external_https_port }} ssl http2; {{- /* Do not add `default_server` (see comment above). */}} {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} ssl http2 default_server; + listen [::]:{{ $globals.external_https_port }} ssl http2; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} {{- end }} {{ $globals.access_log }} diff --git a/test/test_fallback.data/custom-fallback.conf b/test/test_fallback.data/custom-fallback.conf new file mode 100644 index 0000000..ebe8814 --- /dev/null +++ b/test/test_fallback.data/custom-fallback.conf @@ -0,0 +1,5 @@ +server { + server_name __; + listen 80 default_server; + return 418; +} diff --git a/test/test_fallback.data/custom-fallback.yml b/test/test_fallback.data/custom-fallback.yml new file mode 100644 index 0000000..bc44b11 --- /dev/null +++ b/test/test_fallback.data/custom-fallback.yml @@ -0,0 +1,14 @@ +services: + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro + http-only: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: http-only.nginx-proxy.test + HTTPS_METHOD: nohttps diff --git a/test/test_fallback.py b/test/test_fallback.py index ce3d68f..1ee923a 100644 --- a/test/test_fallback.py +++ b/test/test_fallback.py @@ -89,6 +89,11 @@ CONNECTION_REFUSED_RE = re.compile("Connection refused") ("nohttps-on-app.yml", "https://http-only.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), ("nohttps-on-app.yml", "http://unknown.nginx-proxy.test/", 503, None), ("nohttps-on-app.yml", "https://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + # Custom nginx config that has a `server` directive that uses `default_server` and simply + # returns 418. Nginx should successfully start (in particular, the `default_server` in the + # custom config should not conflict with the fallback server generated by nginx-proxy) and nginx + # should prefer that server for handling requests for unknown vhosts. + ("custom-fallback.yml", "http://unknown.nginx-proxy.test/", 418, None), ]) def test_fallback(get, url, want_code, want_err_re): if want_err_re is None: From b34c9179775dc7bfb5f5f1a8aa97f3bcc9e8a0cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 05:01:01 +0000 Subject: [PATCH 088/105] ci: bump pytest from 7.3.0 to 7.3.1 in /test/requirements Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.0 to 7.3.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.3.0...7.3.1) --- 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 0357d65..95e3e03 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.0.1 -pytest==7.3.0 +pytest==7.3.1 requests==2.28.2 From 9f735aab821ebc1063f616e9677698716d446e5c Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 18 Apr 2023 07:58:38 +0200 Subject: [PATCH 089/105] build: dockergen 0.10.3 -> 0.10.4 --- Dockerfile | 2 +- Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ccf62fc..85c7296 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.10.3 +ARG DOCKER_GEN_VERSION=0.10.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 68de02a..a51a893 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.10.3 +ARG DOCKER_GEN_VERSION=0.10.4 ARG FOREGO_VERSION=v0.17.0 # Use a specific version of golang to build both binaries From 00a1e5ef5cc20d88e19176eccb093aba101483c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 05:00:28 +0000 Subject: [PATCH 090/105] ci: bump requests from 2.28.2 to 2.29.0 in /test/requirements Bumps [requests](https://github.com/psf/requests) from 2.28.2 to 2.29.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.28.2...v2.29.0) --- updated-dependencies: - dependency-name: requests 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 95e3e03..0344210 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -2,4 +2,4 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.0.1 pytest==7.3.1 -requests==2.28.2 +requests==2.29.0 From 21321a4495c31d8b29c04ac62e300c36c6ecc2ea Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 30 Apr 2023 15:02:09 +0200 Subject: [PATCH 091/105] ci: add GitHub Actions to Dependabot config --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eb6a749..fb8c8fc 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,3 +20,13 @@ updates: prefix: "ci" labels: - "type/ci" + + # Maintain GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" + labels: + - "type/ci" From 43eed7d0dfbf48412cf5834550f49f8281d778fa Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 30 Apr 2023 15:02:44 +0200 Subject: [PATCH 092/105] ci: check test suite dependencies weekly --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fb8c8fc..a21c2b1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,7 +15,7 @@ updates: - package-ecosystem: "pip" directory: "/test/requirements" schedule: - interval: "daily" + interval: "weekly" commit-message: prefix: "ci" labels: From edb6c5dfd88e40455b08b15f5ca964be8e045759 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 Apr 2023 13:17:20 +0000 Subject: [PATCH 093/105] ci: bump docker/build-push-action from 3 to 4 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v3...v4) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dockerhub.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 8b33fb2..1226e8b 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -67,7 +67,7 @@ jobs: - name: Build and push the Debian based image id: docker_build_debian - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: Dockerfile @@ -129,7 +129,7 @@ jobs: - name: Build and push the Alpine based image id: docker_build_alpine - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . file: Dockerfile.alpine From 4655ba9f51f2e235854e1fb38424371ce0840def Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 30 Apr 2023 15:30:29 +0200 Subject: [PATCH 094/105] ci: only trigger test workflow on push for main branch --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3dd2674..e58e632 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,6 +3,8 @@ name: Tests on: workflow_dispatch: push: + branches: + - main paths-ignore: - "LICENSE" - "**.md" From 70c9ea6ccc311ac0651c34e5c6c17bde787e07fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 Apr 2023 13:17:16 +0000 Subject: [PATCH 095/105] ci: bump actions/setup-python from 2 to 4 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e58e632..c549094 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 From eabb808b85e4e08324e5660577dfde5801d6df3f Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 30 Apr 2023 15:51:46 +0200 Subject: [PATCH 096/105] ci: rename build / publish workflow --- .github/workflows/{dockerhub.yml => build-publish.yml} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename .github/workflows/{dockerhub.yml => build-publish.yml} (97%) diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/build-publish.yml similarity index 97% rename from .github/workflows/dockerhub.yml rename to .github/workflows/build-publish.yml index 1226e8b..5eac674 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/build-publish.yml @@ -1,4 +1,4 @@ -name: DockerHub +name: Build and publish Docker images on: workflow_dispatch: @@ -20,6 +20,7 @@ on: jobs: multiarch-build-debian: + name: Build and publish Debian image runs-on: ubuntu-latest steps: - name: Checkout @@ -81,6 +82,7 @@ jobs: run: echo ${{ steps.docker_build_debian.outputs.digest }} multiarch-build-alpine: + name: Build and publish Alpine image runs-on: ubuntu-latest steps: - name: Checkout From 0501c540029918d617007fa44f7087be64ed1d81 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Tue, 2 May 2023 05:21:41 +0200 Subject: [PATCH 097/105] build: get docker-gen from pre-built image (#2230) * build: get docker-gen from pre-built image * build: requested changes --- .github/workflows/build-publish.yml | 26 +++++++++++++++++------ Dockerfile | 29 ++++++------------------- Dockerfile.alpine | 33 +++++++---------------------- 3 files changed, 34 insertions(+), 54 deletions(-) diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 5eac674..2b4eb3e 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -28,8 +28,9 @@ jobs: with: fetch-depth: 0 - - name: Retrieve version - run: echo "GIT_DESCRIBE=$(git describe --tags)" >> $GITHUB_ENV + - name: Retrieve nginx-proxy version + id: nginx-proxy_version + run: echo "VERSION=$(git describe --tags)" >> "$GITHUB_OUTPUT" - name: Get Docker tags for Debian based image id: docker_meta_debian @@ -66,13 +67,19 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Retrieve docker-gen version + id: docker-gen_version + run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile >> "$GITHUB_OUTPUT" + - name: Build and push the Debian based image id: docker_build_debian uses: docker/build-push-action@v4 with: context: . file: Dockerfile - build-args: NGINX_PROXY_VERSION=${{ env.GIT_DESCRIBE }} + build-args: | + NGINX_PROXY_VERSION=${{ steps.nginx-proxy_version.outputs.VERSION }} + DOCKER_GEN_VERSION=${{ steps.docker-gen_version.outputs.VERSION }} platforms: linux/amd64,linux/arm64,linux/arm/v7 push: true tags: ${{ steps.docker_meta_debian.outputs.tags }} @@ -90,8 +97,9 @@ jobs: with: fetch-depth: 0 - - name: Retrieve version - run: echo "GIT_DESCRIBE=$(git describe --tags)" >> $GITHUB_ENV + - name: Retrieve nginx-proxy version + id: nginx-proxy_version + run: echo "VERSION=$(git describe --tags)" >> "$GITHUB_OUTPUT" - name: Get Docker tags for Alpine based image id: docker_meta_alpine @@ -129,13 +137,19 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Retrieve docker-gen version + id: docker-gen_version + run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile >> "$GITHUB_OUTPUT" + - name: Build and push the Alpine based image id: docker_build_alpine uses: docker/build-push-action@v4 with: context: . file: Dockerfile.alpine - build-args: NGINX_PROXY_VERSION=${{ env.GIT_DESCRIBE }} + build-args: | + NGINX_PROXY_VERSION=${{ steps.nginx-proxy_version.outputs.VERSION }} + DOCKER_GEN_VERSION=${{ steps.docker-gen_version.outputs.VERSION }} platforms: linux/amd64,linux/arm64,linux/arm/v7 push: true tags: ${{ steps.docker_meta_alpine.outputs.tags }} diff --git a/Dockerfile b/Dockerfile index 85c7296..7617c83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,10 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.10.4 ARG FOREGO_VERSION=v0.17.0 -# Use a specific version of golang to build both binaries -FROM golang:1.20.3 as gobuilder - -# Build docker-gen from scratch -FROM gobuilder as dockergen - -ARG DOCKER_GEN_VERSION - -RUN git clone https://github.com/nginx-proxy/docker-gen \ - && cd /go/docker-gen \ - && git -c advice.detachedHead=false checkout $DOCKER_GEN_VERSION \ - && go mod download \ - && CGO_ENABLED=0 GOOS=linux go build -ldflags "-X main.buildVersion=${DOCKER_GEN_VERSION}" ./cmd/docker-gen \ - && go clean -cache \ - && mv docker-gen /usr/local/bin/ \ - && cd - \ - && rm -rf /go/docker-gen +FROM nginxproxy/docker-gen:0.10.4-debian AS docker-gen # Build forego from scratch -FROM gobuilder as forego +FROM golang:1.20.3 as forego ARG FOREGO_VERSION @@ -39,9 +22,9 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ FROM nginx:1.23.4 ARG NGINX_PROXY_VERSION -# Add DOCKER_GEN_VERSION environment variable -# Because some external projects rely on it -ARG DOCKER_GEN_VERSION +# Add DOCKER_GEN_VERSION environment variable because +# acme-companion rely on it (but the actual value is not important) +ARG DOCKER_GEN_VERSION="unknown" ENV NGINX_PROXY_VERSION=${NGINX_PROXY_VERSION} \ DOCKER_GEN_VERSION=${DOCKER_GEN_VERSION} \ DOCKER_HOST=unix:///tmp/docker.sock @@ -63,7 +46,7 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ # Install Forego + docker-gen COPY --from=forego /usr/local/bin/forego /usr/local/bin/forego -COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen +COPY --from=docker-gen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index a51a893..c3ab0f7 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,32 +1,15 @@ # setup build arguments for version of dependencies to use -ARG DOCKER_GEN_VERSION=0.10.4 ARG FOREGO_VERSION=v0.17.0 -# Use a specific version of golang to build both binaries -FROM golang:1.20.3-alpine as gobuilder -RUN apk add --no-cache git musl-dev - -# Build docker-gen from scratch -FROM gobuilder as dockergen - -ARG DOCKER_GEN_VERSION - -RUN git clone https://github.com/nginx-proxy/docker-gen \ - && cd /go/docker-gen \ - && git -c advice.detachedHead=false checkout $DOCKER_GEN_VERSION \ - && go mod download \ - && CGO_ENABLED=0 go build -ldflags "-X main.buildVersion=${DOCKER_GEN_VERSION}" ./cmd/docker-gen \ - && go clean -cache \ - && mv docker-gen /usr/local/bin/ \ - && cd - \ - && rm -rf /go/docker-gen +FROM nginxproxy/docker-gen:0.10.4 AS docker-gen # Build forego from scratch -FROM gobuilder as forego +FROM golang:1.20.3-alpine as forego ARG FOREGO_VERSION -RUN git clone https://github.com/nginx-proxy/forego/ \ +RUN apk add --no-cache git musl-dev \ + && git clone https://github.com/nginx-proxy/forego/ \ && cd /go/forego \ && git -c advice.detachedHead=false checkout $FOREGO_VERSION \ && go mod download \ @@ -40,9 +23,9 @@ RUN git clone https://github.com/nginx-proxy/forego/ \ FROM nginx:1.23.4-alpine ARG NGINX_PROXY_VERSION -# Add DOCKER_GEN_VERSION environment variable -# Because some external projects rely on it -ARG DOCKER_GEN_VERSION +# Add DOCKER_GEN_VERSION environment variable because +# acme-companion rely on it (but the actual value is not important) +ARG DOCKER_GEN_VERSION="unknown" ENV NGINX_PROXY_VERSION=${NGINX_PROXY_VERSION} \ DOCKER_GEN_VERSION=${DOCKER_GEN_VERSION} \ DOCKER_HOST=unix:///tmp/docker.sock @@ -60,7 +43,7 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ # Install Forego + docker-gen COPY --from=forego /usr/local/bin/forego /usr/local/bin/forego -COPY --from=dockergen /usr/local/bin/docker-gen /usr/local/bin/docker-gen +COPY --from=docker-gen /usr/local/bin/docker-gen /usr/local/bin/docker-gen COPY network_internal.conf /etc/nginx/ From 64e21100d3d8b16faad3322930cd54bd3a694b82 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Sun, 30 Apr 2023 17:31:56 +0200 Subject: [PATCH 098/105] build: don't install wget --- Dockerfile | 6 ++---- Dockerfile.alpine | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7617c83..6a70317 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,11 +29,9 @@ ENV NGINX_PROXY_VERSION=${NGINX_PROXY_VERSION} \ DOCKER_GEN_VERSION=${DOCKER_GEN_VERSION} \ DOCKER_HOST=unix:///tmp/docker.sock -# Install wget and install/updates certificates +# Install/update certificates RUN apt-get update \ - && apt-get install -y -q --no-install-recommends \ - ca-certificates \ - wget \ + && apt-get install -y -q --no-install-recommends ca-certificates \ && apt-get clean \ && rm -r /var/lib/apt/lists/* diff --git a/Dockerfile.alpine b/Dockerfile.alpine index c3ab0f7..233d9f1 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -30,9 +30,11 @@ ENV NGINX_PROXY_VERSION=${NGINX_PROXY_VERSION} \ DOCKER_GEN_VERSION=${DOCKER_GEN_VERSION} \ DOCKER_HOST=unix:///tmp/docker.sock -# Install wget and install/updates certificates +# Install dependencies RUN apk add --no-cache --virtual .run-deps \ - ca-certificates bash wget openssl \ + bash \ + ca-certificates \ + openssl \ && update-ca-certificates # Configure Nginx From 442e577c0ef2dda4aac9650f1064a858b88d722b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 May 2023 04:57:51 +0000 Subject: [PATCH 099/105] build: bump golang from 1.20.3-alpine to 1.20.4-alpine Bumps golang from 1.20.3-alpine to 1.20.4-alpine. --- 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 7617c83..a4cfcb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG FOREGO_VERSION=v0.17.0 FROM nginxproxy/docker-gen:0.10.4-debian AS docker-gen # Build forego from scratch -FROM golang:1.20.3 as forego +FROM golang:1.20.4 as forego ARG FOREGO_VERSION diff --git a/Dockerfile.alpine b/Dockerfile.alpine index c3ab0f7..c23e60f 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -4,7 +4,7 @@ ARG FOREGO_VERSION=v0.17.0 FROM nginxproxy/docker-gen:0.10.4 AS docker-gen # Build forego from scratch -FROM golang:1.20.3-alpine as forego +FROM golang:1.20.4-alpine as forego ARG FOREGO_VERSION From 28fef687adcafcbf78e2f987fb26c2bb3a84e61d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 04:58:23 +0000 Subject: [PATCH 100/105] ci: bump docker from 6.0.1 to 6.1.1 in /test/requirements Bumps [docker](https://github.com/docker/docker-py) from 6.0.1 to 6.1.1. - [Release notes](https://github.com/docker/docker-py/releases) - [Commits](https://github.com/docker/docker-py/compare/6.0.1...6.1.1) --- updated-dependencies: - dependency-name: docker 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 0344210..25f439a 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,5 +1,5 @@ backoff==2.2.1 docker-compose==1.29.2 -docker==6.0.1 +docker==6.1.1 pytest==7.3.1 requests==2.29.0 From faad1cc29e111d897e5a78f6c618e6bc8d1f2cd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 10:48:18 +0000 Subject: [PATCH 101/105] ci: bump requests from 2.29.0 to 2.30.0 in /test/requirements Bumps [requests](https://github.com/psf/requests) from 2.29.0 to 2.30.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.29.0...v2.30.0) --- updated-dependencies: - dependency-name: requests 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 25f439a..f10abfa 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -2,4 +2,4 @@ backoff==2.2.1 docker-compose==1.29.2 docker==6.1.1 pytest==7.3.1 -requests==2.29.0 +requests==2.30.0 From 6e5e8f4c9d360c0b13f37053d18b55d4eb3ba35c Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 8 May 2023 15:36:04 +0200 Subject: [PATCH 102/105] ci: fix image version labelling --- .github/workflows/build-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 2b4eb3e..7d9143b 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -46,7 +46,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder - org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} + org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }} - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -115,7 +115,7 @@ jobs: type=raw,value=alpine,enable={{is_default_branch}} labels: | org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder - org.opencontainers.image.version=${{ env.GIT_DESCRIBE }} + org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }} flavor: latest=false - name: Set up QEMU From 2b621599ffbdd3f7849f7819985b3fbc0b96ce83 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 8 May 2023 21:20:47 +0200 Subject: [PATCH 103/105] test: fix wildcard_certs_and_nohttps test --- .../wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py index 603d281..590eafc 100644 --- a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py +++ b/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py @@ -27,7 +27,7 @@ def test_https_get_served(docker_compose, nginxproxy, subdomain): def test_https_request_to_nohttps_vhost_goes_to_fallback_server(docker_compose, nginxproxy): with pytest.raises( (CertificateError, SSLError) ) as excinfo: nginxproxy.get("https://3.web.nginx-proxy.tld/port") - assert """hostname '3.web.nginx-proxy.tld' doesn't match 'nginx-proxy.tld'""" in str(excinfo.value) + assert """certificate is not valid for '3.web.nginx-proxy.tld'""" in str(excinfo.value) r = nginxproxy.get("https://3.web.nginx-proxy.tld/port", verify=False) assert r.status_code == 503 From e2539b04f58a640444e61813531cb4eac0cec7e4 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 8 May 2023 23:14:09 +0200 Subject: [PATCH 104/105] fix: always on session cache on HTTPS fallback listener --- nginx.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 2556acf..f9d9a03 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -422,11 +422,11 @@ server { {{- if $globals.enable_ipv6 }} listen [::]:{{ $globals.external_https_port }} ssl http2; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; {{- end }} {{ $globals.access_log }} {{- if $globals.default_cert_ok }} - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; {{- else }} From 5d237b7a0aa0e0497afc75defaea39751640760c Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 8 May 2023 20:00:34 +0200 Subject: [PATCH 105/105] build: get Forego from pre-built image --- Dockerfile | 19 +------------------ Dockerfile.alpine | 19 +------------------ 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index 30389b3..55a8a37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,6 @@ -# setup build arguments for version of dependencies to use -ARG FOREGO_VERSION=v0.17.0 - FROM nginxproxy/docker-gen:0.10.4-debian AS docker-gen -# Build forego from scratch -FROM golang:1.20.4 as forego - -ARG FOREGO_VERSION - -RUN git clone https://github.com/nginx-proxy/forego/ \ - && cd /go/forego \ - && git -c advice.detachedHead=false checkout $FOREGO_VERSION \ - && go mod download \ - && CGO_ENABLED=0 GOOS=linux go build -o forego . \ - && go clean -cache \ - && mv forego /usr/local/bin/ \ - && cd - \ - && rm -rf /go/forego +FROM nginxproxy/forego:0.17.1-debian AS forego # Build the final image FROM nginx:1.23.4 @@ -35,7 +19,6 @@ RUN apt-get update \ && apt-get clean \ && rm -r /var/lib/apt/lists/* - # Configure Nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ && sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf \ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 04343e1..b3d3304 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,23 +1,6 @@ -# setup build arguments for version of dependencies to use -ARG FOREGO_VERSION=v0.17.0 - FROM nginxproxy/docker-gen:0.10.4 AS docker-gen -# Build forego from scratch -FROM golang:1.20.4-alpine as forego - -ARG FOREGO_VERSION - -RUN apk add --no-cache git musl-dev \ - && git clone https://github.com/nginx-proxy/forego/ \ - && cd /go/forego \ - && git -c advice.detachedHead=false checkout $FOREGO_VERSION \ - && go mod download \ - && CGO_ENABLED=0 go build -o forego . \ - && go clean -cache \ - && mv forego /usr/local/bin/ \ - && cd - \ - && rm -rf /go/forego +FROM nginxproxy/forego:0.17.1 AS forego # Build the final image FROM nginx:1.23.4-alpine