Compare commits

..

30 commits

Author SHA1 Message Date
4856fbe7eb Aggiorna README.md
Some checks failed
Update Docker Hub Description / Update Docker Hub Description (push) Has been cancelled
2025-02-05 21:22:14 +08:00
23d56b2185 Aggiorna README.md
Some checks are pending
Update Docker Hub Description / Update Docker Hub Description (push) Waiting to run
2025-02-05 21:20:42 +08:00
Nicolas Duchon
1da623019f
Merge pull request #2576 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.14.5-debian
build: bump nginxproxy/docker-gen from 0.14.4 to 0.14.5
2025-01-19 23:03:10 +01:00
dependabot[bot]
e234ffba20
build: bump nginxproxy/docker-gen from 0.14.4-debian to 0.14.5-debian
Bumps nginxproxy/docker-gen from 0.14.4-debian to 0.14.5-debian.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-19 20:34:13 +00:00
Nicolas Duchon
18030a7896
Merge pull request #1737 from junderw/fix-redirect
feat: redirect non-GET methods using 308 instead of 301
2025-01-18 22:03:27 +01:00
Nicolas Duchon
dfbff1eb9c
Merge pull request #2561 from nginx-proxy/fix/proto-with-multiports
fix: add proto to VIRTUAL_HOST_MULTIPORTS
2025-01-18 22:01:09 +01:00
Nicolas Duchon
34a33a2255 tests: virtual proto 2025-01-18 21:50:41 +01:00
Nicolas Duchon
a61e485410 tests: refactor due to rebase 2025-01-18 20:41:17 +01:00
Nicolas Duchon
9312d5239a docs: typo 2025-01-18 20:25:01 +01:00
Nicolas Duchon
9fc7cec15c feat: customizable non get redirect code 2025-01-18 20:25:01 +01:00
Nicolas Duchon
8447a36046 tests: parameterize test 2025-01-18 20:25:01 +01:00
Nicolas Duchon
923f05032f tests: fix tests & test compose file 2025-01-18 20:25:01 +01:00
junderw
820d4a29ac tests: redirects 2025-01-18 20:25:01 +01:00
junderw
1859811311 feat: redirect using 308 for non-GET requests 2025-01-18 20:25:01 +01:00
Nicolas Duchon
691724c81f
Merge pull request #2570 from nginx-proxy/test/refactor-darwin
tests: factor out base nginx-proxy config and enable local testing on macOS / Darwin
2025-01-05 11:37:45 +01:00
Nicolas Duchon
aa8145b62d tests: review changes
Co-authored-by: Niek <100143256+SchoNie@users.noreply.github.com>
2025-01-05 00:05:30 +01:00
Nicolas Duchon
836012cad6 docs: update test README 2025-01-03 16:00:36 +01:00
Nicolas Duchon
005377c6e5 tests: remove remaining unneeded container config 2024-12-30 20:45:08 +01:00
Nicolas Duchon
bfdd72fe95 tests: type hints and linting 2024-12-30 14:17:03 +01:00
Nicolas Duchon
40309e2441 tests: enable local testing on macOS / Darwin 2024-12-30 13:41:47 +01:00
Nicolas Duchon
daa9449176 tests: factor out base nginx-proxy config 2024-12-30 12:07:30 +01:00
Nicolas Duchon
4ccbc3edec
Merge pull request #2569 from nginx-proxy/test/cleanup
tests: fix, cleanup and restructure test code
2024-12-27 21:47:56 +01:00
Nicolas Duchon
1f732a54c6 tests: missing doubles quotes on WEB_PORTS 2024-12-27 21:36:39 +01:00
Nicolas Duchon
ae0c9a8e96 tests: fixture type hints and style standardization 2024-12-27 21:36:07 +01:00
Nicolas Duchon
ea99c1a6f9 tests: review comments 2024-12-27 16:16:55 +01:00
Nicolas Duchon
1e9745f604 tests: complete typing, minor fixes 2024-12-26 16:21:30 +01:00
Nicolas Duchon
7b6baa43cd tests: remove custom system_has_ipv6() method 2024-12-26 01:13:29 +01:00
Nicolas Duchon
a2c316a876
docs: add powered by section with relevant JetBains IDEs
JetBrains is providing a license for GoLang and PyCharm to the
maintainer of this project as part of their open source program.
2024-12-25 15:39:00 +01:00
Nicolas Duchon
a25b7ea1ef docs: add proto to VIRTUAL_HOST_MULTIPORTS 2024-12-08 14:06:38 +01:00
Nicolas Duchon
9bd84fc95e fix: add proto to VIRTUAL_HOST_MULTIPORTS 2024-12-08 11:59:48 +01:00
146 changed files with 853 additions and 624 deletions

View file

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

View file

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

View file

@ -20,7 +20,17 @@ 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
@ -29,7 +39,12 @@ 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 :
@ -70,3 +85,8 @@ 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

@ -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_PATH` and `VIRTUAL_DEST` environment variables on this container. If your container expose more than one service on different ports and those services need to be proxied, you'll need to use the `VIRTUAL_HOST_MULTIPORTS` environment variable. This variable takes virtual host, path, port and dest definition in YAML (or JSON) form, and completely override the `VIRTUAL_HOST`, `VIRTUAL_PORT`, `VIRTUAL_PROTO`, `VIRTUAL_PATH` and `VIRTUAL_DEST` environment variables on this container.
The YAML syntax should be easier to write on Docker compose files, while the JSON syntax can be used for CLI invocation. The YAML syntax should be easier to write on Docker compose files, while the JSON syntax can be used for CLI invocation.
@ -66,19 +66,21 @@ The expected format is the following:
hostname: hostname:
path: path:
port: int port: int
proto: string
dest: string dest: string
``` ```
For each hostname entry, `path`, `port` and `dest` are optional and are assigned default values when missing: For each hostname entry, `path`, `port`, `proto` and `dest` are optional and are assigned default values when missing:
- `path` = "/" - `path` = "/"
- `port` = default port - `port` = default port
- `proto` = "http"
- `dest` = "" - `dest` = ""
The following examples use an hypothetical container running services on port 80, 8000 and 9000:
#### Multiple ports routed to different hostnames #### Multiple ports routed to different hostnames
The following example use an hypothetical container running services over HTTP on port 80, 8000 and 9000:
```yaml ```yaml
services: services:
multiport-container: multiport-container:
@ -111,12 +113,14 @@ services:
This would result in the following proxy config: This would result in the following proxy config:
- `www.example.org` -> `multiport-container:80` - `www.example.org` -> `multiport-container:80` over `HTTP`
- `service1.example.org` -> `multiport-container:8000` - `service1.example.org` -> `multiport-container:8000` over `HTTP`
- `service2.example.org` -> `multiport-container:9000` - `service2.example.org` -> `multiport-container:9000` over `HTTP`
#### 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:
@ -130,11 +134,12 @@ services:
port: 8000 port: 8000
dest: "/" dest: "/"
"/service2": "/service2":
port: 9000 port: 9443
proto: "https"
dest: "/" dest: "/"
# port and dest are not specified on the / path, so this path is routed # port and dest are not specified on the / path, so this path is routed to the
# to the default port with the default dest value (empty string) # default port with the default dest value (empty string) and default proto (http)
# JSON equivalent: # JSON equivalent:
# VIRTUAL_HOST_MULTIPORTS: |- # VIRTUAL_HOST_MULTIPORTS: |-
@ -142,16 +147,16 @@ services:
# "www.example.org": { # "www.example.org": {
# "/": {}, # "/": {},
# "/service1": { "port": 8000, "dest": "/" }, # "/service1": { "port": 8000, "dest": "/" },
# "/service2": { "port": 9000, "dest": "/" } # "/service2": { "port": 9443, "proto": "https", "dest": "/" }
# } # }
# } # }
``` ```
This would result in the following proxy config: This would result in the following proxy config:
- `www.example.org` -> `multiport-container:80` - `www.example.org` -> `multiport-container:80` over `HTTP`
- `www.example.org/service1` -> `multiport-container:8000` - `www.example.org/service1` -> `multiport-container:8000` over `HTTP`
- `www.example.org/service2` -> `multiport-container:9000` - `www.example.org/service2` -> `multiport-container:9443` over `HTTPS`
⬆️ [back to table of contents](#table-of-contents) ⬆️ [back to table of contents](#table-of-contents)
@ -575,6 +580,8 @@ 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).
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.
By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable `HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`. By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable `HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`.

View file

@ -32,6 +32,7 @@
{{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }} {{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }}
{{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }} {{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }}
{{- $_ := set $config "https_method" ($globals.Env.HTTPS_METHOD | default "redirect") }} {{- $_ := set $config "https_method" ($globals.Env.HTTPS_METHOD | default "redirect") }}
{{- $_ := set $config "non_get_redirect" ($globals.Env.NON_GET_REDIRECT | default "301") }}
{{- $_ := set $config "default_host" $globals.Env.DEFAULT_HOST }} {{- $_ := set $config "default_host" $globals.Env.DEFAULT_HOST }}
{{- $_ := set $config "resolvers" $globals.Env.RESOLVERS }} {{- $_ := set $config "resolvers" $globals.Env.RESOLVERS }}
{{- /* LOG_JSON is a shorthand that sets logging defaults to JSON format */}} {{- /* LOG_JSON is a shorthand that sets logging defaults to JSON format */}}
@ -589,18 +590,31 @@ proxy_set_header Proxy "";
{{- range $path, $vpath := $vhost }} {{- range $path, $vpath := $vhost }}
{{- if (empty $vpath) }} {{- if (empty $vpath) }}
{{- $vpath = dict "dest" "" "port" "default" }} {{- $vpath = dict
"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 }}
@ -635,6 +649,8 @@ 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" }}
@ -642,9 +658,15 @@ 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 }}
@ -664,8 +686,6 @@ 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" }}
@ -678,7 +698,6 @@ 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 }}
@ -718,8 +737,11 @@ proxy_set_header Proxy "";
{{- if and $https_method_disable_http (not $cert_ok) $enable_http_on_missing_cert }} {{- if and $https_method_disable_http (not $cert_ok) $enable_http_on_missing_cert }}
{{- $https_method = "noredirect" }} {{- $https_method = "noredirect" }}
{{- end }} {{- end }}
{{- $non_get_redirect := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.non-get-redirect" | keys | first | default $globals.config.non_get_redirect }}
{{- $http2_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable" | keys | first | default $globals.config.enable_http2 | parseBool }} {{- $http2_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable" | keys | first | default $globals.config.enable_http2 | parseBool }}
{{- $http3_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable" | keys | first | default $globals.config.enable_http3 | parseBool }} {{- $http3_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable" | keys | first | default $globals.config.enable_http3 | parseBool }}
{{- $acme_http_challenge := groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION" | first | default $globals.config.acme_http_challenge }} {{- $acme_http_challenge := groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION" | first | default $globals.config.acme_http_challenge }}
{{- $acme_http_challenge_legacy := eq $acme_http_challenge "legacy" }} {{- $acme_http_challenge_legacy := eq $acme_http_challenge "legacy" }}
{{- $acme_http_challenge_enabled := false }} {{- $acme_http_challenge_enabled := false }}
@ -746,6 +768,7 @@ proxy_set_header Proxy "";
"default" $default "default" $default
"hsts" $hsts "hsts" $hsts
"https_method" $https_method "https_method" $https_method
"non_get_redirect" $non_get_redirect
"http2_enabled" $http2_enabled "http2_enabled" $http2_enabled
"http3_enabled" $http3_enabled "http3_enabled" $http3_enabled
"is_regexp" $is_regexp "is_regexp" $is_regexp
@ -886,11 +909,14 @@ server {
{{- end }} {{- end }}
location / { location / {
{{- if eq $globals.config.external_https_port "443" }} {{- $redirect_uri := "https://$host$request_uri" }}
return 301 https://$host$request_uri; {{- if ne $globals.config.external_https_port "443" }}
{{- else }} {{- $redirect_uri = printf "https://$host:%s$request_uri" $globals.config.external_https_port }}
return 301 https://$host:{{ $globals.config.external_https_port }}$request_uri; {{- end}}
{{- end }} if ($request_method ~ (OPTIONS|POST|PUT|PATCH|DELETE)) {
return {{ $vhost.non_get_redirect }} {{ $redirect_uri }};
}
return 301 {{ $redirect_uri }};
} }
} }
{{- end }} {{- end }}

View file

@ -57,13 +57,39 @@ 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 find a yml file named after your test module filename. For instance, if your test module is `test_example.py`, then the `docker_compose` fixture will try to load a `test_example.yml` [docker compose file](https://docs.docker.com/compose/compose-file/). When using the `docker_compose` fixture in a test, pytest will try to start the [Docker Compose](https://docs.docker.com/compose/) services corresponding to the current test module, based on the test module filename.
Once the docker compose file found, the fixture will remove all containers, run `docker compose up`, and finally your test will be executed. By default, if your test module file is `test/test_subdir/test_example.py`, then the `docker_compose` fixture will try to load the following files, [merging them](https://docs.docker.com/reference/compose-file/merge/) in this order:
The fixture will run the _docker compose_ command with the `-f` option to load the given compose file. So you can test your docker compose file syntax by running it yourself with: 1. `test/compose.base.yml`
2. `test/test_subdir/compose.base.override.yml` (if it exists)
3. `test/test_subdir/test_example.yml`
docker compose -f test_example.yml up -d The fixture will run the _docker compose_ command with the `-f` option to load the given compose files. So you can test your docker compose file syntax by running it yourself with:
docker compose -f test/compose.base.yml -f test/test_subdir/test_example.yml up -d
The first file contains the base configuration of the nginx-proxy container common to most tests:
```yaml
services:
nginx-proxy:
image: nginxproxy/nginx-proxy:test
container_name: nginx-proxy
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
ports:
- "80:80"
- "443:443"
```
The second optional file allow you to override this base configuration for all test modules in a subfolder.
The third file contains the services and overrides specific to a given test module.
This automatic merge can be bypassed by using a file named `test_example.base.yml` (instead of `test_example.yml`). When this file exist, it will be the only one used by the test and no merge with other compose files will automatically occur.
The `docker_compose` fixture also set the `PYTEST_MODULE_PATH` environment variable to the absolute path of the current test module directory, so it can be used to mount files or directory relatives to the current test.
In the case you are running pytest from within a docker container, the `docker_compose` fixture will make sure the container running pytest is attached to all docker networks. That way, your test will be able to reach any of them. In the case you are running pytest from within a docker container, the `docker_compose` fixture will make sure the container running pytest is attached to all docker networks. That way, your test will be able to reach any of them.
@ -71,7 +97,10 @@ In your tests, you can use the `docker_compose` variable to query and command th
Also this fixture alters the way the python interpreter resolves domain names to IP addresses in the following ways: Also this fixture alters the way the python interpreter resolves domain names to IP addresses in the following ways:
Any domain name containing the substring `nginx-proxy` will resolve to the IP address of the container that was created from the `nginxproxy/nginx-proxy:test` image. So all the following domain names will resolve to the nginx-proxy container in tests: Any domain name containing the substring `nginx-proxy` will resolve to `127.0.0.1` if the tests are executed on a Darwin (macOS) system, otherwise the IP address of the container that was created from the `nginxproxy/nginx-proxy:test` image.
So, in tests, all the following domain names will resolve to either localhost or the nginx-proxy container's IP:
- `nginx-proxy` - `nginx-proxy`
- `nginx-proxy.com` - `nginx-proxy.com`
- `www.nginx-proxy.com` - `www.nginx-proxy.com`
@ -80,14 +109,16 @@ Any domain name containing the substring `nginx-proxy` will resolve to the IP ad
- `whatever.nginx-proxyooooooo` - `whatever.nginx-proxyooooooo`
- ... - ...
Any domain name ending with `XXX.container.docker` will resolve to the IP address of the XXX container. Any domain name ending with `XXX.container.docker` will resolve to `127.0.0.1` if the tests are executed on a Darwin (macOS) system, otherwise the IP address of the container named `XXX`.
So, on a non-Darwin system:
- `web1.container.docker` will resolve to the IP address of the `web1` container - `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

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

View file

@ -1,6 +1,7 @@
import contextlib import contextlib
import logging import logging
import os import os
import pathlib
import platform import platform
import re import re
import shlex import shlex
@ -8,24 +9,27 @@ import socket
import subprocess import subprocess
import time import time
from io import StringIO from io import StringIO
from typing import List from typing import Iterator, List, Optional
import backoff import backoff
import docker.errors import docker.errors
import pathlib
import pytest import pytest
import requests import requests
from _pytest.fixtures import FixtureRequest
from docker import DockerClient
from docker.models.containers import Container from docker.models.containers import Container
from docker.models.networks import Network from docker.models.networks import Network
from packaging.version import Version from packaging.version import Version
from requests import Response
from urllib3.util.connection import HAS_IPV6 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 = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt') CA_ROOT_CERTIFICATE = pathlib.Path(__file__).parent.joinpath("certs/ca-root.crt")
PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1" 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
@ -45,7 +49,7 @@ test_container = 'nginx-proxy-pytest'
@contextlib.contextmanager @contextlib.contextmanager
def ipv6(force_ipv6=True): def ipv6(force_ipv6: bool = True):
""" """
Meant to be used as a context manager to force IPv6 sockets: Meant to be used as a context manager to force IPv6 sockets:
@ -63,7 +67,7 @@ def ipv6(force_ipv6=True):
FORCE_CONTAINER_IPV6 = False FORCE_CONTAINER_IPV6 = False
class RequestsForDocker(object): class RequestsForDocker:
""" """
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 an HTTP response failed due to HTTP Error 404 or 502, retry a few times.
@ -71,22 +75,11 @@ class RequestsForDocker(object):
""" """
def __init__(self): def __init__(self):
self.session = requests.Session() self.session = requests.Session()
if os.path.isfile(CA_ROOT_CERTIFICATE): if CA_ROOT_CERTIFICATE.is_file():
self.session.verify = CA_ROOT_CERTIFICATE self.session.verify = CA_ROOT_CERTIFICATE.as_posix()
@staticmethod @staticmethod
def __backoff_predicate(expected_status_codes=None): def get_nginx_proxy_container() -> Container:
if expected_status_codes is not None:
if isinstance(expected_status_codes, int):
expected_status_codes = [expected_status_codes]
return lambda r: r.status_code not in expected_status_codes
else:
return lambda r: r.status_code not in (200, 301)
__backed_off_exceptions = (requests.exceptions.SSLError, requests.exceptions.ConnectionError)
@staticmethod
def get_nginx_proxy_containers() -> List[Container]:
""" """
Return list of containers Return list of containers
""" """
@ -95,66 +88,58 @@ class RequestsForDocker(object):
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 return nginx_proxy_containers.pop()
def get_conf(self): def get_conf(self) -> bytes:
""" """
Return the nginx config file Return the nginx config file
""" """
nginx_proxy_containers = self.get_nginx_proxy_containers() nginx_proxy_container = self.get_nginx_proxy_container()
return get_nginx_conf_from_container(nginx_proxy_containers[0]) return get_nginx_conf_from_container(nginx_proxy_container)
def get_ip(self) -> str: def get_ip(self) -> str:
""" """
Return the nginx container ip address Return the nginx container ip address
""" """
nginx_proxy_containers = self.get_nginx_proxy_containers() nginx_proxy_container = self.get_nginx_proxy_container()
return container_ip(nginx_proxy_containers[0]) return container_ip(nginx_proxy_container)
def get(self, *args, **kwargs): def get(self, *args, **kwargs) -> Response:
_expected_status_code = kwargs.pop('expected_status_code', None)
with ipv6(kwargs.pop('ipv6', False)): with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_exception(backoff.expo, self.__backed_off_exceptions, max_time=8) @backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
@backoff.on_predicate(backoff.expo, self.__backoff_predicate(_expected_status_code), max_time=8)
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 get_without_backoff(self, *args, **kwargs): def post(self, *args, **kwargs) -> Response:
with ipv6(kwargs.pop('ipv6', False)):
def _get(*_args, **_kwargs):
return self.session.get(*_args, **_kwargs)
return _get(*args, **kwargs)
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): def put(self, *args, **kwargs) -> Response:
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): def head(self, *args, **kwargs) -> Response:
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): def delete(self, *args, **kwargs) -> Response:
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): def options(self, *args, **kwargs) -> Response:
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):
@ -165,7 +150,7 @@ class RequestsForDocker(object):
return getattr(requests, name) return getattr(requests, name)
def container_ip(container: Container): def container_ip(container: Container) -> str:
""" """
return the IP address of a container. return the IP address of a container.
@ -194,7 +179,7 @@ def container_ip(container: Container):
return net_info[network_name]["IPAddress"] return net_info[network_name]["IPAddress"]
def container_ipv6(container): def container_ipv6(container: Container) -> str:
""" """
return the IPv6 address of a container. return the IPv6 address of a container.
""" """
@ -211,7 +196,7 @@ def container_ipv6(container):
return net_info[network_name]["GlobalIPv6Address"] return net_info[network_name]["GlobalIPv6Address"]
def nginx_proxy_dns_resolver(domain_name): def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]:
""" """
if "nginx-proxy" if found in host, return the ip address of the docker container 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.
@ -228,16 +213,16 @@ def nginx_proxy_dns_resolver(domain_name):
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.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
return return None
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): def docker_container_dns_resolver(domain_name: str) -> Optional[str]:
""" """
if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", return the ip address of the docker container if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker",
named XXX. return the ip address of the docker container named XXX.
:return: IP or None :return: IP or None
""" """
@ -247,7 +232,7 @@ def docker_container_dns_resolver(domain_name):
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 return None
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}")
@ -255,7 +240,7 @@ def docker_container_dns_resolver(domain_name):
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.warning(f"container named {container_name!r} not found while resolving {domain_name!r}")
return return None
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)
@ -267,7 +252,10 @@ 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 = {}
@ -282,6 +270,7 @@ def monkey_patch_urllib_dns_resolver():
# custom DNS resolvers # custom DNS resolvers
ip = None ip = None
# Docker Desktop can't route traffic directly to Linux containers.
if platform.system() == "Darwin": if platform.system() == "Darwin":
ip = "127.0.0.1" ip = "127.0.0.1"
if ip is None: if ip is None:
@ -306,7 +295,7 @@ def restore_urllib_dns_resolver(getaddrinfo_func):
socket.getaddrinfo = getaddrinfo_func socket.getaddrinfo = getaddrinfo_func
def get_nginx_conf_from_container(container): def get_nginx_conf_from_container(container: Container) -> bytes:
""" """
return the nginx /etc/nginx/conf.d/default.conf file content from a container return the nginx /etc/nginx/conf.d/default.conf file content from a container
""" """
@ -321,7 +310,10 @@ def get_nginx_conf_from_container(container):
return conffile.read() return conffile.read()
def __prepare_and_execute_compose_cmd(compose_files:List[str], project_name:str, cmd: str): def __prepare_and_execute_compose_cmd(compose_files: List[str], project_name: str, cmd: str):
"""
Prepare and execute the Docker Compose command with the provided compose files and project name.
"""
compose_cmd = StringIO() compose_cmd = StringIO()
compose_cmd.write(DOCKER_COMPOSE) compose_cmd.write(DOCKER_COMPOSE)
compose_cmd.write(f" --project-name {project_name}") compose_cmd.write(f" --project-name {project_name}")
@ -336,13 +328,19 @@ def __prepare_and_execute_compose_cmd(compose_files:List[str], project_name:str,
pytest.fail(f"Error while running '{compose_cmd.getvalue()}':\n{e.output}", pytrace=False) pytest.fail(f"Error while running '{compose_cmd.getvalue()}':\n{e.output}", pytrace=False)
def docker_compose_up(compose_files:List[str], project_name:str): def docker_compose_up(compose_files: List[str], project_name: str):
"""
Execute compose up --detach with the provided compose files and project name.
"""
if compose_files is None or len(compose_files) == 0: if compose_files is None or len(compose_files) == 0:
pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False) pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False)
__prepare_and_execute_compose_cmd(compose_files, project_name, cmd="up --detach") __prepare_and_execute_compose_cmd(compose_files, project_name, cmd="up --detach")
def docker_compose_down(compose_files:List[str], project_name:str): 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: if compose_files is None or len(compose_files) == 0:
pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False) 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") __prepare_and_execute_compose_cmd(compose_files, project_name, cmd="down --volumes")
@ -353,35 +351,33 @@ def wait_for_nginxproxy_to_be_ready():
If one (and only one) container started from image nginxproxy/nginx-proxy:test is found, If one (and only one) container started from image nginxproxy/nginx-proxy:test is found,
wait for its log to contain substring "Watching docker events" wait for its log to contain substring "Watching docker events"
""" """
timeout = time.time() + 10 containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"})
while True:
containers = docker_client.containers.list(
filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"}
)
if len(containers) != 1: if len(containers) != 1:
logging.warning(f"Found {len(containers)} nginxproxy/nginx-proxy:test containers running")
else:
for line in containers.pop().logs(stream=True):
if b"Generated '/etc/nginx/conf.d/default.conf'" in line:
return return
container = containers[0]
if time.time() > timeout: for line in container.logs(stream=True):
pytest.fail("nginxproxy/nginx-proxy:test container not ready after 10s", pytrace=False) if b"Watching docker events" in line:
time.sleep(1) logging.debug("nginx-proxy ready")
break
@pytest.fixture @pytest.fixture
def docker_compose_files(request) -> List[str]: def docker_compose_files(request: FixtureRequest) -> List[str]:
"""Fixture naming the docker compose file to consider. """Fixture returning the docker compose files to consider:
If a YAML file exists with the same name as the test module (with the `.py` extension replaced If a YAML file exists with the same name as the test module (with the `.py` extension
with `.yml`), use that. Otherwise, use `docker-compose.yml` in the same directory replaced with `.base.yml`, ie `test_foo.py`-> `test_foo.base.yml`) and in the same
as the test module. directory as the test module, use only that file.
Otherwise, merge the following files in this order:
- the `compose.base.yml` file in the parent `test` directory.
- if present in the same directory as the test module, the `compose.base.override.yml` file.
- the YAML file named after the current test module (ie `test_foo.py`-> `test_foo.yml`)
Tests can override this fixture to specify a custom location. Tests can override this fixture to specify a custom location.
""" """
compose_files:List[str] = [] compose_files: List[str] = []
test_module_path = pathlib.Path(request.module.__file__).parent test_module_path = pathlib.Path(request.module.__file__).parent
module_base_file = test_module_path.joinpath(f"{request.module.__name__}.base.yml") module_base_file = test_module_path.joinpath(f"{request.module.__name__}.base.yml")
@ -409,7 +405,7 @@ def docker_compose_files(request) -> List[str]:
return compose_files return compose_files
def connect_to_network(network:Network): def connect_to_network(network: Network) -> Optional[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
@ -437,7 +433,7 @@ def connect_to_network(network:Network):
return network return network
def disconnect_from_network(network:Network=None): def disconnect_from_network(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.
@ -491,7 +487,7 @@ class DockerComposer(contextlib.AbstractContextManager):
self._docker_compose_file = None self._docker_compose_file = None
self._project_name = None self._project_name = None
def compose(self, docker_compose_files:List[str], project_name:str): def compose(self, docker_compose_files: List[str], project_name: str):
if docker_compose_files == self._docker_compose_files and project_name == self._project_name: if docker_compose_files == self._docker_compose_files and project_name == self._project_name:
return return
self._down() self._down()
@ -500,6 +496,7 @@ class DockerComposer(contextlib.AbstractContextManager):
docker_compose_up(docker_compose_files, project_name) docker_compose_up(docker_compose_files, project_name)
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
self._docker_compose_files = docker_compose_files self._docker_compose_files = docker_compose_files
self._project_name = project_name self._project_name = project_name
@ -512,14 +509,14 @@ class DockerComposer(contextlib.AbstractContextManager):
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def docker_composer(): def docker_composer() -> Iterator[DockerComposer]:
with DockerComposer() as d: with DockerComposer() as d:
yield d yield d
@pytest.fixture @pytest.fixture
def ca_root_certificate(): def ca_root_certificate() -> str:
return CA_ROOT_CERTIFICATE return CA_ROOT_CERTIFICATE.as_posix()
@pytest.fixture @pytest.fixture
@ -530,23 +527,34 @@ def monkey_patched_dns():
@pytest.fixture @pytest.fixture
def docker_compose(request, monkeypatch, monkey_patched_dns, docker_composer, docker_compose_files): def docker_compose(
"""Ensures containers described in a docker compose file are started. request: FixtureRequest,
monkeypatch,
A custom docker compose file name can be specified by overriding the `docker_compose_file` monkey_patched_dns,
fixture. docker_composer,
docker_compose_files
Also, in the case where pytest is running from a docker container, this fixture makes sure ) -> Iterator[DockerClient]:
our container will be attached to all the docker networks.
""" """
Ensures containers necessary for the test module are started in a compose project,
and set the environment variable `PYTEST_MODULE_PATH` to the test module's parent folder.
A list of custom docker compose files path can be specified by overriding
the `docker_compose_file` fixture.
Also, in the case where pytest is running from a docker container, this fixture
makes sure our container will be attached to all the docker networks.
"""
pytest_module_path = pathlib.Path(request.module.__file__).parent
monkeypatch.setenv("PYTEST_MODULE_PATH", pytest_module_path.as_posix())
project_name = request.module.__name__ project_name = request.module.__name__
monkeypatch.setenv("PYTEST_MODULE_PATH", pathlib.Path(request.module.__file__).parent.as_posix())
docker_composer.compose(docker_compose_files, project_name) docker_composer.compose(docker_compose_files, project_name)
yield docker_client yield docker_client
@pytest.fixture() @pytest.fixture
def nginxproxy(): def nginxproxy() -> Iterator[RequestsForDocker]:
""" """
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:
@ -562,8 +570,8 @@ def nginxproxy():
yield RequestsForDocker() yield RequestsForDocker()
@pytest.fixture() @pytest.fixture
def acme_challenge_path(): def acme_challenge_path() -> str:
""" """
Provides fake Let's Encrypt ACME challenge path used in certain tests Provides fake Let's Encrypt ACME challenge path used in certain tests
""" """

View file

@ -1,12 +1,10 @@
def test_redirect_acme_challenge_location_disabled(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://web1.nginx-proxy.tld/{acme_challenge_path}", f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False, allow_redirects=False
expected_status_code=301
) )
assert r.status_code == 301 assert r.status_code == 301
def test_redirect_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://web2.nginx-proxy.tld/{acme_challenge_path}", f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
@ -14,16 +12,13 @@ def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, ac
) )
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://web3.nginx-proxy.tld/{acme_challenge_path}", f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False, allow_redirects=False
expected_status_code=404
) )
assert r.status_code == 404 assert r.status_code == 404
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path): 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}",

