Compare commits

..

6 commits

Author SHA1 Message Date
Nicolas Duchon
0fbd68392b feat: customizable non get redirect code 2024-12-08 21:54:54 +01:00
Nicolas Duchon
c20561003f test: parameterize test 2024-12-08 19:41:31 +01:00
Nicolas Duchon
695eabb0f4 tests: fix tests 2024-12-08 19:15:03 +01:00
Nicolas Duchon
ee899506f0 tests: fix redirect test compose file 2024-12-08 18:38:05 +01:00
junderw
2dd672ac9d tests: redirects 2021-08-19 16:47:44 +09:00
junderw
bf35d98f7a feat: redirect using 308 for non-GET requests 2021-08-19 07:29:39 +09:00
248 changed files with 1328 additions and 1571 deletions

View file

@ -25,10 +25,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up Python 3.12 - name: Set up Python 3.9
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: 3.12 python-version: 3.9
- name: Install dependencies - name: Install dependencies
run: | run: |

View file

@ -1,4 +1,4 @@
FROM docker.io/nginxproxy/docker-gen:0.14.5 AS docker-gen FROM docker.io/nginxproxy/docker-gen:0.14.4 AS docker-gen
FROM docker.io/nginxproxy/forego:0.18.2 AS forego FROM docker.io/nginxproxy/forego:0.18.2 AS forego

View file

@ -1,4 +1,4 @@
FROM docker.io/nginxproxy/docker-gen:0.14.5-debian AS docker-gen FROM docker.io/nginxproxy/docker-gen:0.14.4-debian AS docker-gen
FROM docker.io/nginxproxy/forego:0.18.2-debian AS forego FROM docker.io/nginxproxy/forego:0.18.2-debian AS forego

View file

