diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9824d632a..65b80f03b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -8,28 +8,5 @@ Instead please use the relevant Discussions section's category: - 🙏 [Ask a question](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/q-a) - 💡 [Request a feature](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/ideas) -## Bugs - -If you are logging a bug, please search the current open issues first to see if there is already a bug opened. - -For bugs, the easier you make it to reproduce the issue you see and the more initial information you provide, the easier and faster the bug can be identified and can get fixed. - -Please at least provide: -- the exact nginx-proxy version you're using (if using `latest` please make sure it is up to date and provide the version number printed at container startup). -- complete configuration (compose file, command line, etc) of both your nginx-proxy container(s) and proxied containers. You should redact sensitive info if needed but please provide **full** configurations. -- generated nginx configuration obtained with `docker exec nameofyournginxproxycontainer nginx -T` - -If you can provide a script or docker-compose file that reproduces the problems, that is very helpful. - -## General advice about `latest` - -Do not use the `latest` tag for production setups. - -`latest` is nothing more than a convenient default used by Docker if no specific tag is provided, there isn't any strict convention on what goes into this tag over different projects, and it does not carry any promise of stability. - -Using `latest` will most certainly put you at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes) and makes it harder for maintainers to track which exact version of the container you are experiencing an issue with. - -This recommendation stands for pretty much every Docker image in existence, not just nginx-proxy's ones. - Thanks, Nicolas diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..66d881f3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,44 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +# ⚠️ PLEASE READ ⚠️ + +## Questions or Features + +If you have a question or want to request a feature, please **DO NOT SUBMIT** a new issue. + +Instead please use the relevant Discussions section's category: +- 🙏 [Ask a question](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/q-a) +- 💡 [Request a feature](https://github.com/nginx-proxy/nginx-proxy/discussions/categories/ideas) + +## Bugs + +If you are logging a bug, please search the current open issues first to see if there is already a bug opened. + +For bugs, the easier you make it to reproduce the issue you see and the more initial information you provide, the easier and faster the bug can be identified and can get fixed. + +Please at least provide: +- the exact nginx-proxy version you're using (if using `latest` please make sure it is up to date and provide the version number printed at container startup). +- complete configuration (compose file, command line, etc) of both your nginx-proxy container(s) and proxied containers. You should redact sensitive info if needed but please provide **full** configurations. +- generated nginx configuration obtained with `docker exec nameofyournginxproxycontainer nginx -T` + +If you can provide a script or docker-compose file that reproduces the problems, that is very helpful. + +## General advice about `latest` + +Do not use the `latest` tag for production setups. + +`latest` is nothing more than a convenient default used by Docker if no specific tag is provided, there isn't any strict convention on what goes into this tag over different projects, and it does not carry any promise of stability. + +Using `latest` will most certainly put you at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes) and makes it harder for maintainers to track which exact version of the container you are experiencing an issue with. + +This recommendation stands for pretty much every Docker image in existence, not just nginx-proxy's ones. + +Thanks, +Nicolas diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..7c19e5e3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: GitHub Discussions + url: https://github.com/orgs/community/discussions + about: Please ask and answer questions here. \ No newline at end of file diff --git a/.github/workflows/build-publish-dispatch.yml b/.github/workflows/build-publish-dispatch.yml new file mode 100644 index 000000000..3dc1d3d95 --- /dev/null +++ b/.github/workflows/build-publish-dispatch.yml @@ -0,0 +1,89 @@ +name: Build and publish Docker images on demand + +permissions: + contents: read + packages: write + +on: + workflow_dispatch: + inputs: + image_tag: + description: "Image tag" + type: string + required: true + +jobs: + multiarch-build: + name: Build and publish ${{ matrix.base }} image with tag ${{ inputs.image_tag }} + strategy: + matrix: + base: [alpine, debian] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Retrieve nginx-proxy version + id: nginx-proxy_version + run: echo "VERSION=$(git describe --tags)" >> "$GITHUB_OUTPUT" + + - name: Retrieve docker-gen version + id: docker-gen_version + run: sed -n -e 's;^FROM docker.io/nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" + + - name: Get Docker tags + id: docker_meta + uses: docker/metadata-action@v5 + with: + images: | + nginxproxy/nginx-proxy + tags: | + type=raw,value=${{ inputs.image_tag }},enable=${{ matrix.base == 'debian' }} + type=raw,value=${{ inputs.image_tag }},suffix=-alpine,enable=${{ matrix.base == 'alpine' }} + labels: | + org.opencontainers.image.authors=Nicolas Duchon (@buchdag), Jason Wilder + org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }} + flavor: | + latest=false + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push the image + id: docker_build + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.${{ matrix.base }} + 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/s390x,linux/arm/v7 + sbom: true + push: true + provenance: mode=max + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Images digests + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 6aeb9f540..a86ab0d9d 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -1,5 +1,9 @@ name: Build and publish Docker images +permissions: + contents: read + packages: write + on: workflow_dispatch: schedule: @@ -25,9 +29,10 @@ jobs: matrix: base: [alpine, debian] runs-on: ubuntu-latest + if: (github.event_name == 'schedule' && github.repository == 'nginx-proxy/nginx-proxy') || (github.event_name != 'schedule') steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 @@ -37,7 +42,7 @@ jobs: - name: Retrieve docker-gen version id: docker-gen_version - run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" + run: sed -n -e 's;^FROM docker.io/nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT" - name: Get Docker tags id: docker_meta @@ -81,7 +86,7 @@ jobs: - name: Build and push the image id: docker_build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: Dockerfile.${{ matrix.base }} diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml index 4be8cc29f..dfcab9342 100644 --- a/.github/workflows/dockerhub-description.yml +++ b/.github/workflows/dockerhub-description.yml @@ -1,5 +1,8 @@ name: Update Docker Hub Description +permissions: + contents: read + on: push: branches: @@ -15,7 +18,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Docker Hub Description uses: peter-evans/dockerhub-description@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36e6273e6..50d998b1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Tests +permissions: + contents: read + on: workflow_dispatch: push: @@ -21,14 +24,15 @@ jobs: strategy: matrix: base_docker_image: [alpine, debian] + fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: Install dependencies run: | @@ -36,6 +40,9 @@ jobs: pip install -r python-requirements.txt working-directory: test/requirements + - name: Pull nginx:alpine image + run: docker pull nginx:alpine + - name: Build Docker web server image run: make build-webserver @@ -43,5 +50,5 @@ jobs: run: make build-nginx-proxy-test-${{ matrix.base_docker_image }} - name: Run tests - run: pytest + run: pytest --ignore-flaky working-directory: test diff --git a/.gitignore b/.gitignore index 5daab4f78..7a6f79faa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/__pycache__/ **/.cache/ .idea/ +wip diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 244ecb76c..a1e73e5d2 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,9 +1,10 @@ -FROM docker.io/nginxproxy/docker-gen:0.14.0 AS docker-gen +# syntax=docker/dockerfile:1 +FROM docker.io/nginxproxy/docker-gen:0.15.0 AS docker-gen -FROM docker.io/nginxproxy/forego:0.18.1 AS forego +FROM docker.io/nginxproxy/forego:0.18.3 AS forego # Build the final image -FROM docker.io/library/nginx:1.27.0-alpine +FROM docker.io/library/nginx:1.29.1-alpine ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable because diff --git a/Dockerfile.debian b/Dockerfile.debian index d3f5945ce..52f712c97 100644 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -1,9 +1,10 @@ -FROM docker.io/nginxproxy/docker-gen:0.14.0-debian AS docker-gen +# syntax=docker/dockerfile:1 +FROM docker.io/nginxproxy/docker-gen:0.15.0-debian AS docker-gen -FROM docker.io/nginxproxy/forego:0.18.1-debian AS forego +FROM docker.io/nginxproxy/forego:0.18.3-debian AS forego # Build the final image -FROM docker.io/library/nginx:1.27.0 +FROM docker.io/library/nginx:1.29.1 ARG NGINX_PROXY_VERSION # Add DOCKER_GEN_VERSION environment variable because diff --git a/README.md b/README.md index d29c15d38..5349ddd21 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Test](https://github.com/nginx-proxy/nginx-proxy/actions/workflows/test.yml/badge.svg)](https://github.com/nginx-proxy/nginx-proxy/actions/workflows/test.yml) [![GitHub release](https://img.shields.io/github/v/release/nginx-proxy/nginx-proxy)](https://github.com/nginx-proxy/nginx-proxy/releases) -![nginx 1.27.0](https://img.shields.io/badge/nginx-1.27.0-brightgreen.svg) +[![nginx 1.29.1](https://img.shields.io/badge/nginx-1.29.1-brightgreen.svg?logo=nginx)](https://nginx.org/en/CHANGES) [![Docker Image Size](https://img.shields.io/docker/image-size/nginxproxy/nginx-proxy?sort=semver)](https://hub.docker.com/r/nginxproxy/nginx-proxy "Click to view the image on Docker Hub") [![Docker stars](https://img.shields.io/docker/stars/nginxproxy/nginx-proxy.svg)](https://hub.docker.com/r/nginxproxy/nginx-proxy "DockerHub") [![Docker pulls](https://img.shields.io/docker/pulls/nginxproxy/nginx-proxy.svg)](https://hub.docker.com/r/nginxproxy/nginx-proxy "DockerHub") @@ -18,7 +18,7 @@ docker run --detach \ --name nginx-proxy \ --publish 80:80 \ --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - nginxproxy/nginx-proxy:1.5 + nginxproxy/nginx-proxy:1.8 ``` Then start any containers (here an nginx container) you want proxied with an env var `VIRTUAL_HOST=subdomain.yourdomain.com` @@ -48,7 +48,7 @@ The nginx-proxy images are available in two flavors. This image is based on the nginx:mainline image, itself based on the debian slim image. ```console -docker pull nginxproxy/nginx-proxy:1.5 +docker pull nginxproxy/nginx-proxy:1.8 ``` #### Alpine based version (`-alpine` suffix) @@ -56,15 +56,22 @@ docker pull nginxproxy/nginx-proxy:1.5 This image is based on the nginx:alpine image. ```console -docker pull nginxproxy/nginx-proxy:1.5-alpine +docker pull nginxproxy/nginx-proxy:1.8-alpine ``` -#### :warning: a note on `latest` and `alpine`: - -It is not recommended to use the `latest` (`nginxproxy/nginx-proxy`, `nginxproxy/nginx-proxy:latest`) or `alpine` (`nginxproxy/nginx-proxy:alpine`) tag for production setups. - -[Those tags point](https://hub.docker.com/r/nginxproxy/nginx-proxy/tags) to the latest commit in the `main` branch. They do not carry any promise of stability, and using them will probably put your nginx-proxy setup at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes). You should always specify the version you want to use explicitly to ensure your setup doesn't break when the image is updated. +> [!IMPORTANT] +> +> #### A note on `latest` and `alpine`: +> +> It is not recommended to use the `latest` (`nginxproxy/nginx-proxy`, `nginxproxy/nginx-proxy:latest`) or `alpine` (`nginxproxy/nginx-proxy:alpine`) tag for production setups. +> +> [Those tags point](https://hub.docker.com/r/nginxproxy/nginx-proxy/tags) to the latest commit in the `main` branch. They do not carry any promise of stability, and using them will probably put your nginx-proxy setup at risk of experiencing uncontrolled updates to non backward compatible versions (or versions with breaking changes). You should always specify the version you want to use explicitly to ensure your setup doesn't break when the image is updated. ### Additional documentation Please check the [docs section](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs). + +### Powered by + +[![GoLand logo](https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand_icon.svg)](https://www.jetbrains.com/go/) +[![PyCharm logo](https://resources.jetbrains.com/storage/products/company/brand/logos/PyCharm_icon.svg)](https://www.jetbrains.com/pycharm/) diff --git a/docker-compose-separate-containers.yml b/docker-compose-separate-containers.yml index 5a6489596..c36063feb 100644 --- a/docker-compose-separate-containers.yml +++ b/docker-compose-separate-containers.yml @@ -1,4 +1,5 @@ -version: "2" +volumes: + nginx_conf: services: nginx: @@ -7,17 +8,19 @@ services: ports: - "80:80" volumes: - - /etc/nginx/conf.d + - nginx_conf:/etc/nginx/conf.d:ro + - network_internal.conf:/etc/nginx/network_internal.conf:ro dockergen: image: nginxproxy/docker-gen - command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl - /etc/nginx/conf.d/default.conf - volumes_from: - - nginx + command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + # the name "nginx" passed to the "-notify-sighup" flag must exactly match the container name used for the nginx container above. + environment: + # nginx-proxy environment variable (HTTP_PORT etc.) must be set on the docker-gen container (not the nginx container) volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - nginx_conf:/etc/nginx/conf.d whoami: image: jwilder/whoami diff --git a/docker-compose.yml b/docker-compose.yml index a261ffce0..a26375500 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "2" - services: nginx-proxy: image: nginxproxy/nginx-proxy diff --git a/docs/README.md b/docs/README.md index 6dea7c8e6..565f09e46 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,6 +5,7 @@ - [Docker Networking](#docker-networking) - [Upstream (Backend) features](#upstream-backend-features) - [Basic Authentication Support](#basic-authentication-support) +- [mTLS client side certificate authentication](#mtls-client-side-certificate-authentication) - [Logging](#logging) - [SSL Support](#ssl-support) - [IPv6 Support](#ipv6-nat) @@ -15,6 +16,7 @@ - [Unhashed vs SHA1 upstream names](#unhashed-vs-sha1-upstream-names) - [Separate Containers](#separate-containers) - [Docker Compose](#docker-compose) +- [Configuration Summary](#configuration-summary) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) @@ -33,13 +35,19 @@ You can also use wildcards at the beginning and the end of host name, like `*.ba To set the default host for nginx use the env var `DEFAULT_HOST=foo.bar.com` for example ```console -docker run -d -p 80:80 -e DEFAULT_HOST=foo.bar.com -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 80:80 \ + --env DEFAULT_HOST=foo.bar.com \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` nginx-proxy will then redirect all requests to a container where `VIRTUAL_HOST` is set to `DEFAULT_HOST`, if they don't match any (other) `VIRTUAL_HOST`. Using the example above requests without matching `VIRTUAL_HOST` will be redirected to a plain nginx instance after running the following command: ```console -docker run -d -e VIRTUAL_HOST=foo.bar.com nginx +docker run --detach \ + --env VIRTUAL_HOST=foo.bar.com \ + nginx ``` ### Virtual Ports @@ -56,7 +64,7 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie ### Multiple ports -If your container expose more than one service on different ports and those services need to be proxied, you'll need to use the `VIRTUAL_HOST_MULTIPORTS` environment variable. This variable takes virtual host, path, port and dest definition in YAML (or JSON) form, and completely override the `VIRTUAL_HOST`, `VIRTUAL_PORT`, `VIRTUAL_PATH` and `VIRTUAL_DEST` environment variables on this container. +If your container expose more than one service on different ports and those services need to be proxied, you'll need to use the `VIRTUAL_HOST_MULTIPORTS` environment variable. This variable takes virtual host, path, port and dest definition in YAML (or JSON) form, and completely override the `VIRTUAL_HOST`, `VIRTUAL_PORT`, `VIRTUAL_PROTO`, `VIRTUAL_PATH` and `VIRTUAL_DEST` environment variables on this container. The YAML syntax should be easier to write on Docker compose files, while the JSON syntax can be used for CLI invocation. @@ -66,19 +74,21 @@ The expected format is the following: hostname: path: port: int + proto: string dest: string ``` -For each hostname entry, `path`, `port` and `dest` are optional and are assigned default values when missing: +For each hostname entry, `path`, `port`, `proto` and `dest` are optional and are assigned default values when missing: - `path` = "/" - `port` = default port +- `proto` = "http" - `dest` = "" -The following examples use an hypothetical container running services on port 80, 8000 and 9000: - #### Multiple ports routed to different hostnames +The following example use an hypothetical container running services over HTTP on port 80, 8000 and 9000: + ```yaml services: multiport-container: @@ -111,12 +121,14 @@ services: This would result in the following proxy config: -- `www.example.org` -> `multiport-container:80` -- `service1.example.org` -> `multiport-container:8000` -- `service2.example.org` -> `multiport-container:9000` +- `www.example.org` -> `multiport-container:80` over `HTTP` +- `service1.example.org` -> `multiport-container:8000` over `HTTP` +- `service2.example.org` -> `multiport-container:9000` over `HTTP` #### Multiple ports routed to same hostname and different paths +The following example use an hypothetical container running services over HTTP on port 80 and 8000 and over HTTPS on port 9443: + ```yaml services: multiport-container: @@ -130,11 +142,12 @@ services: port: 8000 dest: "/" "/service2": - port: 9000 + port: 9443 + proto: "https" dest: "/" -# port and dest are not specified on the / path, so this path is routed -# to the default port with the default dest value (empty string) +# port and dest are not specified on the / path, so this path is routed to the +# default port with the default dest value (empty string) and default proto (http) # JSON equivalent: # VIRTUAL_HOST_MULTIPORTS: |- @@ -142,16 +155,16 @@ services: # "www.example.org": { # "/": {}, # "/service1": { "port": 8000, "dest": "/" }, -# "/service2": { "port": 9000, "dest": "/" } +# "/service2": { "port": 9443, "proto": "https", "dest": "/" } # } # } ``` This would result in the following proxy config: -- `www.example.org` -> `multiport-container:80` -- `www.example.org/service1` -> `multiport-container:8000` -- `www.example.org/service2` -> `multiport-container:9000` +- `www.example.org` -> `multiport-container:80` over `HTTP` +- `www.example.org/service1` -> `multiport-container:8000` over `HTTP` +- `www.example.org/service2` -> `multiport-container:9443` over `HTTPS` ⬆️ [back to table of contents](#table-of-contents) @@ -162,7 +175,8 @@ It is also possible to specify multiple paths with regex locations like `VIRTUAL The full request URI will be forwarded to the serving container in the `X-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. +> [!NOTE] +> Your application needs to be able to generate links starting with `VIRTUAL_PATH`. This can be achieved by it being natively on this path or having an option to prepend this path. The application does not need to expect this path in the request. ### VIRTUAL_DEST @@ -173,7 +187,12 @@ If the application runs natively on this sub-path or has a setting to do so, `VI If the requests are expected to not contain a sub-path and the generated links contain the sub-path, `VIRTUAL_DEST=/` should be used. ```console -$ docker run -d -e VIRTUAL_HOST=example.tld -e VIRTUAL_PATH=/app1/ -e VIRTUAL_DEST=/ --name app1 app +docker run --detach \ + --name app1 \ + --env VIRTUAL_HOST=example.tld \ + --env VIRTUAL_PATH=/app1/ \ + --env VIRTUAL_DEST=/ \ + app ``` In this example, the incoming request `http://example.tld/app1/foo` will be proxied as `http://app1/foo` instead of `http://app1/app1/foo`. @@ -181,8 +200,9 @@ In this example, the incoming request `http://example.tld/app1/foo` will be prox ### Per-VIRTUAL_PATH location configuration The same options as from [Per-VIRTUAL_HOST location configuration](#Per-VIRTUAL_HOST-location-configuration) are available on a `VIRTUAL_PATH` basis. -The only difference is that the filename gets an additional block `HASH=$(echo -n $VIRTUAL_PATH | sha1sum | awk '{ print $1 }')`. This is the sha1-hash of the `VIRTUAL_PATH` (no newline). This is done filename sanitization purposes. -The used filename is `${VIRTUAL_HOST}_${HASH}_location` +The only difference is that the filename gets an additional block `HASH=$(echo -n $VIRTUAL_PATH | sha1sum | awk '{ print $1 }')`. This is the sha1-hash of the `VIRTUAL_PATH` (no newline). This is done for filename sanitization purposes. + +The used filename is `${VIRTUAL_HOST}_${PATH_HASH}_location`, or when `VIRTUAL_HOST` is a regex, `${VIRTUAL_HOST_HASH}_${PATH_HASH}_location`. The filename of the previous example would be `example.tld_8610f6c344b4096614eab6e09d58885349f42faf_location`. @@ -214,7 +234,13 @@ Nginx variables such as `$scheme`, `$host`, and `$request_uri` can be used. Howe If you want to use `nginx-proxy` with different external ports that the default ones of `80` for `HTTP` traffic and `443` for `HTTPS` traffic, you'll have to use the environment variable(s) `HTTP_PORT` and/or `HTTPS_PORT` in addition to the changes to the Docker port mapping. If you change the `HTTPS` port, the redirect for `HTTPS` traffic will also be configured to redirect to the custom port. Typical usage, here with the custom ports `1080` and `10443`: ```console -docker run -d -p 1080:1080 -p 10443:10443 -e HTTP_PORT=1080 -e HTTPS_PORT=10443 -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 1080:1080 \ + --publish 10443:10443 \ + --env HTTP_PORT=1080 \ + --env HTTPS_PORT=10443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` ### Multiple Networks @@ -224,8 +250,12 @@ With the addition of [overlay networking](https://docs.docker.com/engine/usergui If you want your `nginx-proxy` container to be attached to a different network, you must pass the `--net=my-network` option in your `docker create` or `docker run` command. At the time of this writing, only a single network can be specified at container creation time. To attach to other networks, you can use the `docker network connect` command after your container is created: ```console -docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro \ - --name my-nginx-proxy --net my-network nginxproxy/nginx-proxy +docker run --detach \ + --name my-nginx-proxy \ + --publish 80:80 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --net my-network \ + nginxproxy/nginx-proxy docker network connect my-other-network my-nginx-proxy ``` @@ -241,7 +271,7 @@ Proxyed containers running in host network mode **must** use the [`VIRTUAL_PORT` If you allow traffic from the public internet to access your `nginx-proxy` container, you may want to restrict some containers to the internal network only, so they cannot be accessed from the public internet. On containers that should be restricted to the internal network, you should set the environment variable `NETWORK_ACCESS=internal`. By default, the _internal_ network is defined as `127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16`. To change the list of networks considered internal, mount a file on the `nginx-proxy` at `/etc/nginx/network_internal.conf` with these contents, edited to suit your needs: -```Nginx +```nginx # These networks are considered "internal" allow 127.0.0.0/8; allow 10.0.0.0/8; @@ -254,6 +284,7 @@ deny all; When internal-only access is enabled, external clients will be denied with an `HTTP 403 Forbidden` +> [!NOTE] > If there is a load-balancer / reverse proxy in front of `nginx-proxy` that hides the client IP (example: AWS Application/Elastic Load Balancer), you will need to use the nginx `realip` module (already installed) to extract the client's IP from the HTTP request headers. Please see the [nginx realip module configuration](http://nginx.org/en/docs/http/ngx_http_realip_module.html) for more details. This configuration can be added to a new config file and mounted in `/etc/nginx/conf.d/`. ⬆️ [back to table of contents](#table-of-contents) @@ -264,7 +295,8 @@ When internal-only access is enabled, external clients will be denied with an `H If you would like the reverse proxy to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container. -> Note: If you use `VIRTUAL_PROTO=https` and your backend container exposes port 80 and 443, `nginx-proxy` will use HTTPS on port 80. This is almost certainly not what you want, so you should also include `VIRTUAL_PORT=443`. +> [!NOTE] +> If you use `VIRTUAL_PROTO=https` and your backend container exposes port 80 and 443, `nginx-proxy` will use HTTPS on port 80. This is almost certainly not what you want, so you should also include `VIRTUAL_PORT=443`. ### uWSGI Upstream @@ -280,12 +312,9 @@ If you use fastcgi,you can set `VIRTUAL_ROOT=xxx` for your root directory ### Upstream 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** +> [!NOTE] > > - Don't forget the terminating semicolon (`;`). > - If you are using Docker Compose, remember to escape any dollar sign (`$`) characters (`$` becomes `$$`). @@ -302,6 +331,7 @@ services: - /var/run/docker.sock:/tmp/docker.sock:ro environment: HTTPS_METHOD: nohttps + myapp: image: jwilder/whoami expose: @@ -317,10 +347,7 @@ services: ### Upstream 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 backend server(s), set the `com.github.nginx-proxy.nginx-proxy.keepalive` label on the server's container either to `auto` or to the desired maximum number of idle connections. The `auto` setting will dynamically set the maximum number of idle connections to twice the number of servers listed in the corresponding `upstream{}` block, [per nginx recommendation](https://www.nginx.com/blog/avoiding-top-10-nginx-configuration-mistakes/#no-keepalives). +By default `nginx-proxy` will enable HTTP keep-alive between itself and backend server(s) and set the maximum number of idle connections to twice the number of servers listed in the corresponding `upstream{}` block, [per nginx recommendation](https://www.nginx.com/blog/avoiding-top-10-nginx-configuration-mistakes/#no-keepalives). To manually set the maximum number of idle connections or disable HTTP keep-alive entirely, use the `com.github.nginx-proxy.nginx-proxy.keepalive` label on the server's container (setting it to `disabled` will disable HTTP keep-alive). 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. @@ -328,14 +355,16 @@ See the [nginx keepalive documentation](https://nginx.org/en/docs/http/ngx_http_ ## 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 in directory -`/etc/nginx/htpasswd/{$VIRTUAL_HOST}` +In order to be able to secure your virtual host, you have to create a file named as its equivalent `VIRTUAL_HOST` variable (or if using a regex `VIRTUAL_HOST`, as the sha1 hash of the regex) in directory +`/etc/nginx/htpasswd/`. Example: `/etc/nginx/htpasswd/app.example.com`. ```console -docker run -d -p 80:80 -p 443:443 \ - -v /path/to/htpasswd:/etc/nginx/htpasswd \ - -v /path/to/certs:/etc/nginx/certs \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ +docker run --detach \ + --publish 80:80 \ + --publish 443:443 \ + --volume /path/to/htpasswd:/etc/nginx/htpasswd \ + --volume /path/to/certs:/etc/nginx/certs \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ nginxproxy/nginx-proxy ``` @@ -346,6 +375,42 @@ You'll need apache2-utils on the machine where you plan to create the htpasswd f ⬆️ [back to table of contents](#table-of-contents) +## mTLS client side certificate authentication +In mTLS, both the client and server have a certificate, and both sides authenticate using their public/private key pair. +A "root" TLS certificate is necessary for mTLS; this enables an organization to be their own certificate authority. The certificates used by authorized clients and servers have to correspond to this root certificate. The root certificate is self-signed, meaning that the organization creates it themselves. +Make sure you have a root certificate (CA) and client public/private key pair. There is a [howto in the wiki](https://github.com/nginx-proxy/nginx-proxy/wiki/mTLS-client-side-certificate-authentication). + +### Certificate Authority (CA) +#### Per-VIRTUAL_HOST CA +In order to secure a virtual host, you have to copy your CA certificate file (ca.crt) named as its equivalent `VIRTUAL_HOST` variable or if `VIRTUAL_HOST` is a regex, after the sha1 hash of the regex with the suffix `.ca.crt` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/app.example.com.ca.crt`. +Or if your `VIRTUAL_HOST` is a regex: `/etc/nginx/certs/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt`. + +#### Global CA +If you want to secure everything globally you can copy your CA certificate file (ca.crt) named as `ca.crt` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/ca.crt`. + +### Certificate Revocation List (CRL) +#### Per-VIRTUAL_HOST CRL +In order to use a certificate revocation list, you have to copy your CRL file named as its equivalent `VIRTUAL_HOST` variable or if `VIRTUAL_HOST` is a regex, after the sha1 hash of the regex with the suffix `.crl.pem` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/app.example.com.crl.pem`. +Or if your `VIRTUAL_HOST` is a regex: `/etc/nginx/certs/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem`. + +#### Global CRL +If you want to use a global CRL file you have to copy your CRL file named as `ca.crl.pem` in directory +`/etc/nginx/certs/`. Example: `/etc/nginx/certs/ca.crl.pem`. + +> [!NOTE] +> Use Per-VIRTUAL_HOST CRL if you configured the [Per-VIRTUAL_HOST CA](#per-virtual_host-ca) or Global CRL if you configured the [Global CA](#global-ca) + +> [!IMPORTANT] +> Make sure you rotate the CRL before it's expiration date, even if nothing has changed. An expired CRL will make Nginx unable to validate the certificates that were issued. + +### optional ssl_verify_client +Optional [`ssl_verify_client`](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client) can be activated by using the `com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional"` label on a proxied container. If this label is set on a proxied container access is not blocked but the result of the mTLS verify is stored in the [$ssl_client_verify](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify) variable which you can use this in the [Per-VIRTUAL_HOST location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_host-location-configuration) and [Per-VIRTUAL_PATH location](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#per-virtual_path-location-configuration) configurations. + +⬆️ [back to table of contents](#table-of-contents) + ## Logging The default nginx access log format is @@ -395,22 +460,27 @@ To remove colors from the container log output, set the [`NO_COLOR` environment ```console docker run --detach \ - --publish 80:80 \ - --env NO_COLOR=1 \ - --volume /var/run/docker.sock:/tmp/docker.sock:ro \ - nginxproxy/nginx-proxy + --publish 80:80 \ + --env NO_COLOR=1 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` ⬆️ [back to table of contents](#table-of-contents) ## SSL Support -SSL is supported using single host, wildcard and SNI certificates using naming conventions for certificates or optionally specifying a cert name (for SNI) as an environment variable. +SSL is supported using single host, wildcard and SAN certificates using naming conventions for certificates or optionally [specifying a cert name as an environment variable](#san-certificates). To enable SSL: ```console -docker run -d -p 80:80 -p 443:443 -v /path/to/certs:/etc/nginx/certs -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 80:80 \ + --publish 443:443 \ + --volume /path/to/certs:/etc/nginx/certs \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` The contents of `/path/to/certs` should contain the certificates and private keys for any virtual hosts in use. The certificate and keys should be named after the virtual host with a `.crt` and `.key` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` should have a `foo.bar.com.crt` and `foo.bar.com.key` file in the certs directory. @@ -427,27 +497,33 @@ By default nginx-proxy generates location blocks to handle ACME HTTP Challenge. - `false`: do not handle ACME HTTP Challenge at all. - `legacy`: legacy behavior for compatibility with older (<= `2.3`) versions of acme-companion, only handle ACME HTTP challenge when there is a certificate for the domain and `HTTPS_METHOD=redirect`. +By default, nginx-proxy does not handle ACME HTTP Challenges for unknown virtual hosts. This may happen in cases when a container is not running at the time of the renewal. To enable handling of unknown virtual hosts, set `ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST` environment variable to `true` on the nginx-proxy container. + ### Diffie-Hellman Groups [RFC7919 groups](https://datatracker.ietf.org/doc/html/rfc7919#appendix-A) with key lengths of 2048, 3072, and 4096 bits are [provided by `nginx-proxy`](https://github.com/nginx-proxy/nginx-proxy/dhparam). The ENV `DHPARAM_BITS` can be set to `2048` or `3072` to change from the default 4096-bit key. The DH key file will be located in the container at `/etc/nginx/dhparam/dhparam.pem`. Mounting a different `dhparam.pem` file at that location will override the RFC7919 key. To use custom `dhparam.pem` files per-virtual-host, the files should be named after the virtual host with a `dhparam` suffix and `.pem` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` should have a `foo.bar.com.dhparam.pem` file in the `/etc/nginx/certs` directory. -> COMPATIBILITY WARNING: The default generated `dhparam.pem` key is 4096 bits for A+ security. Some older clients (like Java 6 and 7) do not support DH keys with over 1024 bits. In order to support these clients, you must provide your own `dhparam.pem`. +> [!WARNING] +> The default generated `dhparam.pem` key is 4096 bits for A+ security. Some older clients (like Java 6 and 7) do not support DH keys with over 1024 bits. In order to support these clients, you must provide your own `dhparam.pem`. In the separate container setup, no pre-generated key will be available and neither the [nginxproxy/docker-gen](https://hub.docker.com/r/nginxproxy/docker-gen) image, nor the offical [nginx](https://registry.hub.docker.com/_/nginx/) image will provide one. If you still want A+ security in a separate container setup, you should mount an RFC7919 DH key file to the nginx container at `/etc/nginx/dhparam/dhparam.pem`. Set `DHPARAM_SKIP` environment variable to `true` to disable using default Diffie-Hellman parameters. The default value is `false`. ```console -docker run -e DHPARAM_SKIP=true .... +docker run --env DHPARAM_SKIP=true .... ``` ### Wildcard Certificates -Wildcard certificates and keys should be named after the domain name with a `.crt` and `.key` extension. For example `VIRTUAL_HOST=foo.bar.com` would use cert name `bar.com.crt` and `bar.com.key`. +Wildcard certificates and keys should be named after the parent domain name with a `.crt` and `.key` extension. For example: -### SNI +- `VIRTUAL_HOST=foo.bar.com` would use cert name `bar.com.crt` and `bar.com.key` if `foo.bar.com.crt` and `foo.bar.com.key` are not available +- `VIRTUAL_HOST=sub.foo.bar.com` use cert name `foo.bar.com.crt` and `foo.bar.com.key` if `sub.foo.bar.com.crt` and `sub.foo.bar.com.key` are not available, but won't use `bar.com.crt` and `bar.com.key`. + +### SAN Certificates If your certificate(s) supports multiple domain names, you can start a container with `CERT_NAME=` to identify the certificate to be used. For example, a certificate for `*.foo.com` and `*.bar.com` could be named `shared.crt` and `shared.key`. A container running with `VIRTUAL_HOST=foo.bar.com` and `CERT_NAME=shared` will then use this shared cert. @@ -459,7 +535,10 @@ To enable OCSP Stapling for a domain, `nginx-proxy` looks for a PEM certificate The default SSL cipher configuration is based on the [Mozilla intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29) version 5.0 which should provide compatibility with clients back to Firefox 27, Android 4.4.2, Chrome 31, Edge, IE 11 on Windows 7, Java 8u31, OpenSSL 1.0.1, Opera 20, and Safari 9. Note that the DES-based TLS ciphers were removed for security. The configuration also enables HSTS, PFS, OCSP stapling and SSL session caches. Currently TLS 1.2 and 1.3 are supported. -If you don't require backward compatibility, you can use the [Mozilla modern profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility) profile instead by including the environment variable `SSL_POLICY=Mozilla-Modern` to the nginx-proxy container or to your container. This profile is compatible with clients back to Firefox 63, Android 10.0, Chrome 70, Edge 75, Java 11, OpenSSL 1.1.1, Opera 57, and Safari 12.1. Note that this profile is **not** compatible with any version of Internet Explorer. +If you don't require backward compatibility, you can use the [Mozilla modern profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility) profile instead by including the environment variable `SSL_POLICY=Mozilla-Modern` to the nginx-proxy container or to your container. This profile is compatible with clients back to Firefox 63, Android 10.0, Chrome 70, Edge 75, Java 11, OpenSSL 1.1.1, Opera 57, and Safari 12.1. + +> [!NOTE] +> This profile is **not** compatible with any version of Internet Explorer. Complete list of policies available through the `SSL_POLICY` environment variable, including the [AWS ELB Security Policies](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies) and [AWS Classic ELB security policies](https://docs.aws.amazon.com/fr_fr/elasticloadbalancing/latest/classic/elb-security-policy-table.html): @@ -480,6 +559,7 @@ Complete list of policies available through the `SSL_POLICY` environment variabl Mozilla-Old + (this policy should use a 1024 bits DH key for compatibility but this container provides a 4096 bits key. The Diffie-Hellman Groups section details different methods of bypassing this, either globally or per virtual-host.) @@ -562,57 +642,98 @@ Complete list of policies available through the `SSL_POLICY` environment variabl
-Note that the `Mozilla-Old` policy should use a 1024 bits DH key for compatibility but this container provides a 4096 bits key. The [Diffie-Hellman Groups](#diffie-hellman-groups) section details different methods of bypassing this, either globally or per virtual-host. - The default behavior for the proxy when port 80 and 443 are exposed is as follows: - 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). +- 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. +- If the virtual host does not have a usable cert, and `default.crt` and `default.key` do not exist, or if the virtual host is configured not to trust the default certificate, SSL handshake will be rejected (see [Default and Missing Certificate](#default-and-missing-certificate) below). + +The redirection from HTTP to HTTPS use by default a [`301`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301) response for every HTTP methods (except `CONNECT` and `TRACE` which are disabled on nginx). If you wish to use a custom redirection response for the `OPTIONS`, `POST`, `PUT`, `PATCH` and `DELETE` HTTP methods, you can either do it globally with the environment variable `NON_GET_REDIRECT` on the proxy container or per virtual host with the `com.github.nginx-proxy.nginx-proxy.non-get-redirect` label on proxied containers. Valid values are [`307`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307) and [`308`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308). 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. By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable `HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`. -_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. +> [!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 +### Default and Missing Certificate -If no matching certificate is found for a given virtual host, nginx-proxy will: +If no matching certificate is found for a given virtual host, nginx-proxy will configure nginx to use the default certificate (`default.crt` with `default.key`). -- configure nginx to use the default certificate (`default.crt` with `default.key`) and return a 500 error for HTTPS, -- force enable HTTP; i.e. `HTTPS_METHOD` will switch to `noredirect` if it was set to `nohttp` or `redirect`. - If this switch to HTTP is not wanted set `ENABLE_HTTP_ON_MISSING_CERT=false` (default is `true`). +If the default certificate is also missing, nginx-proxy will: -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: +- force enable HTTP; i.e. `HTTPS_METHOD` will switch to `noredirect` if it was set to `nohttp` or `redirect`. If this switch to HTTP is not wanted set `ENABLE_HTTP_ON_MISSING_CERT=false` (default is `true`). +- configure nginx to reject the SSL handshake for this vhost. Client browsers will render a TLS error page. As of October 2024, web browsers display the following error messages: -- Chrome: +#### Chrome: - > This site can't provide a secure connection - > - > example.test sent an invalid response. - > - > Try running Connectivity Diagnostics. - > - > `ERR_SSL_PROTOCOL_ERROR` +> This site can’t be reached +> +> The web page at https://example.test/ might be temporarily down or it may have moved permanently to a new web address. +> +> `ERR_SSL_UNRECOGNIZED_NAME_ALERT` -- Firefox: +#### 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". +> Secure Connection Failed +> +> An error occurred during a connection to example.test. SSL peer has no certificate for the requested DNS name. +> +> Error code: `SSL_ERROR_UNRECOGNIZED_NAME_ALERT` +> +> - The page you are trying to view cannot be shown because the authenticity of the received data could not be verified. +> - Please contact the website owners to inform them of this problem. + +#### Safari: + +> Safari Can't Open the Page +> +> Safari can't open the page "https://example.test" because Safari can't establish a secure connection to the server "example.test". + +> [!NOTE] +> Prior to version `1.7`, nginx-proxy never trusted the default certificate: when the default certificate was present, virtual hosts that did not have a usable per-virtual-host cert used the default cert but always returned a 500 error over HTTPS. If you want to restore this behaviour, you can do it globally by setting the enviroment variable `TRUST_DEFAULT_CERT` to `false` on the proxy container, or per-virtual-host by setting the label `com.github.nginx-proxy.nginx-proxy.trust-default-cert`to `false` on a proxied container. + +### Certificate selection + +Summarizing all the above informations, nginx-proxy will select the certificate for a given virtual host using the following sequence: + +1. if `CERT_NAME` is used, nginx-proxy will use the corresponding certificate if it exists (eg `foor.bar.com` → `CERT_NAME.crt`), or disable HTTPS for this virtual host if it does not. See [SAN certificates](#san-certificates). +2. if a certificate exactly matching the virtual host hostname exist, nginx-proxy will use it (eg `foo.bar.com` → `foo.bar.com.crt`). +3. if the virtual host hostname is a subdomain (eg `foo.bar.com` but not `bar.com`) and a certificate exactly matching its parent domain exist , nginx-proxy will use it (eg `foor.bar.com` → `bar.com.crt`). See [wildcard certificates](#wildcard-certificates). +4. if the default certificate (`default.crt`) exist and is trusted, nginx-proxy will use it (eg `foor.bar.com` → `default.crt`). See [default and missing certificate](#default-and-missing-certificate). +5. if the default certificate does not exist or isn't trusted, nginx-proxy will disable HTTPS for this virtual host (eg `foor.bar.com` → no HTTPS). + +> [!IMPORTANT] +> Using `CERT_NAME` take precedence over the certificate selection process, meaning nginx-proxy will not auto select a correct certificate in step 2 trough 5 if `CERT_NAME` was used with an incorrect value or without corresponding certificate. + +> [!NOTE] +> In all the above cases, if a private key file corresponding to the selected certificate (eg `foo.bar.com.key` for the `foor.bar.com.crt` certificate) does not exist, HTTPS will be disabled for this virtual host. ⬆️ [back to table of contents](#table-of-contents) ## IPv6 Support -You can activate the IPv6 support for the nginx-proxy container by passing the value `true` to the `ENABLE_IPV6` environment variable: +### IPv6 Docker Networks + +nginx-proxy support both IPv4 and IPv6 on Docker networks. + +By default nginx-proxy will prefer IPv4: if a container can be reached over both IPv4 and IPv6, only its IPv4 will be used. + +This can be changed globally by setting the environment variable `PREFER_IPV6_NETWORK` to `true` on the proxy container: with this setting the proxy will only use IPv6 for containers that can be reached over both IPv4 and IPv6. + +IPv4 and IPv6 are never both used at the same time on containers that use both IP stacks to avoid artificially inflating the effective round robin weight of those containers. + +### Listening on IPv6 + +By default the nginx-proxy container will only listen on IPv4. To enable listening on IPv6 too, set the `ENABLE_IPV6` environment variable to `true`: ```console -docker run -d -p 80:80 -e ENABLE_IPV6=true -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --publish 80:80 \ + --env ENABLE_IPV6=true \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` ### Scoped IPv6 Resolvers @@ -639,14 +760,17 @@ More reading on the potential TCP head-of-line blocking issue with HTTP/2: [HTTP ### HTTP/3 support -> **Warning** +> [!WARNING] > HTTP/3 support [is still considered experimental in nginx](https://www.nginx.com/blog/binary-packages-for-preview-nginx-quic-http3-implementation/) and as such is considered experimental in nginx-proxy too and is disabled by default. [Feedbacks for the HTTP/3 support are welcome in #2271.](https://github.com/nginx-proxy/nginx-proxy/discussions/2271) HTTP/3 use the QUIC protocol over UDP (unlike HTTP/1.1 and HTTP/2 which work over TCP), so if you want to use HTTP/3 you'll have to explicitely publish the 443/udp port of the proxy in addition to the 443/tcp port: ```console -docker run -d -p 80:80 -p 443:443/tcp -p 443:443/udp \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ +docker run --detach \ + --publish 80:80 \ + --publish 443:443/tcp \ + --publish 443:443/udp \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ nginxproxy/nginx-proxy ``` @@ -681,6 +805,25 @@ For legacy compatibility reasons, `nginx-proxy` forwards any client-supplied `X- 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. +### Proxy Protocol Support + +`nginx-proxy` has support for the [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). This allows a separate proxy to send requests to `nginx-proxy` and encode information about the client connection without relying on HTTP headers. This can be enabled by setting `ENABLE_PROXY_PROTOCOL=true` on the main `nginx-proxy` container. It's important to note that enabling the proxy protocol will require all connections to `nginx-proxy` to use the protocol. + +You can use this feature in conjunction with the `realip` module in nginx. This will allow for setting the `$remote_addr` and `$remote_port` nginx variables to the IP and port that are provided from the protocol message. Documentation for this functionality can be found in the [nginx documentation](https://nginx.org/en/docs/http/ngx_http_realip_module.html). + +A simple example is as follows: + +1. Create a configuration file for nginx, this can be global (in `conf.d`) or host specific (in `vhost.d`) +2. Add your `realip` configuration: + +```nginx +# Your proxy server ip address +set_real_ip_from 192.168.1.0/24; + +# Where to replace `$remote_addr` and `$remote_port` from +real_ip_header proxy_protocol; +``` + ⬆️ [back to table of contents](#table-of-contents) ## Custom Nginx Configuration @@ -691,7 +834,7 @@ If you need to configure Nginx beyond what is possible using environment variabl If you want to replace the default proxy settings for the nginx container, add a configuration file at `/etc/nginx/proxy.conf`. A file with the default settings would look like this: -```Nginx +```nginx # HTTP 1.1 support proxy_http_version 1.1; proxy_set_header Host $http_host; @@ -709,7 +852,8 @@ proxy_set_header X-Original-URI $request_uri; proxy_set_header Proxy ""; ``` -**_NOTE_**: If you provide this file it will replace the defaults; you may want to check the [nginx.tmpl](https://github.com/nginx-proxy/nginx-proxy/blob/main/nginx.tmpl) file to make sure you have all of the needed options. +> [!IMPORTANT] +> If you provide this file it will replace the defaults; you may want to check the [nginx.tmpl](https://github.com/nginx-proxy/nginx-proxy/blob/main/nginx.tmpl) file to make sure you have all of the needed options. ### Proxy-wide @@ -725,57 +869,232 @@ RUN { \ } > /etc/nginx/conf.d/my_proxy.conf ``` -Or it can be done by mounting in your custom configuration in your `docker run` command: +Or it can be done by mounting in your custom configuration in your `docker run` command or your Docker Compose file: + +```nginx +# content of the my_proxy.conf file +server_tokens off; +client_max_body_size 100m; +``` + +
+ Docker CLI ```console -docker run -d -p 80:80 -p 443:443 -v /path/to/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy +docker run --detach \ + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro \ + nginxproxy/nginx-proxy +``` + +
+ +
+ Docker Compose file + +```yaml +services: + proxy: + image: nginxproxy/nginx-proxy + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - /path/to/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro ``` +
+ +> [!NOTE] +> The filenames of extra configuration files affect the order in which configuration is applied. +> nginx reads configuration from `/etc/nginx/conf.d` directory in alphabetical order. +> Note that the configuration managed by nginx-proxy is placed at `/etc/nginx/conf.d/default.conf`. + ### Per-VIRTUAL_HOST -To add settings on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d`. Unlike in the proxy-wide case, which allows multiple config files with any name ending in `.conf`, the per-`VIRTUAL_HOST` file must be named exactly after the `VIRTUAL_HOST`. +To add settings on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d`. Unlike in the proxy-wide case, which allows multiple config files with any name ending in `.conf`, the per-`VIRTUAL_HOST` file must be named exactly after the `VIRTUAL_HOST`, or if `VIRTUAL_HOST` is a regex, after the sha1 hash of the regex. In order to allow virtual hosts to be dynamically configured as backends are added and removed, it makes the most sense to mount an external directory as `/etc/nginx/vhost.d` as opposed to using derived images or mounting individual configuration files. For example, if you have a virtual host named `app.example.com`, you could provide a custom configuration for that host as follows: +1. create your virtual host config file: + +```nginx +# content of the custom-vhost-config.conf file +client_max_body_size 100m; +``` + +2. mount it to `/etc/nginx/vhost.d/app.example.com`: +
+ Docker CLI + ```console -docker run -d -p 80:80 -p 443:443 -v /path/to/vhost.d:/etc/nginx/vhost.d:ro -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy -{ echo 'server_tokens off;'; echo 'client_max_body_size 100m;'; } > /path/to/vhost.d/app.example.com +docker run --detach \ + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/app.example.com:ro \ + nginxproxy/nginx-proxy ``` -If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=example.com,www.example.com`), the virtual host configuration file must exist for each hostname. If you would like to use the same configuration for multiple virtual host names, you can use a symlink: +
+ +
+ Docker Compose file + +```yaml +services: + proxy: + image: nginxproxy/nginx-proxy + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/app.example.com:ro +``` + +
+ +If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=example.com,www.example.com`), the virtual host configuration file must exist for each hostname: + +
+ Docker CLI ```console -{ echo 'server_tokens off;'; echo 'client_max_body_size 100m;'; } > /path/to/vhost.d/www.example.com -ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com +docker run --detach \ + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/example.com:ro \ + --volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/www.example.com:ro \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + nginxproxy/nginx-proxy ``` +
+ +
+ Docker Compose file + +```yaml +services: + proxy: + image: nginxproxy/nginx-proxy + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/example.com:ro + - /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/www.example.com:ro +``` + +
+ ### Per-VIRTUAL_HOST default configuration -If you want most of your virtual hosts to use a default single configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default` file. This file will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}` file associated with it. +If you want most of your virtual hosts to use a default single configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default` file. This file will be used on any virtual host which does not have a [per-VIRTUAL_HOST file](#per-virtual_host) associated with it. ### Per-VIRTUAL_HOST location configuration -To add settings to the "location" block on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d` just like the previous section except with the suffix `_location`. +To add settings to the "location" block on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d` just like the per-`VIRTUAL_HOST` section except with the suffix `_location` (like this section, if your `VIRTUAl_HOST` is a regex, use the sha1 hash of the regex instead, with the suffix `_location` appended). For example, if you have a virtual host named `app.example.com` and you have configured a proxy_cache `my-cache` in another custom file, you could tell it to use a proxy cache as follows: +1. create your virtual host location config file: + +```nginx +# content of the custom-vhost-location-config.conf file +proxy_cache my-cache; +proxy_cache_valid 200 302 60m; +proxy_cache_valid 404 1m; +``` + +2. mount it to `/etc/nginx/vhost.d/app.example.com_location`: + +
+ Docker CLI + ```console -docker run -d -p 80:80 -p 443:443 -v /path/to/vhost.d:/etc/nginx/vhost.d:ro -v /var/run/docker.sock:/tmp/docker.sock:ro nginxproxy/nginx-proxy -{ echo 'proxy_cache my-cache;'; echo 'proxy_cache_valid 200 302 60m;'; echo 'proxy_cache_valid 404 1m;' } > /path/to/vhost.d/app.example.com_location +docker run --detach \ + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/app.example.com_location:ro \ + nginxproxy/nginx-proxy ``` -If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=example.com,www.example.com`), the virtual host configuration file must exist for each hostname. If you would like to use the same configuration for multiple virtual host names, you can use a symlink: +
+ +
+ Docker Compose file + +```yaml +services: + proxy: + image: nginxproxy/nginx-proxy + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/app.example.com_location:ro +``` + +
+ +If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=example.com,www.example.com`), the virtual host configuration file must exist for each hostname: + +
+ Docker CLI ```console -{ echo 'proxy_cache my-cache;'; echo 'proxy_cache_valid 200 302 60m;'; echo 'proxy_cache_valid 404 1m;' } > /path/to/vhost.d/app.example.com_location -ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com +docker run --detach \ + --name nginx-proxy \ + --publish 80:80 \ + --publish 443:443 \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/example.com_location:ro \ + --volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/www.example.com_location:ro \ + nginxproxy/nginx-proxy +``` + +
+ +
+ Docker Compose file + +```yaml +services: + proxy: + image: nginxproxy/nginx-proxy + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/example.com_location:ro + - /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/www.example.com_location:ro ``` +
+ ### Per-VIRTUAL_HOST location default configuration -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. +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 [Per-VIRTUAL_HOST location file](#per-virtual_host-location-configuration) associated with it. ### Overriding `location` blocks @@ -785,7 +1104,7 @@ The `${VIRTUAL_HOST}_${PATH_HASH}_location`, `${VIRTUAL_HOST}_location`, and `de /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). +where `${VIRTUAL_HOST}` is the name of the virtual host (the `VIRTUAL_HOST` environment variable), or the sha1 hash of `VIRTUAL_HOST` when it's a regex, 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 `/`: @@ -797,7 +1116,7 @@ When an override file exists, the `location` block that is normally created by ` 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: -``` +```nginx location / { proxy_pass http://app.example.com; } @@ -817,10 +1136,11 @@ docker run --detach \ --publish 80:80 \ --volume /var/run/docker.sock:/tmp/docker.sock:ro \ --volume /path/to/error.html:/usr/share/nginx/html/errors/50x.html:ro \ - nginxproxy/nginx-proxy:1.5 + nginxproxy/nginx-proxy ``` -Note that this will not replace your own services error pages. +> [!NOTE] +> This will not replace your own services error pages. ⬆️ [back to table of contents](#table-of-contents) @@ -872,10 +1192,11 @@ docker run --detach \ --publish 53:53:udp \ --volume /var/run/docker.sock:/tmp/docker.sock:ro \ --volume ./stream.conf:/etc/nginx/toplevel.conf.d/stream.conf:ro \ - nginxproxy/nginx-proxy:1.5 + nginxproxy/nginx-proxy ``` -Please note that TCP and UDP stream are not core features of nginx-proxy, so the above is provided as an example only, without any guarantee. +> [!NOTE] +> TCP and UDP stream are not core features of nginx-proxy, so the above is provided as an example only, without any guarantee. ⬆️ [back to table of contents](#table-of-contents) @@ -883,7 +1204,8 @@ Please note that TCP and UDP stream are not core features of nginx-proxy, so the By default the nginx configuration `upstream` blocks will use this block's corresponding hostname as a predictable name. However, this can cause issues in some setups (see [this issue](https://github.com/nginx-proxy/nginx-proxy/issues/1162)). In those cases you might want to switch to SHA1 names for the `upstream` blocks by setting the `SHA1_UPSTREAM_NAME` environment variable to `true` on the nginx-proxy container. -Please note that using regular expressions in `VIRTUAL_HOST` will always result in a corresponding `upstream` block with an SHA1 name. +> [!NOTE] +> Using regular expressions in `VIRTUAL_HOST` will always result in a corresponding `upstream` block with an SHA1 name. ⬆️ [back to table of contents](#table-of-contents) @@ -908,25 +1230,65 @@ I'm 5b129ab83266 To run nginx proxy as a separate container you'll need to have [nginx.tmpl](https://github.com/nginx-proxy/nginx-proxy/blob/main/nginx.tmpl) on your host system. -First start nginx with a volume: +First start nginx with a volume mounted to `/etc/nginx/conf.d`: ```console -docker run -d -p 80:80 --name nginx -v /tmp/nginx:/etc/nginx/conf.d -t nginx +docker run --detach \ + --name nginx \ + --publish 80:80 \ + --volume /tmp/nginx:/etc/nginx/conf.d \ + nginx ``` Then start the docker-gen container with the shared volume and template: ```console -docker run --volumes-from nginx \ - -v /var/run/docker.sock:/tmp/docker.sock:ro \ - -v $(pwd):/etc/docker-gen/templates \ - -t nginxproxy/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf +docker run --detach \ + --name docker-gen \ + --volumes-from nginx \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume $(pwd):/etc/docker-gen/templates \ + nginxproxy/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf ``` Finally, start your containers with `VIRTUAL_HOST` environment variables. ```console -docker run -e VIRTUAL_HOST=foo.bar.com ... +docker run --env VIRTUAL_HOST=foo.bar.com ... +``` + +### Network segregation + +To allow for network segregation of the nginx and docker-gen containers, the label `com.github.nginx-proxy.nginx-proxy.nginx` must be applied to the nginx container, otherwise it is assumed that nginx and docker-gen share the same network: + +```console +docker run --detach \ + --name nginx \ + --publish 80:80 \ + --label "com.github.nginx-proxy.nginx-proxy.nginx" \ + --volume /tmp/nginx:/etc/nginx/conf.d \ + nginx +``` + +Network segregation make it possible to run the docker-gen container in an [internal network](https://docs.docker.com/reference/cli/docker/network/create/#internal), unreachable from the outside. + +You can also customise the label being used by docker-gen to find the nginx container with the `NGINX_CONTAINER_LABEL`environment variable (on the docker-gen container): + +```console +docker run --detach \ + --name docker-gen \ + --volumes-from nginx \ + --volume /var/run/docker.sock:/tmp/docker.sock:ro \ + --volume $(pwd):/etc/docker-gen/templates \ + --env "NGINX_CONTAINER_LABEL=com.github.foobarbuzz" \ + nginxproxy/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + +docker run --detach \ + --name nginx \ + --publish 80:80 \ + --label "com.github.foobarbuzz" \ + --volume "/tmp/nginx:/etc/nginx/conf.d" \ + nginx ``` ⬆️ [back to table of contents](#table-of-contents) @@ -934,8 +1296,6 @@ docker run -e VIRTUAL_HOST=foo.bar.com ... ## Docker Compose ```yaml -version: "2" - services: nginx-proxy: image: nginxproxy/nginx-proxy @@ -966,6 +1326,124 @@ I'm 5b129ab83266 ⬆️ [back to table of contents](#table-of-contents) +## Configuration summary + +This section summarize the configurations available on the proxy and proxied container. + +### Proxy container + +Configuration available either on the nginx-proxy container, or the docker-gen container in a [separate containers setup](#separate-containers): + +| Environment Variable | Default Value | +|---------------------|---------------| +| [`ACME_HTTP_CHALLENGE_LOCATION`](#ssl-support-using-an-acme-ca) | `true` | +| [`ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST`](#ssl-support-using-an-acme-ca) | `false` | +| [`DEBUG_ENDPOINT`](#debug-endpoint) | `false` | +| [`DEFAULT_HOST`](#default-host) | no default value | +| [`DEFAULT_ROOT`](#default_root) | `404` | +| [`DHPARAM_SKIP`](#diffie-hellman-groups) | `false` | +| [`DHPARAM_BITS`](#diffie-hellman-groups) | `4096` | +| [`DISABLE_ACCESS_LOGS`](#disable-access-logs) | `false` | +| [`ENABLE_HTTP_ON_MISSING_CERT`](#default-and-missing-certificate) | `true` | +| [`ENABLE_HTTP2`](#http2-support) | `true` | +| [`ENABLE_HTTP3`](#http3-support) | `false` | +| [`ENABLE_IPV6`](#listening-on-ipv6) | `false` | +| [`ENABLE_PROXY_PROTOCOL`](#proxy-protocol-support) | `false` | +| [`HTTP_PORT`](#custom-external-httphttps-ports) | `80` | +| [`HTTPS_PORT`](#custom-external-httphttps-ports) | `443` | +| [`HTTPS_METHOD`](#how-ssl-support-works) | `redirect` | +| [`HSTS`](#how-ssl-support-works) | `max-age=31536000` | +| [`LOG_FORMAT`](#custom-log-format) | no default value | +| [`LOG_FORMAT_ESCAPE`](#log-format-escaping) | no default value | +| [`LOG_JSON`](#json-log-format) | `false` | +| [`NGINX_CONTAINER_LABEL`](#network-segregation) | `com.github.nginx-proxy.nginx-proxy.nginx` | +| [`NON_GET_REDIRECT`](#how-ssl-support-works) | `301` | +| [`PREFER_IPV6_NETWORK`](#ipv6-docker-networks) | `false` | +| `RESOLVERS` | no default value | +| [`SHA1_UPSTREAM_NAME`](#unhashed-vs-sha1-upstream-names) | `false` | +| [`SSL_POLICY`](#how-ssl-support-works) | `Mozilla-Intermediate` | +| [`TRUST_DEFAULT_CERT`](#default-and-missing-certificate) | `true` | +| [`TRUST_DOWNSTREAM_PROXY`](#trusting-downstream-proxy-headers) | `true` | + +### Proxyied container + +Configuration available on each proxied container, either by environment variable or by label: + +| Environment Variable | Label | Default Value | +|---------------------|---------------|---------------| +| [`ACME_HTTP_CHALLENGE_LOCATION`](#ssl-support-using-an-acme-ca) | n/a | global (proxy) value | +| [`CERT_NAME`](#san-certificates) | n/a | no default value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.debug-endpoint`](#debug-endpoint) | global (proxy) value | +| [`ENABLE_HTTP_ON_MISSING_CERT`](#default-and-missing-certificate) | n/a | global (proxy) value | +| [`HSTS`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.http2.enable`](#http2-support) | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.http3.enable`](#http3-support) | global (proxy) value | +| [`HTTPS_METHOD`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.keepalive`](#upstream-server-http-keep-alive-support) | `auto` | +| n/a | [`com.github.nginx-proxy.nginx-proxy.loadbalance`](#upstream-server-http-load-balancing-support) | no default value | +| [`NETWORK_ACCESS`](#internet-vs-local-network-access) | n/a | `external` | +| n/a | [`com.github.nginx-proxy.nginx-proxy.non-get-redirect`](#how-ssl-support-works) | global (proxy) value | +| [`SERVER_TOKENS`](#per-virtual_host-server_tokens-configuration) | n/a | no default value | +| [`SSL_POLICY`](#how-ssl-support-works) | n/a | global (proxy) value | +| n/a | [`com.github.nginx-proxy.nginx-proxy.ssl_verify_client`](#optional-ssl_verify_client) | `on` | +| n/a | [`com.github.nginx-proxy.nginx-proxy.trust-default-cert`](#default-and-missing-certificate) | global (proxy) value | +| [`VIRTUAL_DEST`](#virtual_dest) | n/a | `empty string` | +| [`VIRTUAL_HOST`](#virtual-hosts-and-ports) | n/a | no default value | +| [`VIRTUAL_HOST_MULTIPORTS`](#multiple-ports) | n/a | no default value | +| [`VIRTUAL_PATH`](#path-based-routing) | n/a | `/` | +| [`VIRTUAL_PORT`](#virtual-ports) | n/a | no default value | +| [`VIRTUAL_PROTO`](#upstream-backend-features) | n/a | `http` | +| [`VIRTUAL_ROOT`](#fastcgi-file-root-directory) | n/a | `/var/www/public` | + +### Configuration by files + +Additional configuration and/or features available by mounting files to the nginx-proxy container (or to both the nginx and docker-gen containers in a [separate containers setup](#separate-containers)). + +In the following tables, `` is the value of the `VIRTUAL_HOST` environment variable, or the SHA-1 hash of the regex if `VIRTUAL_HOST` is a regex. `` is the SHA-1 hash of the path, as described in [Per-Virtual Path Location Configuration](#per-virtual_path-location-configuration). + +#### Proxy-wide + +| File Path | Description | +|-----------|-------------| +| [`/etc/nginx/conf.d/proxy.conf`](#replacing-default-proxy-settings) | Proxy-wide configuration, replacing the default proxy settings. | +| [`/etc/nginx/conf.d/.conf`](#proxy-wide) | Proxy-wide configuration, augmenting the default proxy settings. | +| [`/etc/nginx/toplevel.conf.d/.conf`](#tcp-and-udp-stream) | Custom Nginx configuration, augmenting the default Nginx settings. | +| [`/usr/share/nginx/html/errors/50x.html`](#custom-error-page) | Custom error page for 50x errors, replacing the default error page. | + +#### Per-VIRTUAL_HOST configuration + +| File Path | Description | +|-----------|-------------| +| [`/etc/nginx/vhost.d/`](#per-virtual_host) | Per-`VIRTUAL_HOST` additional configuration. | +| [`/etc/nginx/vhost.d/default`](#per-virtual_host-default-configuration) | Per-`VIRTUAL_HOST` default additional configuration. | +| [`/etc/nginx/vhost.d/_location`](#per-virtual_host-location-configuration) | Per-`VIRTUAL_HOST` additional location configuration. | +| [`/etc/nginx/vhost.d/__location`](#per-virtual_path-location-configuration) | Per-`VIRTUAL_PATH` additional location configuration. | +| [`/etc/nginx/vhost.d/default_location`](#per-virtual_host-location-default-configuration) | Per-`VIRTUAL_HOST` default additional location configuration. | +| [`/etc/nginx/vhost.d/_location_override`](#overriding-location-blocks) | Per-`VIRTUAL_HOST` location configuration override. | +| [`/etc/nginx/vhost.d/__location_override`](#overriding-location-blocks) | Per-`VIRTUAL_PATH` location configuration override. | + +#### Authentication + +| File Path | Description | +|-----------|-------------| +| [`/etc/nginx/htpasswd/`](#basic-authentication-support) | Basic authentication for a specific `VIRTUAL_HOST`. | +| [`/etc/nginx/htpasswd/_`](#basic-authentication-support) | Basic authentication for a specific path within a `VIRTUAL_HOST`. | +| [`/etc/nginx/certs/.ca.crt`](#per-virtual_host-ca) | Per-`VIRTUAL_HOST` Certificate Authority (CA) certificate for mTLS client authentication. | +| [`/etc/nginx/certs/.crl.pem`](#per-virtual_host-crl) | Per-`VIRTUAL_HOST` Certificate Revocation List (CRL) for mTLS client authentication. | +| [`/etc/nginx/certs/ca.crt`](#global-ca) | Global Certificate Authority (CA) certificate for mTLS client authentication. | +| [`/etc/nginx/certs/ca.crl.pem`](#global-crl) | Global Certificate Revocation List (CRL) for mTLS client authentication. | + +#### SSL/TLS + +| File Path | Description | +|-----------|-------------| +| [`/etc/nginx/certs/.crt`](#ssl-support) | Per-`VIRTUAL_HOST` SSL/TLS certificate. | +| [`/etc/nginx/certs/.key`](#ssl-support) | Per-`VIRTUAL_HOST` SSL/TLS private key. | +| [`/etc/nginx/certs/.dhparam.pem`](#diffie-hellman-groups) | Per-`VIRTUAL_HOST` Diffie-Hellman parameters. | +| [`/etc/nginx/certs/dhparam.pem`](#diffie-hellman-groups) | Global Diffie-Hellman parameters. | + +⬆️ [back to table of contents](#table-of-contents) + ## Troubleshooting If you can't access your `VIRTUAL_HOST`, inspect the generated nginx configuration: @@ -976,17 +1454,17 @@ docker exec nginx -T Pay attention to the `upstream` definition blocks, which should look like this: -```Nginx +```nginx # foo.example.com upstream foo.example.com { - ## Can be connected with "my_network" network - # Exposed ports: [{ tcp } { tcp } ...] - # Default virtual port: - # VIRTUAL_PORT: - # foo - server 172.18.0.9:; - # Fallback entry - server 127.0.0.1 down; + ## Can be connected with "my_network" network + # Exposed ports: [{ tcp } { tcp } ...] + # Default virtual port: + # VIRTUAL_PORT: + # foo + server 172.18.0.9:; + # Fallback entry + server 127.0.0.1 down; } ``` @@ -996,6 +1474,115 @@ The effective `Port` is retrieved by order of precedence: 1. From the container's exposed port if there is only one 1. From the default port 80 when none of the above methods apply +### Debug endpoint + +The debug endpoint can be enabled: + +- globally by setting the `DEBUG_ENDPOINT` environment variable to `true` on the nginx-proxy container. +- per container by setting the `com.github.nginx-proxy.nginx-proxy.debug-endpoint` label to `true` on a proxied container. + +Enabling it will expose the endpoint at `/nginx-proxy-debug`. + +Querying the debug endpoint will show the global config, along with the virtual host and per path configs in JSON format. + +```yaml +services: + nginx-proxy: + image: nginxproxy/nginx-proxy + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + DEBUG_ENDPOINT: "true" + + test: + image: nginx + environment: + VIRTUAL_HOST: test.nginx-proxy.tld +``` + +(on the CLI, using [`jq`](https://jqlang.github.io/jq/) to format the output of `curl` is recommended) + +```console +curl -s -H "Host: test.nginx-proxy.tld" localhost/nginx-proxy-debug | jq +``` + +```json +{ + "global": { + "acme_http_challenge": "true", + "default_cert_ok": false, + "default_host": null, + "default_root_response": "404", + "enable_access_log": true, + "enable_debug_endpoint": "true", + "enable_http2": "true", + "enable_http3": "false", + "enable_proxy_protocol": "false", + "enable_http_on_missing_cert": "true", + "enable_ipv6": false, + "enable_json_logs": false, + "external_http_port": "80", + "external_https_port": "443", + "hsts": "max-age=31536000", + "https_method": "redirect", + "log_format": null, + "log_format_escape": null, + "nginx_proxy_version": "1.8.0", + "resolvers": "127.0.0.11", + "sha1_upstream_name": false, + "ssl_policy": "Mozilla-Intermediate", + "trust_downstream_proxy": true + }, + "request": { + "host": "test.nginx-proxy.tld", + "http2": "", + "http3": "", + "https": "", + "ssl_cipher": "", + "ssl_protocol": "" + }, + "vhost": { + "acme_http_challenge_enabled": true, + "acme_http_challenge_legacy": false, + "cert": "", + "cert_ok": false, + "default": false, + "enable_debug_endpoint": true, + "hostname": "test.nginx-proxy.tld", + "hsts": "max-age=31536000", + "http2_enabled": true, + "http3_enabled": false, + "https_method": "noredirect", + "is_regexp": false, + "paths": { + "/": { + "dest": "", + "keepalive": "disabled", + "network_tag": "external", + "ports": { + "legacy": [ + { + "Name": "wip-test-1" + } + ] + }, + "proto": "http", + "upstream": "test.nginx-proxy.tld" + } + }, + "server_tokens": "", + "ssl_policy": "", + "upstream_name": "test.nginx-proxy.tld", + "vhost_root": "/var/www/public" + } +} +``` + +> [!WARNING] +> Please be aware that the debug endpoint work by rendering the JSON response straight to the nginx configuration in plaintext. nginx has an upper limit on the size of the configuration files it can parse, so only activate it when needed, and preferably on a per container basis if your setup has a large number of virtual hosts. + ⬆️ [back to table of contents](#table-of-contents) ## Contributing diff --git a/nginx.tmpl b/nginx.tmpl index a7f4a0c18..b3cf568eb 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1,47 +1,74 @@ # 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 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.) + * templates (Go templates cannot access variables outside of their own + * scope) and displayed in the debug endpoint output. */}} {{- $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 "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")) }} -{{- $_ := 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") }} + +{{- $config := dict }} +{{- $_ := set $config "nginx_proxy_version" $.Env.NGINX_PROXY_VERSION }} +{{- $_ := set $config "nginx_container_label" ($.Env.NGINX_CONTAINER_LABEL | default "com.github.nginx-proxy.nginx-proxy.nginx") }} +{{- $_ := set $config "default_cert_ok" (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +{{- $_ := set $config "external_http_port" ($globals.Env.HTTP_PORT | default "80") }} +{{- $_ := set $config "external_https_port" ($globals.Env.HTTPS_PORT | default "443") }} +{{- $_ := set $config "sha1_upstream_name" ($globals.Env.SHA1_UPSTREAM_NAME | default "false" | parseBool) }} +{{- $_ := set $config "default_root_response" ($globals.Env.DEFAULT_ROOT | default "404") }} +{{- $_ := set $config "trust_default_cert" ($globals.Env.TRUST_DEFAULT_CERT | default "true") }} +{{- $_ := set $config "trust_downstream_proxy" ($globals.Env.TRUST_DOWNSTREAM_PROXY | default "true" | parseBool) }} +{{- $_ := set $config "enable_access_log" ($globals.Env.DISABLE_ACCESS_LOGS | default "false" | parseBool | not) }} +{{- $_ := set $config "enable_ipv6" ($globals.Env.ENABLE_IPV6 | default "false" | parseBool) }} +{{- $_ := set $config "prefer_ipv6_network" ($globals.Env.PREFER_IPV6_NETWORK | default "false" | parseBool) }} +{{- $_ := set $config "ssl_policy" ($globals.Env.SSL_POLICY | default "Mozilla-Intermediate") }} +{{- $_ := set $config "enable_debug_endpoint" ($globals.Env.DEBUG_ENDPOINT | default "false") }} +{{- $_ := set $config "hsts" ($globals.Env.HSTS | default "max-age=31536000") }} +{{- $_ := set $config "acme_http_challenge" ($globals.Env.ACME_HTTP_CHALLENGE_LOCATION | default "true") }} +{{- $_ := set $config "acme_http_challenge_accept_unknown_host" ($globals.Env.ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST | default "false" | parseBool) }} +{{- $_ := set $config "enable_http2" ($globals.Env.ENABLE_HTTP2 | default "true") }} +{{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }} +{{- $_ := set $config "enable_proxy_protocol" ($globals.Env.ENABLE_PROXY_PROTOCOL | default "false" | parseBool) }} +{{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }} +{{- $_ := set $config "https_method" ($globals.Env.HTTPS_METHOD | default "redirect") }} +{{- $_ := set $config "non_get_redirect" ($globals.Env.NON_GET_REDIRECT | default "301") }} +{{- $_ := set $config "default_host" $globals.Env.DEFAULT_HOST }} +{{- $_ := set $config "resolvers" $globals.Env.RESOLVERS }} +{{- /* LOG_JSON is a shorthand that sets logging defaults to JSON format */}} +{{- $_ := set $config "enable_json_logs" ($globals.Env.LOG_JSON | default "false" | parseBool) }} +{{- $_ := set $config "log_format" $globals.Env.LOG_FORMAT }} +{{- $_ := set $config "log_format_escape" $globals.Env.LOG_FORMAT_ESCAPE }} + +{{- $_ := set $globals "config" $config }} + {{- $_ := 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): + +{{- $currentContainer := where $globals.containers "ID" $globals.Docker.CurrentContainerID | first }} +{{- $labeledContainer := whereLabelExists $globals.containers $globals.config.nginx_container_label | first }} +{{- $_ := set $globals "NetworkContainer" ($labeledContainer | default $currentContainer) }} +# Networks available to the container labeled "{{ $globals.config.nginx_container_label }}" or the one running docker-gen +# (which are assumed to match the networks available to the container running nginx): {{- /* - * Note: $globals.CurrentContainer may be nil in some circumstances due to - * . For more context - * see . + * Note: + * $globals.NetworkContainer may be nil in some circumstances due to https://github.com/nginx-proxy/docker-gen/issues/458. + * For more context see https://github.com/nginx-proxy/nginx-proxy/issues/2189. */}} -{{- if $globals.CurrentContainer }} - {{- range sortObjectsByKeysAsc $globals.CurrentContainer.Networks "Name" }} +{{- if $globals.NetworkContainer }} + {{- range sortObjectsByKeysAsc $globals.NetworkContainer.Networks "Name" }} {{- $_ := set $globals.networks .Name . }} # {{ .Name }} {{- else }} # (none) {{- end }} {{- else }} -# /!\ WARNING: Failed to find the Docker container running docker-gen. All -# upstream (backend) application containers will appear to be -# unreachable. Try removing the -only-exposed and -only-published -# arguments to docker-gen if you pass either of those. See -# . +# /!\ WARNING: Failed to find the Docker container labeled "{{ $globals.config.nginx_container_label }}" or the one running docker-gen. +# All upstream (backend) application containers will appear to be unreachable. +# Try removing the -only-exposed and -only-published arguments to docker-gen if you pass either of those. +# See https://github.com/nginx-proxy/docker-gen/issues/458. {{- end }} {{- /* @@ -56,7 +83,8 @@ * The return value will be added to the dot dict with key "ip". */}} {{- define "container_ip" }} - {{- $ip := "" }} + {{- $ipv4 := "" }} + {{- $ipv6 := "" }} # networks: {{- range sortObjectsByKeysAsc $.container.Networks "Name" }} {{- /* @@ -71,17 +99,17 @@ {{- /* Handle containers in host nework mode */}} {{- if (index $.globals.networks "host") }} # both container and proxy are in host network mode, using localhost IP - {{- $ip = "127.0.0.1" }} + {{- $ipv4 = "127.0.0.1" }} {{- continue }} {{- end }} - {{- range sortObjectsByKeysAsc $.globals.CurrentContainer.Networks "Name" }} + {{- range sortObjectsByKeysAsc $.globals.NetworkContainer.Networks "Name" }} {{- if and . .Gateway (not .Internal) }} # container is in host network mode, using {{ .Name }} gateway IP - {{- $ip = .Gateway }} + {{- $ipv4 = .Gateway }} {{- break }} {{- end }} {{- end }} - {{- if $ip }} + {{- if $ipv4 }} {{- continue }} {{- end }} {{- end }} @@ -91,26 +119,41 @@ {{- 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 + * is reachable over multiple networks or multiple IP stacks. This avoids + * accidentally inflating the effective round-robin weight of a server due + * to the redundant upstream addresses that nginx sees as belonging to * distinct servers. */}} - {{- if $ip }} + {{- if or $ipv4 $ipv6 }} # {{ .Name }} (ignored; reachable but redundant) {{- continue }} {{- end }} # {{ .Name }} (reachable) {{- if and . .IP }} - {{- $ip = .IP }} - {{- else }} - # /!\ No IP for this network! + {{- $ipv4 = .IP }} + {{- end }} + {{- if and . .GlobalIPv6Address }} + {{- $ipv6 = .GlobalIPv6Address }} + {{- end }} + {{- if and (empty $ipv4) (empty $ipv6) }} + # /!\ No IPv4 or IPv6 for this network! {{- end }} {{- else }} # (none) {{- end }} - # IP address: {{ if $ip }}{{ $ip }}{{ else }}(none usable){{ end }} - {{- $_ := set $ "ip" $ip }} + {{ if and $ipv6 $.globals.config.prefer_ipv6_network }} + # IPv4 address: {{ if $ipv4 }}{{ $ipv4 }} (ignored; reachable but IPv6 prefered){{ else }}(none usable){{ end }} + # IPv6 address: {{ $ipv6 }} + {{- $_ := set $ "ip" (printf "[%s]" $ipv6) }} + {{- else }} + # IPv4 address: {{ if $ipv4 }}{{ $ipv4 }}{{ else }}(none usable){{ end }} + # IPv6 address: {{ if $ipv6 }}{{ $ipv6 }}{{ if $ipv4 }} (ignored; reachable but IPv4 prefered){{ end }}{{ else }}(none usable){{ end }} + {{- if $ipv4 }} + {{- $_ := set $ "ip" $ipv4 }} + {{- else if $ipv6}} + {{- $_ := set $ "ip" (printf "[%s]" $ipv6) }} + {{- end }} + {{- end }} {{- end }} {{- /* @@ -125,10 +168,10 @@ */}} {{- define "container_port" }} {{- /* If only 1 port exposed, use that as a default, else 80. */}} - # exposed ports:{{ range sortObjectsByKeysAsc $.container.Addresses "Port" }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} + # exposed ports (first ten):{{ range $index, $address := (sortObjectsByKeysAsc $.container.Addresses "Port") }}{{ if lt $index 10 }} {{ $address.Port }}/{{ $address.Proto }}{{ end }}{{ else }} (none){{ end }} {{- $default_port := when (eq (len $.container.Addresses) 1) (first $.container.Addresses).Port "80" }} # default port: {{ $default_port }} - {{- $port := when (eq $.port "default") $default_port (when (eq $.port "legacy") (or $.container.Env.VIRTUAL_PORT $default_port) $.port) }} + {{- $port := eq $.port "default" | ternary $default_port $.port }} # using port: {{ $port }} {{- $addr_obj := where $.container.Addresses "Port" $port | first }} {{- if and $addr_obj $addr_obj.HostPort }} @@ -155,7 +198,7 @@ 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-TLS13-1-3-2021-06" }} ssl_protocols TLSv1.3; @@ -184,11 +227,11 @@ ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-TLS13-1-1-2021-06" }} 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-TLS13-1-0-2021-06" }} 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-FS-1-2-Res-2020-10" }} ssl_protocols TLSv1.2; @@ -204,11 +247,11 @@ ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-FS-1-1-2019-08" }} ssl_protocols TLSv1.1 TLSv1.2; - 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'; + 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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-FS-2018-06" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 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'; + 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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-TLS-1-2-Ext-2018-06" }} ssl_protocols TLSv1.2; @@ -220,23 +263,23 @@ ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }} ssl_protocols TLSv1.1 TLSv1.2; - 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-2016-08" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-2015-05" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-2015-03" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- else if eq .ssl_policy "AWS-2015-02" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - 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_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:@SECLEVEL=0'; ssl_prefer_server_ciphers on; {{- end }} {{- end }} @@ -248,7 +291,7 @@ {{- $override = printf "/etc/nginx/vhost.d/%s_location_override" .Host }} {{- end }} {{- if exists $override }} - include {{ $override }}; + include {{ printf "%s" (replace $override "*" "\\*" -1) }}; {{- else }} {{- $keepalive := $vpath.keepalive }} location {{ .Path }} { @@ -264,16 +307,24 @@ include uwsgi_params; uwsgi_pass {{ trim $proto }}://{{ trim $upstream }}; {{- else if eq $proto "fastcgi" }} - root {{ trim .VhostRoot }}; + {{- if (exists "/etc/nginx/fastcgi.conf") }} include fastcgi.conf; + {{- else if (exists "/etc/nginx/fastcgi_params") }} + include fastcgi_params; + {{- else }} + # neither /etc/nginx/fastcgi.conf nor /etc/nginx/fastcgi_params found, fastcgi won't work + {{- end }} + root {{ trim .VhostRoot }}; fastcgi_pass {{ trim $upstream }}; {{- if ne $keepalive "disabled" }} fastcgi_keep_conn on; {{- end }} {{- else if eq $proto "grpc" }} grpc_pass {{ trim $proto }}://{{ trim $upstream }}; + grpc_set_header X-Real-IP $remote_addr; {{- else if eq $proto "grpcs" }} grpc_pass {{ trim $proto }}://{{ trim $upstream }}; + grpc_set_header X-Real-IP $remote_addr; {{- else }} proxy_pass {{ trim $proto }}://{{ trim $upstream }}{{ trim $dest }}; set $upstream_keepalive {{ if ne $keepalive "disabled" }}true{{ else }}false{{ end }}; @@ -283,14 +334,14 @@ auth_basic "Restricted {{ .Host }}{{ .Path }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s_%s" .Host (sha1 .Path)) }}; {{- else if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} - auth_basic "Restricted {{ .Host }}"; + auth_basic "Restricted {{ .HostIsRegexp | ternary "access" .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) }}; + include {{ printf "/etc/nginx/vhost.d/%s_%s_location" (replace .Host "*" "\\*" -1) (sha1 .Path) }}; {{- else if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} - include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; + include {{ printf "/etc/nginx/vhost.d/%s_location" (replace .Host "*" "\\*" -1) }}; {{- else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; {{- end }} @@ -338,22 +389,101 @@ upstream {{ $vpath.upstream }} { } {{- end }} +{{- /* debug "endpoint" location template */}} +{{- define "debug_location" }} + {{- $debug_paths := dict }} + {{- range $path, $vpath := .VHost.paths }} + {{- $tmp_ports := dict }} + {{- range $port, $containers := $vpath.ports }} + {{- $tmp_containers := list }} + {{- range $container := $containers }} + {{- $tmp_containers = dict "Name" $container.Name | append $tmp_containers }} + {{- end }} + {{- $_ := set $tmp_ports $port $tmp_containers }} + {{- end }} + {{- $debug_vpath := deepCopy $vpath | merge (dict "ports" $tmp_ports) }} + {{- $_ := set $debug_paths $path $debug_vpath }} + {{- end }} + + {{- $debug_vhost := deepCopy .VHost }} + {{- /* If it's a regexp, do not render the Hostname to the response to avoid rendering config breaking characters */}} + {{- $_ := set $debug_vhost "hostname" (.VHost.is_regexp | ternary "Hostname is a regexp and unsafe to include in the debug response." .Hostname) }} + {{- $_ := set $debug_vhost "paths" $debug_paths }} + + {{- $debug_response := dict + "global" .GlobalConfig + "request" (dict + "host" "$host" + "https" "$https" + "http2" "$http2" + "http3" "$http3" + "ssl_cipher" "$ssl_cipher" + "ssl_protocol" "$ssl_protocol" + ) + "vhost" $debug_vhost + }} + + {{- /* + * The maximum line length in an nginx config is 4096 characters. + * If we're nearing this limit (with headroom for the rest + * of the directive), strip vhost.paths from the response. + */}} + {{- if gt (toJson $debug_response | len) 4000 }} + {{- $_ := unset $debug_vhost "paths" }} + {{- $_ := set $debug_response "warning" "Virtual paths configuration for this hostname is too large and has been stripped from response." }} + {{- end }} + + location /nginx-proxy-debug { + default_type application/json; + return 200 '{{ toJson $debug_response }}{{ "\\n" }}'; + } +{{- end }} + +{{- define "access_log" }} + {{- when .Enable "access_log /var/log/nginx/access.log vhost;" "" }} +{{- end }} + +map $proxy_add_x_forwarded_for $proxy_x_forwarded_for { + {{- if $globals.config.trust_downstream_proxy }} + {{- if $globals.config.enable_proxy_protocol }} + default $proxy_protocol_addr; + {{- else }} + default $proxy_add_x_forwarded_for; + {{- end }} + {{- else }} + default $remote_addr; + {{- end }} + '' $remote_addr; +} + # 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 $globals.trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; + default {{ if $globals.config.trust_downstream_proxy }}$http_x_forwarded_proto{{ else }}$scheme{{ end }}; '' $scheme; } map $http_x_forwarded_host $proxy_x_forwarded_host { - default {{ if $globals.trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$host{{ end }}; + default {{ if $globals.config.trust_downstream_proxy }}$http_x_forwarded_host{{ else }}$host{{ end }}; '' $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 $globals.trust_downstream_proxy }}$http_x_forwarded_port{{ else }}$server_port{{ end }}; +map $http_x_forwarded_port $_proxy_x_forwarded_port { + {{ if $globals.config.trust_downstream_proxy }} + {{ if $globals.config.enable_proxy_protocol }} + default $proxy_protocol_server_port; + {{- else }} + default $http_x_forwarded_port; + {{- end }} + {{- else }} + default $server_port; + {{- end }} +} + +map $_proxy_x_forwarded_port $proxy_x_forwarded_port { + default $_proxy_x_forwarded_port; '' $server_port; } @@ -414,27 +544,38 @@ gzip_types text/plain text/css application/javascript application/json applicati * LOG_FORMAT_ESCAPE sets the escape part of the log format * LOG_FORMAT sets the log format */}} -{{- $logEscape := printf "escape=%s" (or $globals.Env.LOG_FORMAT_ESCAPE "default") }} -{{- $logFormat := or $globals.Env.LOG_FORMAT `$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$upstream_addr"` }} +{{- $logEscape := $globals.config.log_format_escape | default "default" | printf "escape=%s" }} +{{- $logFormat := $globals.config.log_format | default `$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$upstream_addr"` }} -{{- if parseBool (or $globals.Env.LOG_JSON "false") }} - {{- /* LOG_JSON is a shorthand - * that sets logging defaults to JSON format - */}} +{{- if $globals.config.enable_json_logs }} # JSON Logging enabled (via LOG_JSON env variable) - {{- $logEscape = printf "escape=%s" (or $globals.Env.LOG_FORMAT_ESCAPE "json") }} - {{- $logFormat = or $globals.Env.LOG_FORMAT `{"time_local":"$time_iso8601","client_ip":"$http_x_forwarded_for","remote_addr":"$remote_addr","request":"$request","status":"$status","body_bytes_sent":"$body_bytes_sent","request_time":"$request_time","upstream_response_time":"$upstream_response_time","upstream_addr":"$upstream_addr","http_referrer":"$http_referer","http_user_agent":"$http_user_agent","request_id":"$request_id"}` }} + {{- $logEscape = $globals.config.log_format_escape | default "json" | printf "escape=%s" }} + {{- $logFormat = $globals.config.log_format | default `{"time_local":"$time_iso8601","client_ip":"$http_x_forwarded_for","remote_addr":"$remote_addr","request":"$request","status":"$status","body_bytes_sent":"$body_bytes_sent","request_time":"$request_time","upstream_response_time":"$upstream_response_time","upstream_addr":"$upstream_addr","http_referrer":"$http_referer","http_user_agent":"$http_user_agent","request_id":"$request_id"}` }} {{- end }} -log_format vhost {{ $logEscape }} '{{ or $globals.Env.LOG_FORMAT $logFormat }}'; +log_format vhost {{ $logEscape }} '{{ $logFormat }}'; access_log off; -{{- template "ssl_policy" (dict "ssl_policy" $globals.ssl_policy) }} +{{- /* Lower the SSL policy of the http context + * if at least one vhost use a TLSv1 or TLSv1.1 policy + * so TLSv1 and TLSv1.1 can be enabled on those vhosts + */}} +{{- $httpContextSslPolicy := $globals.config.ssl_policy }} +{{- $inUseSslPolicies := groupByKeys $globals.containers "Env.SSL_POLICY" }} +{{- range $tls1Policy := list "AWS-TLS13-1-1-2021-06" "AWS-TLS13-1-0-2021-06" "AWS-FS-1-1-2019-08" "AWS-FS-2018-06" "AWS-TLS-1-1-2017-01" "AWS-2016-08" "AWS-2015-05" "AWS-2015-03" "AWS-2015-02" "Mozilla-Old" }} + {{- if has $tls1Policy $inUseSslPolicies }} +# Using Mozilla-Old SSL policy on the http context to allow TLSv1 and TLSv1.1 + {{- $httpContextSslPolicy = "Mozilla-Old" }} + {{- break }} + {{- end }} +{{- end }} + +{{- template "ssl_policy" (dict "ssl_policy" $httpContextSslPolicy) }} error_log /dev/stderr; -{{- if $globals.Env.RESOLVERS }} -resolver {{ $globals.Env.RESOLVERS }}; +{{- if $globals.config.resolvers }} +resolver {{ $globals.config.resolvers }}; {{- end }} {{- if (exists "/etc/nginx/proxy.conf") }} @@ -446,7 +587,7 @@ proxy_set_header Host $host$host_port; 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-For $proxy_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; @@ -472,33 +613,43 @@ proxy_set_header Proxy ""; {{- end }} {{- range $hostname, $vhost := $parsedVhosts }} - {{- $vhost_data := when (hasKey $globals.vhosts $hostname) (get $globals.vhosts $hostname) (dict) }} - {{- $paths := coalesce $vhost_data.paths (dict) }} - + {{- $vhost_data := get $globals.vhosts $hostname | default (dict) }} + {{- $paths := $vhost_data.paths | default (dict) }} + {{- if (empty $vhost) }} {{ $vhost = dict "/" (dict) }} {{- end }} {{- range $path, $vpath := $vhost }} {{- if (empty $vpath) }} - {{- $vpath = dict "dest" "" "port" "default" }} + {{- $vpath = dict + "dest" "" + "port" "default" + "proto" "http" + }} {{- end }} - {{- $dest := coalesce $vpath.dest "" }} - {{- $port := when (hasKey $vpath "port") (toString $vpath.port) "default" }} - {{- $path_data := when (hasKey $paths $path) (get $paths $path) (dict) }} - {{- $path_ports := when (hasKey $path_data "ports") (get $path_data "ports") (dict) }} - {{- $path_port_containers := when (hasKey $path_ports $port) (get $path_ports $port) (list) }} - {{- $path_port_containers = concat $path_port_containers $containers }} + + {{- $dest := $vpath.dest | default "" }} + {{- $port := $vpath.port | default "default" | toString }} + {{- $proto := $vpath.proto | default "http" }} + + {{- $path_data := get $paths $path | default (dict) }} + {{- $path_ports := $path_data.ports | default (dict) }} + {{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }} {{- $_ := set $path_ports $port $path_port_containers }} {{- $_ := set $path_data "ports" $path_ports }} + {{- if (not (hasKey $path_data "dest")) }} {{- $_ := set $path_data "dest" $dest }} {{- end }} + + {{- if (not (hasKey $path_data "proto")) }} + {{- $_ := set $path_data "proto" $proto }} + {{- end }} + {{- $_ := set $paths $path $path_data }} {{- end }} {{- $_ := set $vhost_data "paths" $paths }} - {{- $is_regexp := hasPrefix "~" $hostname }} - {{- $_ := set $vhost_data "upstream_name" (when (or $is_regexp $globals.sha1_upstream_name) (sha1 $hostname) $hostname) }} {{- $_ := set $globals.vhosts $hostname $vhost_data }} {{- end }} {{- end }} @@ -523,55 +674,62 @@ proxy_set_header Proxy ""; {{- continue }} {{- end }} - {{- $vhost_data := when (hasKey $globals.vhosts $hostname) (get $globals.vhosts $hostname) (dict) }} - {{- $paths := coalesce $vhost_data.paths (dict) }} + {{- $vhost_data := get $globals.vhosts $hostname | default (dict) }} + {{- $paths := $vhost_data.paths | default (dict) }} {{- $tmp_paths := groupByWithDefault $containers "Env.VIRTUAL_PATH" "/" }} {{- range $path, $containers := $tmp_paths }} - {{- $dest := or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "" }} - {{- $port := "legacy" }} - {{- $path_data := when (hasKey $paths $path) (get $paths $path) (dict) }} - {{- $path_ports := when (hasKey $path_data "ports") (get $path_data "ports") (dict) }} - {{- $path_port_containers := when (hasKey $path_ports $port) (get $path_ports $port) (list) }} - {{- $path_port_containers = concat $path_port_containers $containers }} - {{- $_ := set $path_ports $port $path_port_containers }} + {{- $dest := groupByKeys $containers "Env.VIRTUAL_DEST" | first | default "" }} + {{- $proto := groupByKeys $containers "Env.VIRTUAL_PROTO" | first | default "http" | trim }} + + {{- $path_data := get $paths $path | default (dict) }} + {{- $path_ports := $path_data.ports | default (dict) }} + {{- range $port, $containers := groupByWithDefault $containers "Env.VIRTUAL_PORT" "default" }} + {{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }} + {{- $_ := set $path_ports $port $path_port_containers }} + {{- end }} {{- $_ := set $path_data "ports" $path_ports }} + {{- if (not (hasKey $path_data "dest")) }} {{- $_ := set $path_data "dest" $dest }} {{- end }} + + {{- if (not (hasKey $path_data "proto")) }} + {{- $_ := set $path_data "proto" $proto }} + {{- end }} + {{- $_ := set $paths $path $path_data }} {{- end }} {{- $_ := set $vhost_data "paths" $paths }} - {{- $is_regexp := hasPrefix "~" $hostname }} - {{- $_ := set $vhost_data "upstream_name" (when (or $is_regexp $globals.sha1_upstream_name) (sha1 $hostname) $hostname) }} {{- $_ := set $globals.vhosts $hostname $vhost_data }} {{- end }} {{- /* Loop over $globals.vhosts and update it with the remaining informations about each vhost. */}} {{- range $hostname, $vhost_data := $globals.vhosts }} + {{- $is_regexp := hasPrefix "~" $hostname }} + {{- $upstream_name := or $is_regexp $globals.config.sha1_upstream_name | ternary (sha1 $hostname) $hostname }} + {{- $vhost_containers := list }} + {{- range $path, $vpath_data := $vhost_data.paths }} {{- $vpath_containers := list }} {{- range $port, $vport_containers := $vpath_data.ports }} {{ $vpath_containers = concat $vpath_containers $vport_containers }} {{- end }} - {{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http". */}} - {{- $proto := trim (or (first (groupByKeys $vpath_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 $vpath_containers "Env.NETWORK_ACCESS")) "external" }} + {{- $network_tag := groupByKeys $vpath_containers "Env.NETWORK_ACCESS" | first | default "external" }} - {{- $loadbalance := first (keys (groupByLabel $vpath_containers "com.github.nginx-proxy.nginx-proxy.loadbalance")) }} - {{- $keepalive := coalesce (first (keys (groupByLabel $vpath_containers "com.github.nginx-proxy.nginx-proxy.keepalive"))) "disabled" }} + {{- $loadbalance := groupByLabel $vpath_containers "com.github.nginx-proxy.nginx-proxy.loadbalance" | keys | first }} + {{- $keepalive := groupByLabel $vpath_containers "com.github.nginx-proxy.nginx-proxy.keepalive" | keys | first | default "auto" }} - {{- $upstream := $vhost_data.upstream_name }} + {{- $upstream := $upstream_name }} {{- if (not (eq $path "/")) }} {{- $sum := sha1 $path }} {{- $upstream = printf "%s-%s" $upstream $sum }} {{- end }} - {{- $_ := set $vpath_data "proto" $proto }} {{- $_ := set $vpath_data "network_tag" $network_tag }} {{- $_ := set $vpath_data "upstream" $upstream }} {{- $_ := set $vpath_data "loadbalance" $loadbalance }} @@ -581,23 +739,42 @@ proxy_set_header Proxy ""; {{ $vhost_containers = concat $vhost_containers $vpath_containers }} {{- end }} - {{- $certName := first (groupByKeys $vhost_containers "Env.CERT_NAME") }} - {{- $vhostCert := closest (dir "/etc/nginx/certs") (printf "%s.crt" $hostname) }} - {{- $vhostCert = trimSuffix ".crt" $vhostCert }} - {{- $vhostCert = trimSuffix ".key" $vhostCert }} - {{- $cert := or $certName $vhostCert }} + {{- $userIdentifiedCert := groupByKeys $vhost_containers "Env.CERT_NAME" | first }} + + {{- $vhostCert := "" }} + {{- if exists (printf "/etc/nginx/certs/%s.crt" $hostname) }} + {{- $vhostCert = $hostname }} + {{- end }} + + {{- $parentVhostCert := "" }} + {{- if gt ($hostname | sprigSplit "." | len) 2 }} + {{- $parentHostname := ($hostname | sprigSplitn "." 2)._1 }} + {{- if exists (printf "/etc/nginx/certs/%s.crt" $parentHostname) }} + {{- $parentVhostCert = $parentHostname }} + {{- end }} + {{- end }} + + {{- $trust_default_cert := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.trust-default-cert" | keys | first | default $globals.config.trust_default_cert | parseBool }} + {{- $defaultCert := and $trust_default_cert $globals.config.default_cert_ok | ternary "default" "" }} + + {{- $cert := or $userIdentifiedCert $vhostCert $parentVhostCert $defaultCert }} {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} - {{- $default := eq $globals.Env.DEFAULT_HOST $hostname }} - {{- $https_method := or (first (groupByKeys $vhost_containers "Env.HTTPS_METHOD")) $globals.Env.HTTPS_METHOD "redirect" }} - {{- $enable_http_on_missing_cert := parseBool (or (first (groupByKeys $vhost_containers "Env.ENABLE_HTTP_ON_MISSING_CERT")) $globals.Env.ENABLE_HTTP_ON_MISSING_CERT "true") }} - {{- /* When the certificate is missing we want to ensure that HTTP is enabled; hence switching from 'nohttp' or 'redirect' to 'noredirect' */}} - {{- if (and $enable_http_on_missing_cert (not $cert_ok) (or (eq $https_method "nohttp") (eq $https_method "redirect"))) }} + {{- $enable_debug_endpoint := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.debug-endpoint" | keys | first | default $globals.config.enable_debug_endpoint | parseBool }} + {{- $default := eq $globals.config.default_host $hostname }} + {{- $https_method := groupByKeys $vhost_containers "Env.HTTPS_METHOD" | first | default $globals.config.https_method }} + {{- $enable_http_on_missing_cert := groupByKeys $vhost_containers "Env.ENABLE_HTTP_ON_MISSING_CERT" | first | default $globals.config.enable_http_on_missing_cert | parseBool }} + {{- /* When no trusted certs (default and/or vhost) are present we want to ensure that HTTP is enabled; hence switching from 'nohttp' or 'redirect' to 'noredirect' */}} + {{- $https_method_disable_http := list "nohttp" "redirect" | has $https_method }} + {{- if and $https_method_disable_http (not $cert_ok) $enable_http_on_missing_cert }} {{- $https_method = "noredirect" }} {{- end }} - {{- $http2_enabled := parseBool (or (first (keys (groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable"))) $globals.Env.ENABLE_HTTP2 "true")}} - {{- $http3_enabled := parseBool (or (first (keys (groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable"))) $globals.Env.ENABLE_HTTP3 "false")}} - {{- $acme_http_challenge := or (first (groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION")) $globals.Env.ACME_HTTP_CHALLENGE_LOCATION "true" }} + {{- $non_get_redirect := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.non-get-redirect" | keys | first | default $globals.config.non_get_redirect }} + + {{- $http2_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable" | keys | first | default $globals.config.enable_http2 | parseBool }} + {{- $http3_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable" | keys | first | default $globals.config.enable_http3 | parseBool }} + + {{- $acme_http_challenge := groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION" | first | default $globals.config.acme_http_challenge }} {{- $acme_http_challenge_legacy := eq $acme_http_challenge "legacy" }} {{- $acme_http_challenge_enabled := false }} {{- if (not $acme_http_challenge_legacy) }} @@ -605,29 +782,38 @@ proxy_set_header Proxy ""; {{- end }} {{- /* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "". */}} - {{- $server_tokens := trim (or (first (groupByKeys $vhost_containers "Env.SERVER_TOKENS")) "") }} + {{- $server_tokens := groupByKeys $vhost_containers "Env.SERVER_TOKENS" | first | default "" | trim }} {{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default). */}} - {{- $ssl_policy := or (first (groupByKeys $vhost_containers "Env.SSL_POLICY")) "" }} + {{- $ssl_policy := groupByKeys $vhost_containers "Env.SSL_POLICY" | first | default "" }} + + {{- /* Get ssl_verify_client defined by containers w/ the same vhost, falling back to "on" */}} + {{- $ssl_verify_client := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.ssl_verify_client" | keys | first | default "on" }} {{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000". */}} - {{- $hsts := or (first (groupByKeys $vhost_containers "Env.HSTS")) (or $globals.Env.HSTS "max-age=31536000") }} + {{- $hsts := groupByKeys $vhost_containers "Env.HSTS" | first | default $globals.config.hsts }} {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} - {{- $vhost_root := or (first (groupByKeys $vhost_containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} + {{- $vhost_root := groupByKeys $vhost_containers "Env.VIRTUAL_ROOT" | first | default "/var/www/public" }} {{- $vhost_data = merge $vhost_data (dict "cert" $cert "cert_ok" $cert_ok + "enable_debug_endpoint" $enable_debug_endpoint "default" $default "hsts" $hsts "https_method" $https_method + "non_get_redirect" $non_get_redirect "http2_enabled" $http2_enabled "http3_enabled" $http3_enabled + "is_regexp" $is_regexp "acme_http_challenge_legacy" $acme_http_challenge_legacy "acme_http_challenge_enabled" $acme_http_challenge_enabled "server_tokens" $server_tokens "ssl_policy" $ssl_policy + "ssl_verify_client" $ssl_verify_client + "trust_default_cert" $trust_default_cert + "upstream_name" $upstream_name "vhost_root" $vhost_root ) }} {{- $_ := set $globals.vhosts $hostname $vhost_data }} @@ -663,8 +849,9 @@ proxy_set_header Proxy ""; {{- $default_https_exists = or $default_https_exists (and $https $vhost.default) }} {{- $http3_enabled = or $http3_enabled $vhost.http3_enabled }} {{- end }} + {{- $proxy_protocol := when $globals.config.enable_proxy_protocol " proxy_protocol" "" }} {{- $fallback_http := not $default_http_exists }} - {{- $fallback_https := and $https_exists (not $default_https_exists) }} + {{- $fallback_https := 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 @@ -677,42 +864,45 @@ proxy_set_header Proxy ""; server { server_name _; # This is just an invalid value which will never trigger on a real hostname. server_tokens off; - {{ $globals.access_log }} + {{ template "access_log" (dict "Enable" $globals.config.enable_access_log) }} http2 on; {{- if $fallback_http }} - listen {{ $globals.external_http_port }}; {{- /* Do not add `default_server` (see comment above). */}} - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_http_port }}; {{- /* Do not add `default_server` (see comment above). */}} + listen {{ $globals.config.external_http_port }} {{- $proxy_protocol }}; {{- /* Do not add `default_server` (see comment above). */}} + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_http_port }} {{- $proxy_protocol }}; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} {{- end }} {{- if $fallback_https }} - listen {{ $globals.external_https_port }} ssl; {{- /* Do not add `default_server` (see comment above). */}} - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} ssl; {{- /* Do not add `default_server` (see comment above). */}} + listen {{ $globals.config.external_https_port }} ssl {{- $proxy_protocol }}; {{- /* Do not add `default_server` (see comment above). */}} + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_https_port }} ssl {{- $proxy_protocol }}; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} {{- if $http3_enabled }} http3 on; - listen {{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} quic reuseport; {{- /* Do not add `default_server` (see comment above). */}} + listen {{ $globals.config.external_https_port }} quic reuseport {{- $proxy_protocol }}; {{- /* Do not add `default_server` (see comment above). */}} + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_https_port }} quic reuseport {{- $proxy_protocol }}; {{- /* Do not add `default_server` (see comment above). */}} {{- end }} {{- end }} ssl_session_cache shared:SSL:50m; ssl_session_tickets off; {{- end }} - {{- if $globals.default_cert_ok }} + {{- if $globals.config.default_cert_ok }} 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; + # No default certificate found, so reject SSL handshake; + ssl_reject_handshake on; + {{- end }} + + {{- if $globals.config.acme_http_challenge_accept_unknown_host }} + location ^~ /.well-known/acme-challenge/ { + auth_basic off; + auth_request off; + allow all; + root /usr/share/nginx/html; + try_files $uri =404; + break; } {{- end }} @@ -731,7 +921,8 @@ server { {{- end }} {{- range $hostname, $vhost := $globals.vhosts }} - {{- $default_server := when $vhost.default "default_server" "" }} + {{- $default_server := when $vhost.default " default_server" "" }} + {{- $proxy_protocol := when $globals.config.enable_proxy_protocol " proxy_protocol" "" }} {{- range $path, $vpath := $vhost.paths }} # {{ $hostname }}{{ $path }} @@ -744,10 +935,10 @@ server { {{- if $vhost.server_tokens }} server_tokens {{ $vhost.server_tokens }}; {{- end }} - {{ $globals.access_log }} - listen {{ $globals.external_http_port }} {{ $default_server }}; - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; + {{ template "access_log" (dict "Enable" $globals.config.enable_access_log) }} + listen {{ $globals.config.external_http_port }} {{- $default_server }} {{- $proxy_protocol }}; + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_http_port }} {{- $default_server }} {{- $proxy_protocol }}; {{- end }} {{- if (or $vhost.acme_http_challenge_legacy $vhost.acme_http_challenge_enabled) }} @@ -762,34 +953,59 @@ server { } {{- end }} - location / { - {{- if eq $globals.external_https_port "443" }} - return 301 https://$host$request_uri; - {{- else }} - return 301 https://$host:{{ $globals.external_https_port }}$request_uri; + {{- if $vhost.enable_debug_endpoint }} + {{ template "debug_location" (dict "GlobalConfig" $globals.config "Hostname" $hostname "VHost" $vhost) }} {{- end }} + + location / { + {{- $redirect_uri := "https://$host$request_uri" }} + {{- if ne $globals.config.external_https_port "443" }} + {{- $redirect_uri = printf "https://$host:%s$request_uri" $globals.config.external_https_port }} + {{- end}} + if ($request_method ~ (OPTIONS|POST|PUT|PATCH|DELETE)) { + return {{ $vhost.non_get_redirect }} {{ $redirect_uri }}; + } + return 301 {{ $redirect_uri }}; } } {{- end }} server { + {{- if $vhost.is_regexp }} + {{- if or + (printf "/etc/nginx/vhost.d/%s" $hostname | exists) + (printf "/etc/nginx/vhost.d/%s_location" $hostname | exists) + (printf "/etc/nginx/vhost.d/%s_location_override" $hostname | exists) + (printf "/etc/nginx/htpasswd/%s" $hostname | exists) + }} + # https://github.com/nginx-proxy/nginx-proxy/issues/2529#issuecomment-2437609249 + # Support for vhost config file(s) named like a regexp ({{ $hostname }}) has been removed from nginx-proxy. + # Please name your vhost config file(s) with the sha1 of the regexp instead ({{ $hostname }} -> {{ sha1 $hostname }}) : + # - /etc/nginx/vhost.d/{{ sha1 $hostname }} + # - /etc/nginx/vhost.d/{{ sha1 $hostname }}_location + # - /etc/nginx/vhost.d/{{ sha1 $hostname }}_location_override + # - /etc/nginx/htpasswd/{{ sha1 $hostname }} + {{- end }} + {{- end }} + server_name {{ $hostname }}; {{- if $vhost.server_tokens }} server_tokens {{ $vhost.server_tokens }}; {{- end }} - {{ $globals.access_log }} + {{ template "access_log" (dict "Enable" $globals.config.enable_access_log) }} {{- if $vhost.http2_enabled }} http2 on; {{- end }} {{- if or (eq $vhost.https_method "nohttps") (eq $vhost.https_method "noredirect") }} - listen {{ $globals.external_http_port }} {{ $default_server }}; - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_http_port }} {{ $default_server }}; + listen {{ $globals.config.external_http_port }} {{- $default_server }} {{- $proxy_protocol }}; + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_http_port }} {{- $default_server }} {{- $proxy_protocol }}; {{- end }} {{- if (and (eq $vhost.https_method "noredirect") $vhost.acme_http_challenge_enabled) }} location /.well-known/acme-challenge/ { auth_basic off; + auth_request off; allow all; root /usr/share/nginx/html; try_files $uri =404; @@ -798,17 +1014,17 @@ server { {{- end }} {{- end }} {{- if ne $vhost.https_method "nohttps" }} - listen {{ $globals.external_https_port }} ssl {{ $default_server }}; - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} ssl {{ $default_server }}; + listen {{ $globals.config.external_https_port }} ssl {{- $default_server }} {{- $proxy_protocol }}; + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_https_port }} ssl {{- $default_server }} {{- $proxy_protocol }}; {{- end }} {{- if $vhost.http3_enabled }} http3 on; - add_header alt-svc 'h3=":{{ $globals.external_https_port }}"; ma=86400;'; - listen {{ $globals.external_https_port }} quic {{ $default_server }}; - {{- if $globals.enable_ipv6 }} - listen [::]:{{ $globals.external_https_port }} quic {{ $default_server }}; + add_header alt-svc 'h3=":{{ $globals.config.external_https_port }}"; ma=86400;'; + listen {{ $globals.config.external_https_port }} quic {{- $default_server }} {{- $proxy_protocol }}; + {{- if $globals.config.enable_ipv6 }} + listen [::]:{{ $globals.config.external_https_port }} quic {{- $default_server }} {{- $proxy_protocol }}; {{- end }} {{- end }} @@ -839,54 +1055,59 @@ server { } 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 if not $vhost.trust_default_cert | and $globals.config.default_cert_ok }} + # No certificate found for this vhost, and the default certificate isn't trusted, so reject SSL handshake. + ssl_reject_handshake on; {{- 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; - } + # No certificate for this vhost nor default certificate found, so reject SSL handshake. + ssl_reject_handshake on; {{- end }} {{- end }} - {{- if (exists (printf "/etc/nginx/vhost.d/%s" $hostname)) }} - include {{ printf "/etc/nginx/vhost.d/%s" $hostname }}; + {{- $vhostFileName := $vhost.is_regexp | ternary (sha1 $hostname) $hostname }} + + {{- if (exists (printf "/etc/nginx/vhost.d/%s" $vhostFileName)) }} + include {{ printf "/etc/nginx/vhost.d/%s" (replace $vhostFileName "*" "\\*" -1) }}; {{- else if (exists "/etc/nginx/vhost.d/default") }} include /etc/nginx/vhost.d/default; {{- end }} + {{/* SSL Client Certificate Validation */}} + {{/* If vhost(hash).ca.crt exists, include CA */}} + {{- if (exists (printf "/etc/nginx/certs/%s.ca.crt" $vhostFileName)) }} + ssl_client_certificate {{ printf "/etc/nginx/certs/%s.ca.crt" $vhostFileName }}; + ssl_verify_client {{ $vhost.ssl_verify_client }}; + {{/* If vhost(hash).crl.pem exists, include CRL */}} + {{- if (exists (printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName)) }} + ssl_crl {{ printf "/etc/nginx/certs/%s.crl.pem" $vhostFileName }}; + {{ end }} + {{/* Else if no vhost CA file exists, but a global ca.crt exists include it */}} + {{ else if (exists "/etc/nginx/certs/ca.crt") }} + ssl_client_certificate /etc/nginx/certs/ca.crt; + ssl_verify_client {{ $vhost.ssl_verify_client }}; + {{/* If no vhost CA file exists, but a global ca.crl.pem exists include it */}} + {{ if (exists "/etc/nginx/certs/ca.crl.pem")}} + ssl_crl /etc/nginx/certs/ca.crl.pem; + {{ end }} + {{ end }} + + {{- if $vhost.enable_debug_endpoint }} + {{ template "debug_location" (dict "GlobalConfig" $globals.config "Hostname" $hostname "VHost" $vhost) }} + {{- end }} + {{- range $path, $vpath := $vhost.paths }} {{- template "location" (dict "Path" $path - "Host" $hostname + "Host" $vhostFileName + "HostIsRegexp" $vhost.is_regexp "VhostRoot" $vhost.vhost_root "VPath" $vpath ) }} {{- end }} - {{- if and (not (contains $vhost.paths "/")) (ne $globals.default_root_response "none")}} + {{- if and (not (contains $vhost.paths "/")) (ne $globals.config.default_root_response "none")}} location / { - return {{ $globals.default_root_response }}; + return {{ $globals.config.default_root_response }}; } {{- end }} } diff --git a/test/README.md b/test/README.md index 07a29d28b..29723f127 100644 --- a/test/README.md +++ b/test/README.md @@ -57,13 +57,39 @@ This test suite uses [pytest](http://doc.pytest.org/en/latest/). The [conftest.p ### docker_compose fixture -When using the `docker_compose` fixture in a test, pytest will try to find a yml file named after your test module filename. For instance, if your test module is `test_example.py`, then the `docker_compose` fixture will try to load a `test_example.yml` [docker compose file](https://docs.docker.com/compose/compose-file/). +When using the `docker_compose` fixture in a test, pytest will try to start the [Docker Compose](https://docs.docker.com/compose/) services corresponding to the current test module, based on the test module filename. -Once the docker compose file found, the fixture will remove all containers, run `docker compose up`, and finally your test will be executed. +By default, if your test module file is `test/test_subdir/test_example.py`, then the `docker_compose` fixture will try to load the following files, [merging them](https://docs.docker.com/reference/compose-file/merge/) in this order: -The fixture will run the _docker compose_ command with the `-f` option to load the given compose file. So you can test your docker compose file syntax by running it yourself with: +1. `test/compose.base.yml` +2. `test/test_subdir/compose.base.override.yml` (if it exists) +3. `test/test_subdir/test_example.yml` - docker compose -f test_example.yml up -d +The fixture will run the _docker compose_ command with the `-f` option to load the given compose files. So you can test your docker compose file syntax by running it yourself with: + + docker compose -f test/compose.base.yml -f test/test_subdir/test_example.yml up -d + +The first file contains the base configuration of the nginx-proxy container common to most tests: + +```yaml +services: + nginx-proxy: + image: nginxproxy/nginx-proxy:test + container_name: nginx-proxy + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + ports: + - "80:80" + - "443:443" +``` + +The second optional file allow you to override this base configuration for all test modules in a subfolder. + +The third file contains the services and overrides specific to a given test module. + +This automatic merge can be bypassed by using a file named `test_example.base.yml` (instead of `test_example.yml`). When this file exist, it will be the only one used by the test and no merge with other compose files will automatically occur. + +The `docker_compose` fixture also set the `PYTEST_MODULE_PATH` environment variable to the absolute path of the current test module directory, so it can be used to mount files or directory relatives to the current test. In the case you are running pytest from within a docker container, the `docker_compose` fixture will make sure the container running pytest is attached to all docker networks. That way, your test will be able to reach any of them. @@ -71,7 +97,10 @@ In your tests, you can use the `docker_compose` variable to query and command th Also this fixture alters the way the python interpreter resolves domain names to IP addresses in the following ways: -Any domain name containing the substring `nginx-proxy` will resolve to the IP address of the container that was created from the `nginxproxy/nginx-proxy:test` image. So all the following domain names will resolve to the nginx-proxy container in tests: +Any domain name containing the substring `nginx-proxy` will resolve to `127.0.0.1` if the tests are executed on a Darwin (macOS) system, otherwise the IP address of the container that was created from the `nginxproxy/nginx-proxy:test` image. + +So, in tests, all the following domain names will resolve to either localhost or the nginx-proxy container's IP: + - `nginx-proxy` - `nginx-proxy.com` - `www.nginx-proxy.com` @@ -80,14 +109,16 @@ Any domain name containing the substring `nginx-proxy` will resolve to the IP ad - `whatever.nginx-proxyooooooo` - ... -Any domain name ending with `XXX.container.docker` will resolve to the IP address of the XXX container. +Any domain name ending with `XXX.container.docker` will resolve to `127.0.0.1` if the tests are executed on a Darwin (macOS) system, otherwise the IP address of the container named `XXX`. + +So, on a non-Darwin system: + - `web1.container.docker` will resolve to the IP address of the `web1` container - `f00.web1.container.docker` will resolve to the IP address of the `web1` container - `anything.whatever.web2.container.docker` will resolve to the IP address of the `web2` container Otherwise, domain names are resoved as usual using your system DNS resolver. - ### nginxproxy fixture The `nginxproxy` fixture will provide you with a replacement for the python [requests](https://pypi.python.org/pypi/requests/) module. This replacement will just repeat up to 30 times a requests if it receives the HTTP error 404 or 502. This error occurs when you try to send queries to nginx-proxy too early after the container creation. diff --git a/test/certs/create_server_certificate.sh b/test/certs/create_server_certificate.sh index f9d6b9764..c2e28e112 100755 --- a/test/certs/create_server_certificate.sh +++ b/test/certs/create_server_certificate.sh @@ -24,7 +24,7 @@ fi # Create a nginx container (which conveniently provides the `openssl` command) ############################################################################### -CONTAINER=$(docker run -d -v $DIR:/work -w /work -e SAN="$ALTERNATE_DOMAINS" nginx:1.27.0) +CONTAINER=$(docker run -d -v $DIR:/work -w /work -e SAN="$ALTERNATE_DOMAINS" nginx:1.29.1) # Configure openssl docker exec $CONTAINER bash -c ' mkdir -p /ca/{certs,crl,private,newcerts} 2>/dev/null diff --git a/test/test_events.yml b/test/compose.base.yml similarity index 54% rename from test/test_events.yml rename to test/compose.base.yml index ac27e1d0c..36e804bc0 100644 --- a/test/test_events.yml +++ b/test/compose.base.yml @@ -1,7 +1,9 @@ -version: "2" - services: - nginxproxy: + nginx-proxy: image: nginxproxy/nginx-proxy:test + container_name: nginx-proxy volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + ports: + - "80:80" + - "443:443" diff --git a/test/conftest.py b/test/conftest.py index dda20f60b..c95e42ca6 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,28 +1,35 @@ import contextlib import logging import os +import pathlib +import platform import re import shlex import socket import subprocess import time -from typing import List +from io import StringIO +from typing import Iterator, List, Optional import backoff -import docker +import docker.errors import pytest import requests -from _pytest._code.code import ReprExceptionInfo -from distutils.version import LooseVersion +from _pytest.fixtures import FixtureRequest +from docker import DockerClient from docker.models.containers import Container -from requests.packages.urllib3.util.connection import HAS_IPV6 +from docker.models.networks import Network +from packaging.version import Version +from requests import Response +from urllib3.util.connection import HAS_IPV6 + logging.basicConfig(level=logging.INFO) logging.getLogger('backoff').setLevel(logging.INFO) logging.getLogger('DNS').setLevel(logging.DEBUG) logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN) -CA_ROOT_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt') +CA_ROOT_CERTIFICATE = pathlib.Path(__file__).parent.joinpath("certs/ca-root.crt") PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1" FORCE_CONTAINER_IPV6 = False # ugly global state to consider containers' IPv6 address instead of IPv4 @@ -40,8 +47,9 @@ # ############################################################################### + @contextlib.contextmanager -def ipv6(force_ipv6=True): +def ipv6(force_ipv6: bool = True): """ Meant to be used as a context manager to force IPv6 sockets: @@ -59,19 +67,19 @@ def ipv6(force_ipv6=True): FORCE_CONTAINER_IPV6 = False -class requests_for_docker(object): +class RequestsForDocker: """ Proxy for calling methods of the requests module. - When a HTTP response failed due to HTTP Error 404 or 502, retry a few times. + When an HTTP response failed due to HTTP Error 404 or 502, retry a few times. Provides method `get_conf` to extract the nginx-proxy configuration content. """ def __init__(self): self.session = requests.Session() - if os.path.isfile(CA_ROOT_CERTIFICATE): - self.session.verify = CA_ROOT_CERTIFICATE + if CA_ROOT_CERTIFICATE.is_file(): + self.session.verify = CA_ROOT_CERTIFICATE.as_posix() @staticmethod - def get_nginx_proxy_containers() -> List[Container]: + def get_nginx_proxy_container() -> Container: """ Return list of containers """ @@ -80,69 +88,69 @@ def get_nginx_proxy_containers() -> List[Container]: pytest.fail("Too many running nginxproxy/nginx-proxy:test containers", pytrace=False) elif len(nginx_proxy_containers) == 0: pytest.fail("No running nginxproxy/nginx-proxy:test container", pytrace=False) - return nginx_proxy_containers + return nginx_proxy_containers.pop() - def get_conf(self): + def get_conf(self) -> bytes: """ Return the nginx config file """ - nginx_proxy_containers = self.get_nginx_proxy_containers() - return get_nginx_conf_from_container(nginx_proxy_containers[0]) + nginx_proxy_container = self.get_nginx_proxy_container() + return get_nginx_conf_from_container(nginx_proxy_container) def get_ip(self) -> str: """ Return the nginx container ip address """ - nginx_proxy_containers = self.get_nginx_proxy_containers() - return container_ip(nginx_proxy_containers[0]) + nginx_proxy_container = self.get_nginx_proxy_container() + return container_ip(nginx_proxy_container) - def get(self, *args, **kwargs): + def get(self, *args, **kwargs) -> Response: with ipv6(kwargs.pop('ipv6', False)): @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None) - def _get(*args, **kwargs): - return self.session.get(*args, **kwargs) + def _get(*_args, **_kwargs): + return self.session.get(*_args, **_kwargs) return _get(*args, **kwargs) - def post(self, *args, **kwargs): + def post(self, *args, **kwargs) -> Response: with ipv6(kwargs.pop('ipv6', False)): @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None) - def _post(*args, **kwargs): - return self.session.post(*args, **kwargs) + def _post(*_args, **_kwargs): + return self.session.post(*_args, **_kwargs) return _post(*args, **kwargs) - def put(self, *args, **kwargs): + def put(self, *args, **kwargs) -> Response: with ipv6(kwargs.pop('ipv6', False)): @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None) - def _put(*args, **kwargs): - return self.session.put(*args, **kwargs) + def _put(*_args, **_kwargs): + return self.session.put(*_args, **_kwargs) return _put(*args, **kwargs) - def head(self, *args, **kwargs): + def head(self, *args, **kwargs) -> Response: with ipv6(kwargs.pop('ipv6', False)): @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None) - def _head(*args, **kwargs): - return self.session.head(*args, **kwargs) + def _head(*_args, **_kwargs): + return self.session.head(*_args, **_kwargs) return _head(*args, **kwargs) - def delete(self, *args, **kwargs): + def delete(self, *args, **kwargs) -> Response: with ipv6(kwargs.pop('ipv6', False)): @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None) - def _delete(*args, **kwargs): - return self.session.delete(*args, **kwargs) + def _delete(*_args, **_kwargs): + return self.session.delete(*_args, **_kwargs) return _delete(*args, **kwargs) - def options(self, *args, **kwargs): + def options(self, *args, **kwargs) -> Response: with ipv6(kwargs.pop('ipv6', False)): @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None) - def _options(*args, **kwargs): - return self.session.options(*args, **kwargs) + def _options(*_args, **_kwargs): + return self.session.options(*_args, **_kwargs) return _options(*args, **kwargs) def __getattr__(self, name): return getattr(requests, name) -def container_ip(container: Container): +def container_ip(container: Container) -> str: """ return the IP address of a container. @@ -161,7 +169,7 @@ def container_ip(container: Container): net_info = container.attrs["NetworkSettings"]["Networks"] if "bridge" in net_info: return net_info["bridge"]["IPAddress"] - + # container is running in host network mode if "host" in net_info: return "127.0.0.1" @@ -171,14 +179,14 @@ def container_ip(container: Container): return net_info[network_name]["IPAddress"] -def container_ipv6(container): +def container_ipv6(container: Container) -> str: """ return the IPv6 address of a container. """ net_info = container.attrs["NetworkSettings"]["Networks"] if "bridge" in net_info: return net_info["bridge"]["GlobalIPv6Address"] - + # container is running in host network mode if "host" in net_info: return "::1" @@ -188,33 +196,55 @@ def container_ipv6(container): return net_info[network_name]["GlobalIPv6Address"] -def nginx_proxy_dns_resolver(domain_name): +def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]: """ if "nginx-proxy" if found in host, return the ip address of the docker container - issued from the docker image nginxproxy/nginx-proxy:test. + issued from the docker image nginxproxy/nginx-proxy:test or nginx:latest. :return: IP or None """ log = logging.getLogger('DNS') log.debug(f"nginx_proxy_dns_resolver({domain_name!r})") + if 'nginx-proxy' in domain_name: nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"}) - if len(nginxproxy_containers) == 0: - log.warn(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}") + nginx_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginx:latest"}) + + if len(nginxproxy_containers) == 0 and len(nginx_containers) == 0: + log.warning(f"no running container found from image nginxproxy/nginx-proxy:test or nginx:latest while resolving {domain_name!r}") + exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"}) + exited_nginx_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginx:latest"}) + if len(exited_nginxproxy_containers) > 0: exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs() - log.warn(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode()) - return - nginxproxy_container = nginxproxy_containers[0] - ip = container_ip(nginxproxy_container) - log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}") + log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode()) + if len(exited_nginx_containers) > 0: + exited_nginx_container_logs = exited_nginx_containers[0].logs() + log.warning(f"nginx:latest container might have exited unexpectedly. Container logs: " + "\n" + exited_nginx_container_logs.decode()) + + return None + + container = None + container_type = "nginx-proxy" + + if len(nginxproxy_containers) >= 1: + container = nginxproxy_containers[0] + if len(nginx_containers) >= 1: + container = nginx_containers[0] + container_type = "nginx" + + ip = container_ip(container) + log.info(f"resolving domain name {domain_name!r} as IP address {ip} of {container_type} container {container.name}") return ip -def docker_container_dns_resolver(domain_name): + return None + + +def docker_container_dns_resolver(domain_name: str) -> Optional[str]: """ - if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", return the ip address of the docker container - named XXX. + if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", + return the ip address of the docker container named XXX. :return: IP or None """ @@ -224,15 +254,15 @@ def docker_container_dns_resolver(domain_name): match = re.search(r'(^|.+\.)(?P[^.]+)\.container\.docker$', domain_name) if not match: log.debug(f"{domain_name!r} does not match") - return + return None container_name = match.group('container') log.debug(f"looking for container {container_name!r}") try: container = docker_client.containers.get(container_name) except docker.errors.NotFound: - log.warn(f"container named {container_name!r} not found while resolving {domain_name!r}") - return + log.warning(f"container named {container_name!r} not found while resolving {domain_name!r}") + return None log.debug(f"container {container.name!r} found ({container.short_id})") ip = container_ip(container) @@ -244,7 +274,10 @@ def monkey_patch_urllib_dns_resolver(): """ Alter the behavior of the urllib DNS resolver so that any domain name containing substring 'nginx-proxy' will resolve to the IP address - of the container created from image 'nginxproxy/nginx-proxy:test'. + of the container created from image 'nginxproxy/nginx-proxy:test', + or to 127.0.0.1 on Darwin. + + see https://docs.docker.com/desktop/features/networking/#i-want-to-connect-to-a-container-from-the-host """ prv_getaddrinfo = socket.getaddrinfo dns_cache = {} @@ -252,13 +285,18 @@ def new_getaddrinfo(*args): logging.getLogger('DNS').debug(f"resolving domain name {repr(args)}") _args = list(args) - # Fail early when querying IP directly and it is forced ipv6 when not supported, + # Fail early when querying IP directly, and it is forced ipv6 when not supported, # Otherwise a pytest container not using the host network fails to pass `test_raw-ip-vhost`. if FORCE_CONTAINER_IPV6 and not HAS_IPV6: pytest.skip("This system does not support IPv6") # custom DNS resolvers - ip = nginx_proxy_dns_resolver(args[0]) + ip = None + # Docker Desktop can't route traffic directly to Linux containers. + if platform.system() == "Darwin": + ip = "127.0.0.1" + if ip is None: + ip = nginx_proxy_dns_resolver(args[0]) if ip is None: ip = docker_container_dns_resolver(args[0]) if ip is not None: @@ -274,19 +312,12 @@ def new_getaddrinfo(*args): socket.getaddrinfo = new_getaddrinfo return prv_getaddrinfo + def restore_urllib_dns_resolver(getaddrinfo_func): socket.getaddrinfo = getaddrinfo_func -def remove_all_containers(): - for container in docker_client.containers.list(all=True): - if PYTEST_RUNNING_IN_CONTAINER and container.name == test_container: - continue # pytest is running within a Docker container, so we do not want to remove that particular container - logging.info(f"removing container {container.name}") - container.remove(v=True, force=True) - - -def get_nginx_conf_from_container(container): +def get_nginx_conf_from_container(container: Container) -> bytes: """ return the nginx /etc/nginx/conf.d/default.conf file content from a container """ @@ -301,67 +332,105 @@ def get_nginx_conf_from_container(container): return conffile.read() -def docker_compose_up(compose_file='docker-compose.yml'): - logging.info(f'{DOCKER_COMPOSE} -f {compose_file} up -d') +def __prepare_and_execute_compose_cmd(compose_files: List[str], project_name: str, cmd: str): + """ + Prepare and execute the Docker Compose command with the provided compose files and project name. + """ + compose_cmd = StringIO() + compose_cmd.write(DOCKER_COMPOSE) + compose_cmd.write(f" --project-name {project_name}") + for compose_file in compose_files: + compose_cmd.write(f" --file {compose_file}") + compose_cmd.write(f" {cmd}") + try: - subprocess.check_output(shlex.split(f'{DOCKER_COMPOSE} -f {compose_file} up -d'), stderr=subprocess.STDOUT) + subprocess.check_output(shlex.split(compose_cmd.getvalue()), stderr=subprocess.STDOUT) + logging.info(f"Executed '{compose_cmd.getvalue()}'") except subprocess.CalledProcessError as e: - pytest.fail(f"Error while runninng '{DOCKER_COMPOSE} -f {compose_file} up -d':\n{e.output}", pytrace=False) + logging.error(f"Error while running '{compose_cmd.getvalue()}'") -def docker_compose_down(compose_file='docker-compose.yml'): - logging.info(f'{DOCKER_COMPOSE} -f {compose_file} down -v') - try: - subprocess.check_output(shlex.split(f'{DOCKER_COMPOSE} -f {compose_file} down -v'), stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - pytest.fail(f"Error while runninng '{DOCKER_COMPOSE} -f {compose_file} down -v':\n{e.output}", pytrace=False) +def docker_compose_up(compose_files: List[str], project_name: str): + """ + Execute compose up --detach with the provided compose files and project name. + """ + if compose_files is None or len(compose_files) == 0: + pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False) + __prepare_and_execute_compose_cmd(compose_files, project_name, cmd="up --detach") + + +def docker_compose_down(compose_files: List[str], project_name: str): + """ + Execute compose down --volumes with the provided compose files and project name. + """ + if compose_files is None or len(compose_files) == 0: + pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False) + __prepare_and_execute_compose_cmd(compose_files, project_name, cmd="down --volumes") def wait_for_nginxproxy_to_be_ready(): """ - If one (and only one) container started from image nginxproxy/nginx-proxy:test is found, - wait for its log to contain substring "Watching docker events" + Wait for logs of running containers started from image nginxproxy/nginx-proxy:test and/or + nginxproxy/docker-gen:latest to contain the substring "Watching docker events" """ - containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"}) - if len(containers) != 1: - return - container = containers[0] - for line in container.logs(stream=True): - if b"Watching docker events" in line: - logging.debug("nginx-proxy ready") - break + nginx_proxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"}) + docker_gen_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/docker-gen:latest"}) + + containers = nginx_proxy_containers + docker_gen_containers + + for container in containers: + logging.debug(f"waiting for container {container.name} to be ready") + for line in container.logs(stream=True): + if b"Watching docker events" in line: + logging.debug(f"{container.name} ready") + break @pytest.fixture -def docker_compose_file(request): - """Fixture naming the docker compose file to consider. +def docker_compose_files(request: FixtureRequest) -> List[str]: + """Fixture returning the docker compose files to consider: + + If a YAML file exists with the same name as the test module (with the `.py` extension + replaced with `.base.yml`, ie `test_foo.py`-> `test_foo.base.yml`) and in the same + directory as the test module, use only that file. - 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. + Otherwise, merge the following files in this order: + + - the `compose.base.yml` file in the parent `test` directory. + - if present in the same directory as the test module, the `compose.base.override.yml` file. + - the YAML file named after the current test module (ie `test_foo.py`-> `test_foo.yml`) 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') + compose_files: List[str] = [] + test_module_path = pathlib.Path(request.module.__file__).parent - 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 + module_base_file = test_module_path.joinpath(f"{request.module.__name__}.base.yml") + if module_base_file.is_file(): + return [module_base_file.as_posix()] + + global_base_file = test_module_path.parent.joinpath("compose.base.yml") + if global_base_file.is_file(): + compose_files.append(global_base_file.as_posix()) + + module_base_override_file = test_module_path.joinpath("compose.base.override.yml") + if module_base_override_file.is_file(): + compose_files.append(module_base_override_file.as_posix()) - 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__)) + module_compose_file = test_module_path.joinpath(f"{request.module.__name__}.yml") + if module_compose_file.is_file(): + compose_files.append(module_compose_file.as_posix()) - logging.debug(f"using docker compose file {docker_compose_file}") - return docker_compose_file + if not module_base_file.is_file() and not module_compose_file.is_file(): + logging.error( + f"Could not find any docker compose file named '{module_base_file.name}' or '{module_compose_file.name}'" + ) + logging.debug(f"using docker compose files {compose_files}") + return compose_files -def connect_to_network(network): + +def connect_to_network(network: Network) -> Optional[Network]: """ If we are running from a container, connect our container to the given network @@ -371,8 +440,8 @@ def connect_to_network(network): try: my_container = docker_client.containers.get(test_container) except docker.errors.NotFound: - logging.warn(f"container {test_container} not found") - return + logging.warning(f"container {test_container} not found") + return None # figure out our container networks my_networks = list(my_container.attrs["NetworkSettings"]["Networks"].keys()) @@ -384,12 +453,18 @@ def connect_to_network(network): # Make sure our container is connected to the nginx-proxy's network, # but avoid connecting to `none` network (not valid) with `test_server-down` tests if network.name not in my_networks and network.name != 'none': - logging.info(f"Connecting to docker network: {network.name}") - network.connect(my_container) - return network + try: + logging.info(f"Connecting to docker network: {network.name}") + network.connect(my_container) + return network + except docker.errors.APIError as e: + logging.warning(f"Failed to connect to network {network.name}: {e}") + return network # Ensure the network is still tracked for later removal + return None -def disconnect_from_network(network=None): + +def disconnect_from_network(network: Network = None): """ If we are running from a container, disconnect our container from the given network. @@ -399,7 +474,7 @@ def disconnect_from_network(network=None): try: my_container = docker_client.containers.get(test_container) except docker.errors.NotFound: - logging.warn(f"container {test_container} not found") + logging.warning(f"container {test_container} not found") return # figure out our container networks @@ -411,7 +486,7 @@ def disconnect_from_network(network=None): network.disconnect(my_container) -def connect_to_all_networks(): +def connect_to_all_networks() -> List[Network]: """ If we are running from a container, connect our container to all current docker networks. @@ -427,31 +502,62 @@ def connect_to_all_networks(): class DockerComposer(contextlib.AbstractContextManager): def __init__(self): - self._docker_compose_file = None + logging.debug("DockerComposer __init__") + self._networks = None + self._docker_compose_files = None + self._project_name = None def __exit__(self, *exc_info): + logging.debug("DockerComposer __exit__") self._down() def _down(self): - if self._docker_compose_file is None: + logging.debug(f"DockerComposer _down {self._docker_compose_files} {self._project_name} {self._networks}") + if self._docker_compose_files is None: + logging.debug("docker_compose_files is None, nothing to cleanup") 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: + if self._networks: + for network in self._networks: + disconnect_from_network(network) + docker_compose_down(self._docker_compose_files, self._project_name) + self._docker_compose_files = None + self._project_name = None + self._networks = [] + + def compose(self, docker_compose_files: List[str], project_name: str): + if docker_compose_files == self._docker_compose_files and project_name == self._project_name: + logging.info(f"Skipping compose: {docker_compose_files} (already running under project {project_name})") return - self._down() - if docker_compose_file is None: + if docker_compose_files is None or project_name is None: + logging.info(f"Skipping compose: no compose file specified") 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 + self._down() + self._docker_compose_files = docker_compose_files + self._project_name = project_name + logging.debug(f"DockerComposer compose {self._docker_compose_files} {self._project_name} {self._networks}") + + try: + docker_compose_up(docker_compose_files, project_name) + self._networks = connect_to_all_networks() + wait_for_nginxproxy_to_be_ready() + time.sleep(3) # give time to containers to be ready + + except KeyboardInterrupt: + logging.warning("KeyboardInterrupt detected! Force cleanup...") + self._down() # Ensure proper shutdown + raise # Re-raise to allow pytest to exit cleanly + + except docker.errors.APIError as e: + logging.error(f"Docker API error ({e.status_code}): {e.explanation}") + logging.debug(f"Full error message: {str(e)}") + self._down() # Ensure proper cleanup even on failure + pytest.fail(f"Docker Compose setup failed due to Docker API error: {e.explanation}") + + except RuntimeError as e: + logging.error(f"RuntimeEror encountered in: {project_name}") + logging.debug(f"Full error message: {str(e)}") + self._down() # Ensure proper cleanup even on failure + pytest.fail(f"Docker Compose setup failed due to RuntimeError in: {project_name}") ############################################################################### @@ -462,14 +568,14 @@ def compose(self, docker_compose_file): @pytest.fixture(scope="module") -def docker_composer(): +def docker_composer() -> Iterator[DockerComposer]: with DockerComposer() as d: yield d @pytest.fixture -def ca_root_certificate(): - return CA_ROOT_CERTIFICATE +def ca_root_certificate() -> str: + return CA_ROOT_CERTIFICATE.as_posix() @pytest.fixture @@ -480,25 +586,38 @@ def monkey_patched_dns(): @pytest.fixture -def docker_compose(monkey_patched_dns, docker_composer, docker_compose_file): - """Ensures containers described in a docker compose file are started. +def docker_compose( + request: FixtureRequest, + monkeypatch, + monkey_patched_dns, + docker_composer, + docker_compose_files +) -> Iterator[DockerClient]: + """ + Ensures containers necessary for the test module are started in a compose project, + and set the environment variable `PYTEST_MODULE_PATH` to the test module's parent folder. - A custom docker compose file name can be specified by overriding the `docker_compose_file` - fixture. + A list of custom docker compose files path 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. + 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_composer.compose(docker_compose_file) + pytest_module_path = pathlib.Path(request.module.__file__).parent + monkeypatch.setenv("PYTEST_MODULE_PATH", pytest_module_path.as_posix()) + + project_name = request.module.__name__ + docker_composer.compose(docker_compose_files, project_name) + yield docker_client -@pytest.fixture() -def nginxproxy(): +@pytest.fixture +def nginxproxy() -> Iterator[RequestsForDocker]: """ Provides the `nginxproxy` object that can be used in the same way the requests module is: - r = nginxproxy.get("http://foo.com") + r = nginxproxy.get("https://foo.com") The difference is that in case an HTTP requests has status code 404 or 502 (which mostly indicates that nginx has just reloaded), we retry up to 30 times the query. @@ -507,11 +626,11 @@ def nginxproxy(): made against containers to use the containers IPv6 address when set to `True`. If IPv6 is not supported by the system or docker, that particular test will be skipped. """ - yield requests_for_docker() + yield RequestsForDocker() -@pytest.fixture() -def acme_challenge_path(): +@pytest.fixture +def acme_challenge_path() -> str: """ Provides fake Let's Encrypt ACME challenge path used in certain tests """ @@ -523,14 +642,13 @@ def acme_challenge_path(): # ############################################################################### -# pytest hook to display additionnal stuff in test report +# pytest hook to display additional stuff in test report def pytest_runtest_logreport(report): if report.failed: - if isinstance(report.longrepr, ReprExceptionInfo): - test_containers = docker_client.containers.list(all=True, filters={"ancestor": "nginxproxy/nginx-proxy:test"}) - for container in test_containers: - report.longrepr.addsection('nginx-proxy logs', container.logs()) - report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container)) + test_containers = docker_client.containers.list(all=True, filters={"ancestor": "nginxproxy/nginx-proxy:test"}) + for container in test_containers: + report.longrepr.addsection('nginx-proxy logs', container.logs().decode()) + report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container).decode()) # Py.test `incremental` marker, see http://stackoverflow.com/a/12579625/107049 @@ -557,5 +675,5 @@ def pytest_runtest_setup(item): except docker.errors.ImageNotFound: pytest.exit("The docker image 'nginxproxy/nginx-proxy:test' is missing") -if LooseVersion(docker.__version__) < LooseVersion("5.0.0"): +if Version(docker.__version__) < Version("5.0.0"): pytest.exit("This test suite is meant to work with the python docker module v5.0.0 or later") diff --git a/test/pytest.sh b/test/pytest.sh index 9cbe75017..30e40de24 100755 --- a/test/pytest.sh +++ b/test/pytest.sh @@ -3,7 +3,7 @@ # # # This script is meant to run the test suite from a Docker container. # # # -# This is usefull when you want to run the test suite from Mac or # +# This is useful when you want to run the test suite from Mac or # # Docker Toolbox. # # # ############################################################################### diff --git a/test/requirements/Dockerfile-nginx-proxy-tester b/test/requirements/Dockerfile-nginx-proxy-tester index 1af4a16b2..90f6c5bd0 100644 --- a/test/requirements/Dockerfile-nginx-proxy-tester +++ b/test/requirements/Dockerfile-nginx-proxy-tester @@ -1,4 +1,4 @@ -FROM python:3.9 +FROM python:3.12 ENV PYTEST_RUNNING_IN_CONTAINER=1 @@ -10,14 +10,13 @@ RUN apt-get update \ && apt-get install -y \ ca-certificates \ curl \ - gnupg \ && install -m 0755 -d /etc/apt/keyrings \ - && curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \ - && chmod a+r /etc/apt/keyrings/docker.gpg + && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \ + && chmod a+r /etc/apt/keyrings/docker.asc # Add the Docker repository to Apt sources RUN echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null diff --git a/test/requirements/python-requirements.txt b/test/requirements/python-requirements.txt index c3e2218a1..cf0a1d74c 100644 --- a/test/requirements/python-requirements.txt +++ b/test/requirements/python-requirements.txt @@ -1,4 +1,7 @@ backoff==2.2.1 docker==7.1.0 -pytest==8.2.1 -requests==2.32.3 +packaging==25.0 +pytest==8.4.1 +pytest-ignore-flaky==2.2.1 +requests==2.32.4 +urllib3==2.5.0 diff --git a/test/requirements/web/Dockerfile b/test/requirements/web/Dockerfile index da755534c..2fb7edf53 100644 --- a/test/requirements/web/Dockerfile +++ b/test/requirements/web/Dockerfile @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 # Docker Image running one (or multiple) webservers listening on all given ports from WEB_PORTS environment variable FROM python:3-alpine diff --git a/test/requirements/web/webserver.py b/test/requirements/web/webserver.py index c4750bdb1..3b584ed29 100755 --- a/test/requirements/web/webserver.py +++ b/test/requirements/web/webserver.py @@ -28,7 +28,7 @@ def do_GET(self): self.send_header("Content-Type", "text/plain") self.end_headers() - if (len(response_body)): + if len(response_body): self.wfile.write(response_body.encode()) if __name__ == '__main__': diff --git a/test/stress_tests/README.md b/test/stress_tests/README.md deleted file mode 100644 index ca20cc109..000000000 --- a/test/stress_tests/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory contains tests that showcase scenarios known to break the expected behavior of nginx-proxy. \ No newline at end of file diff --git a/test/test_acme_http_challenge_location/acme_root/.well-known/acme-challenge/test-filename b/test/test_acme-http-challenge-location/acme_root/.well-known/acme-challenge/test-filename similarity index 100% rename from test/test_acme_http_challenge_location/acme_root/.well-known/acme-challenge/test-filename rename to test/test_acme-http-challenge-location/acme_root/.well-known/acme-challenge/test-filename diff --git a/test/test_acme_http_challenge_location/certs/nginx-proxy.tld.crt b/test/test_acme-http-challenge-location/certs/nginx-proxy.tld.crt similarity index 100% rename from test/test_acme_http_challenge_location/certs/nginx-proxy.tld.crt rename to test/test_acme-http-challenge-location/certs/nginx-proxy.tld.crt diff --git a/test/test_acme_http_challenge_location/certs/nginx-proxy.tld.key b/test/test_acme-http-challenge-location/certs/nginx-proxy.tld.key similarity index 100% rename from test/test_acme_http_challenge_location/certs/nginx-proxy.tld.key rename to test/test_acme-http-challenge-location/certs/nginx-proxy.tld.key diff --git a/test/test_acme-http-challenge-location/compose.base.override.yml b/test/test_acme-http-challenge-location/compose.base.override.yml new file mode 100644 index 000000000..6bef2d35b --- /dev/null +++ b/test/test_acme-http-challenge-location/compose.base.override.yml @@ -0,0 +1,6 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/acme_root:/usr/share/nginx/html:ro diff --git a/test/test_acme_http_challenge_location/test_acme_challenge_location_enabled_is_default.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py similarity index 78% rename from test/test_acme_http_challenge_location/test_acme_challenge_location_enabled_is_default.py rename to test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py index fd06e847d..8643b4bf9 100644 --- a/test/test_acme_http_challenge_location/test_acme_challenge_location_enabled_is_default.py +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.py @@ -1,6 +1,3 @@ -import pytest - - def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): r = nginxproxy.get( f"http://web1.nginx-proxy.tld/{acme_challenge_path}", @@ -28,3 +25,10 @@ def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, allow_redirects=False ) assert r.status_code == 404 + +def test_unknown_domain_acme_challenge_location_default_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 200 diff --git a/test/test_acme_http_challenge_location/test_acme_challenge_location_enabled_is_default.yml b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml similarity index 77% rename from test/test_acme_http_challenge_location/test_acme_challenge_location_enabled_is_default.yml rename to test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml index 41439e30c..25965e493 100644 --- a/test/test_acme_http_challenge_location/test_acme_challenge_location_enabled_is_default.yml +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-accept-unknown-host.yml @@ -1,6 +1,8 @@ -version: "2" - services: + nginx-proxy: + environment: + ACME_HTTP_CHALLENGE_ACCEPT_UNKNOWN_HOST: "true" + web1: image: web expose: @@ -36,10 +38,3 @@ services: VIRTUAL_HOST: "web4.nginx-proxy.tld" HTTPS_METHOD: noredirect ACME_HTTP_CHALLENGE_LOCATION: "false" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_acme_http_challenge_location/test_acme_challenge_location_disabled.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py similarity index 79% rename from test/test_acme_http_challenge_location/test_acme_challenge_location_disabled.py rename to test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py index acbc8feb9..5588167b4 100644 --- a/test/test_acme_http_challenge_location/test_acme_challenge_location_disabled.py +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.py @@ -1,6 +1,3 @@ -import pytest - - def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): r = nginxproxy.get( f"http://web1.nginx-proxy.tld/{acme_challenge_path}", @@ -28,3 +25,10 @@ def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, allow_redirects=False ) assert r.status_code == 200 + +def test_unknown_domain_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 503 diff --git a/test/test_acme_http_challenge_location/test_acme_challenge_location_disabled.yml b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.yml similarity index 78% rename from test/test_acme_http_challenge_location/test_acme_challenge_location_disabled.yml rename to test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.yml index 3cd4f2d62..587aa49bb 100644 --- a/test/test_acme_http_challenge_location/test_acme_challenge_location_disabled.yml +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-disabled.yml @@ -1,6 +1,8 @@ -version: "2" - services: + nginx-proxy: + environment: + ACME_HTTP_CHALLENGE_LOCATION: "false" + web1: image: web expose: @@ -36,12 +38,3 @@ services: VIRTUAL_HOST: "web4.nginx-proxy.tld" HTTPS_METHOD: noredirect ACME_HTTP_CHALLENGE_LOCATION: "true" - - sut: - image: nginxproxy/nginx-proxy:test - environment: - ACME_HTTP_CHALLENGE_LOCATION: "false" - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py new file mode 100644 index 000000000..80eea97e8 --- /dev/null +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.py @@ -0,0 +1,34 @@ +def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web1.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 200 + +def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web2.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 301 + +def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web3.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 200 + +def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web4.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 404 + +def test_unknown_domain_acme_challenge_location_default_enabled(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 503 diff --git a/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.yml b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.yml new file mode 100644 index 000000000..eda25afce --- /dev/null +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-enabled-is-default.yml @@ -0,0 +1,36 @@ +services: + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "web1.nginx-proxy.tld" + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "web2.nginx-proxy.tld" + ACME_HTTP_CHALLENGE_LOCATION: "false" + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "web3.nginx-proxy.tld" + HTTPS_METHOD: noredirect + + web4: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: "web4.nginx-proxy.tld" + HTTPS_METHOD: noredirect + ACME_HTTP_CHALLENGE_LOCATION: "false" diff --git a/test/test_acme_http_challenge_location/test_acme_challenge_location_legacy.py b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py similarity index 65% rename from test/test_acme_http_challenge_location/test_acme_challenge_location_legacy.py rename to test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py index d2051d0aa..495ad834a 100644 --- a/test/test_acme_http_challenge_location/test_acme_challenge_location_legacy.py +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.py @@ -1,6 +1,3 @@ -import pytest - - def test_redirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path): r = nginxproxy.get( f"http://web1.nginx-proxy.tld/{acme_challenge_path}", @@ -14,3 +11,10 @@ def test_noredirect_acme_challenge_location_legacy(docker_compose, nginxproxy, a allow_redirects=False ) assert r.status_code == 404 + +def test_unknown_domain_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path): + r = nginxproxy.get( + f"http://web-unknown.nginx-proxy.tld/{acme_challenge_path}", + allow_redirects=False + ) + assert r.status_code == 503 diff --git a/test/test_acme_http_challenge_location/test_acme_challenge_location_legacy.yml b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.yml similarity index 64% rename from test/test_acme_http_challenge_location/test_acme_challenge_location_legacy.yml rename to test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.yml index 693f9e013..e4fc01fc5 100644 --- a/test/test_acme_http_challenge_location/test_acme_challenge_location_legacy.yml +++ b/test/test_acme-http-challenge-location/test_acme-http-challenge-location-legacy.yml @@ -1,6 +1,8 @@ -version: "2" - services: + nginx-proxy: + environment: + ACME_HTTP_CHALLENGE_LOCATION: "legacy" + web1: image: web expose: @@ -17,12 +19,3 @@ services: WEB_PORTS: "82" VIRTUAL_HOST: "web2.nginx-proxy.tld" HTTPS_METHOD: noredirect - - sut: - image: nginxproxy/nginx-proxy:test - environment: - ACME_HTTP_CHALLENGE_LOCATION: "legacy" - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_build.py b/test/test_build/test_build.py similarity index 90% rename from test/test_build.py rename to test/test_build/test_build.py index 4052d1ee0..3931fcd4e 100644 --- a/test/test_build.py +++ b/test/test_build/test_build.py @@ -1,22 +1,25 @@ """ Test that nginx-proxy-tester can build successfully """ -import pytest -import docker +import pathlib import re -import os + +import docker +import pytest + client = docker.from_env() @pytest.fixture(scope = "session") def docker_build(request): # Define Dockerfile path - dockerfile_path = os.path.join(os.path.dirname(__file__), "requirements/") + current_file_path = pathlib.Path(__file__) + dockerfile_path = current_file_path.parent.parent.joinpath("requirements") dockerfile_name = "Dockerfile-nginx-proxy-tester" # Build the Docker image image, logs = client.images.build( - path = dockerfile_path, + path = dockerfile_path.as_posix(), dockerfile = dockerfile_name, rm = True, # Remove intermediate containers tag = "nginx-proxy-tester-ci", # Tag for the built image diff --git a/test/test_custom-error-page/test_custom-error-page.py b/test/test_custom-error-page/test_custom-error-page.py index 32cb0b542..6317aeaac 100644 --- a/test/test_custom-error-page/test_custom-error-page.py +++ b/test/test_custom-error-page/test_custom-error-page.py @@ -1,4 +1,3 @@ -import pytest import re diff --git a/test/test_custom-error-page/test_custom-error-page.yml b/test/test_custom-error-page/test_custom-error-page.yml index 419b7eb8d..b0aa7db38 100644 --- a/test/test_custom-error-page/test_custom-error-page.yml +++ b/test/test_custom-error-page/test_custom-error-page.yml @@ -1,8 +1,5 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./50x.html:/usr/share/nginx/html/errors/50x.html:ro + - ${PYTEST_MODULE_PATH}/50x.html:/usr/share/nginx/html/errors/50x.html:ro diff --git a/test/test_custom/my_custom_proxy_settings_baz.conf b/test/test_custom/my_custom_proxy_settings_baz.conf new file mode 100644 index 000000000..d13493050 --- /dev/null +++ b/test/test_custom/my_custom_proxy_settings_baz.conf @@ -0,0 +1 @@ +add_header X-test-2 baz; \ No newline at end of file diff --git a/test/test_custom/my_custom_proxy_settings.conf b/test/test_custom/my_custom_proxy_settings_f00.conf similarity index 100% rename from test/test_custom/my_custom_proxy_settings.conf rename to test/test_custom/my_custom_proxy_settings_f00.conf diff --git a/test/test_custom/test_defaults-location.yml b/test/test_custom/test_defaults-location.yml deleted file mode 100644 index 9a3ab44f3..000000000 --- a/test/test_custom/test_defaults-location.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: "2" - -services: - nginx-proxy: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/default_location:ro - - ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.example_location:ro - - web1: - image: web - expose: - - "81" - environment: - WEB_PORTS: 81 - VIRTUAL_HOST: web1.nginx-proxy.example - - web2: - image: web - expose: - - "82" - environment: - WEB_PORTS: 82 - VIRTUAL_HOST: web2.nginx-proxy.example - - web3: - image: web - expose: - - "83" - environment: - WEB_PORTS: 83 - VIRTUAL_HOST: web3.nginx-proxy.example diff --git a/test/test_custom/test_defaults.py b/test/test_custom/test_defaults.py index c9cb2dfb0..74e17c99b 100644 --- a/test/test_custom/test_defaults.py +++ b/test/test_custom/test_defaults.py @@ -1,5 +1,3 @@ -import pytest - def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 @@ -18,3 +16,10 @@ def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): assert r.text == "answer from port 82\n" assert "X-test" in r.headers assert "f00" == r.headers["X-test"] + +def test_custom_conf_applies_to_wildcard(docker_compose, nginxproxy): + r = nginxproxy.get("http://wildcard.nginx-proxy.example/port") + assert r.status_code == 200 + assert r.text == "answer from port 83\n" + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] diff --git a/test/test_custom/test_defaults.yml b/test/test_custom/test_defaults.yml index d6a959a48..4826fb1e5 100644 --- a/test/test_custom/test_defaults.yml +++ b/test/test_custom/test_defaults.yml @@ -1,18 +1,15 @@ -version: "2" - services: nginx-proxy: - image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./my_custom_proxy_settings.conf:/etc/nginx/proxy.conf:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/proxy.conf:ro web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example web2: @@ -20,5 +17,13 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.example + + wildcard: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "*.nginx-proxy.example" # wildcard for all subdomains diff --git a/test/test_custom/test_defaults-location.py b/test/test_custom/test_location-defaults.py similarity index 78% rename from test/test_custom/test_defaults-location.py rename to test/test_custom/test_location-defaults.py index 5af359da0..de4faf04e 100644 --- a/test/test_custom/test_defaults-location.py +++ b/test/test_custom/test_location-defaults.py @@ -1,5 +1,3 @@ -import pytest - def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 @@ -19,10 +17,16 @@ def test_custom_default_conf_applies_to_web2(docker_compose, nginxproxy): assert "X-test" in r.headers assert "f00" == r.headers["X-test"] - def test_custom_default_conf_is_overriden_for_web3(docker_compose, nginxproxy): r = nginxproxy.get("http://web3.nginx-proxy.example/port") assert r.status_code == 200 assert r.text == "answer from port 83\n" assert "X-test" in r.headers assert "bar" == r.headers["X-test"] + +def test_custom_default_conf_applies_to_wildcard(docker_compose, nginxproxy): + r = nginxproxy.get("http://wildcard.nginx-proxy.example/port") + assert r.status_code == 200 + assert r.text == "answer from port 84\n" + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] diff --git a/test/test_custom/test_location-defaults.yml b/test/test_custom/test_location-defaults.yml new file mode 100644 index 000000000..e76030224 --- /dev/null +++ b/test/test_custom/test_location-defaults.yml @@ -0,0 +1,38 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/default_location:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.example_location:ro + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: web1.nginx-proxy.example + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: web2.nginx-proxy.example + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: web3.nginx-proxy.example + + wildcard: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: "*.nginx-proxy.example" # wildcard for all subdomains diff --git a/test/test_custom/test_location-per-vhost.py b/test/test_custom/test_location-per-vhost.py index 53a146b55..6e8b52555 100644 --- a/test/test_custom/test_location-per-vhost.py +++ b/test/test_custom/test_location-per-vhost.py @@ -1,22 +1,41 @@ -import pytest - def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 assert "X-test" not in r.headers + assert "X-test-2" not in r.headers def test_custom_conf_applies_to_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy.example/port") - assert r.status_code == 200 + assert r.status_code == 200 assert r.text == "answer from port 81\n" assert "X-test" in r.headers + assert "X-test-2" not in r.headers assert "f00" == r.headers["X-test"] +def test_custom_conf_applies_to_regex(docker_compose, nginxproxy): + r = nginxproxy.get("http://foo.nginx-proxy.regex/port") + assert r.status_code == 200 + assert r.text == "answer from port 83\n" + assert "X-test" in r.headers + assert "X-test-2" not in r.headers + assert "bar" == r.headers["X-test"] + +def test_custom_conf_applies_to_wildcard(docker_compose, nginxproxy): + r = nginxproxy.get("http://foo.nginx-proxy.example/port") + assert r.status_code == 200 + assert r.text == "answer from port 84\n" + # we should get the config from *.nginx-proxy.example_location.conf + assert "X-test-2" in r.headers + assert "baz" == r.headers["X-test-2"] + # but not the config from web1.nginx-proxy.example_location.conf + assert "X-test" not in r.headers + def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy): r = nginxproxy.get("http://web2.nginx-proxy.example/port") - assert r.status_code == 200 + assert r.status_code == 200 assert r.text == "answer from port 82\n" assert "X-test" not in r.headers + assert "X-test-2" not in r.headers def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): assert b"include /etc/nginx/vhost.d/web1.nginx-proxy.example_location;" in nginxproxy.get_conf() \ No newline at end of file diff --git a/test/test_custom/test_location-per-vhost.yml b/test/test_custom/test_location-per-vhost.yml index 52943086e..b8501f2a6 100644 --- a/test/test_custom/test_location-per-vhost.yml +++ b/test/test_custom/test_location-per-vhost.yml @@ -1,18 +1,17 @@ -version: "2" - services: nginx-proxy: - image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example_location:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example_location:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/97db860fb631ba3c047db9fec60638fd72a180b1_location:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_baz.conf:/etc/nginx/vhost.d/*.nginx-proxy.example_location:ro web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example web2: @@ -20,5 +19,21 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.example + + regex: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: ~^.*\.nginx-proxy\.regex$$ # we need to double the `$` because of docker compose variable interpolation + + wildcard: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: "*.nginx-proxy.example" # wildcard for all subdomains diff --git a/test/test_custom/test_per-vhost.py b/test/test_custom/test_per-vhost.py index 6a85e6948..8cd8f3bd6 100644 --- a/test/test_custom/test_per-vhost.py +++ b/test/test_custom/test_per-vhost.py @@ -1,19 +1,38 @@ -import pytest - def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 assert "X-test" not in r.headers + assert "X-test-2" not in r.headers def test_custom_conf_applies_to_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy.example/port") - assert r.status_code == 200 + assert r.status_code == 200 assert r.text == "answer from port 81\n" assert "X-test" in r.headers + assert "X-test-2" not in r.headers assert "f00" == r.headers["X-test"] +def test_custom_conf_applies_to_regex(docker_compose, nginxproxy): + r = nginxproxy.get("http://foo.nginx-proxy.regex/port") + assert r.status_code == 200 + assert r.text == "answer from port 83\n" + assert "X-test" in r.headers + assert "X-test-2" not in r.headers + assert "bar" == r.headers["X-test"] + +def test_custom_conf_applies_to_wildcard(docker_compose, nginxproxy): + r = nginxproxy.get("http://foo.nginx-proxy.example/port") + assert r.status_code == 200 + assert r.text == "answer from port 84\n" + # we should get the config from *.nginx-proxy.example.conf + assert "X-test-2" in r.headers + assert "baz" == r.headers["X-test-2"] + # but not the config from web1.nginx-proxy.example.conf + assert "X-test" not in r.headers + def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy): r = nginxproxy.get("http://web2.nginx-proxy.example/port") - assert r.status_code == 200 + assert r.status_code == 200 assert r.text == "answer from port 82\n" assert "X-test" not in r.headers + assert "X-test-2" not in r.headers diff --git a/test/test_custom/test_per-vhost.yml b/test/test_custom/test_per-vhost.yml index 63d33b2b1..bc21ed6d2 100644 --- a/test/test_custom/test_per-vhost.yml +++ b/test/test_custom/test_per-vhost.yml @@ -1,18 +1,17 @@ -version: "2" - services: nginx-proxy: - image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/97db860fb631ba3c047db9fec60638fd72a180b1:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_baz.conf:/etc/nginx/vhost.d/*.nginx-proxy.example:ro web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example web2: @@ -20,5 +19,21 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.example + + regex: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: ~^.*\.nginx-proxy\.regex$$ # we need to double the `$` because of docker compose variable interpolation + + wildcard: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: "*.nginx-proxy.example" # wildcard for all subdomains diff --git a/test/test_custom/test_proxy-wide.py b/test/test_custom/test_proxy-wide.py index c9cb2dfb0..74e17c99b 100644 --- a/test/test_custom/test_proxy-wide.py +++ b/test/test_custom/test_proxy-wide.py @@ -1,5 +1,3 @@ -import pytest - def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 @@ -18,3 +16,10 @@ def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): assert r.text == "answer from port 82\n" assert "X-test" in r.headers assert "f00" == r.headers["X-test"] + +def test_custom_conf_applies_to_wildcard(docker_compose, nginxproxy): + r = nginxproxy.get("http://wildcard.nginx-proxy.example/port") + assert r.status_code == 200 + assert r.text == "answer from port 83\n" + assert "X-test" in r.headers + assert "f00" == r.headers["X-test"] diff --git a/test/test_custom/test_proxy-wide.yml b/test/test_custom/test_proxy-wide.yml index 1322bcde7..725ffa86e 100644 --- a/test/test_custom/test_proxy-wide.yml +++ b/test/test_custom/test_proxy-wide.yml @@ -1,18 +1,15 @@ -version: "2" - services: nginx-proxy: - image: nginxproxy/nginx-proxy:test volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./my_custom_proxy_settings.conf:/etc/nginx/conf.d/my_custom_proxy_settings.conf:ro + - ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/conf.d/my_custom_proxy_settings_f00.conf:ro web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example web2: @@ -20,5 +17,13 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.example + + wildcard: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "*.nginx-proxy.example" # wildcard for all subdomains diff --git a/test/test_debug-endpoint/test_global.py b/test/test_debug-endpoint/test_global.py new file mode 100644 index 000000000..cf0f8b75b --- /dev/null +++ b/test/test_debug-endpoint/test_global.py @@ -0,0 +1,48 @@ +import json + +import pytest + + +def test_debug_endpoint_is_enabled_globally(docker_compose, nginxproxy): + r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + r = nginxproxy.get("http://stripped.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + + +def test_debug_endpoint_response_contains_expected_values(docker_compose, nginxproxy): + r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + try: + jsonResponse = json.loads(r.text) + except ValueError as err: + pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False) + assert jsonResponse["global"]["enable_debug_endpoint"] == "true" + assert jsonResponse["vhost"]["enable_debug_endpoint"] == True + + +def test_debug_endpoint_paths_stripped_if_response_too_long(docker_compose, nginxproxy): + r = nginxproxy.get("http://stripped.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + try: + jsonResponse = json.loads(r.text) + except ValueError as err: + pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False) + if "paths" in jsonResponse["vhost"]: + pytest.fail("Expected paths to be stripped from debug endpoint response", pytrace=False) + assert jsonResponse["warning"] == "Virtual paths configuration for this hostname is too large and has been stripped from response." + + +def test_debug_endpoint_hostname_replaced_by_warning_if_regexp(docker_compose, nginxproxy): + r = nginxproxy.get("http://regexp.foo.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + try: + jsonResponse = json.loads(r.text) + except ValueError as err: + pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False) + assert jsonResponse["vhost"]["hostname"] == "Hostname is a regexp and unsafe to include in the debug response." + + +def test_debug_endpoint_is_disabled_per_container(docker_compose, nginxproxy): + r = nginxproxy.get("http://disabled.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 404 diff --git a/test/test_debug-endpoint/test_global.yml b/test/test_debug-endpoint/test_global.yml new file mode 100644 index 000000000..d4be9139f --- /dev/null +++ b/test/test_debug-endpoint/test_global.yml @@ -0,0 +1,59 @@ +services: + nginx-proxy: + environment: + DEBUG_ENDPOINT: "true" + + debug_enabled: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: enabled.debug.nginx-proxy.example + + debug_stripped: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST_MULTIPORTS: |- + stripped.debug.nginx-proxy.example: + "/1": + "/2": + "/3": + "/4": + "/5": + "/6": + "/7": + "/8": + "/9": + "/10": + "/11": + "/12": + "/13": + "/14": + "/15": + "/16": + "/17": + "/18": + "/19": + "/20": + + debug_regexp: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST: ~^regexp.*\.debug.nginx-proxy.example + + debug_disabled: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: disabled.debug.nginx-proxy.example + labels: + com.github.nginx-proxy.nginx-proxy.debug-endpoint: "false" diff --git a/test/test_debug-endpoint/test_per-container.py b/test/test_debug-endpoint/test_per-container.py new file mode 100644 index 000000000..ffc4db1fd --- /dev/null +++ b/test/test_debug-endpoint/test_per-container.py @@ -0,0 +1,26 @@ +import json + +import pytest + + +def test_debug_endpoint_is_disabled_globally(docker_compose, nginxproxy): + r = nginxproxy.get("http://disabled1.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 404 + r = nginxproxy.get("http://disabled2.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 404 + + +def test_debug_endpoint_is_enabled_per_container(docker_compose, nginxproxy): + r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + + +def test_debug_endpoint_response_contains_expected_values(docker_compose, nginxproxy): + r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug") + assert r.status_code == 200 + try: + jsonResponse = json.loads(r.text) + except ValueError as err: + pytest.fail("Failed to parse debug endpoint response as JSON:: %s" % err, pytrace=False) + assert jsonResponse["global"]["enable_debug_endpoint"] == "false" + assert jsonResponse["vhost"]["enable_debug_endpoint"] == True diff --git a/test/test_debug-endpoint/test_per-container.yml b/test/test_debug-endpoint/test_per-container.yml new file mode 100644 index 000000000..d6a381a10 --- /dev/null +++ b/test/test_debug-endpoint/test_per-container.yml @@ -0,0 +1,27 @@ +services: + debug_disabled1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: disabled1.debug.nginx-proxy.example + + debug_disabled2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: disabled2.debug.nginx-proxy.example + + + debug_enabled: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: enabled.debug.nginx-proxy.example + labels: + com.github.nginx-proxy.nginx-proxy.debug-endpoint: "true" diff --git a/test/test_default-host.yml b/test/test_default-host.yml deleted file mode 100644 index 405bb081a..000000000 --- a/test/test_default-host.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: "2" - -services: - # GIVEN a webserver with VIRTUAL_HOST set to web1.tld - web1: - image: web - expose: - - "81" - environment: - WEB_PORTS: 81 - VIRTUAL_HOST: web1.tld - - # WHEN nginx-proxy runs with DEFAULT_HOST set to web1.tld - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - DEFAULT_HOST: web1.tld diff --git a/test/test_default-host.py b/test/test_default-host/test_default-host.py similarity index 92% rename from test/test_default-host.py rename to test/test_default-host/test_default-host.py index 90809a5cc..560baf745 100644 --- a/test/test_default-host.py +++ b/test/test_default-host/test_default-host.py @@ -1,6 +1,3 @@ -import pytest - - def test_fallback_on_default(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_default-host/test_default-host.yml b/test/test_default-host/test_default-host.yml new file mode 100644 index 000000000..458cb39bb --- /dev/null +++ b/test/test_default-host/test_default-host.yml @@ -0,0 +1,12 @@ +services: + nginx-proxy: + environment: + DEFAULT_HOST: web1.tld + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: web1.tld diff --git a/test/test_DOCKER_HOST_unix_socket.py b/test/test_docker-unix-socket/test_docker-unix-socket.py similarity index 97% rename from test/test_DOCKER_HOST_unix_socket.py rename to test/test_docker-unix-socket/test_docker-unix-socket.py index b31da1627..c7e72114d 100644 --- a/test/test_DOCKER_HOST_unix_socket.py +++ b/test/test_docker-unix-socket/test_docker-unix-socket.py @@ -1,5 +1,3 @@ -import pytest - def test_unknown_virtual_host(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/port") assert r.status_code == 503 diff --git a/test/test_DOCKER_HOST_unix_socket.yml b/test/test_docker-unix-socket/test_docker-unix-socket.yml similarity index 76% rename from test/test_DOCKER_HOST_unix_socket.yml rename to test/test_docker-unix-socket/test_docker-unix-socket.yml index caeaa2a55..03cb41457 100644 --- a/test/test_DOCKER_HOST_unix_socket.yml +++ b/test/test_docker-unix-socket/test_docker-unix-socket.yml @@ -1,12 +1,16 @@ -version: "2" - services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/f00.sock:ro + environment: + DOCKER_HOST: unix:///f00.sock + web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.tld web2: @@ -14,12 +18,5 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/f00.sock:ro - environment: - DOCKER_HOST: unix:///f00.sock diff --git a/test/test_dockergen/.gitignore b/test/test_dockergen/.gitignore deleted file mode 100644 index 98c0b068e..000000000 --- a/test/test_dockergen/.gitignore +++ /dev/null @@ -1 +0,0 @@ -nginx.tmpl \ No newline at end of file diff --git a/test/test_dockergen/test_dockergen_v3.yml b/test/test_dockergen/test_dockergen.base.yml similarity index 69% rename from test/test_dockergen/test_dockergen_v3.yml rename to test/test_dockergen/test_dockergen.base.yml index 7e4aef8c1..3012abefa 100644 --- a/test/test_dockergen/test_dockergen_v3.yml +++ b/test/test_dockergen/test_dockergen.base.yml @@ -1,13 +1,18 @@ -version: "3" +volumes: + nginx_conf: + services: - nginx: + nginx-proxy-nginx: image: nginx container_name: nginx volumes: - - nginx_conf:/etc/nginx/conf.d + - nginx_conf:/etc/nginx/conf.d:ro + ports: + - "80:80" + - "443:443" - dockergen: + nginx-proxy-dockergen: image: nginxproxy/docker-gen command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf volumes: @@ -21,8 +26,5 @@ services: expose: - "80" environment: - WEB_PORTS: 80 - VIRTUAL_HOST: whoami.nginx.container.docker - -volumes: - nginx_conf: {} + WEB_PORTS: "80" + VIRTUAL_HOST: whoami.nginx-proxy.tld diff --git a/test/test_dockergen/test_dockergen_v2.py b/test/test_dockergen/test_dockergen.py similarity index 70% rename from test/test_dockergen/test_dockergen_v2.py rename to test/test_dockergen/test_dockergen.py index dbb15d47f..8fd4be910 100644 --- a/test/test_dockergen/test_dockergen_v2.py +++ b/test/test_dockergen/test_dockergen.py @@ -1,10 +1,10 @@ def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): - r = nginxproxy.get("http://unknown.nginx.container.docker/") + r = nginxproxy.get("http://unknown.nginx-proxy.tld/") assert r.status_code == 503 def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami.nginx.container.docker/") + r = nginxproxy.get("http://whoami.nginx-proxy.tld/") assert r.status_code == 200 whoami_container = docker_compose.containers.get("whoami") assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml b/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml new file mode 100644 index 000000000..bc79d2dba --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.base.yml @@ -0,0 +1,45 @@ +networks: + proxy: + private: + internal: true + +volumes: + nginx_conf: + + +services: + nginx-proxy-nginx: + image: nginx + container_name: nginx + volumes: + - nginx_conf:/etc/nginx/conf.d:ro + ports: + - "80:80" + - "443:443" + networks: + - proxy + labels: + - "com.github.nginx-proxy.nginx-proxy.foobarbuzz" + + nginx-proxy-dockergen: + image: nginxproxy/docker-gen + command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - nginx_conf:/etc/nginx/conf.d + environment: + NGINX_CONTAINER_LABEL: "com.github.nginx-proxy.nginx-proxy.foobarbuzz" + networks: + - private + + web: + image: web + container_name: whoami2 + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: whoami2.nginx-proxy.tld + networks: + - proxy diff --git a/test/test_dockergen/test_dockergen_network_segregation-custom-label.py b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py new file mode 100644 index 000000000..09e7e6d12 --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation-custom-label.py @@ -0,0 +1,15 @@ +import pytest + + +@pytest.mark.flaky +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): + r = nginxproxy.get("http://unknown.nginx-proxy.tld/") + assert r.status_code == 503 + + +@pytest.mark.flaky +def test_forwards_to_whoami(docker_compose, nginxproxy): + r = nginxproxy.get("http://whoami2.nginx-proxy.tld/") + assert r.status_code == 200 + whoami_container = docker_compose.containers.get("whoami2") + assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_network_segregation.base.yml b/test/test_dockergen/test_dockergen_network_segregation.base.yml new file mode 100644 index 000000000..10f61a76b --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation.base.yml @@ -0,0 +1,43 @@ +networks: + proxy: + private: + internal: true + +volumes: + nginx_conf: + + +services: + nginx-proxy-nginx: + image: nginx + container_name: nginx + volumes: + - nginx_conf:/etc/nginx/conf.d:ro + ports: + - "80:80" + - "443:443" + networks: + - proxy + labels: + - "com.github.nginx-proxy.nginx-proxy.nginx" + + nginx-proxy-dockergen: + image: nginxproxy/docker-gen + command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl + - nginx_conf:/etc/nginx/conf.d + networks: + - private + + web: + image: web + container_name: whoami2 + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: whoami2.nginx-proxy.tld + networks: + - proxy diff --git a/test/test_dockergen/test_dockergen_network_segregation.py b/test/test_dockergen/test_dockergen_network_segregation.py new file mode 100644 index 000000000..09e7e6d12 --- /dev/null +++ b/test/test_dockergen/test_dockergen_network_segregation.py @@ -0,0 +1,15 @@ +import pytest + + +@pytest.mark.flaky +def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): + r = nginxproxy.get("http://unknown.nginx-proxy.tld/") + assert r.status_code == 503 + + +@pytest.mark.flaky +def test_forwards_to_whoami(docker_compose, nginxproxy): + r = nginxproxy.get("http://whoami2.nginx-proxy.tld/") + assert r.status_code == 200 + whoami_container = docker_compose.containers.get("whoami2") + assert r.text == f"I'm {whoami_container.id[:12]}\n" diff --git a/test/test_dockergen/test_dockergen_v2.yml b/test/test_dockergen/test_dockergen_v2.yml deleted file mode 100644 index f98992f20..000000000 --- a/test/test_dockergen/test_dockergen_v2.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: "2" - -services: - nginx: - image: nginx - container_name: nginx - volumes: - - /etc/nginx/conf.d - - dockergen: - image: nginxproxy/docker-gen - command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf - volumes_from: - - nginx - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl - - web: - image: web - container_name: whoami - expose: - - "80" - environment: - WEB_PORTS: 80 - VIRTUAL_HOST: whoami.nginx.container.docker diff --git a/test/test_dockergen/test_dockergen_v3.py b/test/test_dockergen/test_dockergen_v3.py deleted file mode 100644 index b696e6c62..000000000 --- a/test/test_dockergen/test_dockergen_v3.py +++ /dev/null @@ -1,27 +0,0 @@ -import docker -import pytest -from distutils.version import LooseVersion - - -raw_version = docker.from_env().version()["Version"] -pytestmark = pytest.mark.skipif( - LooseVersion(raw_version) < LooseVersion("1.13"), - reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" -) - - -def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): - r = nginxproxy.get("http://unknown.nginx.container.docker/") - assert r.status_code == 503 - - -def test_forwards_to_whoami(docker_compose, nginxproxy): - r = nginxproxy.get("http://whoami.nginx.container.docker/") - assert r.status_code == 200 - whoami_container = docker_compose.containers.get("whoami") - assert r.text == f"I'm {whoami_container.id[:12]}\n" - - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/test/test_enable_http_on_missing_cert.py b/test/test_enable-http-on-missing-cert/test_enable-http-on-missing-cert.py similarity index 98% rename from test/test_enable_http_on_missing_cert.py rename to test/test_enable-http-on-missing-cert/test_enable-http-on-missing-cert.py index cdedc8a2c..cda948c93 100644 --- a/test/test_enable_http_on_missing_cert.py +++ b/test/test_enable-http-on-missing-cert/test_enable-http-on-missing-cert.py @@ -1,6 +1,3 @@ -import pytest - - def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy): r = nginxproxy.get("http://nohttp-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False) assert r.status_code == 503 diff --git a/test/test_enable_http_on_missing_cert.yml b/test/test_enable-http-on-missing-cert/test_enable-http-on-missing-cert.yml similarity index 84% rename from test/test_enable_http_on_missing_cert.yml rename to test/test_enable-http-on-missing-cert/test_enable-http-on-missing-cert.yml index 1149ef720..56db74393 100644 --- a/test/test_enable_http_on_missing_cert.yml +++ b/test/test_enable-http-on-missing-cert/test_enable-http-on-missing-cert.yml @@ -1,11 +1,5 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./withdefault.certs:/etc/nginx/certs:ro + nginx-proxy: environment: ENABLE_HTTP_ON_MISSING_CERT: "false" diff --git a/test/test_events.py b/test/test_events/test_events.py similarity index 93% rename from test/test_events.py rename to test/test_events/test_events.py index 9b99e93b8..e137c8700 100644 --- a/test/test_events.py +++ b/test/test_events/test_events.py @@ -7,7 +7,7 @@ from docker.errors import NotFound -@pytest.fixture() +@pytest.fixture def web1(docker_compose): """ pytest fixture creating a web container with `VIRTUAL_HOST=web1.nginx-proxy` listening on port 81. @@ -22,7 +22,7 @@ def web1(docker_compose): }, ports={"81/tcp": None} ) - docker_compose.networks.get("test_default").connect(container) + docker_compose.networks.get("test_events-net").connect(container) sleep(2) # give it some time to initialize and for docker-gen to detect it yield container try: @@ -30,7 +30,7 @@ def web1(docker_compose): except NotFound: pass -@pytest.fixture() +@pytest.fixture def web2(docker_compose): """ pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy`, `VIRTUAL_PATH=/web2/` and `VIRTUAL_DEST=/` listening on port 82. @@ -47,7 +47,7 @@ def web2(docker_compose): }, ports={"82/tcp": None} ) - docker_compose.networks.get("test_default").connect(container) + docker_compose.networks.get("test_events-net").connect(container) sleep(2) # give it some time to initialize and for docker-gen to detect it yield container try: diff --git a/test/test_events/test_events.yml b/test/test_events/test_events.yml new file mode 100644 index 000000000..cd7fb8f7f --- /dev/null +++ b/test/test_events/test_events.yml @@ -0,0 +1,3 @@ +networks: + default: + name: test_events-net diff --git a/test/test_fallback.data/custom-fallback.yml b/test/test_fallback.data/custom-fallback.yml deleted file mode 100644 index a15044181..000000000 --- a/test/test_fallback.data/custom-fallback.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "2" - -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/test_fallback.data/compose.base.yml b/test/test_fallback/test_fallback.data/compose.base.yml new file mode 100644 index 000000000..36e804bc0 --- /dev/null +++ b/test/test_fallback/test_fallback.data/compose.base.yml @@ -0,0 +1,9 @@ +services: + nginx-proxy: + image: nginxproxy/nginx-proxy:test + container_name: nginx-proxy + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + ports: + - "80:80" + - "443:443" diff --git a/test/test_fallback.data/custom-fallback.conf b/test/test_fallback/test_fallback.data/custom-fallback.conf similarity index 100% rename from test/test_fallback.data/custom-fallback.conf rename to test/test_fallback/test_fallback.data/custom-fallback.conf diff --git a/test/test_fallback.data/nohttps.yml b/test/test_fallback/test_fallback.data/custom-fallback.yml similarity index 64% rename from test/test_fallback.data/nohttps.yml rename to test/test_fallback/test_fallback.data/custom-fallback.yml index c73349247..32c01b9b7 100644 --- a/test/test_fallback.data/nohttps.yml +++ b/test/test_fallback/test_fallback.data/custom-fallback.yml @@ -1,12 +1,8 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - HTTPS_METHOD: nohttps + - ${PYTEST_MODULE_PATH}/test_fallback.data/custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro http-only: image: web @@ -15,3 +11,4 @@ services: environment: WEB_PORTS: "83" VIRTUAL_HOST: http-only.nginx-proxy.test + HTTPS_METHOD: nohttps diff --git a/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt b/test/test_fallback/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt similarity index 100% rename from test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt rename to test/test_fallback/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.crt diff --git a/test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key b/test/test_fallback/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key similarity index 100% rename from test/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key rename to test/test_fallback/test_fallback.data/nodefault.certs/http-only.nginx-proxy.test.key diff --git a/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt b/test/test_fallback/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt similarity index 100% rename from test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt rename to test/test_fallback/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.crt diff --git a/test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key b/test/test_fallback/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key similarity index 100% rename from test/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key rename to test/test_fallback/test_fallback.data/nodefault.certs/https-and-http.nginx-proxy.test.key diff --git a/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt b/test/test_fallback/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt similarity index 100% rename from test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt rename to test/test_fallback/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.crt diff --git a/test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key b/test/test_fallback/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key similarity index 100% rename from test/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key rename to test/test_fallback/test_fallback.data/nodefault.certs/https-only.nginx-proxy.test.key diff --git a/test/test_fallback.data/nodefault.yml b/test/test_fallback/test_fallback.data/nodefault.yml similarity index 86% rename from test/test_fallback.data/nodefault.yml rename to test/test_fallback/test_fallback.data/nodefault.yml index 6397a0bc6..34724ce3b 100644 --- a/test/test_fallback.data/nodefault.yml +++ b/test/test_fallback/test_fallback.data/nodefault.yml @@ -1,11 +1,8 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./nodefault.certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/test_fallback.data/nodefault.certs:/etc/nginx/certs:ro https-and-http: image: web diff --git a/test/test_fallback.data/nohttp-on-app.yml b/test/test_fallback/test_fallback.data/nohttp-on-app.yml similarity index 72% rename from test/test_fallback.data/nohttp-on-app.yml rename to test/test_fallback/test_fallback.data/nohttp-on-app.yml index b6ca82a19..8e13fcb38 100644 --- a/test/test_fallback.data/nohttp-on-app.yml +++ b/test/test_fallback/test_fallback.data/nohttp-on-app.yml @@ -1,11 +1,8 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./withdefault.certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro environment: HTTPS_METHOD: redirect diff --git a/test/test_fallback.data/nohttp-with-missing-cert.yml b/test/test_fallback/test_fallback.data/nohttp-with-missing-cert.yml similarity index 52% rename from test/test_fallback.data/nohttp-with-missing-cert.yml rename to test/test_fallback/test_fallback.data/nohttp-with-missing-cert.yml index 70ece7b77..47b7d9245 100644 --- a/test/test_fallback.data/nohttp-with-missing-cert.yml +++ b/test/test_fallback/test_fallback.data/nohttp-with-missing-cert.yml @@ -1,11 +1,8 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./withdefault.certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro environment: HTTPS_METHOD: nohttp @@ -24,3 +21,13 @@ services: environment: WEB_PORTS: "84" VIRTUAL_HOST: missing-cert.nginx-proxy.test + + missing-cert-default-untrusted: + image: web + expose: + - "85" + environment: + WEB_PORTS: "85" + VIRTUAL_HOST: missing-cert.default-untrusted.nginx-proxy.test + labels: + com.github.nginx-proxy.nginx-proxy.trust-default-cert: "false" diff --git a/test/test_fallback.data/nohttp.yml b/test/test_fallback/test_fallback.data/nohttp.yml similarity index 70% rename from test/test_fallback.data/nohttp.yml rename to test/test_fallback/test_fallback.data/nohttp.yml index 9d47d51a3..5f1ec5978 100644 --- a/test/test_fallback.data/nohttp.yml +++ b/test/test_fallback/test_fallback.data/nohttp.yml @@ -1,11 +1,8 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./withdefault.certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro environment: HTTPS_METHOD: nohttp diff --git a/test/test_fallback.data/nohttps-on-app.yml b/test/test_fallback/test_fallback.data/nohttps-on-app.yml similarity index 64% rename from test/test_fallback.data/nohttps-on-app.yml rename to test/test_fallback/test_fallback.data/nohttps-on-app.yml index 966154cf9..eac59e0fe 100644 --- a/test/test_fallback.data/nohttps-on-app.yml +++ b/test/test_fallback/test_fallback.data/nohttps-on-app.yml @@ -1,10 +1,5 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro + nginx-proxy: environment: HTTPS_METHOD: redirect diff --git a/test/test_fallback/test_fallback.data/nohttps.yml b/test/test_fallback/test_fallback.data/nohttps.yml new file mode 100644 index 000000000..8a03d5ea0 --- /dev/null +++ b/test/test_fallback/test_fallback.data/nohttps.yml @@ -0,0 +1,12 @@ +services: + nginx-proxy: + 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.data/withdefault.yml b/test/test_fallback/test_fallback.data/untrusteddefault.yml similarity index 82% rename from test/test_fallback.data/withdefault.yml rename to test/test_fallback/test_fallback.data/untrusteddefault.yml index 46ab1267a..e5143e1c1 100644 --- a/test/test_fallback.data/withdefault.yml +++ b/test/test_fallback/test_fallback.data/untrusteddefault.yml @@ -1,11 +1,10 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./withdefault.certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro + environment: + TRUST_DEFAULT_CERT: "false" https-and-http: image: web diff --git a/test/test_fallback.data/withdefault.certs/default.crt b/test/test_fallback/test_fallback.data/withdefault.certs/default.crt similarity index 100% rename from test/test_fallback.data/withdefault.certs/default.crt rename to test/test_fallback/test_fallback.data/withdefault.certs/default.crt diff --git a/test/test_fallback.data/withdefault.certs/default.key b/test/test_fallback/test_fallback.data/withdefault.certs/default.key similarity index 100% rename from test/test_fallback.data/withdefault.certs/default.key rename to test/test_fallback/test_fallback.data/withdefault.certs/default.key diff --git a/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt b/test/test_fallback/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt similarity index 100% rename from test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt rename to test/test_fallback/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.crt diff --git a/test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key b/test/test_fallback/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key similarity index 100% rename from test/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key rename to test/test_fallback/test_fallback.data/withdefault.certs/http-only.nginx-proxy.test.key diff --git a/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt b/test/test_fallback/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt similarity index 100% rename from test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt rename to test/test_fallback/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.crt diff --git a/test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key b/test/test_fallback/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key similarity index 100% rename from test/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key rename to test/test_fallback/test_fallback.data/withdefault.certs/https-and-http.nginx-proxy.test.key diff --git a/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt b/test/test_fallback/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt similarity index 100% rename from test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt rename to test/test_fallback/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.crt diff --git a/test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key b/test/test_fallback/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key similarity index 100% rename from test/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key rename to test/test_fallback/test_fallback.data/withdefault.certs/https-only.nginx-proxy.test.key diff --git a/test/test_fallback/test_fallback.data/withdefault.yml b/test/test_fallback/test_fallback.data/withdefault.yml new file mode 100644 index 000000000..fe0d19ac0 --- /dev/null +++ b/test/test_fallback/test_fallback.data/withdefault.yml @@ -0,0 +1,49 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/test_fallback.data/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 + + missing-cert-default-untrusted: + image: web + expose: + - "85" + environment: + WEB_PORTS: "85" + VIRTUAL_HOST: missing-cert.default-untrusted.nginx-proxy.test + labels: + com.github.nginx-proxy.nginx-proxy.trust-default-cert: "false" diff --git a/test/test_fallback.py b/test/test_fallback/test_fallback.py similarity index 64% rename from test/test_fallback.py rename to test/test_fallback/test_fallback.py index 16da3d7d6..0375a2d75 100644 --- a/test/test_fallback.py +++ b/test/test_fallback/test_fallback.py @@ -1,39 +1,38 @@ -import os.path +import pathlib import re +from typing import List, Callable import backoff import pytest import requests +from requests import Response @pytest.fixture -def data_dir(): - return f"{os.path.splitext(__file__)[0]}.data" +def docker_compose_files(compose_file) -> List[str]: + data_dir = pathlib.Path(__file__).parent.joinpath("test_fallback.data") + return [ + data_dir.joinpath("compose.base.yml"), + data_dir.joinpath(compose_file).as_posix() + ] @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): - +def get(docker_compose, nginxproxy, want_err_re: re.Pattern[str]) -> Callable[[str], Response]: @backoff.on_exception( backoff.constant, - requests.exceptions.RequestException, - giveup=lambda e: want_err_re and want_err_re.search(str(e)), + requests.exceptions.SSLError, + giveup=lambda e: want_err_re and bool(want_err_re.search(str(e))), interval=.3, max_tries=30, jitter=None) - def _get(url): + def _get(url) -> Response: return nginxproxy.get(url, allow_redirects=False) return _get -INTERNAL_ERR_RE = re.compile("TLSV1_ALERT_INTERNAL_ERROR") -CONNECTION_REFUSED_RE = re.compile("Connection refused") +INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME") @pytest.mark.parametrize("compose_file,url,want_code,want_err_re", [ @@ -44,10 +43,23 @@ def _get(url): ("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://missing-cert.nginx-proxy.test/", 301, None), + ("withdefault.yml", "https://missing-cert.nginx-proxy.test/", 200, None), + ("withdefault.yml", "http://missing-cert.default-untrusted.nginx-proxy.test/", 200, None), + ("withdefault.yml", "https://missing-cert.default-untrusted.nginx-proxy.test/", None, INTERNAL_ERR_RE), ("withdefault.yml", "http://unknown.nginx-proxy.test/", 503, None), ("withdefault.yml", "https://unknown.nginx-proxy.test/", 503, None), + # Same as withdefault.yml, except default.crt is not trusted (TRUST_DEFAULT_CERT=false). + ("untrusteddefault.yml", "http://https-and-http.nginx-proxy.test/", 301, None), + ("untrusteddefault.yml", "https://https-and-http.nginx-proxy.test/", 200, None), + ("untrusteddefault.yml", "http://https-only.nginx-proxy.test/", 503, None), + ("untrusteddefault.yml", "https://https-only.nginx-proxy.test/", 200, None), + ("untrusteddefault.yml", "http://http-only.nginx-proxy.test/", 200, None), + ("untrusteddefault.yml", "https://http-only.nginx-proxy.test/", 503, None), + ("untrusteddefault.yml", "http://missing-cert.nginx-proxy.test/", 200, None), + ("untrusteddefault.yml", "https://missing-cert.nginx-proxy.test/", None, INTERNAL_ERR_RE), + ("untrusteddefault.yml", "http://unknown.nginx-proxy.test/", 503, None), + ("untrusteddefault.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), @@ -69,34 +81,37 @@ def _get(url): ("nohttp-on-app.yml", "https://https-only.nginx-proxy.test/", 200, None), ("nohttp-on-app.yml", "http://unknown.nginx-proxy.test/", 503, None), ("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 + # Same as nohttp.yml, except there are two vhosts with a missing cert, the second + # one being configured not to trust the default certificate. This causes its # HTTPS_METHOD=nohttp setting to effectively become HTTPS_METHOD=noredirect. ("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://missing-cert.nginx-proxy.test/", 503, None), + ("nohttp-with-missing-cert.yml", "https://missing-cert.nginx-proxy.test/", 200, None), + ("nohttp-with-missing-cert.yml", "http://missing-cert.default-untrusted.nginx-proxy.test/", 200, None), + ("nohttp-with-missing-cert.yml", "https://missing-cert.default-untrusted.nginx-proxy.test/", None, INTERNAL_ERR_RE), ("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", "https://http-only.nginx-proxy.test/", None, INTERNAL_ERR_RE), ("nohttps.yml", "http://unknown.nginx-proxy.test/", 503, None), - ("nohttps.yml", "https://unknown.nginx-proxy.test/", None, CONNECTION_REFUSED_RE), + ("nohttps.yml", "https://unknown.nginx-proxy.test/", None, INTERNAL_ERR_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", "https://http-only.nginx-proxy.test/", None, INTERNAL_ERR_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), + ("nohttps-on-app.yml", "https://unknown.nginx-proxy.test/", None, INTERNAL_ERR_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): +def test_fallback(get, compose_file, 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): + with pytest.raises(requests.exceptions.SSLError, match=want_err_re): get(url) diff --git a/test/test_headers/certs/default.crt b/test/test_headers/certs/default.crt new file mode 100644 index 000000000..aed93498c --- /dev/null +++ b/test/test_headers/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: 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_headers/certs/default.key b/test/test_headers/certs/default.key new file mode 100644 index 000000000..8365ecfd0 --- /dev/null +++ b/test/test_headers/certs/default.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_headers/test_http.yml b/test/test_headers/test_http.yml index fa5984028..41d0c309b 100644 --- a/test/test_headers/test_http.yml +++ b/test/test_headers/test_http.yml @@ -1,12 +1,10 @@ -version: "2" - services: web: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: web.nginx-proxy.tld web-server-tokens-off: @@ -14,11 +12,6 @@ services: expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld SERVER_TOKENS: "off" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_headers/test_https.yml b/test/test_headers/test_https.yml index 0300d4386..d3a96c6b1 100644 --- a/test/test_headers/test_https.yml +++ b/test/test_headers/test_https.yml @@ -1,12 +1,15 @@ -version: "2" - services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro + web: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: web.nginx-proxy.tld web-server-tokens-off: @@ -14,17 +17,6 @@ services: expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld SERVER_TOKENS: "off" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs/web.nginx-proxy.tld.crt:/etc/nginx/certs/default.crt:ro - - ./certs/web.nginx-proxy.tld.key:/etc/nginx/certs/default.key: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 - - ./certs/web-server-tokens-off.nginx-proxy.tld.crt:/etc/nginx/certs/web-server-tokens-off.nginx-proxy.tld.crt:ro - - ./certs/web-server-tokens-off.nginx-proxy.tld.key:/etc/nginx/certs/web-server-tokens-off.nginx-proxy.tld.key:ro diff --git a/test/test_host-network-mode/test_host-network-mode.py b/test/test_host-network-mode/test_host-network-mode.py index 0c2134810..c80fbf0d4 100644 --- a/test/test_host-network-mode/test_host-network-mode.py +++ b/test/test_host-network-mode/test_host-network-mode.py @@ -1,6 +1,3 @@ -import pytest - - def test_forwards_to_bridge_network_container(docker_compose, nginxproxy): r = nginxproxy.get("http://bridge-network.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_host-network-mode/test_host-network-mode.yml b/test/test_host-network-mode/test_host-network-mode.yml index 1f2769c7d..452c73c74 100644 --- a/test/test_host-network-mode/test_host-network-mode.yml +++ b/test/test_host-network-mode/test_host-network-mode.yml @@ -1,11 +1,14 @@ -version: "2" - networks: net1: internal: true net2: services: + nginx-proxy: + networks: + - net1 + - net2 + bridge-network: image: web environment: @@ -21,11 +24,3 @@ services: VIRTUAL_HOST: "host-network.nginx-proxy.tld" VIRTUAL_PORT: "8080" network_mode: host - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - networks: - - net1 - - net2 diff --git a/test/test_host-network-mode/test_proxy-host-network-mode.yml b/test/test_host-network-mode/test_proxy-host-network-mode.base.yml similarity index 91% rename from test/test_host-network-mode/test_proxy-host-network-mode.yml rename to test/test_host-network-mode/test_proxy-host-network-mode.base.yml index 32106e2eb..1917d062b 100644 --- a/test/test_host-network-mode/test_proxy-host-network-mode.yml +++ b/test/test_host-network-mode/test_proxy-host-network-mode.base.yml @@ -1,6 +1,13 @@ -version: "2" - services: + nginx-proxy: + image: nginxproxy/nginx-proxy:test + container_name: nginx-proxy + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + HTTP_PORT: 8888 + network_mode: host + host-network-1: image: web environment: @@ -16,11 +23,3 @@ services: VIRTUAL_HOST: "host-network-2.nginx-proxy.tld" VIRTUAL_PORT: "8181" network_mode: host - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - HTTP_PORT: 8888 - network_mode: host diff --git a/test/test_host-network-mode/test_proxy-host-network-mode.py b/test/test_host-network-mode/test_proxy-host-network-mode.py index 330334b8b..5e6e72bfb 100644 --- a/test/test_host-network-mode/test_proxy-host-network-mode.py +++ b/test/test_host-network-mode/test_proxy-host-network-mode.py @@ -1,5 +1,15 @@ +# Note: on Docker Desktop, host networking must be manually enabled. +# See https://docs.docker.com/engine/network/drivers/host/ +import os + import pytest +PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1" + +pytestmark = pytest.mark.skipif( + PYTEST_RUNNING_IN_CONTAINER, + reason="Connecting to host network not supported when pytest is running in container" +) def test_forwards_to_host_network_container_1(docker_compose, nginxproxy): r = nginxproxy.get("http://host-network-1.nginx-proxy.tld:8888/port") diff --git a/test/test_htpasswd/compose.base.override.yml b/test/test_htpasswd/compose.base.override.yml new file mode 100644 index 000000000..f289bd3b8 --- /dev/null +++ b/test/test_htpasswd/compose.base.override.yml @@ -0,0 +1,5 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/htpasswd:/etc/nginx/htpasswd:ro diff --git a/test/test_htpasswd/htpasswd/561032515ede3ab3a015edfb244608b72409c430 b/test/test_htpasswd/htpasswd/561032515ede3ab3a015edfb244608b72409c430 new file mode 100644 index 000000000..336275a1b --- /dev/null +++ b/test/test_htpasswd/htpasswd/561032515ede3ab3a015edfb244608b72409c430 @@ -0,0 +1 @@ +vhost:$2a$13$/aPYmoK0mmgyAI4TpKdFY.6441Ugo39MdXjhpm.Pp6D15rbz9tvz. diff --git a/test/test_htpasswd/test_htpasswd-regex-virtual-host.py b/test/test_htpasswd/test_htpasswd-regex-virtual-host.py new file mode 100644 index 000000000..b82055f62 --- /dev/null +++ b/test/test_htpasswd/test_htpasswd-regex-virtual-host.py @@ -0,0 +1,11 @@ +def test_htpasswd_regex_virtual_host_is_restricted(docker_compose, nginxproxy): + r = nginxproxy.get("http://regex.htpasswd.nginx-proxy.example/port") + assert r.status_code == 401 + assert "WWW-Authenticate" in r.headers + assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted access"' + + +def test_htpasswd_regex_virtual_host_basic_auth(docker_compose, nginxproxy): + r = nginxproxy.get("http://regex.htpasswd.nginx-proxy.example/port", auth=("vhost", "password")) + assert r.status_code == 200 + assert r.text == "answer from port 80\n" diff --git a/test/test_htpasswd/test_htpasswd-regex-virtual-host.yml b/test/test_htpasswd/test_htpasswd-regex-virtual-host.yml new file mode 100644 index 000000000..9eac0d182 --- /dev/null +++ b/test/test_htpasswd/test_htpasswd-regex-virtual-host.yml @@ -0,0 +1,8 @@ +services: + regex: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$ # we need to double the `$` because of docker compose variable interpolation diff --git a/test/test_htpasswd/test_htpasswd_virtual_host.py b/test/test_htpasswd/test_htpasswd-virtual-host.py similarity index 97% rename from test/test_htpasswd/test_htpasswd_virtual_host.py rename to test/test_htpasswd/test_htpasswd-virtual-host.py index aff3a62ad..cfb9934cc 100644 --- a/test/test_htpasswd/test_htpasswd_virtual_host.py +++ b/test/test_htpasswd/test_htpasswd-virtual-host.py @@ -1,5 +1,3 @@ -import pytest - def test_htpasswd_virtual_host_is_restricted(docker_compose, nginxproxy): r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/port") assert r.status_code == 401 diff --git a/test/test_htpasswd/test_htpasswd-virtual-host.yml b/test/test_htpasswd/test_htpasswd-virtual-host.yml new file mode 100644 index 000000000..952d1dd20 --- /dev/null +++ b/test/test_htpasswd/test_htpasswd-virtual-host.yml @@ -0,0 +1,8 @@ +services: + web: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: htpasswd.nginx-proxy.tld diff --git a/test/test_htpasswd/test_htpasswd_virtual_path.py b/test/test_htpasswd/test_htpasswd-virtual-path.py similarity index 97% rename from test/test_htpasswd/test_htpasswd_virtual_path.py rename to test/test_htpasswd/test_htpasswd-virtual-path.py index 262b31472..da5b4fbda 100644 --- a/test/test_htpasswd/test_htpasswd_virtual_path.py +++ b/test/test_htpasswd/test_htpasswd-virtual-path.py @@ -1,5 +1,3 @@ -import pytest - def test_htpasswd_virtual_path_is_restricted(docker_compose, nginxproxy): r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/foo/port") assert r.status_code == 401 diff --git a/test/test_htpasswd/test_htpasswd-virtual-path.yml b/test/test_htpasswd/test_htpasswd-virtual-path.yml new file mode 100644 index 000000000..ad7d79208 --- /dev/null +++ b/test/test_htpasswd/test_htpasswd-virtual-path.yml @@ -0,0 +1,10 @@ +services: + web: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: htpasswd.nginx-proxy.tld + VIRTUAL_PATH: /foo/ + VIRTUAL_DEST: / diff --git a/test/test_htpasswd/test_htpasswd_virtual_host.yml b/test/test_htpasswd/test_htpasswd_virtual_host.yml deleted file mode 100644 index b3f15df9e..000000000 --- a/test/test_htpasswd/test_htpasswd_virtual_host.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "2" - -services: - web: - image: web - expose: - - "80" - environment: - WEB_PORTS: 80 - VIRTUAL_HOST: htpasswd.nginx-proxy.tld - - sut: - container_name: sut - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./htpasswd:/etc/nginx/htpasswd:ro diff --git a/test/test_htpasswd/test_htpasswd_virtual_path.yml b/test/test_htpasswd/test_htpasswd_virtual_path.yml deleted file mode 100644 index ffe1a0858..000000000 --- a/test/test_htpasswd/test_htpasswd_virtual_path.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: "2" - -services: - web: - image: web - expose: - - "80" - environment: - WEB_PORTS: 80 - VIRTUAL_HOST: htpasswd.nginx-proxy.tld - VIRTUAL_PATH: /foo/ - VIRTUAL_DEST: / - - sut: - container_name: sut - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./htpasswd:/etc/nginx/htpasswd:ro diff --git a/test/test_http_port.py b/test/test_http-port/test_http-port.py similarity index 100% rename from test/test_http_port.py rename to test/test_http-port/test_http-port.py diff --git a/test/test_http_port.yml b/test/test_http-port/test_http-port.yml similarity index 59% rename from test/test_http_port.yml rename to test/test_http-port/test_http-port.yml index a346982c3..f1203eec0 100644 --- a/test/test_http_port.yml +++ b/test/test_http-port/test_http-port.yml @@ -1,6 +1,10 @@ -version: "2" - services: + nginx-proxy: + environment: + HTTP_PORT: 8080 + ports: + - "8080:8080" + web1: image: web expose: @@ -8,10 +12,3 @@ services: environment: WEB_PORTS: "81" VIRTUAL_HOST: "*.nginx-proxy.tld" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - HTTP_PORT: 8080 diff --git a/test/test_http2/test_http2_global_disabled.py b/test/test_http2/test_http2-global-disabled.py similarity index 95% rename from test/test_http2/test_http2_global_disabled.py rename to test/test_http2/test_http2-global-disabled.py index 42e102d12..e09960378 100644 --- a/test/test_http2/test_http2_global_disabled.py +++ b/test/test_http2/test_http2-global-disabled.py @@ -1,6 +1,6 @@ -import pytest import re + def test_http2_global_disabled_config(docker_compose, nginxproxy): conf = nginxproxy.get_conf().decode('ASCII') r = nginxproxy.get("http://http2-global-disabled.nginx-proxy.tld") diff --git a/test/test_http2/test_http2_global_disabled.yml b/test/test_http2/test_http2-global-disabled.yml similarity index 57% rename from test/test_http2/test_http2_global_disabled.yml rename to test/test_http2/test_http2-global-disabled.yml index 8fd3cde63..f37755755 100644 --- a/test/test_http2/test_http2_global_disabled.yml +++ b/test/test_http2/test_http2-global-disabled.yml @@ -1,17 +1,12 @@ -version: "2" - services: + nginx-proxy: + environment: + ENABLE_HTTP2: "false" + http2-global-disabled: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: http2-global-disabled.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - ENABLE_HTTP2: "false" diff --git a/test/test_http3/test_http3_global_disabled.py b/test/test_http3/test_http3-global-disabled.py similarity index 83% rename from test/test_http3/test_http3_global_disabled.py rename to test/test_http3/test_http3-global-disabled.py index 508823e6b..4f9d60a5f 100644 --- a/test/test_http3/test_http3_global_disabled.py +++ b/test/test_http3/test_http3-global-disabled.py @@ -1,8 +1,8 @@ -import pytest import re - #Python Requests is not able to do native http3 requests. - #We only check for directives which should enable http3. + +# Python Requests is not able to do native http3 requests. +# We only check for directives which should enable http3. def test_http3_global_disabled_ALTSVC_header(docker_compose, nginxproxy): r = nginxproxy.get("http://http3-global-disabled.nginx-proxy.tld/headers") diff --git a/test/test_http3/test_http3-global-disabled.yml b/test/test_http3/test_http3-global-disabled.yml new file mode 100644 index 000000000..9b49841b2 --- /dev/null +++ b/test/test_http3/test_http3-global-disabled.yml @@ -0,0 +1,12 @@ +services: +# nginx-proxy: +# environment: +# ENABLE_HTTP3: "false" #Disabled by default + + http3-global-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: http3-global-disabled.nginx-proxy.tld diff --git a/test/test_http3/test_http3_global_enabled.py b/test/test_http3/test_http3-global-enabled.py similarity index 87% rename from test/test_http3/test_http3_global_enabled.py rename to test/test_http3/test_http3-global-enabled.py index c678ab6b5..0efc2e388 100644 --- a/test/test_http3/test_http3_global_enabled.py +++ b/test/test_http3/test_http3-global-enabled.py @@ -1,8 +1,8 @@ -import pytest import re - #Python Requests is not able to do native http3 requests. - #We only check for directives which should enable http3. + +# Python Requests is not able to do native http3 requests. +# We only check for directives which should enable http3. def test_http3_global_enabled_ALTSVC_header(docker_compose, nginxproxy): r = nginxproxy.get("http://http3-global-enabled.nginx-proxy.tld/headers") diff --git a/test/test_http3/test_http3_global_enabled.yml b/test/test_http3/test_http3-global-enabled.yml similarity index 57% rename from test/test_http3/test_http3_global_enabled.yml rename to test/test_http3/test_http3-global-enabled.yml index 1cd40e0a0..46697c5ac 100644 --- a/test/test_http3/test_http3_global_enabled.yml +++ b/test/test_http3/test_http3-global-enabled.yml @@ -1,17 +1,12 @@ -version: "2" - services: + nginx-proxy: + environment: + ENABLE_HTTP3: "true" + http3-global-enabled: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: http3-global-enabled.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - ENABLE_HTTP3: "true" diff --git a/test/test_http3/test_http3_vhost.py b/test/test_http3/test_http3-vhost.py similarity index 95% rename from test/test_http3/test_http3_vhost.py rename to test/test_http3/test_http3-vhost.py index 93a217cab..85d90d205 100644 --- a/test/test_http3/test_http3_vhost.py +++ b/test/test_http3/test_http3-vhost.py @@ -1,8 +1,8 @@ -import pytest import re - #Python Requests is not able to do native http3 requests. - #We only check for directives which should enable http3. + +# Python Requests is not able to do native http3 requests. +# We only check for directives which should enable http3. def test_http3_vhost_enabled_ALTSVC_header(docker_compose, nginxproxy): r = nginxproxy.get("http://http3-vhost-enabled.nginx-proxy.tld/headers") diff --git a/test/test_http3/test_http3_vhost.yml b/test/test_http3/test_http3-vhost.yml similarity index 76% rename from test/test_http3/test_http3_vhost.yml rename to test/test_http3/test_http3-vhost.yml index 345bb30e5..afa330e57 100644 --- a/test/test_http3/test_http3_vhost.yml +++ b/test/test_http3/test_http3-vhost.yml @@ -1,12 +1,14 @@ -version: "2" - services: +# nginx-proxy: +# environment: +# ENABLE_HTTP3: "false" #Disabled by default + http3-vhost-enabled: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: http3-vhost-enabled.nginx-proxy.tld labels: com.github.nginx-proxy.nginx-proxy.http3.enable: "true" @@ -16,7 +18,7 @@ services: expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: http3-vhost-disabled.nginx-proxy.tld labels: com.github.nginx-proxy.nginx-proxy.http3.enable: "false" @@ -26,10 +28,5 @@ services: expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: http3-vhost-default-disabled.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_http3/test_http3_global_disabled.yml b/test/test_http3/test_http3_global_disabled.yml deleted file mode 100644 index 482a5eaea..000000000 --- a/test/test_http3/test_http3_global_disabled.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "2" - -services: - http3-global-disabled: - image: web - expose: - - "80" - environment: - WEB_PORTS: 80 - VIRTUAL_HOST: http3-global-disabled.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - #environment: - #ENABLE_HTTP3: "false" #Disabled by default diff --git a/test/test_internal/compose.base.override.yml b/test/test_internal/compose.base.override.yml new file mode 100644 index 000000000..c455021e4 --- /dev/null +++ b/test/test_internal/compose.base.override.yml @@ -0,0 +1,5 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/network_internal.conf:/etc/nginx/network_internal.conf:ro diff --git a/test/test_internal/test_internal-per-vhost.py b/test/test_internal/test_internal-per-vhost.py index e64cc62e1..04649e262 100644 --- a/test/test_internal/test_internal-per-vhost.py +++ b/test/test_internal/test_internal-per-vhost.py @@ -1,5 +1,3 @@ -import pytest - def test_network_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy.example/port") assert r.status_code == 200 diff --git a/test/test_internal/test_internal-per-vhost.yml b/test/test_internal/test_internal-per-vhost.yml index cc67c2240..3b0399f68 100644 --- a/test/test_internal/test_internal-per-vhost.yml +++ b/test/test_internal/test_internal-per-vhost.yml @@ -1,12 +1,10 @@ -version: "2" - services: web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example NETWORK_ACCESS: internal @@ -15,11 +13,5 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.example - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./network_internal.conf:/etc/nginx/network_internal.conf:ro diff --git a/test/test_internal/test_internal-per-vpath.py b/test/test_internal/test_internal-per-vpath.py index def806c12..f788fa518 100644 --- a/test/test_internal/test_internal-per-vpath.py +++ b/test/test_internal/test_internal-per-vpath.py @@ -1,5 +1,3 @@ -import pytest - def test_network_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy.example/web1/port") assert r.status_code == 200 diff --git a/test/test_internal/test_internal-per-vpath.yml b/test/test_internal/test_internal-per-vpath.yml index 35e92f051..301a756f9 100644 --- a/test/test_internal/test_internal-per-vpath.yml +++ b/test/test_internal/test_internal-per-vpath.yml @@ -1,12 +1,10 @@ -version: "2" - services: web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: nginx-proxy.example VIRTUAL_PATH: /web1/ VIRTUAL_DEST: / @@ -17,13 +15,8 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: nginx-proxy.example VIRTUAL_PATH: /web2/ VIRTUAL_DEST: / - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./network_internal.conf:/etc/nginx/network_internal.conf:ro diff --git a/test/test_ipv6/test_ipv6-prefer-ipv4-network.py b/test/test_ipv6/test_ipv6-prefer-ipv4-network.py new file mode 100644 index 000000000..f11c797da --- /dev/null +++ b/test/test_ipv6/test_ipv6-prefer-ipv4-network.py @@ -0,0 +1,16 @@ +def test_forwards_to_ipv4_only_network(docker_compose, nginxproxy): + r = nginxproxy.get("http://ipv4only.nginx-proxy.tld/port") + assert r.status_code == 200 + assert r.text == "answer from port 80\n" + + +def test_forwards_to_dualstack_network(docker_compose, nginxproxy): + r = nginxproxy.get("http://dualstack.nginx-proxy.tld") + assert r.status_code == 200 + assert "Welcome to nginx!" in r.text + + +def test_dualstack_network_prefer_ipv4_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + assert "IPv6 address: fd00:cafe:face:feed::2 (ignored; reachable but IPv4 prefered)" in conf + assert "server 172.16.20.2:80;" in conf diff --git a/test/test_ipv6/test_ipv6-prefer-ipv4-network.yml b/test/test_ipv6/test_ipv6-prefer-ipv4-network.yml new file mode 100644 index 000000000..8691ecf96 --- /dev/null +++ b/test/test_ipv6/test_ipv6-prefer-ipv4-network.yml @@ -0,0 +1,41 @@ +networks: + ipv4net: + ipam: + config: + - subnet: 172.16.10.0/24 + dualstacknet: + enable_ipv6: true + ipam: + config: + - subnet: 172.16.20.0/24 + - subnet: fd00:cafe:face:feed::/64 + +services: + nginx-proxy: + networks: + ipv4net: + ipv4_address: 172.16.10.3 + dualstacknet: + ipv4_address: 172.16.20.3 + ipv6_address: fd00:cafe:face:feed::3 + + ipv4only: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: ipv4only.nginx-proxy.tld + networks: + ipv4net: + ipv4_address: 172.16.10.2 + + dualstack: + image: nginx:alpine + environment: + VIRTUAL_HOST: dualstack.nginx-proxy.tld + networks: + dualstacknet: + ipv4_address: 172.16.20.2 + ipv6_address: fd00:cafe:face:feed::2 + diff --git a/test/test_ipv6/test_ipv6-prefer-ipv6-network.py b/test/test_ipv6/test_ipv6-prefer-ipv6-network.py new file mode 100644 index 000000000..3e1988d6b --- /dev/null +++ b/test/test_ipv6/test_ipv6-prefer-ipv6-network.py @@ -0,0 +1,16 @@ +def test_forwards_to_ipv4_only_network(docker_compose, nginxproxy): + r = nginxproxy.get("http://ipv4only.nginx-proxy.tld/port") + assert r.status_code == 200 + assert r.text == "answer from port 80\n" + + +def test_forwards_to_dualstack_network(docker_compose, nginxproxy): + r = nginxproxy.get("http://dualstack.nginx-proxy.tld") + assert r.status_code == 200 + assert "Welcome to nginx!" in r.text + + +def test_dualstack_network_prefer_ipv6_config(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + assert "IPv4 address: 172.16.20.2 (ignored; reachable but IPv6 prefered)" in conf + assert "server [fd00:cafe:face:feed::2]:80;" in conf diff --git a/test/test_ipv6/test_ipv6-prefer-ipv6-network.yml b/test/test_ipv6/test_ipv6-prefer-ipv6-network.yml new file mode 100644 index 000000000..54925be24 --- /dev/null +++ b/test/test_ipv6/test_ipv6-prefer-ipv6-network.yml @@ -0,0 +1,43 @@ +networks: + ipv4net: + ipam: + config: + - subnet: 172.16.10.0/24 + dualstacknet: + enable_ipv6: true + ipam: + config: + - subnet: 172.16.20.0/24 + - subnet: fd00:cafe:face:feed::/64 + +services: + nginx-proxy: + environment: + PREFER_IPV6_NETWORK: "true" + networks: + ipv4net: + ipv4_address: 172.16.10.3 + dualstacknet: + ipv4_address: 172.16.20.3 + ipv6_address: fd00:cafe:face:feed::3 + + ipv4only: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: ipv4only.nginx-proxy.tld + networks: + ipv4net: + ipv4_address: 172.16.10.2 + + dualstack: + image: nginx:alpine + environment: + VIRTUAL_HOST: dualstack.nginx-proxy.tld + networks: + dualstacknet: + ipv4_address: 172.16.20.2 + ipv6_address: fd00:cafe:face:feed::2 + diff --git a/test/test_ipv6.py b/test/test_ipv6/test_ipv6.py similarity index 86% rename from test/test_ipv6.py rename to test/test_ipv6/test_ipv6.py index 36bf653af..914dd4eb4 100644 --- a/test/test_ipv6.py +++ b/test/test_ipv6/test_ipv6.py @@ -1,5 +1,12 @@ +import platform + import pytest +pytestmark = pytest.mark.skipif( + platform.system() == "Darwin", + reason="Those tests rely entirely on being able to directly contact the container's IP" +) + def test_unknown_virtual_host_ipv4(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/port") diff --git a/test/test_ipv6.yml b/test/test_ipv6/test_ipv6.yml similarity index 73% rename from test/test_ipv6.yml rename to test/test_ipv6/test_ipv6.yml index d5fec9b40..b391ff5a9 100644 --- a/test/test_ipv6.yml +++ b/test/test_ipv6/test_ipv6.yml @@ -1,5 +1,3 @@ -version: "2" - networks: net1: enable_ipv6: true @@ -8,12 +6,18 @@ networks: - subnet: fd00:1::/80 services: + nginx-proxy: + environment: + ENABLE_IPV6: "true" + networks: + - net1 + web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.tld networks: - net1 @@ -23,16 +27,8 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.tld networks: - net1 - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - ENABLE_IPV6: "true" - networks: - - net1 diff --git a/test/test_keepalive.py b/test/test_keepalive/test_keepalive.py similarity index 100% rename from test/test_keepalive.py rename to test/test_keepalive/test_keepalive.py diff --git a/test/test_keepalive.yml b/test/test_keepalive/test_keepalive.yml similarity index 70% rename from test/test_keepalive.yml rename to test/test_keepalive/test_keepalive.yml index 6da66f0b3..3ec3f1e17 100644 --- a/test/test_keepalive.yml +++ b/test/test_keepalive/test_keepalive.yml @@ -1,24 +1,28 @@ -version: "2" - services: + nginx-proxy: + environment: + HTTPS_METHOD: nohttps + keepalive-disabled: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: keepalive-disabled.nginx-proxy.test + labels: + com.github.nginx-proxy.nginx-proxy.keepalive: "disabled" keepalive-enabled: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: keepalive-enabled.nginx-proxy.test labels: com.github.nginx-proxy.nginx-proxy.keepalive: "64" - + keepalive-auto: image: web deploy: @@ -27,14 +31,6 @@ services: expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: keepalive-auto.nginx-proxy.test - labels: - com.github.nginx-proxy.nginx-proxy.keepalive: "auto" - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - HTTPS_METHOD: nohttps diff --git a/test/test_loadbalancing.py b/test/test_loadbalancing/test_loadbalancing.py similarity index 95% rename from test/test_loadbalancing.py rename to test/test_loadbalancing/test_loadbalancing.py index 4b43aa525..08f853775 100644 --- a/test/test_loadbalancing.py +++ b/test/test_loadbalancing/test_loadbalancing.py @@ -1,16 +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 +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/test_loadbalancing.yml similarity index 69% rename from test/test_loadbalancing.yml rename to test/test_loadbalancing/test_loadbalancing.yml index 72155fb11..1ffec1f54 100644 --- a/test/test_loadbalancing.yml +++ b/test/test_loadbalancing/test_loadbalancing.yml @@ -1,29 +1,22 @@ -version: "2" - -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 +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 diff --git a/test/test_location-override.py b/test/test_location-override/test_location-override.py similarity index 100% rename from test/test_location-override.py rename to test/test_location-override/test_location-override.py diff --git a/test/test_location-override.yml b/test/test_location-override/test_location-override.yml similarity index 89% rename from test/test_location-override.yml rename to test/test_location-override/test_location-override.yml index 9ba4eb82b..00bac6fce 100644 --- a/test/test_location-override.yml +++ b/test/test_location-override/test_location-override.yml @@ -1,11 +1,8 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./test_location-override.vhost.d:/etc/nginx/vhost.d:ro + - ${PYTEST_MODULE_PATH}/vhost.d:/etc/nginx/vhost.d:ro explicit-root: image: web 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 similarity index 100% rename from test/test_location-override.vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override rename to test/test_location-override/vhost.d/explicit-nonroot.nginx-proxy.test_8d960560c82f4e6c8b1b0f03eb30a1afd00e5696_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override rename to test/test_location-override/vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override rename to test/test_location-override/vhost.d/explicit-root-hash-and-nohash.nginx-proxy.test_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override rename to test/test_location-override/vhost.d/explicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/explicit-root-nohash.nginx-proxy.test_location_override rename to test/test_location-override/vhost.d/explicit-root-nohash.nginx-proxy.test_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override rename to test/test_location-override/vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override rename to test/test_location-override/vhost.d/implicit-root-hash-and-nohash.nginx-proxy.test_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override rename to test/test_location-override/vhost.d/implicit-root-hash.nginx-proxy.test_42099b4af021e53fd8fd4e056c2568d7c2e3ffa8_location_override 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 similarity index 100% rename from test/test_location-override.vhost.d/implicit-root-nohash.nginx-proxy.test_location_override rename to test/test_location-override/vhost.d/implicit-root-nohash.nginx-proxy.test_location_override diff --git a/test/test_logs/test_log-disabled.py b/test/test_logs/test_log-disabled.py new file mode 100644 index 000000000..ce0782d00 --- /dev/null +++ b/test/test_logs/test_log-disabled.py @@ -0,0 +1,9 @@ +def test_log_disabled(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("nginx-proxy") + 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 len(docker_logs) == 0 diff --git a/test/test_logs/test_log-disabled.yml b/test/test_logs/test_log-disabled.yml new file mode 100644 index 000000000..14b8e8b32 --- /dev/null +++ b/test/test_logs/test_log-disabled.yml @@ -0,0 +1,12 @@ +services: + nginx-proxy: + environment: + DISABLE_ACCESS_LOGS: "true" + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: nginx-proxy.test diff --git a/test/test_logs/test_log_format.py b/test/test_logs/test_log-format.py similarity index 86% rename from test/test_logs/test_log_format.py rename to test/test_logs/test_log-format.py index 589f0c70b..f65b11e6f 100644 --- a/test/test_logs/test_log_format.py +++ b/test/test_logs/test_log-format.py @@ -1,10 +1,8 @@ -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") + sut_container = docker_compose.containers.get("nginx-proxy") 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] diff --git a/test/test_logs/test_log_format.yml b/test/test_logs/test_log-format.yml similarity index 66% rename from test/test_logs/test_log_format.yml rename to test/test_logs/test_log-format.yml index 2a32e3d00..fba270cb1 100644 --- a/test/test_logs/test_log_format.yml +++ b/test/test_logs/test_log-format.yml @@ -1,18 +1,12 @@ -version: "2" - services: + nginx-proxy: + 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' + web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + 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' diff --git a/test/test_logs/test_log_json_format.py b/test/test_logs/test_log-json-format.py similarity index 90% rename from test/test_logs/test_log_json_format.py rename to test/test_logs/test_log-json-format.py index 2d158cb7c..dd473f706 100644 --- a/test/test_logs/test_log_json_format.py +++ b/test/test_logs/test_log-json-format.py @@ -1,5 +1,3 @@ -import pytest - def test_log_json_format(docker_compose, nginxproxy): log_conf = [line for line in nginxproxy.get_conf().decode('ASCII').splitlines() if "log_format vhost escape=" in line] assert "{\"time_local\":\"$time_iso8601\"," in log_conf[0] @@ -7,7 +5,7 @@ def test_log_json_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") + sut_container = docker_compose.containers.get("nginx-proxy") 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 "{\"time_local\":" in line] diff --git a/test/test_logs/test_log_json_format.yml b/test/test_logs/test_log-json-format.yml similarity index 61% rename from test/test_logs/test_log_json_format.yml rename to test/test_logs/test_log-json-format.yml index 5e01e924f..f0c887882 100644 --- a/test/test_logs/test_log_json_format.yml +++ b/test/test_logs/test_log-json-format.yml @@ -1,18 +1,12 @@ -version: "2" - services: + nginx-proxy: + environment: + LOG_FORMAT: '{"time_local":"$$time_iso8601","remote_addr":"$$remote_addr","request":"$$request","upstream_addr":"$$upstream_addr"}' + web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + 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: '{"time_local":"$$time_iso8601","remote_addr":"$$remote_addr","request":"$$request","upstream_addr":"$$upstream_addr"}' diff --git a/test/test_logs/test_log_json.py b/test/test_logs/test_log-json.py similarity index 89% rename from test/test_logs/test_log_json.py rename to test/test_logs/test_log-json.py index 1a04b228c..7931623b2 100644 --- a/test/test_logs/test_log_json.py +++ b/test/test_logs/test_log-json.py @@ -1,5 +1,3 @@ -import pytest - def test_log_json(docker_compose, nginxproxy): log_conf = [line for line in nginxproxy.get_conf().decode('ASCII').splitlines() if "log_format vhost escape=" in line] assert "{\"time_local\":\"$time_iso8601\"," in log_conf[0] @@ -7,7 +5,7 @@ def test_log_json(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") + sut_container = docker_compose.containers.get("nginx-proxy") 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 "{\"time_local\":" in line] diff --git a/test/test_logs/test_log-json.yml b/test/test_logs/test_log-json.yml new file mode 100644 index 000000000..db48b53ed --- /dev/null +++ b/test/test_logs/test_log-json.yml @@ -0,0 +1,12 @@ +services: + nginx-proxy: + environment: + LOG_JSON: 1 + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: nginx-proxy.test diff --git a/test/test_logs/test_log_json.yml b/test/test_logs/test_log_json.yml deleted file mode 100644 index 2e6fefdba..000000000 --- a/test/test_logs/test_log_json.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: "2" - -services: - 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_JSON: 1 diff --git a/test/test_multiple-hosts.yml b/test/test_multiple-hosts.yml deleted file mode 100644 index 832aac4cf..000000000 --- a/test/test_multiple-hosts.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: "2" - -services: - web: - image: web - expose: - - "81" - environment: - WEB_PORTS: 81 - VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_multiple-hosts.py b/test/test_multiple-hosts/test_multiple-hosts.py similarity index 97% rename from test/test_multiple-hosts.py rename to test/test_multiple-hosts/test_multiple-hosts.py index 76e7de67c..211930ccb 100644 --- a/test/test_multiple-hosts.py +++ b/test/test_multiple-hosts/test_multiple-hosts.py @@ -1,6 +1,3 @@ -import pytest - - def test_unknown_virtual_host_is_503(docker_compose, nginxproxy): r = nginxproxy.get("http://unknown.nginx-proxy.tld/port") assert r.status_code == 503 diff --git a/test/test_multiple-hosts/test_multiple-hosts.yml b/test/test_multiple-hosts/test_multiple-hosts.yml new file mode 100644 index 000000000..c726bb45d --- /dev/null +++ b/test/test_multiple-hosts/test_multiple-hosts.yml @@ -0,0 +1,8 @@ +services: + web: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld diff --git a/test/test_multiple-networks.py b/test/test_multiple-networks/test_multiple-networks.py similarity index 86% rename from test/test_multiple-networks.py rename to test/test_multiple-networks/test_multiple-networks.py index 9d48cbea5..daf2b0f46 100644 --- a/test/test_multiple-networks.py +++ b/test/test_multiple-networks/test_multiple-networks.py @@ -1,7 +1,5 @@ import re -import pytest - def test_unknown_virtual_host(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") @@ -23,6 +21,5 @@ def test_multipath(docker_compose, nginxproxy): 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)] + 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/test_multiple-networks.yml similarity index 70% rename from test/test_multiple-networks.yml rename to test/test_multiple-networks/test_multiple-networks.yml index 3e34b481e..d522ed552 100644 --- a/test/test_multiple-networks.yml +++ b/test/test_multiple-networks/test_multiple-networks.yml @@ -1,16 +1,11 @@ -version: "2" - networks: - net1: {} - net2: {} - net3a: {} - net3b: {} + net1: + net2: + net3a: + net3b: services: nginx-proxy: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro networks: - net1 - net2 @@ -22,7 +17,7 @@ services: expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example networks: - net1 @@ -32,7 +27,7 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.example networks: - net2 @@ -42,7 +37,7 @@ services: expose: - "83" environment: - WEB_PORTS: 83 + WEB_PORTS: "83" VIRTUAL_HOST: web3.nginx-proxy.test networks: - net3a diff --git a/test/test_multiports/test_multiports-base-json.py b/test/test_multiports/test_multiports-base-json.py index 7f1ef0fb0..cab5df86f 100644 --- a/test/test_multiports/test_multiports-base-json.py +++ b/test/test_multiports/test_multiports-base-json.py @@ -1,6 +1,3 @@ -import pytest - - def test_virtual_host_is_dropped_when_using_multiports(docker_compose, nginxproxy): r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_multiports/test_multiports-base-json.yml b/test/test_multiports/test_multiports-base-json.yml index fc0d0fa81..cf9bcbbc6 100644 --- a/test/test_multiports/test_multiports-base-json.yml +++ b/test/test_multiports/test_multiports-base-json.yml @@ -1,5 +1,3 @@ -version: "2" - services: skipvirtualhost: image: web @@ -70,8 +68,3 @@ services: } } } - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_multiports/test_multiports-base-yaml.py b/test/test_multiports/test_multiports-base-yaml.py index 7f1ef0fb0..cab5df86f 100644 --- a/test/test_multiports/test_multiports-base-yaml.py +++ b/test/test_multiports/test_multiports-base-yaml.py @@ -1,6 +1,3 @@ -import pytest - - def test_virtual_host_is_dropped_when_using_multiports(docker_compose, nginxproxy): r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_multiports/test_multiports-base-yaml.yml b/test/test_multiports/test_multiports-base-yaml.yml index 8bd58030c..56c02aa8c 100644 --- a/test/test_multiports/test_multiports-base-yaml.yml +++ b/test/test_multiports/test_multiports-base-yaml.yml @@ -1,5 +1,3 @@ -version: "2" - services: skipvirtualhost: image: web @@ -54,8 +52,3 @@ services: "/customdest": port: 10002 dest: "/port" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_multiports/test_multiports-invalid-syntax.py b/test/test_multiports/test_multiports-invalid-syntax.py index ed1c77338..ceb7436a8 100644 --- a/test/test_multiports/test_multiports-invalid-syntax.py +++ b/test/test_multiports/test_multiports-invalid-syntax.py @@ -1,4 +1,3 @@ -import pytest import re diff --git a/test/test_multiports/test_multiports-invalid-syntax.yml b/test/test_multiports/test_multiports-invalid-syntax.yml index 9f4022061..7dd7e9658 100644 --- a/test/test_multiports/test_multiports-invalid-syntax.yml +++ b/test/test_multiports/test_multiports-invalid-syntax.yml @@ -1,5 +1,3 @@ -version: "2" - services: invalidsyntax: image: web @@ -37,8 +35,3 @@ services: port: 8080 "/": port: 9000 - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_multiports/test_multiports-merge.py b/test/test_multiports/test_multiports-merge.py index f5aa69782..ff423a7e0 100644 --- a/test/test_multiports/test_multiports-merge.py +++ b/test/test_multiports/test_multiports-merge.py @@ -1,5 +1,4 @@ import backoff -import pytest def test_multiports_and_legacy_configs_should_be_merged(docker_compose, nginxproxy): diff --git a/test/test_multiports/test_multiports-merge.yml b/test/test_multiports/test_multiports-merge.yml index 5c5cd8bd0..c0506fc46 100644 --- a/test/test_multiports/test_multiports-merge.yml +++ b/test/test_multiports/test_multiports-merge.yml @@ -1,5 +1,3 @@ -version: "2" - services: merged-singleport: image: web @@ -34,8 +32,3 @@ services: "/foo": port: 9191 dest: "/" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_nominal.py b/test/test_nominal/test_nominal.py similarity index 85% rename from test/test_nominal.py rename to test/test_nominal/test_nominal.py index a3f9c87f5..ce8e681d8 100644 --- a/test/test_nominal.py +++ b/test/test_nominal/test_nominal.py @@ -1,3 +1,5 @@ +import platform + import pytest from requests import ConnectionError @@ -19,6 +21,10 @@ def test_forwards_to_web2(docker_compose, nginxproxy): assert r.text == "answer from port 82\n" +@pytest.mark.skipif( + platform.system() == "Darwin", + reason="This test depends on direct communication with the container's IP" +) def test_ipv6_is_disabled_by_default(docker_compose, nginxproxy): with pytest.raises(ConnectionError): nginxproxy.get("http://nginx-proxy/port", ipv6=True) diff --git a/test/test_nominal.yml b/test/test_nominal/test_nominal.yml similarity index 71% rename from test/test_nominal.yml rename to test/test_nominal/test_nominal.yml index 60678951b..93f9ff03b 100644 --- a/test/test_nominal.yml +++ b/test/test_nominal/test_nominal.yml @@ -1,5 +1,3 @@ -version: "2" - networks: net1: enable_ipv6: true @@ -8,12 +6,16 @@ networks: - subnet: fd00:1::/80 services: + nginx-proxy: + networks: + - net1 + web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.tld networks: - net1 @@ -23,14 +25,7 @@ services: expose: - "82" environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: web2.nginx-proxy.tld networks: - net1 - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - networks: - - net1 diff --git a/test/test_ports/test_default-80.py b/test/test_ports/test_default-80.py index 74c2f9fd2..67b1a0860 100644 --- a/test/test_ports/test_default-80.py +++ b/test/test_ports/test_default-80.py @@ -1,6 +1,3 @@ -import pytest - - def test_answer_is_served_from_port_80_by_default(docker_compose, nginxproxy): r = nginxproxy.get("http://web.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_ports/test_default-80.yml b/test/test_ports/test_default-80.yml index 0cfdd7e62..e5531c0c4 100644 --- a/test/test_ports/test_default-80.yml +++ b/test/test_ports/test_default-80.yml @@ -1,5 +1,3 @@ -version: "2" - services: web: image: web @@ -9,8 +7,3 @@ services: environment: WEB_PORTS: "80 81" VIRTUAL_HOST: "web.nginx-proxy.tld" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_ports/test_single-port-not-80.py b/test/test_ports/test_single-port-not-80.py index ee86eca76..771d8356d 100644 --- a/test/test_ports/test_single-port-not-80.py +++ b/test/test_ports/test_single-port-not-80.py @@ -1,6 +1,3 @@ -import pytest - - def test_answer_is_served_from_exposed_port_even_if_not_80(docker_compose, nginxproxy): r = nginxproxy.get("http://web.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_ports/test_single-port-not-80.yml b/test/test_ports/test_single-port-not-80.yml index eef4e0899..2f6be9bb0 100644 --- a/test/test_ports/test_single-port-not-80.yml +++ b/test/test_ports/test_single-port-not-80.yml @@ -1,5 +1,3 @@ -version: "2" - services: web: image: web @@ -8,8 +6,3 @@ services: environment: WEB_PORTS: "81" VIRTUAL_HOST: "web.nginx-proxy.tld" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_ports/test_VIRTUAL_PORT-single-different-from-single-port.py b/test/test_ports/test_virtual-port-single-different-from-single-port.py similarity index 95% rename from test/test_ports/test_VIRTUAL_PORT-single-different-from-single-port.py rename to test/test_ports/test_virtual-port-single-different-from-single-port.py index 4008166e2..b61850a02 100644 --- a/test/test_ports/test_VIRTUAL_PORT-single-different-from-single-port.py +++ b/test/test_ports/test_virtual-port-single-different-from-single-port.py @@ -1,4 +1,3 @@ -import pytest import re diff --git a/test/test_ports/test_VIRTUAL_PORT-single-different-from-single-port.yml b/test/test_ports/test_virtual-port-single-different-from-single-port.yml similarity index 56% rename from test/test_ports/test_VIRTUAL_PORT-single-different-from-single-port.yml rename to test/test_ports/test_virtual-port-single-different-from-single-port.yml index ba58d4ec1..822b6d444 100644 --- a/test/test_ports/test_VIRTUAL_PORT-single-different-from-single-port.yml +++ b/test/test_ports/test_virtual-port-single-different-from-single-port.yml @@ -1,5 +1,3 @@ -version: "2" - services: web: image: web @@ -9,8 +7,3 @@ services: WEB_PORTS: "81" VIRTUAL_HOST: "web.nginx-proxy.tld" VIRTUAL_PORT: "90" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_ports/test_VIRTUAL_PORT.py b/test/test_ports/test_virtual-port.py similarity index 92% rename from test/test_ports/test_VIRTUAL_PORT.py rename to test/test_ports/test_virtual-port.py index 3c95ba629..9c2d15271 100644 --- a/test/test_ports/test_VIRTUAL_PORT.py +++ b/test/test_ports/test_virtual-port.py @@ -1,6 +1,3 @@ -import pytest - - def test_answer_is_served_from_chosen_port(docker_compose, nginxproxy): r = nginxproxy.get("http://web.nginx-proxy.tld/port") assert r.status_code == 200 diff --git a/test/test_ports/test_VIRTUAL_PORT.yml b/test/test_ports/test_virtual-port.yml similarity index 59% rename from test/test_ports/test_VIRTUAL_PORT.yml rename to test/test_ports/test_virtual-port.yml index ab1af4664..4d3ba5f52 100644 --- a/test/test_ports/test_VIRTUAL_PORT.yml +++ b/test/test_ports/test_virtual-port.yml @@ -1,5 +1,3 @@ -version: "2" - services: web: image: web @@ -10,8 +8,3 @@ services: WEB_PORTS: "80 90" VIRTUAL_HOST: "web.nginx-proxy.tld" VIRTUAL_PORT: 90 - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_proxy_protocol/test_proxy-protocol-global-disabled.py b/test/test_proxy_protocol/test_proxy-protocol-global-disabled.py new file mode 100644 index 000000000..b4ea76fc7 --- /dev/null +++ b/test/test_proxy_protocol/test_proxy-protocol-global-disabled.py @@ -0,0 +1,58 @@ +import socket + + +def test_proxy_protocol_global_disabled_X_Forwarded_For_is_generated( + docker_compose, nginxproxy +): + r = nginxproxy.get("http://proxy-protocol-global-disabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "X-Forwarded-For:" in r.text + + +def test_proxy_protocol_global_disabled_X_Forwarded_For_is_passed_on( + docker_compose, nginxproxy +): + r = nginxproxy.get( + "http://proxy-protocol-global-disabled.nginx-proxy.tld/headers", + headers={"X-Forwarded-For": "1.2.3.4"}, + ) + assert r.status_code == 200 + assert "X-Forwarded-For: 1.2.3.4, " in r.text + + +def test_proxy_protocol_global_disabled_X_Forwarded_Port_is_generated( + docker_compose, nginxproxy +): + r = nginxproxy.get("http://proxy-protocol-global-disabled.nginx-proxy.tld/headers") + assert r.status_code == 200 + assert "X-Forwarded-Port: 80\n" in r.text + + +def test_proxy_protocol_global_disabled_X_Forwarded_Port_is_passed_on( + docker_compose, nginxproxy +): + r = nginxproxy.get( + "http://proxy-protocol-global-disabled.nginx-proxy.tld/headers", + headers={"X-Forwarded-Port": "1234"}, + ) + assert r.status_code == 200 + assert "X-Forwarded-Port: 1234\n" in r.text + + +def test_proxy_protocol_global_disabled_proto_request_fails(docker_compose, nginxproxy): + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect((nginxproxy.get_ip(), 80)) + + # 1.2.3.4 is the client IP + # 4.3.2.1 is the proxy server IP + # 8080 is the client port + # 9090 is the proxy server port + client.send(f"PROXY TCP4 1.2.3.4 4.3.2.1 8080 9090\r\n".encode("utf-8")) + client.send( + "GET /headers HTTP/1.1\r\nHost: proxy-protocol-global-enabled.nginx-proxy.tld\r\n\r\n".encode( + "utf-8" + ) + ) + + response = client.recv(4096).decode("utf-8") + assert "HTTP/1.1 400 Bad Request" in response diff --git a/test/test_proxy_protocol/test_proxy-protocol-global-disabled.yml b/test/test_proxy_protocol/test_proxy-protocol-global-disabled.yml new file mode 100644 index 000000000..593e21f8d --- /dev/null +++ b/test/test_proxy_protocol/test_proxy-protocol-global-disabled.yml @@ -0,0 +1,12 @@ +services: +# nginx-proxy: +# environment: +# ENABLE_PROXY_PROTOCOL: "false" #Disabled by default + + proxy-protocol-global-disabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: proxy-protocol-global-disabled.nginx-proxy.tld diff --git a/test/test_proxy_protocol/test_proxy-protocol-global-enabled.py b/test/test_proxy_protocol/test_proxy-protocol-global-enabled.py new file mode 100644 index 000000000..48a8f7e71 --- /dev/null +++ b/test/test_proxy_protocol/test_proxy-protocol-global-enabled.py @@ -0,0 +1,31 @@ +import socket + + +def test_proxy_protocol_global_enabled_normal_request_fails(docker_compose, nginxproxy): + try: + r = nginxproxy.get( + "http://proxy-protocol-global-enabled.nginx-proxy.tld/headers" + ) + assert False + except Exception as e: + assert "Remote end closed connection without response" in str(e) + + +def test_proxy_protocol_global_enabled_proto_request_works(docker_compose, nginxproxy): + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect((nginxproxy.get_ip(), 80)) + + # 1.2.3.4 is the client IP + # 4.3.2.1 is the proxy server IP + # 8080 is the client port + # 9090 is the proxy server port + client.send(f"PROXY TCP4 1.2.3.4 4.3.2.1 8080 9090\r\n".encode("utf-8")) + client.send( + "GET /headers HTTP/1.1\r\nHost: proxy-protocol-global-enabled.nginx-proxy.tld\r\n\r\n".encode( + "utf-8" + ) + ) + + response = client.recv(4096).decode("utf-8") + assert "X-Forwarded-For: 1.2.3.4" in response + assert "X-Forwarded-Port: 9090" in response diff --git a/test/test_proxy_protocol/test_proxy-protocol-global-enabled.yml b/test/test_proxy_protocol/test_proxy-protocol-global-enabled.yml new file mode 100644 index 000000000..04f7a379e --- /dev/null +++ b/test/test_proxy_protocol/test_proxy-protocol-global-enabled.yml @@ -0,0 +1,12 @@ +services: + nginx-proxy: + environment: + ENABLE_PROXY_PROTOCOL: "true" + + proxy-protocol-global-enabled: + image: web + expose: + - "80" + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: proxy-protocol-global-enabled.nginx-proxy.tld diff --git a/test/test_raw-ip-vhost.py b/test/test_raw-ip-vhost/test_raw-ip-vhost.py similarity index 79% rename from test/test_raw-ip-vhost.py rename to test/test_raw-ip-vhost/test_raw-ip-vhost.py index aaff8529a..4708f9ad7 100644 --- a/test/test_raw-ip-vhost.py +++ b/test/test_raw-ip-vhost/test_raw-ip-vhost.py @@ -1,15 +1,12 @@ -import pytest - - def test_raw_ipv4_vhost_forwards_to_web1(docker_compose, nginxproxy): - r = nginxproxy.get("http://172.20.0.4") + r = nginxproxy.get("http://172.20.0.1") assert r.status_code == 200 web1_container = docker_compose.containers.get("web1") assert r.text == f"I'm {web1_container.id[:12]}\n" def test_raw_ipv6_vhost_forwards_to_web2(docker_compose, nginxproxy): - r = nginxproxy.get("http://[fd00::4]", ipv6=True) + r = nginxproxy.get("http://[fd00::1]") assert r.status_code == 200 web2_container = docker_compose.containers.get("web2") assert r.text == f"I'm {web2_container.id[:12]}\n" diff --git a/test/test_raw-ip-vhost.yml b/test/test_raw-ip-vhost/test_raw-ip-vhost.yml similarity index 73% rename from test/test_raw-ip-vhost.yml rename to test/test_raw-ip-vhost/test_raw-ip-vhost.yml index f034bdb87..c0b0b02ee 100644 --- a/test/test_raw-ip-vhost.yml +++ b/test/test_raw-ip-vhost/test_raw-ip-vhost.yml @@ -1,5 +1,3 @@ -version: "2" - networks: net1: enable_ipv6: true @@ -9,14 +7,22 @@ networks: - subnet: fd00::/80 services: + nginx-proxy: + environment: + ENABLE_IPV6: "true" + networks: + net1: + ipv4_address: 172.20.0.4 + ipv6_address: fd00::4 + web1: container_name: web1 image: web expose: - "81" environment: - WEB_PORTS: 81 - VIRTUAL_HOST: "172.20.0.4" + WEB_PORTS: "81" + VIRTUAL_HOST: "172.20.0.1" networks: net1: ipv4_address: 172.20.0.2 @@ -28,20 +34,10 @@ services: expose: - "82" environment: - WEB_PORTS: 82 - VIRTUAL_HOST: "[fd00::4]" + WEB_PORTS: "82" + VIRTUAL_HOST: "[fd00::1]" networks: net1: ipv4_address: 172.20.0.3 ipv6_address: fd00::3 - sut: - image: nginxproxy/nginx-proxy:test - environment: - ENABLE_IPV6: "true" - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - networks: - net1: - ipv4_address: 172.20.0.4 - ipv6_address: fd00::4 diff --git a/test/test_server-down/test_load-balancing.py b/test/test_server-down/test_load-balancing.py index b65d0a16b..6caa9e5ae 100644 --- a/test/test_server-down/test_load-balancing.py +++ b/test/test_server-down/test_load-balancing.py @@ -1,5 +1,3 @@ -import pytest - def test_web_has_no_server_down(docker_compose, nginxproxy): conf = nginxproxy.get_conf().decode('ASCII') r = nginxproxy.get("http://web.nginx-proxy.tld/port") diff --git a/test/test_server-down/test_load-balancing.yml b/test/test_server-down/test_load-balancing.yml index 232259020..662dd102e 100644 --- a/test/test_server-down/test_load-balancing.yml +++ b/test/test_server-down/test_load-balancing.yml @@ -1,12 +1,10 @@ -version: "2" - services: web1: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web.nginx-proxy.tld web2: @@ -14,7 +12,7 @@ services: expose: - "82" environment: - WEB_PORTS: 83 + WEB_PORTS: "83" VIRTUAL_HOST: web.nginx-proxy.tld web3: @@ -22,11 +20,6 @@ services: expose: - "83" environment: - WEB_PORTS: 83 + WEB_PORTS: "83" VIRTUAL_HOST: web.nginx-proxy.tld network_mode: "none" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_server-down/test_no-server-down.py b/test/test_server-down/test_no-server-down.py index a98ed564c..4fe9b1d93 100644 --- a/test/test_server-down/test_no-server-down.py +++ b/test/test_server-down/test_no-server-down.py @@ -1,5 +1,3 @@ -import pytest - def test_web_has_no_server_down(docker_compose, nginxproxy): conf = nginxproxy.get_conf().decode('ASCII') r = nginxproxy.get("http://web.nginx-proxy.tld/port") diff --git a/test/test_server-down/test_no-server-down.yml b/test/test_server-down/test_no-server-down.yml index cd7e5acf5..859d8a56e 100644 --- a/test/test_server-down/test_no-server-down.yml +++ b/test/test_server-down/test_no-server-down.yml @@ -1,15 +1,8 @@ -version: "2" - services: web: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_server-down/test_server-down.py b/test/test_server-down/test_server-down.py index 995cd7d8e..a5b33c3cc 100644 --- a/test/test_server-down/test_server-down.py +++ b/test/test_server-down/test_server-down.py @@ -1,5 +1,3 @@ -import pytest - def test_web_has_server_down(docker_compose, nginxproxy): conf = nginxproxy.get_conf().decode('ASCII') r = nginxproxy.get("http://web.nginx-proxy.tld/port") diff --git a/test/test_server-down/test_server-down.yml b/test/test_server-down/test_server-down.yml index 98fd30f28..f79808212 100644 --- a/test/test_server-down/test_server-down.yml +++ b/test/test_server-down/test_server-down.yml @@ -1,16 +1,9 @@ -version: "2" - services: web: image: web expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web.nginx-proxy.tld network_mode: "none" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_ssl/cert_selection/nginx-proxy.tld.crt b/test/test_ssl/cert_selection/nginx-proxy.tld.crt new file mode 100644 index 000000000..228b6dcb1 --- /dev/null +++ b/test/test_ssl/cert_selection/nginx-proxy.tld.crt @@ -0,0 +1,77 @@ +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: Dec 8 19:41:13 2024 GMT + Not After : Apr 25 19:41:13 2052 GMT + Subject: CN=nginx-proxy.tld + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bb:ba:fc:be:dc:b4:94:57:9f:87:07:75:d3:da: + 15:4d:7a:7e:01:43:05:0f:84:73:f2:69:74:41:ac: + e2:d1:9c:c6:56:e6:ad:fb:b7:b6:a6:d2:94:f1:68: + 52:c7:09:b6:19:ef:5b:68:60:ae:77:87:6e:62:b6: + 5d:b6:aa:a5:8e:01:a7:8c:39:24:e0:c1:4f:29:90: + 53:c8:3c:5b:b3:a4:f1:ed:56:33:ed:9c:69:1a:51: + 60:6f:a0:b9:57:22:73:8a:78:fe:e2:7e:f2:ac:e4: + 78:4c:9c:65:4b:db:f9:29:ba:59:8c:de:b3:56:80: + 14:5b:05:e1:0e:f0:dc:ac:19:9c:76:f8:77:5e:a8: + 37:2c:75:79:8d:9f:37:2b:22:30:98:ce:6e:9e:b6: + 27:68:e9:f5:23:54:38:3b:7d:3a:d1:34:02:3f:dd: + 86:c7:07:22:81:ec:f3:e2:53:18:96:50:3e:2b:66: + 07:b4:9d:2b:bd:95:16:f6:6b:96:a3:0a:ec:13:42: + 03:82:d2:07:95:77:1c:57:ab:3d:ad:21:8e:85:d8: + 78:96:55:bd:a3:30:33:fe:8b:45:31:ff:53:82:f1: + 2a:da:87:4e:89:10:d4:81:9e:bd:3d:10:4e:ba:6d: + 07:f5:f0:e2:14:33:10:2a:a5:8c:46:c1:06:4d:35: + d9:19 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:nginx-proxy.tld, DNS:*.nginx-proxy.tld + X509v3 Subject Key Identifier: + FD:F3:7F:43:D7:67:61:02:8A:DF:71:BD:BA:92:2D:BC:72:ED:B7:0A + X509v3 Authority Key Identifier: + 24:BE:7D:A4:7A:26:B7:EF:1D:50:DC:6F:F0:78:2B:62:32:9F:38:0F + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 9b:19:3b:42:3f:4d:a5:30:83:06:98:32:ed:f4:81:ff:14:76: + a4:aa:b7:dc:ef:d0:41:ee:64:2e:d1:ab:13:b3:76:c9:c8:46: + 59:06:c5:0a:f9:aa:ea:89:e5:cc:0b:8f:6e:f8:4a:28:a0:63: + 04:36:13:05:c4:98:e7:a1:16:88:ea:2b:8b:c8:95:d7:e4:50: + 0f:b7:38:15:82:06:ee:1d:18:bc:91:5c:1f:a8:c2:88:ed:a4: + 7e:5c:38:39:6c:ab:d3:4c:14:4d:ff:e0:de:f2:74:9b:e5:58: + a1:a7:47:e8:c3:79:36:e0:ff:83:6d:5b:87:43:83:ed:5c:ad: + e1:96:3d:7a:2d:a3:ff:f0:4b:32:57:d5:c0:3f:81:c2:69:86: + 86:9e:e4:51:f4:49:9e:c5:78:a8:49:17:06:3f:26:53:11:6b: + 88:c3:44:e3:b1:68:81:55:02:50:21:72:03:eb:0f:72:5c:04: + be:65:63:b0:9b:7b:7a:e1:ef:c1:da:cc:d6:5c:e0:ae:51:be: + 9e:b3:9f:9a:aa:63:84:98:a8:b3:77:86:26:a8:24:c0:b8:10: + 74:b4:f6:4f:53:6d:6b:77:f5:06:a0:b7:16:14:fa:94:47:49: + fa:7d:80:63:8c:0b:d3:f8:72:33:94:a0:b3:21:2c:a9:5b:2f: + d6:93:39:de +-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yNDEyMDgxOTQxMTNaGA8yMDUyMDQyNTE5NDExM1owGjEYMBYGA1UEAwwP +bmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +u7r8vty0lFefhwd109oVTXp+AUMFD4Rz8ml0Qazi0ZzGVuat+7e2ptKU8WhSxwm2 +Ge9baGCud4duYrZdtqqljgGnjDkk4MFPKZBTyDxbs6Tx7VYz7ZxpGlFgb6C5VyJz +inj+4n7yrOR4TJxlS9v5KbpZjN6zVoAUWwXhDvDcrBmcdvh3Xqg3LHV5jZ83KyIw +mM5unrYnaOn1I1Q4O3060TQCP92Gxwcigezz4lMYllA+K2YHtJ0rvZUW9muWowrs +E0IDgtIHlXccV6s9rSGOhdh4llW9ozAz/otFMf9TgvEq2odOiRDUgZ69PRBOum0H +9fDiFDMQKqWMRsEGTTXZGQIDAQABo3EwbzAtBgNVHREEJjAkgg9uZ2lueC1wcm94 +eS50bGSCESoubmdpbngtcHJveHkudGxkMB0GA1UdDgQWBBT9839D12dhAorfcb26 +ki28cu23CjAfBgNVHSMEGDAWgBQkvn2keia37x1Q3G/weCtiMp84DzANBgkqhkiG +9w0BAQsFAAOCAQEAmxk7Qj9NpTCDBpgy7fSB/xR2pKq33O/QQe5kLtGrE7N2ychG +WQbFCvmq6onlzAuPbvhKKKBjBDYTBcSY56EWiOori8iV1+RQD7c4FYIG7h0YvJFc +H6jCiO2kflw4OWyr00wUTf/g3vJ0m+VYoadH6MN5NuD/g21bh0OD7Vyt4ZY9ei2j +//BLMlfVwD+BwmmGhp7kUfRJnsV4qEkXBj8mUxFriMNE47FogVUCUCFyA+sPclwE +vmVjsJt7euHvwdrM1lzgrlG+nrOfmqpjhJios3eGJqgkwLgQdLT2T1Nta3f1BqC3 +FhT6lEdJ+n2AY4wL0/hyM5SgsyEsqVsv1pM53g== +-----END CERTIFICATE----- diff --git a/test/test_ssl/cert_selection/nginx-proxy.tld.key b/test/test_ssl/cert_selection/nginx-proxy.tld.key new file mode 100644 index 000000000..776478ca6 --- /dev/null +++ b/test/test_ssl/cert_selection/nginx-proxy.tld.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7uvy+3LSUV5+H +B3XT2hVNen4BQwUPhHPyaXRBrOLRnMZW5q37t7am0pTxaFLHCbYZ71toYK53h25i +tl22qqWOAaeMOSTgwU8pkFPIPFuzpPHtVjPtnGkaUWBvoLlXInOKeP7ifvKs5HhM +nGVL2/kpulmM3rNWgBRbBeEO8NysGZx2+HdeqDcsdXmNnzcrIjCYzm6etido6fUj +VDg7fTrRNAI/3YbHByKB7PPiUxiWUD4rZge0nSu9lRb2a5ajCuwTQgOC0geVdxxX +qz2tIY6F2HiWVb2jMDP+i0Ux/1OC8Srah06JENSBnr09EE66bQf18OIUMxAqpYxG +wQZNNdkZAgMBAAECggEAHhvUU4w0TiJrjI2OwUaV6hI+SVeFZUK6EeQF6E6hJjs9 +uExRFTEW01Ss3Lb9p7yP/0LbZzcyZCtNcfmLuDNQUCFFzQCRQJbgamsidNfxYTOQ +NZ+Avpg6ZyUsEVs2vpng7BGR16PCqiYOgUMIIA2MvbuMh0JkgvgRy7N6sBnDWrqO +f950DN1dmE1f+HNCx0Y0quwUf42ME4kio0gWodMx6cqtNJUMFmThUTtks/2t9qlV +l9rbYfKRJen9MgZ9qYX5oMnnOU5tUvYb7ziayBVylGSbtx+2NpiouOKwujaBW3GH +lnoGcfw63H6gJIh6AofQnzVAC2m9QqBbzHxqfJtZ8QKBgQDfGoiMX8k8Lbc+2FKj +CBIpp6/Oe6hJIKwOKAswMf9fL/Ei3zKWrtlMwYxDovvGhIFIQSJ9KqFW6TfQ9m6i +EGWbTL3jQfhT6x/LdOSI/aa+XqWVkiMdDAt+t7Zn91RA3uzIt6p/TAGFnItSznyy +yt/Ranq79otmy2Wa4D6w309iFQKBgQDXaTmVO3tITq2pNow8+uzb+cvbA1Fj/dSu +vBbvMlcEczDTRENcu7oB7fQphzRlYAflvQyW6pQfo0KstNv2G1yWJT7ukCkDGnaE +j167fPw+/6QWlViEIsB6TIVHsGvOz+JPL//iHR0GJt9UC6E16SBEebvfUqId1LNz +UOzKv7bP9QKBgH7bB2lJzZJ6LAd/2Pz6SdmFj2FIzpdGDI7AFhyBUUM+b030uz8S +jFwocSjEM4TdMuVyo7mn07UnRqV6ec2MpTl4mT/u8tREjP+Sp599GqKEDoLNZUnO +33pd20miSDXai6hVRoy7GuJt8IpU/nJrCHh6ou7dcMYyQMOfXgYT839BAoGBAKJo +ODz2Wq2afD4e3T7ApMH5JKRcbQnpf2e6ccQn/Qli28X9KpiJBOWdhT+mK2/fJjUl +Ao/qDoGH8qYJuelkmbquHDN7RyCK3B53haHvyrKoxQst0E3Xca0Dcl0pey6y91pi +wxa0goR33FseZY4i1Owrp+TpBUdebWmc8tquDmPdAoGASnKqmt2XQtaY98oX7B66 +5ILSuigzXQ/U/cJBZ1VMJKlCJnIPNF3hApCMN8xTizoposDcHYdNGx7RkjnR+f+1 +2cMJogntrablkLdh0dPU8wlS04NcC7JXoclqj6ID75YlLD3BGwsBx7BLeS4h1cQ1 +NCKinLnF0touxbwjGqF+478= +-----END PRIVATE KEY----- diff --git a/test/test_ssl/cert_selection/web1.nginx-proxy.tld.crt b/test/test_ssl/cert_selection/web1.nginx-proxy.tld.crt new file mode 100644 index 000000000..76042b996 --- /dev/null +++ b/test/test_ssl/cert_selection/web1.nginx-proxy.tld.crt @@ -0,0 +1,77 @@ +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: Dec 8 19:42:32 2024 GMT + Not After : Apr 25 19:42:32 2052 GMT + Subject: CN=web1.nginx-proxy.tld + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e7:5c:7f:56:b3:eb:8f:16:81:07:05:1d:88:d4: + bf:cd:3f:16:b8:3d:a5:a8:31:80:a3:c5:f0:a7:8f: + 29:d6:09:35:ad:48:6d:d6:20:8c:fe:78:98:50:aa: + a1:03:86:ff:48:c0:e5:59:27:f8:89:13:08:99:20: + c9:86:49:53:63:5a:c8:19:c5:c8:46:26:dc:3a:40: + 80:43:b8:86:e5:b8:73:33:1a:81:87:31:21:ea:aa: + 4e:9e:f9:cf:60:0a:66:06:81:dc:fc:6b:1c:72:1d: + 73:bc:2d:f5:eb:aa:87:cb:87:83:88:bc:a8:eb:4a: + 94:23:9d:56:77:cc:8b:46:0a:25:c7:27:17:45:3f: + 11:73:52:8d:5f:91:54:3b:b3:b2:28:cd:5b:55:79: + 32:88:fd:25:21:4f:f8:83:b3:f8:be:f7:ff:7f:f3: + 5d:b7:5b:40:ad:c5:87:e3:72:06:bf:93:22:12:f7: + 8d:d6:7d:49:44:1c:ac:7d:9a:02:cf:e0:e0:8d:db: + aa:69:58:9e:5e:f2:00:ac:e6:8b:73:b3:aa:26:b0: + 50:40:ff:0a:73:d3:91:c3:1e:7c:6f:59:eb:ee:75: + 31:9d:71:19:ac:9e:ce:32:90:3e:bd:cb:30:fa:a4: + a0:19:1f:0f:b5:91:5f:c9:3e:94:b8:52:d8:8c:6f: + 79:e9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:web1.nginx-proxy.tld + X509v3 Subject Key Identifier: + F4:08:B3:4A:A1:95:69:35:6B:2C:C7:D6:BE:99:25:F6:16:3F:21:7E + X509v3 Authority Key Identifier: + 24:BE:7D:A4:7A:26:B7:EF:1D:50:DC:6F:F0:78:2B:62:32:9F:38:0F + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 84:ba:99:25:10:82:ae:3f:55:14:3c:3c:e6:aa:5e:96:a7:76: + df:aa:71:77:54:bd:6d:6a:e9:19:c0:d9:81:7c:36:ff:be:39: + 2e:03:a8:f2:a2:24:cf:63:ba:d0:cc:45:a5:58:0f:36:5f:8f: + 21:23:b7:66:a6:5a:40:93:2f:25:18:3e:1b:40:bc:9c:77:4c: + ec:32:36:ff:64:49:89:98:5e:6c:e8:a5:dd:28:79:8a:b6:10: + 05:9d:b4:f5:33:4d:32:5f:6b:44:d5:27:b7:e0:fb:b7:6e:04: + a6:3a:2e:4a:0f:3b:14:e1:94:39:e2:cb:c3:b9:97:da:9f:db: + c5:f4:fe:e3:58:6f:5c:43:2c:15:3b:24:5f:18:0e:d8:7b:73: + 0e:64:8b:64:de:44:ac:96:1d:18:7b:54:d8:4e:72:86:df:f9: + 8e:ae:dc:0d:3a:6d:9b:88:8e:c5:ee:55:ce:f8:0e:93:b2:fe: + 06:6d:6e:91:f4:5b:55:b3:c4:09:b3:cc:c7:a8:7f:ab:d0:f9: + 1c:04:30:07:51:ae:bc:18:10:1b:98:db:56:32:6a:0a:59:cd: + 25:1d:a9:83:35:85:b6:2b:51:ad:4c:8f:b2:6f:50:8f:3d:da: + 40:9b:8c:cc:d5:1e:ad:a1:00:fd:ac:b9:ba:fc:e9:1c:e9:a2: + 08:cd:8b:be +-----BEGIN CERTIFICATE----- +MIIDPzCCAiegAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAgFw0yNDEyMDgxOTQyMzJaGA8yMDUyMDQyNTE5NDIzMlowHzEdMBsGA1UEAwwU +d2ViMS5uZ2lueC1wcm94eS50bGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDnXH9Ws+uPFoEHBR2I1L/NPxa4PaWoMYCjxfCnjynWCTWtSG3WIIz+eJhQ +qqEDhv9IwOVZJ/iJEwiZIMmGSVNjWsgZxchGJtw6QIBDuIbluHMzGoGHMSHqqk6e ++c9gCmYGgdz8axxyHXO8LfXrqofLh4OIvKjrSpQjnVZ3zItGCiXHJxdFPxFzUo1f +kVQ7s7IozVtVeTKI/SUhT/iDs/i+9/9/8123W0CtxYfjcga/kyIS943WfUlEHKx9 +mgLP4OCN26ppWJ5e8gCs5otzs6omsFBA/wpz05HDHnxvWevudTGdcRmsns4ykD69 +yzD6pKAZHw+1kV/JPpS4UtiMb3npAgMBAAGjYzBhMB8GA1UdEQQYMBaCFHdlYjEu +bmdpbngtcHJveHkudGxkMB0GA1UdDgQWBBT0CLNKoZVpNWssx9a+mSX2Fj8hfjAf +BgNVHSMEGDAWgBQkvn2keia37x1Q3G/weCtiMp84DzANBgkqhkiG9w0BAQsFAAOC +AQEAhLqZJRCCrj9VFDw85qpelqd236pxd1S9bWrpGcDZgXw2/745LgOo8qIkz2O6 +0MxFpVgPNl+PISO3ZqZaQJMvJRg+G0C8nHdM7DI2/2RJiZhebOil3Sh5irYQBZ20 +9TNNMl9rRNUnt+D7t24EpjouSg87FOGUOeLLw7mX2p/bxfT+41hvXEMsFTskXxgO +2HtzDmSLZN5ErJYdGHtU2E5yht/5jq7cDTptm4iOxe5VzvgOk7L+Bm1ukfRbVbPE +CbPMx6h/q9D5HAQwB1GuvBgQG5jbVjJqClnNJR2pgzWFtitRrUyPsm9Qjz3aQJuM +zNUeraEA/ay5uvzpHOmiCM2Lvg== +-----END CERTIFICATE----- diff --git a/test/test_ssl/cert_selection/web1.nginx-proxy.tld.key b/test/test_ssl/cert_selection/web1.nginx-proxy.tld.key new file mode 100644 index 000000000..7ee5cc501 --- /dev/null +++ b/test/test_ssl/cert_selection/web1.nginx-proxy.tld.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDnXH9Ws+uPFoEH +BR2I1L/NPxa4PaWoMYCjxfCnjynWCTWtSG3WIIz+eJhQqqEDhv9IwOVZJ/iJEwiZ +IMmGSVNjWsgZxchGJtw6QIBDuIbluHMzGoGHMSHqqk6e+c9gCmYGgdz8axxyHXO8 +LfXrqofLh4OIvKjrSpQjnVZ3zItGCiXHJxdFPxFzUo1fkVQ7s7IozVtVeTKI/SUh +T/iDs/i+9/9/8123W0CtxYfjcga/kyIS943WfUlEHKx9mgLP4OCN26ppWJ5e8gCs +5otzs6omsFBA/wpz05HDHnxvWevudTGdcRmsns4ykD69yzD6pKAZHw+1kV/JPpS4 +UtiMb3npAgMBAAECggEASj98/i4vYnQuZDpYCBl+lDey6t2d1CROzEAxwxRGsNGz +0DB2T0mS9fdtqjWYBrHEK2z8LWiOci8Ri1LiWZ4ejLwNi3fVle3Srz8T6qfFYuOW +1Mek2eYCpHSTVqGE06WCqIP0j20wa87X3YRCb6cCfWa356Hj896zO9q6bSfPamIq +/hW6x7jJcqY7T+/DnkIu5gFB135F8KTA+wcVtfkyuO1P85dqZKyAiBQ4xHDZ7dKp +sqELryBVCT45DxXlIjY3QhuwqDas0o8em2ayoPSF372V52QqksnR4BS9tX6NIDQd +ro10ZmEveNYJ8agKhBvpHHuDcjKQ0c5Mc4lG8qHEoQKBgQD8dF4p0DSF8uFyi42F +vu/4tDidY738dWE5AhuHgfWtoUYlh5p7fX/bCajLjsNZJxNsEVqSQ3ubvTTepU9r +iHZiWsbmZA60FiNB5iyYOhDY1ULYLW9K+SVzSejCOJOG+1RFciqq6Jm3PEmfhlsm +s3fOYHad5RHes9Ewfl1RFzHwKwKBgQDqnEtmbbHSWWXucoEu0NOIqwMR+Fn25KQt +80mpCJKpJ6BpGdogG2RXRWfNRBvbFg6p9vdFMB8hxijrk8B2EpkFQVwCx8FuT3qA +U/QbcgbWIoxEsvCc04fLMcDOcNszZpnKisMieVzA9HS+ek93SbnaSRtZNNGnAcSp +PkVxshdgOwKBgEXJ0YITmYFx/BrTM0NG3kXNZF02Bn404LSGvZ9/MLBYBfE4BF+g ++OhPSkdDZwNNRxQ30+bdGt2Zt3TZFBRw+/ECkTt9a4cjG4oLHEbUvnWCtuDV5o8+ +QrkVNHAEfdvxvIEaJ2tps5yw4NVlqesP782wYvoRKhh7cxfJxvg0xkxTAoGBAJGT +rg2+32pg4qkVECfWmIvjYaJS8rAKq/1/SyPCGA8ycY0F552ooZitjn2GV6qYgzzf +7AtYktjINzgGlJQvchgauFdZUCN82cyrS3uEQ8233kX6qucFMuvY0YTH7px0e9V8 +SR0pJW7twUs4ZfrlGpfiaT/vUHBK3pcN9tVX5r1lAoGAFss3yu7iwxZq5m/tzv53 +8lf35kq02Miia3Xu5Ahi6LHrhJmqc7OV6CV1bTaWb5ojf7R82nny5w0CTBk9qkFw +i0hF4jYS0eEp5AF4fC0tW4nPim1Dz/Yr9EnWAfCIsS56fktqsPr12OR2OMMepnuf +8gQm89DpS1l+hZou7msI41A= +-----END PRIVATE KEY----- diff --git a/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem new file mode 100644 index 000000000..de292c16d --- /dev/null +++ b/test/test_ssl/certs_mtls/9ae5d1b655182b052fed458ec701f9ae1524e1c2.crl.pem @@ -0,0 +1,13 @@ +-----BEGIN X509 CRL----- +MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 +eS10ZXN0LXN1aXRlFw0yNTA3MDkxMDEyNDFaFw0zNTA3MDcxMDEyNDFaMDIwMAIR +AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg +MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV +BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t +MA0GCSqGSIb3DQEBCwUAA4IBAQAji33L7enDzhw8qNYLtMxrJuuLAMJeRDO4qYeI +pIJu38K+9RTKG2U/BPPKmdtos/M1NEVJrLqZ/eKHoEU/+u0f1pod3Vh2tAlyB+qp +aGwsg5o07hdB85VDAJ7zwPLFjHtChhhVTS5qOqidaSdVBE0/IFifWBEyHyC7yJDl +dlNY7jmarlmFnpDWmXqAdgMqNlS/t9KN8RtCjiHlF8lF+qjimCWAcfecMmdbAUFC +RFHmo6ENxmcDXQDRVqKAXMzmk/YAe0SCqdT0EsWSvUmRBKdtXSBHAQRz8hl2xI2Z +6CtJXYw6Oy4eA+Ge2JMSRUuEKYwpVSLGdxCoHAkZkz+2rU2X +-----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/ca.crl.pem b/test/test_ssl/certs_mtls/ca.crl.pem new file mode 100644 index 000000000..de292c16d --- /dev/null +++ b/test/test_ssl/certs_mtls/ca.crl.pem @@ -0,0 +1,13 @@ +-----BEGIN X509 CRL----- +MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 +eS10ZXN0LXN1aXRlFw0yNTA3MDkxMDEyNDFaFw0zNTA3MDcxMDEyNDFaMDIwMAIR +AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg +MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV +BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t +MA0GCSqGSIb3DQEBCwUAA4IBAQAji33L7enDzhw8qNYLtMxrJuuLAMJeRDO4qYeI +pIJu38K+9RTKG2U/BPPKmdtos/M1NEVJrLqZ/eKHoEU/+u0f1pod3Vh2tAlyB+qp +aGwsg5o07hdB85VDAJ7zwPLFjHtChhhVTS5qOqidaSdVBE0/IFifWBEyHyC7yJDl +dlNY7jmarlmFnpDWmXqAdgMqNlS/t9KN8RtCjiHlF8lF+qjimCWAcfecMmdbAUFC +RFHmo6ENxmcDXQDRVqKAXMzmk/YAe0SCqdT0EsWSvUmRBKdtXSBHAQRz8hl2xI2Z +6CtJXYw6Oy4eA+Ge2JMSRUuEKYwpVSLGdxCoHAkZkz+2rU2X +-----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/ca.crt b/test/test_ssl/certs_mtls/ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem new file mode 100644 index 000000000..de292c16d --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-enabled.nginx-proxy.tld.crl.pem @@ -0,0 +1,13 @@ +-----BEGIN X509 CRL----- +MIICADCB6QIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZuZ2lueC1wcm94 +eS10ZXN0LXN1aXRlFw0yNTA3MDkxMDEyNDFaFw0zNTA3MDcxMDEyNDFaMDIwMAIR +AK8a1AmezG56vTp5WqtpnScXDTI1MDEwMzEyMzAwN1owDDAKBgNVHRUEAwoBBaBg +MF4wXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNV +BAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8t +MA0GCSqGSIb3DQEBCwUAA4IBAQAji33L7enDzhw8qNYLtMxrJuuLAMJeRDO4qYeI +pIJu38K+9RTKG2U/BPPKmdtos/M1NEVJrLqZ/eKHoEU/+u0f1pod3Vh2tAlyB+qp +aGwsg5o07hdB85VDAJ7zwPLFjHtChhhVTS5qOqidaSdVBE0/IFifWBEyHyC7yJDl +dlNY7jmarlmFnpDWmXqAdgMqNlS/t9KN8RtCjiHlF8lF+qjimCWAcfecMmdbAUFC +RFHmo6ENxmcDXQDRVqKAXMzmk/YAe0SCqdT0EsWSvUmRBKdtXSBHAQRz8hl2xI2Z +6CtJXYw6Oy4eA+Ge2JMSRUuEKYwpVSLGdxCoHAkZkz+2rU2X +-----END X509 CRL----- diff --git a/test/test_ssl/certs_mtls/mtls-optional-foo-bar_location b/test/test_ssl/certs_mtls/mtls-optional-foo-bar_location new file mode 100644 index 000000000..285a3bd36 --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-optional-foo-bar_location @@ -0,0 +1,4 @@ +if ($ssl_client_verify = SUCCESS) { + add_header Content-Type text/plain; + return 418 'ssl_client_verify is SUCCESS'; +} diff --git a/test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt b/test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt new file mode 100644 index 000000000..e3d27c54b --- /dev/null +++ b/test/test_ssl/certs_mtls/mtls-optional.nginx-proxy.tld.ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUNvSKwCRCnOATsXaluKSE3RI6fy0wDQYJKoZIhvcNAQEL +BQAwITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMx +MjE0NTdaFw0zNTAxMDExMjE0NTdaMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRl +c3Qtc3VpdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb78gTRxTy +6NrnApjBZ9Gjuj+0FcqE6ARpYBcQU+F/yokyNhE0SR4AyQaGtymDkqPcrfdDVd4b +ylz3wsbJ5jCjHyPFOQ+6trVZ6eKfQ+VoMTlTCj+ystKAba85bWdXriLdMNoqZdkY +jT6ryHUQ/mmeVdhUKBtCrqayiYNbjGTkhCwH2h2jYBdK5ngZ5mc1Z+4NGCUi14aY +AHU+nwSae/y6OU2gcYhr4NuFtwxLfxXXj2vgdTMcqoeu08u6kEjY0g7A7dzMEJ0r +VGB6+aMVK22KklPLn2IgZ/w4TC/kq/DhyWivL4HYjsKbmA6O9tM6xQqpxUwIyo+y +CdTbtv5uSX3jAgMBAAGjgZswgZgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQU9X5P +1mF9ZBIYOSikqH40bUmpgRYwXAYDVR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmp +gRahJaQjMCExHzAdBgNVBAMMFm5naW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAk +QpzgE7F2pbikhN0SOn8tMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA +kE32r6zEm6ntNRVdu3wYR6oSkk7wQr1m2YmVQjb4LIdSL3nP8VJ9PhBxUpLV03tX +evUqrAiMRL6luwFaTSb3mDxm4RGfWUzd0OVjS5NMki7Tr2RXwCCxvKApYcoJ8pwP +dNsqAmhxEwhQXIx3lG8EAxiSXe26S9JU6ILVDgdnYgfr/S9k99OjtUQOS88XiKuL +fwV7n/xSAQDiczGjwWpJLdZiKgic5VFtKOVHHMo5BIX30SJSd//BjZ5Z3N++FhLg +RzwEjbnVxxOkyz30N8VD5M7I5XwTa+VVTj5Cqoj+ImBy+/IsqjqIX8KIjl2AoJER ++2EdFJ1a5odHU3olzbuRcA== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/nginx-proxy.tld.crt b/test/test_ssl/certs_mtls/nginx-proxy.tld.crt new file mode 100644 index 000000000..cd7284b06 --- /dev/null +++ b/test/test_ssl/certs_mtls/nginx-proxy.tld.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Jan 10 00:08:52 2017 GMT + Not After : May 28 00:08:52 2044 GMT + Subject: CN=*.nginx-proxy.tld + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:cb:45:f4:14:9b:fe:64:85:79:4a:36:8d:3d:d1: + 27:d0:7c:36:28:30:e6:73:80:6f:7c:49:23:d0:6c: + 17:e4:44:c0:77:4d:9a:c2:bc:24:84:e3:a5:4d:ba: + d2:da:51:7b:a1:2a:12:d4:c0:19:55:69:2c:22:27: + 2d:1a:f6:fc:4b:7f:e9:cb:a8:3c:e8:69:b8:d2:4f: + de:4e:50:e2:d0:74:30:7c:42:5a:ae:aa:85:a5:b1: + 71:4d:c9:7e:86:8b:62:8c:3e:0d:e3:3b:c3:f5:81: + 0b:8c:68:79:fe:bf:10:fb:ae:ec:11:49:6d:64:5e: + 1a:7d:b3:92:93:4e:96:19:3a:98:04:a7:66:b2:74: + 61:2d:41:13:0c:a4:54:0d:2c:78:fd:b4:a3:e8:37: + 78:9a:de:fa:bc:2e:a8:0f:67:14:58:ce:c3:87:d5: + 14:0e:8b:29:7d:48:19:b2:a9:f5:b4:e8:af:32:21: + 67:15:7e:43:52:8b:20:cf:9f:38:43:bf:fd:c8:24: + 7f:52:a3:88:f2:f1:4a:14:91:2a:6e:91:6f:fb:7d: + 6a:78:c6:6d:2e:dd:1e:4c:2b:63:bb:3a:43:9c:91: + f9:df:d3:08:13:63:86:7d:ce:e8:46:cf:f1:6c:1f: + ca:f7:4c:de:d8:4b:e0:da:bc:06:d9:87:0f:ff:96: + 45:85 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:*.nginx-proxy.tld + Signature Algorithm: sha256WithRSAEncryption + 6e:a5:0e:e4:d3:cc:d5:b7:fc:34:75:89:4e:98:8c:e7:08:06: + a8:5b:ec:13:7d:83:99:a2:61:b8:d5:12:6e:c5:b4:53:4e:9a: + 22:cd:ad:14:30:6a:7d:58:d7:23:d9:a4:2a:96:a0:40:9e:50: + 9f:ce:f2:fe:8c:dd:9a:ac:99:39:5b:89:2d:ca:e5:3e:c3:bc: + 03:04:1c:12:d9:6e:b8:9f:f0:3a:be:12:44:7e:a4:21:86:73: + af:d5:00:51:3f:2c:56:70:34:8f:26:b0:7f:b0:cf:cf:7f:f9: + 40:6f:00:29:c4:cf:c3:b7:c2:49:3d:3f:b0:26:78:87:b9:c7: + 6c:1b:aa:6a:1a:dd:c5:eb:f2:69:ba:6d:46:0b:92:49:b5:11: + 3c:eb:48:c7:2f:fb:33:a6:6a:82:a2:ab:f8:1e:5f:7d:e3:b7: + f2:fd:f5:88:a5:09:4d:a0:bc:f4:3b:cd:d2:8b:d7:57:1f:86: + 3b:d2:3e:a4:92:21:b0:02:0b:e9:e0:c4:1c:f1:78:e2:58:a7: + 26:5f:4c:29:c8:23:f0:6e:12:3f:bd:ad:44:7b:0b:bd:db:ba: + 63:8d:07:c6:9d:dc:46:cc:63:40:ba:5e:45:82:dd:9a:e5:50: + e8:e7:d7:27:88:fc:6f:1d:8a:e7:5c:49:28:aa:10:29:75:28: + c7:52:de:f9 +-----BEGIN CERTIFICATE----- +MIIC9zCCAd+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAeFw0xNzAxMTAwMDA4NTJaFw00NDA1MjgwMDA4NTJaMBwxGjAYBgNVBAMMESou +bmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +y0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02awrwkhOOlTbrS2lF7 +oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJarqqFpbFxTcl+hoti +jD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdmsnRhLUETDKRUDSx4 +/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFnFX5DUosgz584Q7/9 +yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MIE2OGfc7oRs/xbB/K +90ze2Evg2rwG2YcP/5ZFhQIDAQABoyAwHjAcBgNVHREEFTATghEqLm5naW54LXBy +b3h5LnRsZDANBgkqhkiG9w0BAQsFAAOCAQEAbqUO5NPM1bf8NHWJTpiM5wgGqFvs +E32DmaJhuNUSbsW0U06aIs2tFDBqfVjXI9mkKpagQJ5Qn87y/ozdmqyZOVuJLcrl +PsO8AwQcEtluuJ/wOr4SRH6kIYZzr9UAUT8sVnA0jyawf7DPz3/5QG8AKcTPw7fC +ST0/sCZ4h7nHbBuqahrdxevyabptRguSSbURPOtIxy/7M6ZqgqKr+B5ffeO38v31 +iKUJTaC89DvN0ovXVx+GO9I+pJIhsAIL6eDEHPF44linJl9MKcgj8G4SP72tRHsL +vdu6Y40Hxp3cRsxjQLpeRYLdmuVQ6OfXJ4j8bx2K51xJKKoQKXUox1Le+Q== +-----END CERTIFICATE----- diff --git a/test/test_ssl/certs_mtls/nginx-proxy.tld.key b/test/test_ssl/certs_mtls/nginx-proxy.tld.key new file mode 100644 index 000000000..91adb14e1 --- /dev/null +++ b/test/test_ssl/certs_mtls/nginx-proxy.tld.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAy0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02a +wrwkhOOlTbrS2lF7oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJa +rqqFpbFxTcl+hotijD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdm +snRhLUETDKRUDSx4/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFn +FX5DUosgz584Q7/9yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MI +E2OGfc7oRs/xbB/K90ze2Evg2rwG2YcP/5ZFhQIDAQABAoIBAQCjAro2PNLJMfCO +fyjNRgmzu6iCmpR0U68T8GN0JPsT576g7e8J828l0pkhuIyW33lRSThIvLSUNf9a +dChL032H3lBTLduKVh4NKleQXnVFzaeEPoISSFVdButiAhAhPW4OIUVp0OfY3V+x +fac3j2nDLAfL5SKAtqZv363Py9m66EBYm5BmGTQqT/frQWeCEBvlErQef5RIaU8p +e2zMWgSNNojVai8U3nKNRvYHWeWXM6Ck7lCvkHhMF+RpbmCZuqhbEARVnehU/Jdn +QHJ3nxeA2OWpoWKXvAHtSnno49yxq1UIstiQvY+ng5C5i56UlB60UiU2NJ6doZkB +uQ7/1MaBAoGBAORdcFtgdgRALjXngFWhpCp0CseyUehn1KhxDCG+D1pJ142/ymcf +oJOzKJPMRNDdDUBMnR1GBfy7rmwvYevI/SMNy2Qs7ofcXPbdtwwvTCToZ1V9/54k +VfuPBFT+3QzWRvG1tjTV3E4L2VV3nrl2qNPhE5DlfIaU3nQq5Fl0HprJAoGBAOPf +MWOTGev61CdODO5KN3pLAoamiPs5lEUlz3kM3L1Q52YLITxNDjRj9hWBUATJZOS2 +pLOoYRwmhD7vrnimMc41+NuuFX+4T7hWPc8uSuOxX0VijYtULyNRK57mncG1Fq9M +RMLbOJ7FD+8jdXNsSMqpQ+pxLJRX/A10O2fOQnbdAoGAL5hV4YWSM0KZHvz332EI +ER0MXiCJN7HkPZMKH0I4eu3m8hEmAyYxVndBnsQ1F37q0xrkqAQ/HTSUntGlS/og +4Bxw5pkCwegoq/77tpto+ExDtSrEitYx4XMmSPyxX4qNULU5m3tzJgUML+b1etwD +Rd2kMU/TC02dq4KBAy/TbRkCgYAl1xN5iJz+XenLGR/2liZ+TWR+/bqzlU006mF4 +pZUmbv/uJxz+yYD5XDwqOA4UrWjuvhG9r9FoflDprp2XdWnB556KxG7XhcDfSJr9 +A5/2DadXe1Ur9O/a+oi2228JEsxQkea9QPA3FVxfBtFjOHEiDlez39VaUP4PMeUH +iO3qlQKBgFQhdTb7HeYnApYIDHLmd1PvjRvp8XKR1CpEN0nkw8HpHcT1q1MUjQCr +iT6FQupULEvGmO3frQsgVeRIQDbEdZK3C5xCtn6qOw70sYATVf361BbTtidmU9yV +THFxwDSVLiVZgFryoY/NtAc27sVdJnGsPRjjaeVgALAsLbmZ1K/H +-----END RSA PRIVATE KEY----- diff --git a/test/test_ssl/wildcard_cert_and_nohttps/certs/default.crt b/test/test_ssl/certs_wildcard_nohttps/default.crt similarity index 100% rename from test/test_ssl/wildcard_cert_and_nohttps/certs/default.crt rename to test/test_ssl/certs_wildcard_nohttps/default.crt diff --git a/test/test_ssl/wildcard_cert_and_nohttps/certs/default.key b/test/test_ssl/certs_wildcard_nohttps/default.key similarity index 100% rename from test/test_ssl/wildcard_cert_and_nohttps/certs/default.key rename to test/test_ssl/certs_wildcard_nohttps/default.key diff --git a/test/test_ssl/wildcard_cert_and_nohttps/certs/web.nginx-proxy.tld.crt b/test/test_ssl/certs_wildcard_nohttps/web.nginx-proxy.tld.crt similarity index 100% rename from test/test_ssl/wildcard_cert_and_nohttps/certs/web.nginx-proxy.tld.crt rename to test/test_ssl/certs_wildcard_nohttps/web.nginx-proxy.tld.crt diff --git a/test/test_ssl/wildcard_cert_and_nohttps/certs/web.nginx-proxy.tld.key b/test/test_ssl/certs_wildcard_nohttps/web.nginx-proxy.tld.key similarity index 100% rename from test/test_ssl/wildcard_cert_and_nohttps/certs/web.nginx-proxy.tld.key rename to test/test_ssl/certs_wildcard_nohttps/web.nginx-proxy.tld.key diff --git a/test/test_ssl/clientcerts/Revoked.crt b/test/test_ssl/clientcerts/Revoked.crt new file mode 100644 index 000000000..2fcd03ec9 --- /dev/null +++ b/test/test_ssl/clientcerts/Revoked.crt @@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + af:1a:d4:09:9e:cc:6e:7a:bd:3a:79:5a:ab:69:9d:27 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=nginx-proxy-test-suite + Validity + Not Before: Jan 3 12:28:03 2025 GMT + Not After : Jan 1 12:28:03 2035 GMT + Subject: CN=Revoked + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c0:ba:0d:13:f3:c9:8e:1a:e8:53:03:0e:fd:1e: + c1:a9:08:b5:7a:0a:64:47:9f:a2:1c:d1:da:4a:cb: + 9b:b5:ab:32:6e:34:03:ee:0d:54:cf:0d:1b:5b:0a: + b3:0c:f0:16:38:81:b2:d9:f9:7d:49:d4:b8:b6:d0: + bc:ce:25:ad:e2:8b:24:58:d6:77:3f:50:4e:32:0e: + 77:84:65:39:6d:41:4c:98:f6:dc:f4:01:e4:f1:b4: + 0d:43:da:54:ed:46:a7:eb:b7:fb:8e:51:dd:f8:77: + 60:be:76:be:43:14:7d:e7:2c:e3:46:0c:40:cc:b1: + 39:cc:7d:ee:67:18:c6:02:79:5e:4e:f3:25:4a:19: + c8:81:9f:d2:11:f0:84:14:ab:f6:e7:4a:b6:c6:e1: + a0:d1:4d:62:03:50:49:41:87:c2:79:32:b0:66:be: + a2:fa:08:31:f4:79:4c:cc:4a:4d:ad:81:3c:a6:6f: + e5:a1:41:d9:8c:f0:ba:68:50:f6:11:23:f7:27:8e: + 0d:2f:27:06:43:d0:b8:78:cd:ef:7d:6b:36:28:7e: + 7c:96:17:3b:6e:34:b7:4f:14:3c:b2:a6:79:98:9b: + 97:08:9b:19:ab:06:21:a9:fa:7d:55:c2:7b:7f:f4: + c1:e4:c7:5a:b8:0e:70:ef:1a:59:cd:05:bd:29:4f: + 43:67 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 13:7E:71:8D:FC:AD:51:DE:05:9F:5F:48:A0:BB:BB:C6:D9:26:1B:61 + X509v3 Authority Key Identifier: + keyid:F5:7E:4F:D6:61:7D:64:12:18:39:28:A4:A8:7E:34:6D:49:A9:81:16 + DirName:/CN=nginx-proxy-test-suite + serial:36:F4:8A:C0:24:42:9C:E0:13:B1:76:A5:B8:A4:84:DD:12:3A:7F:2D + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 50:ae:d0:c6:32:da:28:c4:5b:d9:35:9b:46:f1:f1:22:eb:40: + 88:e9:4b:3e:a6:f8:06:60:8e:e0:03:15:07:26:9d:51:04:db: + a8:5b:72:3e:55:cf:87:89:a9:c1:4d:94:07:cc:4a:ab:bf:a6: + e2:54:19:ee:ee:f8:cb:22:60:cb:fc:e6:25:1d:a0:b2:7f:61: + ed:de:2a:62:24:f2:14:eb:b3:e8:04:51:85:b9:c7:26:2e:42: + ef:94:bf:dd:67:b3:8d:44:78:14:4e:83:93:4e:c3:f3:75:82: + 00:9c:bc:aa:45:c2:45:80:30:43:28:ea:57:c8:a3:1b:fc:d7: + f7:9f:29:46:c1:05:8e:09:ff:e3:70:0b:fb:35:e7:94:15:dd: + 72:b9:e6:18:f7:68:65:57:cb:c7:31:c2:22:cc:94:a4:ee:3b: + b1:8b:90:80:56:da:6e:f7:01:84:74:e8:8b:65:ac:a2:1c:c4: + bd:e5:b6:d8:03:c2:97:6b:f0:da:24:fa:df:a0:1c:8b:f4:4f: + 47:f4:58:34:4f:de:40:99:7d:37:94:20:9f:0e:51:b1:49:c8: + 53:ef:39:b6:ea:25:ab:f4:4c:23:c2:a1:ae:40:8f:b0:bc:1e: + 6e:fa:4f:d9:2b:b3:4c:d0:55:cf:19:c5:7a:ed:52:4f:da:4a: + 24:03:7c:1f +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIRAK8a1AmezG56vTp5WqtpnScwDQYJKoZIhvcNAQELBQAw +ITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMxMjI4 +MDNaFw0zNTAxMDExMjI4MDNaMBIxEDAOBgNVBAMMB1Jldm9rZWQwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAug0T88mOGuhTAw79HsGpCLV6CmRHn6Ic +0dpKy5u1qzJuNAPuDVTPDRtbCrMM8BY4gbLZ+X1J1Li20LzOJa3iiyRY1nc/UE4y +DneEZTltQUyY9tz0AeTxtA1D2lTtRqfrt/uOUd34d2C+dr5DFH3nLONGDEDMsTnM +fe5nGMYCeV5O8yVKGciBn9IR8IQUq/bnSrbG4aDRTWIDUElBh8J5MrBmvqL6CDH0 +eUzMSk2tgTymb+WhQdmM8LpoUPYRI/cnjg0vJwZD0Lh4ze99azYofnyWFztuNLdP +FDyypnmYm5cImxmrBiGp+n1Vwnt/9MHkx1q4DnDvGlnNBb0pT0NnAgMBAAGjga0w +gaowCQYDVR0TBAIwADAdBgNVHQ4EFgQUE35xjfytUd4Fn19IoLu7xtkmG2EwXAYD +VR0jBFUwU4AU9X5P1mF9ZBIYOSikqH40bUmpgRahJaQjMCExHzAdBgNVBAMMFm5n +aW54LXByb3h5LXRlc3Qtc3VpdGWCFDb0isAkQpzgE7F2pbikhN0SOn8tMBMGA1Ud +JQQMMAoGCCsGAQUFBwMCMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEA +UK7QxjLaKMRb2TWbRvHxIutAiOlLPqb4BmCO4AMVByadUQTbqFtyPlXPh4mpwU2U +B8xKq7+m4lQZ7u74yyJgy/zmJR2gsn9h7d4qYiTyFOuz6ARRhbnHJi5C75S/3Wez +jUR4FE6Dk07D83WCAJy8qkXCRYAwQyjqV8ijG/zX958pRsEFjgn/43AL+zXnlBXd +crnmGPdoZVfLxzHCIsyUpO47sYuQgFbabvcBhHToi2WsohzEveW22APCl2vw2iT6 +36Aci/RPR/RYNE/eQJl9N5Qgnw5RsUnIU+85tuolq/RMI8KhrkCPsLwebvpP2Suz +TNBVzxnFeu1ST9pKJAN8Hw== +-----END CERTIFICATE----- diff --git a/test/test_ssl/clientcerts/Revoked.key b/test/test_ssl/clientcerts/Revoked.key new file mode 100644 index 000000000..42f0dc8cb --- /dev/null +++ b/test/test_ssl/clientcerts/Revoked.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAug0T88mOGuhT +Aw79HsGpCLV6CmRHn6Ic0dpKy5u1qzJuNAPuDVTPDRtbCrMM8BY4gbLZ+X1J1Li2 +0LzOJa3iiyRY1nc/UE4yDneEZTltQUyY9tz0AeTxtA1D2lTtRqfrt/uOUd34d2C+ +dr5DFH3nLONGDEDMsTnMfe5nGMYCeV5O8yVKGciBn9IR8IQUq/bnSrbG4aDRTWID +UElBh8J5MrBmvqL6CDH0eUzMSk2tgTymb+WhQdmM8LpoUPYRI/cnjg0vJwZD0Lh4 +ze99azYofnyWFztuNLdPFDyypnmYm5cImxmrBiGp+n1Vwnt/9MHkx1q4DnDvGlnN +Bb0pT0NnAgMBAAECggEALzFf4nLf+Bw+p5UoJnNRmMK5LZk91QwR9lysx4P0LRgu +0S2LiM9a5RigijqkfZaM2mloElg1hc7BLIMQuKohWkgYLmjV6nsPqtJAEft3hHlo ++Ev67wVHuqgMV4EvKqsSk3YJ81+4qw8QcZNCI8rwyZsETDLT60u6i4iKyFQYqKIC +ivkWxI1WUTe1VYfsL3peTlCfT3oYUR7vk6+gJ6vLgCA+V8jjoAh++MxkPCTEU2vL +BRx0jJYy5C7hyNTzHz0Xq+vu9m+DzLmP0/maUMlsKACF5RsGfO5Jdp499LL//sC0 +/5Z5rC55sJs8/bHGt8nA5N8qNLpTNDSTwuAT5fMqgQKBgQDv4tY2NWND+p9+RIdS +Uc4XsXbvj3Se3Rsx4FB8ApX89KdKUO74dBJ8/0lGttNApYoWL44TfmJDFemmC5eT ++ya7BwQLPmOKnhzc9U8DKziFEQhWrF2FndUm3prAlWolqe/5ddoTel51fEgXooEP +2M4kwBv6Y7DWA+4X72Fvk8tL0wKBgQDNrD8bh4C+vmsqf5u0EV/ST14nLtpg+jS6 +r+a6PfzzhmYos249ZVoHog4f+0mcmO/F+K1X/XAPBnDoFWgHJ9ooVIykO1D5ias8 +CBEzHEiw0E3ayMkYNR1bRbo9ykTuUjNXH7ctdCl+QEAXM2cf83VXvc8fsXQxyRpD +iJZpkExRnQKBgA3zD+fZFOuoEExEaeYUkbp4/GST4AE49FLjK2r6r4QlKfE9YZgb +D9Qq+DTffstclPoTS9zAVbB2/r5EIE1fpnHrx4Vr3Ff8N8t1jGGvyrqaMfTwUwPp +GLMI8NDQH3sPNcA45TSKwiFs17hgH8cvIVWrwjS+RVM8qUTFC9J0Mrc3AoGACeq8 +QD+QXaIg2LO+djhAPovFJm6D6RknYbkJjwFeKP/Z/SxprFwusx+FPtWG/x4AsbMD +6LI3rQHKf+ZIVc/+HOO2xFR32xBgSUy6R5SdjKj+mAYGbDxjZfs+t6wBFtyvzQui +cXagaY3/iR7ZYhkDF2/3hLexupTPx2HWntBuXaECgYBLXINRHusQaJLN1Jjxe22h +9Tx6mPNdnM0KzNeg16F+ix1O5hArNUXaHEwPj6XX6mOq1HbM6crKeGXEzaTw7vTa +o82m4nYjxa+Yh4nDiH0/4bxjZxLRl83+Y13PSxv87lqWXNdzsGdpzkWRasrrs+0J +85NwFxyxcH1wiylc86g0BQ== +-----END PRIVATE KEY----- diff --git a/test/test_ssl/clientcerts/Valid.crt b/test/test_ssl/clientcerts/Valid.crt new file mode 100644 index 000000000..03558ff9c --- /dev/null +++ b/test/test_ssl/clientcerts/Valid.crt @@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + e8:b3:fd:e8:a7:be:5e:a0:ea:3f:23:33:bc:2d:72:da + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=nginx-proxy-test-suite + Validity + Not Before: Jan 3 12:21:59 2025 GMT + Not After : Jan 1 12:21:59 2035 GMT + Subject: CN=Valid + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d8:40:8d:5d:2e:a1:19:de:c5:20:01:fe:dc:9a: + ee:15:fe:9e:e1:30:a0:c7:a2:5a:22:dd:99:6e:dd: + 34:4d:6e:ad:ec:32:17:a6:e4:37:99:11:ea:67:b1: + db:1f:52:7c:17:c8:02:46:04:7f:86:b3:54:6e:55: + 18:26:af:e8:ce:84:c4:52:7c:42:fd:37:ca:ce:bf: + 88:65:09:30:4d:91:55:6f:33:0d:66:bd:e9:49:08: + 4f:65:a3:bd:41:17:67:78:57:41:94:55:6f:da:91: + cb:48:da:a1:df:fb:7d:35:16:c8:1c:ef:9a:d7:fe: + c9:19:4b:c5:89:e1:29:da:5d:01:8f:99:d4:45:da: + 9b:a8:a2:d6:f1:c2:83:36:f5:8f:bd:4b:6a:54:03: + 37:6b:d8:62:e9:15:31:97:34:43:15:a5:6a:df:2f: + b6:df:19:90:4e:53:c5:42:2a:95:b0:94:43:03:02: + 73:a4:f0:20:10:77:71:5d:55:9c:f8:d4:08:11:07: + 94:c6:bb:45:62:93:c5:bd:61:95:75:49:27:ea:b5: + e4:0b:be:27:52:61:9d:cd:11:c8:12:b3:29:d9:3a: + e9:80:6c:65:44:b0:84:cd:7f:8d:e6:ce:07:86:1c: + c1:a2:75:2f:bf:e4:66:b1:b2:b2:2f:5a:fc:44:82: + e4:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + ED:86:4A:75:0A:B2:ED:D1:B8:D9:88:3C:BE:42:DF:8B:18:C3:99:E8 + X509v3 Authority Key Identifier: + keyid:F5:7E:4F:D6:61:7D:64:12:18:39:28:A4:A8:7E:34:6D:49:A9:81:16 + DirName:/CN=nginx-proxy-test-suite + serial:36:F4:8A:C0:24:42:9C:E0:13:B1:76:A5:B8:A4:84:DD:12:3A:7F:2D + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 25:aa:b5:6e:07:0a:3e:d2:ed:61:33:6b:48:ef:81:41:3b:7f: + 02:1c:58:74:13:25:7f:29:d2:5e:14:31:10:7a:12:81:9b:9f: + 6f:3b:b2:3e:07:32:d8:f8:3b:15:5a:18:ec:00:41:15:b1:49: + ea:c1:38:d1:7f:c9:55:bd:dd:99:a0:b9:a8:80:76:f1:6c:db: + 75:bd:23:23:2b:e4:71:9b:d7:fd:74:42:51:31:29:cd:28:32: + a9:d9:09:c5:1f:48:93:e4:6d:e6:45:97:4e:a8:4e:bd:48:d1: + e7:45:e5:34:88:74:aa:98:a3:8d:03:af:c1:10:5b:1d:ec:cb: + 55:90:7c:b4:4c:d0:33:92:63:81:eb:39:50:2b:6e:39:e3:1b: + 74:9d:67:1b:55:76:95:af:8e:83:24:47:c2:fc:aa:c5:7d:f1: + 10:e2:d1:8b:7a:b4:da:5e:40:c4:ce:32:e1:93:d9:17:76:fa: + a2:5d:a8:48:73:ff:0a:b4:b3:ac:97:e4:8b:6e:18:56:09:e0: + 05:be:42:3e:c6:8e:30:cf:29:da:0f:a1:05:c0:7d:3d:8f:8f: + 52:27:e0:dd:06:f2:06:5b:8a:40:e5:0f:5c:a5:3b:16:10:32: + 49:9f:9c:ca:d8:02:9f:f2:5c:b1:20:96:b9:b0:ff:87:7a:20: + 15:1c:4b:88 +-----BEGIN CERTIFICATE----- +MIIDajCCAlKgAwIBAgIRAOiz/einvl6g6j8jM7wtctowDQYJKoZIhvcNAQELBQAw +ITEfMB0GA1UEAwwWbmdpbngtcHJveHktdGVzdC1zdWl0ZTAeFw0yNTAxMDMxMjIx +NTlaFw0zNTAxMDExMjIxNTlaMBAxDjAMBgNVBAMMBVZhbGlkMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ECNXS6hGd7FIAH+3JruFf6e4TCgx6JaIt2Z +bt00TW6t7DIXpuQ3mRHqZ7HbH1J8F8gCRgR/hrNUblUYJq/ozoTEUnxC/TfKzr+I +ZQkwTZFVbzMNZr3pSQhPZaO9QRdneFdBlFVv2pHLSNqh3/t9NRbIHO+a1/7JGUvF +ieEp2l0Bj5nURdqbqKLW8cKDNvWPvUtqVAM3a9hi6RUxlzRDFaVq3y+23xmQTlPF +QiqVsJRDAwJzpPAgEHdxXVWc+NQIEQeUxrtFYpPFvWGVdUkn6rXkC74nUmGdzRHI +ErMp2TrpgGxlRLCEzX+N5s4HhhzBonUvv+RmsbKyL1r8RILk8QIDAQABo4GtMIGq +MAkGA1UdEwQCMAAwHQYDVR0OBBYEFO2GSnUKsu3RuNmIPL5C34sYw5noMFwGA1Ud +IwRVMFOAFPV+T9ZhfWQSGDkopKh+NG1JqYEWoSWkIzAhMR8wHQYDVQQDDBZuZ2lu +eC1wcm94eS10ZXN0LXN1aXRlghQ29IrAJEKc4BOxdqW4pITdEjp/LTATBgNVHSUE +DDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBACWq +tW4HCj7S7WEza0jvgUE7fwIcWHQTJX8p0l4UMRB6EoGbn287sj4HMtj4OxVaGOwA +QRWxSerBONF/yVW93ZmguaiAdvFs23W9IyMr5HGb1/10QlExKc0oMqnZCcUfSJPk +beZFl06oTr1I0edF5TSIdKqYo40Dr8EQWx3sy1WQfLRM0DOSY4HrOVArbjnjG3Sd +ZxtVdpWvjoMkR8L8qsV98RDi0Yt6tNpeQMTOMuGT2Rd2+qJdqEhz/wq0s6yX5Itu +GFYJ4AW+Qj7GjjDPKdoPoQXAfT2Pj1In4N0G8gZbikDlD1ylOxYQMkmfnMrYAp/y +XLEglrmw/4d6IBUcS4g= +-----END CERTIFICATE----- diff --git a/test/test_ssl/clientcerts/Valid.key b/test/test_ssl/clientcerts/Valid.key new file mode 100644 index 000000000..ff63056fb --- /dev/null +++ b/test/test_ssl/clientcerts/Valid.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYQI1dLqEZ3sUg +Af7cmu4V/p7hMKDHoloi3Zlu3TRNbq3sMhem5DeZEepnsdsfUnwXyAJGBH+Gs1Ru +VRgmr+jOhMRSfEL9N8rOv4hlCTBNkVVvMw1mvelJCE9lo71BF2d4V0GUVW/akctI +2qHf+301Fsgc75rX/skZS8WJ4SnaXQGPmdRF2puootbxwoM29Y+9S2pUAzdr2GLp +FTGXNEMVpWrfL7bfGZBOU8VCKpWwlEMDAnOk8CAQd3FdVZz41AgRB5TGu0Vik8W9 +YZV1SSfqteQLvidSYZ3NEcgSsynZOumAbGVEsITNf43mzgeGHMGidS+/5GaxsrIv +WvxEguTxAgMBAAECggEAA9DrzbOncyHrv5tZaIIxZmD2Y8nsZGl4hrn7Xan4pM8p +sYvWwAKx+nkG9mp8j+pwNyk35Q1qRLHAcSv+P5yGErLkFgKMWhSUdx6JJSNK32uL +ouk3ONNsHPZRFF2V1uK3WDfN9/SkAyrkd/YnuiChfoDW6i/OPzaHTPN52muHRguq +z5Iaw9BTTJXFhzdLqd+U6JfdXpn6brmoySXVANYwi/Ug470pR8wyulTIXIhyu8gc +9O9U/QTfpV3uRIxCdci72w9UICVHljPG1ARW1JAZsmTAgxpioGjXcwn5soSNlQ4g +dO6MJ9M7Y/amkvLfAh0Cabj4vRrEJ83TGKaYhlAquQKBgQDwKtQdftvxrQtQiU0Y +7zglGtjBRKtVjb3rcgRs2cvU1GITjIEAuMyMHnyJmGDGRVPdI2us7EZwtpsOmlk1 +nZppLOe478GBA7twaS6vONxBbegZTBtSClyktY8RG5g/siIE6y3m+JcJnZO7XELA +p1r1JUeVLWXJkO2dmgpDjVJIPQKBgQDmgh71G4iLdIxSR/VGmrHX8kwnBRV3rn2+ +O0XcY0XQZPk4XL4zx3Qw5QUC7QMxikaLDQOXKZXylyjyGGyFAjJI8HrLr7Sm7xb4 +g91EjlfQWmTjrvI/6RBndwsExiEzgkASDxm6iOMXFx658tDK6dIsZWhh/59thUVE +u+ZSBp1mxQKBgQDfQVDxIk5fONc9xISw2x+8DlrUPntvClY0GkdW0JeUfuG0/nWl +MCSlVGm8lrPPW/77oMOlefZ5LKazSnQHTTyO7LlzxxyAS/HgK0bEh/znrb2GVqNG +/m7khgo6gwZin7rUC7Md9JSi0aLVFozO/kOlg0QpvovSdjEMwncsGKEWmQKBgQDS +TiuSc1Fr8qTHuVFF3oOdwznJa/D/JZshwZBml8gtbsKWsr7yHOqcZYbh+X4tZ7we +x3vcIZvmHhXEc5Ym8C8Srx1J0wAeQgsSJ7TsBHaH6MEdnhL1Tl2iGFFcRKwsA40T +LOXLc3LFMVneS3RFfXk8+jR3HLLHSI0/PbPQaKqZBQKBgHggyUNuWtSwWTByicnU +MmGv+fb+0NEFN/c+lf2HGb5GFifhFTppAi2L+FT/D5cW+vFpC0f/O7m+ymIoa5YC +fjLvUE6L83D17H/TN34pwxpUxZg/cWl3HkVHRVLnpm4L5HKnt2Qj7q1n4q2FTjhR +vuihVh5K2G3n+Sysf1rViP5I +-----END PRIVATE KEY----- diff --git a/test/test_ssl/compose.base.override.yml b/test/test_ssl/compose.base.override.yml new file mode 100644 index 000000000..6bef2d35b --- /dev/null +++ b/test/test_ssl/compose.base.override.yml @@ -0,0 +1,6 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/acme_root:/usr/share/nginx/html:ro diff --git a/test/test_ssl/test_cert-selection.py b/test/test_ssl/test_cert-selection.py new file mode 100644 index 000000000..c736dab6c --- /dev/null +++ b/test/test_ssl/test_cert-selection.py @@ -0,0 +1,26 @@ +import json + +import pytest + + +@pytest.mark.parametrize("host,expected_cert_ok,expected_cert", [ + ("https://nginx-proxy.tld", True, "nginx-proxy.tld"), + ("https://www.nginx-proxy.tld", True, "nginx-proxy.tld"), + ("http://subdomain.www.nginx-proxy.tld", False, ""), + ("https://web1.nginx-proxy.tld", True, "web1.nginx-proxy.tld"), +]) +def test_certificate_selection( + docker_compose, + nginxproxy, + host: str, + expected_cert_ok: bool, + expected_cert: str, +): + r = nginxproxy.get(f"{host}/nginx-proxy-debug") + assert r.status_code == 200 + try: + jsonResponse = json.loads(r.text) + except ValueError as err: + pytest.fail("Failed to parse debug endpoint response as JSON:: %s" % err, pytrace=False) + assert jsonResponse["vhost"]["cert_ok"] == expected_cert_ok + assert jsonResponse["vhost"]["cert"] == expected_cert diff --git a/test/test_ssl/test_cert-selection.yml b/test/test_ssl/test_cert-selection.yml new file mode 100644 index 000000000..51672f872 --- /dev/null +++ b/test/test_ssl/test_cert-selection.yml @@ -0,0 +1,32 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/cert_selection:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/acme_root:/usr/share/nginx/html:ro + environment: + DEBUG_ENDPOINT: "true" + + base: + image: web + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: "nginx-proxy.tld" + + www: + image: web + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: "www.nginx-proxy.tld" + + sub-www: + image: web + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: "subdomain.www.nginx-proxy.tld" + + web1: + image: web + environment: + WEB_PORTS: "80" + VIRTUAL_HOST: "web1.nginx-proxy.tld" diff --git a/test/test_ssl/test_dhparam.yml b/test/test_ssl/test_dhparam.base.yml similarity index 97% rename from test/test_ssl/test_dhparam.yml rename to test/test_ssl/test_dhparam.base.yml index 456b1b3f8..428015ffa 100644 --- a/test/test_ssl/test_dhparam.yml +++ b/test/test_ssl/test_dhparam.base.yml @@ -1,4 +1,6 @@ -version: "2" +networks: + default: + name: test_dhparam-net services: web5: diff --git a/test/test_ssl/test_dhparam.py b/test/test_ssl/test_dhparam.py index 1cbc0ac9a..9c8ee4d7b 100644 --- a/test/test_ssl/test_dhparam.py +++ b/test/test_ssl/test_dhparam.py @@ -1,3 +1,4 @@ +import platform import re import subprocess @@ -7,6 +8,10 @@ docker_client = docker.from_env() +pytestmark = pytest.mark.skipif( + platform.system() == "Darwin", + reason="Those tests rely entirely on being able to directly contact container's IP" +) ############################################################################### # @@ -61,7 +66,7 @@ def versiontuple(v): @require_openssl("1.0.2") def negotiate_cipher(sut_container, additional_params='', grep='Cipher is'): sut_container.reload() - host = f"{sut_container.attrs['NetworkSettings']['Networks']['test_ssl_default']['IPAddress']}:443" + host = f"{sut_container.attrs['NetworkSettings']['Networks']['test_dhparam-net']['IPAddress']}:443" try: # Enforce TLS 1.2 as newer versions don't support custom dhparam or ciphersuite preference. diff --git a/test/test_ssl/test_hsts.py b/test/test_ssl/test_hsts.py index 890c4adb0..2007e0e6a 100644 --- a/test/test_ssl/test_hsts.py +++ b/test/test_ssl/test_hsts.py @@ -1,6 +1,3 @@ -import pytest - - def test_web1_HSTS_default(docker_compose, nginxproxy): r = nginxproxy.get("https://web1.nginx-proxy.tld/port", allow_redirects=False) assert "answer from port 81\n" in r.text diff --git a/test/test_ssl/test_hsts.yml b/test/test_ssl/test_hsts.yml index b84fb8009..50e624988 100644 --- a/test/test_ssl/test_hsts.yml +++ b/test/test_ssl/test_hsts.yml @@ -1,5 +1,3 @@ -version: "2" - services: web1: image: web @@ -46,9 +44,3 @@ services: VIRTUAL_HOST: http3-vhost-enabled.nginx-proxy.tld labels: com.github.nginx-proxy.nginx-proxy.http3.enable: "true" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro diff --git a/test/test_ssl/test_https_port.py b/test/test_ssl/test_https-port.py similarity index 99% rename from test/test_ssl/test_https_port.py rename to test/test_ssl/test_https-port.py index ebe305f70..6a42f2d43 100644 --- a/test/test_ssl/test_https_port.py +++ b/test/test_ssl/test_https-port.py @@ -1,5 +1,6 @@ import pytest + @pytest.mark.parametrize("subdomain", ["foo", "bar"]) def test_web1_http_redirects_to_https(docker_compose, nginxproxy, subdomain): r = nginxproxy.get("http://%s.nginx-proxy.tld:8080/" % subdomain, allow_redirects=False) diff --git a/test/test_ssl/test_https-port.yml b/test/test_ssl/test_https-port.yml new file mode 100644 index 000000000..de07aa73c --- /dev/null +++ b/test/test_ssl/test_https-port.yml @@ -0,0 +1,16 @@ +services: + nginx-proxy: + environment: + HTTP_PORT: 8080 + HTTPS_PORT: 8443 + ports: + - "8080:8080" + - "8443:8443" + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "*.nginx-proxy.tld" diff --git a/test/test_ssl/test_https_port.yml b/test/test_ssl/test_https_port.yml deleted file mode 100644 index b6541acb0..000000000 --- a/test/test_ssl/test_https_port.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: "2" - -services: - web1: - image: web - expose: - - "81" - environment: - WEB_PORTS: "81" - VIRTUAL_HOST: "*.nginx-proxy.tld" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro - environment: - HTTP_PORT: 8080 - HTTPS_PORT: 8443 diff --git a/test/test_ssl/test_mtls-client-certificate.py b/test/test_ssl/test_mtls-client-certificate.py new file mode 100644 index 000000000..c7c03aea3 --- /dev/null +++ b/test/test_ssl/test_mtls-client-certificate.py @@ -0,0 +1,59 @@ +import pathlib + +import pytest +from requests.exceptions import SSLError + + +@pytest.fixture(scope="session") +def clientcerts(): + """ + Pytest fixture to provide paths to client certificates and keys. + """ + current_file_path = pathlib.Path(__file__) + clientcerts_path = current_file_path.parent.joinpath("clientcerts") + + return { + "valid_client_cert": clientcerts_path.joinpath("Valid.crt"), + "valid_client_key": clientcerts_path.joinpath("Valid.key"), + "revoked_client_cert": clientcerts_path.joinpath("Revoked.crt"), + "revoked_client_key": clientcerts_path.joinpath("Revoked.key"), + } + +@pytest.mark.parametrize("description, url, cert, expected_code, expected_text", [ + #Enforced: Test connection to a website with mTLS enabled without providing a client certificate. + ("Enforced: No client certificate, virtual_host", "https://mtls-enabled.nginx-proxy.tld/port", None, 400, "400 No required SSL certificate was sent"), + ("Enforced: No client certificate, virtual_path", "https://mtls-enabled.nginx-proxy.tld/bar/port", None, 400, "400 No required SSL certificate was sent"), + ("Enforced: No client certificate, regex", "https://regex.nginx-proxy.tld/port", None, 400, "400 No required SSL certificate was sent"), + ("Enforced: No client certificate, global CA", "https://global-mtls-enabled.nginx-proxy.tld/port", None, 400, "400 No required SSL certificate was sent"), + #Authenticated: Test connection to a website with mTLS enabled providing a valid client certificate. + ("Authenticated: Valid client certificate, virtual_host", "https://mtls-enabled.nginx-proxy.tld/port", "valid", 200, "answer from port 81\n"), + ("Authenticated: Valid client certificate, virtual_path", "https://mtls-enabled.nginx-proxy.tld/bar/port", "valid", 200, "answer from port 83\n"), + ("Authenticated: Valid client certificate, regex", "https://regex.nginx-proxy.tld/port", "valid", 200, "answer from port 85\n"), + ("Authenticated: Valid client certificate, global CA", "https://global-mtls-enabled.nginx-proxy.tld/port", "valid", 200, "answer from port 81\n"), + #Revoked: Test connection to a website with mTLS enabled providing a revoked client certificate on the CRL. + ("Revoked: Invalid client certificate, virtual_host", "https://mtls-enabled.nginx-proxy.tld/port", "revoked", 400, "400 The SSL certificate error"), + ("Revoked: Invalid client certificate, virtual_path", "https://mtls-enabled.nginx-proxy.tld/bar/port", "revoked", 400, "400 The SSL certificate error"), + ("Revoked: Invalid client certificate, regex", "https://regex.nginx-proxy.tld/port", "revoked", 400, "400 The SSL certificate error"), + ("Revoked: Invalid client certificate, global CA", "https://global-mtls-enabled.nginx-proxy.tld/port", "revoked", 400, "400 The SSL certificate error"), + #Optional: Test connection to a website with optional mTLS. Access is not blocked but can be controlled with "$ssl_client_verify" directive. We assert on /foo if $ssl_client_verify = SUCCESS response with status code 418. + ("Optional, Not enforced: No client certificate", "https://mtls-optional.nginx-proxy.tld/port", None, 200, "answer from port 82\n"), + ("Optional: Enforced, Valid client certificate", "https://mtls-optional.nginx-proxy.tld/foo/port", "valid", 418, "ssl_client_verify is SUCCESS"), + ("Optional, Not enforced: No client certificate", "https://mtls-optional.nginx-proxy.tld/bar/port", None, 200, "answer from port 84\n"), + ("Optional: Enforced, Valid client certificate", "https://mtls-optional.nginx-proxy.tld/foo/bar/port", "valid", 418, "ssl_client_verify is SUCCESS"), + ("Optional, Not enforced: No client certificate, global CA", "https://global-mtls-optional.nginx-proxy.tld/port", None, 200, "answer from port 82\n"), + ("Optional: Enforced, Valid client certificate, global CA", "https://global-mtls-optional.nginx-proxy.tld/foo/port", "valid", 418, "ssl_client_verify is SUCCESS"), +]) +def test_mtls_client_certificates(docker_compose, nginxproxy, clientcerts, description, url, cert, expected_code, expected_text): + """ + Parameterized test for mTLS client certificate scenarios. + """ + if cert == "valid": + client_cert = (clientcerts["valid_client_cert"], clientcerts["valid_client_key"]) + elif cert == "revoked": + client_cert = (clientcerts["revoked_client_cert"], clientcerts["revoked_client_key"]) + else: + client_cert = None + + r = nginxproxy.get(url, cert=client_cert if client_cert else None) + assert r.status_code == expected_code + assert expected_text in r.text diff --git a/test/test_ssl/test_mtls-client-certificate.yml b/test/test_ssl/test_mtls-client-certificate.yml new file mode 100644 index 000000000..99edc83fd --- /dev/null +++ b/test/test_ssl/test_mtls-client-certificate.yml @@ -0,0 +1,69 @@ +services: + mtls-vhost-enabled: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "mtls-enabled.nginx-proxy.tld,global-mtls-enabled.nginx-proxy.tld" + + mtls-vhost-optional: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST_MULTIPORTS: |- + mtls-optional.nginx-proxy.tld: + "/": + dest: "/" + "/foo": + dest: "/" + global-mtls-optional.nginx-proxy.tld: + "/": + dest: "/" + "/foo": + dest: "/" + labels: + com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional" + + mtls-vpath-enabled: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "mtls-enabled.nginx-proxy.tld" + VIRTUAL_PATH: /bar/ + VIRTUAL_DEST: / + + mtls-vpath-optional: + image: web + expose: + - "84" + environment: + WEB_PORTS: "84" + VIRTUAL_HOST_MULTIPORTS: |- + mtls-optional.nginx-proxy.tld: + "/bar": + dest: "/" + "/foo/bar": + dest: "/" + labels: + com.github.nginx-proxy.nginx-proxy.ssl_verify_client: "optional" + + mtls-regex-enabled: + image: web + expose: + - "85" + environment: + WEB_PORTS: "85" + VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.tld$ + CERT_NAME: nginx-proxy.tld + + nginx-proxy: + volumes: + - ${PYTEST_MODULE_PATH}/certs_mtls:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/certs_mtls/mtls-optional-foo-bar_location:/etc/nginx/vhost.d/mtls-optional.nginx-proxy.tld_6dbd548cc03e44b8b44b6e68e56255ce4273ae49_location:ro #/foo + - ${PYTEST_MODULE_PATH}/certs_mtls/mtls-optional-foo-bar_location:/etc/nginx/vhost.d/mtls-optional.nginx-proxy.tld_a82cce35fd860de6f63f97e6c482dc6a14d002e8_location:ro #/bar + - ${PYTEST_MODULE_PATH}/certs_mtls/mtls-optional-foo-bar_location:/etc/nginx/vhost.d/global-mtls-optional.nginx-proxy.tld_6dbd548cc03e44b8b44b6e68e56255ce4273ae49_location:ro #/foo diff --git a/test/test_ssl/test_nohttp.py b/test/test_ssl/test_nohttp.py index 032f60fd4..23aef783a 100644 --- a/test/test_ssl/test_nohttp.py +++ b/test/test_ssl/test_nohttp.py @@ -1,7 +1,3 @@ -import pytest -import requests - - def test_web2_http_is_connection_refused(docker_compose, nginxproxy): r = nginxproxy.get("http://web2.nginx-proxy.tld/", allow_redirects=False) assert r.status_code == 503 diff --git a/test/test_ssl/test_nohttp.yml b/test/test_ssl/test_nohttp.yml index 0c21bf9ca..4f6398a10 100644 --- a/test/test_ssl/test_nohttp.yml +++ b/test/test_ssl/test_nohttp.yml @@ -1,5 +1,3 @@ -version: "2" - services: web2: image: web @@ -9,10 +7,3 @@ services: WEB_PORTS: "82" VIRTUAL_HOST: "web2.nginx-proxy.tld" HTTPS_METHOD: nohttp - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_ssl/test_nohttps.py b/test/test_ssl/test_nohttps.py index 23e822477..3efacbbb1 100644 --- a/test/test_ssl/test_nohttps.py +++ b/test/test_ssl/test_nohttps.py @@ -1,6 +1,7 @@ import pytest from requests import ConnectionError + def test_http_is_forwarded(docker_compose, nginxproxy): r = nginxproxy.get("http://web.nginx-proxy.tld/port", allow_redirects=False) assert r.status_code == 200 diff --git a/test/test_ssl/test_nohttps.yml b/test/test_ssl/test_nohttps.yml index 209f57a8c..8106e79fe 100644 --- a/test/test_ssl/test_nohttps.yml +++ b/test/test_ssl/test_nohttps.yml @@ -1,5 +1,3 @@ -version: "2" - services: web: image: web @@ -9,9 +7,3 @@ services: WEB_PORTS: "83" VIRTUAL_HOST: "web.nginx-proxy.tld" HTTPS_METHOD: nohttps - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_ssl/test_noredirect.py b/test/test_ssl/test_noredirect.py index 1d956d198..41147f44a 100644 --- a/test/test_ssl/test_noredirect.py +++ b/test/test_ssl/test_noredirect.py @@ -1,6 +1,3 @@ -import pytest - - def test_web3_http_is_forwarded(docker_compose, nginxproxy): r = nginxproxy.get("http://web3.nginx-proxy.tld/port", allow_redirects=False) assert r.status_code == 200 diff --git a/test/test_ssl/test_noredirect.yml b/test/test_ssl/test_noredirect.yml index 7610ae212..e31358a93 100644 --- a/test/test_ssl/test_noredirect.yml +++ b/test/test_ssl/test_noredirect.yml @@ -1,5 +1,3 @@ -version: "2" - services: web3: image: web @@ -9,10 +7,3 @@ services: WEB_PORTS: "83" VIRTUAL_HOST: "web3.nginx-proxy.tld" HTTPS_METHOD: noredirect - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_ssl/test_redirect_custom.py b/test/test_ssl/test_redirect_custom.py new file mode 100644 index 000000000..07c951d98 --- /dev/null +++ b/test/test_ssl/test_redirect_custom.py @@ -0,0 +1,38 @@ +import pytest + + +@pytest.mark.parametrize("host,http_method,expected_code", [ + ("nginx-proxy.tld", "GET", 301), + ("nginx-proxy.tld", "HEAD", 301), + ("nginx-proxy.tld", "POST", 308), + ("nginx-proxy.tld", "PUT", 308), + ("nginx-proxy.tld", "PATCH", 308), + ("nginx-proxy.tld", "DELETE", 308), + ("nginx-proxy.tld", "OPTIONS", 308), + ("nginx-proxy.tld", "CONNECT", 405), + ("nginx-proxy.tld", "TRACE", 405), + ("web2.nginx-proxy.tld", "GET", 301), + ("web2.nginx-proxy.tld", "HEAD", 301), + ("web2.nginx-proxy.tld", "POST", 307), + ("web2.nginx-proxy.tld", "PUT", 307), + ("web2.nginx-proxy.tld", "PATCH", 307), + ("web2.nginx-proxy.tld", "DELETE", 307), + ("web2.nginx-proxy.tld", "OPTIONS", 307), + ("web2.nginx-proxy.tld", "CONNECT", 405), + ("web2.nginx-proxy.tld", "TRACE", 405), +]) +def test_custom_redirect_by_method( + docker_compose, + nginxproxy, + host: str, + http_method: str, + expected_code: int, +): + r = nginxproxy.request( + method=http_method, + url=f'http://{host}', + allow_redirects=False, + ) + assert r.status_code == expected_code + if expected_code in { 301, 302, 307, 308 }: + assert r.headers['Location'] == f'https://{host}/' diff --git a/test/test_ssl/test_redirect_custom.yml b/test/test_ssl/test_redirect_custom.yml new file mode 100644 index 000000000..19f883afe --- /dev/null +++ b/test/test_ssl/test_redirect_custom.yml @@ -0,0 +1,22 @@ +services: + nginx-proxy: + environment: + NON_GET_REDIRECT: 308 + + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "nginx-proxy.tld" + + web2: + image: web + expose: + - "82" + environment: + WEB_PORTS: "82" + VIRTUAL_HOST: "web2.nginx-proxy.tld" + labels: + com.github.nginx-proxy.nginx-proxy.non-get-redirect: 307 diff --git a/test/test_ssl/test_redirect_default.py b/test/test_ssl/test_redirect_default.py new file mode 100644 index 000000000..ca558a00d --- /dev/null +++ b/test/test_ssl/test_redirect_default.py @@ -0,0 +1,28 @@ +import pytest + + +@pytest.mark.parametrize("http_method,expected_code", [ + ("GET", 301), + ("HEAD", 301), + ("POST", 301), + ("PUT", 301), + ("PATCH", 301), + ("DELETE", 301), + ("OPTIONS", 301), + ("CONNECT", 405), + ("TRACE", 405), +]) +def test_default_redirect_by_method( + docker_compose, + nginxproxy, + http_method: str, + expected_code: int, +): + r = nginxproxy.request( + method=http_method, + url='http://nginx-proxy.tld', + allow_redirects=False, + ) + assert r.status_code == expected_code + if expected_code in { 301, 302, 307, 308 }: + assert r.headers['Location'] == 'https://nginx-proxy.tld/' diff --git a/test/test_ssl/test_redirect_default.yml b/test/test_ssl/test_redirect_default.yml new file mode 100644 index 000000000..17044fc54 --- /dev/null +++ b/test/test_ssl/test_redirect_default.yml @@ -0,0 +1,8 @@ +services: + web1: + image: web + expose: + - "81" + environment: + WEB_PORTS: "81" + VIRTUAL_HOST: "nginx-proxy.tld" diff --git a/test/test_ssl/test_virtual_path.py b/test/test_ssl/test_virtual-path.py similarity index 99% rename from test/test_ssl/test_virtual_path.py rename to test/test_ssl/test_virtual-path.py index 72ac433b9..7dae6699b 100644 --- a/test/test_ssl/test_virtual_path.py +++ b/test/test_ssl/test_virtual-path.py @@ -1,6 +1,7 @@ import pytest from requests import ConnectionError + @pytest.mark.parametrize("path", ["web1", "web2"]) def test_web1_http_redirects_to_https(docker_compose, nginxproxy, path): r = nginxproxy.get("http://www.nginx-proxy.tld/%s/port" % path, allow_redirects=False) diff --git a/test/test_ssl/test_virtual_path.yml b/test/test_ssl/test_virtual-path.yml similarity index 64% rename from test/test_ssl/test_virtual_path.yml rename to test/test_ssl/test_virtual-path.yml index eb09ef0f6..bb52a7cb2 100644 --- a/test/test_ssl/test_virtual_path.yml +++ b/test/test_ssl/test_virtual-path.yml @@ -1,5 +1,3 @@ -version: "2" - services: web1: image: web @@ -20,10 +18,3 @@ services: VIRTUAL_HOST: "www.nginx-proxy.tld" VIRTUAL_PATH: "/web2/" VIRTUAL_DEST: "/" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py b/test/test_ssl/test_wildcard-cert-nohttps.py similarity index 100% rename from test/test_ssl/wildcard_cert_and_nohttps/test_wildcard_cert_nohttps.py rename to test/test_ssl/test_wildcard-cert-nohttps.py diff --git a/test/test_ssl/wildcard_cert_and_nohttps/docker-compose.yml b/test/test_ssl/test_wildcard-cert-nohttps.yml similarity index 54% rename from test/test_ssl/wildcard_cert_and_nohttps/docker-compose.yml rename to test/test_ssl/test_wildcard-cert-nohttps.yml index 7cc64e76d..21ca303c5 100644 --- a/test/test_ssl/wildcard_cert_and_nohttps/docker-compose.yml +++ b/test/test_ssl/test_wildcard-cert-nohttps.yml @@ -1,13 +1,16 @@ -version: "3" +# In this scenario, we have a wildcard certificate for `*.web.nginx-proxy.tld` and 3 web containers: +# - 1.web.nginx-proxy.tld +# - 2.web.nginx-proxy.tld +# - 3.web.nginx-proxy.tld +# +# We want web containers 1 and 2 to support SSL, but 3 should not (using `HTTPS_METHOD=nohttps`) services: - - proxy: - image: nginxproxy/nginx-proxy:test + nginx-proxy: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro + - ${PYTEST_MODULE_PATH}/certs_wildcard_nohttps:/etc/nginx/certs:ro + - ${PYTEST_MODULE_PATH}/acme_root:/usr/share/nginx/html:ro web1: image: web diff --git a/test/test_ssl/test_wildcard.yml b/test/test_ssl/test_wildcard.yml index b101e9f11..e2f50a48a 100644 --- a/test/test_ssl/test_wildcard.yml +++ b/test/test_ssl/test_wildcard.yml @@ -1,5 +1,3 @@ -version: "2" - services: web1: image: web @@ -8,10 +6,3 @@ services: environment: WEB_PORTS: "81" VIRTUAL_HOST: "*.nginx-proxy.tld" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - - ./acme_root:/usr/share/nginx/html:ro diff --git a/test/test_ssl/wildcard_cert_and_nohttps/README.md b/test/test_ssl/wildcard_cert_and_nohttps/README.md deleted file mode 100644 index 0ccdd2ee9..000000000 --- a/test/test_ssl/wildcard_cert_and_nohttps/README.md +++ /dev/null @@ -1,6 +0,0 @@ -In this scenario, we have a wildcard certificate for `*.web.nginx-proxy.tld` and 3 web containers: -- 1.web.nginx-proxy.tld -- 2.web.nginx-proxy.tld -- 3.web.nginx-proxy.tld - -We want web containers 1 and 2 to support SSL, but 3 should not (using `HTTPS_METHOD=nohttps`) \ No newline at end of file diff --git a/test/test_ssl/wildcard_cert_and_nohttps/acme_root/.well-known/acme-challenge/test-filename b/test/test_ssl/wildcard_cert_and_nohttps/acme_root/.well-known/acme-challenge/test-filename deleted file mode 100644 index 5b45dff28..000000000 --- a/test/test_ssl/wildcard_cert_and_nohttps/acme_root/.well-known/acme-challenge/test-filename +++ /dev/null @@ -1 +0,0 @@ -challenge-teststring diff --git a/test/test_trust-downstream-proxy/compose.base.override.yml b/test/test_trust-downstream-proxy/compose.base.override.yml new file mode 100644 index 000000000..c51e6ff62 --- /dev/null +++ b/test/test_trust-downstream-proxy/compose.base.override.yml @@ -0,0 +1,5 @@ +services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro diff --git a/test/test_trust-downstream-proxy/test_default.py b/test/test_trust-downstream-proxy/test_default.py index f56c40628..b02bdae44 100644 --- a/test/test_trust-downstream-proxy/test_default.py +++ b/test/test_trust-downstream-proxy/test_default.py @@ -1,6 +1,7 @@ -import pytest import re +import pytest + @pytest.mark.parametrize('url,header,input,want', [ ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'http'), diff --git a/test/test_trust-downstream-proxy/test_default.yml b/test/test_trust-downstream-proxy/test_default.yml index 936dbad53..62e432a28 100644 --- a/test/test_trust-downstream-proxy/test_default.yml +++ b/test/test_trust-downstream-proxy/test_default.yml @@ -1,18 +1,9 @@ -version: "2" - services: web: image: web expose: - "80" environment: - WEB_PORTS: 80 + 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 index 88c8054ca..201069c66 100644 --- a/test/test_trust-downstream-proxy/test_disabled.py +++ b/test/test_trust-downstream-proxy/test_disabled.py @@ -1,6 +1,7 @@ -import pytest import re +import pytest + @pytest.mark.parametrize('url,header,input,want', [ ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'http'), diff --git a/test/test_trust-downstream-proxy/test_disabled.yml b/test/test_trust-downstream-proxy/test_disabled.yml index 9318d6381..ebbedf2c9 100644 --- a/test/test_trust-downstream-proxy/test_disabled.yml +++ b/test/test_trust-downstream-proxy/test_disabled.yml @@ -1,20 +1,13 @@ -version: "2" - services: + nginx-proxy: + environment: + TRUST_DOWNSTREAM_PROXY: "false" + web: image: web expose: - "80" environment: - WEB_PORTS: 80 + 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 index f56c40628..b02bdae44 100644 --- a/test/test_trust-downstream-proxy/test_enabled.py +++ b/test/test_trust-downstream-proxy/test_enabled.py @@ -1,6 +1,7 @@ -import pytest import re +import pytest + @pytest.mark.parametrize('url,header,input,want', [ ('http://web.nginx-proxy.tld/headers', 'X-Forwarded-Proto', None, 'http'), diff --git a/test/test_trust-downstream-proxy/test_enabled.yml b/test/test_trust-downstream-proxy/test_enabled.yml index 9a3e97554..29ef7dcef 100644 --- a/test/test_trust-downstream-proxy/test_enabled.yml +++ b/test/test_trust-downstream-proxy/test_enabled.yml @@ -1,20 +1,13 @@ -version: "2" - services: + nginx-proxy: + environment: + TRUST_DOWNSTREAM_PROXY: "true" + web: image: web expose: - "80" environment: - WEB_PORTS: 80 + 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 diff --git a/test/stress_tests/test_unreachable_network/README.md b/test/test_unreachable-network/README.md similarity index 100% rename from test/stress_tests/test_unreachable_network/README.md rename to test/test_unreachable-network/README.md diff --git a/test/stress_tests/test_unreachable_network/test_unreachable_net.py b/test/test_unreachable-network/test_unreachable-network.py similarity index 100% rename from test/stress_tests/test_unreachable_network/test_unreachable_net.py rename to test/test_unreachable-network/test_unreachable-network.py diff --git a/test/stress_tests/test_unreachable_network/docker-compose.yml b/test/test_unreachable-network/test_unreachable-network.yml similarity index 67% rename from test/stress_tests/test_unreachable_network/docker-compose.yml rename to test/test_unreachable-network/test_unreachable-network.yml index f90a441e7..5b2cb2af2 100644 --- a/test/stress_tests/test_unreachable_network/docker-compose.yml +++ b/test/test_unreachable-network/test_unreachable-network.yml @@ -1,17 +1,12 @@ -version: "2" - networks: netA: netB: services: - reverseproxy: + nginx-proxy: container_name: reverseproxy networks: - netA - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro webA: networks: @@ -20,7 +15,7 @@ services: expose: - 81 environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: webA.nginx-proxy webB: @@ -30,5 +25,5 @@ services: expose: - 82 environment: - WEB_PORTS: 82 + WEB_PORTS: "82" VIRTUAL_HOST: webB.nginx-proxy diff --git a/test/test_upstream-name/test_predictable-name.py b/test/test_upstream-name/test_predictable-name.py index 7e196461e..67ade3bde 100644 --- a/test/test_upstream-name/test_predictable-name.py +++ b/test/test_upstream-name/test_predictable-name.py @@ -1,4 +1,3 @@ -import pytest import re diff --git a/test/test_upstream-name/test_predictable-name.yml b/test/test_upstream-name/test_predictable-name.yml index b5caad0fb..bc81fb366 100644 --- a/test/test_upstream-name/test_predictable-name.yml +++ b/test/test_upstream-name/test_predictable-name.yml @@ -1,15 +1,8 @@ -version: "2" - services: web: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: web.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_upstream-name/test_sha1-name.py b/test/test_upstream-name/test_sha1-name.py index 663ca28b5..8993440ac 100644 --- a/test/test_upstream-name/test_sha1-name.py +++ b/test/test_upstream-name/test_sha1-name.py @@ -1,4 +1,3 @@ -import pytest import re diff --git a/test/test_upstream-name/test_sha1-name.yml b/test/test_upstream-name/test_sha1-name.yml index b52a5c946..83c767d98 100644 --- a/test/test_upstream-name/test_sha1-name.yml +++ b/test/test_upstream-name/test_sha1-name.yml @@ -1,17 +1,12 @@ -version: "2" - services: + nginx-proxy: + environment: + SHA1_UPSTREAM_NAME: "true" + web: image: web expose: - "80" environment: - WEB_PORTS: 80 + WEB_PORTS: "80" VIRTUAL_HOST: web.nginx-proxy.tld - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - environment: - SHA1_UPSTREAM_NAME: "true" diff --git a/test/test_vhost-empty-string.py b/test/test_vhost-empty-string/test_vhost-empty-string.py similarity index 100% rename from test/test_vhost-empty-string.py rename to test/test_vhost-empty-string/test_vhost-empty-string.py diff --git a/test/test_vhost-empty-string.yml b/test/test_vhost-empty-string/test_vhost-empty-string.yml similarity index 86% rename from test/test_vhost-empty-string.yml rename to test/test_vhost-empty-string/test_vhost-empty-string.yml index 9d2ff529e..4daf5c2e1 100644 --- a/test/test_vhost-empty-string.yml +++ b/test/test_vhost-empty-string/test_vhost-empty-string.yml @@ -1,11 +1,4 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - web1: image: web expose: diff --git a/test/test_vhost-in-multiple-networks.py b/test/test_vhost-in-multiple-networks/test_vhost-in-multiple-networks.py similarity index 91% rename from test/test_vhost-in-multiple-networks.py rename to test/test_vhost-in-multiple-networks/test_vhost-in-multiple-networks.py index 02ef19ea8..6b30b530a 100644 --- a/test/test_vhost-in-multiple-networks.py +++ b/test/test_vhost-in-multiple-networks/test_vhost-in-multiple-networks.py @@ -1,6 +1,8 @@ -import pytest import logging -import time +from time import sleep + +import pytest + def test_forwards_to_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy.example/port") @@ -11,11 +13,11 @@ 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(): + def get_conf_after_web_container_restart() -> bytes: web_containers = docker_compose.containers.list(filters={"ancestor": "web:latest"}) assert len(web_containers) == 1 web_containers[0].restart() - time.sleep(3) + sleep(3) return nginxproxy.get_conf() diff --git a/test/test_vhost-in-multiple-networks.yml b/test/test_vhost-in-multiple-networks/test_vhost-in-multiple-networks.yml similarity index 57% rename from test/test_vhost-in-multiple-networks.yml rename to test/test_vhost-in-multiple-networks/test_vhost-in-multiple-networks.yml index 4e9bb3e9e..5b8b1cfa3 100644 --- a/test/test_vhost-in-multiple-networks.yml +++ b/test/test_vhost-in-multiple-networks/test_vhost-in-multiple-networks.yml @@ -1,15 +1,10 @@ -version: "2" - networks: - net1: {} - net2: {} - net3: {} + net1: + net2: + net3: services: nginx-proxy: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro networks: - net1 @@ -18,7 +13,7 @@ services: expose: - "81" environment: - WEB_PORTS: 81 + WEB_PORTS: "81" VIRTUAL_HOST: web1.nginx-proxy.example networks: - net1 diff --git a/test/test_virtual-path/test_custom_conf.py b/test/test_virtual-path/test_custom-conf.py similarity index 99% rename from test/test_virtual-path/test_custom_conf.py rename to test/test_virtual-path/test_custom-conf.py index eec149f37..48a89b3b6 100644 --- a/test/test_virtual-path/test_custom_conf.py +++ b/test/test_virtual-path/test_custom-conf.py @@ -1,5 +1,6 @@ import pytest + def test_default_root_response(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy.test/") assert r.status_code == 418 diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom-conf.yml similarity index 68% rename from test/test_virtual-path/test_custom_conf.yml rename to test/test_virtual-path/test_custom-conf.yml index 2c4d6b9e8..b699352b6 100644 --- a/test/test_virtual-path/test_custom_conf.yml +++ b/test/test_virtual-path/test_custom-conf.yml @@ -1,6 +1,13 @@ -version: "2" - services: + nginx-proxy: + environment: + DEFAULT_ROOT: 418 + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro + - ${PYTEST_MODULE_PATH}/bar.conf:/etc/nginx/vhost.d/nginx-proxy.test_918d687a929083edd0c7224ee2293e0e7c062ab4_location:ro + - ${PYTEST_MODULE_PATH}/alternate.conf:/etc/nginx/vhost.d/nginx-proxy.test_7fb22b74bbdf907425dbbad18e4462565cada230_location:ro + foo: image: web expose: @@ -37,13 +44,3 @@ services: WEB_PORTS: "83" VIRTUAL_HOST: "nginx-proxy.test" VIRTUAL_PATH: "~ ^/(web3|alt)/" - - sut: - image: nginxproxy/nginx-proxy:test - environment: - DEFAULT_ROOT: 418 - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro - - ./bar.conf:/etc/nginx/vhost.d/nginx-proxy.test_918d687a929083edd0c7224ee2293e0e7c062ab4_location:ro - - ./alternate.conf:/etc/nginx/vhost.d/nginx-proxy.test_7fb22b74bbdf907425dbbad18e4462565cada230_location:ro diff --git a/test/test_virtual-path/test_default-root-none.yml b/test/test_virtual-path/test_default-root-none.yml index f0172e8f5..2f7cddace 100644 --- a/test/test_virtual-path/test_default-root-none.yml +++ b/test/test_virtual-path/test_default-root-none.yml @@ -1,10 +1,5 @@ -version: "2" - services: - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro + nginx-proxy: environment: DEFAULT_ROOT: none diff --git a/test/test_virtual-path/test_forwarding.py b/test/test_virtual-path/test_forwarding.py index 062dd6cc3..9035ad99e 100644 --- a/test/test_virtual-path/test_forwarding.py +++ b/test/test_virtual-path/test_forwarding.py @@ -1,5 +1,3 @@ -import pytest - def test_root_redirects_to_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://www.nginx-proxy.tld/port", allow_redirects=False) assert r.status_code == 301 diff --git a/test/test_virtual-path/test_forwarding.yml b/test/test_virtual-path/test_forwarding.yml index 9b15c7416..8988da0c6 100644 --- a/test/test_virtual-path/test_forwarding.yml +++ b/test/test_virtual-path/test_forwarding.yml @@ -1,6 +1,11 @@ -version: "2" - services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro + environment: + DEFAULT_ROOT: "301 http://$$host/web1$$request_uri" + web1: image: web expose: @@ -10,11 +15,3 @@ services: VIRTUAL_HOST: "www.nginx-proxy.tld" VIRTUAL_PATH: "/web1/" VIRTUAL_DEST: "/" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./certs:/etc/nginx/certs:ro - environment: - - DEFAULT_ROOT=301 http://$$host/web1$$request_uri diff --git a/test/test_virtual-path/test_location_precedence.py b/test/test_virtual-path/test_location-precedence.py similarity index 98% rename from test/test_virtual-path/test_location_precedence.py rename to test/test_virtual-path/test_location-precedence.py index 415c6c1c8..9d4d1ec49 100644 --- a/test/test_virtual-path/test_location_precedence.py +++ b/test/test_virtual-path/test_location-precedence.py @@ -1,5 +1,3 @@ -import pytest - def test_location_precedence_case1(docker_compose, nginxproxy): r = nginxproxy.get(f"http://foo.nginx-proxy.test/web1/port") assert r.status_code == 200 diff --git a/test/test_virtual-path/test_location_precedence.yml b/test/test_virtual-path/test_location-precedence.yml similarity index 65% rename from test/test_virtual-path/test_location_precedence.yml rename to test/test_virtual-path/test_location-precedence.yml index a250a1d27..9d948a5fd 100644 --- a/test/test_virtual-path/test_location_precedence.yml +++ b/test/test_virtual-path/test_location-precedence.yml @@ -1,6 +1,11 @@ -version: "2" - services: + nginx-proxy: + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ${PYTEST_MODULE_PATH}/default.conf:/etc/nginx/vhost.d/default_location:ro + - ${PYTEST_MODULE_PATH}/host.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_location:ro + - ${PYTEST_MODULE_PATH}/path.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_99f2db0ed8aa95dbb5b87fca79c7eff2ff6bb8bd_location:ro + web1: image: web expose: @@ -30,11 +35,3 @@ services: VIRTUAL_HOST: "bar.nginx-proxy.test" VIRTUAL_PATH: "/web3/" VIRTUAL_DEST: "/" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./default.conf:/etc/nginx/vhost.d/default_location:ro - - ./host.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_location:ro - - ./path.conf:/etc/nginx/vhost.d/bar.nginx-proxy.test_99f2db0ed8aa95dbb5b87fca79c7eff2ff6bb8bd_location:ro diff --git a/test/test_virtual-path/test_virtual_paths.py b/test/test_virtual-path/test_virtual-paths.py similarity index 94% rename from test/test_virtual-path/test_virtual_paths.py rename to test/test_virtual-path/test_virtual-paths.py index a91a8dd42..a8cd276aa 100644 --- a/test/test_virtual-path/test_virtual_paths.py +++ b/test/test_virtual-path/test_virtual-paths.py @@ -3,6 +3,7 @@ import pytest from docker.errors import NotFound + @pytest.mark.parametrize("stub,expected_port", [ ("nginx-proxy.test/web1", 81), ("nginx-proxy.test/web2", 82), @@ -22,7 +23,7 @@ def test_invalid_path(docker_compose, nginxproxy, stub): r = nginxproxy.get(f"http://{stub}/port") assert r.status_code in [404, 503] -@pytest.fixture() +@pytest.fixture def web4(docker_compose): """ pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy.test`, `VIRTUAL_PATH=/web4/` and `VIRTUAL_DEST=/` listening on port 84. @@ -39,7 +40,7 @@ def web4(docker_compose): }, ports={"84/tcp": None} ) - docker_compose.networks.get("test_virtual-path_default").connect(container) + docker_compose.networks.get("test_virtual-path-net").connect(container) sleep(2) # give it some time to initialize and for docker-gen to detect it yield container try: diff --git a/test/test_virtual-path/test_virtual_paths.yml b/test/test_virtual-path/test_virtual-paths.yml similarity index 84% rename from test/test_virtual-path/test_virtual_paths.yml rename to test/test_virtual-path/test_virtual-paths.yml index ac70297ec..47fb7ffef 100644 --- a/test/test_virtual-path/test_virtual_paths.yml +++ b/test/test_virtual-path/test_virtual-paths.yml @@ -1,4 +1,6 @@ -version: "2" +networks: + default: + name: "test_virtual-path-net" services: foo: @@ -37,8 +39,3 @@ services: WEB_PORTS: "83" VIRTUAL_HOST: "nginx-proxy.test" VIRTUAL_PATH: "/" - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/test/test_virtual-proto/nginx-proxy.tld.crt b/test/test_virtual-proto/nginx-proxy.tld.crt new file mode 100644 index 000000000..cd7284b06 --- /dev/null +++ b/test/test_virtual-proto/nginx-proxy.tld.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld + Validity + Not Before: Jan 10 00:08:52 2017 GMT + Not After : May 28 00:08:52 2044 GMT + Subject: CN=*.nginx-proxy.tld + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:cb:45:f4:14:9b:fe:64:85:79:4a:36:8d:3d:d1: + 27:d0:7c:36:28:30:e6:73:80:6f:7c:49:23:d0:6c: + 17:e4:44:c0:77:4d:9a:c2:bc:24:84:e3:a5:4d:ba: + d2:da:51:7b:a1:2a:12:d4:c0:19:55:69:2c:22:27: + 2d:1a:f6:fc:4b:7f:e9:cb:a8:3c:e8:69:b8:d2:4f: + de:4e:50:e2:d0:74:30:7c:42:5a:ae:aa:85:a5:b1: + 71:4d:c9:7e:86:8b:62:8c:3e:0d:e3:3b:c3:f5:81: + 0b:8c:68:79:fe:bf:10:fb:ae:ec:11:49:6d:64:5e: + 1a:7d:b3:92:93:4e:96:19:3a:98:04:a7:66:b2:74: + 61:2d:41:13:0c:a4:54:0d:2c:78:fd:b4:a3:e8:37: + 78:9a:de:fa:bc:2e:a8:0f:67:14:58:ce:c3:87:d5: + 14:0e:8b:29:7d:48:19:b2:a9:f5:b4:e8:af:32:21: + 67:15:7e:43:52:8b:20:cf:9f:38:43:bf:fd:c8:24: + 7f:52:a3:88:f2:f1:4a:14:91:2a:6e:91:6f:fb:7d: + 6a:78:c6:6d:2e:dd:1e:4c:2b:63:bb:3a:43:9c:91: + f9:df:d3:08:13:63:86:7d:ce:e8:46:cf:f1:6c:1f: + ca:f7:4c:de:d8:4b:e0:da:bc:06:d9:87:0f:ff:96: + 45:85 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:*.nginx-proxy.tld + Signature Algorithm: sha256WithRSAEncryption + 6e:a5:0e:e4:d3:cc:d5:b7:fc:34:75:89:4e:98:8c:e7:08:06: + a8:5b:ec:13:7d:83:99:a2:61:b8:d5:12:6e:c5:b4:53:4e:9a: + 22:cd:ad:14:30:6a:7d:58:d7:23:d9:a4:2a:96:a0:40:9e:50: + 9f:ce:f2:fe:8c:dd:9a:ac:99:39:5b:89:2d:ca:e5:3e:c3:bc: + 03:04:1c:12:d9:6e:b8:9f:f0:3a:be:12:44:7e:a4:21:86:73: + af:d5:00:51:3f:2c:56:70:34:8f:26:b0:7f:b0:cf:cf:7f:f9: + 40:6f:00:29:c4:cf:c3:b7:c2:49:3d:3f:b0:26:78:87:b9:c7: + 6c:1b:aa:6a:1a:dd:c5:eb:f2:69:ba:6d:46:0b:92:49:b5:11: + 3c:eb:48:c7:2f:fb:33:a6:6a:82:a2:ab:f8:1e:5f:7d:e3:b7: + f2:fd:f5:88:a5:09:4d:a0:bc:f4:3b:cd:d2:8b:d7:57:1f:86: + 3b:d2:3e:a4:92:21:b0:02:0b:e9:e0:c4:1c:f1:78:e2:58:a7: + 26:5f:4c:29:c8:23:f0:6e:12:3f:bd:ad:44:7b:0b:bd:db:ba: + 63:8d:07:c6:9d:dc:46:cc:63:40:ba:5e:45:82:dd:9a:e5:50: + e8:e7:d7:27:88:fc:6f:1d:8a:e7:5c:49:28:aa:10:29:75:28: + c7:52:de:f9 +-----BEGIN CERTIFICATE----- +MIIC9zCCAd+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp +bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs +ZDAeFw0xNzAxMTAwMDA4NTJaFw00NDA1MjgwMDA4NTJaMBwxGjAYBgNVBAMMESou +bmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +y0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02awrwkhOOlTbrS2lF7 +oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJarqqFpbFxTcl+hoti +jD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdmsnRhLUETDKRUDSx4 +/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFnFX5DUosgz584Q7/9 +yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MIE2OGfc7oRs/xbB/K +90ze2Evg2rwG2YcP/5ZFhQIDAQABoyAwHjAcBgNVHREEFTATghEqLm5naW54LXBy +b3h5LnRsZDANBgkqhkiG9w0BAQsFAAOCAQEAbqUO5NPM1bf8NHWJTpiM5wgGqFvs +E32DmaJhuNUSbsW0U06aIs2tFDBqfVjXI9mkKpagQJ5Qn87y/ozdmqyZOVuJLcrl +PsO8AwQcEtluuJ/wOr4SRH6kIYZzr9UAUT8sVnA0jyawf7DPz3/5QG8AKcTPw7fC +ST0/sCZ4h7nHbBuqahrdxevyabptRguSSbURPOtIxy/7M6ZqgqKr+B5ffeO38v31 +iKUJTaC89DvN0ovXVx+GO9I+pJIhsAIL6eDEHPF44linJl9MKcgj8G4SP72tRHsL +vdu6Y40Hxp3cRsxjQLpeRYLdmuVQ6OfXJ4j8bx2K51xJKKoQKXUox1Le+Q== +-----END CERTIFICATE----- diff --git a/test/test_virtual-proto/nginx-proxy.tld.key b/test/test_virtual-proto/nginx-proxy.tld.key new file mode 100644 index 000000000..91adb14e1 --- /dev/null +++ b/test/test_virtual-proto/nginx-proxy.tld.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAy0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02a +wrwkhOOlTbrS2lF7oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJa +rqqFpbFxTcl+hotijD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdm +snRhLUETDKRUDSx4/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFn +FX5DUosgz584Q7/9yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MI +E2OGfc7oRs/xbB/K90ze2Evg2rwG2YcP/5ZFhQIDAQABAoIBAQCjAro2PNLJMfCO +fyjNRgmzu6iCmpR0U68T8GN0JPsT576g7e8J828l0pkhuIyW33lRSThIvLSUNf9a +dChL032H3lBTLduKVh4NKleQXnVFzaeEPoISSFVdButiAhAhPW4OIUVp0OfY3V+x +fac3j2nDLAfL5SKAtqZv363Py9m66EBYm5BmGTQqT/frQWeCEBvlErQef5RIaU8p +e2zMWgSNNojVai8U3nKNRvYHWeWXM6Ck7lCvkHhMF+RpbmCZuqhbEARVnehU/Jdn +QHJ3nxeA2OWpoWKXvAHtSnno49yxq1UIstiQvY+ng5C5i56UlB60UiU2NJ6doZkB +uQ7/1MaBAoGBAORdcFtgdgRALjXngFWhpCp0CseyUehn1KhxDCG+D1pJ142/ymcf +oJOzKJPMRNDdDUBMnR1GBfy7rmwvYevI/SMNy2Qs7ofcXPbdtwwvTCToZ1V9/54k +VfuPBFT+3QzWRvG1tjTV3E4L2VV3nrl2qNPhE5DlfIaU3nQq5Fl0HprJAoGBAOPf +MWOTGev61CdODO5KN3pLAoamiPs5lEUlz3kM3L1Q52YLITxNDjRj9hWBUATJZOS2 +pLOoYRwmhD7vrnimMc41+NuuFX+4T7hWPc8uSuOxX0VijYtULyNRK57mncG1Fq9M +RMLbOJ7FD+8jdXNsSMqpQ+pxLJRX/A10O2fOQnbdAoGAL5hV4YWSM0KZHvz332EI +ER0MXiCJN7HkPZMKH0I4eu3m8hEmAyYxVndBnsQ1F37q0xrkqAQ/HTSUntGlS/og +4Bxw5pkCwegoq/77tpto+ExDtSrEitYx4XMmSPyxX4qNULU5m3tzJgUML+b1etwD +Rd2kMU/TC02dq4KBAy/TbRkCgYAl1xN5iJz+XenLGR/2liZ+TWR+/bqzlU006mF4 +pZUmbv/uJxz+yYD5XDwqOA4UrWjuvhG9r9FoflDprp2XdWnB556KxG7XhcDfSJr9 +A5/2DadXe1Ur9O/a+oi2228JEsxQkea9QPA3FVxfBtFjOHEiDlez39VaUP4PMeUH +iO3qlQKBgFQhdTb7HeYnApYIDHLmd1PvjRvp8XKR1CpEN0nkw8HpHcT1q1MUjQCr +iT6FQupULEvGmO3frQsgVeRIQDbEdZK3C5xCtn6qOw70sYATVf361BbTtidmU9yV +THFxwDSVLiVZgFryoY/NtAc27sVdJnGsPRjjaeVgALAsLbmZ1K/H +-----END RSA PRIVATE KEY----- diff --git a/test/test_virtual-proto/ssl-app-multi-ports.conf b/test/test_virtual-proto/ssl-app-multi-ports.conf new file mode 100644 index 000000000..03956b2ea --- /dev/null +++ b/test/test_virtual-proto/ssl-app-multi-ports.conf @@ -0,0 +1,26 @@ +server { + listen 80; + server_name web2.nginx-proxy.tld; + + location / { + default_type text/plain; + return 200 'This is web2.nginx-proxy.tld'; + } +} + +server { + listen 443 ssl; + server_name web3.nginx-proxy.tld; + + ssl_certificate /etc/nginx/certs/server.crt; + ssl_certificate_key /etc/nginx/certs/server.key; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + 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'; + + location / { + default_type text/plain; + return 200 'This is web3.nginx-proxy.tld'; + } +} diff --git a/test/test_virtual-proto/ssl-app-single-port.conf b/test/test_virtual-proto/ssl-app-single-port.conf new file mode 100644 index 000000000..26c7b6633 --- /dev/null +++ b/test/test_virtual-proto/ssl-app-single-port.conf @@ -0,0 +1,16 @@ +server { + listen 443 ssl; + server_name web1.nginx-proxy.tld; + + ssl_certificate /etc/nginx/certs/server.crt; + ssl_certificate_key /etc/nginx/certs/server.key; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + 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'; + + location / { + default_type text/plain; + return 200 'This is web1.nginx-proxy.tld'; + } +} diff --git a/test/test_virtual-proto/test_virtual-proto.py b/test/test_virtual-proto/test_virtual-proto.py new file mode 100644 index 000000000..b605b1312 --- /dev/null +++ b/test/test_virtual-proto/test_virtual-proto.py @@ -0,0 +1,8 @@ +import pytest + + +@pytest.mark.parametrize('subdomain', ['web1', 'web2', 'web3']) +def test_upstream_with_https_virtual_proto(docker_compose, nginxproxy, subdomain): + r = nginxproxy.get(f"http://{subdomain}.nginx-proxy.tld") + assert r.status_code == 200 + assert r.text == f"This is {subdomain}.nginx-proxy.tld" diff --git a/test/test_virtual-proto/test_virtual-proto.yml b/test/test_virtual-proto/test_virtual-proto.yml new file mode 100644 index 000000000..60d072f31 --- /dev/null +++ b/test/test_virtual-proto/test_virtual-proto.yml @@ -0,0 +1,25 @@ +services: + ssl-app-single-port: + image: nginx:alpine + volumes: + - ${PYTEST_MODULE_PATH}/ssl-app-single-port.conf:/etc/nginx/conf.d/default.conf:ro + - ${PYTEST_MODULE_PATH}/nginx-proxy.tld.crt:/etc/nginx/certs/server.crt:ro + - ${PYTEST_MODULE_PATH}/nginx-proxy.tld.key:/etc/nginx/certs/server.key:ro + environment: + VIRTUAL_HOST: "web1.nginx-proxy.tld" + VIRTUAL_PROTO: "https" + VIRTUAL_PORT: "443" + + ssl-app-multi-ports: + image: nginx:alpine + volumes: + - ${PYTEST_MODULE_PATH}/ssl-app-multi-ports.conf:/etc/nginx/conf.d/default.conf:ro + - ${PYTEST_MODULE_PATH}/nginx-proxy.tld.crt:/etc/nginx/certs/server.crt:ro + - ${PYTEST_MODULE_PATH}/nginx-proxy.tld.key:/etc/nginx/certs/server.key:ro + environment: + VIRTUAL_HOST_MULTIPORTS: |- + web2.nginx-proxy.tld: + web3.nginx-proxy.tld: + "/": + port: 443 + proto: "https" diff --git a/test/test_wildcard_host.py b/test/test_wildcard-host/test_wildcard-host.py similarity index 100% rename from test/test_wildcard_host.py rename to test/test_wildcard-host/test_wildcard-host.py diff --git a/test/test_wildcard_host.yml b/test/test_wildcard-host/test_wildcard-host.yml similarity index 83% rename from test/test_wildcard_host.yml rename to test/test_wildcard-host/test_wildcard-host.yml index 47e75e447..f82f7bcfb 100644 --- a/test/test_wildcard_host.yml +++ b/test/test_wildcard-host/test_wildcard-host.yml @@ -1,5 +1,3 @@ -version: "2" - services: web1: image: web @@ -32,8 +30,3 @@ services: environment: WEB_PORTS: "84" VIRTUAL_HOST: ~^web4\..*\.nginx-proxy\.regexp$$ # we need to double the `$` because of docker compose variable interpolation - - sut: - image: nginxproxy/nginx-proxy:test - volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro