This commit is contained in:
Tim Schneider 2018-05-24 22:58:19 +02:00
commit db468e1c1b
20 changed files with 412 additions and 28 deletions

14
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,14 @@
# !!!PLEASE READ!!!
## Questions
If you have a question, DO NOT SUBMIT a new issue. Please ask the question on the Q&A Group: https://groups.google.com/forum/#!forum/nginx-proxy
## Bugs or Features
If you are logging a bug or feature request, please search the current open issues to see if there is already a bug or feature opened.
For bugs, the easier you make it to reproduce the issue you see, the easier and faster it can get fixed. If you can provide a script or docker-compose file that reproduces the problems, that is very helpful.
Thanks,
Jason

View file

@ -7,7 +7,8 @@ env:
- TEST_TARGET: test-alpine - TEST_TARGET: test-alpine
before_install: before_install:
- sudo apt-get remove docker docker-engine - sudo apt-get -y remove docker docker-engine docker-ce
- sudo rm /etc/apt/sources.list.d/docker.list
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- sudo apt-get update - sudo apt-get update

View file

@ -18,7 +18,7 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego
RUN chmod u+x /usr/local/bin/forego RUN chmod u+x /usr/local/bin/forego
ENV DOCKER_GEN_VERSION 0.7.3 ENV DOCKER_GEN_VERSION 0.7.4
RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \ RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
&& tar -C /usr/local/bin -xvzf docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \ && tar -C /usr/local/bin -xvzf docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \

View file

@ -15,12 +15,14 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego
RUN chmod u+x /usr/local/bin/forego RUN chmod u+x /usr/local/bin/forego
ENV DOCKER_GEN_VERSION 0.7.3 ENV DOCKER_GEN_VERSION 0.7.4
RUN wget --quiet https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \ RUN wget --quiet https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
&& tar -C /usr/local/bin -xvzf docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \ && tar -C /usr/local/bin -xvzf docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
&& rm /docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz && rm /docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz
COPY network_internal.conf /etc/nginx/
COPY . /app/ COPY . /app/
WORKDIR /app/ WORKDIR /app/

View file