@ -20,17 +20,7 @@ docker run --detach \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \ --volume /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy:1.6 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` Then start any containers (here an nginx container) you want proxied with an env var `VIRTUAL_HOST=subdomain.yourdomain.com`
```console ```console
@ -39,12 +29,7 @@ docker run --detach \
--env VIRTUAL_HOST=foo.bar.com \ --env VIRTUAL_HOST=foo.bar.com \
nginx 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). 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 : The containers being proxied must :
@ -85,8 +70,3 @@ docker pull nginxproxy/nginx-proxy:1.6-alpine
### Additional documentation ### Additional documentation
Please check the [docs section](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs). Please check the [docs section](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs).
### Powered by
[![GoLand logo](https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand_icon.svg)](https://www.jetbrains.com/go/)
[![PyCharm logo](https://resources.jetbrains.com/storage/products/company/brand/logos/PyCharm_icon.svg)](https://www.jetbrains.com/pycharm/)

View file

@ -1,5 +1,4 @@
volumes: version: "2"
nginx_conf:
services: services:
nginx: nginx:
@ -8,15 +7,17 @@ services:
ports: ports:
- "80:80" - "80:80"
volumes: volumes:
- nginx_conf:/etc/nginx/conf.d:ro - /etc/nginx/conf.d
dockergen: dockergen:
image: nginxproxy/docker-gen image: nginxproxy/docker-gen
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl
/etc/nginx/conf.d/default.conf
volumes_from:
- nginx
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
- nginx_conf:/etc/nginx/conf.d
whoami: whoami:
image: jwilder/whoami image: jwilder/whoami

View file

@ -1,3 +1,5 @@
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy image: nginxproxy/nginx-proxy

View file

@ -56,7 +56,7 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie
### Multiple ports ### 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_PROTO`, `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_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. The YAML syntax should be easier to write on Docker compose files, while the JSON syntax can be used for CLI invocation.
@ -66,20 +66,18 @@ The expected format is the following:
hostname: hostname:
path: path:
port: int port: int
proto: string
dest: string dest: string
``` ```
For each hostname entry, `path`, `port`, `proto` and `dest` are optional and are assigned default values when missing: For each hostname entry, `path`, `port` and `dest` are optional and are assigned default values when missing:
- `path` = "/" - `path` = "/"
- `port` = default port - `port` = default port
- `proto` = "http"
- `dest` = "" - `dest` = ""
#### Multiple ports routed to different hostnames The following examples use an hypothetical container running services on port 80, 8000 and 9000:
The following example use an hypothetical container running services over HTTP on port 80, 8000 and 9000: #### Multiple ports routed to different hostnames
```yaml ```yaml
services: services:
@ -113,14 +111,12 @@ services:
This would result in the following proxy config: This would result in the following proxy config:
- `www.example.org` -> `multiport-container:80` over `HTTP` - `www.example.org` -> `multiport-container:80`
- `service1.example.org` -> `multiport-container:8000` over `HTTP` - `service1.example.org` -> `multiport-container:8000`
- `service2.example.org` -> `multiport-container:9000` over `HTTP` - `service2.example.org` -> `multiport-container:9000`
#### Multiple ports routed to same hostname and different paths #### 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 ```yaml
services: services:
multiport-container: multiport-container:
@ -134,12 +130,11 @@ services:
port: 8000 port: 8000
dest: "/" dest: "/"
"/service2": "/service2":
port: 9443 port: 9000
proto: "https"
dest: "/" dest: "/"
# port and dest are not specified on the / path, so this path is routed to the # port and dest are not specified on the / path, so this path is routed
# default port with the default dest value (empty string) and default proto (http) # to the default port with the default dest value (empty string)
# JSON equivalent: # JSON equivalent:
# VIRTUAL_HOST_MULTIPORTS: |- # VIRTUAL_HOST_MULTIPORTS: |-
@ -147,16 +142,16 @@ services:
# "www.example.org": { # "www.example.org": {
# "/": {}, # "/": {},
# "/service1": { "port": 8000, "dest": "/" }, # "/service1": { "port": 8000, "dest": "/" },
# "/service2": { "port": 9443, "proto": "https", "dest": "/" } # "/service2": { "port": 9000, "dest": "/" }
# } # }
# } # }
``` ```
This would result in the following proxy config: This would result in the following proxy config:
- `www.example.org` -> `multiport-container:80` over `HTTP` - `www.example.org` -> `multiport-container:80`
- `www.example.org/service1` -> `multiport-container:8000` over `HTTP` - `www.example.org/service1` -> `multiport-container:8000`
- `www.example.org/service2` -> `multiport-container:9443` over `HTTPS` - `www.example.org/service2` -> `multiport-container:9000`
⬆️ [back to table of contents](#table-of-contents) ⬆️ [back to table of contents](#table-of-contents)
@ -248,7 +243,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: 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" # These networks are considered "internal"
allow 127.0.0.0/8; allow 127.0.0.0/8;
allow 10.0.0.0/8; allow 10.0.0.0/8;
@ -308,7 +303,6 @@ services:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
environment: environment:
HTTPS_METHOD: nohttps HTTPS_METHOD: nohttps
myapp: myapp:
image: jwilder/whoami image: jwilder/whoami
expose: expose:
@ -451,7 +445,6 @@ docker run -e DHPARAM_SKIP=true ....
### Wildcard Certificates ### Wildcard Certificates
Wildcard certificates and keys should be named after the parent domain name with a `.crt` and `.key` extension. For example: Wildcard certificates and keys should be named after the parent 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` if `foo.bar.com.crt` and `foo.bar.com.key` are not available - `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`. - `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`.
@ -580,7 +573,7 @@ The default behavior for the proxy when port 80 and 443 are exposed is as follow
- 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, 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). - 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). 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 (for example to use a [308](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308)), 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.
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. 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.
@ -594,7 +587,6 @@ By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.or
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`). 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`).
If the default certificate is also missing, nginx-proxy will: If the default certificate is also missing, nginx-proxy will:
- 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`). - 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: - 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:
@ -629,7 +621,6 @@ If the default certificate is also missing, nginx-proxy will:
### Certificate selection ### Certificate selection
Summarizing all the above informations, nginx-proxy will select the certificate for a given virtual host using the following sequence: 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). 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`). 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). 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).
@ -740,7 +731,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: 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 # HTTP 1.1 support
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
@ -775,47 +766,12 @@ RUN { \
} > /etc/nginx/conf.d/my_proxy.conf } > /etc/nginx/conf.d/my_proxy.conf
``` ```
Or it can be done by mounting in your custom configuration in your `docker run` command or your Docker Compose file: Or it can be done by mounting in your custom configuration in your `docker run` command:
```nginx
# content of the my_proxy.conf file
server_tokens off;
client_max_body_size 100m;
```
<details>
<summary>Docker CLI</summary>
```console ```console
docker run --detach \ 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
--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 ### 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. 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.
@ -824,87 +780,21 @@ 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: For example, if you have a virtual host named `app.example.com`, you could provide a custom configuration for that host as follows:
1. create your virtual host config file: ```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
```nginx { echo 'server_tokens off;'; echo 'client_max_body_size 100m;'; } > /path/to/vhost.d/app.example.com
# content of the custom-vhost-config.conf file
client_max_body_size 100m;
``` ```
2. mount it to `/etc/nginx/vhost.d/app.example.com`: 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:
<details>
<summary>Docker CLI</summary>
```console ```console
docker run --detach \ { echo 'server_tokens off;'; echo 'client_max_body_size 100m;'; } > /path/to/vhost.d/www.example.com
--name nginx-proxy \ ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com
--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 ### 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 [per-VIRTUAL_HOST file](#per-virtual_host) 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 `/etc/nginx/vhost.d/{VIRTUAL_HOST}` file associated with it.
### Per-VIRTUAL_HOST location configuration ### Per-VIRTUAL_HOST location configuration
@ -912,90 +802,21 @@ 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: For example, if you have a virtual host named `app.example.com` and you have configured a proxy_cache `my-cache` in another custom file, you could tell it to use a proxy cache as follows:
1. create your virtual host location config file: ```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
```nginx { 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
# content of the custom-vhost-location-config.conf file
proxy_cache my-cache;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;
``` ```
2. mount it to `/etc/nginx/vhost.d/app.example.com_location`: 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:
<details>
<summary>Docker CLI</summary>
```console ```console
docker run --detach \ { 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
--name nginx-proxy \ ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com
--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 ### 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 [Per-VIRTUAL_HOST location file](#per-virtual_host-location-configuration) 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 `/etc/nginx/vhost.d/{VIRTUAL_HOST}_location` file associated with it.
### Overriding `location` blocks ### Overriding `location` blocks
@ -1017,7 +838,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: 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 / { location / {
proxy_pass http://app.example.com; proxy_pass http://app.example.com;
} }
@ -1157,6 +978,8 @@ docker run -e VIRTUAL_HOST=foo.bar.com ...
## Docker Compose ## Docker Compose
```yaml ```yaml
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy image: nginxproxy/nginx-proxy

View file

@ -590,31 +590,18 @@ proxy_set_header Proxy "";
{{- range $path, $vpath := $vhost }} {{- range $path, $vpath := $vhost }}
{{- if (empty $vpath) }} {{- if (empty $vpath) }}
{{- $vpath = dict {{- $vpath = dict "dest" "" "port" "default" }}
"dest" ""
"port" "default"
"proto" "http"
}}
{{- end }} {{- end }}
{{- $dest := $vpath.dest | default "" }} {{- $dest := $vpath.dest | default "" }}
{{- $port := $vpath.port | default "default" | toString }} {{- $port := $vpath.port | default "default" | toString }}
{{- $proto := $vpath.proto | default "http" }}
{{- $path_data := get $paths $path | default (dict) }} {{- $path_data := get $paths $path | default (dict) }}
{{- $path_ports := $path_data.ports | default (dict) }} {{- $path_ports := $path_data.ports | default (dict) }}
{{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }} {{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }}
{{- $_ := set $path_ports $port $path_port_containers }} {{- $_ := set $path_ports $port $path_port_containers }}
{{- $_ := set $path_data "ports" $path_ports }} {{- $_ := set $path_data "ports" $path_ports }}
{{- if (not (hasKey $path_data "dest")) }} {{- if (not (hasKey $path_data "dest")) }}
{{- $_ := set $path_data "dest" $dest }} {{- $_ := set $path_data "dest" $dest }}
{{- end }} {{- end }}
{{- if (not (hasKey $path_data "proto")) }}
{{- $_ := set $path_data "proto" $proto }}
{{- end }}
{{- $_ := set $paths $path $path_data }} {{- $_ := set $paths $path $path_data }}
{{- end }} {{- end }}
{{- $_ := set $vhost_data "paths" $paths }} {{- $_ := set $vhost_data "paths" $paths }}
@ -649,8 +636,6 @@ proxy_set_header Proxy "";
{{- range $path, $containers := $tmp_paths }} {{- range $path, $containers := $tmp_paths }}
{{- $dest := groupByKeys $containers "Env.VIRTUAL_DEST" | first | default "" }} {{- $dest := groupByKeys $containers "Env.VIRTUAL_DEST" | first | default "" }}
{{- $proto := groupByKeys $containers "Env.VIRTUAL_PROTO" | first | default "http" | trim }}
{{- $path_data := get $paths $path | default (dict) }} {{- $path_data := get $paths $path | default (dict) }}
{{- $path_ports := $path_data.ports | default (dict) }} {{- $path_ports := $path_data.ports | default (dict) }}
{{- range $port, $containers := groupByWithDefault $containers "Env.VIRTUAL_PORT" "default" }} {{- range $port, $containers := groupByWithDefault $containers "Env.VIRTUAL_PORT" "default" }}
@ -658,15 +643,9 @@ proxy_set_header Proxy "";
{{- $_ := set $path_ports $port $path_port_containers }} {{- $_ := set $path_ports $port $path_port_containers }}
{{- end }} {{- end }}
{{- $_ := set $path_data "ports" $path_ports }} {{- $_ := set $path_data "ports" $path_ports }}
{{- if (not (hasKey $path_data "dest")) }} {{- if (not (hasKey $path_data "dest")) }}
{{- $_ := set $path_data "dest" $dest }} {{- $_ := set $path_data "dest" $dest }}
{{- end }} {{- end }}
{{- if (not (hasKey $path_data "proto")) }}
{{- $_ := set $path_data "proto" $proto }}
{{- end }}
{{- $_ := set $paths $path $path_data }} {{- $_ := set $paths $path $path_data }}
{{- end }} {{- end }}
{{- $_ := set $vhost_data "paths" $paths }} {{- $_ := set $vhost_data "paths" $paths }}
@ -686,6 +665,8 @@ proxy_set_header Proxy "";
{{ $vpath_containers = concat $vpath_containers $vport_containers }} {{ $vpath_containers = concat $vpath_containers $vport_containers }}
{{- end }} {{- 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". */}} {{- /* 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" }} {{- $network_tag := groupByKeys $vpath_containers "Env.NETWORK_ACCESS" | first | default "external" }}
@ -698,6 +679,7 @@ proxy_set_header Proxy "";
{{- $upstream = printf "%s-%s" $upstream $sum }} {{- $upstream = printf "%s-%s" $upstream $sum }}
{{- end }} {{- end }}
{{- $_ := set $vpath_data "proto" $proto }}
{{- $_ := set $vpath_data "network_tag" $network_tag }} {{- $_ := set $vpath_data "network_tag" $network_tag }}
{{- $_ := set $vpath_data "upstream" $upstream }} {{- $_ := set $vpath_data "upstream" $upstream }}
{{- $_ := set $vpath_data "loadbalance" $loadbalance }} {{- $_ := set $vpath_data "loadbalance" $loadbalance }}

View file

@ -57,39 +57,13 @@ This test suite uses [pytest](http://doc.pytest.org/en/latest/). The [conftest.p
### docker_compose fixture ### docker_compose fixture
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. 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/).
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: Once the docker compose file found, the fixture will remove all containers, run `docker compose up`, and finally your test will be executed.
1. `test/compose.base.yml` 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:
2. `test/test_subdir/compose.base.override.yml` (if it exists)
3. `test/test_subdir/test_example.yml`
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_example.yml up -d
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. 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.
@ -97,10 +71,7 @@ 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: 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 `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. 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:
So, in tests, all the following domain names will resolve to either localhost or the nginx-proxy container's IP:
- `nginx-proxy` - `nginx-proxy`
- `nginx-proxy.com` - `nginx-proxy.com`
- `www.nginx-proxy.com` - `www.nginx-proxy.com`
@ -109,16 +80,14 @@ So, in tests, all the following domain names will resolve to either localhost or
- `whatever.nginx-proxyooooooo` - `whatever.nginx-proxyooooooo`
- ... - ...
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`. Any domain name ending with `XXX.container.docker` will resolve to the IP address of the XXX container.
So, on a non-Darwin system:
- `web1.container.docker` will resolve to the IP address of the `web1` container - `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 - `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 - `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. Otherwise, domain names are resoved as usual using your system DNS resolver.
### nginxproxy fixture ### 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. 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.