View file

@ -5,16 +5,13 @@ def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, ac
) )
assert r.status_code == 200 assert r.status_code == 200
def test_redirect_acme_challenge_location_disabled(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
expected_status_code=301
) )
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_noredirect_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://web3.nginx-proxy.tld/{acme_challenge_path}",
@ -22,11 +19,9 @@ def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy,
) )
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://web4.nginx-proxy.tld/{acme_challenge_path}",
allow_redirects=False, allow_redirects=False
expected_status_code=404
) )
assert r.status_code == 404 assert r.status_code == 404

View file

@ -5,11 +5,9 @@ def test_redirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acm
) )
assert r.status_code == 200 assert r.status_code == 200
def test_noredirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path): def test_noredirect_acme_challenge_location_legacy(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
expected_status_code=404
) )
assert r.status_code == 404 assert r.status_code == 404

View file

@ -2,6 +2,6 @@ import re
def test_custom_error_page(docker_compose, nginxproxy): def test_custom_error_page(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld", expected_status_code=503) r = nginxproxy.get("http://unknown.nginx-proxy.tld")
assert r.status_code == 503 assert r.status_code == 503
assert re.search(r"Damn, there's some maintenance in progress.", r.text) assert re.search(r"Damn, there's some maintenance in progress.", r.text)

View file

@ -1,3 +1,8 @@
def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_default_conf_applies_to_web1(docker_compose, nginxproxy): def test_custom_default_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.example/port") r = nginxproxy.get("http://web1.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -5,7 +10,6 @@ def test_custom_default_conf_applies_to_web1(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_default_conf_applies_to_web2(docker_compose, nginxproxy): def test_custom_default_conf_applies_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -20,9 +24,3 @@ def test_custom_default_conf_is_overriden_for_web3(docker_compose, nginxproxy):
assert r.text == "answer from port 83\n" assert r.text == "answer from port 83\n"
assert "X-test" in r.headers assert "X-test" in r.headers
assert "bar" == r.headers["X-test"] assert "bar" == r.headers["X-test"]
def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503
assert "X-test" not in r.headers

View file

@ -10,7 +10,7 @@ services:
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 +18,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 +26,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,8 @@
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy): def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.example/port") r = nginxproxy.get("http://web1.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -5,16 +10,9 @@ def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): def test_custom_conf_applies_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503
assert "X-test" not in r.headers

View file

@ -9,7 +9,7 @@ services:
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 +17,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,8 @@
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy): def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.example/port") r = nginxproxy.get("http://web1.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -5,7 +10,6 @@ def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_conf_applies_to_regex(docker_compose, nginxproxy): def test_custom_conf_applies_to_regex(docker_compose, nginxproxy):
r = nginxproxy.get("http://regex.foo.nginx-proxy.example/port") r = nginxproxy.get("http://regex.foo.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -13,19 +17,11 @@ def test_custom_conf_applies_to_regex(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "bar" == r.headers["X-test"] assert "bar" == r.headers["X-test"]
def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy): def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
assert "X-test" not in r.headers assert "X-test" not in r.headers
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy): def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy):
assert b"include /etc/nginx/vhost.d/web1.nginx-proxy.example_location;" in nginxproxy.get_conf() assert b"include /etc/nginx/vhost.d/web1.nginx-proxy.example_location;" in nginxproxy.get_conf()

View file

@ -10,7 +10,7 @@ services:
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 +18,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 +26,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,8 @@
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy): def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.example/port") r = nginxproxy.get("http://web1.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -5,7 +10,6 @@ def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_conf_applies_to_regex(docker_compose, nginxproxy): def test_custom_conf_applies_to_regex(docker_compose, nginxproxy):
r = nginxproxy.get("http://regex.foo.nginx-proxy.example/port") r = nginxproxy.get("http://regex.foo.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -13,15 +17,8 @@ def test_custom_conf_applies_to_regex(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "bar" == r.headers["X-test"] assert "bar" == r.headers["X-test"]
def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy): def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
assert "X-test" not in r.headers assert "X-test" not in r.headers
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503
assert "X-test" not in r.headers

View file

@ -10,7 +10,7 @@ services:
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 +18,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 +26,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,8 @@
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy): def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.example/port") r = nginxproxy.get("http://web1.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
@ -5,16 +10,9 @@ def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_conf_applies_to_web2(docker_compose, nginxproxy): def test_custom_conf_applies_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
assert "X-test" in r.headers assert "X-test" in r.headers
assert "f00" == r.headers["X-test"] assert "f00" == r.headers["X-test"]
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503
assert "X-test" not in r.headers

View file

@ -9,7 +9,7 @@ services:
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 +17,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

@ -14,36 +14,35 @@ def test_debug_endpoint_response_contains_expected_values(docker_compose, nginxp
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
try: try:
json_response = json.loads(r.text) jsonResponse = json.loads(r.text)
assert json_response["global"]["enable_debug_endpoint"] == "true"
assert json_response["vhost"]["enable_debug_endpoint"] == True
except ValueError as err: except ValueError as err:
pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False) pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False)
assert jsonResponse["global"]["enable_debug_endpoint"] == "true"
assert jsonResponse["vhost"]["enable_debug_endpoint"] == True
def test_debug_endpoint_paths_stripped_if_response_too_long(docker_compose, nginxproxy): def test_debug_endpoint_paths_stripped_if_response_too_long(docker_compose, nginxproxy):
r = nginxproxy.get("http://stripped.debug.nginx-proxy.example/nginx-proxy-debug") r = nginxproxy.get("http://stripped.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 200 assert r.status_code == 200
try: try:
json_response = json.loads(r.text) jsonResponse = json.loads(r.text)
if "paths" in json_response["vhost"]:
pytest.fail("Expected paths to be stripped from debug endpoint response", pytrace=False)
assert json_response[
"warning"] == "Virtual paths configuration for this hostname is too large and has been stripped from response."
except ValueError as err: except ValueError as err:
pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False) pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False)
if "paths" in jsonResponse["vhost"]:
pytest.fail("Expected paths to be stripped from debug endpoint response", pytrace=False)
assert jsonResponse["warning"] == "Virtual paths configuration for this hostname is too large and has been stripped from response."
def test_debug_endpoint_hostname_replaced_by_warning_if_regexp(docker_compose, nginxproxy): def test_debug_endpoint_hostname_replaced_by_warning_if_regexp(docker_compose, nginxproxy):
r = nginxproxy.get("http://regexp.foo.debug.nginx-proxy.example/nginx-proxy-debug") r = nginxproxy.get("http://regexp.foo.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 200 assert r.status_code == 200
try: try:
json_response = json.loads(r.text) jsonResponse = json.loads(r.text)
assert json_response["vhost"]["hostname"] == "Hostname is a regexp and unsafe to include in the debug response."
except ValueError as err: except ValueError as err:
pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False) pytest.fail("Failed to parse debug endpoint response as JSON: %s" % err, pytrace=False)
assert jsonResponse["vhost"]["hostname"] == "Hostname is a regexp and unsafe to include in the debug response."
def test_debug_endpoint_is_disabled_per_container(docker_compose, nginxproxy): def test_debug_endpoint_is_disabled_per_container(docker_compose, nginxproxy):
r = nginxproxy.get("http://disabled.debug.nginx-proxy.example/nginx-proxy-debug", expected_status_code=404) r = nginxproxy.get("http://disabled.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 404 assert r.status_code == 404

View file

@ -8,7 +8,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 +16,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 +45,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 +53,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

@ -4,9 +4,9 @@ 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", expected_status_code=404) r = nginxproxy.get("http://disabled1.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 404 assert r.status_code == 404
r = nginxproxy.get("http://disabled2.debug.nginx-proxy.example/nginx-proxy-debug", expected_status_code=404) r = nginxproxy.get("http://disabled2.debug.nginx-proxy.example/nginx-proxy-debug")
assert r.status_code == 404 assert r.status_code == 404
@ -19,8 +19,8 @@ def test_debug_endpoint_response_contains_expected_values(docker_compose, nginxp
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
try: try:
json_response = json.loads(r.text) jsonResponse = json.loads(r.text)
assert json_response["global"]["enable_debug_endpoint"] == "false"
assert json_response["vhost"]["enable_debug_endpoint"] == True
except ValueError as err: except ValueError as err:
pytest.fail("Failed to parse debug endpoint response as JSON:: %s" % err, pytrace=False) pytest.fail("Failed to parse debug endpoint response as JSON:: %s" % err, pytrace=False)
assert jsonResponse["global"]["enable_debug_endpoint"] == "false"
assert jsonResponse["vhost"]["enable_debug_endpoint"] == True

View file

@ -4,7 +4,7 @@ services:
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 +12,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 +21,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

@ -8,5 +8,5 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: web1.tld VIRTUAL_HOST: web1.tld

View file

@ -1,15 +1,13 @@
def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port")
assert r.status_code == 503
def test_forwards_to_web1(docker_compose, nginxproxy): def test_forwards_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port") r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"
def test_forwards_to_web2(docker_compose, nginxproxy): def test_forwards_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/port") r = nginxproxy.get("http://web2.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port", expected_status_code=503)
assert r.status_code == 503

View file

@ -10,7 +10,7 @@ services:
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 +18,5 @@ 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

View file

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

View file

@ -6,11 +6,11 @@ services:
nginx-proxy-nginx: nginx-proxy-nginx:
image: nginx image: nginx
container_name: nginx container_name: nginx
volumes:
- nginx_conf:/etc/nginx/conf.d:ro
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"
volumes:
- nginx_conf:/etc/nginx/conf.d:ro
nginx-proxy-dockergen: nginx-proxy-dockergen:
image: nginxproxy/docker-gen image: nginxproxy/docker-gen
@ -26,5 +26,5 @@ 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

View file

@ -2,26 +2,26 @@ import docker
import pytest import pytest
from packaging.version import Version from packaging.version import Version
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"), Version(raw_version) < Version("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})"
) )
@pytest.mark.skip("not ready")
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): def test_forwards_to_whoami(docker_compose, nginxproxy):
r = nginxproxy.get("http://whoami.nginx.container.docker/") r = nginxproxy.get("http://whoami.nginx.container.docker/")
assert r.status_code == 200 assert r.status_code == 200
whoami_container = docker_compose.containers.get("whoami") whoami_container = docker_compose.containers.get("whoami")
assert r.text == f"I'm {whoami_container.id[:12]}\n" assert r.text == f"I'm {whoami_container.id[:12]}\n"
@pytest.mark.skip("not ready")
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx.container.docker/", expected_status_code=503)
assert r.status_code == 503
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest
doctest.testmod() doctest.testmod()

View file

@ -1,29 +1,15 @@
def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy):
r = nginxproxy.get("http://nohttp-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False)
assert r.status_code == 503
def test_nohttp_missing_cert_enabled(docker_compose, nginxproxy): def test_nohttp_missing_cert_enabled(docker_compose, nginxproxy):
r = nginxproxy.get( r = nginxproxy.get("http://nohttp-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False)
"http://nohttp-missing-cert-enabled.nginx-proxy.tld/",
allow_redirects=False,
expected_status_code=301
)
assert r.status_code == 200 assert r.status_code == 200
def test_redirect_missing_cert_disabled(docker_compose, nginxproxy): def test_redirect_missing_cert_disabled(docker_compose, nginxproxy):
r = nginxproxy.get( r = nginxproxy.get("http://redirect-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False)
"http://redirect-missing-cert-disabled.nginx-proxy.tld/",
allow_redirects=False,
expected_status_code=301
)
assert r.status_code == 301 assert r.status_code == 301
def test_redirect_missing_cert_enabled(docker_compose, nginxproxy): def test_redirect_missing_cert_enabled(docker_compose, nginxproxy):
r = nginxproxy.get("http://redirect-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False) r = nginxproxy.get("http://redirect-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False)
assert r.status_code == 200 assert r.status_code == 200
def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy):
r = nginxproxy.get(
"http://nohttp-missing-cert-disabled.nginx-proxy.tld/",
allow_redirects=False,
expected_status_code=503
)
assert r.status_code == 503

View file

@ -1,14 +1,13 @@
""" """
Test that nginx-proxy detects new containers Test that nginx-proxy detects new containers
""" """
import time
from time import sleep from time import sleep
import pytest 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.
@ -31,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.
@ -57,8 +56,7 @@ def web2(docker_compose):
pass pass
def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy): def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy):
time.sleep(3) r = nginxproxy.get("http://nginx-proxy/")
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503 assert r.status_code == 503
@ -69,18 +67,18 @@ def test_new_container_is_detected_vhost(web1, nginxproxy):
web1.remove(force=True) web1.remove(force=True)
sleep(2) sleep(2)
r = nginxproxy.get("http://web1.nginx-proxy/port", expected_status_code=503) r = nginxproxy.get("http://web1.nginx-proxy/port")
assert r.status_code == 503 assert r.status_code == 503
def test_new_container_is_detected_vpath(web2, nginxproxy): def test_new_container_is_detected_vpath(web2, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/web2/port") r = nginxproxy.get("http://nginx-proxy/web2/port")
assert r.status_code == 200 assert r.status_code == 200
assert "answer from port 82\n" == r.text assert "answer from port 82\n" == r.text
r = nginxproxy.get("http://nginx-proxy/port", expected_status_code=[404, 503]) r = nginxproxy.get("http://nginx-proxy/port")
assert r.status_code in [404, 503] assert r.status_code in [404, 503]
web2.remove(force=True) web2.remove(force=True)
sleep(2) sleep(2)
r = nginxproxy.get("http://nginx-proxy/web2/port", expected_status_code=503) r = nginxproxy.get("http://nginx-proxy/web2/port")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,6 +1,3 @@
include:
- ../compose.base.yml
networks: networks:
default: default:
name: test_events-net name: test_events-net

View file

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

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro
http-only: http-only:
image: web image: web

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./nodefault.certs:/etc/nginx/certs:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/nodefault.certs:/etc/nginx/certs:ro
https-and-http: https-and-http:
image: web image: web

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./withdefault.certs:/etc/nginx/certs:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
environment: environment:
HTTPS_METHOD: redirect HTTPS_METHOD: redirect

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./withdefault.certs:/etc/nginx/certs:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
environment: environment:
HTTPS_METHOD: nohttp HTTPS_METHOD: nohttp

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./withdefault.certs:/etc/nginx/certs:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
environment: environment:
HTTPS_METHOD: nohttp HTTPS_METHOD: nohttp

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./withdefault.certs:/etc/nginx/certs:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
environment: environment:
TRUST_DEFAULT_CERT: "false" TRUST_DEFAULT_CERT: "false"

View file

@ -2,7 +2,7 @@ services:
nginx-proxy: nginx-proxy:
volumes: volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro - /var/run/docker.sock:/tmp/docker.sock:ro
- ./withdefault.certs:/etc/nginx/certs:ro - ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
https-and-http: https-and-http:
image: web image: web

View file

@ -1,36 +1,35 @@
import pathlib
import re import re
from typing import List from typing import List, Callable
import backoff import backoff
import pathlib
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 docker_compose_files(compose_file) -> List[str]:
data_dir = pathlib.Path(__file__).parent.joinpath("test_fallback.data") data_dir = pathlib.Path(__file__).parent.joinpath("test_fallback.data")
yield [ return [
data_dir.joinpath("compose.base.yml"), data_dir.joinpath("compose.base.yml"),
data_dir.joinpath(compose_file).as_posix() data_dir.joinpath(compose_file).as_posix()
] ]
@pytest.fixture @pytest.fixture
def get(docker_compose, nginxproxy, want_err_re): def get(docker_compose, nginxproxy, want_err_re: re.Pattern[str]) -> Callable[[str], Response]:
@backoff.on_exception( @backoff.on_exception(
backoff.constant, backoff.constant,
requests.exceptions.SSLError, requests.exceptions.SSLError,
giveup=lambda e: want_err_re and want_err_re.search(str(e)), giveup=lambda e: want_err_re and bool(want_err_re.search(str(e))),
interval=.3, interval=.3,
max_tries=30, max_tries=30,
jitter=None) jitter=None)
def _get(url, want_code=None): def _get(url) -> Response:
if want_code is None: return nginxproxy.get(url, allow_redirects=False)
return nginxproxy.get_without_backoff(url, allow_redirects=False)
else: return _get
return nginxproxy.get(url, allow_redirects=False, expected_status_code=want_code)
yield _get
INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME") INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
@ -111,7 +110,7 @@ INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
]) ])
def test_fallback(get, compose_file, url, want_code, want_err_re): def test_fallback(get, compose_file, url, want_code, want_err_re):
if want_err_re is None: if want_err_re is None:
r = get(url, want_code) r = get(url)
assert r.status_code == want_code assert r.status_code == want_code
else: else:
with pytest.raises(requests.exceptions.SSLError, match=want_err_re): with pytest.raises(requests.exceptions.SSLError, match=want_err_re):

View file

@ -11,7 +11,6 @@ def test_X_Forwarded_For_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-For:" in r.text assert "X-Forwarded-For:" in r.text
def test_X_Forwarded_For_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_For_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-For': '1.2.3.4'}) r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-For': '1.2.3.4'})
assert r.status_code == 200 assert r.status_code == 200
@ -25,7 +24,6 @@ def test_X_Forwarded_Proto_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Proto: http" in r.text assert "X-Forwarded-Proto: http" in r.text
def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Proto': 'f00'}) r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Proto': 'f00'})
assert r.status_code == 200 assert r.status_code == 200
@ -39,7 +37,6 @@ def test_X_Forwarded_Host_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text
def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'}) r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'})
assert r.status_code == 200 assert r.status_code == 200
@ -53,7 +50,6 @@ def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Port: 80\n" in r.text assert "X-Forwarded-Port: 80\n" in r.text
def test_X_Forwarded_Port_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_Port_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Port': '1234'}) r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Port': '1234'})
assert r.status_code == 200 assert r.status_code == 200
@ -67,7 +63,6 @@ def test_X_Forwarded_Ssl_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Ssl: off\n" in r.text assert "X-Forwarded-Ssl: off\n" in r.text
def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy): def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Ssl': 'f00'}) r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Ssl': 'f00'})
assert r.status_code == 200 assert r.status_code == 200
@ -81,13 +76,11 @@ def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Real-IP: " in r.text assert "X-Real-IP: " in r.text
def test_Host_is_passed_on(docker_compose, nginxproxy): def test_Host_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers") r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200 assert r.status_code == 200
assert "Host: web.nginx-proxy.tld" in r.text assert "Host: web.nginx-proxy.tld" in r.text
def test_httpoxy_safe(docker_compose, nginxproxy): def test_httpoxy_safe(docker_compose, nginxproxy):
""" """
See https://httpoxy.org/ See https://httpoxy.org/
@ -100,7 +93,7 @@ def test_httpoxy_safe(docker_compose, nginxproxy):
def test_no_host_server_tokens_off(docker_compose, nginxproxy): def test_no_host_server_tokens_off(docker_compose, nginxproxy):
ip = nginxproxy.get_ip() ip = nginxproxy.get_ip()
r = nginxproxy.get(f"http://{ip}/headers", expected_status_code=503) r = nginxproxy.get(f"http://{ip}/headers")
assert r.status_code == 503 assert r.status_code == 503
assert r.headers["Server"] == "nginx" assert r.headers["Server"] == "nginx"

View file

@ -4,7 +4,7 @@ services:
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 +12,6 @@ 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"

View file

@ -14,7 +14,6 @@ def test_X_Forwarded_For_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-For:" in r.text assert "X-Forwarded-For:" in r.text
def test_X_Forwarded_For_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_For_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-For': '1.2.3.4'}) r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-For': '1.2.3.4'})
assert r.status_code == 200 assert r.status_code == 200
@ -28,7 +27,6 @@ def test_X_Forwarded_Proto_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Proto: https" in r.text assert "X-Forwarded-Proto: https" in r.text
def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_Proto_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Proto': 'f00'}) r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Proto': 'f00'})
assert r.status_code == 200 assert r.status_code == 200
@ -42,7 +40,6 @@ def test_X_Forwarded_Host_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text assert "X-Forwarded-Host: web.nginx-proxy.tld\n" in r.text
def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_Host_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'}) r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Host': 'example.com'})
assert r.status_code == 200 assert r.status_code == 200
@ -56,7 +53,6 @@ def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Port: 443\n" in r.text assert "X-Forwarded-Port: 443\n" in r.text
def test_X_Forwarded_Port_is_passed_on(docker_compose, nginxproxy): def test_X_Forwarded_Port_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Port': '1234'}) r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Port': '1234'})
assert r.status_code == 200 assert r.status_code == 200
@ -70,7 +66,6 @@ def test_X_Forwarded_Ssl_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Forwarded-Ssl: on\n" in r.text assert "X-Forwarded-Ssl: on\n" in r.text
def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy): def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Ssl': 'f00'}) r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Ssl': 'f00'})
assert r.status_code == 200 assert r.status_code == 200
@ -84,13 +79,11 @@ def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert "X-Real-IP: " in r.text assert "X-Real-IP: " in r.text
def test_Host_is_passed_on(docker_compose, nginxproxy): def test_Host_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers") r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200 assert r.status_code == 200
assert "Host: web.nginx-proxy.tld" in r.text assert "Host: web.nginx-proxy.tld" in r.text
def test_httpoxy_safe(docker_compose, nginxproxy): def test_httpoxy_safe(docker_compose, nginxproxy):
""" """
See https://httpoxy.org/ See https://httpoxy.org/
@ -104,7 +97,7 @@ def test_httpoxy_safe(docker_compose, nginxproxy):
@pytest.mark.filterwarnings('ignore::urllib3.exceptions.InsecureRequestWarning') @pytest.mark.filterwarnings('ignore::urllib3.exceptions.InsecureRequestWarning')
def test_no_host_server_tokens_off(docker_compose, nginxproxy): def test_no_host_server_tokens_off(docker_compose, nginxproxy):
ip = nginxproxy.get_ip() ip = nginxproxy.get_ip()
r = nginxproxy.get(f"https://{ip}/headers", verify=False, expected_status_code=503) r = nginxproxy.get(f"https://{ip}/headers", verify=False)
assert r.status_code == 503 assert r.status_code == 503
assert r.headers["Server"] == "nginx" assert r.headers["Server"] == "nginx"

View file

@ -9,7 +9,7 @@ services:
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 +17,6 @@ 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"

View file

@ -24,5 +24,3 @@ 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

View file

@ -1,16 +1,12 @@
import platform # Note: on Docker Desktop, host networking must be manually enabled.
# See https://docs.docker.com/engine/network/drivers/host/
import pytest
@pytest.mark.xfail(platform.system() == "Darwin", reason="Host networking only work on Linux")
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")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 8080\n" assert r.text == "answer from port 8080\n"
@pytest.mark.xfail(platform.system() == "Darwin", reason="Host networking only work on Linux")
def test_forwards_to_host_network_container_2(docker_compose, nginxproxy): def test_forwards_to_host_network_container_2(docker_compose, nginxproxy):
r = nginxproxy.get("http://host-network-2.nginx-proxy.tld:8888/port") r = nginxproxy.get("http://host-network-2.nginx-proxy.tld:8888/port")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -1,9 +1,5 @@
services: services:
nginx-proxy: nginx-proxy:
image: nginxproxy/nginx-proxy:test
container_name: nginx-proxy
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
environment: environment:
HTTP_PORT: 8888 HTTP_PORT: 8888
network_mode: host network_mode: host

View file