@ -1,3 +1,4 @@
![latest 0.7.0](https://img.shields.io/badge/latest-0.7.0-green.svg?style=flat)
![nginx 1.13](https://img.shields.io/badge/nginx-1.13-brightgreen.svg) ![License MIT](https://img.shields.io/badge/license-MIT-blue.svg) [![Build Status](https://travis-ci.org/jwilder/nginx-proxy.svg?branch=master)](https://travis-ci.org/jwilder/nginx-proxy) [![](https://img.shields.io/docker/stars/jwilder/nginx-proxy.svg)](https://hub.docker.com/r/jwilder/nginx-proxy 'DockerHub') [![](https://img.shields.io/docker/pulls/jwilder/nginx-proxy.svg)](https://hub.docker.com/r/jwilder/nginx-proxy 'DockerHub') ![nginx 1.13](https://img.shields.io/badge/nginx-1.13-brightgreen.svg) ![License MIT](https://img.shields.io/badge/license-MIT-blue.svg) [![Build Status](https://travis-ci.org/jwilder/nginx-proxy.svg?branch=master)](https://travis-ci.org/jwilder/nginx-proxy) [![](https://img.shields.io/docker/stars/jwilder/nginx-proxy.svg)](https://hub.docker.com/r/jwilder/nginx-proxy 'DockerHub') [![](https://img.shields.io/docker/pulls/jwilder/nginx-proxy.svg)](https://hub.docker.com/r/jwilder/nginx-proxy 'DockerHub')
@ -245,6 +246,15 @@ to identify the certificate to be used. For example, a certificate for `*.foo.c
could be named `shared.crt` and `shared.key`. A container running with `VIRTUAL_HOST=foo.bar.com` could be named `shared.crt` and `shared.key`. A container running with `VIRTUAL_HOST=foo.bar.com`
and `CERT_NAME=shared` will then use this shared cert. and `CERT_NAME=shared` will then use this shared cert.
#### OCSP Stapling
To enable OCSP Stapling for a domain, `nginx-proxy` looks for a PEM certificate containing the trusted
CA certificate chain at `/etc/nginx/certs/<domain>.chain.pem`, where `<domain>` is the domain name in
the `VIRTUAL_HOST` directive. The format of this file is a concatenation of the public PEM CA
certificates starting with the intermediate CA most near the SSL certificate, down to the root CA. This is
often referred to as the "SSL Certificate Chain". If found, this filename is passed to the NGINX
[`ssl_trusted_certificate` directive](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate)
and OCSP Stapling is enabled.
#### How SSL Support Works #### How SSL Support Works
The default SSL cipher configuration is based on the [Mozilla intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29) which The default SSL cipher configuration is based on the [Mozilla intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29) which
@ -425,3 +435,7 @@ If your system has the `make` command, you can automate those tasks by calling:
You can learn more about how the test suite works and how to write new tests in the [test/README.md](test/README.md) file. You can learn more about how the test suite works and how to write new tests in the [test/README.md](test/README.md) file.
### Need help?
If you have questions on how to use the image, please ask them on the [Q&A Group](https://groups.google.com/forum/#!forum/nginx-proxy)

View file

@ -130,7 +130,7 @@ upstream {{ $upstream_name }} {
{{ range $knownNetwork := $CurrentContainer.Networks }} {{ range $knownNetwork := $CurrentContainer.Networks }}
{{ range $containerNetwork := $container.Networks }} {{ range $containerNetwork := $container.Networks }}
{{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
## Can be connect with "{{ $containerNetwork.Name }}" network ## Can be connected with "{{ $containerNetwork.Name }}" network
{{/* If only 1 port exposed, use that */}} {{/* If only 1 port exposed, use that */}}
{{ if eq $addrLen 1 }} {{ if eq $addrLen 1 }}
@ -142,6 +142,9 @@ upstream {{ $upstream_name }} {
{{ $address := where $container.Addresses "Port" $port | first }} {{ $address := where $container.Addresses "Port" $port | first }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{ end }} {{ end }}
{{ else }}
# Cannot connect to network of this container
server 127.0.0.1 down;
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ end }} {{ end }}
@ -253,14 +256,14 @@ server {
ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
{{ end }} {{ end }}
{{ if (exists (printf "/etc/nginx/certs/%s.chain.crt" $cert)) }} {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
ssl_stapling on; ssl_stapling on;
ssl_stapling_verify on; ssl_stapling_verify on;
ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.crt" $cert }}; ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
{{ end }} {{ end }}
{{ if (and (ne $https_method "noredirect") (ne $hsts "off")) }} {{ if (and (ne $https_method "noredirect") (ne $hsts "off")) }}
add_header Strict-Transport-Security "{{ trim $hsts }}"; add_header Strict-Transport-Security "{{ trim $hsts }}" always;
{{ end }} {{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}

View file

@ -445,6 +445,18 @@ def pytest_runtest_logreport(report):
report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container)) report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container))
# Py.test `incremental` marker, see http://stackoverflow.com/a/12579625/107049
def pytest_runtest_makereport(item, call):
if "incremental" in item.keywords:
if call.excinfo is not None:
parent = item.parent
parent._previousfailed = item
def pytest_runtest_setup(item):
previousfailed = getattr(item.parent, "_previousfailed", None)
if previousfailed is not None:
pytest.xfail("previous test failed (%s)" % previousfailed.name)
############################################################################### ###############################################################################
# #
@ -457,5 +469,5 @@ try:
except docker.errors.ImageNotFound: except docker.errors.ImageNotFound:
pytest.exit("The docker image 'jwilder/nginx-proxy:test' is missing") pytest.exit("The docker image 'jwilder/nginx-proxy:test' is missing")
if docker.__version__ != "2.0.2": if docker.__version__ != "2.1.0":
pytest.exit("This test suite is meant to work with the python docker module v2.0.2") pytest.exit("This test suite is meant to work with the python docker module v2.1.0")

View file

@ -1,5 +1,5 @@
backoff==1.3.2 backoff==1.3.2
docker-compose==1.11.1 docker-compose==1.11.2
docker==2.0.2 docker==2.1.0
pytest==3.0.5 pytest==3.0.5
requests==2.11.1 requests==2.11.1

View file

@ -1,28 +1,35 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os, sys import os, sys, re
import http.server import http.server
import socketserver import socketserver
class Handler(http.server.SimpleHTTPRequestHandler): class Handler(http.server.SimpleHTTPRequestHandler):
def do_GET(self): def do_GET(self):
self.send_response(200) response_body = ""
response_code = 200
if self.path == "/headers":
response_body += self.headers.as_string()
elif self.path == "/port":
response_body += "answer from port %s\n" % PORT
elif re.match("/status/(\d+)", self.path):
result = re.match("/status/(\d+)", self.path)
response_code = int(result.group(1))
response_body += "answer with response code %s\n" % response_code
elif self.path == "/":
response_body += "I'm %s\n" % os.environ['HOSTNAME']
else:
response_body += "No route for this path!\n"
response_code = 404
self.send_response(response_code)
self.send_header("Content-Type", "text/plain") self.send_header("Content-Type", "text/plain")
self.end_headers() self.end_headers()
if self.path == "/headers": if (len(response_body)):
self.wfile.write(self.headers.as_string().encode()) self.wfile.write(response_body.encode())
elif self.path == "/port":
response = "answer from port %s\n" % PORT
self.wfile.write(response.encode())
elif self.path == "/":
response = "I'm %s\n" % os.environ['HOSTNAME']
self.wfile.write(response.encode())
else:
self.wfile.write("No route for this path!\n".encode())
if __name__ == '__main__': if __name__ == '__main__':
PORT = int(sys.argv[1]) PORT = int(sys.argv[1])

View file

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

View file

@ -0,0 +1,5 @@
Test the behavior of nginx-proxy when restarted after deleting a certificate file is was using.
1. nginx-proxy is created with a virtual host having a certificate
1. while nginx-proxy is running, the certificate file is deleted
1. nginx-proxy is then restarted (without removing the container)

View file

@ -0,0 +1,70 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4096 (0x1000)
Signature Algorithm: sha256WithRSAEncryption
Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld
Validity
Not Before: Feb 17 23:20:54 2017 GMT
Not After : Jul 5 23:20:54 2044 GMT
Subject: CN=web.nginx-proxy
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b6:27:63:a5:c6:e8:f4:7a:94:0e:cc:a2:62:76:
6d:5d:33:6f:cf:19:fc:e7:e5:bb:0e:0e:d0:7c:4f:
73:4c:48:2b:17:d1:4d:d5:9f:42:08:73:84:54:8c:
86:d2:c5:da:59:01:3f:42:22:e0:36:f0:dc:ab:de:
0a:bd:26:2b:22:13:87:a6:1f:23:ef:0e:99:27:8b:
15:4a:1b:ef:93:c9:6b:91:de:a0:02:0c:62:bb:cc:
56:37:e8:25:92:c3:1f:f1:69:d8:7c:a8:33:e0:89:
ce:14:67:a0:39:77:88:91:e6:a3:07:97:90:22:88:
d0:79:18:63:fb:6f:7e:ee:2b:42:7e:23:f5:e7:da:
e9:ee:6a:fa:96:65:9f:e1:2b:15:49:c8:cd:2d:ce:
86:4f:2c:2a:67:79:bf:41:30:14:cc:f6:0f:14:74:
9e:b6:d3:d0:3b:f0:1b:b8:e8:19:2a:fd:d6:fd:dc:
4b:4e:65:7d:9b:bf:37:7e:2d:35:22:2e:74:90:ce:
41:35:3d:41:a0:99:db:97:1f:bf:3e:18:3c:48:fb:
da:df:c6:4e:4e:b9:67:b8:10:d5:a5:13:03:c4:b7:
65:e7:aa:f0:14:4b:d3:4d:ea:fe:8f:69:cf:50:21:
63:27:cf:9e:4c:67:15:7b:3f:3b:da:cb:17:80:61:
1e:25
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:web.nginx-proxy
Signature Algorithm: sha256WithRSAEncryption
09:31:be:db:4e:b0:b6:68:da:ae:5b:16:51:29:fc:9f:61:b6:
5a:2f:3c:35:ef:67:76:97:b0:34:4e:3b:b4:d6:88:19:4f:84:
2e:73:d3:c0:3a:4c:41:54:6c:bb:67:89:67:ad:25:55:d7:d4:
80:fe:a7:3f:3d:9e:f1:34:96:d8:da:5a:78:51:c0:63:f1:52:
29:35:55:f4:7d:70:1c:d3:96:62:7f:64:86:81:52:27:c4:c6:
10:13:c6:73:56:4d:32:d0:b3:c3:c8:2c:25:83:e4:2b:1d:d4:
74:30:e5:85:af:2d:b6:a5:6b:fe:5d:d3:3c:00:58:94:f4:6a:
f5:a6:1d:cf:f9:ed:d5:27:ed:13:24:b2:4f:2b:f3:b8:e4:af:
0c:1d:fe:e0:6a:01:5e:a2:44:ff:3e:96:fa:6c:39:a3:51:37:
f3:72:55:d8:2d:29:6e:de:95:b9:d8:e3:1e:65:a5:9c:0d:79:
2d:39:ab:c7:ac:16:b6:a5:71:4b:35:a4:6c:72:47:1b:72:9c:
67:58:c1:fc:f6:7f:a7:73:50:7b:d6:27:57:74:a1:31:38:a7:
31:e3:b9:d4:c9:45:33:ec:ed:16:cf:c5:bd:d0:03:b1:45:3f:
68:0d:91:5c:26:4e:37:05:74:ed:3e:75:5e:ca:5e:ee:e2:51:
4b:da:08:99
-----BEGIN CERTIFICATE-----
MIIC8zCCAdugAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp
bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs
ZDAeFw0xNzAyMTcyMzIwNTRaFw00NDA3MDUyMzIwNTRaMBoxGDAWBgNVBAMMD3dl
Yi5uZ2lueC1wcm94eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALYn
Y6XG6PR6lA7MomJ2bV0zb88Z/Ofluw4O0HxPc0xIKxfRTdWfQghzhFSMhtLF2lkB
P0Ii4Dbw3KveCr0mKyITh6YfI+8OmSeLFUob75PJa5HeoAIMYrvMVjfoJZLDH/Fp
2HyoM+CJzhRnoDl3iJHmoweXkCKI0HkYY/tvfu4rQn4j9efa6e5q+pZln+ErFUnI
zS3Ohk8sKmd5v0EwFMz2DxR0nrbT0DvwG7joGSr91v3cS05lfZu/N34tNSIudJDO
QTU9QaCZ25cfvz4YPEj72t/GTk65Z7gQ1aUTA8S3Zeeq8BRL003q/o9pz1AhYyfP
nkxnFXs/O9rLF4BhHiUCAwEAAaMeMBwwGgYDVR0RBBMwEYIPd2ViLm5naW54LXBy
b3h5MA0GCSqGSIb3DQEBCwUAA4IBAQAJMb7bTrC2aNquWxZRKfyfYbZaLzw172d2
l7A0Tju01ogZT4Quc9PAOkxBVGy7Z4lnrSVV19SA/qc/PZ7xNJbY2lp4UcBj8VIp
NVX0fXAc05Zif2SGgVInxMYQE8ZzVk0y0LPDyCwlg+QrHdR0MOWFry22pWv+XdM8
AFiU9Gr1ph3P+e3VJ+0TJLJPK/O45K8MHf7gagFeokT/Ppb6bDmjUTfzclXYLSlu
3pW52OMeZaWcDXktOavHrBa2pXFLNaRsckcbcpxnWMH89n+nc1B71idXdKExOKcx
47nUyUUz7O0Wz8W90AOxRT9oDZFcJk43BXTtPnVeyl7u4lFL2giZ
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAtidjpcbo9HqUDsyiYnZtXTNvzxn85+W7Dg7QfE9zTEgrF9FN
1Z9CCHOEVIyG0sXaWQE/QiLgNvDcq94KvSYrIhOHph8j7w6ZJ4sVShvvk8lrkd6g
Agxiu8xWN+glksMf8WnYfKgz4InOFGegOXeIkeajB5eQIojQeRhj+29+7itCfiP1
59rp7mr6lmWf4SsVScjNLc6GTywqZ3m/QTAUzPYPFHSettPQO/AbuOgZKv3W/dxL
TmV9m783fi01Ii50kM5BNT1BoJnblx+/Phg8SPva38ZOTrlnuBDVpRMDxLdl56rw
FEvTTer+j2nPUCFjJ8+eTGcVez872ssXgGEeJQIDAQABAoIBAGQCMFW+ZfyEqHGP
rMA+oUEAkqy0agSwPwky3QjDXlxNa0uCYSeebtTRB6CcHxHuCzm+04puN4gyqhW6
rU64fAoTivCMPGBuNWxekmvD9r+/YM4P2u4E+th9EgFT9f0kII+dO30FpKXtQzY0
xuWGWXcxl+T9M+eiEkPKPmq4BoqgTDo5ty7qDv0ZqksGotKFmdYbtSvgBAueJdwu
VWJvenI9F42ExBRKOW1aldiRiaYBCLiCVPKJtOg9iuOP9RHUL1SE8xy5I5mm78g3
a13ji3BNq3yS+VhGjQ7zDy1V1jGupLoJw4I7OThu8hy+B8Vt8EN/iqakufOkjlTN
xTJ33CkCgYEA5Iymg0NTjWk6aEkFa9pERjfUWqdVp9sWSpFFZZgi55n7LOx6ohi3
vuLim3is/gYfK2kU/kHGZZLPnT0Rdx0MbOB4XK0CAUlqtUd0IyO4jMZ06g4/kn3N
e2jLdCCIBoEQuLk4ELxj2mHsLQhEvDrg7nzU2WpTHHhvJbIbDWOAxhsCgYEAzAgv
rKpanF+QDf4yeKHxAj2rrwRksTw4Pe7ZK/bog/i+HIVDA70vMapqftHbual/IRrB
JL7hxskoJ/h9c1w4xkWDjqkSKz8/Ihr4dyPfWyGINWbx/rarT/m5MU5SarScoK7o
Xgb25x+W+61rtI+2JhVRGO86+JiAeT4LkAX88L8CgYAwHHug/jdEeXZWJakCfzwI
HBCT1M3vO+uBXvtg25ndb0i0uENIhDOJ93EEkW65Osis9r34mBgPocwaqZRXosHO
2aH8wF6/rpjL+HK2QvrCh7Rs4Pr494qeA/1wQLjhxaGjgToQK9hJTHvPLwJpLWvU
SGr2Ka+9Oo0LPmb7dorRKQKBgQCLsNcjOodLJMp2KiHYIdfmlt6itzlRd09yZ8Nc
rHHJWVagJEUbnD1hnbHIHlp3pSqbObwfMmlWNoc9xo3tm6hrZ1CJLgx4e5b3/Ms8
ltznge/F0DPDFsH3wZwfu+YFlJ7gDKCfL9l/qEsxCS0CtJobPOEHV1NivNbJK8ey
1ca19QKBgDTdMOUsobAmDEkPQIpxfK1iqYAB7hpRLi79OOhLp23NKeyRNu8FH9fo
G3DZ4xUi6hP2bwiYugMXDyLKfvxbsXwQC84kGF8j+bGazKNhHqEC1OpYwmaTB3kg
qL9cHbjWySeRdIsRY/eWmiKjUwmiO54eAe1HWUdcsuz8yM3xf636
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,17 @@
web:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web.nginx-proxy
reverseproxy:
image: jwilder/nginx-proxy:test
container_name: reverseproxy
environment:
DEBUG: "true"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./tmp_certs:/etc/nginx/certs:ro

View file

@ -0,0 +1,73 @@
import logging
import os
from os.path import join, isfile
from shutil import copy
from time import sleep
import pytest
from requests import ConnectionError
script_dir = os.path.dirname(__file__)
pytestmark = pytest.mark.xfail() # TODO delete this marker once those issues are fixed
@pytest.yield_fixture(scope="module", autouse=True)
def certs():
"""
pytest fixture that provides cert and key files into the tmp_certs directory
"""
file_names = ("web.nginx-proxy.crt", "web.nginx-proxy.key")
logging.info("copying server cert and key files into tmp_certs")
for f_name in file_names:
copy(join(script_dir, "certs", f_name), join(script_dir, "tmp_certs"))
yield
logging.info("cleaning up the tmp_cert directory")
for f_name in file_names:
if isfile(join(script_dir, "tmp_certs", f_name)):
os.remove(join(script_dir, "tmp_certs", f_name))
###############################################################################
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://foo.nginx-proxy/")
assert r.status_code == 503
def test_http_web_is_301(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy/port", allow_redirects=False)
assert r.status_code == 301
def test_https_web_is_200(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy/port")
assert r.status_code == 200
assert 'answer from port 81\n' in r.text
@pytest.mark.incremental
def test_delete_cert_and_restart_reverseproxy(docker_compose):
os.remove(join(script_dir, "tmp_certs", "web.nginx-proxy.crt"))
docker_compose.containers.get("reverseproxy").restart()
sleep(3) # give time for the container to initialize
assert "running" == docker_compose.containers.get("reverseproxy").status
@pytest.mark.incremental
def test_unknown_virtual_host_is_still_503(nginxproxy):
r = nginxproxy.get("http://foo.nginx-proxy/")
assert r.status_code == 503
@pytest.mark.incremental
def test_http_web_is_now_200(nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy/port", allow_redirects=False)
assert r.status_code == 200
assert "answer from port 81\n" == r.text
@pytest.mark.incremental
def test_https_web_is_now_broken_since_there_is_no_cert(nginxproxy):
with pytest.raises(ConnectionError):
nginxproxy.get("https://web.nginx-proxy/port")

View file

@ -0,0 +1,2 @@
*
!.gitignore

View file

@ -0,0 +1,59 @@
# nginx-proxy template is not considered when a container is not reachable
Having a container with the `VIRTUAL_HOST` environment variable set but on a network not reachable from the nginx-proxy container will result in nginx-proxy serving the default nginx welcome page for all requests.
Furthermore, if the nginx-proxy in such state is restarted, the nginx process will crash and the container stops.
In the generated nginx config file, we can notice the presence of an empty `upstream {}` block.
This can be fixed by merging [PR-585](https://github.com/jwilder/nginx-proxy/pull/585).
## How to reproduce
1. a first web container is created on network `netA`
1. a second web container is created on network `netB`
1. nginx-proxy is created with access to `netA` only
## Erratic behavior
- nginx serves the default welcome page for all requests to `/` and error 404 for any other path
- nginx-container crash on restart
Log shows:
```
webB_1 | starting a web server listening on port 82
webA_1 | starting a web server listening on port 81
reverseproxy | forego | starting dockergen.1 on port 5000
reverseproxy | forego | starting nginx.1 on port 5100
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Generated '/etc/nginx/conf.d/default.conf' from 3 containers
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Running 'nginx -s reload'
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Error running notify command: nginx -s reload, exit status 1
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Watching docker events
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
reverseproxy | reverseproxy | forego | starting dockergen.1 on port 5000 <---- nginx-proxy container restarted
reverseproxy | forego | starting nginx.1 on port 5100
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Generated '/etc/nginx/conf.d/default.conf' from 3 containers
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Running 'nginx -s reload'
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Error running notify command: nginx -s reload, exit status 1
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Watching docker events
reverseproxy | dockergen.1 | 2017/02/20 01:10:24 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
reverseproxy | forego | starting dockergen.1 on port 5000
reverseproxy | forego | starting nginx.1 on port 5100
reverseproxy | nginx.1 | 2017/02/20 01:11:02 [emerg] 17#17: no servers are inside upstream in /etc/nginx/conf.d/default.conf:64
reverseproxy | forego | starting nginx.1 on port 5200
reverseproxy | forego | sending SIGTERM to nginx.1
reverseproxy | forego | sending SIGTERM to dockergen.1
reverseproxy exited with code 0
reverseproxy exited with code 0
```
## Expected behavior
- no default nginx welcome page should be served
- nginx is able to forward requests to containers of `netA`
- nginx respond with error 503 for unknown virtual hosts
- nginx is not able to forward requests to containers of `netB` and responds with an error
- nginx should survive restarts

View file

@ -0,0 +1,35 @@
version: "2"
networks:
netA:
netB:
services:
reverseproxy:
container_name: reverseproxy
networks:
- netA
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
webA:
networks:
- netA
image: web
expose:
- 81
environment:
WEB_PORTS: 81
VIRTUAL_HOST: webA.nginx-proxy
webB:
networks:
- netB
image: web
expose:
- 82
environment:
WEB_PORTS: 82
VIRTUAL_HOST: webB.nginx-proxy

View file

@ -0,0 +1,35 @@
from time import sleep
import pytest
import requests
pytestmark = pytest.mark.xfail() # TODO delete this marker once #585 is merged
def test_default_nginx_welcome_page_should_not_be_served(docker_compose, nginxproxy):
r = nginxproxy.get("http://whatever.nginx-proxy/", allow_redirects=False)
assert "<title>Welcome to nginx!</title>" not in r.text
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy/", allow_redirects=False)
assert r.status_code == 503
def test_http_web_a_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webA.nginx-proxy/port", allow_redirects=False)
assert r.status_code == 200
assert "answer from port 81\n" == r.text
def test_http_web_b_gets_an_error(docker_compose, nginxproxy):
r = nginxproxy.get("http://webB.nginx-proxy/", allow_redirects=False)
assert "<title>Welcome to nginx!</title>" not in r.text
with pytest.raises(requests.exceptions.HTTPError):
r.raise_for_status()
def test_reverseproxy_survive_restart(docker_compose):
docker_compose.containers.get("reverseproxy").restart()
sleep(2) # give time for the container to initialize
assert docker_compose.containers.get("reverseproxy").status == "running"

View file

@ -7,6 +7,13 @@ def test_web1_HSTS_default(docker_compose, nginxproxy):
assert "Strict-Transport-Security" in r.headers assert "Strict-Transport-Security" in r.headers
assert "max-age=31536000" == r.headers["Strict-Transport-Security"] assert "max-age=31536000" == r.headers["Strict-Transport-Security"]
# Regression test to ensure HSTS is enabled even when the upstream sends an error in response
# Issue #1073 https://github.com/jwilder/nginx-proxy/pull/1073
def test_web1_HSTS_error(docker_compose, nginxproxy):
r = nginxproxy.get("https://web1.nginx-proxy.tld/status/500", allow_redirects=False)
assert "Strict-Transport-Security" in r.headers
assert "max-age=31536000" == r.headers["Strict-Transport-Security"]
def test_web2_HSTS_off(docker_compose, nginxproxy): def test_web2_HSTS_off(docker_compose, nginxproxy):
r = nginxproxy.get("https://web2.nginx-proxy.tld/port", allow_redirects=False) r = nginxproxy.get("https://web2.nginx-proxy.tld/port", allow_redirects=False)
assert "answer from port 81\n" in r.text assert "answer from port 81\n" in r.text