View file

@ -1,35 +1,28 @@
import contextlib import contextlib
import logging import logging
import os import os
import pathlib
import platform
import re import re
import shlex import shlex
import socket import socket
import subprocess import subprocess
import time import time
from io import StringIO from typing import List
from typing import Iterator, List, Optional
import backoff import backoff
import docker.errors import docker
import pytest import pytest
import requests import requests
from _pytest.fixtures import FixtureRequest from _pytest._code.code import ReprExceptionInfo
from docker import DockerClient from distutils.version import LooseVersion
from docker.models.containers import Container from docker.models.containers import Container
from docker.models.networks import Network from requests.packages.urllib3.util.connection import HAS_IPV6
from packaging.version import Version
from requests import Response
from urllib3.util.connection import HAS_IPV6
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logging.getLogger('backoff').setLevel(logging.INFO) logging.getLogger('backoff').setLevel(logging.INFO)
logging.getLogger('DNS').setLevel(logging.DEBUG) logging.getLogger('DNS').setLevel(logging.DEBUG)
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN) logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN)
CA_ROOT_CERTIFICATE = pathlib.Path(__file__).parent.joinpath("certs/ca-root.crt") CA_ROOT_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt')
PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1" 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 FORCE_CONTAINER_IPV6 = False # ugly global state to consider containers' IPv6 address instead of IPv4
@ -47,9 +40,8 @@ test_container = 'nginx-proxy-pytest'
# #
############################################################################### ###############################################################################
@contextlib.contextmanager @contextlib.contextmanager
def ipv6(force_ipv6: bool = True): def ipv6(force_ipv6=True):
""" """
Meant to be used as a context manager to force IPv6 sockets: Meant to be used as a context manager to force IPv6 sockets:
@ -67,19 +59,19 @@ def ipv6(force_ipv6: bool = True):
FORCE_CONTAINER_IPV6 = False FORCE_CONTAINER_IPV6 = False
class RequestsForDocker: class requests_for_docker(object):
""" """
Proxy for calling methods of the requests module. Proxy for calling methods of the requests module.
When an HTTP response failed due to HTTP Error 404 or 502, retry a few times. When a 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. Provides method `get_conf` to extract the nginx-proxy configuration content.
""" """
def __init__(self): def __init__(self):
self.session = requests.Session() self.session = requests.Session()
if CA_ROOT_CERTIFICATE.is_file(): if os.path.isfile(CA_ROOT_CERTIFICATE):
self.session.verify = CA_ROOT_CERTIFICATE.as_posix() self.session.verify = CA_ROOT_CERTIFICATE
@staticmethod @staticmethod
def get_nginx_proxy_container() -> Container: def get_nginx_proxy_containers() -> List[Container]:
""" """
Return list of containers Return list of containers
""" """
@ -88,69 +80,69 @@ class RequestsForDocker:
pytest.fail("Too many running nginxproxy/nginx-proxy:test containers", pytrace=False) pytest.fail("Too many running nginxproxy/nginx-proxy:test containers", pytrace=False)
elif len(nginx_proxy_containers) == 0: elif len(nginx_proxy_containers) == 0:
pytest.fail("No running nginxproxy/nginx-proxy:test container", pytrace=False) pytest.fail("No running nginxproxy/nginx-proxy:test container", pytrace=False)
return nginx_proxy_containers.pop() return nginx_proxy_containers
def get_conf(self) -> bytes: def get_conf(self):
""" """
Return the nginx config file Return the nginx config file
""" """
nginx_proxy_container = self.get_nginx_proxy_container() nginx_proxy_containers = self.get_nginx_proxy_containers()
return get_nginx_conf_from_container(nginx_proxy_container) return get_nginx_conf_from_container(nginx_proxy_containers[0])
def get_ip(self) -> str: def get_ip(self) -> str:
""" """
Return the nginx container ip address Return the nginx container ip address
""" """
nginx_proxy_container = self.get_nginx_proxy_container() nginx_proxy_containers = self.get_nginx_proxy_containers()
return container_ip(nginx_proxy_container) return container_ip(nginx_proxy_containers[0])
def get(self, *args, **kwargs) -> Response: def get(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)): 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) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _get(*_args, **_kwargs): def _get(*args, **kwargs):
return self.session.get(*_args, **_kwargs) return self.session.get(*args, **kwargs)
return _get(*args, **kwargs) return _get(*args, **kwargs)
def post(self, *args, **kwargs) -> Response: def post(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)): 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) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _post(*_args, **_kwargs): def _post(*args, **kwargs):
return self.session.post(*_args, **_kwargs) return self.session.post(*args, **kwargs)
return _post(*args, **kwargs) return _post(*args, **kwargs)
def put(self, *args, **kwargs) -> Response: def put(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)): 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) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _put(*_args, **_kwargs): def _put(*args, **kwargs):
return self.session.put(*_args, **_kwargs) return self.session.put(*args, **kwargs)
return _put(*args, **kwargs) return _put(*args, **kwargs)
def head(self, *args, **kwargs) -> Response: def head(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)): 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) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _head(*_args, **_kwargs): def _head(*args, **kwargs):
return self.session.head(*_args, **_kwargs) return self.session.head(*args, **kwargs)
return _head(*args, **kwargs) return _head(*args, **kwargs)
def delete(self, *args, **kwargs) -> Response: def delete(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)): 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) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _delete(*_args, **_kwargs): def _delete(*args, **kwargs):
return self.session.delete(*_args, **_kwargs) return self.session.delete(*args, **kwargs)
return _delete(*args, **kwargs) return _delete(*args, **kwargs)
def options(self, *args, **kwargs) -> Response: def options(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)): 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) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _options(*_args, **_kwargs): def _options(*args, **kwargs):
return self.session.options(*_args, **_kwargs) return self.session.options(*args, **kwargs)
return _options(*args, **kwargs) return _options(*args, **kwargs)
def __getattr__(self, name): def __getattr__(self, name):
return getattr(requests, name) return getattr(requests, name)
def container_ip(container: Container) -> str: def container_ip(container: Container):
""" """
return the IP address of a container. return the IP address of a container.
@ -179,7 +171,7 @@ def container_ip(container: Container) -> str:
return net_info[network_name]["IPAddress"] return net_info[network_name]["IPAddress"]
def container_ipv6(container: Container) -> str: def container_ipv6(container):
""" """
return the IPv6 address of a container. return the IPv6 address of a container.
""" """
@ -196,7 +188,7 @@ def container_ipv6(container: Container) -> str:
return net_info[network_name]["GlobalIPv6Address"] return net_info[network_name]["GlobalIPv6Address"]
def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]: def nginx_proxy_dns_resolver(domain_name):
""" """
if "nginx-proxy" if found in host, return the ip address of the docker container if "nginx-proxy" if found in host, return the ip address of the docker container
issued from the docker image nginxproxy/nginx-proxy:test. issued from the docker image nginxproxy/nginx-proxy:test.
@ -208,21 +200,21 @@ def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]:
if 'nginx-proxy' in domain_name: if 'nginx-proxy' in domain_name:
nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"}) nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"})
if len(nginxproxy_containers) == 0: if len(nginxproxy_containers) == 0:
log.warning(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}") log.warn(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"}) exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"})
if len(exited_nginxproxy_containers) > 0: if len(exited_nginxproxy_containers) > 0:
exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs() exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs()
log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode()) log.warn(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
return None return
nginxproxy_container = nginxproxy_containers[0] nginxproxy_container = nginxproxy_containers[0]
ip = container_ip(nginxproxy_container) ip = container_ip(nginxproxy_container)
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}") log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}")
return ip return ip
def docker_container_dns_resolver(domain_name: str) -> Optional[str]: def docker_container_dns_resolver(domain_name):
""" """
if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", return the ip address of the docker container
return the ip address of the docker container named XXX. named XXX.
:return: IP or None :return: IP or None
""" """
@ -232,15 +224,15 @@ def docker_container_dns_resolver(domain_name: str) -> Optional[str]:
match = re.search(r'(^|.+\.)(?P<container>[^.]+)\.container\.docker$', domain_name) match = re.search(r'(^|.+\.)(?P<container>[^.]+)\.container\.docker$', domain_name)
if not match: if not match:
log.debug(f"{domain_name!r} does not match") log.debug(f"{domain_name!r} does not match")
return None return
container_name = match.group('container') container_name = match.group('container')
log.debug(f"looking for container {container_name!r}") log.debug(f"looking for container {container_name!r}")
try: try:
container = docker_client.containers.get(container_name) container = docker_client.containers.get(container_name)
except docker.errors.NotFound: except docker.errors.NotFound:
log.warning(f"container named {container_name!r} not found while resolving {domain_name!r}") log.warn(f"container named {container_name!r} not found while resolving {domain_name!r}")
return None return
log.debug(f"container {container.name!r} found ({container.short_id})") log.debug(f"container {container.name!r} found ({container.short_id})")
ip = container_ip(container) ip = container_ip(container)
@ -252,10 +244,7 @@ def monkey_patch_urllib_dns_resolver():
""" """
Alter the behavior of the urllib DNS resolver so that any domain name Alter the behavior of the urllib DNS resolver so that any domain name
containing substring 'nginx-proxy' will resolve to the IP address 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 prv_getaddrinfo = socket.getaddrinfo
dns_cache = {} dns_cache = {}
@ -263,17 +252,12 @@ def monkey_patch_urllib_dns_resolver():
logging.getLogger('DNS').debug(f"resolving domain name {repr(args)}") logging.getLogger('DNS').debug(f"resolving domain name {repr(args)}")
_args = list(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`. # Otherwise a pytest container not using the host network fails to pass `test_raw-ip-vhost`.
if FORCE_CONTAINER_IPV6 and not HAS_IPV6: if FORCE_CONTAINER_IPV6 and not HAS_IPV6:
pytest.skip("This system does not support IPv6") pytest.skip("This system does not support IPv6")
# custom DNS resolvers # custom DNS resolvers
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]) ip = nginx_proxy_dns_resolver(args[0])
if ip is None: if ip is None:
ip = docker_container_dns_resolver(args[0]) ip = docker_container_dns_resolver(args[0])
@ -290,12 +274,19 @@ def monkey_patch_urllib_dns_resolver():
socket.getaddrinfo = new_getaddrinfo socket.getaddrinfo = new_getaddrinfo
return prv_getaddrinfo return prv_getaddrinfo
def restore_urllib_dns_resolver(getaddrinfo_func): def restore_urllib_dns_resolver(getaddrinfo_func):
socket.getaddrinfo = getaddrinfo_func socket.getaddrinfo = getaddrinfo_func
def get_nginx_conf_from_container(container: Container) -> bytes: 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):
""" """
return the nginx /etc/nginx/conf.d/default.conf file content from a container return the nginx /etc/nginx/conf.d/default.conf file content from a container
""" """
@ -310,40 +301,20 @@ def get_nginx_conf_from_container(container: Container) -> bytes:
return conffile.read() return conffile.read()
def __prepare_and_execute_compose_cmd(compose_files: List[str], project_name: str, cmd: str): def docker_compose_up(compose_file='docker-compose.yml'):
""" logging.info(f'{DOCKER_COMPOSE} -f {compose_file} up -d')
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: try:
subprocess.check_output(shlex.split(compose_cmd.getvalue()), stderr=subprocess.STDOUT) subprocess.check_output(shlex.split(f'{DOCKER_COMPOSE} -f {compose_file} up -d'), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
pytest.fail(f"Error while running '{compose_cmd.getvalue()}':\n{e.output}", pytrace=False) pytest.fail(f"Error while runninng '{DOCKER_COMPOSE} -f {compose_file} up -d':\n{e.output}", pytrace=False)
def docker_compose_up(compose_files: List[str], project_name: str): def docker_compose_down(compose_file='docker-compose.yml'):
""" logging.info(f'{DOCKER_COMPOSE} -f {compose_file} down -v')
Execute compose up --detach with the provided compose files and project name. try:
""" subprocess.check_output(shlex.split(f'{DOCKER_COMPOSE} -f {compose_file} down -v'), stderr=subprocess.STDOUT)
if compose_files is None or len(compose_files) == 0: except subprocess.CalledProcessError as e:
pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False) pytest.fail(f"Error while runninng '{DOCKER_COMPOSE} -f {compose_file} down -v':\n{e.output}", 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(): def wait_for_nginxproxy_to_be_ready():
@ -362,50 +333,35 @@ def wait_for_nginxproxy_to_be_ready():
@pytest.fixture @pytest.fixture
def docker_compose_files(request: FixtureRequest) -> List[str]: def docker_compose_file(request):
"""Fixture returning the docker compose files to consider: """Fixture naming the docker compose file to consider.
If a YAML file exists with the same name as the test module (with the `.py` extension If a YAML file exists with the same name as the test module (with the `.py` extension replaced
replaced with `.base.yml`, ie `test_foo.py`-> `test_foo.base.yml`) and in the same with `.yml` or `.yaml`), use that. Otherwise, use `docker-compose.yml` in the same directory
directory as the test module, use only that file. as the test module.
Otherwise, merge the following files in this order:
- the `compose.base.yml` file in the parent `test` directory.
- if present in the same directory as the test module, the `compose.base.override.yml` file.
- the YAML file named after the current test module (ie `test_foo.py`-> `test_foo.yml`)
Tests can override this fixture to specify a custom location. Tests can override this fixture to specify a custom location.
""" """
compose_files: List[str] = [] test_module_dir = os.path.dirname(request.module.__file__)
test_module_path = pathlib.Path(request.module.__file__).parent 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')
module_base_file = test_module_path.joinpath(f"{request.module.__name__}.base.yml") if os.path.isfile(yml_file):
if module_base_file.is_file(): docker_compose_file = yml_file
return [module_base_file.as_posix()] elif os.path.isfile(yaml_file):
docker_compose_file = yaml_file
else:
docker_compose_file = default_file
global_base_file = test_module_path.parent.joinpath("compose.base.yml") if not os.path.isfile(docker_compose_file):
if global_base_file.is_file(): logging.error("Could not find any docker compose file named either '{0}.yml', '{0}.yaml' or 'docker-compose.yml'".format(request.module.__name__))
compose_files.append(global_base_file.as_posix())
module_base_override_file = test_module_path.joinpath("compose.base.override.yml") logging.debug(f"using docker compose file {docker_compose_file}")
if module_base_override_file.is_file(): return docker_compose_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: Network) -> Optional[Network]: def connect_to_network(network):
""" """
If we are running from a container, connect our container to the given network If we are running from a container, connect our container to the given network
@ -415,8 +371,8 @@ def connect_to_network(network: Network) -> Optional[Network]:
try: try:
my_container = docker_client.containers.get(test_container) my_container = docker_client.containers.get(test_container)
except docker.errors.NotFound: except docker.errors.NotFound:
logging.warning(f"container {test_container} not found") logging.warn(f"container {test_container} not found")
return None return
# figure out our container networks # figure out our container networks
my_networks = list(my_container.attrs["NetworkSettings"]["Networks"].keys()) my_networks = list(my_container.attrs["NetworkSettings"]["Networks"].keys())
@ -433,7 +389,7 @@ def connect_to_network(network: Network) -> Optional[Network]:
return network return network
def disconnect_from_network(network: Network = None): def disconnect_from_network(network=None):
""" """
If we are running from a container, disconnect our container from the given network. If we are running from a container, disconnect our container from the given network.
@ -443,7 +399,7 @@ def disconnect_from_network(network: Network = None):
try: try:
my_container = docker_client.containers.get(test_container) my_container = docker_client.containers.get(test_container)
except docker.errors.NotFound: except docker.errors.NotFound:
logging.warning(f"container {test_container} not found") logging.warn(f"container {test_container} not found")
return return
# figure out our container networks # figure out our container networks
@ -455,7 +411,7 @@ def disconnect_from_network(network: Network = None):
network.disconnect(my_container) network.disconnect(my_container)
def connect_to_all_networks() -> List[Network]: def connect_to_all_networks():
""" """
If we are running from a container, connect our container to all current docker networks. If we are running from a container, connect our container to all current docker networks.
@ -471,34 +427,31 @@ def connect_to_all_networks() -> List[Network]:
class DockerComposer(contextlib.AbstractContextManager): class DockerComposer(contextlib.AbstractContextManager):
def __init__(self): def __init__(self):
self._networks = None self._docker_compose_file = None
self._docker_compose_files = None
self._project_name = None
def __exit__(self, *exc_info): def __exit__(self, *exc_info):
self._down() self._down()
def _down(self): def _down(self):
if self._docker_compose_files is None: if self._docker_compose_file is None:
return return
for network in self._networks: for network in self._networks:
disconnect_from_network(network) disconnect_from_network(network)
docker_compose_down(self._docker_compose_files, self._project_name) docker_compose_down(self._docker_compose_file)
self._docker_compose_file = None self._docker_compose_file = None
self._project_name = None
def compose(self, docker_compose_files: List[str], project_name: str): def compose(self, docker_compose_file):
if docker_compose_files == self._docker_compose_files and project_name == self._project_name: if docker_compose_file == self._docker_compose_file:
return return
self._down() self._down()
if docker_compose_files is None or project_name is None: if docker_compose_file is None:
return return
docker_compose_up(docker_compose_files, project_name) remove_all_containers()
docker_compose_up(docker_compose_file)
self._networks = connect_to_all_networks() self._networks = connect_to_all_networks()
wait_for_nginxproxy_to_be_ready() wait_for_nginxproxy_to_be_ready()
time.sleep(3) # give time to containers to be ready time.sleep(3) # give time to containers to be ready
self._docker_compose_files = docker_compose_files self._docker_compose_file = docker_compose_file
self._project_name = project_name
############################################################################### ###############################################################################
@ -509,14 +462,14 @@ class DockerComposer(contextlib.AbstractContextManager):
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def docker_composer() -> Iterator[DockerComposer]: def docker_composer():
with DockerComposer() as d: with DockerComposer() as d:
yield d yield d
@pytest.fixture @pytest.fixture
def ca_root_certificate() -> str: def ca_root_certificate():
return CA_ROOT_CERTIFICATE.as_posix() return CA_ROOT_CERTIFICATE
@pytest.fixture @pytest.fixture
@ -527,38 +480,25 @@ def monkey_patched_dns():
@pytest.fixture @pytest.fixture
def docker_compose( def docker_compose(monkey_patched_dns, docker_composer, docker_compose_file):
request: FixtureRequest, """Ensures containers described in a docker compose file are started.
monkeypatch,
monkey_patched_dns, A custom docker compose file name can be specified by overriding the `docker_compose_file`
docker_composer, fixture.
docker_compose_files
) -> Iterator[DockerClient]: 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.
""" """
Ensures containers necessary for the test module are started in a compose project, docker_composer.compose(docker_compose_file)
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 yield docker_client
@pytest.fixture @pytest.fixture()
def nginxproxy() -> Iterator[RequestsForDocker]: def nginxproxy():
""" """
Provides the `nginxproxy` object that can be used in the same way the requests module is: Provides the `nginxproxy` object that can be used in the same way the requests module is:
r = nginxproxy.get("https://foo.com") r = nginxproxy.get("http://foo.com")
The difference is that in case an HTTP requests has status code 404 or 502 (which mostly 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. indicates that nginx has just reloaded), we retry up to 30 times the query.
@ -567,11 +507,11 @@ def nginxproxy() -> Iterator[RequestsForDocker]:
made against containers to use the containers IPv6 address when set to `True`. If IPv6 is not 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. supported by the system or docker, that particular test will be skipped.
""" """
yield RequestsForDocker() yield requests_for_docker()
@pytest.fixture @pytest.fixture()
def acme_challenge_path() -> str: def acme_challenge_path():
""" """
Provides fake Let's Encrypt ACME challenge path used in certain tests Provides fake Let's Encrypt ACME challenge path used in certain tests
""" """
@ -583,13 +523,14 @@ def acme_challenge_path() -> str:
# #
############################################################################### ###############################################################################
# pytest hook to display additional stuff in test report # pytest hook to display additionnal stuff in test report
def pytest_runtest_logreport(report): def pytest_runtest_logreport(report):
if report.failed: if report.failed:
if isinstance(report.longrepr, ReprExceptionInfo):
test_containers = docker_client.containers.list(all=True, filters={"ancestor": "nginxproxy/nginx-proxy:test"}) test_containers = docker_client.containers.list(all=True, filters={"ancestor": "nginxproxy/nginx-proxy:test"})
for container in test_containers: for container in test_containers:
report.longrepr.addsection('nginx-proxy logs', container.logs().decode()) report.longrepr.addsection('nginx-proxy logs', container.logs())
report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container).decode()) report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container))
# Py.test `incremental` marker, see http://stackoverflow.com/a/12579625/107049 # Py.test `incremental` marker, see http://stackoverflow.com/a/12579625/107049
@ -616,5 +557,5 @@ try:
except docker.errors.ImageNotFound: except docker.errors.ImageNotFound:
pytest.exit("The docker image 'nginxproxy/nginx-proxy:test' is missing") pytest.exit("The docker image 'nginxproxy/nginx-proxy:test' is missing")
if Version(docker.__version__) < Version("7.0.0"): if LooseVersion(docker.__version__) < LooseVersion("5.0.0"):
pytest.exit("This test suite is meant to work with the python docker module v7.0.0 or later") pytest.exit("This test suite is meant to work with the python docker module v5.0.0 or later")

View file

@ -3,7 +3,7 @@
# # # #
# This script is meant to run the test suite from a Docker container. # # This script is meant to run the test suite from a Docker container. #
# # # #
# This is useful when you want to run the test suite from Mac or # # This is usefull when you want to run the test suite from Mac or #
# Docker Toolbox. # # Docker Toolbox. #
# # # #
############################################################################### ###############################################################################

View file

@ -1,4 +1,4 @@
FROM python:3.12 FROM python:3.9
ENV PYTEST_RUNNING_IN_CONTAINER=1 ENV PYTEST_RUNNING_IN_CONTAINER=1

View file

@ -1,6 +1,4 @@
backoff==2.2.1 backoff==2.2.1
docker==7.1.0 docker==7.1.0
packaging==24.2
pytest==8.3.4 pytest==8.3.4
requests==2.32.3 requests==2.32.3
urllib3==2.3.0

View file

@ -28,7 +28,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
self.send_header("Content-Type", "text/plain") self.send_header("Content-Type", "text/plain")
self.end_headers() self.end_headers()
if len(response_body): if (len(response_body)):
self.wfile.write(response_body.encode()) self.wfile.write(response_body.encode())
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -0,0 +1 @@
This directory contains tests that showcase scenarios known to break the expected behavior of nginx-proxy.

View file

@ -1,12 +1,17 @@
version: "2"
networks: networks:
netA: netA:
netB: netB:
services: services:
nginx-proxy: reverseproxy:
container_name: reverseproxy container_name: reverseproxy
networks: networks:
- netA - netA
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
webA: webA:
networks: networks:
@ -15,7 +20,7 @@ services:
expose: expose:
- 81 - 81
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: webA.nginx-proxy VIRTUAL_HOST: webA.nginx-proxy
webB: webB:
@ -25,5 +30,5 @@ services:
expose: expose:
- 82 - 82
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: webB.nginx-proxy VIRTUAL_HOST: webB.nginx-proxy

View file

@ -1,3 +1,5 @@
import pytest
def test_unknown_virtual_host(docker_compose, nginxproxy): def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port") r = nginxproxy.get("http://nginx-proxy/port")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,16 +1,12 @@
services: version: "2"
nginx-proxy:
volumes:
- /var/run/docker.sock:/f00.sock:ro
environment:
DOCKER_HOST: unix:///f00.sock
services:
web1: web1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.tld VIRTUAL_HOST: web1.nginx-proxy.tld
web2: web2:
@ -18,5 +14,12 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.tld 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

View file

@ -1,6 +0,0 @@
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

View file

@ -1,27 +1,30 @@
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): import pytest
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web1.nginx-proxy.tld/{acme_challenge_path}", f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False allow_redirects=False
) )
assert r.status_code == 200
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get(
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False
)
assert r.status_code == 301 assert r.status_code == 301
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web3.nginx-proxy.tld/{acme_challenge_path}", f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False allow_redirects=False
) )
assert r.status_code == 200 assert r.status_code == 200
def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web4.nginx-proxy.tld/{acme_challenge_path}", f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False allow_redirects=False
) )
assert r.status_code == 404 assert r.status_code == 404
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get(
f"http://web4.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False
)
assert r.status_code == 200

View file

@ -1,8 +1,6 @@
services: version: "2"
nginx-proxy:
environment:
ACME_HTTP_CHALLENGE_LOCATION: "false"
services:
web1: web1:
image: web image: web
expose: expose:
@ -38,3 +36,12 @@ services:
VIRTUAL_HOST: "web4.nginx-proxy.tld" VIRTUAL_HOST: "web4.nginx-proxy.tld"
HTTPS_METHOD: noredirect HTTPS_METHOD: noredirect
ACME_HTTP_CHALLENGE_LOCATION: "true" 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

View file

@ -1,27 +1,30 @@
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): import pytest
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web1.nginx-proxy.tld/{acme_challenge_path}", f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False allow_redirects=False
) )
assert r.status_code == 301 assert r.status_code == 200
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web2.nginx-proxy.tld/{acme_challenge_path}", f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False allow_redirects=False
) )
assert r.status_code == 301
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get(
f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False
)
assert r.status_code == 200 assert r.status_code == 200
def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path): def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get(
f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False
)
assert r.status_code == 404
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web4.nginx-proxy.tld/{acme_challenge_path}", f"http://web4.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False allow_redirects=False
) )
assert r.status_code == 200 assert r.status_code == 404

View file

@ -1,3 +1,5 @@
version: "2"
services: services:
web1: web1:
image: web image: web
@ -34,3 +36,10 @@ services:
VIRTUAL_HOST: "web4.nginx-proxy.tld" VIRTUAL_HOST: "web4.nginx-proxy.tld"
HTTPS_METHOD: noredirect HTTPS_METHOD: noredirect
ACME_HTTP_CHALLENGE_LOCATION: "false" 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

View file

@ -1,3 +1,6 @@
import pytest
def test_redirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path): def test_redirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path):
r = nginxproxy.get( r = nginxproxy.get(
f"http://web1.nginx-proxy.tld/{acme_challenge_path}", f"http://web1.nginx-proxy.tld/{acme_challenge_path}",

View file

@ -1,8 +1,6 @@
services: version: "2"
nginx-proxy:
environment:
ACME_HTTP_CHALLENGE_LOCATION: "legacy"
services:
web1: web1:
image: web image: web
expose: expose:
@ -19,3 +17,12 @@ services:
WEB_PORTS: "82" WEB_PORTS: "82"
VIRTUAL_HOST: "web2.nginx-proxy.tld" VIRTUAL_HOST: "web2.nginx-proxy.tld"
HTTPS_METHOD: noredirect 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

View file

@ -1,25 +1,22 @@
""" """
Test that nginx-proxy-tester can build successfully Test that nginx-proxy-tester can build successfully
""" """
import pathlib
import re
import docker
import pytest import pytest
import docker
import re
import os
client = docker.from_env() client = docker.from_env()
@pytest.fixture(scope = "session") @pytest.fixture(scope = "session")
def docker_build(request): def docker_build(request):
# Define Dockerfile path # Define Dockerfile path
current_file_path = pathlib.Path(__file__) dockerfile_path = os.path.join(os.path.dirname(__file__), "requirements/")
dockerfile_path = current_file_path.parent.parent.joinpath("requirements")
dockerfile_name = "Dockerfile-nginx-proxy-tester" dockerfile_name = "Dockerfile-nginx-proxy-tester"
# Build the Docker image # Build the Docker image
image, logs = client.images.build( image, logs = client.images.build(
path = dockerfile_path.as_posix(), path = dockerfile_path,
dockerfile = dockerfile_name, dockerfile = dockerfile_name,
rm = True, # Remove intermediate containers rm = True, # Remove intermediate containers
tag = "nginx-proxy-tester-ci", # Tag for the built image tag = "nginx-proxy-tester-ci", # Tag for the built image

View file

@ -1,3 +1,4 @@
import pytest
import re import re

View file

@ -1,5 +1,8 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/50x.html:/usr/share/nginx/html/errors/50x.html:ro - ./50x.html:/usr/share/nginx/html/errors/50x.html:ro

View file

@ -1,3 +1,5 @@
import pytest
def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/") r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,16 +1,19 @@
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/default_location:ro - ./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 - ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.example_location:ro
web1: web1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
web2: web2:
@ -18,7 +21,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.example VIRTUAL_HOST: web2.nginx-proxy.example
web3: web3:
@ -26,5 +29,5 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: "83" WEB_PORTS: 83
VIRTUAL_HOST: web3.nginx-proxy.example VIRTUAL_HOST: web3.nginx-proxy.example

View file

@ -1,3 +1,5 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/") r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,15 +1,18 @@
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/proxy.conf:ro - ./my_custom_proxy_settings_f00.conf:/etc/nginx/proxy.conf:ro
web1: web1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
web2: web2:
@ -17,5 +20,5 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.example VIRTUAL_HOST: web2.nginx-proxy.example

View file

@ -1,3 +1,5 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/") r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,16 +1,19 @@
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example_location:ro - ./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 - ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430_location:ro
web1: web1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
web2: web2:
@ -18,7 +21,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.example VIRTUAL_HOST: web2.nginx-proxy.example
regex: regex:
@ -26,5 +29,5 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: "83" WEB_PORTS: 83
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$ VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$

View file

@ -1,3 +1,5 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/") r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,16 +1,19 @@
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example:ro - ./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 - ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430:ro
web1: web1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
web2: web2:
@ -18,7 +21,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.example VIRTUAL_HOST: web2.nginx-proxy.example
regex: regex:
@ -26,5 +29,5 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: "83" WEB_PORTS: 83
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$ VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$

View file

@ -1,3 +1,5 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy): def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/") r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,15 +1,18 @@
version: "2"
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/conf.d/my_custom_proxy_settings_f00.conf:ro - ./my_custom_proxy_settings_f00.conf:/etc/nginx/conf.d/my_custom_proxy_settings_f00.conf:ro
web1: web1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
web2: web2:
@ -17,5 +20,5 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.example VIRTUAL_HOST: web2.nginx-proxy.example

View file

@ -1,8 +1,6 @@
import json import json
import pytest import pytest
def test_debug_endpoint_is_enabled_globally(docker_compose, nginxproxy): def test_debug_endpoint_is_enabled_globally(docker_compose, nginxproxy):
r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug") r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -1,5 +1,8 @@
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
environment: environment:
DEBUG_ENDPOINT: "true" DEBUG_ENDPOINT: "true"
@ -8,7 +11,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: enabled.debug.nginx-proxy.example VIRTUAL_HOST: enabled.debug.nginx-proxy.example
debug_stripped: debug_stripped:
@ -16,7 +19,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST_MULTIPORTS: |- VIRTUAL_HOST_MULTIPORTS: |-
stripped.debug.nginx-proxy.example: stripped.debug.nginx-proxy.example:
"/1": "/1":
@ -45,7 +48,7 @@ services:
expose: expose:
- "84" - "84"
environment: environment:
WEB_PORTS: "84" WEB_PORTS: 84
VIRTUAL_HOST: ~^regexp.*\.debug.nginx-proxy.example VIRTUAL_HOST: ~^regexp.*\.debug.nginx-proxy.example
debug_disabled: debug_disabled:
@ -53,7 +56,7 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: "83" WEB_PORTS: 83
VIRTUAL_HOST: disabled.debug.nginx-proxy.example VIRTUAL_HOST: disabled.debug.nginx-proxy.example
labels: labels:
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "false" com.github.nginx-proxy.nginx-proxy.debug-endpoint: "false"

View file

@ -1,8 +1,6 @@
import json import json
import pytest import pytest
def test_debug_endpoint_is_disabled_globally(docker_compose, nginxproxy): def test_debug_endpoint_is_disabled_globally(docker_compose, nginxproxy):
r = nginxproxy.get("http://disabled1.debug.nginx-proxy.example/nginx-proxy-debug") r = nginxproxy.get("http://disabled1.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 404 assert r.status_code == 404

View file

@ -1,10 +1,15 @@
services: services:
nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
debug_disabled1: debug_disabled1:
image: web image: web
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: "81" WEB_PORTS: 81
VIRTUAL_HOST: disabled1.debug.nginx-proxy.example VIRTUAL_HOST: disabled1.debug.nginx-proxy.example
debug_disabled2: debug_disabled2:
@ -12,7 +17,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: "82" WEB_PORTS: 82
VIRTUAL_HOST: disabled2.debug.nginx-proxy.example VIRTUAL_HOST: disabled2.debug.nginx-proxy.example
@ -21,7 +26,7 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: "83" WEB_PORTS: 83
VIRTUAL_HOST: enabled.debug.nginx-proxy.example VIRTUAL_HOST: enabled.debug.nginx-proxy.example
labels: labels:
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "true" com.github.nginx-proxy.nginx-proxy.debug-endpoint: "true"

View file

@ -1,3 +1,6 @@
import pytest
def test_fallback_on_default(docker_compose, nginxproxy): def test_fallback_on_default(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port") r = nginxproxy.get("http://unknown.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -0,0 +1,19 @@
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

View file

@ -1,12 +0,0 @@
services:
nginx-proxy:
environment:
DEFAULT_HOST: web1.tld
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: web1.tld

1
test/test_dockergen/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
nginx.tmpl

View file

@ -0,0 +1,10 @@
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx.container.docker/")
assert r.status_code == 503
def test_forwards_to_whoami(docker_compose, nginxproxy):
r = nginxproxy.get("http://whoami.nginx.container.docker/")
assert r.status_code == 200
whoami_container = docker_compose.containers.get("whoami")
assert r.text == f"I'm {whoami_container.id[:12]}\n"

View file

@ -0,0 +1,26 @@
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

View file

@ -1,11 +1,11 @@
import docker import docker
import pytest import pytest
from packaging.version import Version from distutils.version import LooseVersion
raw_version = docker.from_env().version()["Version"] raw_version = docker.from_env().version()["Version"]
pytestmark = pytest.mark.skipif( pytestmark = pytest.mark.skipif(
Version(raw_version) < Version("1.13"), LooseVersion(raw_version) < LooseVersion("1.13"),
reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})" reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})"
) )

View file

@ -1,18 +1,13 @@
volumes: version: "3"
nginx_conf:
services: services:
nginx-proxy-nginx: nginx:
image: nginx image: nginx
container_name: nginx container_name: nginx
volumes: volumes:
- nginx_conf:/etc/nginx/conf.d:ro - nginx_conf:/etc/nginx/conf.d
ports:
- "80:80"
- "443:443"
nginx-proxy-dockergen: dockergen:
image: nginxproxy/docker-gen image: nginxproxy/docker-gen
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes: volumes:
@ -26,5 +21,8 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: "80" WEB_PORTS: 80
VIRTUAL_HOST: whoami.nginx.container.docker VIRTUAL_HOST: whoami.nginx.container.docker
volumes:
nginx_conf: {}

View file

@ -1,3 +1,6 @@
import pytest
def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy): def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy):
r = nginxproxy.get("http://nohttp-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False) r = nginxproxy.get("http://nohttp-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False)
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,5 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./withdefault.certs:/etc/nginx/certs:ro
environment: environment:
ENABLE_HTTP_ON_MISSING_CERT: "false" ENABLE_HTTP_ON_MISSING_CERT: "false"

View file

@ -7,7 +7,7 @@ import pytest
from docker.errors import NotFound from docker.errors import NotFound
@pytest.fixture @pytest.fixture()
def web1(docker_compose): def web1(docker_compose):
""" """
pytest fixture creating a web container with `VIRTUAL_HOST=web1.nginx-proxy` listening on port 81. 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} ports={"81/tcp": None}
) )
docker_compose.networks.get("test_events-net").connect(container) docker_compose.networks.get("test_default").connect(container)
sleep(2) # give it some time to initialize and for docker-gen to detect it sleep(2) # give it some time to initialize and for docker-gen to detect it
yield container yield container
try: try:
@ -30,7 +30,7 @@ def web1(docker_compose):
except NotFound: except NotFound:
pass pass
@pytest.fixture @pytest.fixture()
def web2(docker_compose): 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. 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} ports={"82/tcp": None}
) )
docker_compose.networks.get("test_events-net").connect(container) docker_compose.networks.get("test_default").connect(container)
sleep(2) # give it some time to initialize and for docker-gen to detect it sleep(2) # give it some time to initialize and for docker-gen to detect it
yield container yield container
try: try:

View file

@ -1,9 +1,7 @@
version: "2"
services: services:
nginx-proxy: nginxproxy:
image: nginxproxy/nginx-proxy:test image: nginxproxy/nginx-proxy:test
container_name: nginx-proxy
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
ports:
- "80:80"
- "443:443"

View file

@ -1,3 +0,0 @@
networks:
default:
name: test_events-net

View file

@ -0,0 +1,17 @@
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

View file

@ -1,8 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/nodefault.certs:/etc/nginx/certs:ro - ./nodefault.certs:/etc/nginx/certs:ro
https-and-http: https-and-http:
image: web image: web

View file

@ -1,8 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro - ./withdefault.certs:/etc/nginx/certs:ro
environment: environment:
HTTPS_METHOD: redirect HTTPS_METHOD: redirect

View file

@ -1,8 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro - ./withdefault.certs:/etc/nginx/certs:ro
environment: environment:
HTTPS_METHOD: nohttp HTTPS_METHOD: nohttp

View file

@ -1,8 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro - ./withdefault.certs:/etc/nginx/certs:ro
environment: environment:
HTTPS_METHOD: nohttp HTTPS_METHOD: nohttp

View file

@ -1,5 +1,10 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
environment: environment:
HTTPS_METHOD: redirect HTTPS_METHOD: redirect

View file

@ -1,8 +1,12 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro environment:
HTTPS_METHOD: nohttps
http-only: http-only:
image: web image: web
@ -11,4 +15,3 @@ services:
environment: environment:
WEB_PORTS: "83" WEB_PORTS: "83"
VIRTUAL_HOST: http-only.nginx-proxy.test VIRTUAL_HOST: http-only.nginx-proxy.test
HTTPS_METHOD: nohttps

View file

@ -1,8 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro - ./withdefault.certs:/etc/nginx/certs:ro
environment: environment:
TRUST_DEFAULT_CERT: "false" TRUST_DEFAULT_CERT: "false"

View file

@ -1,8 +1,11 @@
version: "2"
services: services:
nginx-proxy: sut:
image: nginxproxy/nginx-proxy:test
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro - ./withdefault.certs:/etc/nginx/certs:ro
https-and-http: https-and-http:
image: web image: web

View file

@ -1,32 +1,32 @@
import pathlib import os.path
import re import re
from typing import List, Callable
import backoff import backoff
import pytest import pytest
import requests import requests
from requests import Response
@pytest.fixture @pytest.fixture
def docker_compose_files(compose_file) -> List[str]: def data_dir():
data_dir = pathlib.Path(__file__).parent.joinpath("test_fallback.data") return f"{os.path.splitext(__file__)[0]}.data"
return [
data_dir.joinpath("compose.base.yml"),
data_dir.joinpath(compose_file).as_posix()
]
@pytest.fixture @pytest.fixture
def get(docker_compose, nginxproxy, want_err_re: re.Pattern[str]) -> Callable[[str], Response]: def docker_compose_file(data_dir, compose_file):
return os.path.join(data_dir, compose_file)
@pytest.fixture
def get(docker_compose, nginxproxy, want_err_re):
@backoff.on_exception( @backoff.on_exception(
backoff.constant, backoff.constant,
requests.exceptions.SSLError, requests.exceptions.SSLError,
giveup=lambda e: want_err_re and bool(want_err_re.search(str(e))), giveup=lambda e: want_err_re and want_err_re.search(str(e)),
interval=.3, interval=.3,
max_tries=30, max_tries=30,
jitter=None) jitter=None)
def _get(url) -> Response: def _get(url):
return nginxproxy.get(url, allow_redirects=False) return nginxproxy.get(url, allow_redirects=False)
return _get return _get
@ -108,7 +108,7 @@ INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
# should prefer that server for handling requests for unknown vhosts. # should prefer that server for handling requests for unknown vhosts.
("custom-fallback.yml", "http://unknown.nginx-proxy.test/", 418, None), ("custom-fallback.yml", "http://unknown.nginx-proxy.test/", 418, None),
]) ])
def test_fallback(get, compose_file, url, want_code, want_err_re): def test_fallback(get, url, want_code, want_err_re):
if want_err_re is None: if want_err_re is None:
r = get(url) r = get(url)
assert r.status_code == want_code assert r.status_code == want_code

View file

@ -1,9 +0,0 @@
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"

View file

@ -1,12 +0,0 @@
services:
nginx-proxy:
environment:
HTTPS_METHOD: nohttps
http-only:
image: web
expose:
- "83"
environment:
WEB_PORTS: "83"
VIRTUAL_HOST: http-only.nginx-proxy.test

View file

@ -1,70 +0,0 @@
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-----

View file

@ -1,27 +0,0 @@
-----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-----

View file

@ -1,10 +1,12 @@
version: "2"
services: services:
web: web:
image: web image: web
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: "80" WEB_PORTS: 80
VIRTUAL_HOST: web.nginx-proxy.tld VIRTUAL_HOST: web.nginx-proxy.tld
web-server-tokens-off: web-server-tokens-off:
@ -12,6 +14,11 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: "80" WEB_PORTS: 80
VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld
SERVER_TOKENS: "off" SERVER_TOKENS: "off"
sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro

View file

@ -1,15 +1,12 @@
services: version: "2"
nginx-proxy:
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro
services:
web: web:
image: web image: web
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: "80" WEB_PORTS: 80
VIRTUAL_HOST: web.nginx-proxy.tld VIRTUAL_HOST: web.nginx-proxy.tld
web-server-tokens-off: web-server-tokens-off:
@ -17,6 +14,17 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: "80" WEB_PORTS: 80
VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld
SERVER_TOKENS: "off" 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

View file

@ -1,3 +1,6 @@
import pytest
def test_forwards_to_bridge_network_container(docker_compose, nginxproxy): def test_forwards_to_bridge_network_container(docker_compose, nginxproxy):
r = nginxproxy.get("http://bridge-network.nginx-proxy.tld/port") r = nginxproxy.get("http://bridge-network.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -1,14 +1,11 @@
version: "2"
networks: networks:
net1: net1:
internal: true internal: true
net2: net2:
services: services:
nginx-proxy:
networks:
- net1
- net2
bridge-network: bridge-network:
image: web image: web
environment: environment:
@ -24,3 +21,11 @@ services:
VIRTUAL_HOST: "host-network.nginx-proxy.tld" VIRTUAL_HOST: "host-network.nginx-proxy.tld"
VIRTUAL_PORT: "8080" VIRTUAL_PORT: "8080"
network_mode: host network_mode: host
sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- net1
- net2

View file

@ -1,5 +1,5 @@
# Note: on Docker Desktop, host networking must be manually enabled. import pytest
# See https://docs.docker.com/engine/network/drivers/host/
def test_forwards_to_host_network_container_1(docker_compose, nginxproxy): def test_forwards_to_host_network_container_1(docker_compose, nginxproxy):
r = nginxproxy.get("http://host-network-1.nginx-proxy.tld:8888/port") r = nginxproxy.get("http://host-network-1.nginx-proxy.tld:8888/port")

View file

@ -1,9 +1,6 @@
services: version: "2"
nginx-proxy:
environment:
HTTP_PORT: 8888
network_mode: host
services:
host-network-1: host-network-1:
image: web image: web
environment: environment:
@ -19,3 +16,11 @@ services:
VIRTUAL_HOST: "host-network-2.nginx-proxy.tld" VIRTUAL_HOST: "host-network-2.nginx-proxy.tld"
VIRTUAL_PORT: "8181" VIRTUAL_PORT: "8181"
network_mode: host 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

View file

@ -1,5 +0,0 @@
services:
nginx-proxy:
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ${PYTEST_MODULE_PATH}/htpasswd:/etc/nginx/htpasswd:ro

View file

@ -1,8 +0,0 @@
services:
regex:
image: web
expose:
- "80"
environment:
WEB_PORTS: "80"
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$

View file

@ -1,8 +0,0 @@
services:
web:
image: web
expose:
- "80"
environment:
WEB_PORTS: "80"
VIRTUAL_HOST: htpasswd.nginx-proxy.tld

View file

@ -1,10 +0,0 @@
services:
web:
image: web
expose:
- "80"
environment:
WEB_PORTS: "80"
VIRTUAL_HOST: htpasswd.nginx-proxy.tld
VIRTUAL_PATH: /foo/
VIRTUAL_DEST: /

View file

@ -1,3 +1,5 @@
import pytest
def test_htpasswd_regex_virtual_host_is_restricted(docker_compose, nginxproxy): def test_htpasswd_regex_virtual_host_is_restricted(docker_compose, nginxproxy):
r = nginxproxy.get("http://regex.htpasswd.nginx-proxy.example/port") r = nginxproxy.get("http://regex.htpasswd.nginx-proxy.example/port")
assert r.status_code == 401 assert r.status_code == 401

Some files were not shown because too many files have changed in this diff Show more