@ -1,5 +1,5 @@
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", expected_status_code=401) r = nginxproxy.get("http://regex.htpasswd.nginx-proxy.example/port")
assert r.status_code == 401 assert r.status_code == 401
assert "WWW-Authenticate" in r.headers assert "WWW-Authenticate" in r.headers
assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted access"' assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted access"'

View file

@ -4,5 +4,5 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$ VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$

View file

@ -1,5 +1,5 @@
def test_htpasswd_virtual_host_is_restricted(docker_compose, nginxproxy): def test_htpasswd_virtual_host_is_restricted(docker_compose, nginxproxy):
r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/port", expected_status_code=401) r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/port")
assert r.status_code == 401 assert r.status_code == 401
assert "WWW-Authenticate" in r.headers assert "WWW-Authenticate" in r.headers
assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted htpasswd.nginx-proxy.tld"' assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted htpasswd.nginx-proxy.tld"'

View file

@ -4,5 +4,5 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: htpasswd.nginx-proxy.tld VIRTUAL_HOST: htpasswd.nginx-proxy.tld

View file

@ -1,10 +1,9 @@
def test_htpasswd_virtual_path_is_restricted(docker_compose, nginxproxy): def test_htpasswd_virtual_path_is_restricted(docker_compose, nginxproxy):
r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/foo/port", expected_status_code=401) r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/foo/port")
assert r.status_code == 401 assert r.status_code == 401
assert "WWW-Authenticate" in r.headers assert "WWW-Authenticate" in r.headers
assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted htpasswd.nginx-proxy.tld/foo/"' assert r.headers["WWW-Authenticate"] == 'Basic realm="Restricted htpasswd.nginx-proxy.tld/foo/"'
def test_htpasswd_virtual_path_basic_auth(docker_compose, nginxproxy): def test_htpasswd_virtual_path_basic_auth(docker_compose, nginxproxy):
r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/foo/port", auth=("vpath", "password")) r = nginxproxy.get("http://htpasswd.nginx-proxy.tld/foo/port", auth=("vpath", "password"))
assert r.status_code == 200 assert r.status_code == 200

View file

@ -4,7 +4,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: htpasswd.nginx-proxy.tld VIRTUAL_HOST: htpasswd.nginx-proxy.tld
VIRTUAL_PATH: /foo/ VIRTUAL_PATH: /foo/
VIRTUAL_DEST: / VIRTUAL_DEST: /

View file

