Compare commits
85 commits
Author | SHA1 | Date | |
---|---|---|---|
4856fbe7eb | |||
23d56b2185 | |||
![]() |
1da623019f | ||
![]() |
e234ffba20 | ||
![]() |
18030a7896 | ||
![]() |
dfbff1eb9c | ||
![]() |
34a33a2255 | ||
![]() |
a61e485410 | ||
![]() |
9312d5239a | ||
![]() |
9fc7cec15c | ||
![]() |
8447a36046 | ||
![]() |
923f05032f | ||
![]() |
820d4a29ac | ||
![]() |
1859811311 | ||
![]() |
691724c81f | ||
![]() |
aa8145b62d | ||
![]() |
836012cad6 | ||
![]() |
005377c6e5 | ||
![]() |
bfdd72fe95 | ||
![]() |
40309e2441 | ||
![]() |
daa9449176 | ||
![]() |
4ccbc3edec | ||
![]() |
1f732a54c6 | ||
![]() |
ae0c9a8e96 | ||
![]() |
ea99c1a6f9 | ||
![]() |
1e9745f604 | ||
![]() |
7b6baa43cd | ||
![]() |
a2c316a876 | ||
![]() |
fb0fc331c0 | ||
![]() |
eb09876f97 | ||
![]() |
35e2d21527 | ||
![]() |
b5dea1cf50 | ||
![]() |
c60eff5d16 | ||
![]() |
142a159d56 | ||
![]() |
92eb45f0ec | ||
![]() |
2bb0bbe5c9 | ||
![]() |
fbad3ac3fc | ||
![]() |
0c4be4e90f | ||
![]() |
b9657874b9 | ||
![]() |
56a4bb8601 | ||
![]() |
c67739f1cc | ||
![]() |
a25b7ea1ef | ||
![]() |
22e6e59034 | ||
![]() |
0c141e64ba | ||
![]() |
3c5bbcf029 | ||
![]() |
9bd84fc95e | ||
![]() |
b6c8851794 | ||
![]() |
30299e69bc | ||
![]() |
271e31dec4 | ||
![]() |
559ddc7d13 | ||
![]() |
e96a3ddec2 | ||
![]() |
f0034c661a | ||
![]() |
7c003a4aa4 | ||
![]() |
ac4ce5b775 | ||
![]() |
baa2ce5e8b | ||
![]() |
993bcc07c0 | ||
![]() |
07b469d8ac | ||
![]() |
ce2e076d65 | ||
![]() |
d4b1925624 | ||
![]() |
d433b3bee6 | ||
![]() |
7fee758a70 | ||
![]() |
d30b573b2c | ||
![]() |
f600ce3e11 | ||
![]() |
b4c2b0dd5e | ||
![]() |
12b482a7ea | ||
![]() |
c6664335b7 | ||
![]() |
f54f89a5f3 | ||
![]() |
760c891a0c | ||
![]() |
627b6ad75f | ||
![]() |
b0efe80f05 | ||
![]() |
90e9308f87 | ||
![]() |
f325dadd6a | ||
![]() |
4d03645200 | ||
![]() |
57016e43f3 | ||
![]() |
c944877b70 | ||
![]() |
52ffab1ce6 | ||
![]() |
7bd26d624d | ||
![]() |
40c153e8b0 | ||
![]() |
469765bbb7 | ||
![]() |
023a3d17da | ||
![]() |
57e503c830 | ||
![]() |
f0f71f72f7 | ||
![]() |
dcc97b9cff | ||
![]() |
5aea820aaa | ||
![]() |
a4c694fefc |
253 changed files with 2117 additions and 1336 deletions
85
.github/workflows/build-publish-dispatch.yml
vendored
Normal file
85
.github/workflows/build-publish-dispatch.yml
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
name: Build and publish Docker images on demand
|
||||
|
||||
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@v4
|
||||
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 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 <nicolas.duchon@gmail.com> (@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 }}
|
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
|
@ -25,10 +25,10 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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 +36,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
|
||||
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
**/__pycache__/
|
||||
**/.cache/
|
||||
.idea/
|
||||
wip
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
FROM docker.io/nginxproxy/docker-gen:0.14.3 AS docker-gen
|
||||
FROM docker.io/nginxproxy/docker-gen:0.14.5 AS docker-gen
|
||||
|
||||
FROM docker.io/nginxproxy/forego:0.18.2 AS forego
|
||||
|
||||
# Build the final image
|
||||
FROM docker.io/library/nginx:1.27.2-alpine
|
||||
FROM docker.io/library/nginx:1.27.3-alpine
|
||||
|
||||
ARG NGINX_PROXY_VERSION
|
||||
# Add DOCKER_GEN_VERSION environment variable because
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
FROM docker.io/nginxproxy/docker-gen:0.14.3-debian AS docker-gen
|
||||
FROM docker.io/nginxproxy/docker-gen:0.14.5-debian AS docker-gen
|
||||
|
||||
FROM docker.io/nginxproxy/forego:0.18.2-debian AS forego
|
||||
|
||||
# Build the final image
|
||||
FROM docker.io/library/nginx:1.27.2
|
||||
FROM docker.io/library/nginx:1.27.3
|
||||
|
||||
ARG NGINX_PROXY_VERSION
|
||||
# Add DOCKER_GEN_VERSION environment variable because
|
||||
|
|
38
README.md
38
README.md
|
@ -1,6 +1,6 @@
|
|||
[](https://github.com/nginx-proxy/nginx-proxy/actions/workflows/test.yml)
|
||||
[](https://github.com/nginx-proxy/nginx-proxy/releases)
|
||||
[](https://nginx.org/en/CHANGES)
|
||||
[](https://nginx.org/en/CHANGES)
|
||||
[](https://hub.docker.com/r/nginxproxy/nginx-proxy "Click to view the image on Docker Hub")
|
||||
[](https://hub.docker.com/r/nginxproxy/nginx-proxy "DockerHub")
|
||||
[](https://hub.docker.com/r/nginxproxy/nginx-proxy "DockerHub")
|
||||
|
@ -20,7 +20,17 @@ docker run --detach \
|
|||
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
|
||||
nginxproxy/nginx-proxy:1.6
|
||||
```
|
||||
|
||||
docker-compose
|
||||
```docker-compose
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/tmp/docker.sock"
|
||||
```
|
||||
Then start any containers (here an nginx container) you want proxied with an env var `VIRTUAL_HOST=subdomain.yourdomain.com`
|
||||
|
||||
```console
|
||||
|
@ -29,7 +39,12 @@ docker run --detach \
|
|||
--env VIRTUAL_HOST=foo.bar.com \
|
||||
nginx
|
||||
```
|
||||
|
||||
docker-compose
|
||||
```docker-compose
|
||||
environment:
|
||||
- VIRTUAL_HOST=git.patachina.casacam.net
|
||||
- VIRTUAL_PORT=3000
|
||||
```
|
||||
Provided your DNS is setup to resolve `foo.bar.com` to the host running nginx-proxy, a request to `http://foo.bar.com` will then be routed to a container with the `VIRTUAL_HOST` env var set to `foo.bar.com` (in this case, the **your-proxied-app** container).
|
||||
|
||||
The containers being proxied must :
|
||||
|
@ -59,12 +74,19 @@ This image is based on the nginx:alpine image.
|
|||
docker pull nginxproxy/nginx-proxy:1.6-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
|
||||
|
||||
[](https://www.jetbrains.com/go/)
|
||||
[](https://www.jetbrains.com/pycharm/)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
version: "2"
|
||||
volumes:
|
||||
nginx_conf:
|
||||
|
||||
services:
|
||||
nginx:
|
||||
|
@ -7,17 +8,15 @@ services:
|
|||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- /etc/nginx/conf.d
|
||||
- nginx_conf:/etc/nginx/conf.d: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
|
||||
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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
|
|
352
docs/README.md
352
docs/README.md
|
@ -56,7 +56,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 +66,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 +113,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 +134,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 +147,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 +167,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
|
||||
|
||||
|
@ -242,7 +248,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;
|
||||
|
@ -255,6 +261,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)
|
||||
|
@ -265,7 +272,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
|
||||
|
||||
|
@ -281,12 +289,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 `$$`).
|
||||
|
@ -303,6 +308,7 @@ services:
|
|||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
environment:
|
||||
HTTPS_METHOD: nohttps
|
||||
|
||||
myapp:
|
||||
image: jwilder/whoami
|
||||
expose:
|
||||
|
@ -318,10 +324,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.
|
||||
|
||||
|
@ -406,7 +409,7 @@ docker run --detach \
|
|||
|
||||
## 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:
|
||||
|
||||
|
@ -434,7 +437,8 @@ By default nginx-proxy generates location blocks to handle ACME HTTP Challenge.
|
|||
|
||||
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`.
|
||||
|
||||
|
@ -446,9 +450,12 @@ docker run -e 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=<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.
|
||||
|
||||
|
@ -460,7 +467,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):
|
||||
|
||||
|
@ -567,24 +577,26 @@ Complete list of policies available through the `SSL_POLICY` environment variabl
|
|||
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, SSL handshake will be rejected (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 reject the SSL handshake. Client browsers will render a TLS error page. As of October 2024, 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:
|
||||
|
||||
|
@ -606,16 +618,47 @@ If the default certificate is also missing, nginx-proxy will configure nginx to
|
|||
> - 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
|
||||
|
@ -645,7 +688,7 @@ 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:
|
||||
|
@ -697,7 +740,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;
|
||||
|
@ -715,7 +758,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
|
||||
|
||||
|
@ -731,12 +775,47 @@ 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;
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Docker CLI</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker Compose file</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 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`, or if `VIRTUAL_HOST` is a regex, after the sha1 hash of the regex.
|
||||
|
@ -745,21 +824,87 @@ In order to allow virtual hosts to be dynamically configured as backends are add
|
|||
|
||||
For example, if you have a virtual host named `app.example.com`, you could provide a custom configuration for that host as follows:
|
||||
|
||||
```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
|
||||
1. create your virtual host config file:
|
||||
|
||||
```nginx
|
||||
# content of the custom-vhost-config.conf file
|
||||
client_max_body_size 100m;
|
||||
```
|
||||
|
||||
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:
|
||||
2. mount it to `/etc/nginx/vhost.d/app.example.com`:
|
||||
<details>
|
||||
<summary>Docker CLI</summary>
|
||||
|
||||
```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 /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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker Compose file</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
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:
|
||||
|
||||
<details>
|
||||
<summary>Docker CLI</summary>
|
||||
|
||||
```console
|
||||
docker run --detach \
|
||||
--name nginx-proxy \
|
||||
--publish 80:80 \
|
||||
--publish 443:443 \
|
||||
--volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/example.com:ro \
|
||||
--volume /path/to/custom-vhost-config.conf:/etc/nginx/vhost.d/www.example.com:ro \
|
||||
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
|
||||
nginxproxy/nginx-proxy
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker Compose file</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 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
|
||||
|
||||
|
@ -767,21 +912,90 @@ To add settings to the "location" block on a per-`VIRTUAL_HOST` basis, add your
|
|||
|
||||
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:
|
||||
|
||||
```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
|
||||
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;
|
||||
```
|
||||
|
||||
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:
|
||||
2. mount it to `/etc/nginx/vhost.d/app.example.com_location`:
|
||||
|
||||
<details>
|
||||
<summary>Docker CLI</summary>
|
||||
|
||||
```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/app.example.com_location:ro \
|
||||
nginxproxy/nginx-proxy
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker Compose file</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
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:
|
||||
|
||||
<details>
|
||||
<summary>Docker CLI</summary>
|
||||
|
||||
```console
|
||||
docker run --detach \
|
||||
--name nginx-proxy \
|
||||
--publish 80:80 \
|
||||
--publish 443:443 \
|
||||
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
|
||||
--volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/example.com_location:ro \
|
||||
--volume /path/to/custom-vhost-location-config.conf:/etc/nginx/vhost.d/www.example.com_location:ro \
|
||||
nginxproxy/nginx-proxy
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker Compose file</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 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
|
||||
|
||||
|
@ -803,7 +1017,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;
|
||||
}
|
||||
|
@ -826,7 +1040,8 @@ docker run --detach \
|
|||
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)
|
||||
|
||||
|
@ -881,7 +1096,8 @@ docker run --detach \
|
|||
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)
|
||||
|
||||
|
@ -889,7 +1105,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)
|
||||
|
||||
|
@ -940,8 +1157,6 @@ docker run -e VIRTUAL_HOST=foo.bar.com ...
|
|||
## Docker Compose
|
||||
|
||||
```yaml
|
||||
version: "2"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
|
@ -1005,6 +1220,7 @@ The effective `Port` is retrieved by order of precedence:
|
|||
### 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.
|
||||
|
||||
|
@ -1106,8 +1322,8 @@ curl -s -H "Host: test.nginx-proxy.tld" localhost/nginx-proxy-debug | jq
|
|||
}
|
||||
```
|
||||
|
||||
: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.
|
||||
|
||||
> [!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)
|
||||
|
||||
|
|
177
nginx.tmpl
177
nginx.tmpl
|
@ -19,9 +19,11 @@
|
|||
{{- $_ := 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") }}
|
||||
|
@ -30,6 +32,7 @@
|
|||
{{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }}
|
||||
{{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }}
|
||||
{{- $_ := 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 */}}
|
||||
|
@ -75,7 +78,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" }}
|
||||
{{- /*
|
||||
|
@ -90,17 +94,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" }}
|
||||
{{- 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 }}
|
||||
|
@ -110,26 +114,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 }}
|
||||
|
||||
{{- /*
|
||||
|
@ -147,7 +166,7 @@
|
|||
# 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 }}
|
||||
|
@ -367,16 +386,16 @@ upstream {{ $vpath.upstream }} {
|
|||
{{- define "debug_location" }}
|
||||
{{- $debug_paths := dict }}
|
||||
{{- range $path, $vpath := .VHost.paths }}
|
||||
{{- $tmp_port := dict }}
|
||||
{{- $tmp_ports := dict }}
|
||||
{{- range $port, $containers := $vpath.ports }}
|
||||
{{- $tmp_containers := list }}
|
||||
{{- range $container := $containers }}
|
||||
{{- $tmp_containers = dict "Name" $container.Name | append $tmp_containers }}
|
||||
{{- end }}
|
||||
{{- $_ := dict $port $tmp_containers | set $tmp_port "ports" }}
|
||||
{{- $tmp_port = deepCopy $vpath | merge $tmp_port }}
|
||||
{{- $_ := set $tmp_ports $port $tmp_containers }}
|
||||
{{- end }}
|
||||
{{- $_ := set $debug_paths $path $tmp_port }}
|
||||
{{- $debug_vpath := deepCopy $vpath | merge (dict "ports" $tmp_ports) }}
|
||||
{{- $_ := set $debug_paths $path $debug_vpath }}
|
||||
{{- end }}
|
||||
|
||||
{{- $debug_vhost := deepCopy .VHost }}
|
||||
|
@ -409,7 +428,7 @@ upstream {{ $vpath.upstream }} {
|
|||
|
||||
location /nginx-proxy-debug {
|
||||
default_type application/json;
|
||||
return 200 '{{ toJson $debug_response }}';
|
||||
return 200 '{{ toJson $debug_response }}{{ "\\n" }}';
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
|
@ -562,7 +581,7 @@ proxy_set_header Proxy "";
|
|||
{{- end }}
|
||||
|
||||
{{- range $hostname, $vhost := $parsedVhosts }}
|
||||
{{- $vhost_data := when (hasKey $globals.vhosts $hostname) (get $globals.vhosts $hostname) (dict) }}
|
||||
{{- $vhost_data := get $globals.vhosts $hostname | default (dict) }}
|
||||
{{- $paths := $vhost_data.paths | default (dict) }}
|
||||
|
||||
{{- if (empty $vhost) }}
|
||||
|
@ -571,24 +590,34 @@ proxy_set_header Proxy "";
|
|||
|
||||
{{- range $path, $vpath := $vhost }}
|
||||
{{- if (empty $vpath) }}
|
||||
{{- $vpath = dict "dest" "" "port" "default" }}
|
||||
{{- $vpath = dict
|
||||
"dest" ""
|
||||
"port" "default"
|
||||
"proto" "http"
|
||||
}}
|
||||
{{- end }}
|
||||
|
||||
{{- $dest := $vpath.dest | default "" }}
|
||||
{{- $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 }}
|
||||
{{- $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.config.sha1_upstream_name) (sha1 $hostname) $hostname) }}
|
||||
{{- $_ := set $globals.vhosts $hostname $vhost_data }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
@ -613,34 +642,42 @@ proxy_set_header Proxy "";
|
|||
{{- continue }}
|
||||
{{- end }}
|
||||
|
||||
{{- $vhost_data := when (hasKey $globals.vhosts $hostname) (get $globals.vhosts $hostname) (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 := groupByKeys $containers "Env.VIRTUAL_DEST" | first | default "" }}
|
||||
{{- $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 }}
|
||||
{{- $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.config.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 }}
|
||||
|
@ -649,21 +686,18 @@ proxy_set_header Proxy "";
|
|||
{{ $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 := groupByKeys $vpath_containers "Env.VIRTUAL_PROTO" | first | default "http" | trim }}
|
||||
{{- /* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external". */}}
|
||||
{{- $network_tag := groupByKeys $vpath_containers "Env.NETWORK_ACCESS" | first | default "external" }}
|
||||
|
||||
{{- $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 "disabled" }}
|
||||
{{- $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 }}
|
||||
|
@ -673,23 +707,41 @@ proxy_set_header Proxy "";
|
|||
{{ $vhost_containers = concat $vhost_containers $vpath_containers }}
|
||||
{{- end }}
|
||||
|
||||
{{- $certName := groupByKeys $vhost_containers "Env.CERT_NAME" | first }}
|
||||
{{- $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)) }}
|
||||
|
||||
{{- $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 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"))) }}
|
||||
{{- /* 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 }}
|
||||
{{- $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 }}
|
||||
|
@ -716,6 +768,7 @@ proxy_set_header Proxy "";
|
|||
"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
|
||||
|
@ -723,6 +776,8 @@ proxy_set_header Proxy "";
|
|||
"acme_http_challenge_enabled" $acme_http_challenge_enabled
|
||||
"server_tokens" $server_tokens
|
||||
"ssl_policy" $ssl_policy
|
||||
"trust_default_cert" $trust_default_cert
|
||||
"upstream_name" $upstream_name
|
||||
"vhost_root" $vhost_root
|
||||
) }}
|
||||
{{- $_ := set $globals.vhosts $hostname $vhost_data }}
|
||||
|
@ -854,11 +909,14 @@ server {
|
|||
{{- end }}
|
||||
|
||||
location / {
|
||||
{{- if eq $globals.config.external_https_port "443" }}
|
||||
return 301 https://$host$request_uri;
|
||||
{{- else }}
|
||||
return 301 https://$host:{{ $globals.config.external_https_port }}$request_uri;
|
||||
{{- end }}
|
||||
{{- $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 }}
|
||||
|
@ -947,14 +1005,9 @@ server {
|
|||
}
|
||||
add_header Strict-Transport-Security $sts_header always;
|
||||
{{- end }}
|
||||
{{- else if $globals.config.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 for this vhost nor default certificate found, so reject SSL handshake.
|
||||
ssl_reject_handshake on;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.2)
|
||||
CONTAINER=$(docker run -d -v $DIR:/work -w /work -e SAN="$ALTERNATE_DOMAINS" nginx:1.27.3)
|
||||
# Configure openssl
|
||||
docker exec $CONTAINER bash -c '
|
||||
mkdir -p /ca/{certs,crl,private,newcerts} 2>/dev/null
|
||||
|
|
|
@ -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"
|
319
test/conftest.py
319
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 @@ test_container = 'nginx-proxy-pytest'
|
|||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
@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 @@ class requests_for_docker(object):
|
|||
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.
|
||||
|
||||
|
@ -171,7 +179,7 @@ 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.
|
||||
"""
|
||||
|
@ -188,7 +196,7 @@ 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.
|
||||
|
@ -200,21 +208,21 @@ def nginx_proxy_dns_resolver(domain_name):
|
|||
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}")
|
||||
log.warning(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}")
|
||||
exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"})
|
||||
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
|
||||
log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
|
||||
return None
|
||||
nginxproxy_container = nginxproxy_containers[0]
|
||||
ip = container_ip(nginxproxy_container)
|
||||
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}")
|
||||
return ip
|
||||
|
||||
def docker_container_dns_resolver(domain_name):
|
||||
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 +232,15 @@ def docker_container_dns_resolver(domain_name):
|
|||
match = re.search(r'(^|.+\.)(?P<container>[^.]+)\.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 +252,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 +263,18 @@ def monkey_patch_urllib_dns_resolver():
|
|||
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 +290,12 @@ def monkey_patch_urllib_dns_resolver():
|
|||
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,20 +310,40 @@ 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}")
|
||||
|
||||
logging.info(compose_cmd.getvalue())
|
||||
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)
|
||||
except subprocess.CalledProcessError as e:
|
||||
pytest.fail(f"Error while runninng '{DOCKER_COMPOSE} -f {compose_file} up -d':\n{e.output}", pytrace=False)
|
||||
pytest.fail(f"Error while running '{compose_cmd.getvalue()}':\n{e.output}", pytrace=False)
|
||||
|
||||
|
||||
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():
|
||||
|
@ -333,35 +362,50 @@ def wait_for_nginxproxy_to_be_ready():
|
|||
|
||||
|
||||
@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 `.yml` or `.yaml`), use that. Otherwise, use `docker-compose.yml` in the same directory
|
||||
as the test module.
|
||||
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.
|
||||
|
||||
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()]
|
||||
|
||||
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__))
|
||||
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())
|
||||
|
||||
logging.debug(f"using docker compose file {docker_compose_file}")
|
||||
return docker_compose_file
|
||||
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())
|
||||
|
||||
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())
|
||||
|
||||
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 +415,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())
|
||||
|
@ -389,7 +433,7 @@ def connect_to_network(network):
|
|||
return network
|
||||
|
||||
|
||||
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 +443,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 +455,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 +471,34 @@ def connect_to_all_networks():
|
|||
|
||||
class DockerComposer(contextlib.AbstractContextManager):
|
||||
def __init__(self):
|
||||
self._docker_compose_file = None
|
||||
self._networks = None
|
||||
self._docker_compose_files = None
|
||||
self._project_name = None
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
self._down()
|
||||
|
||||
def _down(self):
|
||||
if self._docker_compose_file is None:
|
||||
if self._docker_compose_files is None:
|
||||
return
|
||||
for network in self._networks:
|
||||
disconnect_from_network(network)
|
||||
docker_compose_down(self._docker_compose_file)
|
||||
docker_compose_down(self._docker_compose_files, self._project_name)
|
||||
self._docker_compose_file = None
|
||||
self._project_name = None
|
||||
|
||||
def compose(self, docker_compose_file):
|
||||
if docker_compose_file == self._docker_compose_file:
|
||||
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:
|
||||
return
|
||||
self._down()
|
||||
if docker_compose_file is None:
|
||||
if docker_compose_files is None or project_name is None:
|
||||
return
|
||||
remove_all_containers()
|
||||
docker_compose_up(docker_compose_file)
|
||||
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
|
||||
self._docker_compose_file = docker_compose_file
|
||||
self._docker_compose_files = docker_compose_files
|
||||
self._project_name = project_name
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
@ -462,14 +509,14 @@ class DockerComposer(contextlib.AbstractContextManager):
|
|||
|
||||
|
||||
@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 +527,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.
|
||||
|
||||
A custom docker compose file name can be specified by overriding the `docker_compose_file`
|
||||
fixture.
|
||||
|
||||
Also, in the case where pytest is running from a docker container, this fixture makes sure
|
||||
our container will be attached to all the docker networks.
|
||||
def docker_compose(
|
||||
request: FixtureRequest,
|
||||
monkeypatch,
|
||||
monkey_patched_dns,
|
||||
docker_composer,
|
||||
docker_compose_files
|
||||
) -> Iterator[DockerClient]:
|
||||
"""
|
||||
docker_composer.compose(docker_compose_file)
|
||||
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 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.
|
||||
"""
|
||||
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 +567,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 +583,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 +616,5 @@ try:
|
|||
except docker.errors.ImageNotFound:
|
||||
pytest.exit("The docker image 'nginxproxy/nginx-proxy:test' is missing")
|
||||
|
||||
if LooseVersion(docker.__version__) < LooseVersion("5.0.0"):
|
||||
pytest.exit("This test suite is meant to work with the python docker module v5.0.0 or later")
|
||||
if Version(docker.__version__) < Version("7.0.0"):
|
||||
pytest.exit("This test suite is meant to work with the python docker module v7.0.0 or later")
|
||||
|
|
|
@ -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. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
backoff==2.2.1
|
||||
docker==7.1.0
|
||||
pytest==8.3.3
|
||||
packaging==24.2
|
||||
pytest==8.3.4
|
||||
requests==2.32.3
|
||||
urllib3==2.3.0
|
||||
|
|
|
@ -28,7 +28,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
|
|||
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__':
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
This directory contains tests that showcase scenarios known to break the expected behavior of nginx-proxy.
|
|
@ -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
|
|
@ -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}",
|
|
@ -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
|
|
@ -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}",
|
|
@ -1,5 +1,3 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
web1:
|
||||
image: web
|
||||
|
@ -36,10 +34,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
|
|
@ -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}",
|
|
@ -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
|
|
@ -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
|
|
@ -1,4 +1,3 @@
|
|||
import pytest
|
||||
import re
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/default_location:ro
|
||||
- ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.example_location: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
|
||||
WEB_PORTS: "81"
|
||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||
|
||||
web2:
|
||||
|
@ -21,7 +18,7 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||
|
||||
web3:
|
||||
|
@ -29,5 +26,5 @@ services:
|
|||
expose:
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: 83
|
||||
WEB_PORTS: "83"
|
||||
VIRTUAL_HOST: web3.nginx-proxy.example
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_f00.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,5 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example_location:ro
|
||||
- ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430_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/561032515ede3ab3a015edfb244608b72409c430_location:ro
|
||||
|
||||
web1:
|
||||
image: web
|
||||
expose:
|
||||
- "81"
|
||||
environment:
|
||||
WEB_PORTS: 81
|
||||
WEB_PORTS: "81"
|
||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||
|
||||
web2:
|
||||
|
@ -21,7 +18,7 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||
|
||||
regex:
|
||||
|
@ -29,5 +26,5 @@ services:
|
|||
expose:
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: 83
|
||||
WEB_PORTS: "83"
|
||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example:ro
|
||||
- ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430: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/561032515ede3ab3a015edfb244608b72409c430:ro
|
||||
|
||||
web1:
|
||||
image: web
|
||||
expose:
|
||||
- "81"
|
||||
environment:
|
||||
WEB_PORTS: 81
|
||||
WEB_PORTS: "81"
|
||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||
|
||||
web2:
|
||||
|
@ -21,7 +18,7 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||
|
||||
regex:
|
||||
|
@ -29,5 +26,5 @@ services:
|
|||
expose:
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: 83
|
||||
WEB_PORTS: "83"
|
||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_f00.conf:/etc/nginx/conf.d/my_custom_proxy_settings_f00.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,5 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
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
|
|
@ -1,8 +1,5 @@
|
|||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
environment:
|
||||
DEBUG_ENDPOINT: "true"
|
||||
|
||||
|
@ -11,7 +8,7 @@ services:
|
|||
expose:
|
||||
- "81"
|
||||
environment:
|
||||
WEB_PORTS: 81
|
||||
WEB_PORTS: "81"
|
||||
VIRTUAL_HOST: enabled.debug.nginx-proxy.example
|
||||
|
||||
debug_stripped:
|
||||
|
@ -19,7 +16,7 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST_MULTIPORTS: |-
|
||||
stripped.debug.nginx-proxy.example:
|
||||
"/1":
|
||||
|
@ -48,7 +45,7 @@ services:
|
|||
expose:
|
||||
- "84"
|
||||
environment:
|
||||
WEB_PORTS: 84
|
||||
WEB_PORTS: "84"
|
||||
VIRTUAL_HOST: ~^regexp.*\.debug.nginx-proxy.example
|
||||
|
||||
debug_disabled:
|
||||
|
@ -56,7 +53,7 @@ services:
|
|||
expose:
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: 83
|
||||
WEB_PORTS: "83"
|
||||
VIRTUAL_HOST: disabled.debug.nginx-proxy.example
|
||||
labels:
|
||||
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "false"
|
|
@ -1,6 +1,8 @@
|
|||
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
|
|
@ -1,15 +1,10 @@
|
|||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy:test
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
|
||||
debug_disabled1:
|
||||
image: web
|
||||
expose:
|
||||
- "81"
|
||||
environment:
|
||||
WEB_PORTS: 81
|
||||
WEB_PORTS: "81"
|
||||
VIRTUAL_HOST: disabled1.debug.nginx-proxy.example
|
||||
|
||||
debug_disabled2:
|
||||
|
@ -17,7 +12,7 @@ services:
|
|||
expose:
|
||||
- "82"
|
||||
environment:
|
||||
WEB_PORTS: 82
|
||||
WEB_PORTS: "82"
|
||||
VIRTUAL_HOST: disabled2.debug.nginx-proxy.example
|
||||
|
||||
|
||||
|
@ -26,7 +21,7 @@ services:
|
|||
expose:
|
||||
- "83"
|
||||
environment:
|
||||
WEB_PORTS: 83
|
||||
WEB_PORTS: "83"
|
||||
VIRTUAL_HOST: enabled.debug.nginx-proxy.example
|
||||
labels:
|
||||
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "true"
|
|
@ -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
|
|
@ -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
|
12
test/test_default-host/test_default-host.yml
Normal file
12
test/test_default-host/test_default-host.yml
Normal file
|
@ -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
|
|
@ -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
|
|
@ -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
|
1
test/test_dockergen/.gitignore
vendored
1
test/test_dockergen/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
nginx.tmpl
|
|
@ -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
|
||||
WEB_PORTS: "80"
|
||||
VIRTUAL_HOST: whoami.nginx.container.docker
|
||||
|
||||
volumes:
|
||||
nginx_conf: {}
|
|
@ -1,11 +1,11 @@
|
|||
import docker
|
||||
import pytest
|
||||
from distutils.version import LooseVersion
|
||||
from packaging.version import Version
|
||||
|
||||
|
||||
raw_version = docker.from_env().version()["Version"]
|
||||
pytestmark = pytest.mark.skipif(
|
||||
LooseVersion(raw_version) < LooseVersion("1.13"),
|
||||
Version(raw_version) < Version("1.13"),
|
||||
reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})"
|
||||
)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://unknown.nginx.container.docker/")
|
||||
assert r.status_code == 503
|
||||
|
||||
|
||||
def test_forwards_to_whoami(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://whoami.nginx.container.docker/")
|
||||
assert r.status_code == 200
|
||||
whoami_container = docker_compose.containers.get("whoami")
|
||||
assert r.text == f"I'm {whoami_container.id[:12]}\n"
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -7,7 +7,7 @@ import pytest
|
|||
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:
|
3
test/test_events/test_events.yml
Normal file
3
test/test_events/test_events.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
networks:
|
||||
default:
|
||||
name: test_events-net
|
|
@ -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
|
9
test/test_fallback/test_fallback.data/compose.base.yml
Normal file
9
test/test_fallback/test_fallback.data/compose.base.yml
Normal file
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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"
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
12
test/test_fallback/test_fallback.data/nohttps.yml
Normal file
12
test/test_fallback/test_fallback.data/nohttps.yml
Normal file
|
@ -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
|
|
@ -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
|
49
test/test_fallback/test_fallback.data/withdefault.yml
Normal file
49
test/test_fallback/test_fallback.data/withdefault.yml
Normal file
|
@ -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"
|
|
@ -1,32 +1,32 @@
|
|||
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.SSLError,
|
||||
giveup=lambda e: want_err_re and want_err_re.search(str(e)),
|
||||
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
|
||||
|
@ -43,10 +43,23 @@ INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
|
|||
("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),
|
||||
|
@ -68,12 +81,15 @@ INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
|
|||
("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.
|
||||
|
@ -92,7 +108,7 @@ INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
|
|||
# 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
|
70
test/test_headers/certs/default.crt
Normal file
70
test/test_headers/certs/default.crt
Normal file
|
@ -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-----
|
27
test/test_headers/certs/default.key
Normal file
27
test/test_headers/certs/default.key
Normal file
|
@ -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-----
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import pytest
|
||||
|
||||
# Note: on Docker Desktop, host networking must be manually enabled.
|
||||
# See https://docs.docker.com/engine/network/drivers/host/
|
||||
|
||||
def test_forwards_to_host_network_container_1(docker_compose, nginxproxy):
|
||||
r = nginxproxy.get("http://host-network-1.nginx-proxy.tld:8888/port")
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
version: "2"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
environment:
|
||||
HTTP_PORT: 8888
|
||||
network_mode: host
|
||||
|
||||
host-network-1:
|
||||
image: web
|
||||
environment:
|
||||
|
@ -16,11 +19,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
|
||||
|
|
5
test/test_htpasswd/compose.base.override.yml
Normal file
5
test/test_htpasswd/compose.base.override.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
services:
|
||||
nginx-proxy:
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ${PYTEST_MODULE_PATH}/htpasswd:/etc/nginx/htpasswd:ro
|
|
@ -1,5 +1,3 @@
|
|||
import pytest
|
||||
|
||||
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
|
8
test/test_htpasswd/test_htpasswd-regex-virtual-host.yml
Normal file
8
test/test_htpasswd/test_htpasswd-regex-virtual-host.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
services:
|
||||
regex:
|
||||
image: web
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
WEB_PORTS: "80"
|
||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
|
@ -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
|
8
test/test_htpasswd/test_htpasswd-virtual-host.yml
Normal file
8
test/test_htpasswd/test_htpasswd-virtual-host.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
services:
|
||||
web:
|
||||
image: web
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
WEB_PORTS: "80"
|
||||
VIRTUAL_HOST: htpasswd.nginx-proxy.tld
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue