- `dhparam_generation` tests are no longer necessary, dropped. Modified the remaining `dhparam` test to use multiple `nginx-proxy` images to verify correct behavior for different configs. Tests now cover: - Default (ffdhe4096) is used. - Alternative via ENV (ffdhe2048) works correctly. - Invalid group via ENV (1024-bit) fails. - Custom DH params provided via file mount works with warning emitted. --- - `assert_log_contains`: added a `container_name` arg with `nginxproxy` as the default value. This allows multiple nginx-proxy containers to utilize this method instead. - Extracted out the `openssl` test (_to `negotiate_cipher()`_) and modified it to be a bit more flexible. It now takes a container with optional extra args to pass to `openssl` command called, as well as the `grep` string to match. This made the original test redundant, so I've dropped it. - Added two methods to use `negotiate_cipher()`, one verifies a DHE cipher suite was negotiated and checks that a DH emphermal key was also mentioned in the output. The other method verifies the expectation of failing to negotiate a valid cipher if DH params have not been set, while verifying that non-DHE cipher suites can be successfully negotiated. - Added a `get_env()` method for extracting attached environments on a container. This is useful for verifying invalid `DHPARAM_BITS` values (eg `1024`-bit). - The original `Server Temp Key` assertion was incorrect, it was expecting a value that is unrelated to DHE cipher suite support (_`X25519` is related to ECDHE_). This is due to TLS 1.3 being negotiated where you cannot use custom DH params, nor influence the negotiated cipher due to this mechanism changing from TLS 1.3. TLS 1.3 does support DH params, but it internally negotiates RFC 7919 group between server and client instead. Thus to verify expectations, the connection via `openssl` is made explicitly with TLS 1.2 instead.
Nginx proxy test suite
Install requirements
You need python 3.9 and pip installed. Then run the commands:
pip install -r requirements/python-requirements.txt
Prepare the nginx-proxy test image
make build-nginx-proxy-test-debian
or if you want to test the alpine flavor:
make build-nginx-proxy-test-alpine
Run the test suite
pytest
need more verbosity ?
pytest -s
Run one single test module
pytest test_nominal.py
Write a test module
This test suite uses pytest. The conftest.py file will be automatically loaded by pytest and will provide you with two useful pytest fixtures:
- docker_compose
- nginxproxy
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.
Once the docker compose file found, the fixture will remove all containers, run docker-compose up
, and finally your test will be executed.
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:
docker-compose -f test_example.yml up -d
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 your tests, you can use the docker_compose
variable to query and command the docker daemon as it provides you with a client from the docker python module.
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:
nginx-proxy
nginx-proxy.com
www.nginx-proxy.com
www.nginx-proxy.test
www.nginx-proxy
whatever.nginx-proxyooooooo
- ...
Any domain name ending with XXX.container.docker
will resolve to the IP address of the XXX container.
web1.container.docker
will resolve to the IP address of theweb1
containerf00.web1.container.docker
will resolve to the IP address of theweb1
containeranything.whatever.web2.container.docker
will resolve to the IP address of theweb2
container
Otherwise, domain names are resoved as usual using your system DNS resolver.
nginxproxy fixture
The nginxproxy
fixture will provide you with a replacement for the python requests 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.
Also this requests replacement is preconfigured to use the Certificate Authority root certificate certs/ca-root.crt to validate https connections.
Furthermore, the nginxproxy methods accept an additional keyword parameter: ipv6
which forces requests made against containers to use the containers IPv6 address when set to True
. If IPv6 is not supported by the system or docker, that particular test will be skipped.
def test_forwards_to_web1_ipv6(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port", ipv6=True)
assert r.status_code == 200
assert r.text == "answer from port 81\n"
The web docker image
When you run the make build-webserver
command, you built a web
docker image which is convenient for running a small web server in a container. This image can produce containers that listens on multiple ports at the same time.
Testing TLS
If you need to create server certificates, use the certs/create_server_certificate.sh
script. Pytest will be able to validate any certificate issued from this script.