@ -7,7 +7,6 @@ def test_web1_http_custom_port(docker_compose, nginxproxy, subdomain):
assert r.status_code == 200 assert r.status_code == 200
assert "answer from port 81\n" in r.text assert "answer from port 81\n" in r.text
def test_nonstandardport_Host_header(docker_compose, nginxproxy): def test_nonstandardport_Host_header(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld:8080/headers") r = nginxproxy.get("http://web.nginx-proxy.tld:8080/headers")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -1,9 +1,9 @@
services: services:
nginx-proxy: nginx-proxy:
ports:
- "8080:8080"
environment: environment:
HTTP_PORT: 8080 HTTP_PORT: 8080
ports:
- "8080:8080"
web1: web1:
image: web image: web

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: http2-global-disabled.nginx-proxy.tld VIRTUAL_HOST: http2-global-disabled.nginx-proxy.tld

View file

@ -10,7 +10,6 @@ def test_http3_global_disabled_ALTSVC_header(docker_compose, nginxproxy):
assert "Host: http3-global-disabled.nginx-proxy.tld" in r.text assert "Host: http3-global-disabled.nginx-proxy.tld" in r.text
assert not "alt-svc" in r.headers assert not "alt-svc" in r.headers
def test_http3_global_disabled_config(docker_compose, nginxproxy): def test_http3_global_disabled_config(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
r = nginxproxy.get("http://http3-global-disabled.nginx-proxy.tld") r = nginxproxy.get("http://http3-global-disabled.nginx-proxy.tld")

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: http3-global-disabled.nginx-proxy.tld VIRTUAL_HOST: http3-global-disabled.nginx-proxy.tld

View file

@ -11,12 +11,11 @@ def test_http3_global_enabled_ALTSVC_header(docker_compose, nginxproxy):
assert "alt-svc" in r.headers assert "alt-svc" in r.headers
assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;' assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;'
def test_http3_global_enabled_config(docker_compose, nginxproxy): def test_http3_global_enabled_config(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
r = nginxproxy.get("http://http3-global-enabled.nginx-proxy.tld") r = nginxproxy.get("http://http3-global-enabled.nginx-proxy.tld")
assert r.status_code == 200 assert r.status_code == 200
assert re.search(r"listen 443 quic reuseport;", conf) assert re.search(r"listen 443 quic reuseport\;", conf)
assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*listen 443 quic", conf) assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*listen 443 quic", conf)
assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*http3 on;", conf) assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*http3 on\;", conf)
assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf) assert re.search(r"(?s)http3-global-enabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf)

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: http3-global-enabled.nginx-proxy.tld VIRTUAL_HOST: http3-global-enabled.nginx-proxy.tld

View file

@ -11,32 +11,28 @@ def test_http3_vhost_enabled_ALTSVC_header(docker_compose, nginxproxy):
assert "alt-svc" in r.headers assert "alt-svc" in r.headers
assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;' assert r.headers["alt-svc"] == 'h3=":443"; ma=86400;'
def test_http3_vhost_enabled_config(docker_compose, nginxproxy): def test_http3_vhost_enabled_config(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
r = nginxproxy.get("http://http3-vhost-enabled.nginx-proxy.tld") r = nginxproxy.get("http://http3-vhost-enabled.nginx-proxy.tld")
assert r.status_code == 200 assert r.status_code == 200
assert re.search(r"listen 443 quic reuseport;", conf) assert re.search(r"listen 443 quic reuseport\;", conf)
assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*listen 443 quic", conf) assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*listen 443 quic", conf)
assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*http3 on;", conf) assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*http3 on\;", conf)
assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf) assert re.search(r"(?s)http3-vhost-enabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'", conf)
def test_http3_vhost_disabled_ALTSVC_header(docker_compose, nginxproxy): def test_http3_vhost_disabled_ALTSVC_header(docker_compose, nginxproxy):
r = nginxproxy.get("http://http3-vhost-disabled.nginx-proxy.tld/headers") r = nginxproxy.get("http://http3-vhost-disabled.nginx-proxy.tld/headers")
assert r.status_code == 200 assert r.status_code == 200
assert "Host: http3-vhost-disabled.nginx-proxy.tld" in r.text assert "Host: http3-vhost-disabled.nginx-proxy.tld" in r.text
assert not "alt-svc" in r.headers assert not "alt-svc" in r.headers
def test_http3_vhost_disabled_config(docker_compose, nginxproxy): def test_http3_vhost_disabled_config(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
r = nginxproxy.get("http://http3-vhost-disabled.nginx-proxy.tld") r = nginxproxy.get("http://http3-vhost-disabled.nginx-proxy.tld")
assert r.status_code == 200 assert r.status_code == 200
assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld.*listen 443 quic.*# http3-vhost-enabled\.nginx-proxy\.tld", conf) assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld.*listen 443 quic.*\# http3-vhost-enabled\.nginx-proxy\.tld", conf)
assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld.*http3 on.*# http3-vhost-enabled\.nginx-proxy\.tld", conf) assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld.*http3 on.*\# http3-vhost-enabled\.nginx-proxy\.tld", conf)
assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'.*# http3-vhost-enabled\.nginx-proxy\.tld", conf) assert not re.search(r"(?s)http3-vhost-disabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'.*\# http3-vhost-enabled\.nginx-proxy\.tld", conf)
def test_http3_vhost_disabledbydefault_ALTSVC_header(docker_compose, nginxproxy): def test_http3_vhost_disabledbydefault_ALTSVC_header(docker_compose, nginxproxy):
r = nginxproxy.get("http://http3-vhost-default-disabled.nginx-proxy.tld/headers") r = nginxproxy.get("http://http3-vhost-default-disabled.nginx-proxy.tld/headers")
@ -44,11 +40,10 @@ def test_http3_vhost_disabledbydefault_ALTSVC_header(docker_compose, nginxproxy)
assert "Host: http3-vhost-default-disabled.nginx-proxy.tld" in r.text assert "Host: http3-vhost-default-disabled.nginx-proxy.tld" in r.text
assert not "alt-svc" in r.headers assert not "alt-svc" in r.headers
def test_http3_vhost_disabledbydefault_config(docker_compose, nginxproxy): def test_http3_vhost_disabledbydefault_config(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
r = nginxproxy.get("http://http3-vhost-default-disabled.nginx-proxy.tld") r = nginxproxy.get("http://http3-vhost-default-disabled.nginx-proxy.tld")
assert r.status_code == 200 assert r.status_code == 200
assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld.*listen 443 quic.*# http3-vhost-disabled\.nginx-proxy\.tld", conf) assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld.*listen 443 quic.*\# http3-vhost-disabled\.nginx-proxy\.tld", conf)
assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld.*http3 on.*# http3-vhost-disabled\.nginx-proxy\.tld", conf) assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld.*http3 on.*\# http3-vhost-disabled\.nginx-proxy\.tld", conf)
assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'.*# http3-vhost-disabled\.nginx-proxy\.tld", conf) assert not re.search(r"(?s)http3-vhost-default-disabled\.nginx-proxy\.tld;.*add_header alt-svc \'h3=\":443\"; ma=86400;\'.*\# http3-vhost-disabled\.nginx-proxy\.tld", conf)

View file

@ -8,7 +8,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: http3-vhost-enabled.nginx-proxy.tld VIRTUAL_HOST: http3-vhost-enabled.nginx-proxy.tld
labels: labels:
com.github.nginx-proxy.nginx-proxy.http3.enable: "true" com.github.nginx-proxy.nginx-proxy.http3.enable: "true"
@ -18,7 +18,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: http3-vhost-disabled.nginx-proxy.tld VIRTUAL_HOST: http3-vhost-disabled.nginx-proxy.tld
labels: labels:
com.github.nginx-proxy.nginx-proxy.http3.enable: "false" com.github.nginx-proxy.nginx-proxy.http3.enable: "false"
@ -28,5 +28,5 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: http3-vhost-default-disabled.nginx-proxy.tld VIRTUAL_HOST: http3-vhost-default-disabled.nginx-proxy.tld

View file

@ -5,7 +5,6 @@ def test_network_web1(docker_compose, nginxproxy):
assert "X-network" in r.headers assert "X-network" in r.headers
assert "internal" == r.headers["X-network"] assert "internal" == r.headers["X-network"]
def test_network_web2(docker_compose, nginxproxy): def test_network_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -4,7 +4,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
NETWORK_ACCESS: internal NETWORK_ACCESS: internal
@ -13,5 +13,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

@ -5,7 +5,6 @@ def test_network_web1(docker_compose, nginxproxy):
assert "X-network" in r.headers assert "X-network" in r.headers
assert "internal" == r.headers["X-network"] assert "internal" == r.headers["X-network"]
def test_network_web2(docker_compose, nginxproxy): def test_network_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy.example/web2/port") r = nginxproxy.get("http://nginx-proxy.example/web2/port")
assert r.status_code == 200 assert r.status_code == 200

View file

@ -4,7 +4,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: nginx-proxy.example VIRTUAL_HOST: nginx-proxy.example
VIRTUAL_PATH: /web1/ VIRTUAL_PATH: /web1/
VIRTUAL_DEST: / VIRTUAL_DEST: /
@ -15,7 +15,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: 82 WEB_PORTS: "82"
VIRTUAL_HOST: nginx-proxy.example VIRTUAL_HOST: nginx-proxy.example
VIRTUAL_PATH: /web2/ VIRTUAL_PATH: /web2/
VIRTUAL_DEST: / VIRTUAL_DEST: /

View file

@ -24,7 +24,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: ipv4only.nginx-proxy.tld VIRTUAL_HOST: ipv4only.nginx-proxy.tld
networks: networks:
ipv4net: ipv4net:

View file

@ -26,7 +26,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: ipv4only.nginx-proxy.tld VIRTUAL_HOST: ipv4only.nginx-proxy.tld
networks: networks:
ipv4net: ipv4net:

View file

@ -1,3 +1,18 @@
import platform
import pytest
pytestmark = pytest.mark.skipif(
platform.system() == "Darwin",
reason="Those tests rely entirely on being able to directly contact the container's IP"
)
def test_unknown_virtual_host_ipv4(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port")
assert r.status_code == 503
def test_forwards_to_web1_ipv4(docker_compose, nginxproxy): def test_forwards_to_web1_ipv4(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port") r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
@ -10,8 +25,8 @@ def test_forwards_to_web2_ipv4(docker_compose, nginxproxy):
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_unknown_virtual_host_ipv4(docker_compose, nginxproxy): def test_unknown_virtual_host_ipv6(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port", expected_status_code=503) r = nginxproxy.get("http://nginx-proxy/port", ipv6=True)
assert r.status_code == 503 assert r.status_code == 503
@ -25,8 +40,3 @@ def test_forwards_to_web2_ipv6(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/port", ipv6=True) r = nginxproxy.get("http://web2.nginx-proxy.tld/port", ipv6=True)
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_unknown_virtual_host_ipv6(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port", ipv6=True, expected_status_code=503)
assert r.status_code == 503

View file

@ -17,7 +17,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: web1.nginx-proxy.tld VIRTUAL_HOST: web1.nginx-proxy.tld
networks: networks:
- net1 - net1
@ -27,7 +27,7 @@ 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
networks: networks:
- net1 - net1

View file

@ -6,7 +6,6 @@ def test_keepalive_disabled(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert re.search(fr'(?m)^(?i:Connection): close$', r.text) assert re.search(fr'(?m)^(?i:Connection): close$', r.text)
def test_keepalive_disabled_other_headers_ok(docker_compose, nginxproxy): def test_keepalive_disabled_other_headers_ok(docker_compose, nginxproxy):
"""Make sure the other proxy_set_header headers are still set. """Make sure the other proxy_set_header headers are still set.
@ -20,7 +19,6 @@ def test_keepalive_disabled_other_headers_ok(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert re.search(fr'(?m)^(?i:X-Real-IP): ', r.text) assert re.search(fr'(?m)^(?i:X-Real-IP): ', r.text)
def test_keepalive_enabled(docker_compose, nginxproxy): def test_keepalive_enabled(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
assert re.search(r"keepalive 64\;", conf) assert re.search(r"keepalive 64\;", conf)
@ -29,7 +27,6 @@ def test_keepalive_enabled(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert not re.search(fr'(?m)^(?i:Connection):', r.text) assert not re.search(fr'(?m)^(?i:Connection):', r.text)
def test_keepalive_auto_enabled(docker_compose, nginxproxy): def test_keepalive_auto_enabled(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
assert re.search(r"keepalive 8\;", conf) assert re.search(r"keepalive 8\;", conf)
@ -38,7 +35,6 @@ def test_keepalive_auto_enabled(docker_compose, nginxproxy):
assert r.status_code == 200 assert r.status_code == 200
assert not re.search(fr'(?m)^(?i:Connection):', r.text) assert not re.search(fr'(?m)^(?i:Connection):', r.text)
def test_keepalive_enabled_other_headers_ok(docker_compose, nginxproxy): def test_keepalive_enabled_other_headers_ok(docker_compose, nginxproxy):
"""See the docstring for the disabled case above.""" """See the docstring for the disabled case above."""
r = nginxproxy.get("http://keepalive-enabled.nginx-proxy.test/headers") r = nginxproxy.get("http://keepalive-enabled.nginx-proxy.test/headers")

View file

@ -8,7 +8,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: keepalive-disabled.nginx-proxy.test VIRTUAL_HOST: keepalive-disabled.nginx-proxy.test
labels: labels:
com.github.nginx-proxy.nginx-proxy.keepalive: "disabled" com.github.nginx-proxy.nginx-proxy.keepalive: "disabled"
@ -18,7 +18,7 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: keepalive-enabled.nginx-proxy.test VIRTUAL_HOST: keepalive-enabled.nginx-proxy.test
labels: labels:
com.github.nginx-proxy.nginx-proxy.keepalive: "64" com.github.nginx-proxy.nginx-proxy.keepalive: "64"
@ -31,6 +31,6 @@ services:
expose: expose:
- "80" - "80"
environment: environment:
WEB_PORTS: 80 WEB_PORTS: "80"
VIRTUAL_HOST: keepalive-auto.nginx-proxy.test VIRTUAL_HOST: keepalive-auto.nginx-proxy.test

View file

@ -1,18 +1,15 @@
import re import re
import time
def test_loadbalance_hash(docker_compose, nginxproxy): def test_loadbalance_hash(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
r1 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld") r1 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld")
r2 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld") r2 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld")
assert re.search(r"hash \$remote_addr;", conf) assert re.search(r"hash \$remote_addr\;", conf)
assert r1.status_code == 200 assert r1.status_code == 200
assert r2.text == r1.text assert r2.text == r1.text
def test_loadbalance_roundrobin(docker_compose, nginxproxy): def test_loadbalance_roundrobin(docker_compose, nginxproxy):
time.sleep(3)
r1 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld") r1 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld")
r2 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld") r2 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld")
assert r1.status_code == 200 assert r1.status_code == 200

View file

@ -4,7 +4,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: loadbalance-enabled.nginx-proxy.tld VIRTUAL_HOST: loadbalance-enabled.nginx-proxy.tld
labels: labels:
com.github.nginx-proxy.nginx-proxy.loadbalance: "hash $$remote_addr;" com.github.nginx-proxy.nginx-proxy.loadbalance: "hash $$remote_addr;"
@ -16,7 +16,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: 82 WEB_PORTS: "82"
VIRTUAL_HOST: loadbalance-disabled.nginx-proxy.tld VIRTUAL_HOST: loadbalance-disabled.nginx-proxy.tld
deploy: deploy:
replicas: 2 replicas: 2

View file

@ -1,45 +1,39 @@
def test_explicit_root_nohash(docker_compose, nginxproxy): def test_explicit_root_nohash(docker_compose, nginxproxy):
r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/port", expected_status_code=418) r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/port")
assert r.status_code == 418 assert r.status_code == 418
r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/foo/port") r = nginxproxy.get("http://explicit-root-nohash.nginx-proxy.test/foo/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_explicit_root_hash(docker_compose, nginxproxy): def test_explicit_root_hash(docker_compose, nginxproxy):
r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/port", expected_status_code=418) r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/port")
assert r.status_code == 418 assert r.status_code == 418
r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/foo/port") r = nginxproxy.get("http://explicit-root-hash.nginx-proxy.test/foo/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_explicit_root_hash_and_nohash(docker_compose, nginxproxy): def test_explicit_root_hash_and_nohash(docker_compose, nginxproxy):
r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/port", expected_status_code=418) r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/port")
assert r.status_code == 418 assert r.status_code == 418
r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/foo/port") r = nginxproxy.get("http://explicit-root-hash-and-nohash.nginx-proxy.test/foo/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_explicit_nonroot(docker_compose, nginxproxy): def test_explicit_nonroot(docker_compose, nginxproxy):
r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/port") r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"
r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/foo/port", expected_status_code=418) r = nginxproxy.get("http://explicit-nonroot.nginx-proxy.test/foo/port")
assert r.status_code == 418 assert r.status_code == 418
def test_implicit_root_nohash(docker_compose, nginxproxy): def test_implicit_root_nohash(docker_compose, nginxproxy):
r = nginxproxy.get("http://implicit-root-nohash.nginx-proxy.test/port", expected_status_code=418) r = nginxproxy.get("http://implicit-root-nohash.nginx-proxy.test/port")
assert r.status_code == 418 assert r.status_code == 418
def test_implicit_root_hash(docker_compose, nginxproxy): def test_implicit_root_hash(docker_compose, nginxproxy):
r = nginxproxy.get("http://implicit-root-hash.nginx-proxy.test/port", expected_status_code=418) r = nginxproxy.get("http://implicit-root-hash.nginx-proxy.test/port")
assert r.status_code == 418 assert r.status_code == 418
def test_implicit_root_hash_and_nohash(docker_compose, nginxproxy): def test_implicit_root_hash_and_nohash(docker_compose, nginxproxy):
r = nginxproxy.get("http://implicit-root-hash-and-nohash.nginx-proxy.test/port", expected_status_code=418) r = nginxproxy.get("http://implicit-root-hash-and-nohash.nginx-proxy.test/port")
assert r.status_code == 418 assert r.status_code == 418

View file

@ -1,8 +1,4 @@
import time
def test_log_disabled(docker_compose, nginxproxy): def test_log_disabled(docker_compose, nginxproxy):
time.sleep(3)
r = nginxproxy.get("http://nginx-proxy.test/port") r = nginxproxy.get("http://nginx-proxy.test/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: nginx-proxy.test VIRTUAL_HOST: nginx-proxy.test

View file

@ -1,8 +1,4 @@
import time
def test_log_format(docker_compose, nginxproxy): def test_log_format(docker_compose, nginxproxy):
time.sleep(3)
r = nginxproxy.get("http://nginx-proxy.test/port") r = nginxproxy.get("http://nginx-proxy.test/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: nginx-proxy.test VIRTUAL_HOST: nginx-proxy.test

View file

@ -1,6 +1,5 @@
def test_log_json_format(docker_compose, nginxproxy): def test_log_json_format(docker_compose, nginxproxy):
conf_lines = nginxproxy.get_conf().decode('ASCII').splitlines() log_conf = [line for line in nginxproxy.get_conf().decode('ASCII').splitlines() if "log_format vhost escape=" in line]
log_conf = [line for line in conf_lines if "log_format vhost escape=" in line]
assert "{\"time_local\":\"$time_iso8601\"," in log_conf[0] assert "{\"time_local\":\"$time_iso8601\"," in log_conf[0]
r = nginxproxy.get("http://nginx-proxy.test/port") r = nginxproxy.get("http://nginx-proxy.test/port")

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: nginx-proxy.test VIRTUAL_HOST: nginx-proxy.test

View file

@ -1,6 +1,5 @@
def test_log_json(docker_compose, nginxproxy): def test_log_json(docker_compose, nginxproxy):
conf_lines = nginxproxy.get_conf().decode('ASCII').splitlines() log_conf = [line for line in nginxproxy.get_conf().decode('ASCII').splitlines() if "log_format vhost escape=" in line]
log_conf = [line for line in conf_lines if "log_format vhost escape=" in line]
assert "{\"time_local\":\"$time_iso8601\"," in log_conf[0] assert "{\"time_local\":\"$time_iso8601\"," in log_conf[0]
r = nginxproxy.get("http://nginx-proxy.test/port") r = nginxproxy.get("http://nginx-proxy.test/port")

View file

@ -8,5 +8,5 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: nginx-proxy.test VIRTUAL_HOST: nginx-proxy.test

View file

@ -1,15 +1,13 @@
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port")
assert r.status_code == 503
def test_webA_is_forwarded(docker_compose, nginxproxy): def test_webA_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webA.nginx-proxy.tld/port") r = nginxproxy.get("http://webA.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"
def test_webB_is_forwarded(docker_compose, nginxproxy): def test_webB_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webB.nginx-proxy.tld/port") r = nginxproxy.get("http://webB.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port", expected_status_code=503)
assert r.status_code == 503

View file

@ -4,5 +4,5 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld

View file

@ -1,29 +1,25 @@
import re import re
def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
def test_forwards_to_web1(docker_compose, nginxproxy): def test_forwards_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.example/port") r = nginxproxy.get("http://web1.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 81\n" assert r.text == "answer from port 81\n"
def test_forwards_to_web2(docker_compose, nginxproxy): def test_forwards_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.example/port") r = nginxproxy.get("http://web2.nginx-proxy.example/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_multipath(docker_compose, nginxproxy): def test_multipath(docker_compose, nginxproxy):
r = nginxproxy.get("http://web3.nginx-proxy.test/port") r = nginxproxy.get("http://web3.nginx-proxy.test/port")
assert r.status_code == 200 assert r.status_code == 200
assert r.text == "answer from port 83\n" assert r.text == "answer from port 83\n"
cfg = nginxproxy.get_conf().decode() cfg = nginxproxy.get_conf().decode()
lines = cfg.splitlines() lines = cfg.splitlines()
web3_server_lines = [l for l in lines web3_server_lines = [l for l in lines if re.search(r'(?m)^\s*server\s+\S*:83;\s*$', l)]
if re.search(r'(?m)^\s*server\s+[^\s]*:83;\s*$', l)]
assert len(web3_server_lines) == 1 assert len(web3_server_lines) == 1
def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
assert r.status_code == 503

View file

@ -17,7 +17,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: web1.nginx-proxy.example VIRTUAL_HOST: web1.nginx-proxy.example
networks: networks:
- net1 - net1
@ -27,7 +27,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
networks: networks:
- net2 - net2
@ -37,7 +37,7 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: 83 WEB_PORTS: "83"
VIRTUAL_HOST: web3.nginx-proxy.test VIRTUAL_HOST: web3.nginx-proxy.test
networks: networks:
- net3a - net3a

View file

@ -2,7 +2,7 @@ def test_virtual_host_is_dropped_when_using_multiports(docker_compose, nginxprox
r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port") r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
assert "answer from port 81\n" in r.text assert "answer from port 81\n" in r.text
r = nginxproxy.get("http://skipped.nginx-proxy.tld/", expected_status_code=503) r = nginxproxy.get("http://skipped.nginx-proxy.tld/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -2,7 +2,7 @@ def test_virtual_host_is_dropped_when_using_multiports(docker_compose, nginxprox
r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port") r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
assert "answer from port 81\n" in r.text assert "answer from port 81\n" in r.text
r = nginxproxy.get("http://skipped.nginx-proxy.tld/", expected_status_code=503) r = nginxproxy.get("http://skipped.nginx-proxy.tld/")
assert r.status_code == 503 assert r.status_code == 503

View file

@ -1,6 +1,13 @@
import re import re
def test_virtual_hosts_with_syntax_error_should_not_be_reachable(docker_compose, nginxproxy):
r = nginxproxy.get("http://test1.nginx-proxy.tld")
assert r.status_code == 503
r = nginxproxy.get("http://test2.nginx-proxy.tld")
assert r.status_code == 503
def test_config_should_have_multiports_warning_comments(docker_compose, nginxproxy): def test_config_should_have_multiports_warning_comments(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII') conf = nginxproxy.get_conf().decode('ASCII')
matches = re.findall(r"the VIRTUAL_HOST_MULTIPORTS environment variable used for this container is not a valid YAML string", conf) matches = re.findall(r"the VIRTUAL_HOST_MULTIPORTS environment variable used for this container is not a valid YAML string", conf)
@ -8,10 +15,3 @@ def test_config_should_have_multiports_warning_comments(docker_compose, nginxpro
assert "# invalidsyntax" in conf assert "# invalidsyntax" in conf
assert "# hostnamerepeat" in conf assert "# hostnamerepeat" in conf
assert "# pathrepeat" in conf assert "# pathrepeat" in conf
def test_virtual_hosts_with_syntax_error_should_not_be_reachable(docker_compose, nginxproxy):
r = nginxproxy.get("http://test1.nginx-proxy.tld", expected_status_code=503)
assert r.status_code == 503
r = nginxproxy.get("http://test2.nginx-proxy.tld", expected_status_code=503)
assert r.status_code == 503

View file

@ -4,6 +4,11 @@ import pytest
from requests import ConnectionError from requests import ConnectionError
def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/port")
assert r.status_code == 503
def test_forwards_to_web1(docker_compose, nginxproxy): def test_forwards_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port") r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
assert r.status_code == 200 assert r.status_code == 200
@ -16,15 +21,13 @@ def test_forwards_to_web2(docker_compose, nginxproxy):
assert r.text == "answer from port 82\n" assert r.text == "answer from port 82\n"
def test_unknown_virtual_host(docker_compose, nginxproxy): @pytest.mark.skipif(
r = nginxproxy.get("http://nginx-proxy/port", expected_status_code=503) platform.system() == "Darwin",
assert r.status_code == 503 reason="This test depends on direct communication with the container's IP"
)
@pytest.mark.xfail(platform.system() == "Darwin", reason="This test is flaky on Darwin")
def test_ipv6_is_disabled_by_default(docker_compose, nginxproxy): def test_ipv6_is_disabled_by_default(docker_compose, nginxproxy):
with pytest.raises(ConnectionError): with pytest.raises(ConnectionError):
nginxproxy.get_without_backoff("http://nginx-proxy/port", ipv6=True) nginxproxy.get("http://nginx-proxy/port", ipv6=True)
def test_container_version_is_displayed(docker_compose, nginxproxy): def test_container_version_is_displayed(docker_compose, nginxproxy):

View file

@ -15,7 +15,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: web1.nginx-proxy.tld VIRTUAL_HOST: web1.nginx-proxy.tld
networks: networks:
- net1 - net1
@ -25,7 +25,7 @@ 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
networks: networks:
- net1 - net1

View file

@ -1,9 +1,7 @@
import re import re
import time
def test_answer_is_served_from_virtual_port_which_is_ureachable(docker_compose, nginxproxy): def test_answer_is_served_from_virtual_port_which_is_ureachable(docker_compose, nginxproxy):
time.sleep(3) r = nginxproxy.get("http://web.nginx-proxy.tld/port")
r = nginxproxy.get("http://web.nginx-proxy.tld/port", expected_status_code=502)
assert r.status_code == 502 assert r.status_code == 502
assert re.search(r"\n\s+server \d+\.\d+\.\d+\.\d+:90;\n", nginxproxy.get_conf().decode('ASCII')) assert re.search(r"\n\s+server \d+\.\d+\.\d+\.\d+:90;\n", nginxproxy.get_conf().decode('ASCII'))

View file

@ -1,19 +1,12 @@
import platform
import pytest
@pytest.mark.xfail(platform.system() == "Darwin", reason="Host to Container IP connection only work on Linux")
def test_raw_ipv4_vhost_forwards_to_web1(docker_compose, nginxproxy): def test_raw_ipv4_vhost_forwards_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://172.20.0.4") r = nginxproxy.get("http://172.20.0.1")
assert r.status_code == 200 assert r.status_code == 200
web1_container = docker_compose.containers.get("web1") web1_container = docker_compose.containers.get("web1")
assert r.text == f"I'm {web1_container.id[:12]}\n" assert r.text == f"I'm {web1_container.id[:12]}\n"
@pytest.mark.xfail(platform.system() == "Darwin", reason="Host to Container IP connection only work on Linux")
def test_raw_ipv6_vhost_forwards_to_web2(docker_compose, nginxproxy): def test_raw_ipv6_vhost_forwards_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://[fd00::4]", ipv6=True) r = nginxproxy.get("http://[fd00::1]")
assert r.status_code == 200 assert r.status_code == 200
web2_container = docker_compose.containers.get("web2") web2_container = docker_compose.containers.get("web2")
assert r.text == f"I'm {web2_container.id[:12]}\n" assert r.text == f"I'm {web2_container.id[:12]}\n"

View file

@ -21,8 +21,8 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: "172.20.0.4" VIRTUAL_HOST: "172.20.0.1"
networks: networks:
net1: net1:
ipv4_address: 172.20.0.2 ipv4_address: 172.20.0.2
@ -34,8 +34,8 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: 82 WEB_PORTS: "82"
VIRTUAL_HOST: "[fd00::4]" VIRTUAL_HOST: "[fd00::1]"
networks: networks:
net1: net1:
ipv4_address: 172.20.0.3 ipv4_address: 172.20.0.3

View file

@ -4,7 +4,7 @@ services:
expose: expose:
- "81" - "81"
environment: environment:
WEB_PORTS: 81 WEB_PORTS: "81"
VIRTUAL_HOST: web.nginx-proxy.tld VIRTUAL_HOST: web.nginx-proxy.tld
web2: web2:
@ -12,7 +12,7 @@ services:
expose: expose:
- "82" - "82"
environment: environment:
WEB_PORTS: 83 WEB_PORTS: "83"
VIRTUAL_HOST: web.nginx-proxy.tld VIRTUAL_HOST: web.nginx-proxy.tld
web3: web3:
@ -20,6 +20,6 @@ services:
expose: expose:
- "83" - "83"
environment: environment:
WEB_PORTS: 83 WEB_PORTS: "83"
VIRTUAL_HOST: web.nginx-proxy.tld VIRTUAL_HOST: web.nginx-proxy.tld
network_mode: "none" network_mode: "none"

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