1
0
mirror of https://github.com/thib8956/nginx-proxy synced 2025-04-11 16:21:01 +00:00

Compare commits

...

415 Commits

Author SHA1 Message Date
7470514e7b
Merge pull request #1 from harvdogg/master
Allow complete override of location blocks
2020-04-13 19:45:23 +02:00
Jason Wilder
3cbc5417b7
Merge pull request #1113 from basro/master
Set DISABLE_ACCESS_LOGS env var to disable access logs
2020-03-25 14:27:02 -06:00
Jason Wilder
8219788df6
Merge branch 'master' into master 2020-03-25 14:26:30 -06:00
Jason Wilder
941fd630a6
Merge pull request #1069 from ivoputzer/patch-1
Fixes typo
2020-03-03 00:29:54 -05:00
Jason Wilder
b4709639b3
Merge pull request #1353 from nanawel/feature/custom-external-ports-support
Add support for custom external HTTP/HTTPS ports
2020-03-03 00:24:21 -05:00
Jason Wilder
b8141832a3
Merge pull request #1336 from cherouvim/patch-1
typo
2020-02-29 23:11:12 -05:00
Jason Wilder
0223bae0af
Merge pull request #1402 from sgabe/upgrade-nginx
Upgrade to 1.17.8
2020-02-29 23:09:59 -05:00
sgabe
6798a6b800 Upgrade to 1.17.8 2020-02-28 16:36:36 +01:00
Jason Wilder
4a2dc46002
Merge pull request #1198 from umevoshi/master
Add gRPC protocol support (#1345)
2020-02-11 11:45:53 -07:00
Jason Wilder
718d45feaf
Merge pull request #1338 from mauvm/patch-1
Do not HTTPS redirect Let'sEncrypt ACME challenge
2020-02-09 13:31:42 -07:00
Jason Wilder
0dfe09fb7c
Merge pull request #1366 from jakejarvis/nginx-1.17.6
Upgrade nginx to 1.17.6
2020-02-09 13:30:06 -07:00
Jake Jarvis
77227f8691
Upgrade nginx to 1.17.6 2019-12-04 10:19:17 -05:00
Jason Wilder
e762468759
Merge pull request #1356 from matt-hh/upgrade-nginx-1.17.5
Upgrade to 1.17.5
2019-11-08 07:00:06 -07:00
Matthias Döring
cf911d950a Upgrade to 1.17.5
closes #1337, resolves #1355
2019-11-08 14:11:17 +01:00
nanawel
a3e64a9433 Add support for custom external HTTP/HTTPS ports (see https://groups.google.com/forum/#!topic/nginx-proxy/0I2jevmgTLI) 2019-11-03 14:48:16 +01:00
Maurits van Mastrigt
11d644d645
Do not HTTPS redirect Let'sEncrypt ACME challenge
The auto renewal of Let'sEncrypt certificates fails due to the HTTPS redirect of the ACME challenge.

This workaround resolves the issue:
https://gist.github.com/codekitchen/2c519eb7572002afab6a5f979cd42913#file-letsencrypt-diff

Found through this comment:
https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/issues/526#issuecomment-476253642
2019-10-01 16:00:41 +02:00
Jason Wilder
4443ee8b5a
Merge pull request #1116 from qiqizjl/master
fix fastcgi bug
2019-09-26 13:21:12 -06:00
Jason Wilder
08b953ba70
Merge pull request #1252 from jpomykala/patch-1
Update README.md
2019-09-25 19:44:56 -06:00
Jason Wilder
94f5763c04
Merge pull request #1323 from came88/banner
README.md: fix version in nginx banner
2019-09-25 19:39:17 -06:00
Jason Wilder
8cf9ed7145
Merge pull request #1328 from dnmvisser/patch-1
Typo
2019-09-25 19:37:42 -06:00
Ioannis Cherouvim
3ce7d99aea
typo 2019-09-25 13:21:33 +03:00
Jason Wilder
3a9de2d511
Merge pull request #1322 from came88/ssl-configuration
Update ssl configuration
2019-09-09 09:04:20 -06:00
came88
eba7d8af77
Fix comment about Mozilla Modern Policy and TLS1.3
Thanks to @deAtog for pointing it out
2019-09-09 12:45:20 +02:00
Dick Visser
d8c04f666f
Typo 2019-09-09 07:22:58 +02:00
Lorenzo Cameroni
ede9f9ec25 README.md: fix version in nginx banner 2019-08-29 22:59:43 +02:00
Lorenzo Cameroni
26e764950f Update ssl configuration 2019-08-29 22:14:14 +02:00
Jason Wilder
b886c0bd14
Merge pull request #1318 from came88/nginx
Upgrade nginx to 1.17.3
2019-08-18 09:13:33 -06:00
Lorenzo Cameroni
a4cc268628 Use nginx 1.17.3 2019-08-18 11:20:05 +02:00
Jason Wilder
eb3613695c
Merge pull request #1313 from JIAZHEN/master
Upgrade nginx to 1.17
2019-08-08 08:26:11 -06:00
Jiazhen Xie
23823c4b21 Fix the test 2019-08-07 17:33:02 +01:00
Jiazhen Xie
16169a0f74 Use nginx latest version 2019-08-07 17:32:52 +01:00
Jakub Pomykała
afa2dc53c7
Update README.md 2019-03-23 12:23:12 +01:00
Jason Wilder
8c590fc68f
Merge pull request #913 from panteparak/DH-Param-Generator-Option
Add DH param generator option
2019-03-05 12:46:49 -07:00
Jason Wilder
15d2817384
Merge pull request #1230 from kamermans/bugfix/ssl_tests_fail_dhparam
Fixed tests that are now failing due to the dhparam clearing command …
2019-02-05 13:44:45 -07:00
Jason Wilder
53a396f406
Merge pull request #1213 from seroron/fix_empty_dhparam
Fix empty dhparam.pem
2019-02-05 13:44:02 -07:00
Steve Kamerman
ad41178036
Fixed tests that are now failing due to the dhparam clearing command beating the nginx startup. This is fixed permanently in #1213, but this PR fixes the test so as not to rely on the dhparam autogen, which is tested elsewhere. 2019-02-04 15:15:04 -05:00
Jason Wilder
7dd97d4bc3
Merge pull request #1111 from kamermans/bugfix/cant-disable-hsts-noredirect
Fixed #1080, can't disable HSTS with noredirect
2019-01-29 16:21:30 -07:00
Kenichi HIROSE
6a1a518fec Fix empty dhparam.pem 2018-12-18 14:05:17 +00:00
umevoshi
62d51562b5 Add gRPC protocol support 2018-11-15 01:02:57 +09:00
Jason Wilder
c33dedf10b
Merge pull request #1193 from gpkfr/master
Upgrade to nginx 1.14.1 stable version
2018-11-09 09:50:28 -07:00
Gpkfr
58c1fe3606
Upgrade to nginx 1.14.1 stable version 2018-11-09 15:26:01 +01:00
Trent Harvey
593c3c29b0 Adjusted formatting on README 2018-10-17 19:22:14 -07:00
Trent Harvey
c984ed2b18 Added support for completely overriding the location blocks for proxied containers. 2018-10-17 19:21:02 -07:00
Jason Wilder
e80fc0b304
Merge pull request #1140 from matt-hh/feature/upgrade-1.14
Upgrade to nginx 1.14 stable version
2018-08-17 10:20:50 -06:00
Steve Kamerman
936e57a6de
Fixed #1080, can't disable HSTS with noredirect 2018-08-01 11:30:06 -04:00
Jason Wilder
a285717657
Merge pull request #1123 from kamermans/feature/tls1.3
Add TLSv1.3 support
2018-06-07 15:56:19 -06:00
Matthias Döring
cb2b0e2bd3 Upgrade to nginx 1.14 stable 2018-06-06 00:56:47 +02:00
Steve Kamerman
4e6900e872
Added TLSv1.3 support 2018-04-22 18:29:35 -04:00
Jason Wilder
9521593cbc
Merge pull request #1124 from kamermans/bugfix/travis-ci-errors-fix2
Bugfix: Test reads from out-of-scope var
2018-04-22 15:47:42 -06:00
Steve Kamerman
af266c0b83
Remove old docker.list to avoid getting unstable Docker version 2018-04-22 16:43:00 -04:00
Steve Kamerman
9be2624d09
Increased dependency versions to get around pip internal problem 2018-04-22 16:11:32 -04:00
Steve Kamerman
c417813df9
Fixed out-of-scope variable 2018-04-22 16:03:43 -04:00
耐小心
59aa78a4a6 fix fastcgi bug 2018-04-17 21:52:58 +08:00
Jason Wilder
ccbbbeb928
Merge pull request #1073 from b1f6c1c4/b1f6c1c4-patch-1
Add HSTS header regardless of status code
2018-03-30 17:34:02 -04:00
耐小心
1c7ccc473f fix fastcgi bug 2018-03-30 09:47:57 +08:00
Jason Wilder
556b3364fb
Merge pull request #1115 from kamermans/docs/ocsp-stapling-info
Added docs on enabling OCSP Stapling
2018-03-28 20:50:15 -04:00
Steve Kamerman
d7e939dc27
Added info on enabling OCSP Stapling 2018-03-28 11:43:41 -04:00
Mario Carbajal
f68383add9
Set DISABLE_ACCESS_LOGS to disable access logs 2018-03-27 21:18:45 -03:00
b1f6c1c4
d8777c8689
Merge pull request #2 from kamermans/b1f6c1c4-patch-1
Add tests to HSTS bugfix
2018-03-27 11:15:12 +08:00
Steve Kamerman
3590c1bae0
Added regression test to ensure HSTS works for errors 2018-03-26 14:58:06 -04:00
Steve Kamerman
c1ae91364c
Added endpoint to allow testing alternate response codes 2018-03-26 14:57:50 -04:00
Jason Wilder
71225a28fa
Merge pull request #1026 from jwilder/updates
Updates for 0.7.0 release
2018-03-23 23:34:31 -06:00
Jason Wilder
f8cd4483ac Update version to 0.7.0 2018-03-23 23:03:42 -06:00
Jason Wilder
5266553e1b Add issue template/q&a links 2018-03-23 21:07:43 -06:00
Jason Wilder
1f19ee3c56
Merge pull request #1088 from matt-hh/fix-1076
Enable NETWORK_ACCESS feature for alpine version
2018-03-23 14:54:52 -06:00
Jason Wilder
6290f38069
Merge pull request #1106 from hwellmann/master
do not create an empty upstream entry for invisible containers
2018-03-23 12:14:37 -06:00
Jason Wilder
1dce981707
Merge pull request #984 from sydoveton/master
OCSP Stapling was not working
2018-03-23 08:57:27 -06:00
Harald Wellmann
b61c841929 do not create an empty upstream entry for a container from an invisible Docker network 2018-03-22 10:56:41 +01:00
Jason Wilder
000a44772d
Merge pull request #1090 from sergeifilippov/patch-1
Grammar Police
2018-03-08 15:01:00 -07:00
Sergei Filippov
37714fa4f8
Grammar Police
Tiny grammatical fix.
2018-03-09 10:48:14 +13:00
Matthias Döring
2f8ebe8d45 Enable NETWORK_ACCESS feature for alpine version
This PR fixes a missing line in the alpine version.

- Fixes #1076
- See #842
2018-03-07 22:36:05 +01:00
Jason Wilder
d6042d08f1
Merge pull request #734 from thomasleveil/knwon_issues
TESTS: add tests for known issues
2018-02-20 14:50:11 -07:00
b1f6c1c4
7a769a6a22
Add HSTS header regardless of status code
See nginx [doc](http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header) and [blog](https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/).
2018-02-20 17:59:52 +08:00
Ivo von Putzer Reibegg
0f27ed800c
fixes typo
spotted a typo within the readme ;) cheers
2018-02-14 21:14:25 +01:00
Jason Wilder
226bfe158f
Merge pull request #926 from Paike/patch-1
Fallback if container has no IP
2018-01-20 23:04:40 -07:00
Jason Wilder
32d42ffee7 Update docker-gen to 0.7.4 2018-01-14 15:28:46 -07:00
Jason Wilder
3fab237f34
Merge pull request #1022 from mouhamed/patch-1
Remove duplicate
2018-01-09 21:29:06 -07:00
mouhamed
1eac894902
Remove duplicate 2018-01-09 21:12:37 +01:00
Jason Wilder
73f29846b3
Merge pull request #957 from buchdag/ssl-modern
Allow optional use of Mozilla modern SSL configuration
2017-12-06 16:34:30 -07:00
Sy Doveton
6e9dc343cd
Changed the SSL stapling cert extension to pem from crt. SSL stapling was not working due to the incorrect file extension. 2017-11-19 11:35:30 +00:00
Nicolas Duchon
35f092ca30 Update doc with SSL_POLICY values 2017-11-18 09:18:55 +01:00
Nicolas Duchon
bf16afc665 Use enumerable SSL_POLICY instead of bool 2017-11-18 09:18:55 +01:00
Nicolas Duchon
ea80027525
Merge branch 'master' into ssl-modern 2017-11-16 22:47:25 +01:00
Jason Wilder
a6e8fae7f5
Merge pull request #970 from kamermans/bugfix/failing_ssl_tests
Fixed failing SSL tests
2017-11-09 10:04:20 -07:00
Jason Wilder
9b8323d2e2
Merge pull request #969 from kamermans/feature/custom_hsts
Added custom HSTS support (issue #953)
2017-11-09 08:47:37 -07:00
Steve Kamerman
612bf72ceb
Support old and new versions of requests 2017-11-08 23:19:13 -05:00
Steve Kamerman
ebd1485b09
Catch SSLError instead of CertificateError 2017-11-08 22:53:44 -05:00
Steve Kamerman
58a02f107e
Removed '-verify 0' - to disable verification, exclude -verify entirely 2017-11-08 22:42:52 -05:00
Steve Kamerman
a312472fb5
Added custom HSTS support (issue #953) 2017-11-08 22:30:24 -05:00
Jason Wilder
1374ee5b9e
Merge pull request #962 from cglewis/master
MAINTAINER is deprecated, using LABEL now
2017-11-06 11:07:26 -07:00
cglewis
55610b8425 MAINTAINER is deprecated, using LABEL now 2017-10-31 18:21:12 -07:00
Nicolas Duchon
56fb58cc6f Update doc for mozilla modern profile 2017-10-27 10:28:42 +02:00
Nicolas Duchon
ea98780960 Enable optional mozilla modern profile 2017-10-27 10:28:42 +02:00
Jason Wilder
1b868259fe Merge pull request #955 from buchdag/dhparam-separate-container
Fix default dhparam.pem when using separate containers
2017-10-26 10:09:18 -06:00
Nicolas Duchon
3ac478f284 Update Diffie-Hellman Groups doc
+ corrected a typo
2017-10-25 12:34:22 +02:00
Nicolas Duchon
2528a35656 Don't presume the existence of default dhparam
The default dhparam at /etc/nginx/dhparam/dhparam.pem won't be auto generated with the separate containers setup.
2017-10-25 12:32:09 +02:00
Jason Wilder
3ef600a3b5 Merge pull request #842 from kamermans/feature/external_internal_network
Allow containers to be restricted to internal network
2017-10-20 10:04:08 -06:00
Jason Wilder
fc36514eb8 Merge pull request #863 from qiqizjl/master
support fastcgi
2017-10-20 10:00:27 -06:00
Jason Wilder
795e153c2e Merge pull request #951 from kamermans/bugfix/ipv6_resolvers
Added support for IPv6 DNS servers in resolver generation (issue #938)
2017-10-20 09:50:56 -06:00
Steve Kamerman
5d503b48cb
Added support for IPv6 DNS servers in resolver generation (issue #938) 2017-10-19 20:58:34 -04:00
Steve Kamerman
93d90884e2
Implemented NETWORK_ACCESS (squash commit) 2017-10-18 13:29:12 -04:00
Pan Teparak
92379d8131 Update Readme 2017-09-24 15:15:00 +07:00
Pan Teparak
31d2ed172b Change ENV variable from GENERATE_DHPARAM to DHPARAM_GENERATION 2017-09-24 15:13:24 +07:00
Patrick
3156b97f3a Fallback if container has no IP
Sometimes containers will not be assigned an IP (after reboot or due to misconfiguration). This leads to an incorrect "server <missing ip> down;" line in default.conf and crashes nginx. 
@therealgambo  provided a fix for this: https://github.com/jwilder/nginx-proxy/issues/845
2017-09-13 12:37:06 +02:00
Pan Teparak
a3b1d5b7ab Trigger Build 2017-08-27 13:19:29 +07:00
Pan Teparak
09271a333a Update Readme 2017-08-27 04:40:47 +07:00
Pan Teparak
4b22ccdc81 Add ability to opt-out dh param auto generation 2017-08-27 04:40:35 +07:00
Jason Wilder
f05f7a0ff9 Merge pull request #574 from teohhanhui/ocsp-stapling-chain
Enable OCSP stapling if certificate trust chain is provided
2017-08-16 11:53:17 -06:00
Jason Wilder
817db85aae Merge pull request #898 from remipichon/master
Do not bind upstream with 'ingress' network
2017-08-16 10:12:18 -06:00
Jason Wilder
df24d9dff5 Merge pull request #901 from brikou/patch-2
Simplify docker-compose.yml example
2017-08-13 15:57:09 -06:00
Brikou CARRE
343791b657 Simplify docker-compose.yml example
Remove `container_name` as not required
2017-08-12 07:37:05 +02:00
Remi Pichon
fff84de367 Do not bind upstream with 'ingress' network
Merging https://github.com/jwilder/nginx-proxy/pull/774 and a8ee64b059
2017-08-10 12:30:00 +02:00
Teoh Han Hui
065dd7f1ea
Fix build 2017-07-31 17:46:58 +08:00
Teoh Han Hui
dfe7677eb5
Enable OCSP stapling if certificate trust chain is provided
Previously disabled in 080a5157e6b1de3637e339a7cf54105f0316cfa7
2017-07-31 17:46:58 +08:00
Steve Kamerman
0cc71fad49
Add dynamically-computed DNS resolvers to nginx (for PR #574) 2017-07-31 17:44:27 +08:00
Jason Wilder
6bdd184d6a Merge pull request #882 from matt-hh/feature/auto-update-from-upstream-nginx
Move to 1.13 base image to get auto updates
2017-07-21 08:53:48 -06:00
Matthias Döring
febf85d7e2 Move to 1.13 base image to get auto updates
Less maintenance and more security
Resolves #880
2017-07-21 13:45:33 +02:00
耐小心
2eb2ae9c93 support fastcgi 2017-06-24 14:48:05 +08:00
耐小心
29fffd6de8 Revert "support fastcgi"
This reverts commit 8ac755e1d661ba382da9cb6864e7eac1026080bf.
2017-06-24 14:05:42 +08:00
NaiXiaoXin
8ac755e1d6 support fastcgi 2017-06-24 13:51:02 +08:00
Jason Wilder
02121df3b9 Merge pull request #589 from kamermans/feature_ssl_improvement
SSL security enhancement
2017-06-22 11:54:51 -06:00
Jason Wilder
14779ec4be Merge pull request #860 from jwilder/jw-travis
Update docker
2017-06-22 11:46:48 -06:00
Jason Wilder
a318b57408 Update docker 2017-06-22 11:36:40 -06:00
Jason Wilder
57a33aaf8b Merge pull request #849 from Neilpang/Branch_0.6.0
running proxy on host network
2017-06-22 09:50:39 -06:00
Steve Kamerman
026ba7cdac Added DHParam compatibility note 2017-06-18 21:30:59 -04:00
Steve Kamerman
761bbf9dbc Removed duplicate server_names_hash_bucket_size directive 2017-06-18 21:21:05 -04:00
Jason Wilder
c41186a3a4 Merge branch 'master' into feature_ssl_improvement 2017-06-14 16:31:12 -06:00
Jason Wilder
90b62e0abc Merge pull request #851 from Neilpang/worker_processes
fix worker_processes to "auto"
2017-06-12 19:23:29 -06:00
neil
a3cd96ead4 alpine fix worker_processes to "auto" 2017-06-13 09:11:27 +08:00
Jason Wilder
e3b6be79fc Merge pull request #847 from jwilder/appropriate-trim-earlier
Trim $host and $proto before they are used
2017-06-12 09:48:27 -06:00
neil
1867228cce fix worker_processes to "auto" 2017-06-12 15:59:55 +08:00
neilpang
a8ee64b059 running proxy on host network 2017-06-10 15:07:45 +08:00
Jason Wilder
4e4733f68e Trim $host and $proto before they are used 2017-06-09 12:55:39 -06:00
Jason Wilder
23a2c7d848 Merge pull request #812 from thueske/master
Upgrade to 1.13.0
2017-05-04 15:39:57 -06:00
Tobias Hüske
2c4102d396 Upgrade to 1.13.0 2017-05-04 18:57:00 +02:00
Jason Wilder
fed6df3756 Merge pull request #768 from thomasleveil/wildcard_cert_nohttps
TESTS: add test for wildcard cert + nohttps + default.crt
2017-05-03 17:02:40 -06:00
Jason Wilder
965c722344 Merge pull request #810 from rtalvarez/readme-typo
Fix README typo
2017-05-03 16:57:40 -06:00
Roberto Alvarez
f2487741dc Fix README typo
Fixed a small typo/error with "the a" host
2017-05-03 11:06:34 -05:00
Jason Wilder
b6a445b1a9 Merge pull request #790 from versada/fix-typo
DOC: fixed typo
2017-04-11 10:48:12 -06:00
Naglis Jonaitis
cf88817355
DOC: fixed typo 2017-04-11 11:43:47 +03:00
Jason Wilder
851b0e36cd Merge pull request #789 from matt-hh/feature/upgrade-1.11.13
Upgrade to nginx 1.11.13
2017-04-07 09:38:33 -06:00
Matthias Döring
172d79aff4 Upgrade to nginx 1.11.13 2017-04-07 12:58:49 +02:00
Jason Wilder
619943ac1e Merge pull request #770 from sischnei/http2-in-readme
Updated README to include HTTP/2.0
2017-03-18 09:38:16 -06:00
sischnei
6fd32b5a9b Updated README to include HTTP/2.0 2017-03-18 10:52:43 +01:00
Thomas LEVEIL
8414a94d59 TESTS: add test for the case in which a wildcard cert matches a container having nohttps set 2017-03-15 02:12:07 +01:00
Jason Wilder
82133865d4 Merge pull request #767 from thomasleveil/patch-1
DOC: reflect change from PR #344
2017-03-14 18:57:53 -06:00
Thomas LÉVEIL
77e022cf28 DOC: reflect change from PR #344
PR #344 changed the HTTP status code from `503` to `500`. The README.md file was not updated accordingly.
2017-03-15 01:32:47 +01:00
Thomas LEVEIL
de2f057c10 TESTS: add test for unreachable container
resulting in an empty `upstream {}` block in the generated nginx config file
2017-03-08 21:21:32 +01:00
Thomas LEVEIL
6dfc3f3f70 TESTS: add stress test when a certificate file is missing 2017-03-08 21:21:32 +01:00
Thomas LEVEIL
e25c78b00a TESTS: add pytest incremental marker to mark tests as expected to fail if previous test failed
see http://stackoverflow.com/a/12579625/107049
2017-03-08 21:21:32 +01:00
Thomas LEVEIL
a3fbaa5990 TESTS: add directory for tests featuring scenarios trying to make nginx-proxy fail 2017-03-08 21:21:32 +01:00
Steve Kamerman
6912414750 Merge branch 'master' into feature_ssl_improvement 2017-03-08 11:31:01 -05:00
Steve Kamerman
e373a5199b Merge branch 'master' into feature_ssl_improvement 2017-03-08 10:49:02 -05:00
Jason Wilder
d5deff21c5 Merge pull request #757 from thomasleveil/issue/755
TEST: fix #755: Tests fail with new Docker version scheme
2017-03-07 23:34:45 -07:00
Steve Kamerman
c2544d4c78 Merge pull request #2 from thomasleveil/PR/589
TESTS: refactor dhparam tests
2017-03-07 20:51:06 -05:00
Thomas LEVEIL
abdd5883a1 TESTS: refactor dhparam tests 2017-03-08 02:37:12 +01:00
Steve Kamerman
98b5828f83 Modified tests to include dhparams 2017-03-07 14:04:44 -05:00
Steve Kamerman
0244b4e71e Added dhparams test in new format 2017-03-07 14:04:37 -05:00
Thomas LEVEIL
99a569b2e6 fix #755: Tests fail with new Docker version scheme
Docker introduced a [new version scheme](https://blog.docker.com/2017/03/docker-enterprise-edition/). As the result, the latest docker version is now `17.03.0-ce`.
2017-03-06 23:36:16 +01:00
Steve Kamerman
ad9af2884d Merged master, fixed BATS conflict 2017-03-06 10:48:12 -05:00
Jason Wilder
6554a1cf28 Merge pull request #732 from matt-hh/feature/upgrade-1.11.10
Upgrade to nginx 1.11.10
2017-02-21 10:18:23 -07:00
Matthias Döring
7bafa37a52 Upgrade to nginx 1.11.10 2017-02-21 14:03:54 +01:00
Jason Wilder
9e31d5042f Merge pull request #735 from thomasleveil/test_events
TESTS: add a test to verify nginx conf is updated
2017-02-20 22:57:19 -07:00
Thomas LEVEIL
2aef648f3d TESTS: add a test to verify nginx conf is update on container creation/deletion 2017-02-21 03:20:38 +01:00
Jason Wilder
68816390ee Merge pull request #655 from thomasleveil/pytest
rewrite tests using docker-compose and pytest
2017-02-18 09:52:38 -07:00
Thomas LEVEIL
dd443f3eca TESTS: ignore the requirements and certs directory when collecting tests 2017-02-17 01:24:48 +01:00
Thomas LEVEIL
9620be91fa TESTS: separated containers: fix indentation and remove dependency over jwilder/whoami image 2017-02-17 01:11:11 +01:00
Thomas LEVEIL
85370fa31f TESTS: improve error reporting for failed to run docker-compose commands 2017-02-17 00:54:14 +01:00
Thomas LEVEIL
6069bc53cd TESTS: replace old test suite with the new one
get rid of Bats definitively
2017-02-17 00:29:30 +01:00
Thomas LEVEIL
250a01d235 TESTS: add tests for IPv6 support 2017-02-16 23:27:28 +01:00
Thomas LEVEIL
78375632dd TESTS: adjust Travis-CI config (upgrade to docker 1.13 and no more docker-compose) 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
a6e4f7f3ad TESTS: adjust Travis-CI config 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
02f9e29a31 TESTS: make pytest verbose by default 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
9f26efdf86 TESTS: add tests for IPv6 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
e6b9d2f5e7 TESTS: show skip reason in test summary 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
eeb27f7b50 TESTS: fix failure raising when cannot get nginx-proxy nginx configuration file 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
43d8ba0e78 TESTS: add tests for using nginx and docker-gen in separated containers 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
a533ff6aff TESTS: add support for resolving containers IP address from domain names in the form *.<container name>.container.docker 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
743d9801e9 TESTS: make test now runs tests from a docker container 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
e6bc7e6043 TESTS: rename nginx-proxy-tester.sh to test.sh 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
87926db4c3 TESTS: upgrade to docker python module 2.0.2 and docker-compose 1.11.1 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
fdde850fc2 TESTS: pytest checks the existance of the jwilder/nginx-proxy:test image 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
3e92192dc4 TESTS: add test for multiple networks 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
3ecae2f020 TESTS: the docker_compose pytest fixture is now responsible for attaching the container running pytest to all docker networks 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
dcf8dd4682 TESTS: refactor conftest.py 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
3f88ba8903 TESTS: refactor conftest.py 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
10cd3e5f81 TESTS: refactor conftest.py 2017-02-16 21:40:54 +01:00
Steve Kamerman
ad57c48465 Fixed HTTPS test 2017-02-16 21:40:54 +01:00
Steve Kamerman
912f5615dc Reorganized certs 2017-02-16 21:40:54 +01:00
Steve Kamerman
8995ba02ba Fixed assertion bug 2017-02-16 21:40:54 +01:00
Steve Kamerman
d0e5870060 Added certs, fixed typos 2017-02-16 21:40:54 +01:00
Steve Kamerman
65371e513a Renamed wildcard cert and key 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
087592df2c TESTS: simplify nginx-proxy-tester.sh 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
9d8f0db268 TESTS: remove containers after each test 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
541ce48292 fixup! TESTS: add support for compose syntax v2 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
889dfea4ca TESTS: adjust logging (when using pytest -s) 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
150365e784 fixup! TESTS: add test for custom default configuration 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
23d361d1da TESTS: reorganize tests into subfolders 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
eeed150858 README: fix instruction for overriding default custom conf 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
8aa3158952 TESTS: add test for custom default location configuration 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
713149df8e TESTS: add test for custom default configuration 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
ee5b0f96ad TESTS: add test for custom per vhost configuration in location block 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
a283c95e2e TESTS: nginxproxy fixture can provide the nginx generated conf in tests 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
f512a56faf TESTS: add test for custom per vhost configuration 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
2425574958 TESTS: add test for custom proxy-wide configuration 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
73fda600b3 TESTS: add verbosity 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
0d00bd2322 TESTS: fix passing pytests args to nginx-proxy-tester script 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
399e19e2dc TESTS: add support for compose syntax v2 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
787fa28799 TESTS: add script to run the test suite from a docker container 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
7a4cae050c TESTS: abort test suite if the 'jwilder/nginx-proxy:test' image is missing 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
86be8a9424 TESTS: fix retry delay 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
0f1bb344ff TESTS: port tests for #590 to docker-compose test 2017-02-16 21:40:54 +01:00
Thomas LEVEIL
197d793a25 TESTS: rewrite tests using pytest and docker-compose
Experimentation to see if it is worth the effort
2017-02-16 21:40:54 +01:00
Jason Wilder
985c46d8b5 Merge pull request #679 from thomasleveil/issue-677
regexp: use sha1 for upstream only if regexp is used
2017-02-16 12:11:06 -07:00
Jason Wilder
2d588871f0 Merge pull request #719 from thomasleveil/optional_IPv6
optional IPv6 support
2017-02-16 12:09:30 -07:00
Jason Wilder
c5c6a6149d Merge pull request #724 from rindek/master
First load docker-gen, then nginx
2017-02-16 09:42:22 -07:00
Jason Wilder
d7a2aa7823 Merge pull request #676 from matt-hh/feature/alpine-readme
Add info about alpine base image
2017-02-16 09:40:12 -07:00
Jacek Jakubik
0e94e1e4bd First load docker-gen, then nginx 2017-02-16 10:47:38 +01:00
Thomas LEVEIL
f0951df040 optional IPv6 support
Fix #127 and fix #717 by improving #713
2017-02-15 11:50:16 +01:00
Jason Wilder
c0f60087c3 Merge pull request #713 from schrieveslaach/master
Add IPv6 listen address
2017-02-13 09:25:33 -07:00
Marc Schreiber
8b67b2182f Add IPv6 listen address 2017-02-11 13:28:34 +01:00
Jason Wilder
b9e2053c38 Merge pull request #704 from yosmanyga/patch-1
Fixed typo
2017-02-06 12:43:16 -07:00
Jason Wilder
cdb38d06ba Merge pull request #706 from matt-hh/feature/upgrade-1.11.9
Upgrade to nginx 1.11.9
2017-02-06 12:38:41 -07:00
Matthias Döring
15cbb5debf Upgrade to nginx 1.11.9 2017-02-05 21:40:29 +01:00
Yosmany Garcia
d8f66e2411 Fixed typo 2017-02-03 16:19:40 -08:00
Jason Wilder
c0a0300f12 Merge pull request #696 from kevindoveton/master
Update EXPOSE link in README.md
2017-01-26 23:20:40 -07:00
Kevin Doveton
4c06935b62 Update README.md 2017-01-27 13:24:24 +10:30
Kevin Doveton
2be48ec982 Update EXPOSE link in README.md 2017-01-27 13:16:27 +10:30
Steve Kamerman
d320b43476 Merged conflict in BATS SSL test 2017-01-26 13:46:11 -05:00
Thomas LEVEIL
3f6381d0fa regexp: use sha1 for upstream only if regexp is used
avoid confusions such as in #677
2017-01-14 11:40:33 +01:00
Jason Wilder
2c5d4ee3a9 Merge pull request #590 from kamermans/feature_nohttps
Added HTTPS_METHOD=nohttps to disable SSL site
2017-01-13 16:16:16 -07:00
Steve Kamerman
276b4dbe3e Merge branch 'master' into feature_nohttps 2017-01-13 13:07:03 -05:00
Steve Kamerman
83a28f47d7 Fixed long server name comment, improved dhparam check 2017-01-12 17:43:13 -05:00
Steve Kamerman
b0de1f19d3 Updated to use 256-bit dhparam in tests 2017-01-12 17:25:31 -05:00
Steve Kamerman
7d253dd0f3 Allow passing DHPARAM_BITS via env, lower bits to 256 for unit tests 2017-01-12 14:55:25 -05:00
Steve Kamerman
dffc0c47cf Tweak test for reliability on Travis-CI 2017-01-12 10:52:56 -05:00
Steve Kamerman
c219822cd8 Typos 2017-01-12 01:53:36 -05:00
Steve Kamerman
f73a52afaf Added BATS tests for dhparam generation 2017-01-12 01:45:46 -05:00
Steve Kamerman
7c0f7b9449 Fixed Alpine image, removed dhparams gen from test units 2017-01-12 00:21:39 -05:00
Steve Kamerman
6242403d33 Added dhparam support for alpine variant 2017-01-11 23:04:24 -05:00
Steve Kamerman
ebfe5e9c17 Added note about background generation 2017-01-11 22:49:55 -05:00
Steve Kamerman
dfdd67f5a4 Implemented background dhparam generation 2017-01-11 22:43:09 -05:00
Steve Kamerman
f186815c2d Merged upstream 2017-01-11 22:42:35 -05:00
Matthias Döring
8dfe5ae384 Add info about alpine base image
For todo in #558
2017-01-11 16:57:37 +01:00
Jason Wilder
3d20c626c8 Merge pull request #359 from sw-double/master
Set appropriate X-Forwarded-Ssl header
2017-01-10 09:21:19 -07:00
Jason Wilder
f1ccde2fe3 Merge pull request #671 from thomasleveil/regex-end-of-line
fix crash when using end-of-string symbol `$` in regex VIRTUAL_HOST
2017-01-10 09:20:28 -07:00
Konstantin L
16c9853dc2 Set appropriate X-Forwarded-Ssl header. 2017-01-10 15:44:02 +01:00
Thomas LÉVEIL
019fa89c53 add comment to ease debugging 2017-01-10 10:10:46 +01:00
Thomas LEVEIL
1bfc1c85ce fix regexp in VIRTUAL_HOST using end-of-string matching () 2017-01-08 01:49:05 +01:00
Thomas LEVEIL
d8658bd8d9 TEST: wildcards-hosts.bats - add a test which uses regexp end-of-string 2017-01-07 23:50:54 +01:00
Thomas LEVEIL
a3c6a272f1 TEST: wildcards-hosts.bats - showcase http://foo.bar.why.not.bats-to-infinity-and-beyond/ 2017-01-07 23:33:54 +01:00
Jason Wilder
f837d1256a Merge pull request #670 from thomasleveil/parallel-tests
TRAVIS: run debian and alpine tests in parallel
2017-01-07 14:04:40 -07:00
Thomas LEVEIL
8d017504c0 TRAVIS: run debian and alpine tests in parallel 2017-01-07 21:07:34 +01:00
Jason Wilder
31a0a844e3 Merge pull request #558 from matt-hh/alpine
Add alpine base image
2017-01-05 18:05:38 -07:00
Jason Wilder
e9a5519fb0 Merge pull request #611 from MichaelSp/patch-1
add link to letsencrypt-nginx-proxy-companion
2016-12-30 15:26:40 -07:00
Jason Wilder
a7bc426b61 Merge pull request #666 from matt-hh/feature/upgrade-1.11.8
Upgrade to nginx 1.11.8
2016-12-28 18:42:02 -07:00
Matthias Döring
c1d93d112a Upgrade nginx-alpine to 1.11.8 2016-12-29 00:18:06 +01:00
Matthias Döring
2a8f4554a7 Merge branch 'master' into alpine
* master:
  Updated nginx to 1.11.6
  Travis-CI's apt-get doesn't have --allow-downgrades yet, which is annoying because --force-yes is deprecated
  Put --allow-downgrades in the right place
  Upgrade docker-engine and allow downgrades
  Clarified a couple parts in the README
  Comment typo
  Added httpoxy test
  Updated README to reflect X-Forwarded-Port
  Implemented more advanced webserver with routing and request header echoing, added header tests
  Honor upstream forwarded port if available
  add ssl_session_tickets to default site
  Replace "replace" to "trimSuffix"
  do not enable HSTS for subdomains
  Remove proxy-tier network in favor of the default.
  Added X-Forwarded-Port
  Add docker-compose file for separate containers.
  connect to uWSGI backends
2016-12-29 00:02:05 +01:00
Matthias Döring
004cc3cb8c Upgrade to nginx 1.11.8 2016-12-28 23:38:47 +01:00
Jason Wilder
6186fbf8b0 Merge pull request #653 from cramaker/feature/nginx-1.11.6
Updated nginx to 1.11.6
2016-12-08 12:59:56 -07:00
Cody Ramaker
b66398d1bf Updated nginx to 1.11.6 2016-12-08 13:24:49 -06:00
Jason Wilder
40872c9b43 Merge pull request #649 from kamermans/travisci_fix
Travis-CI - upgrade docker-engine and allow downgrades
2016-12-05 10:10:27 -07:00
Steve Kamerman
59b8806859 Travis-CI's apt-get doesn't have --allow-downgrades yet, which is annoying because --force-yes is deprecated 2016-12-05 09:33:44 -05:00
Steve Kamerman
271729aaaa Put --allow-downgrades in the right place 2016-12-05 09:29:08 -05:00
Steve Kamerman
dc910107cf Upgrade docker-engine and allow downgrades 2016-12-05 09:21:39 -05:00
Steve Kamerman
fc7653bf3d Merge branch 'master' into feature_nohttps 2016-12-05 09:06:39 -05:00
Jason Wilder
e60fefad27 Merge pull request #587 from kamermans/feature_x_forwarded_port
Added X-Forwarded-Port and proxy headers tests
2016-12-02 11:17:44 -07:00
Jason Wilder
51aeee9a2c Merge pull request #619 from max-wilkinson/master
Clarified a couple parts in the README
2016-11-27 13:19:34 -07:00
Max Wilkinson
fddae94ed8 Clarified a couple parts in the README 2016-10-28 14:46:37 -04:00
Michael
1a608eaefb add link to letsencrypt-nginx-proxy-companion 2016-10-22 14:31:57 +02:00
Steve Kamerman
b0de80d46b Moved config edits from Dockerfile to template 2016-10-03 10:21:31 -04:00
Steve Kamerman
8534185b0c Added newline to config 2016-10-03 10:05:55 -04:00
Steve Kamerman
8cf0b75d80 Updated README with HTTPS_METHOD=nohttps 2016-10-01 11:25:11 -04:00
Steve Kamerman
374b1256cd Add HTTPS_METHOD=https to disable SSL site 2016-10-01 11:22:48 -04:00
Steve Kamerman
c091d08fee Updated docs for issue #562 2016-09-29 22:24:06 -04:00
Steve Kamerman
ebbf7a7b74 Expanded documentation in SSL/TLS support 2016-09-29 21:57:28 -04:00
Steve Kamerman
d3a0da451a TLSv1 End-of-life pushed to June 30, 2018, rolled back for compatibility 2016-09-29 21:35:37 -04:00
Steve Kamerman
c51c9980cf Removed TLS 1.0 as it is considered unsafe and must be disabled for PCI compliance 2016-09-29 19:52:20 -04:00
Steve Kamerman
6f2b3f1c54 Issue #586 Removed DES-based SSL ciphers 2016-09-29 17:10:17 -04:00
Steve Kamerman
0b1e9e56e1 Issue #535 Added default 2048-bit dhparam.pem file 2016-09-29 16:48:18 -04:00
Steve Kamerman
9ef0bb3356 Comment typo 2016-09-29 16:06:53 -04:00
Steve Kamerman
b9bf183df2 Added httpoxy test 2016-09-29 15:43:07 -04:00
Steve Kamerman
7422539f20 Updated README to reflect X-Forwarded-Port 2016-09-29 15:42:49 -04:00
Steve Kamerman
112aad39b6 Implemented more advanced webserver with routing and request header echoing, added header tests 2016-09-29 15:36:01 -04:00
Steve Kamerman
124b8cd757 Honor upstream forwarded port if available 2016-09-29 11:33:21 -04:00
Steve Kamerman
6ebbdb10c7 Merge branch 'master' into feature_x_forwarded_port 2016-09-29 11:26:51 -04:00
Jason Wilder
0670a13d92 Merge pull request #581 from chulkilee/ssl_session_tickets
add ssl_session_tickets to default site
2016-09-24 20:22:16 -06:00
Chulki Lee
4661bf4dd9 add ssl_session_tickets to default site
Fixes #580
2016-09-23 21:58:09 -07:00
Jason Wilder
760936d9ea Merge pull request #572 from pvlg/patch-2
Replace "replace" to "trimSuffix"
2016-09-17 09:58:27 -06:00
pvlg
fe9a538ec8 Replace "replace" to "trimSuffix"
I have a domain key-mydomain.com. When I add domain www.key-mydomain.com with ssl cert I did not get the desired result. Function replace cut name ssl cert "www.key-mydomain.com.key" to "www-mydomain.com".
2016-09-17 16:53:01 +03:00
Jason Wilder
f02bc98a6e Merge pull request #566 from mplx/patch/hsts-no-subdomains
do not enable HSTS for subdomains
2016-09-13 09:18:27 -06:00
mplx
37323320c8 do not enable HSTS for subdomains 2016-09-12 09:46:59 +02:00
Jason Wilder
40d433ae80 Merge pull request #493 from ryneeverett/docker_compose_separate_containers
Add docker-compose file for separate containers.
2016-09-09 14:18:06 -06:00
Jason Wilder
ec7169c112 Merge pull request #323 from pabra/master
connect to uWSGI backends
2016-09-09 14:16:08 -06:00
Matthias Döring
7d05f0d924 Add nginx alpine to update-dependencies task 2016-09-03 12:05:27 +02:00
Matthias Döring
ba55d1a0b6 Add alpine base image
- Inspired by #408
- Possible solution for #543
2016-09-02 17:11:36 +02:00
Jason Wilder
e95d3e9fdf Merge pull request #555 from freakinruben/patch-1
Update ciphers and HTST settings to get A+ rating
2016-09-01 09:21:56 -06:00
Ruben
87879c1ee2 Update ciphers and HTST settings to get A+ rating
The default config gets you an 'A' rating. Cipher settings are copied from [Mozilla SSL Configartion Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.10.1&openssl=1.0.1t&hsts=yes&profile=intermediate)
2016-09-01 11:34:56 +02:00
Jason Wilder
579bc1bf6e Merge pull request #518 from huiwang/nginx_upgrade
upgrade nginx to 1.11.3
2016-08-25 20:55:54 -06:00
hwang
176b78943e upgrade nginx to 1.11.3 in makefile 2016-08-25 21:46:57 +02:00
Jason Wilder
a6287d7df1 Merge pull request #545 from auchri/patch-1
Update .travis.yml
2016-08-25 12:30:16 -06:00
Christoph
c113e7ac82 Update .travis.yml 2016-08-25 20:22:35 +02:00
hwang
03e863d838 upgrade nginx to 1.11.3 2016-08-25 20:16:37 +02:00
ryneeverett
478ad17adb Remove proxy-tier network in favor of the default.
As @huiwang pointed out, using a custom network is unnecessary since the
default bridge network works just as well.
2016-07-29 17:23:10 -04:00
hwang
86c408bbdd upgrade nginx to 1.11.0 2016-07-29 21:17:14 +02:00
Steve Kamerman
2e29168d92 Added X-Forwarded-Port 2016-07-21 11:23:35 -04:00
Jason Wilder
30128cfda0 Merge pull request #511 from kamermans/master
Mitigate httpoxy attack by suppressing `Proxy` request header
2016-07-19 12:15:35 -06:00
Steve Kamerman
fd127517b9 Added comments about httpoxy 2016-07-19 11:03:41 -04:00
Steve Kamerman
357d58ad97 Mitigate httpoxy attack (httpoxy.org, CVE-2016-(5385-5388,1000109-1000110) 2016-07-18 13:34:37 -04:00
ryneeverett
5f684d4fc5 Add docker-compose file for separate containers.
Demonstrate that this pattern works.

This is based on the example at
<https://github.com/fatk/docker-letsencrypt-nginx-proxy-companion-examples/blob/master/docker-compose/v2/simple-site/docker-compose.yml>.
2016-07-13 17:31:53 -04:00
Jason Wilder
a2ab8363ca Merge pull request #483 from jwilder/jw-docker-gen
Update to docker-gen 0.7.3
2016-06-13 08:18:07 -06:00
Jason Wilder
8ed5ab38b8 Update to docker-gen 0.7.3 2016-06-13 00:24:21 -06:00
Jason Wilder
db3ef67a7f Merge pull request #482 from jwilder/jw-revert
Revert 9c93efa
2016-06-13 00:21:38 -06:00
Jason Wilder
580517725f Revert 9c93efa 2016-06-13 00:10:49 -06:00
Jason Wilder
d1e6e1c0be Merge pull request #344 from schmunk42/feature/error-code
changed error code for non-usable/default SSL cert, fixes #341
2016-06-12 15:54:40 -06:00
Jason Wilder
fc619d63ad Merge pull request #460 from kumy/patch-1
Fix a typo in comment
2016-06-12 15:28:40 -06:00
Jason Wilder
c36b42933d Merge pull request #462 from kamermans/master
Disable HSTS when HTTPS_METHOD=noredirect
2016-06-12 15:28:08 -06:00
Jason Wilder
a0dee5c833 Remove -only-exposed from separate container instructions 2016-06-12 15:25:32 -06:00
Jason Wilder
fdfb0becd2 Merge pull request #480 from jwilder/jw-certs
Fix template error when /etc/nginx/certs does not exist
2016-06-12 14:28:35 -06:00
Jason Wilder
fdf93cafba Add docker-compose.yml v2 example 2016-06-12 14:10:46 -06:00
Jason Wilder
b325dad98d Remove -only-exposed from Procfile 2016-06-12 14:10:46 -06:00
Jason Wilder
9c93efaef9 Fix template error when /etc/nginx/certs does not exist 2016-06-12 14:10:40 -06:00
Jason Wilder
adf4573de4 Remove Circle CI badge 2016-06-12 10:45:59 -06:00
Jason Wilder
8393a57f1f Merge pull request #477 from jwilder/jw-nginx
Nginx updates
2016-06-10 20:22:07 -06:00
Jason Wilder
9b0138f944 Remove docker compose v2 notes 2016-06-10 20:11:58 -06:00
Jason Wilder
c256f31728 Update nginx 1.9.15 2016-06-10 20:08:10 -06:00
Steve Kamerman
da3e257843 Removed HSTS when HTTPS_METHOD=noredirect, added tests, improved docs wrt HSTS 2016-05-19 23:20:43 -04:00
Steve Kamerman
3d77979efb Expanded documentation on HTTPS_METHOD 2016-05-19 23:16:19 -04:00
kumy
8c76ea9f9b Fix a typo in comment 2016-05-17 01:46:46 +02:00
Jason Wilder
0ef8dca98c Merge pull request #443 from jwilder/jw-circle
Remove circle.yml
2016-05-04 16:52:41 -06:00
Jason Wilder
c30640b024 Remove circle.yml
Using travis now
2016-05-04 16:05:03 -06:00
Jason Wilder
e964b9d29f Merge pull request #441 from jwilder/jw-0.7.1
Update to docker-gen 0.7.1
2016-05-04 16:03:52 -06:00
Jason Wilder
d40d1d4052 Update to docker-gen 0.7.1 2016-05-04 14:51:30 -06:00
Jason Wilder
5b9264d945 Merge pull request #298 from kamermans/master
Added env var to disable SSL redirect
2016-05-01 17:45:45 -06:00
Jason Wilder
1c98df2de4 Merge pull request #337 from baptistedonaux/master
Support Overlay Network
2016-05-01 17:35:48 -06:00
Jason Wilder
a72c7e6e20 Add docker hub badges 2016-04-22 09:56:21 -06:00
Steve Kamerman
786a2375aa Using different path method to hopefully fix cert volume issue in CI systems 2016-04-20 12:47:14 -04:00
Steve Kamerman
f296b273ce Normalized indentation, added explicit CERT_NAME 2016-04-20 12:33:21 -04:00
Steve Kamerman
1d0a5d9194 Added SSL/HTTPS tests 2016-04-20 12:20:08 -04:00
Steve Kamerman
f294582ae8 Merge branch 'master' of https://github.com/jwilder/nginx-proxy 2016-04-20 11:05:20 -04:00
Baptiste Donaux
f66934567e Merge remote-tracking branch 'upstream/master' 2016-04-05 09:22:31 +02:00
Jason Wilder
48c31836be Merge pull request #407 from jwilder/jw-travis
Add travis.yml
2016-04-04 17:27:12 -06:00
Jason Wilder
30e2da01e9 Add travis.yml
From #312
2016-04-04 17:18:05 -06:00
Jason Wilder
59f8d4301a Merge pull request #406 from jwilder/jw-forego
Switch forego downloads
2016-04-04 16:56:49 -06:00
Jason Wilder
a54af7d73b Switch forego downloads
Official ddollar site has been down for a while and not sure if it's
coming back up or not.
2016-04-04 16:48:52 -06:00
Baptiste Donaux
6928d62e1e Upgrade Docker dependency from 1.9 to 1.10 2016-03-24 14:40:50 +01:00
Baptiste Donaux
88cc096aaf [FIX] expose port for separated containers test 2016-03-24 11:16:22 +01:00
Baptiste Donaux
6b88365465 Merge remote-tracking branch 'upstream/master' 2016-03-24 08:41:23 +01:00
Jason Wilder
6adc04be83 Merge pull request #396 from pitkley/docker-gen-0.7.0
Update to docker-gen 0.7.0
2016-03-23 11:33:00 -06:00
Pit Kleyersburg
86aea653c8 Update to docker-gen 0.7.0
Since [1] some timings seem to have changed. This caused the unit tests
to fail intermittently, from my testings especially on Ubuntu systems
(much less often on e.g. Arch).

This commit adds the `dockergen_wait_for_event` helper-function to try
and wait for the configuration to be generated by docker-gen before
continuing on with the actual tests themselves.

Additionally, at the end of every test file, all containers spun up by
the bats-tests will be stopped. This required adding the `bats-type`
label to every container started during the bats-tests.

The stopping of the containers reduces the amount of events docker-gen
has to process, thus resulting in lower wait times for the generation to
happen.

[1]: 50435652b1
2016-03-23 17:45:34 +01:00
Baptiste Donaux
74fb2acc4b Merge remote-tracking branch 'upstream/master' 2016-03-23 08:52:58 +01:00
Jason Wilder
f2966c5db1 Merge pull request #374 from mrohland/master
updated to nginx 1.9.12
2016-03-21 16:28:18 -06:00
Mike Rohland
1f8b480365 moved nginx to 1.9.12 2016-03-21 22:47:01 +01:00
Steve Kamerman
9671e0c1a2 Merge branch 'master' of https://github.com/jwilder/nginx-proxy 2016-03-18 13:47:11 -04:00
Jason Wilder
87d0c68aac Merge pull request #371 from gabriel403/patch-1
Mention the docker-compose networking issues
2016-03-03 15:35:43 -07:00
Baptiste Donaux
b42cd85570 Unit test - removed network creation 2016-02-26 09:12:11 +01:00
Baptiste Donaux
47631eb5de Merge remote-tracking branch 'upstream/master' 2016-02-25 09:59:04 +01:00
Baptiste Donaux
2b418319f2 Test to support overlay networks 2016-02-25 09:58:17 +01:00
Baptiste Donaux
163d1ce174 Upgrade docker-gen from 0.5.0 to 0.7.0 2016-02-25 08:26:41 +01:00
Baptiste Donaux
ebab7cf2b9 [TEMPLATE] fix variable call 2016-02-23 13:59:30 +01:00
Gabriel Baker
d849e5f5d0 Linkify the issue ref 2016-02-23 07:25:32 +00:00
Gabriel Baker
8c6ec27547 Mention the docker-compose issues 2016-02-23 07:23:27 +00:00
Jason Wilder
2b24c1712a Merge pull request #342 from schmunk42/feature/updated-nginx
updated nginx to 1.9.9
2016-02-14 16:20:48 -07:00
Baptiste Donaux
658e20f661 Support container in one network shared with current container 2016-02-05 09:16:43 +01:00
Baptiste Donaux
9341f54e51 Merge pull request #1 from appropriate/overlay-support
Update README wording for overlay networking
2016-01-22 08:44:43 +01:00
Tobias Munk
b4e5f780e3 changed error code for non-usable/default SSL cert, fixes #341 2016-01-21 12:31:03 +01:00
Mike Dillon
5c828cb50a Update README wording for overlay networking 2016-01-20 23:30:55 -08:00
Tobias Munk
627f789f19 updated nginx to 1.9.9 2016-01-20 23:26:21 +01:00
Baptiste Donaux
2eb977589c Complete documentation 2016-01-20 08:54:57 +01:00
Steve Kamerman
99963b4ab0 Merge branch 'master' of https://github.com/jwilder/nginx-proxy 2016-01-18 16:37:03 -05:00
Baptiste Donaux
13bb99c4d2 Update README - Nginx proxy must use host network to support overlay network 2016-01-17 12:35:29 +01:00
Baptiste Donaux
a66115f560 Use new Network interface to support new overlay network 2016-01-17 12:29:55 +01:00
Baptiste Donaux
deb4050fa4 UPGRADE docker-gen release from 0.4.2 to 0.5.0 2016-01-17 12:28:59 +01:00
pabra
51c219d651 connect to uWSGI backends 2015-12-22 21:20:44 +01:00
Jason Wilder
f42bf9175d Merge pull request #314 from thomasleveil/circleci-docker1.9.1
CircleCI: make tests run on Docker 1.9.1
2015-12-15 07:38:25 -07:00
Thomas LEVEIL
c11af183d8 CircleCI: make tests run on Docker 1.9.1
See CircleCI announcement https://discuss.circleci.com/t/docker-1-9-1-is-available/1009
2015-12-14 09:25:21 +01:00
Steve Kamerman
4a95d84d0d Trying to trigger CI 2015-12-02 21:46:05 -05:00
Steve Kamerman
58fc298dea Semantic change to trigger CI rebuild 2015-12-02 00:29:32 -05:00
Steve Kamerman
4bf7f888d0 Documented HTTPS_METHOD 2015-11-20 17:47:08 -05:00
Steve Kamerman
97c6340a9f Implemented HTTPS noredir 2015-11-20 17:37:06 -05:00
Steve Kamerman
9dd6ad8503 First try at HTTPS_METHOD 2015-11-20 16:53:50 -05:00
Jason Wilder
205b391677 Merge pull request #277 from TravisDart/master
Typo fix.
2015-10-30 15:11:37 -06:00
Travis Dart
8225015c85 Typo fix. 2015-10-30 15:48:01 -05:00
Jason Wilder
588cdf6266 Update email address 2015-10-29 13:32:09 -06:00
Jason Wilder
1e7136470e Merge pull request #276 from appropriate/nginx-1.9.6
Update to nginx 1.9.6
2015-10-29 13:31:34 -06:00
Mike Dillon
5404938797 Update to nginx 1.9.6 2015-10-28 21:29:39 -07:00
Jason Wilder
defed25585 Merge pull request #265 from thomasleveil/makefile
makefile: add target to update docker images tests depend on
2015-10-17 16:12:05 -06:00
Thomas LEVEIL
66d401c075 makefile: update docker images before running tests 2015-10-14 19:42:59 +00:00
Jason Wilder
1ea74e9b0a Merge pull request #264 from jwilder/jw-tests
Testing fixups
2015-10-14 11:02:33 -06:00
Jason Wilder
db7a5ab2ae Use make test from circle.yml 2015-10-14 10:48:43 -06:00
Jason Wilder
e09550febb Add Makefile
Make it easier to run tests.
2015-10-14 10:29:22 -06:00
Jason Wilder
1786edd87d Update .dockerignore to recent file additions 2015-10-14 10:19:50 -06:00
Jason Wilder
1670bccd17 Merge pull request #262 from mariusGundersen/master
Use trim to handle VIRTUAL_HOST values with spaces
2015-10-14 09:56:33 -06:00
Jason Wilder
b075fcf250 Merge pull request #260 from appropriate/test-trap-term
Trap TERM to allow quicker exit of web containers
2015-10-14 09:55:49 -06:00
Marius Gundersen
3e530a0784 Use latest docker-gen 2015-10-13 21:49:19 +02:00
Marius Gundersen
1e0b930174 trim whitespace from host and port
based on latest docker-gen
2015-10-13 21:48:59 +02:00
Mike Dillon
098b058b0d Trap TERM to allow quicker exit of web containers 2015-10-12 21:53:52 -07:00
Jason Wilder
f819a4e2e7 Merge pull request #257 from appropriate/osx-fix-test-suite
Fix running test suite under Docker Machine
2015-10-12 22:43:19 -06:00
Mike Dillon
1d9db94535 Use docker:1.7 image to match CircleCI's version 2015-10-12 21:16:55 -07:00
Mike Dillon
5c6a63568d Remove --rm from docker run commands
Trying to avoid "Failed to destroy btrfs snapshot" errors on CircleCI
2015-10-12 20:59:53 -07:00
Mike Dillon
6e7ff3451b Remove commented out line 2015-10-11 11:27:38 -07:00
Mike Dillon
ec393e3d18 Pull additional images needed for Circle CI 2015-10-11 11:05:08 -07:00
Mike Dillon
30e565f0ed Check "docker version" from a sibling container 2015-10-11 11:04:00 -07:00
Mike Dillon
b1a08843da Avoid breakage from IFS being reset in "run"
See https://github.com/sstephenson/bats/issues/89
2015-10-11 10:55:17 -07:00
Mike Dillon
0d2bc38179 Run curl commands from a sibling container 2015-10-09 23:11:03 -07:00
Mike Dillon
2cba8a8d5f Avoid xargs -r
The -r option is not available on OS X
2015-10-09 23:10:38 -07:00
Mike Dillon
5fe9411d88 Change line endings to Unix 2015-10-09 23:07:46 -07:00
Jason Wilder
df6778f673 Merge pull request #256 from thomasleveil/patch-1
fix CircleCI badge link
2015-10-09 09:53:38 -06:00
Thomas LÉVEIL
99560da65c fix CircleCI badge link 2015-10-09 15:33:29 +02:00
Jason Wilder
495b0ad8b6 Add circleci build status badge 2015-10-08 22:08:04 -06:00
Jason Wilder
289a519dce Merge pull request #246 from thomasleveil/fix/197
add test suite. See #197
2015-10-08 22:01:15 -06:00
Thomas LEVEIL
92be4b6d54 doc 2015-09-30 23:57:13 +00:00
Thomas LEVEIL
865b14e029 tests: fix minor bug in docker_wait_for_log 2015-09-30 23:56:25 +00:00
Thomas LEVEIL
b4d1acbf2d tests: refactoring 2015-09-30 23:40:04 +00:00
Thomas LEVEIL
4bd30f5d2c add test suite. See #197
This test suite is implemented using [bats](https://github.com/sstephenson/bats).

Not all features are tested. For instance ssl features and custom nginx config are missing. Probably others.

This test suite won't work with TravisCI. Too many evenings were wasted trying to overcome [issues](http://stackoverflow.com/questions/32846800/travis-fails-to-stop-docker-containers) that arises only on the TravisCI platform. However it runs on [CircleCI](https://circleci.com) which is also free for opensource projects.
2015-09-29 23:46:36 +00:00
116 changed files with 4192 additions and 75 deletions

View File

@ -1,2 +1,6 @@
.git
.dockerignore
circle.yml
Makefile
README.md
test

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

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
**/__pycache__/
**/.cache/
.idea/

22
.travis.yml Normal file
View File

@ -0,0 +1,22 @@
dist: trusty
sudo: required
env:
matrix:
- TEST_TARGET: test-debian
- TEST_TARGET: test-alpine
before_install:
- 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 -
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- sudo apt-get update
- sudo apt-get -y install docker-ce
- docker version
- docker info
# prepare docker test requirements
- make update-dependencies
script:
- make $TEST_TARGET

View File

@ -1,5 +1,5 @@
FROM nginx:1.9.5
MAINTAINER Jason Wilder jwilder@litl.com
FROM nginx:1.17.8
LABEL maintainer="Jason Wilder mail@jasonwilder.com"
# Install wget and install/updates certificates
RUN apt-get update \
@ -9,26 +9,29 @@ RUN apt-get update \
&& apt-get clean \
&& rm -r /var/lib/apt/lists/*
# Configure Nginx and apply fix for very long server names
RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
# Install Forego
RUN wget -P /usr/local/bin https://godist.herokuapp.com/projects/ddollar/forego/releases/current/linux-amd64/forego \
&& chmod u+x /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
ENV DOCKER_GEN_VERSION 0.4.1
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 \
&& tar -C /usr/local/bin -xvzf docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
&& rm /docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz
COPY network_internal.conf /etc/nginx/
COPY . /app/
WORKDIR /app/
ENV DOCKER_HOST unix:///tmp/docker.sock
VOLUME ["/etc/nginx/certs"]
VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"]
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["forego", "start", "-r"]

34
Dockerfile.alpine Normal file
View File

@ -0,0 +1,34 @@
FROM nginx:1.17.8-alpine
LABEL maintainer="Jason Wilder mail@jasonwilder.com"
# Install wget and install/updates certificates
RUN apk add --no-cache --virtual .run-deps \
ca-certificates bash wget openssl \
&& update-ca-certificates
# Configure Nginx and apply fix for very long server names
RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
# Install 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
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 \
&& 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
COPY network_internal.conf /etc/nginx/
COPY . /app/
WORKDIR /app/
ENV DOCKER_HOST unix:///tmp/docker.sock
VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"]
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["forego", "start", "-r"]

16
Makefile Normal file
View File

@ -0,0 +1,16 @@
.SILENT :
.PHONY : test-debian test-alpine test
update-dependencies:
test/requirements/build.sh
test-debian: update-dependencies
docker build -t jwilder/nginx-proxy:test .
test/pytest.sh
test-alpine: update-dependencies
docker build -f Dockerfile.alpine -t jwilder/nginx-proxy:test .
test/pytest.sh
test: test-debian test-alpine

View File

@ -1,2 +1,2 @@
dockergen: docker-gen -watch -notify "nginx -s reload" /app/nginx.tmpl /etc/nginx/conf.d/default.conf
nginx: nginx
dockergen: docker-gen -watch -only-exposed -notify "nginx -s reload" /app/nginx.tmpl /etc/nginx/conf.d/default.conf

263
README.md
View File

@ -1,4 +1,6 @@
![nginx 1.9.5](https://img.shields.io/badge/nginx-1.9.5-brightgreen.svg) ![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)
![latest 0.7.0](https://img.shields.io/badge/latest-0.7.0-green.svg?style=flat)
![nginx 1.17.8](https://img.shields.io/badge/nginx-1.17.8-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-proxy sets up a container running nginx and [docker-gen][1]. docker-gen generates reverse proxy configs for nginx and reloads nginx when containers are started and stopped.
@ -14,9 +16,56 @@ Then start any containers you want proxied with an env var `VIRTUAL_HOST=subdoma
$ docker run -e VIRTUAL_HOST=foo.bar.com ...
The containers being proxied must [expose](https://docs.docker.com/reference/run/#expose-incoming-ports) the port to be proxied, either by using the `EXPOSE` directive in their `Dockerfile` or by using the `--expose` flag to `docker run` or `docker create`.
The containers being proxied must [expose](https://docs.docker.com/engine/reference/run/#expose-incoming-ports) the port to be proxied, either by using the `EXPOSE` directive in their `Dockerfile` or by using the `--expose` flag to `docker run` or `docker create` and be in the same network. By default, if you don't pass the --net flag when your nginx-proxy container is created, it will only be attached to the default bridge network. This means that it will not be able to connect to containers on networks other than bridge.
Provided your DNS is setup to forward foo.bar.com to the a host running nginx-proxy, the request will be routed to a container with the VIRTUAL_HOST env var set.
Provided your DNS is setup to forward foo.bar.com to the host running nginx-proxy, the request will be routed to a container with the VIRTUAL_HOST env var set.
### Image variants
The nginx-proxy images are available in two flavors.
#### jwilder/nginx-proxy:latest
This image uses the debian:jessie based nginx image.
$ docker pull jwilder/nginx-proxy:latest
#### jwilder/nginx-proxy:alpine
This image is based on the nginx:alpine image. Use this image to fully support HTTP/2 (including ALPN required by recent Chrome versions). A valid certificate is required as well (see eg. below "SSL Support using letsencrypt" for more info).
$ docker pull jwilder/nginx-proxy:alpine
### Docker Compose
```yaml
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
whoami:
image: jwilder/whoami
environment:
- VIRTUAL_HOST=whoami.local
```
```shell
$ docker-compose up
$ curl -H "Host: whoami.local" localhost
I'm 5b129ab83266
```
### IPv6 support
You can activate the IPv6 support for the nginx-proxy container by passing the value `true` to the `ENABLE_IPV6` environment variable:
$ docker run -d -p 80:80 -e ENABLE_IPV6=true -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
### Multiple Ports
@ -33,9 +82,61 @@ If you need to support multiple virtual hosts for a container, you can separate
You can also use wildcards at the beginning and the end of host name, like `*.bar.com` or `foo.bar.*`. Or even a regular expression, which can be very useful in conjunction with a wildcard DNS service like [xip.io](http://xip.io), using `~^foo\.bar\..*\.xip\.io` will match `foo.bar.127.0.0.1.xip.io`, `foo.bar.10.0.2.2.xip.io` and all other given IPs. More information about this topic can be found in the nginx documentation about [`server_names`](http://nginx.org/en/docs/http/server_names.html).
### Multiple Networks
With the addition of [overlay networking](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) in Docker 1.9, your `nginx-proxy` container may need to connect to backend containers on multiple networks. By default, if you don't pass the `--net` flag when your `nginx-proxy` container is created, it will only be attached to the default `bridge` network. This means that it will not be able to connect to containers on networks other than `bridge`.
If you want your `nginx-proxy` container to be attached to a different network, you must pass the `--net=my-network` option in your `docker create` or `docker run` command. At the time of this writing, only a single network can be specified at container creation time. To attach to other networks, you can use the `docker network connect` command after your container is created:
```console
$ docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro \
--name my-nginx-proxy --net my-network jwilder/nginx-proxy
$ docker network connect my-other-network my-nginx-proxy
```
In this example, the `my-nginx-proxy` container will be connected to `my-network` and `my-other-network` and will be able to proxy to other containers attached to those networks.
### Internet vs. Local Network Access
If you allow traffic from the public internet to access your `nginx-proxy` container, you may want to restrict some containers to the internal network only, so they cannot be accessed from the public internet. On containers that should be restricted to the internal network, you should set the environment variable `NETWORK_ACCESS=internal`. By default, the *internal* network is defined as `127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16`. To change the list of networks considered internal, mount a file on the `nginx-proxy` at `/etc/nginx/network_internal.conf` with these contents, edited to suit your needs:
```
# These networks are considered "internal"
allow 127.0.0.0/8;
allow 10.0.0.0/8;
allow 192.168.0.0/16;
allow 172.16.0.0/12;
# Traffic from all other networks will be rejected
deny all;
```
When internal-only access is enabled, external clients with be denied with an `HTTP 403 Forbidden`
> If there is a load-balancer / reverse proxy in front of `nginx-proxy` that hides the client IP (example: AWS Application/Elastic Load Balancer), you will need to use the nginx `realip` module (already installed) to extract the client's IP from the HTTP request headers. Please see the [nginx realip module configuration](http://nginx.org/en/docs/http/ngx_http_realip_module.html) for more details. This configuration can be added to a new config file and mounted in `/etc/nginx/conf.d/`.
### SSL Backends
If you would like to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container.
If you would like the reverse proxy to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container.
> Note: If you use `VIRTUAL_PROTO=https` and your backend container exposes port 80 and 443, `nginx-proxy` will use HTTPS on port 80. This is almost certainly not what you want, so you should also include `VIRTUAL_PORT=443`.
### uWSGI Backends
If you would like to connect to uWSGI backend, set `VIRTUAL_PROTO=uwsgi` on the
backend container. Your backend container should then listen on a port rather
than a socket and expose that port.
### FastCGI Backends
If you would like to connect to FastCGI backend, set `VIRTUAL_PROTO=fastcgi` on the
backend container. Your backend container should then listen on a port rather
than a socket and expose that port.
### FastCGI File Root Directory
If you use fastcgi,you can set `VIRTUAL_ROOT=xxx` for your root directory
### Default Host
@ -51,6 +152,14 @@ image and the official [nginx](https://registry.hub.docker.com/_/nginx/) image.
You may want to do this to prevent having the docker socket bound to a publicly exposed container service.
You can demo this pattern with docker-compose:
```console
$ docker-compose --file docker-compose-separate-containers.yml up
$ curl -H "Host: whoami.local" localhost
I'm 5b129ab83266
```
To run nginx proxy as a separate container you'll need to have [nginx.tmpl](https://github.com/jwilder/nginx-proxy/blob/master/nginx.tmpl) on your host system.
First start nginx with a volume:
@ -64,13 +173,20 @@ Then start the docker-gen container with the shared volume and template:
$ docker run --volumes-from nginx \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
-v $(pwd):/etc/docker-gen/templates \
-t jwilder/docker-gen -notify-sighup nginx -watch -only-exposed /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
-t jwilder/docker-gen -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
```
Finally, start your containers with `VIRTUAL_HOST` environment variables.
$ docker run -e VIRTUAL_HOST=foo.bar.com ...
### SSL Support using letsencrypt
[letsencrypt-nginx-proxy-companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion) is a lightweight companion container for the nginx-proxy. It allows the creation/renewal of Let's Encrypt certificates automatically.
Set `DHPARAM_GENERATION` environment variable to `false` to disabled Diffie-Hellman parameters completely. This will also ignore auto-generation made by `nginx-proxy`.
The default value is `true`
$ docker run -e DHPARAM_GENERATION=false ....
### SSL Support
SSL is supported using single host, wildcard and SNI certificates using naming conventions for
@ -85,15 +201,37 @@ hosts in use. The certificate and keys should be named after the virtual host w
`.key` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` should have a
`foo.bar.com.crt` and `foo.bar.com.key` file in the certs directory.
If you are running the container in a virtualized environment (Hyper-V, VirtualBox, etc...),
/path/to/certs must exist in that environment or be made accessible to that environment.
By default, Docker is not able to mount directories on the host machine to containers running in a virtual machine.
#### Diffie-Hellman Groups
If you have Diffie-Hellman groups enabled, the files should be named after the virtual host with a
Diffie-Hellman groups are enabled by default, with a pregenerated key in `/etc/nginx/dhparam/dhparam.pem`.
You can mount a different `dhparam.pem` file at that location to override the default cert.
To use custom `dhparam.pem` files per-virtual-host, the files should be named after the virtual host with a
`dhparam` suffix and `.pem` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com`
should have a `foo.bar.com.dhparam.pem` file in the certs directory.
should have a `foo.bar.com.dhparam.pem` file in the `/etc/nginx/certs` directory.
> NOTE: If you don't mount a `dhparam.pem` file at `/etc/nginx/dhparam/dhparam.pem`, one will be generated
at startup. Since it can take minutes to generate a new `dhparam.pem`, it is done at low priority in the
background. Once generation is complete, the `dhparam.pem` is saved on a persistent volume and nginx
is reloaded. This generation process only occurs the first time you start `nginx-proxy`.
> COMPATIBILITY WARNING: The default generated `dhparam.pem` key is 2048 bits for A+ security. Some
> older clients (like Java 6 and 7) do not support DH keys with over 1024 bits. In order to support these
> clients, you must either provide your own `dhparam.pem`, or tell `nginx-proxy` to generate a 1024-bit
> key on startup by passing `-e DHPARAM_BITS=1024`.
In the separate container setup, no pregenerated key will be available and neither the
[jwilder/docker-gen](https://index.docker.io/u/jwilder/docker-gen/) image nor the offical
[nginx](https://registry.hub.docker.com/_/nginx/) image will generate one. If you still want A+ security
in a separate container setup, you'll have to generate a 2048 bits DH key file manually and mount it on the
nginx container, at `/etc/nginx/dhparam/dhparam.pem`.
#### Wildcard Certificates
Wildcard certificates and keys should be name after the domain name with a `.crt` and `.key` extension.
Wildcard certificates and keys should be named after the domain name with a `.crt` and `.key` extension.
For example `VIRTUAL_HOST=foo.bar.com` would use cert name `bar.com.crt` and `bar.com.key`.
#### SNI
@ -103,14 +241,37 @@ 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`
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
The SSL cipher configuration is based on [mozilla nginx intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) which
should provide compatibility with clients back to Firefox 1, Chrome 1, IE 7, Opera 5, Safari 1,
Windows XP IE8, Android 2.3, Java 7. The configuration also enables HSTS, and SSL
session caches.
The default SSL cipher configuration is based on the [Mozilla intermediate profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29) version 5.0 which
should provide compatibility with clients back to Firefox 27, Android 4.4.2, Chrome 31, Edge, IE 11 on Windows 7,
Java 8u31, OpenSSL 1.0.1, Opera 20, and Safari 9. Note that the DES-based TLS ciphers were removed for security.
The configuration also enables HSTS, PFS, OCSP stapling and SSL session caches. Currently TLS 1.2 and 1.3
are supported.
The behavior for the proxy when port 80 and 443 are exposed is as follows:
If you don't require backward compatibility, you can use the [Mozilla modern profile](https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility)
profile instead by including the environment variable `SSL_POLICY=Mozilla-Modern` to the nginx-proxy container or to your container.
This profile is compatible with clients back to Firefox 63, Android 10.0, Chrome 70, Edge 75, Java 11,
OpenSSL 1.1.1, Opera 57, and Safari 12.1. Note that this profile is **not** compatible with any version of Internet Explorer.
Other policies available through the `SSL_POLICY` environment variable are [`Mozilla-Old`](https://wiki.mozilla.org/Security/Server_Side_TLS#Old_backward_compatibility)
and the [AWS ELB Security Policies](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html)
`AWS-TLS-1-2-2017-01`, `AWS-TLS-1-1-2017-01`, `AWS-2016-08`, `AWS-2015-05`, `AWS-2015-03` and `AWS-2015-02`.
Note that the `Mozilla-Old` policy should use a 1024 bits DH key for compatibility but this container generates
a 2048 bits key. The [Diffie-Hellman Groups](#diffie-hellman-groups) section details different methods of bypassing
this, either globally or per virtual-host.
The default behavior for the proxy when port 80 and 443 are exposed is as follows:
* If a container has a usable cert, port 80 will redirect to 443 for that container so that HTTPS
is always preferred when available.
@ -119,7 +280,24 @@ is always preferred when available.
Note that in the latter case, a browser may get an connection error as no certificate is available
to establish a connection. A self-signed or generic cert named `default.crt` and `default.key`
will allow a client browser to make a SSL connection (likely w/ a warning) and subsequently receive
a 503.
a 500.
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` must be specified on each container for which you want to
override the default behavior. If `HTTPS_METHOD=noredirect` is used, Strict Transport Security (HSTS)
is disabled to prevent HTTPS users from being redirected by the client. If you cannot get to the HTTP
site after changing this setting, your browser has probably cached the HSTS policy and is automatically
redirecting you back to HTTPS. You will need to clear your browser's HSTS cache or use an incognito
window / different browser.
By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable
`HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`.
*WARNING*: HSTS will force your users to visit the HTTPS version of your site for the `max-age` time -
even if they type in `http://` manually. The only way to get to an HTTP site after receiving an HSTS
response is to clear your browser's HSTS cache.
### Basic Authentication Support
@ -155,10 +333,17 @@ proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
```
***NOTE***: If you provide this file it will replace the defaults; you may want to check the .tmpl file to make sure you have all of the needed options.
***NOTE***: The default configuration blocks the `Proxy` HTTP request header from being sent to downstream servers. This prevents attackers from using the so-called [httpoxy attack](http://httpoxy.org). There is no legitimate reason for a client to send this header, and there are many vulnerable languages / platforms (`CVE-2016-5385`, `CVE-2016-5386`, `CVE-2016-5387`, `CVE-2016-5388`, `CVE-2016-1000109`, `CVE-2016-1000110`, `CERT-VU#797896`).
#### Proxy-wide
To add settings on a proxy-wide basis, add your configuration file under `/etc/nginx/conf.d` using a name ending in `.conf`.
@ -216,5 +401,53 @@ If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=e
#### Per-VIRTUAL_HOST location default configuration
If you want most of your virtual hosts to use a default single `location` block configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default_location` file. This file
will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}` file associated with it.
will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}_location` file associated with it.
#### Pre-VIRTUAL_HOST custom location blocks
In some circumstances you may want to override nginx's default `/` location block behavior. Typically, this block acts as a catch-all in order to forward requests not already matched by a specific `location` block directly onto your container as-is.
To provide your own location blocks and bypass the automatic generation of them, simply add your location blocks to a configuration file file under `/etc/nginx/vhost.d` like in the other Per-VIRTUAL_HOST sections except with the suffix `_locations`. Notice the 's' to make the filename plural.
The contents of this file will replace all auto-generated location blocks. Additionally, this file will take priority over the previously described location configuration.
When using location overrides, you are responsible for handling any requests that should be forwarded to your container. Passing a request to your container is done using the `proxy_pass` instruction within your defined location blocks. `proxy_pass` expects a qualified hostname in order
to forward a request. By default, nginx-proxy aliases containers to the defined `VIRTUAL_HOST` name. So if you launch your container with a `VIRTUAL_HOST` value of `app.example.com`, then forwarding a request to your container would look something like this:
```
location / {
proxy_pass http://app.example.com;
}
```
If you are using an SSL-enabled container, you would use `https://` in place of `http://`. You could include any number of other location blocks for nginx to consider and even forward requests to external hosts when they match certain conditions. You can also use any other rules and instructions
available to nginx location blocks.
### Contributing
Before submitting pull requests or issues, please check github to make sure an existing issue or pull request is not already open.
#### Running Tests Locally
To run tests, you need to prepare the docker image to test which must be tagged `jwilder/nginx-proxy:test`:
docker build -t jwilder/nginx-proxy:test . # build the Debian variant image
and call the [test/pytest.sh](test/pytest.sh) script.
Then build the Alpine variant of the image:
docker build -f Dockerfile.alpine -t jwilder/nginx-proxy:test . # build the Alpline variant image
and call the [test/pytest.sh](test/pytest.sh) script again.
If your system has the `make` command, you can automate those tasks by calling:
make test
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)

8
dhparam.pem.default Normal file
View File

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAzB2nIGzpVq7afJnKBm1X0d64avwOlP2oneiKwxRHdDI/5+6TpH1P
F8ipodGuZBUMmupoB3D34pu2Qq5boNW983sm18ww9LMz2i/pxhSdB+mYAew+A6h6
ltQ5pNtyn4NaKw1SDFkqvde3GNPhaWoPDbZDJhpHGblR3w1b/ag+lTLZUvVwcD8L
jYS9f9YWAC6T7WxAxh4zvu1Z0I1EKde8KYBxrreZNheXpXHqMNyJYZCaY2Hb/4oI
EL65qZq1GCWezpWMjhk6pOnV5gbvqfhoazCv/4OdRv6RoWOIYBNs9BmGho4AtXqV
FYLdYDhOvN4aVs9Ir+G8ouwiRnix24+UewIBAg==
-----END DH PARAMETERS-----

View File

@ -0,0 +1,23 @@
version: '2'
services:
nginx:
image: nginx
container_name: nginx
ports:
- "80:80"
volumes:
- /etc/nginx/conf.d
dockergen:
image: jwilder/docker-gen
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes_from:
- nginx
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
whoami:
image: jwilder/whoami
environment:
- VIRTUAL_HOST=whoami.local

14
docker-compose.yml Normal file
View File

@ -0,0 +1,14 @@
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
whoami:
image: jwilder/whoami
environment:
- VIRTUAL_HOST=whoami.local

View File

@ -2,7 +2,7 @@
set -e
# Warn if the DOCKER_HOST socket does not exist
if [[ $DOCKER_HOST == unix://* ]]; then
if [[ $DOCKER_HOST = unix://* ]]; then
socket_file=${DOCKER_HOST#unix://}
if ! [ -S $socket_file ]; then
cat >&2 <<-EOT
@ -14,6 +14,18 @@ if [[ $DOCKER_HOST == unix://* ]]; then
fi
fi
# Generate dhparam file if required
# Note: if $DHPARAM_BITS is not defined, generate-dhparam.sh will use 2048 as a default
# Note2: if $DHPARAM_GENERATION is set to false in environment variable, dh param generator will skip completely
/app/generate-dhparam.sh $DHPARAM_BITS $DHPARAM_GENERATION
# Compute the DNS resolvers for use in the templates - if the IP contains ":", it's IPv6 and must be enclosed in []
export RESOLVERS=$(awk '$1 == "nameserver" {print ($2 ~ ":")? "["$2"]": $2}' ORS=' ' /etc/resolv.conf | sed 's/ *$//g')
if [ "x$RESOLVERS" = "x" ]; then
echo "Warning: unable to determine DNS resolvers for nginx" >&2
unset RESOLVERS
fi
# If the user has run the default command and the socket doesn't exist, fail
if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then
exit 1

52
generate-dhparam.sh Executable file
View File

@ -0,0 +1,52 @@
#!/bin/bash -e
# The first argument is the bit depth of the dhparam, or 2048 if unspecified
DHPARAM_BITS=${1:-2048}
GENERATE_DHPARAM=${2:-true}
# If a dhparam file is not available, use the pre-generated one and generate a new one in the background.
# Note that /etc/nginx/dhparam is a volume, so this dhparam will persist restarts.
PREGEN_DHPARAM_FILE="/app/dhparam.pem.default"
DHPARAM_FILE="/etc/nginx/dhparam/dhparam.pem"
GEN_LOCKFILE="/tmp/dhparam_generating.lock"
# The hash of the pregenerated dhparam file is used to check if the pregen dhparam is already in use
PREGEN_HASH=$(md5sum $PREGEN_DHPARAM_FILE | cut -d" " -f1)
if [[ -f $DHPARAM_FILE ]]; then
CURRENT_HASH=$(md5sum $DHPARAM_FILE | cut -d" " -f1)
if [[ $PREGEN_HASH != $CURRENT_HASH ]]; then
# There is already a dhparam, and it's not the default
echo "Custom dhparam.pem file found, generation skipped"
exit 0
fi
if [[ -f $GEN_LOCKFILE ]]; then
# Generation is already in progress
exit 0
fi
fi
if [[ $GENERATE_DHPARAM =~ ^[Ff][Aa][Ll][Ss][Ee]$ ]]; then
echo "Skipping Diffie-Hellman parameters generation and Ignoring pre-generated dhparam.pem"
exit 0
fi
cat >&2 <<-EOT
WARNING: $DHPARAM_FILE was not found. A pre-generated dhparam.pem will be used for now while a new one
is being generated in the background. Once the new dhparam.pem is in place, nginx will be reloaded.
EOT
# Put the default dhparam file in place so we can start immediately
cp $PREGEN_DHPARAM_FILE $DHPARAM_FILE
touch $GEN_LOCKFILE
# Generate a new dhparam in the background in a low priority and reload nginx when finished (grep removes the progress indicator).
(
(
nice -n +5 openssl dhparam -out $DHPARAM_FILE.tmp $DHPARAM_BITS 2>&1 \
&& mv $DHPARAM_FILE.tmp $DHPARAM_FILE \
&& echo "dhparam generation complete, reloading nginx" \
&& nginx -s reload
) | grep -vE '^[\.+]+'
rm $GEN_LOCKFILE
) &disown

6
network_internal.conf Normal file
View File

@ -0,0 +1,6 @@
# Only allow traffic from internal clients
allow 127.0.0.0/8;
allow 10.0.0.0/8;
allow 192.168.0.0/16;
allow 172.16.0.0/12;
deny all;

View File

@ -1,3 +1,8 @@
{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
{{ $external_http_port := coalesce $.Env.HTTP_PORT "80" }}
{{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }}
{{ define "upstream" }}
{{ if .Address }}
{{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}}
@ -5,13 +10,61 @@
# {{ .Container.Node.Name }}/{{ .Container.Name }}
server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
{{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}}
{{ else }}
{{ else if .Network }}
# {{ .Container.Name }}
server {{ .Address.IP }}:{{ .Address.Port }};
server {{ .Network.IP }}:{{ .Address.Port }};
{{ end }}
{{ else }}
{{ else if .Network }}
# {{ .Container.Name }}
server {{ .Container.IP }} down;
{{ if .Network.IP }}
server {{ .Network.IP }} down;
{{ else }}
server 127.0.0.1 down;
{{ end }}
{{ end }}
{{ end }}
{{ define "ssl_policy" }}
{{ if eq .ssl_policy "Mozilla-Modern" }}
ssl_protocols TLSv1.3;
{{/* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 /*}}
{{/* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) /*}}
{{/* explicitly set ngnix default value in order to allow single servers to override the global http value */}}
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
{{ else if eq .ssl_policy "Mozilla-Intermediate" }}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
{{ else if eq .ssl_policy "Mozilla-Old" }}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
{{ else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256';
ssl_prefer_server_ciphers on;
{{ else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }}
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
ssl_prefer_server_ciphers on;
{{ else if eq .ssl_policy "AWS-2016-08" }}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
ssl_prefer_server_ciphers on;
{{ else if eq .ssl_policy "AWS-2015-05" }}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
{{ else if eq .ssl_policy "AWS-2015-03" }}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
{{ else if eq .ssl_policy "AWS-2015-02" }}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA';
ssl_prefer_server_ciphers on;
{{ end }}
{{ end }}
@ -22,6 +75,13 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
'' $scheme;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
default $http_x_forwarded_port;
'' $server_port;
}
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
@ -29,6 +89,20 @@ map $http_upgrade $proxy_connection {
'' close;
}
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
{{ if (exists "/etc/nginx/dhparam/dhparam.pem") }}
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
{{ end }}
# Set appropriate X-Forwarded-Ssl header
map $scheme $proxy_x_forwarded_ssl {
default off;
https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
@ -37,6 +111,14 @@ log_format vhost '$host $remote_addr - $remote_user [$time_local] '
access_log off;
{{/* Get the SSL_POLICY defined by this container, falling back to "Mozilla-Intermediate" */}}
{{ $ssl_policy := or ($.Env.SSL_POLICY) "Mozilla-Intermediate" }}
{{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
{{ if $.Env.RESOLVERS }}
resolver {{ $.Env.RESOLVERS }};
{{ end }}
{{ if (exists "/etc/nginx/proxy.conf") }}
include /etc/nginx/proxy.conf;
{{ else }}
@ -49,22 +131,38 @@ proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
{{ end }}
{{ $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }}
{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 80;
access_log /var/log/nginx/access.log vhost;
listen {{ $external_http_port }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_http_port }};
{{ end }}
{{ $access_log }}
return 503;
}
{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 443 ssl http2;
access_log /var/log/nginx/access.log vhost;
listen {{ $external_https_port }} ssl http2;
{{ if $enable_ipv6 }}
listen [::]:{{ $external_https_port }} ssl http2;
{{ end }}
{{ $access_log }}
return 503;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/default.crt;
ssl_certificate_key /etc/nginx/certs/default.key;
}
@ -72,18 +170,36 @@ server {
{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}
upstream {{ $host }} {
{{ $host := trim $host }}
{{ $is_regexp := hasPrefix "~" $host }}
{{ $upstream_name := when $is_regexp (sha1 $host) $host }}
# {{ $host }}
upstream {{ $upstream_name }} {
{{ range $container := $containers }}
{{ $addrLen := len $container.Addresses }}
{{/* If only 1 port exposed, use that */}}
{{ if eq $addrLen 1 }}
{{ $address := index $container.Addresses 0 }}
{{ template "upstream" (dict "Container" $container "Address" $address) }}
{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
{{ else }}
{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
{{ $address := where $container.Addresses "Port" $port | first }}
{{ template "upstream" (dict "Container" $container "Address" $address) }}
{{ range $knownNetwork := $CurrentContainer.Networks }}
{{ range $containerNetwork := $container.Networks }}
{{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
## Can be connected with "{{ $containerNetwork.Name }}" network
{{/* If only 1 port exposed, use that */}}
{{ if eq $addrLen 1 }}
{{ $address := index $container.Addresses 0 }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
{{ else }}
{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
{{ $address := where $container.Addresses "Port" $port | first }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{ end }}
{{ else }}
# Cannot connect to network of this container
server 127.0.0.1 down;
{{ end }}
{{ end }}
{{ end }}
{{ end }}
}
@ -92,7 +208,23 @@ upstream {{ $host }} {
{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
{{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }}
{{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }}
{{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}}
{{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }}
{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}}
{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }}
{{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
{{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
{{/* Get the first cert name defined by containers w/ the same vhost */}}
{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
@ -101,32 +233,58 @@ upstream {{ $host }} {
{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}}
{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}}
{{ $vhostCert := replace $vhostCert ".crt" "" -1 }}
{{ $vhostCert := replace $vhostCert ".key" "" -1 }}
{{ $vhostCert := trimSuffix ".crt" $vhostCert }}
{{ $vhostCert := trimSuffix ".key" $vhostCert }}
{{/* Use the cert specifid on the container or fallback to the best vhost match */}}
{{/* Use the cert specified on the container or fallback to the best vhost match */}}
{{ $cert := (coalesce $certName $vhostCert) }}
{{ if (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
{{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
{{ if $is_https }}
{{ if eq $https_method "redirect" }}
server {
server_name {{ $host }};
listen 80 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
return 301 https://$host$request_uri;
listen {{ $external_http_port }} {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_http_port }} {{ $default_server }};
{{ end }}
{{ $access_log }}
# Do not HTTPS redirect Let'sEncrypt ACME challenge
location /.well-known/acme-challenge/ {
auth_basic off;
allow all;
root /usr/share/nginx/html;
try_files $uri =404;
break;
}
location / {
return 301 https://$host$request_uri;
}
}
{{ end }}
server {
server_name {{ $host }};
listen 443 ssl http2 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
listen {{ $external_https_port }} ssl http2 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
{{ end }}
{{ $access_log }}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
{{ if eq $network_tag "internal" }}
# Only allow traffic from internal clients
include /etc/nginx/network_internal.conf;
{{ end }}
{{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
ssl_prefer_server_ciphers on;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }};
ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }};
@ -135,7 +293,15 @@ server {
ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
{{ end }}
add_header Strict-Transport-Security "max-age=31536000";
{{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
{{ end }}
{{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }}
add_header Strict-Transport-Security "{{ trim $hsts }}" always;
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
@ -143,25 +309,52 @@ server {
include /etc/nginx/vhost.d/default;
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_locations" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_locations" $host}};
{{ else }}
location / {
proxy_pass {{ $proto }}://{{ $host }};
{{ if eq $proto "uwsgi" }}
include uwsgi_params;
uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ else if eq $proto "fastcgi" }}
root {{ trim $vhost_root }};
include fastcgi_params;
fastcgi_pass {{ trim $upstream_name }};
{{ else if eq $proto "grpc" }}
grpc_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ else }}
proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ end }}
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
}
{{ end }}
}
{{ else }}
{{ end }}
{{ if or (not $is_https) (eq $https_method "noredirect") }}
server {
server_name {{ $host }};
listen 80 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
listen {{ $external_http_port }} {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:80 {{ $default_server }};
{{ end }}
{{ $access_log }}
{{ if eq $network_tag "internal" }}
# Only allow traffic from internal clients
include /etc/nginx/network_internal.conf;
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
@ -169,26 +362,44 @@ server {
include /etc/nginx/vhost.d/default;
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_locations" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_locations" $host}};
{{ else }}
location / {
proxy_pass {{ $proto }}://{{ $host }};
{{ if eq $proto "uwsgi" }}
include uwsgi_params;
uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ else if eq $proto "fastcgi" }}
root {{ trim $vhost_root }};
include fastcgi_params;
fastcgi_pass {{ trim $upstream_name }};
{{ else if eq $proto "grpc" }}
grpc_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ else }}
proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ end }}
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
}
{{ end }}
}
{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name {{ $host }};
listen 443 ssl http2 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
return 503;
listen {{ $external_https_port }} ssl http2 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
{{ end }}
{{ $access_log }}
return 500;
ssl_certificate /etc/nginx/certs/default.crt;
ssl_certificate_key /etc/nginx/certs/default.key;

107
test/README.md Normal file
View File

@ -0,0 +1,107 @@
Nginx proxy test suite
======================
Install requirements
--------------------
You need [python 2.7](https://www.python.org/) and [pip](https://pip.pypa.io/en/stable/installing/) installed. Then run the commands:
requirements/build.sh
pip install -r requirements/python-requirements.txt
If you can't install those requirements on your computer, you can alternatively use the _pytest.sh_ script which will run the tests from a Docker container which has those requirements.
Prepare the nginx-proxy test image
----------------------------------
docker build -t jwilder/nginx-proxy:test ..
or if you want to test the alpine flavor:
docker build -t jwilder/nginx-proxy:test -f Dockerfile.alpine ..
make sure to tag that test image exactly `jwilder/nginx-proxy:test` or the test suite won't work.
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](http://doc.pytest.org/en/latest/). The [conftest.py](conftest.py) file will be automatically loaded by pytest and will provide you with two useful pytest [fixtures](http://doc.pytest.org/en/latest/fixture.html#fixture):
- 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](https://docs.docker.com/compose/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](https://docker-py.readthedocs.io/en/2.0.2/client.html#client-reference).
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 `jwilder/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 the `web1` container
- `f00.web1.container.docker` will resolve to the IP address of the `web1` container
- `anything.whatever.web2.container.docker` will resolve to the IP address of the `web2` container
Otherwise, domain names are resoved as usual using your system DNS resolver.
### nginxproxy fixture
The `nginxproxy` fixture will provide you with a replacement for the python [requests](https://pypi.python.org/pypi/requests/) module. This replacement will just repeat up to 30 times a requests if it receives the HTTP error 404 or 502. This error occurs when you try to send queries to nginx-proxy too early after the container creation.
Also this requests replacement is preconfigured to use the Certificate Authority root certificate [certs/ca-root.crt](certs/) 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 ran the `requirements/build.sh` script earlier, you built a [`web`](requirements/README.md) 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`](certs/) script. Pytest will be able to validate any certificate issued from this script.

81
test/certs/README.md Normal file
View File

@ -0,0 +1,81 @@
create_server_certificate.sh
============================
`create_server_certificate.sh` is a script helping with issuing server certificates that can be used to provide TLS on web servers.
It also creates a Certificate Authority (CA) root key and certificate. This CA root certificate can be used to validate the server certificates it generates.
For instance, with _curl_:
curl --cacert /somewhere/ca-root.crt https://www.example.com/
or with _wget_:
wget --certificate=/somewhere/ca-root.crt https://www.example.com/
or with the python _requests_ module:
import requests
r = requests.get("https://www.example.com", verify="/somewhere/ca-root.crt")
Usage
-----
### Simple domain
Create a server certificate for domain `www.example.com`:
./create_server_certificate.sh www.example.com
Will produce:
- `www.example.com.key`
- `www.example.com.crt`
### Multiple domains
Create a server certificate for main domain `www.example.com` and alternative domains `example.com`, `foo.com` and `bar.com`:
./create_server_certificate.sh www.example.com foo.com bar.com
Will produce:
- `www.example.com.key`
- `www.example.com.crt`
### Wildcard domain
Create a server certificate for wildcard domain `*.example.com`:
./create_server_certificate.sh "*.example.com"
Note that you need to use quotes around the domain string or the shell would expand `*`.
Will produce:
- `*.example.com.key`
- `*.example.com.crt`
Again, to prevent your shell from expanding `*`, use quotes. i.e.: `cat "*.example.com.crt"`.
Such a server certificate would be valid for domains:
- `foo.example.com`
- `bar.example.com`
but not for domains:
- `example.com`
- `foo.bar.example.com`
### Wildcard domain on multiple levels
While you can technically create a server certificate for wildcard domain `*.example.com` and alternative name `*.*.example.com`, client implementations generally do not support multiple wildcards in a domain name.
For instance, a python script using urllib3 would fail to validate domain `foo.bar.example.com` presenting a certificate with name `*.*.example.com`. It is advised to stay away from producing such certificates.
If you want to give it a try:
./create_server_certificate.sh "*.example.com" "*.*.example.com"
Such a server certificate would be valid for domains:
- `foo.example.com`
- `bar.example.com`
- `foo.bar.example.com`

21
test/certs/ca-root.crt Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZDCCAkygAwIBAgIJAJmW6Ju6iJNNMA0GCSqGSIb3DQEBCwUAMD8xHzAdBgNV
BAoMFm5naW54LXByb3h5IHRlc3Qgc3VpdGUxHDAaBgNVBAMME3d3dy5uZ2lueC1w
cm94eS50bGQwHhcNMTcwMTEwMDAwODUxWhcNMjcwMTA4MDAwODUxWjA/MR8wHQYD
VQQKDBZuZ2lueC1wcm94eSB0ZXN0IHN1aXRlMRwwGgYDVQQDDBN3d3cubmdpbngt
cHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAndjE3OPr
48hIOQigk/HejrowsQDLNfkkc6vej0J983rJitGTgBfxqq27fOPfqhE5bi1M5JDk
KkrOrSitxCJLgpq+4Ls9/RXg8skZFHRAQbNwuKBehaDkPdamJ0i3dv6e4kZy41oI
RqxQ/MKdminC4LShFZvPoKeh9ae7w1MgB2/4E68LO66bYiHlLNL7ENViSHhLyCmt
qIE7kdV9jgn2NuVJ37m6/6SNQ3GBiIjEW+ooRQ3HEVKBCismcwq80+BD5VS/yW18
KqX8m4sBM+IgZbcOqrV+APMbGvd8iNJgQSSQC/r0Wscgt7UeggVYKDazjDSPvLUE
FUN5wEmydkP2AQIDAQABo2MwYTAdBgNVHQ4EFgQUJL59pHomt+8dUNxv8HgrYjKf
OA8wHwYDVR0jBBgwFoAUJL59pHomt+8dUNxv8HgrYjKfOA8wDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBABALxY96YqsZ
CL2crzY0FIGhfjLE7P3mtUGklUpFu7xyI6mGUyL1nJYSnHB5IEV6QLhVVUE/CojI
crXorQWBDkx26AgCt/eIOdvPYC0JDeXiIhH6sld3yH7JGwGqJkfXaUUfUkuwMae7
mMIEG9e6vfSh/YNTRxs0KBjBcXHHl5K+Dz4h9r14OqnQFqVFZaR6T6td44tDDNhn
beW8iIfCWRqDsnvIcJzLa2QR4onmJSw5DaSeFFaKefhdHEzEBZntLfyFbjRYHT/O
+BRdewhg6rSDkGLcL8n/ZnRLOa+xmegjQ/Op94OmWO3TfXOITJAtkaO2YVZoyek8
T6ckVovq4zU=
-----END CERTIFICATE-----

27
test/certs/ca-root.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAndjE3OPr48hIOQigk/HejrowsQDLNfkkc6vej0J983rJitGT
gBfxqq27fOPfqhE5bi1M5JDkKkrOrSitxCJLgpq+4Ls9/RXg8skZFHRAQbNwuKBe
haDkPdamJ0i3dv6e4kZy41oIRqxQ/MKdminC4LShFZvPoKeh9ae7w1MgB2/4E68L
O66bYiHlLNL7ENViSHhLyCmtqIE7kdV9jgn2NuVJ37m6/6SNQ3GBiIjEW+ooRQ3H
EVKBCismcwq80+BD5VS/yW18KqX8m4sBM+IgZbcOqrV+APMbGvd8iNJgQSSQC/r0
Wscgt7UeggVYKDazjDSPvLUEFUN5wEmydkP2AQIDAQABAoIBAQCDM4zetix6lx1B
GuSuVFrTc/vJBInkgQRFiVRi67fZS/R+CJl73WsonWO7+YUNzWdZJxpE2hJs/OUx
lSBqaL8u/gUuszRhS3BBHdpU4BQRCF/ndpVaqVNN+z78ZDrrE9Vo63nPdCRw6gYf
MnzhiVjMghdq6Kn6NZwvno45WrzCsIbrrQ4zU+S2PhG8MTA53jzqqQ8mUSJX0lAl
6b9+1aWA0d0Jnk3M3doaFU/Dlnz3n6kkx0AdqNe8bdsFrPfwsrF+dwGx04SGgLmK
V2OjIDFYYGtiHp3PJ9IYIA32ij+UloSDDZ2BxXkma8Zilw04ytY5l8tlk2ZDWTD9
U2MXxjmBAoGBAMmmI19I/asTPjljlqzrOsrdRkklJvnCHgy/yw9u3nMfkJ0lLGAp
mZoCqJIEsAqlLGM5bOjKy3KQ3n2SBX3mz7/RajnpJRTnNLeJIPAAXHN9TDyKcWRo
Los6xHN7YMSLYKs4HMihXp9Yu4Ms88/8nO/01nufjN0rTgFnWdL0WfxJAoGBAMhk
Qm92ukMmbrXSrV0WF+eFooHwgPmUWZ1oZY5ZHmO3FCuSBHiICGrWKmdbcG6H5zmZ
oFZ0unsvk2Yjl+/+tntxr/dwp6Q+chsqkLms8GE76NWEO8qn4hQNywkFgpKlPci3
n5IqpuQ2DpJ1PAQkwgZD/5rSscNidNMezXO5Uvv5AoGBALR291kjXcJpKlr6AbMn
oipD9c8obMVBMNuAGh7pvjORoD7DMf+tu0XV8z8a6uHcCOmUTx/XvlP9yuDeegO/
OVYV+NdzDDi04r0PAGdKK3NAQ6Y60Fhn1J/OLFqdpHDBu/X/9eKoaKJ7KvWumVUe
YuVtXTauB8c4JkujTwQ4ov/hAoGAHxvhbGhkFhSbT0K7gx3w7BJE3iM2AojTOKqC
SYzwOM6tJO5wHz4PAHbq8kyxsZcLgFenGoTYhlMmcM7JwYorThKiHKmyfL7s++ap
vQlp785bIPp8RcO2RyK1CFuAn79jTgujjA9vBTKXJIlqncIPFOXtgl1/FzPrqvK3
NmXoyhECgYEAje9hM9RYO0jbfmTZoQh+onMRz34SM9XWLH+NQGgfvsGtjeRnrUKK
GuWQz/GQGJLy/Uc1KHIdrfPDjvQhZXmPL1v7pNfCrqyj+EnKCNDPPnYq5Zq4WLsB
x1hKPH0LmfEBkXOiFGrD3h3KAuBK5nb0/EFBDR4JuMaySC5CpbOds9o=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,183 @@
#!/bin/bash
set -u
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [[ "$#" -eq 0 ]]; then
cat <<-EOF
To generate a server certificate, provide the domain name as a parameter:
$(basename $0) www.my-domain.tdl
$(basename $0) www.my-domain.tdl alternate.domain.tld
You can also create certificates for wildcard domains:
$(basename $0) '*.my-domain.tdl'
EOF
exit 0
else
DOMAIN="$1"
ALTERNATE_DOMAINS="DNS:$( echo "$@" | sed 's/ /,DNS:/g')"
fi
###############################################################################
# Create a nginx container (which conveniently provides the `openssl` command)
###############################################################################
CONTAINER=$(docker run -d -v $DIR:/work -w /work -e SAN="$ALTERNATE_DOMAINS" nginx:1.14.1)
# Configure openssl
docker exec $CONTAINER bash -c '
mkdir -p /ca/{certs,crl,private,newcerts} 2>/dev/null
echo 1000 > /ca/serial
touch /ca/index.txt
cat > /ca/openssl.cnf <<-"OESCRIPT"
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = /ca
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The root key and root certificate.
private_key = /work/ca-root.key
certificate = /work/ca-root.crt
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 10000
preserve = no
policy = policy_loose
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = server certificate generated for test purpose (nginx-proxy test suite)
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ san_env ]
subjectAltName=${ENV::SAN}
OESCRIPT
'
# shortcut for calling `openssl` inside the container
function openssl {
docker exec $CONTAINER openssl "$@"
}
function exitfail {
echo
echo ERROR: "$@"
docker rm -f $CONTAINER
exit 1
}
###############################################################################
# Setup Certificate authority
###############################################################################
if ! [[ -f "$DIR/ca-root.key" ]]; then
echo
echo "> Create a Certificate Authority root key: $DIR/ca-root.key"
openssl genrsa -out ca-root.key 2048
[[ $? -eq 0 ]] || exitfail failed to generate CA root key
fi
# Create a CA root certificate
if ! [[ -f "$DIR/ca-root.crt" ]]; then
echo
echo "> Create a CA root certificate: $DIR/ca-root.crt"
openssl req -config /ca/openssl.cnf \
-key ca-root.key \
-new -x509 -days 3650 -subj "/O=nginx-proxy test suite/CN=www.nginx-proxy.tld" -extensions v3_ca \
-out ca-root.crt
[[ $? -eq 0 ]] || exitfail failed to generate CA root certificate
# Verify certificate
openssl x509 -noout -text -in ca-root.crt
fi
###############################################################################
# create server key and certificate signed by the certificate authority
###############################################################################
echo
echo "> Create a host key: $DIR/$DOMAIN.key"
openssl genrsa -out "$DOMAIN.key" 2048
echo
echo "> Create a host certificate signing request"
SAN="$ALTERNATE_DOMAINS" openssl req -config /ca/openssl.cnf \
-key "$DOMAIN.key" \
-new -out "/ca/$DOMAIN.csr" -days 1000 -extensions san_env -subj "/CN=$DOMAIN"
[[ $? -eq 0 ]] || exitfail failed to generate server certificate signing request
echo
echo "> Create server certificate: $DIR/$DOMAIN.crt"
SAN="$ALTERNATE_DOMAINS" openssl ca -config /ca/openssl.cnf -batch \
-extensions server_cert \
-extensions san_env \
-in "/ca/$DOMAIN.csr" \
-out "$DOMAIN.crt"
[[ $? -eq 0 ]] || exitfail failed to generate server certificate
# Verify host certificate
#openssl x509 -noout -text -in "$DOMAIN.crt"
docker rm -f $CONTAINER >/dev/null

473
test/conftest.py Normal file
View File

@ -0,0 +1,473 @@
from __future__ import print_function
import contextlib
import logging
import os
import shlex
import socket
import subprocess
import time
import re
import backoff
import docker
import pytest
import requests
from _pytest._code.code import ReprExceptionInfo
from requests.packages.urllib3.util.connection import HAS_IPV6
logging.basicConfig(level=logging.INFO)
logging.getLogger('backoff').setLevel(logging.INFO)
logging.getLogger('DNS').setLevel(logging.DEBUG)
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN)
CA_ROOT_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt')
I_AM_RUNNING_INSIDE_A_DOCKER_CONTAINER = os.path.isfile("/.dockerenv")
FORCE_CONTAINER_IPV6 = False # ugly global state to consider containers' IPv6 address instead of IPv4
docker_client = docker.from_env()
###############################################################################
#
# utilities
#
###############################################################################
@contextlib.contextmanager
def ipv6(force_ipv6=True):
"""
Meant to be used as a context manager to force IPv6 sockets:
with ipv6():
nginxproxy.get("http://something.nginx-proxy.local") # force use of IPv6
with ipv6(False):
nginxproxy.get("http://something.nginx-proxy.local") # legacy behavior
"""
global FORCE_CONTAINER_IPV6
FORCE_CONTAINER_IPV6 = force_ipv6
yield
FORCE_CONTAINER_IPV6 = False
class requests_for_docker(object):
"""
Proxy for calling methods of the requests module.
When a HTTP response failed due to HTTP Error 404 or 502, retry a few times.
Provides method `get_conf` to extract the nginx-proxy configuration content.
"""
def __init__(self):
self.session = requests.Session()
if os.path.isfile(CA_ROOT_CERTIFICATE):
self.session.verify = CA_ROOT_CERTIFICATE
def get_conf(self):
"""
Return the nginx config file
"""
nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"})
if len(nginx_proxy_containers) > 1:
pytest.fail("Too many running jwilder/nginx-proxy:test containers", pytrace=False)
elif len(nginx_proxy_containers) == 0:
pytest.fail("No running jwilder/nginx-proxy:test container", pytrace=False)
return get_nginx_conf_from_container(nginx_proxy_containers[0])
def get(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _get(*args, **kwargs):
return self.session.get(*args, **kwargs)
return _get(*args, **kwargs)
def post(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _post(*args, **kwargs):
return self.session.post(*args, **kwargs)
return _post(*args, **kwargs)
def put(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _put(*args, **kwargs):
return self.session.put(*args, **kwargs)
return _put(*args, **kwargs)
def head(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _head(*args, **kwargs):
return self.session.head(*args, **kwargs)
return _head(*args, **kwargs)
def delete(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _delete(*args, **kwargs):
return self.session.delete(*args, **kwargs)
return _delete(*args, **kwargs)
def options(self, *args, **kwargs):
with ipv6(kwargs.pop('ipv6', False)):
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
def _options(*args, **kwargs):
return self.session.options(*args, **kwargs)
return _options(*args, **kwargs)
def __getattr__(self, name):
return getattr(requests, name)
def container_ip(container):
"""
return the IP address of a container.
If the global FORCE_CONTAINER_IPV6 flag is set, return the IPv6 address
"""
global FORCE_CONTAINER_IPV6
if FORCE_CONTAINER_IPV6:
if not HAS_IPV6:
pytest.skip("This system does not support IPv6")
ip = container_ipv6(container)
if ip == '':
pytest.skip("Container %s has no IPv6 address" % container.name)
else:
return ip
else:
net_info = container.attrs["NetworkSettings"]["Networks"]
if "bridge" in net_info:
return net_info["bridge"]["IPAddress"]
# not default bridge network, fallback on first network defined
network_name = net_info.keys()[0]
return net_info[network_name]["IPAddress"]
def container_ipv6(container):
"""
return the IPv6 address of a container.
"""
net_info = container.attrs["NetworkSettings"]["Networks"]
if "bridge" in net_info:
return net_info["bridge"]["GlobalIPv6Address"]
# not default bridge network, fallback on first network defined
network_name = net_info.keys()[0]
return net_info[network_name]["GlobalIPv6Address"]
def nginx_proxy_dns_resolver(domain_name):
"""
if "nginx-proxy" if found in host, return the ip address of the docker container
issued from the docker image jwilder/nginx-proxy:test.
:return: IP or None
"""
log = logging.getLogger('DNS')
log.debug("nginx_proxy_dns_resolver(%r)" % domain_name)
if 'nginx-proxy' in domain_name:
nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "jwilder/nginx-proxy:test"})
if len(nginxproxy_containers) == 0:
log.warn("no container found from image jwilder/nginx-proxy:test while resolving %r", domain_name)
return
nginxproxy_container = nginxproxy_containers[0]
ip = container_ip(nginxproxy_container)
log.info("resolving domain name %r as IP address %s of nginx-proxy container %s" % (domain_name, ip, nginxproxy_container.name))
return ip
def docker_container_dns_resolver(domain_name):
"""
if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", return the ip address of the docker container
named XXX.
:return: IP or None
"""
log = logging.getLogger('DNS')
log.debug("docker_container_dns_resolver(%r)" % domain_name)
match = re.search('(^|.+\.)(?P<container>[^.]+)\.container\.docker$', domain_name)
if not match:
log.debug("%r does not match" % domain_name)
return
container_name = match.group('container')
log.debug("looking for container %r" % container_name)
try:
container = docker_client.containers.get(container_name)
except docker.errors.NotFound:
log.warn("container named %r not found while resolving %r" % (container_name, domain_name))
return
log.debug("container %r found (%s)" % (container.name, container.short_id))
ip = container_ip(container)
log.info("resolving domain name %r as IP address %s of container %s" % (domain_name, ip, container.name))
return ip
def monkey_patch_urllib_dns_resolver():
"""
Alter the behavior of the urllib DNS resolver so that any domain name
containing substring 'nginx-proxy' will resolve to the IP address
of the container created from image 'jwilder/nginx-proxy:test'.
"""
prv_getaddrinfo = socket.getaddrinfo
dns_cache = {}
def new_getaddrinfo(*args):
logging.getLogger('DNS').debug("resolving domain name %s" % repr(args))
_args = list(args)
# custom DNS resolvers
ip = nginx_proxy_dns_resolver(args[0])
if ip is None:
ip = docker_container_dns_resolver(args[0])
if ip is not None:
_args[0] = ip
# call on original DNS resolver, with eventually the original host changed to the wanted IP address
try:
return dns_cache[tuple(_args)]
except KeyError:
res = prv_getaddrinfo(*_args)
dns_cache[tuple(_args)] = res
return res
socket.getaddrinfo = new_getaddrinfo
return prv_getaddrinfo
def restore_urllib_dns_resolver(getaddrinfo_func):
socket.getaddrinfo = getaddrinfo_func
def remove_all_containers():
for container in docker_client.containers.list(all=True):
if I_AM_RUNNING_INSIDE_A_DOCKER_CONTAINER and container.id.startswith(socket.gethostname()):
continue # pytest is running within a Docker container, so we do not want to remove that particular container
logging.info("removing container %s" % container.name)
container.remove(v=True, force=True)
def get_nginx_conf_from_container(container):
"""
return the nginx /etc/nginx/conf.d/default.conf file content from a container
"""
import tarfile
from cStringIO import StringIO
strm, stat = container.get_archive('/etc/nginx/conf.d/default.conf')
with tarfile.open(fileobj=StringIO(strm.read())) as tf:
conffile = tf.extractfile('default.conf')
return conffile.read()
def docker_compose_up(compose_file='docker-compose.yml'):
logging.info('docker-compose -f %s up -d' % compose_file)
try:
subprocess.check_output(shlex.split('docker-compose -f %s up -d' % compose_file), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError, e:
pytest.fail("Error while runninng 'docker-compose -f %s up -d':\n%s" % (compose_file, e.output), pytrace=False)
def docker_compose_down(compose_file='docker-compose.yml'):
logging.info('docker-compose -f %s down' % compose_file)
try:
subprocess.check_output(shlex.split('docker-compose -f %s down' % compose_file), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError, e:
pytest.fail("Error while runninng 'docker-compose -f %s down':\n%s" % (compose_file, e.output), pytrace=False)
def wait_for_nginxproxy_to_be_ready():
"""
If one (and only one) container started from image jwilder/nginx-proxy:test is found,
wait for its log to contain substring "Watching docker events"
"""
containers = docker_client.containers.list(filters={"ancestor": "jwilder/nginx-proxy:test"})
if len(containers) != 1:
return
container = containers[0]
for line in container.logs(stream=True):
if "Watching docker events" in line:
logging.debug("nginx-proxy ready")
break
def find_docker_compose_file(request):
"""
helper for fixture functions to figure out the name of the docker-compose file to consider.
- if the test module provides a `docker_compose_file` variable, take that
- else, if a yaml file exists with the same name as the test module (but for the `.yml` extension), use that
- otherwise use `docker-compose.yml`.
"""
test_module_dir = os.path.dirname(request.module.__file__)
yml_file = os.path.join(test_module_dir, request.module.__name__ + '.yml')
yaml_file = os.path.join(test_module_dir, request.module.__name__ + '.yaml')
default_file = os.path.join(test_module_dir, 'docker-compose.yml')
docker_compose_file_module_variable = getattr(request.module, "docker_compose_file", None)
if docker_compose_file_module_variable is not None:
docker_compose_file = os.path.join( test_module_dir, docker_compose_file_module_variable)
if not os.path.isfile(docker_compose_file):
raise ValueError("docker compose file %r could not be found. Check your test module `docker_compose_file` variable value." % docker_compose_file)
else:
if os.path.isfile(yml_file):
docker_compose_file = yml_file
elif os.path.isfile(yaml_file):
docker_compose_file = yaml_file
else:
docker_compose_file = default_file
if not os.path.isfile(docker_compose_file):
logging.error("Could not find any docker-compose file named either '{0}.yml', '{0}.yaml' or 'docker-compose.yml'".format(request.module.__name__))
logging.debug("using docker compose file %s" % docker_compose_file)
return docker_compose_file
def connect_to_network(network):
"""
If we are running from a container, connect our container to the given network
:return: the name of the network we were connected to, or None
"""
if I_AM_RUNNING_INSIDE_A_DOCKER_CONTAINER:
try:
my_container = docker_client.containers.get(socket.gethostname())
except docker.errors.NotFound:
logging.warn("container %r not found" % socket.gethostname())
return
# figure out our container networks
my_networks = my_container.attrs["NetworkSettings"]["Networks"].keys()
# make sure our container is connected to the nginx-proxy's network
if network not in my_networks:
logging.info("Connecting to docker network: %s" % network.name)
network.connect(my_container)
return network
def disconnect_from_network(network=None):
"""
If we are running from a container, disconnect our container from the given network.
:param network: name of a docker network to disconnect from
"""
if I_AM_RUNNING_INSIDE_A_DOCKER_CONTAINER and network is not None:
try:
my_container = docker_client.containers.get(socket.gethostname())
except docker.errors.NotFound:
logging.warn("container %r not found" % socket.gethostname())
return
# figure out our container networks
my_networks_names = my_container.attrs["NetworkSettings"]["Networks"].keys()
# disconnect our container from the given network
if network.name in my_networks_names:
logging.info("Disconnecting from network %s" % network.name)
network.disconnect(my_container)
def connect_to_all_networks():
"""
If we are running from a container, connect our container to all current docker networks.
:return: a list of networks we connected to
"""
if not I_AM_RUNNING_INSIDE_A_DOCKER_CONTAINER:
return []
else:
# find the list of docker networks
networks = filter(lambda network: len(network.containers) > 0 and network.name != 'bridge', docker_client.networks.list())
return [connect_to_network(network) for network in networks]
###############################################################################
#
# Py.test fixtures
#
###############################################################################
@pytest.yield_fixture(scope="module")
def docker_compose(request):
"""
pytest fixture providing containers described in a docker compose file. After the tests, remove the created containers
A custom docker compose file name can be defined in a variable named `docker_compose_file`.
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.
"""
docker_compose_file = find_docker_compose_file(request)
original_dns_resolver = monkey_patch_urllib_dns_resolver()
remove_all_containers()
docker_compose_up(docker_compose_file)
networks = connect_to_all_networks()
wait_for_nginxproxy_to_be_ready()
time.sleep(3) # give time to containers to be ready
yield docker_client
for network in networks:
disconnect_from_network(network)
docker_compose_down(docker_compose_file)
restore_urllib_dns_resolver(original_dns_resolver)
@pytest.yield_fixture()
def nginxproxy():
"""
Provides the `nginxproxy` object that can be used in the same way the requests module is:
r = nginxproxy.get("http://foo.com")
The difference is that in case an HTTP requests has status code 404 or 502 (which mostly
indicates that nginx has just reloaded), we retry up to 30 times the query.
Also, 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.
"""
yield requests_for_docker()
###############################################################################
#
# Py.test hooks
#
###############################################################################
# pytest hook to display additionnal stuff in test report
def pytest_runtest_logreport(report):
if report.failed:
if isinstance(report.longrepr, ReprExceptionInfo):
test_containers = docker_client.containers.list(all=True, filters={"ancestor": "jwilder/nginx-proxy:test"})
for container in test_containers:
report.longrepr.addsection('nginx-proxy logs', container.logs())
report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container))
# 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)
###############################################################################
#
# Check requirements
#
###############################################################################
try:
docker_client.images.get('jwilder/nginx-proxy:test')
except docker.errors.ImageNotFound:
pytest.exit("The docker image 'jwilder/nginx-proxy:test' is missing")
if docker.__version__ != "2.1.0":
pytest.exit("This test suite is meant to work with the python docker module v2.1.0")

8
test/lib/ssl/dhparam.pem Normal file
View File

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA1cae6HqPSgicEuAuSCf6Ii3d6qMX9Ta8lnwoX0JQ0CWK7mzaiiIi
dY7oHmc4cq0S3SH+g0tdLP9yqygFS9hdUGINwS2VV6poj2/vdL/dUshegyxpEH58
nofCPnFDeKkcPDMYAlGS8zjp60TsBkRJKcrxxwnjod1Q5mWuMN5KH3sxs842udKH
0nHFE9kKW/NfXb+EGsjpocGpf786cGuCO2d00THsoItOEcM9/aI8DX1QcyxAHR6D
HaYTFJnyyx8Q44u27M15idI4pbNoKORlotiuOwCTGYCfbN14aOV+Ict7aSF8FWpP
48j9SMNuIu2DlF9pNLo6fsrOjYY3c9X12wIBAg==
-----END DH PARAMETERS-----

3
test/pytest.ini Normal file
View File

@ -0,0 +1,3 @@
[pytest]
# disable the creation of the `.cache` folders
addopts = -p no:cacheprovider --ignore=requirements --ignore=certs -r s -v

24
test/pytest.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
###############################################################################
# #
# This script is meant to run the test suite from a Docker container. #
# #
# This is usefull when you want to run the test suite from Mac or #
# Docker Toolbox. #
# #
###############################################################################
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ARGS="$@"
# check requirements
echo "> Building nginx-proxy-tester image..."
docker build -t nginx-proxy-tester -f $DIR/requirements/Dockerfile-nginx-proxy-tester $DIR/requirements
# run the nginx-proxy-tester container setting the correct value for the working dir in order for
# docker-compose to work properly when run from within that container.
exec docker run --rm -it \
-v ${DIR}:/${DIR} \
-w ${DIR} \
-v /var/run/docker.sock:/var/run/docker.sock \
nginx-proxy-tester ${ARGS}

View File

@ -0,0 +1,10 @@
FROM python:2.7-alpine
# Note: we're using alpine because it has openssl 1.0.2, which we need for testing
RUN apk add --update bash openssl curl && rm -rf /var/cache/apk/*
COPY python-requirements.txt /requirements.txt
RUN pip install -r /requirements.txt
WORKDIR /test
ENTRYPOINT ["pytest"]

View File

@ -0,0 +1,52 @@
This directory contains resources to build Docker images tests depend on
# Build images
./build.sh
# python-requirements.txt
If you want to run the test suite from your computer, you need python and a few python modules.
The _python-requirements.txt_ file describes the python modules required. To install them, use
pip:
pip install -r python-requirements.txt
If you don't want to run the test from your computer, you can run the tests from a docker container, see the _pytest.sh_ script.
# Images
## web
This container will run one or many webservers, each of them listening on a single port.
Ports are specified using the `WEB_PORTS` environment variable:
docker run -d -e WEB_PORTS=80 web # will create a container running one webserver listening on port 80
docker run -d -e WEB_PORTS="80 81" web # will create a container running two webservers, one listening on port 80 and a second one listening on port 81
The webserver answers on two paths:
- `/headers`
- `/port`
```
$ docker run -d -e WEB_PORTS=80 -p 80:80 web
$ curl http://127.0.0.1:80/headers
Host: 127.0.0.1
User-Agent: curl/7.47.0
Accept: */*
$ curl http://127.0.0.1:80/port
answer from port 80
```
## nginx-proxy-tester
This is an optional requirement which is usefull if you cannot (or don't want to) install pytest and its requirements on your computer. In this case, you can use the `nginx-proxy-tester` docker image to run the test suite from a Docker container.
To use this image, it is mandatory to run the container using the `pytest.sh` shell script. The script will build the image and run a container from it with the appropriate volumes and settings.

6
test/requirements/build.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
docker build -t web $DIR/web

View File

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

View File

@ -0,0 +1,8 @@
# Docker Image running one (or multiple) webservers listening on all given ports from WEB_PORTS environment variable
FROM python:3
COPY ./webserver.py /
COPY ./entrypoint.sh /
WORKDIR /opt
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]

View File

@ -0,0 +1,15 @@
#!/bin/bash
set -u
trap '[ ${#PIDS[@]} -gt 0 ] && kill -TERM ${PIDS[@]}' TERM
declare -a PIDS
for port in $WEB_PORTS; do
echo starting a web server listening on port $port;
/webserver.py $port &
PIDS+=($!)
done
wait ${PIDS[@]}
trap - TERM
wait ${PIDS[@]}

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import os, sys, re
import http.server
import socketserver
class Handler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
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.end_headers()
if (len(response_body)):
self.wfile.write(response_body.encode())
if __name__ == '__main__':
PORT = int(sys.argv[1])
socketserver.TCPServer.allow_reuse_address = True
httpd = socketserver.TCPServer(('0.0.0.0', PORT), Handler)
httpd.serve_forever()

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

@ -0,0 +1,15 @@
import pytest
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):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
def test_forwards_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"

View File

@ -0,0 +1,24 @@
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.tld
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/f00.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
environment:
DOCKER_HOST: unix:///f00.sock

10
test/test_composev2.py Normal file
View File

@ -0,0 +1,10 @@
import pytest
def test_unknown_virtual_host(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
def test_forwards_to_whoami(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"

15
test/test_composev2.yml Normal file
View File

@ -0,0 +1,15 @@
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
web:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web.nginx-proxy.local

View File

@ -0,0 +1 @@
add_header X-test f00;

View File

@ -0,0 +1 @@
add_header X-test bar;

View File

@ -0,0 +1,28 @@
import pytest
def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_default_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]
def test_custom_default_conf_applies_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]
def test_custom_default_conf_is_overriden_for_web3(docker_compose, nginxproxy):
r = nginxproxy.get("http://web3.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 83\n"
assert "X-test" in r.headers
assert "bar" == r.headers["X-test"]

View File

@ -0,0 +1,31 @@
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/default_location:ro
- ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.local_location:ro
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.local
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.local
web3:
image: web
expose:
- "83"
environment:
WEB_PORTS: 83
VIRTUAL_HOST: web3.nginx-proxy.local

View File

@ -0,0 +1,20 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]
def test_custom_conf_applies_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]

View File

@ -0,0 +1,24 @@
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./my_custom_proxy_settings.conf:/etc/nginx/proxy.conf:ro
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.local
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.local

View File

@ -0,0 +1,22 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]
def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"
assert "X-test" not in r.headers
def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy):
assert "include /etc/nginx/vhost.d/web1.nginx-proxy.local_location;" in nginxproxy.get_conf()

View File

@ -0,0 +1,24 @@
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local_location:ro
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.local
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.local

View File

@ -0,0 +1,19 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]
def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"
assert "X-test" not in r.headers

View File

@ -0,0 +1,24 @@
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./my_custom_proxy_settings.conf:/etc/nginx/vhost.d/web1.nginx-proxy.local:ro
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.local
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.local

View File

@ -0,0 +1,20 @@
import pytest
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
assert "X-test" not in r.headers
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]
def test_custom_conf_applies_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"
assert "X-test" in r.headers
assert "f00" == r.headers["X-test"]

View File

@ -0,0 +1,24 @@
version: '2'
services:
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./my_custom_proxy_settings.conf:/etc/nginx/conf.d/my_custom_proxy_settings.conf:ro
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.local
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.local

View File

@ -0,0 +1,7 @@
import pytest
def test_fallback_on_default(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"

View File

@ -0,0 +1,18 @@
# GIVEN a webserver with VIRTUAL_HOST set to web1.tld
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.tld
# WHEN nginx-proxy runs with DEFAULT_HOST set to web1.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
environment:
DEFAULT_HOST: web1.tld

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

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

View File

@ -0,0 +1,38 @@
import os
import docker
import logging
import pytest
@pytest.yield_fixture(scope="module")
def nginx_tmpl():
"""
pytest fixture which extracts the the nginx config template from
the jwilder/nginx-proxy:test image
"""
script_dir = os.path.dirname(__file__)
logging.info("extracting nginx.tmpl from jwilder/nginx-proxy:test")
docker_client = docker.from_env()
print(docker_client.containers.run(
image='jwilder/nginx-proxy:test',
remove=True,
volumes=['{current_dir}:{current_dir}'.format(current_dir=script_dir)],
entrypoint='sh',
command='-xc "cp /app/nginx.tmpl {current_dir} && chmod 777 {current_dir}/nginx.tmpl"'.format(
current_dir=script_dir),
stderr=True))
yield
logging.info("removing nginx.tmpl")
os.remove(os.path.join(script_dir, "nginx.tmpl"))
def test_unknown_virtual_host_is_503(nginx_tmpl, docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx.container.docker/")
assert r.status_code == 503
def test_forwards_to_whoami(nginx_tmpl, docker_compose, nginxproxy):
r = nginxproxy.get("http://whoami.nginx.container.docker/")
assert r.status_code == 200
whoami_container = docker_compose.containers.get("whoami")
assert r.text == "I'm %s\n" % whoami_container.id[:12]

View File

@ -0,0 +1,27 @@
version: '2'
services:
nginx:
image: nginx
container_name: nginx
volumes:
- /etc/nginx/conf.d
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
dockergen:
image: jwilder/docker-gen
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes_from:
- nginx
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
web:
image: web
container_name: whoami
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: whoami.nginx.container.docker

View File

@ -0,0 +1,66 @@
import os
import docker
import logging
import pytest
import re
def versiontuple(v):
"""
>>> versiontuple("1.12.3")
(1, 12, 3)
>>> versiontuple("1.13.0")
(1, 13, 0)
>>> versiontuple("17.03.0-ce")
(17, 3, 0)
>>> versiontuple("17.03.0-ce") < (1, 13)
False
"""
return tuple(map(int, (v.split('-')[0].split("."))))
raw_version = docker.from_env().version()['Version']
pytestmark = pytest.mark.skipif(
versiontuple(raw_version) < (1, 13),
reason="Docker compose syntax v3 requires docker engine v1.13 or later (got %s)" % raw_version)
@pytest.yield_fixture(scope="module")
def nginx_tmpl():
"""
pytest fixture which extracts the the nginx config template from
the jwilder/nginx-proxy:test image
"""
script_dir = os.path.dirname(__file__)
logging.info("extracting nginx.tmpl from jwilder/nginx-proxy:test")
docker_client = docker.from_env()
print(docker_client.containers.run(
image='jwilder/nginx-proxy:test',
remove=True,
volumes=['{current_dir}:{current_dir}'.format(current_dir=script_dir)],
entrypoint='sh',
command='-xc "cp /app/nginx.tmpl {current_dir} && chmod 777 {current_dir}/nginx.tmpl"'.format(
current_dir=script_dir),
stderr=True))
yield
logging.info("removing nginx.tmpl")
os.remove(os.path.join(script_dir, "nginx.tmpl"))
def test_unknown_virtual_host_is_503(nginx_tmpl, docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx.container.docker/")
assert r.status_code == 503
def test_forwards_to_whoami(nginx_tmpl, docker_compose, nginxproxy):
r = nginxproxy.get("http://whoami.nginx.container.docker/")
assert r.status_code == 200
whoami_container = docker_compose.containers.get("whoami")
assert r.text == "I'm %s\n" % whoami_container.id[:12]
if __name__ == '__main__':
import doctest
doctest.testmod()

View File

@ -0,0 +1,28 @@
version: '3'
services:
nginx:
image: nginx
container_name: nginx
volumes:
- nginx_conf:/etc/nginx/conf.d
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
dockergen:
image: jwilder/docker-gen
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
- nginx_conf:/etc/nginx/conf.d
web:
image: web
container_name: whoami
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: whoami.nginx.container.docker
volumes:
nginx_conf: {}

46
test/test_events.py Normal file
View File

@ -0,0 +1,46 @@
"""
Test that nginx-proxy detects new containers
"""
from time import sleep
import pytest
from docker.errors import NotFound
@pytest.yield_fixture()
def web1(docker_compose):
"""
pytest fixture creating a web container with `VIRTUAL_HOST=web1.nginx-proxy` listening on port 81.
"""
container = docker_compose.containers.run(
name="web1",
image="web",
detach=True,
environment={
"WEB_PORTS": "81",
"VIRTUAL_HOST": "web1.nginx-proxy"
},
ports={"81/tcp": None}
)
sleep(2) # give it some time to initialize and for docker-gen to detect it
yield container
try:
docker_compose.containers.get("web1").remove(force=True)
except NotFound:
pass
def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy):
r = nginxproxy.get("http://nginx-proxy/")
assert r.status_code == 503
def test_new_container_is_detected(web1, nginxproxy):
r = nginxproxy.get("http://web1.nginx-proxy/port")
assert r.status_code == 200
assert "answer from port 81\n" == r.text
web1.remove(force=True)
sleep(2)
r = nginxproxy.get("http://web1.nginx-proxy/port")
assert r.status_code == 503

5
test/test_events.yml Normal file
View File

@ -0,0 +1,5 @@
nginxproxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

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: Jan 13 03:06:39 2017 GMT
Not After : May 31 03:06:39 2044 GMT
Subject: CN=web.nginx-proxy.tld
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:95:56:c7:0d:48:a5:2b:3c:65:49:3f:26:e1:38:
2b:61:30:56:e4:92:d7:63:e0:eb:ad:ac:f9:33:9b:
b2:31:f1:39:13:0b:e5:43:7b:c5:bd:8a:85:c8:d9:
3d:d8:ac:71:ba:16:e7:81:96:b2:ab:ae:c6:c0:bd:
be:a7:d1:96:8f:b2:9b:df:ba:f9:4d:a1:3b:7e:21:
4a:cd:b6:45:f9:6d:79:50:bf:24:8f:c1:6b:c1:09:
19:5b:62:cb:96:e8:04:14:20:e8:d4:16:62:6a:f2:
37:c1:96:e2:9d:53:05:0b:52:1d:e7:68:92:db:8b:
36:68:cd:8d:5b:02:ff:12:f0:ac:5d:0c:c4:e0:7a:
55:a2:49:60:9f:ff:47:1f:52:73:55:4d:d4:f2:d1:
62:a2:f4:50:9d:c9:f6:f1:43:b3:dc:57:e1:31:76:
b4:e0:a4:69:7e:f2:6d:34:ae:b9:8d:74:26:7b:d9:
f6:07:00:ef:4b:36:61:b3:ef:7a:a1:36:3a:b6:d0:
9e:f8:b8:a9:0d:4c:30:a2:ed:eb:ab:6b:eb:2e:e2:
0b:28:be:f7:04:b1:e9:e0:84:d6:5d:31:77:7c:dc:
d2:1f:d4:1d:71:6f:6f:6c:6d:1b:bf:31:e2:5b:c3:
52:d0:14:fc:8b:fb:45:ea:41:ec:ca:c7:3b:67:12:
c4:df
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:web.nginx-proxy.tld
Signature Algorithm: sha256WithRSAEncryption
4e:48:7d:81:66:ba:2f:50:3d:24:42:61:3f:1f:de:cf:ec:1b:
1b:bd:0a:67:b6:62:c8:79:9d:31:a0:fd:a9:61:ce:ff:69:bf:
0e:f4:f7:e6:15:2b:b0:f0:e4:f2:f4:d2:8f:74:02:b1:1e:4a:
a8:6f:26:0a:77:32:29:cf:dc:b5:61:82:3e:58:47:61:92:f0:
0c:20:25:f8:41:4d:34:09:44:bc:39:9e:aa:82:06:83:13:8b:
1e:2c:3d:cf:cd:1a:f7:77:39:38:e0:a3:a7:f3:09:da:02:8d:
73:75:38:b4:dd:24:a7:f9:03:db:98:c6:88:54:87:dc:e0:65:
4c:95:c5:39:9c:00:30:dc:f0:d3:2c:19:ca:f1:f4:6c:c6:d9:
b5:c4:4a:c7:bc:a1:2e:88:7b:b5:33:d0:ff:fb:48:5e:3e:29:
fa:58:e5:03:de:d8:17:de:ed:96:fc:7e:1f:fe:98:f6:be:99:
38:87:51:c0:d3:b7:9a:0f:26:92:e5:53:1b:d6:25:4c:ac:48:
f3:29:fc:74:64:9d:07:6a:25:57:24:aa:a7:70:fa:8f:6c:a7:
2b:b7:9d:81:46:10:32:93:b9:45:6d:0f:16:18:b2:21:1f:f3:
30:24:62:3f:e1:6c:07:1d:71:28:cb:4c:bb:f5:39:05:f9:b2:
5b:a0:05:1b
-----BEGIN CERTIFICATE-----
MIIC+zCCAeOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp
bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs
ZDAeFw0xNzAxMTMwMzA2MzlaFw00NDA1MzEwMzA2MzlaMB4xHDAaBgNVBAMME3dl
Yi5uZ2lueC1wcm94eS50bGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCVVscNSKUrPGVJPybhOCthMFbkktdj4OutrPkzm7Ix8TkTC+VDe8W9ioXI2T3Y
rHG6FueBlrKrrsbAvb6n0ZaPspvfuvlNoTt+IUrNtkX5bXlQvySPwWvBCRlbYsuW
6AQUIOjUFmJq8jfBluKdUwULUh3naJLbizZozY1bAv8S8KxdDMTgelWiSWCf/0cf
UnNVTdTy0WKi9FCdyfbxQ7PcV+ExdrTgpGl+8m00rrmNdCZ72fYHAO9LNmGz73qh
Njq20J74uKkNTDCi7eura+su4gsovvcEsenghNZdMXd83NIf1B1xb29sbRu/MeJb
w1LQFPyL+0XqQezKxztnEsTfAgMBAAGjIjAgMB4GA1UdEQQXMBWCE3dlYi5uZ2lu
eC1wcm94eS50bGQwDQYJKoZIhvcNAQELBQADggEBAE5IfYFmui9QPSRCYT8f3s/s
Gxu9Cme2Ysh5nTGg/alhzv9pvw709+YVK7Dw5PL00o90ArEeSqhvJgp3MinP3LVh
gj5YR2GS8AwgJfhBTTQJRLw5nqqCBoMTix4sPc/NGvd3OTjgo6fzCdoCjXN1OLTd
JKf5A9uYxohUh9zgZUyVxTmcADDc8NMsGcrx9GzG2bXESse8oS6Ie7Uz0P/7SF4+
KfpY5QPe2Bfe7Zb8fh/+mPa+mTiHUcDTt5oPJpLlUxvWJUysSPMp/HRknQdqJVck
qqdw+o9spyu3nYFGEDKTuUVtDxYYsiEf8zAkYj/hbAcdcSjLTLv1OQX5slugBRs=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAlVbHDUilKzxlST8m4TgrYTBW5JLXY+Drraz5M5uyMfE5Ewvl
Q3vFvYqFyNk92KxxuhbngZayq67GwL2+p9GWj7Kb37r5TaE7fiFKzbZF+W15UL8k
j8FrwQkZW2LLlugEFCDo1BZiavI3wZbinVMFC1Id52iS24s2aM2NWwL/EvCsXQzE
4HpVoklgn/9HH1JzVU3U8tFiovRQncn28UOz3FfhMXa04KRpfvJtNK65jXQme9n2
BwDvSzZhs+96oTY6ttCe+LipDUwwou3rq2vrLuILKL73BLHp4ITWXTF3fNzSH9Qd
cW9vbG0bvzHiW8NS0BT8i/tF6kHsysc7ZxLE3wIDAQABAoIBAEmK7IecKMq7+V0y
3mC3GpXICmKR9cRX9XgX4LkLiZuSoXrBtuuevmhzGSMp6I0VjwQHV4a3wdFORs6Q
Ip3eVvj5Ck4Jc9BJAFVC6+WWR6tnwACFwOmSZRAw/O3GH2B3bdrDwiT/yQPFuLN7
LKoxQiCrFdLp6rh3PBosb9pMBXU7k/HUazIdgmSKg6/JIoo/4Gwyid04TF/4MI2l
RscxtP5/ANtS8VgwBEqhgdafRJ4KnLEpgvswgIQvUKmduVhZQlzd0LMY8FbhKVqz
Utg8gsXaTyH6df/nmgUIInxLMz/MKPnMkv99fS6Sp/hvYlGpLZFWBJ6unMq3lKEr
LMbHfIECgYEAxB+5QWdVqG2r9loJlf8eeuNeMPml4P8Jmi5RKyJC7Cww6DMlMxOS
78ZJfl4b3ZrWuyvhjOfX/aTq7kQaF1BI9o3KJBH8k6EtO4gI8KeNmDONyQk9zsrn
ru8Zwr7hVbAo8fCXxCnmPzhDLsYg6f3BVOsQWoX2SFYKZ1GvkPfIReECgYEAwu6G
qtgFb57Vim10ecfWGM6vrPxvyfqP+zlH/p4nR+aQ+2sFbt27D0B1byWBRZe4KQyw
Vq6XiQ09Fk6MJr8E8iAr9GXPPHcqlYI6bbNc6YOP3jVSKut0tQdTUOHll4kYIY+h
RS3VA3+BA//ADpWpywu+7RZRbaIECA+U2a224r8CgYB5PCMIixgoRaNHZeEHF+1/
iY1wOOKRcxY8eOU0BLnZxHd3EiasrCzoi2pi80nGczDKAxYqRCcAZDHVl8OJJdf0
kTGjmnrHx5pucmkUWn7s1vGOlGfgrQ0K1kLWX6hrj7m/1Tn7yOrLqbvd7hvqiTI5
jBVP3/+eN5G2zIf61TC4AQKBgCX2Q92jojNhsF58AHHy+/vqzIWYx8CC/mVDe4TX
kfjLqzJ7XhyAK/zFZdlWaX1/FYtRAEpxR+uV226rr1mgW7s3jrfS1/ADmRRyvyQ8
CP0k9PCmW7EmF51lptEanRbMyRlIGnUZfuFmhF6eAO4WMXHsgKs1bHg4VCapuihG
T1aLAoGACRGn1UxFuBGqtsh2zhhsBZE7GvXKJSk/eP7QJeEXUNpNjCpgm8kIZM5K
GorpL7PSB8mwVlDl18TpMm3P7nz6YkJYte+HdjO7pg59H39Uvtg3tZnIrFxNxVNb
YF62/yHfk2AyTgjQZQUSmDS84jq1zUK4oS90lxr+u8qwELTniMs=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,81 @@
import pytest
def test_arbitrary_headers_are_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'Foo': 'Bar'})
assert r.status_code == 200
assert "Foo: Bar\n" in r.text
##### Testing the handling of X-Forwarded-For #####
def test_X_Forwarded_For_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-For:" in r.text
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'})
assert r.status_code == 200
assert "X-Forwarded-For: 1.2.3.4, " in r.text
##### Testing the handling of X-Forwarded-Proto #####
def test_X_Forwarded_Proto_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-Proto: http" in r.text
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'})
assert r.status_code == 200
assert "X-Forwarded-Proto: f00\n" in r.text
##### Testing the handling of X-Forwarded-Port #####
def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-Port: 80\n" in r.text
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'})
assert r.status_code == 200
assert "X-Forwarded-Port: 1234\n" in r.text
##### Testing the handling of X-Forwarded-Ssl #####
def test_X_Forwarded_Ssl_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-Ssl: off\n" in r.text
def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Ssl': 'f00'})
assert r.status_code == 200
assert "X-Forwarded-Ssl: off\n" in r.text
##### Other headers
def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Real-IP: " in r.text
def test_Host_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "Host: web.nginx-proxy.tld" in r.text
def test_httpoxy_safe(docker_compose, nginxproxy):
"""
See https://httpoxy.org/
nginx-proxy should suppress the `Proxy` header
"""
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'Proxy': 'tcp://some.hacker.com'})
assert r.status_code == 200
assert "Proxy:" not in r.text

View File

@ -0,0 +1,14 @@
web:
image: web
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: web.nginx-proxy.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

View File

@ -0,0 +1,82 @@
import pytest
def test_arbitrary_headers_are_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'Foo': 'Bar'})
assert r.status_code == 200
assert "Foo: Bar\n" in r.text
##### Testing the handling of X-Forwarded-For #####
def test_X_Forwarded_For_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-For:" in r.text
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'})
assert r.status_code == 200
assert "X-Forwarded-For: 1.2.3.4, " in r.text
##### Testing the handling of X-Forwarded-Proto #####
def test_X_Forwarded_Proto_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-Proto: https" in r.text
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'})
assert r.status_code == 200
assert "X-Forwarded-Proto: f00\n" in r.text
##### Testing the handling of X-Forwarded-Port #####
def test_X_Forwarded_Port_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-Port: 443\n" in r.text
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'})
assert r.status_code == 200
assert "X-Forwarded-Port: 1234\n" in r.text
##### Testing the handling of X-Forwarded-Ssl #####
def test_X_Forwarded_Ssl_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Forwarded-Ssl: on\n" in r.text
def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'X-Forwarded-Ssl': 'f00'})
assert r.status_code == 200
assert "X-Forwarded-Ssl: on\n" in r.text
##### Other headers
def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "X-Real-IP: " in r.text
def test_Host_is_passed_on(docker_compose, nginxproxy):
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
assert r.status_code == 200
assert "Host: web.nginx-proxy.tld" in r.text
def test_httpoxy_safe(docker_compose, nginxproxy):
"""
See https://httpoxy.org/
nginx-proxy should suppress the `Proxy` header
"""
r = nginxproxy.get("https://web.nginx-proxy.tld/headers", headers={'Proxy': 'tcp://some.hacker.com'})
assert r.status_code == 200
assert "Proxy:" not in r.text

View File

@ -0,0 +1,16 @@
web:
image: web
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: web.nginx-proxy.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs/web.nginx-proxy.tld.crt:/etc/nginx/certs/web.nginx-proxy.tld.crt:ro
- ./certs/web.nginx-proxy.tld.key:/etc/nginx/certs/web.nginx-proxy.tld.key:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

35
test/test_ipv6.py Normal file
View File

@ -0,0 +1,35 @@
import pytest
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):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
def test_forwards_to_web2_ipv4(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/port")
assert r.status_code == 200
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)
assert r.status_code == 503
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"
def test_forwards_to_web2_ipv6(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/port", ipv6=True)
assert r.status_code == 200
assert r.text == "answer from port 82\n"

24
test/test_ipv6.yml Normal file
View File

@ -0,0 +1,24 @@
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.tld
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
environment:
ENABLE_IPV6: "true"

View File

@ -0,0 +1,16 @@
import pytest
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):
r = nginxproxy.get("http://webA.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
def test_webB_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webB.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"

View File

@ -0,0 +1,14 @@
web:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

View File

@ -0,0 +1,15 @@
import pytest
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):
r = nginxproxy.get("http://web1.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
def test_forwards_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.local/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"

View File

@ -0,0 +1,35 @@
version: '2'
networks:
net1: {}
net2: {}
services:
nginx-proxy:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
networks:
- net1
- net2
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.local
networks:
- net1
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.local
networks:
- net2

View File

@ -0,0 +1,7 @@
import pytest
def test_answer_is_served_from_chosen_port(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 90\n" in r.text

View File

@ -0,0 +1,15 @@
web:
image: web
expose:
- "80"
- "90"
environment:
WEB_PORTS: "80 90"
VIRTUAL_HOST: "web.nginx-proxy.tld"
VIRTUAL_PORT: 90
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

View File

@ -0,0 +1,7 @@
import pytest
def test_answer_is_served_from_port_80_by_default(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text

View File

@ -0,0 +1,14 @@
web:
image: web
expose:
- "80"
- "81"
environment:
WEB_PORTS: "80 81"
VIRTUAL_HOST: "web.nginx-proxy.tld"
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

View File

@ -0,0 +1,7 @@
import pytest
def test_answer_is_served_from_exposed_port_even_if_not_80(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 81\n" in r.text

View File

@ -0,0 +1,14 @@
web:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: "web.nginx-proxy.tld"
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

24
test/test_nominal.py Normal file
View File

@ -0,0 +1,24 @@
import pytest
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):
r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"
def test_forwards_to_web2(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 82\n"
def test_ipv6_is_disabled_by_default(docker_compose, nginxproxy):
with pytest.raises(ConnectionError):
nginxproxy.get("http://nginx-proxy/port", ipv6=True)

22
test/test_nominal.yml Normal file
View File

@ -0,0 +1,22 @@
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web1.nginx-proxy.tld
web2:
image: web
expose:
- "82"
environment:
WEB_PORTS: 82
VIRTUAL_HOST: web2.nginx-proxy.tld
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

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: Jan 10 00:08:52 2017 GMT
Not After : May 28 00:08:52 2044 GMT
Subject: CN=*.nginx-proxy.tld
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:cb:45:f4:14:9b:fe:64:85:79:4a:36:8d:3d:d1:
27:d0:7c:36:28:30:e6:73:80:6f:7c:49:23:d0:6c:
17:e4:44:c0:77:4d:9a:c2:bc:24:84:e3:a5:4d:ba:
d2:da:51:7b:a1:2a:12:d4:c0:19:55:69:2c:22:27:
2d:1a:f6:fc:4b:7f:e9:cb:a8:3c:e8:69:b8:d2:4f:
de:4e:50:e2:d0:74:30:7c:42:5a:ae:aa:85:a5:b1:
71:4d:c9:7e:86:8b:62:8c:3e:0d:e3:3b:c3:f5:81:
0b:8c:68:79:fe:bf:10:fb:ae:ec:11:49:6d:64:5e:
1a:7d:b3:92:93:4e:96:19:3a:98:04:a7:66:b2:74:
61:2d:41:13:0c:a4:54:0d:2c:78:fd:b4:a3:e8:37:
78:9a:de:fa:bc:2e:a8:0f:67:14:58:ce:c3:87:d5:
14:0e:8b:29:7d:48:19:b2:a9:f5:b4:e8:af:32:21:
67:15:7e:43:52:8b:20:cf:9f:38:43:bf:fd:c8:24:
7f:52:a3:88:f2:f1:4a:14:91:2a:6e:91:6f:fb:7d:
6a:78:c6:6d:2e:dd:1e:4c:2b:63:bb:3a:43:9c:91:
f9:df:d3:08:13:63:86:7d:ce:e8:46:cf:f1:6c:1f:
ca:f7:4c:de:d8:4b:e0:da:bc:06:d9:87:0f:ff:96:
45:85
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:*.nginx-proxy.tld
Signature Algorithm: sha256WithRSAEncryption
6e:a5:0e:e4:d3:cc:d5:b7:fc:34:75:89:4e:98:8c:e7:08:06:
a8:5b:ec:13:7d:83:99:a2:61:b8:d5:12:6e:c5:b4:53:4e:9a:
22:cd:ad:14:30:6a:7d:58:d7:23:d9:a4:2a:96:a0:40:9e:50:
9f:ce:f2:fe:8c:dd:9a:ac:99:39:5b:89:2d:ca:e5:3e:c3:bc:
03:04:1c:12:d9:6e:b8:9f:f0:3a:be:12:44:7e:a4:21:86:73:
af:d5:00:51:3f:2c:56:70:34:8f:26:b0:7f:b0:cf:cf:7f:f9:
40:6f:00:29:c4:cf:c3:b7:c2:49:3d:3f:b0:26:78:87:b9:c7:
6c:1b:aa:6a:1a:dd:c5:eb:f2:69:ba:6d:46:0b:92:49:b5:11:
3c:eb:48:c7:2f:fb:33:a6:6a:82:a2:ab:f8:1e:5f:7d:e3:b7:
f2:fd:f5:88:a5:09:4d:a0:bc:f4:3b:cd:d2:8b:d7:57:1f:86:
3b:d2:3e:a4:92:21:b0:02:0b:e9:e0:c4:1c:f1:78:e2:58:a7:
26:5f:4c:29:c8:23:f0:6e:12:3f:bd:ad:44:7b:0b:bd:db:ba:
63:8d:07:c6:9d:dc:46:cc:63:40:ba:5e:45:82:dd:9a:e5:50:
e8:e7:d7:27:88:fc:6f:1d:8a:e7:5c:49:28:aa:10:29:75:28:
c7:52:de:f9
-----BEGIN CERTIFICATE-----
MIIC9zCCAd+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp
bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs
ZDAeFw0xNzAxMTAwMDA4NTJaFw00NDA1MjgwMDA4NTJaMBwxGjAYBgNVBAMMESou
bmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
y0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02awrwkhOOlTbrS2lF7
oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJarqqFpbFxTcl+hoti
jD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdmsnRhLUETDKRUDSx4
/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFnFX5DUosgz584Q7/9
yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MIE2OGfc7oRs/xbB/K
90ze2Evg2rwG2YcP/5ZFhQIDAQABoyAwHjAcBgNVHREEFTATghEqLm5naW54LXBy
b3h5LnRsZDANBgkqhkiG9w0BAQsFAAOCAQEAbqUO5NPM1bf8NHWJTpiM5wgGqFvs
E32DmaJhuNUSbsW0U06aIs2tFDBqfVjXI9mkKpagQJ5Qn87y/ozdmqyZOVuJLcrl
PsO8AwQcEtluuJ/wOr4SRH6kIYZzr9UAUT8sVnA0jyawf7DPz3/5QG8AKcTPw7fC
ST0/sCZ4h7nHbBuqahrdxevyabptRguSSbURPOtIxy/7M6ZqgqKr+B5ffeO38v31
iKUJTaC89DvN0ovXVx+GO9I+pJIhsAIL6eDEHPF44linJl9MKcgj8G4SP72tRHsL
vdu6Y40Hxp3cRsxjQLpeRYLdmuVQ6OfXJ4j8bx2K51xJKKoQKXUox1Le+Q==
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAy0X0FJv+ZIV5SjaNPdEn0Hw2KDDmc4BvfEkj0GwX5ETAd02a
wrwkhOOlTbrS2lF7oSoS1MAZVWksIictGvb8S3/py6g86Gm40k/eTlDi0HQwfEJa
rqqFpbFxTcl+hotijD4N4zvD9YELjGh5/r8Q+67sEUltZF4afbOSk06WGTqYBKdm
snRhLUETDKRUDSx4/bSj6Dd4mt76vC6oD2cUWM7Dh9UUDospfUgZsqn1tOivMiFn
FX5DUosgz584Q7/9yCR/UqOI8vFKFJEqbpFv+31qeMZtLt0eTCtjuzpDnJH539MI
E2OGfc7oRs/xbB/K90ze2Evg2rwG2YcP/5ZFhQIDAQABAoIBAQCjAro2PNLJMfCO
fyjNRgmzu6iCmpR0U68T8GN0JPsT576g7e8J828l0pkhuIyW33lRSThIvLSUNf9a
dChL032H3lBTLduKVh4NKleQXnVFzaeEPoISSFVdButiAhAhPW4OIUVp0OfY3V+x
fac3j2nDLAfL5SKAtqZv363Py9m66EBYm5BmGTQqT/frQWeCEBvlErQef5RIaU8p
e2zMWgSNNojVai8U3nKNRvYHWeWXM6Ck7lCvkHhMF+RpbmCZuqhbEARVnehU/Jdn
QHJ3nxeA2OWpoWKXvAHtSnno49yxq1UIstiQvY+ng5C5i56UlB60UiU2NJ6doZkB
uQ7/1MaBAoGBAORdcFtgdgRALjXngFWhpCp0CseyUehn1KhxDCG+D1pJ142/ymcf
oJOzKJPMRNDdDUBMnR1GBfy7rmwvYevI/SMNy2Qs7ofcXPbdtwwvTCToZ1V9/54k
VfuPBFT+3QzWRvG1tjTV3E4L2VV3nrl2qNPhE5DlfIaU3nQq5Fl0HprJAoGBAOPf
MWOTGev61CdODO5KN3pLAoamiPs5lEUlz3kM3L1Q52YLITxNDjRj9hWBUATJZOS2
pLOoYRwmhD7vrnimMc41+NuuFX+4T7hWPc8uSuOxX0VijYtULyNRK57mncG1Fq9M
RMLbOJ7FD+8jdXNsSMqpQ+pxLJRX/A10O2fOQnbdAoGAL5hV4YWSM0KZHvz332EI
ER0MXiCJN7HkPZMKH0I4eu3m8hEmAyYxVndBnsQ1F37q0xrkqAQ/HTSUntGlS/og
4Bxw5pkCwegoq/77tpto+ExDtSrEitYx4XMmSPyxX4qNULU5m3tzJgUML+b1etwD
Rd2kMU/TC02dq4KBAy/TbRkCgYAl1xN5iJz+XenLGR/2liZ+TWR+/bqzlU006mF4
pZUmbv/uJxz+yYD5XDwqOA4UrWjuvhG9r9FoflDprp2XdWnB556KxG7XhcDfSJr9
A5/2DadXe1Ur9O/a+oi2228JEsxQkea9QPA3FVxfBtFjOHEiDlez39VaUP4PMeUH
iO3qlQKBgFQhdTb7HeYnApYIDHLmd1PvjRvp8XKR1CpEN0nkw8HpHcT1q1MUjQCr
iT6FQupULEvGmO3frQsgVeRIQDbEdZK3C5xCtn6qOw70sYATVf361BbTtidmU9yV
THFxwDSVLiVZgFryoY/NtAc27sVdJnGsPRjjaeVgALAsLbmZ1K/H
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,71 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4096 (0x1000)
Signature Algorithm: sha256WithRSAEncryption
Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld
Validity
Not Before: Jan 10 00:37:02 2017 GMT
Not After : May 28 00:37:02 2044 GMT
Subject: CN=web2.nginx-proxy.tld
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:da:ee:46:2d:44:7c:f1:e6:91:11:bf:34:d6:02:
4e:fe:43:23:fb:6d:20:f7:8d:1b:c6:9c:cd:1c:1a:
07:95:c2:ed:b9:23:73:c1:02:2b:50:51:3f:33:cf:
8e:aa:f1:13:58:4c:ff:7f:7d:4a:87:fc:f0:0f:54:
af:8c:eb:ba:b4:0f:71:5e:12:1f:64:b1:3d:83:88:
dd:9c:09:50:2d:37:1d:03:3b:18:30:36:f4:82:94:
87:7f:31:27:28:84:0c:99:6d:77:b5:b8:4f:ca:83:
58:d5:d8:4e:36:73:1c:1a:5c:ed:05:ef:95:60:03:
28:0c:9f:d8:6f:98:a8:cd:08:be:af:b1:95:5a:60:
96:fe:2a:d0:98:74:9b:4a:c0:48:66:73:67:54:33:
11:22:20:ea:11:05:ba:a6:ed:74:12:05:d8:de:4f:
01:46:39:74:d8:34:1a:f2:2c:c2:df:6d:94:57:52:
c1:e4:2e:1b:8e:12:0e:43:e7:6f:1f:da:51:80:35:
c2:8a:9b:b6:2a:30:39:6b:a0:77:fa:37:11:b7:ec:
de:6e:8c:6f:93:81:5e:2d:90:69:1b:4b:a4:80:ca:
f4:e5:5b:c0:13:45:b9:76:70:ed:d3:4e:dd:66:98:
99:9f:9d:f0:1e:fd:dd:04:4f:9a:55:bc:38:ad:42:
b9:dd
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:web2.nginx-proxy.tld
Signature Algorithm: sha256WithRSAEncryption
38:d6:8c:be:3c:5e:5d:61:02:77:eb:5b:6e:a7:1d:4f:69:0d:
54:bd:dd:3a:1a:8e:9d:a0:c2:f3:a5:31:91:3e:ec:7a:69:48:
40:27:45:a5:c6:b9:af:6d:d9:76:24:97:ec:c5:cf:4d:cc:49:
93:ab:bc:4f:01:7e:7a:57:73:4d:27:62:a6:68:bf:4c:00:2c:
c0:f3:7b:b3:32:81:6b:96:20:0a:73:a0:85:b5:f8:07:10:88:
e8:62:85:41:63:df:43:c5:f9:4b:90:89:6a:16:d9:a6:85:4a:
04:1b:5e:75:d8:84:ae:69:c7:62:8f:f1:53:c8:c6:31:71:6d:
0c:05:2d:bf:6e:b8:b8:7a:8c:73:6f:17:bb:5c:5a:67:51:12:
af:e2:17:9c:40:0e:35:f6:59:93:69:45:14:74:33:c6:aa:04:
76:8e:3c:a6:ac:f4:60:cb:53:eb:d6:63:1a:47:7b:be:11:8d:
74:de:e8:e5:bc:de:1b:09:db:1b:87:89:b7:6a:20:0a:5e:fb:
35:04:ab:b2:f7:4d:a1:86:65:1d:c7:c3:aa:30:15:3a:6e:66:
f8:43:33:63:ac:fc:c1:58:48:5b:ec:a0:00:bf:d4:f1:06:03:
79:ca:d5:b6:f2:39:0b:62:b4:fd:27:27:e9:37:52:21:ce:a4:
32:8a:bb:c7
-----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp
bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs
ZDAeFw0xNzAxMTAwMDM3MDJaFw00NDA1MjgwMDM3MDJaMB8xHTAbBgNVBAMMFHdl
YjIubmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA2u5GLUR88eaREb801gJO/kMj+20g940bxpzNHBoHlcLtuSNzwQIrUFE/M8+O
qvETWEz/f31Kh/zwD1SvjOu6tA9xXhIfZLE9g4jdnAlQLTcdAzsYMDb0gpSHfzEn
KIQMmW13tbhPyoNY1dhONnMcGlztBe+VYAMoDJ/Yb5iozQi+r7GVWmCW/irQmHSb
SsBIZnNnVDMRIiDqEQW6pu10EgXY3k8BRjl02DQa8izC322UV1LB5C4bjhIOQ+dv
H9pRgDXCipu2KjA5a6B3+jcRt+zeboxvk4FeLZBpG0ukgMr05VvAE0W5dnDt007d
ZpiZn53wHv3dBE+aVbw4rUK53QIDAQABoyMwITAfBgNVHREEGDAWghR3ZWIyLm5n
aW54LXByb3h5LnRsZDANBgkqhkiG9w0BAQsFAAOCAQEAONaMvjxeXWECd+tbbqcd
T2kNVL3dOhqOnaDC86UxkT7semlIQCdFpca5r23ZdiSX7MXPTcxJk6u8TwF+eldz
TSdipmi/TAAswPN7szKBa5YgCnOghbX4BxCI6GKFQWPfQ8X5S5CJahbZpoVKBBte
ddiErmnHYo/xU8jGMXFtDAUtv264uHqMc28Xu1xaZ1ESr+IXnEAONfZZk2lFFHQz
xqoEdo48pqz0YMtT69ZjGkd7vhGNdN7o5bzeGwnbG4eJt2ogCl77NQSrsvdNoYZl
HcfDqjAVOm5m+EMzY6z8wVhIW+ygAL/U8QYDecrVtvI5C2K0/Scn6TdSIc6kMoq7
xw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA2u5GLUR88eaREb801gJO/kMj+20g940bxpzNHBoHlcLtuSNz
wQIrUFE/M8+OqvETWEz/f31Kh/zwD1SvjOu6tA9xXhIfZLE9g4jdnAlQLTcdAzsY
MDb0gpSHfzEnKIQMmW13tbhPyoNY1dhONnMcGlztBe+VYAMoDJ/Yb5iozQi+r7GV
WmCW/irQmHSbSsBIZnNnVDMRIiDqEQW6pu10EgXY3k8BRjl02DQa8izC322UV1LB
5C4bjhIOQ+dvH9pRgDXCipu2KjA5a6B3+jcRt+zeboxvk4FeLZBpG0ukgMr05VvA
E0W5dnDt007dZpiZn53wHv3dBE+aVbw4rUK53QIDAQABAoIBABeTCsl7E30017Ay
h6z3yKvGbQx43tDpR/FmFwwMnX555AFImQFSi3l1ljmtAu7TUML0X5rJ0gm8qdjs
xI6HH66d7xYzG2BLWZVdWoef1RtZUO11IpCmikO5XLHMiCvrtDOdPwO5WhYzeJBm
X12rnX4VPYyjFNGm5Vwepj62EI8rS9G3NG00pDYPmN/vUQJiV/iTRIlvXgFm4Hl2
zJhVi+NhyiptFEycdg65JwVfLKtmUXRmwGFiSxQi1FX8YS5EdIV2S8yDwXlWSxmq
4R1eSID7pKxtzyRtBqZJggzfqtY8cMpoOC12FbLAvzagOavs4ntMgAVy5k2T15G2
syQyLSECgYEA+1NIRF3CxNUaPvpcR8Y4PWhwDEzqn5ZscnXaFrUp1W72f3bpwSCa
/t9lXe9O53R5/yt4nCbwvVM0UWYPHGZGQr+5AS7WWDVWVcwkXX1NIjALi0TXQ0Ty
zeuViXDofUS31yhwFFmgGa52pd+edXaGRvxzGyPVdtwpSLZP/gBLQykCgYEA3wC9
sHNPKMxi6vI2VBvmBXHoCSDAkuRLmQEGDmjjt0Ve4fmwGRbBJxniOlNtufNQRfRg
67qaeM4BTrP03cilZ71yXvLN5G2opY5c/I0dYTXRhV3IV6XwlCC+0xmn+ro7kB8x
J4wAj/h/tJ8T0+0TpnbyjmPH4KTJ9GjRTKwe65UCgYBszIHlbr2JXkONbe6S98GS
++o9uPJ9Aa6S4mf2Gpkwl2fIiF7rR0Ux/t2wC5AZ7Ld/en8tAkKHg0SL1GXIQpI6
BSt+0prh9r0YSVaYzkyc9zWYJcYWjfuan1jN9f3/dMctMolKlf4UAA3HAwZjDVtV
0aW24w1e9jI9EweQCuqJ+QKBgBwZec18GiNn7abxMktS4J8bBUPxLpLT1XrIGD1E
lj0HrrcGwVvH9Dq7FjiHPrJJqHnIG1ZYwxIp0xxZrKctmzoBMyInsi3wa2nBEJJ6
LZOMNoR5lr8El9XyclkjSHldchfs9kKnb4K0q1LVIKh5nRpCrrmmdQ8ndJMpigYB
QjwpAoGAWIhFrN0Acdq7Xc9pScqnAohPMWCTBUbeKrOm0ZwN4Q9D+lLeeggbWlWy
AqR4WHQMHc6B+p4Ncg3XBCFw0V62PkYhSCdaLNH3CPyFT0qoeY8VuMjmqS7yoKvp
uMMHfzMmyGg0dyplVGgANafb/it6Dp8T0pnCmzxhe57jf9xsVBo=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,71 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4096 (0x1000)
Signature Algorithm: sha256WithRSAEncryption
Issuer: O=nginx-proxy test suite, CN=www.nginx-proxy.tld
Validity
Not Before: Jan 10 00:58:11 2017 GMT
Not After : May 28 00:58:11 2044 GMT
Subject: CN=web3.nginx-proxy.tld
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:ac:9b:90:dc:2c:0d:c3:ae:f4:a0:cc:40:15:d2:
c4:c2:2c:8c:15:b6:70:28:cf:32:c4:03:ce:b3:87:
30:5d:a6:12:96:69:7a:fe:67:29:1c:8c:24:bb:6a:
c8:86:13:19:91:00:3e:ef:00:67:50:b0:ea:c9:93:
c6:8a:73:82:d8:37:9f:8e:6e:12:13:ec:fa:08:0f:
ac:73:6e:42:96:67:9f:20:c5:1c:a3:b1:4a:83:36:
0e:0a:31:93:76:b1:b6:37:4f:e0:88:3c:46:dc:c1:
53:60:df:28:ae:3e:20:d8:d9:53:a2:be:09:38:f0:
ff:4a:91:45:cb:cb:b5:b3:3d:7d:09:98:47:dc:0d:
5e:83:73:b6:c9:f3:fb:9a:f2:bb:b0:62:ca:aa:af:
6b:42:e5:08:b2:14:87:f4:fc:f1:14:f8:cc:76:b3:
c0:49:df:66:c6:21:a0:bc:5e:0c:bb:de:e9:9c:e7:
fb:31:a1:cf:c4:e9:bb:bd:c3:5a:0d:23:52:c6:b3:
84:77:f1:0c:3d:ca:c3:60:48:f9:7e:a6:dc:4f:f7:
d2:5b:7c:02:4d:38:09:64:33:7e:bb:b1:65:bb:e2:
2b:1d:9a:49:d4:34:21:42:7a:49:3e:11:6c:10:66:
b4:91:db:bd:3a:c2:8d:f4:e4:03:b1:b4:6e:5c:98:
bf:7d
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:web3.nginx-proxy.tld
Signature Algorithm: sha256WithRSAEncryption
9a:f6:b9:c2:08:a4:b4:d7:e4:b2:d3:22:e9:fe:69:4a:e8:76:
18:60:11:1b:3b:7c:4b:c3:72:66:95:b7:7c:de:c7:34:32:58:
aa:5d:e0:12:f0:df:27:b6:3f:dd:f1:8c:ed:ce:bd:73:50:fc:
9b:e1:8c:c2:7f:ac:6b:54:9d:23:0a:d9:a6:25:cc:99:94:73:
2b:69:e8:f7:07:40:37:d3:d4:0b:14:86:6a:a1:01:53:4b:ae:
85:2d:12:13:bd:23:1e:09:cf:20:50:24:76:a6:5f:b4:d6:d6:
e1:34:40:93:4d:42:f7:d0:95:98:77:53:16:e7:ce:cc:4c:35:
b8:30:3b:62:95:e2:40:0c:a1:73:84:92:93:63:df:c6:21:d5:
eb:1d:a1:d0:f2:ec:a5:cf:d6:10:c9:8f:ac:11:16:39:cd:b0:
38:04:29:cf:e1:1c:dd:21:df:1f:95:35:a5:a4:61:2b:3d:8b:
8a:76:02:6d:31:a1:e8:6b:c5:3b:eb:90:40:34:16:5a:07:93:
96:56:cd:8b:52:ca:65:51:78:d3:f2:af:40:44:43:ec:fe:a2:
c8:d4:6d:21:c7:1f:d2:45:28:0d:d2:51:0f:d1:a5:db:00:2a:
3a:10:88:9e:5a:76:a2:f7:e2:f0:fe:14:a3:e8:ec:ef:00:f0:
35:87:eb:7c
-----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzEfMB0GA1UECgwWbmdp
bngtcHJveHkgdGVzdCBzdWl0ZTEcMBoGA1UEAwwTd3d3Lm5naW54LXByb3h5LnRs
ZDAeFw0xNzAxMTAwMDU4MTFaFw00NDA1MjgwMDU4MTFaMB8xHTAbBgNVBAMMFHdl
YjMubmdpbngtcHJveHkudGxkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEArJuQ3CwNw670oMxAFdLEwiyMFbZwKM8yxAPOs4cwXaYSlml6/mcpHIwku2rI
hhMZkQA+7wBnULDqyZPGinOC2Defjm4SE+z6CA+sc25ClmefIMUco7FKgzYOCjGT
drG2N0/giDxG3MFTYN8orj4g2NlTor4JOPD/SpFFy8u1sz19CZhH3A1eg3O2yfP7
mvK7sGLKqq9rQuUIshSH9PzxFPjMdrPASd9mxiGgvF4Mu97pnOf7MaHPxOm7vcNa
DSNSxrOEd/EMPcrDYEj5fqbcT/fSW3wCTTgJZDN+u7Flu+IrHZpJ1DQhQnpJPhFs
EGa0kdu9OsKN9OQDsbRuXJi/fQIDAQABoyMwITAfBgNVHREEGDAWghR3ZWIzLm5n
aW54LXByb3h5LnRsZDANBgkqhkiG9w0BAQsFAAOCAQEAmva5wgiktNfkstMi6f5p
Suh2GGARGzt8S8NyZpW3fN7HNDJYql3gEvDfJ7Y/3fGM7c69c1D8m+GMwn+sa1Sd
IwrZpiXMmZRzK2no9wdAN9PUCxSGaqEBU0uuhS0SE70jHgnPIFAkdqZftNbW4TRA
k01C99CVmHdTFufOzEw1uDA7YpXiQAyhc4SSk2PfxiHV6x2h0PLspc/WEMmPrBEW
Oc2wOAQpz+Ec3SHfH5U1paRhKz2LinYCbTGh6GvFO+uQQDQWWgeTllbNi1LKZVF4
0/KvQERD7P6iyNRtIccf0kUoDdJRD9Gl2wAqOhCInlp2ovfi8P4Uo+js7wDwNYfr
fA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArJuQ3CwNw670oMxAFdLEwiyMFbZwKM8yxAPOs4cwXaYSlml6
/mcpHIwku2rIhhMZkQA+7wBnULDqyZPGinOC2Defjm4SE+z6CA+sc25ClmefIMUc
o7FKgzYOCjGTdrG2N0/giDxG3MFTYN8orj4g2NlTor4JOPD/SpFFy8u1sz19CZhH
3A1eg3O2yfP7mvK7sGLKqq9rQuUIshSH9PzxFPjMdrPASd9mxiGgvF4Mu97pnOf7
MaHPxOm7vcNaDSNSxrOEd/EMPcrDYEj5fqbcT/fSW3wCTTgJZDN+u7Flu+IrHZpJ
1DQhQnpJPhFsEGa0kdu9OsKN9OQDsbRuXJi/fQIDAQABAoIBAA14DjPAFEriyiAK
EC4jxkrIox3GoLXuhS2ahnSn5fRI00Z9cKWNcz3RCcS+LmuX7fTMqhyIUYeQZqHY
MDP5k4o/vOmmWS7I3THn1zMitXt7FoW+G+ACI6hdfXb6K2GluGxUhVbcLUNoqpLy
lwARxQpm2wnl/l49IA63i1S9zq3vy5HSvxBv3jq8xp2PX3Sho33LhsvW+miCJe+R
etKSV4mAjvR62XVgUGJ40FciVMK3pzwwIKPLI7k9sa7WHZr6fNHeDxIWA6AsVBTH
O+2l8Ufd79KesOD6VqdHlxg2a79s3NoCOflQQAbOSSR9ioCE7Ykgvw0wVl57xNoB
WZVAY0ECgYEA2I6+a56NMzkEMxr0w9ZRqgocXCgbqxZx1v7newDyO5J3X4jYhmMJ
abNZtnVs1Pz0IAgCMCf+N4D+RAi9/ahYxq7jmhIkT/IUHseh93XLgd/x9Q9ZJOGm
9+NI3aIBgWOsy4orKxfwzRAVEakOCChYUCy/GUDDD1MPcjxC8ma5abUCgYEAzAua
15Nayr9Sg0QsHqNvgTVkVlA6u+TwN+vfI8cH5nmXIMm5ShwCc9+Pm8mpcFwUo4uE
vOzQ+NwG9CBbVu6/i1/aR+ZlbctdhpsW51v18B9eFVXSZUvEv1ONGdoJZhq0tW7W
V4Zjf+UwdTcrSZKVpd5woUbRkByROPdir/3Ie6kCgYBhf5LX3SBxSWBMqfw9F4bY
6YhvLVeXpZlHVKhfRsPIcl7wUio6Bui8ABWKAkAnfGNk8HYbvEXGM3tGojD3vQ2L
Fj4+paBXpgPM/9A6G3yuUmcbD/fwlO+Zd2jc8A2BdaDcWq6ozjSJ/o2dz+ETZyar
ohm/gtrPUXQI2HzDqeAcaQKBgEBMd+LvAHFbkPjkhrKw9fZViOTaK2gCYOB+Z7ay
hX7PWhxu9QCxiuRQ0sRY7BgILEjNMmsGhWOmklpjx+TBH4MgFX0K0XOj3jkIrlMB
26JrgA5hGQfqtHlGLvSyjLusNr3ly42RP9GRu490byOkGZxHWF66Hle3aNv2uRaU
dpThAoGBAMIPpf4E6xGzurhgYdXMit3jGAYD85BbNUIm9jOym2lxS63ipoF08QhH
NoMVRf/AUoS4VDGXsuABjMffTZbE8L+DaL1cWSuPJAoF9AXUXtz6A8LRc9Mn2WjS
L8BIs9xZCzrJ5XzY4PSnjjyAU81z6E80azWglkmh8rRDzi/o9O79
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,93 @@
import re
import subprocess
import backoff
import docker
import pytest
docker_client = docker.from_env()
###############################################################################
#
# Tests helpers
#
###############################################################################
@backoff.on_exception(backoff.constant, AssertionError, interval=2, max_tries=15, jitter=None)
def assert_log_contains(expected_log_line):
"""
Check that the nginx-proxy container log contains a given string.
The backoff decorator will retry the check 15 times with a 2 seconds delay.
:param expected_log_line: string to search for
:return: None
:raises: AssertError if the expected string is not found in the log
"""
sut_container = docker_client.containers.get("nginxproxy")
docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False)
assert expected_log_line in docker_logs
def require_openssl(required_version):
"""
This function checks that the required version of OpenSSL is present, and skips the test if not.
Use it as a test function decorator:
@require_openssl("2.3.4")
def test_something():
...
:param required_version: minimal required version as a string: "1.2.3"
"""
def versiontuple(v):
clean_v = re.sub("[^\d\.]", "", v)
return tuple(map(int, (clean_v.split("."))))
try:
command_output = subprocess.check_output(["openssl", "version"])
except OSError:
return pytest.mark.skip("openssl command is not available in test environment")
else:
if not command_output:
raise Exception("Could not get openssl version")
openssl_version = command_output.split()[1]
return pytest.mark.skipif(
versiontuple(openssl_version) < versiontuple(required_version),
reason="openssl v%s is less than required version %s" % (openssl_version, required_version))
###############################################################################
#
# Tests
#
###############################################################################
def test_dhparam_is_not_generated_if_present(docker_compose):
sut_container = docker_client.containers.get("nginxproxy")
assert sut_container.status == "running"
assert_log_contains("Custom dhparam.pem file found, generation skipped")
# Make sure the dhparam in use is not the default, pre-generated one
default_checksum = sut_container.exec_run("md5sum /app/dhparam.pem.default").split()
current_checksum = sut_container.exec_run("md5sum /etc/nginx/dhparam/dhparam.pem").split()
assert default_checksum[0] != current_checksum[0]
def test_web5_https_works(docker_compose, nginxproxy):
r = nginxproxy.get("https://web5.nginx-proxy.tld/port", allow_redirects=False)
assert r.status_code == 200
assert "answer from port 85\n" in r.text
@require_openssl("1.0.2")
def test_web5_dhparam_is_used(docker_compose):
sut_container = docker_client.containers.get("nginxproxy")
assert sut_container.status == "running"
host = "%s:443" % sut_container.attrs["NetworkSettings"]["IPAddress"]
r = subprocess.check_output(
"echo '' | openssl s_client -connect %s -cipher 'EDH' | grep 'Server Temp Key'" % host, shell=True)
assert "Server Temp Key: X25519, 253 bits\n" == r

View File

@ -0,0 +1,16 @@
web5:
image: web
expose:
- "85"
environment:
WEB_PORTS: "85"
VIRTUAL_HOST: "web5.nginx-proxy.tld"
sut:
image: jwilder/nginx-proxy:test
container_name: nginxproxy
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./certs:/etc/nginx/certs:ro

View File

@ -0,0 +1,44 @@
import backoff
import docker
docker_client = docker.from_env()
###############################################################################
#
# Tests helpers
#
###############################################################################
@backoff.on_exception(backoff.constant, AssertionError, interval=2, max_tries=15, jitter=None)
def assert_log_contains(expected_log_line):
"""
Check that the nginx-proxy container log contains a given string.
The backoff decorator will retry the check 15 times with a 2 seconds delay.
:param expected_log_line: string to search for
:return: None
:raises: AssertError if the expected string is not found in the log
"""
sut_container = docker_client.containers.get("nginxproxy")
docker_logs = sut_container.logs(stdout=True, stderr=True, stream=False, follow=False)
assert expected_log_line in docker_logs
###############################################################################
#
# Tests
#
###############################################################################
def test_dhparam_is_generated_if_missing(docker_compose):
sut_container = docker_client.containers.get("nginxproxy")
assert sut_container.status == "running"
assert_log_contains("Generating DH parameters")
assert_log_contains("dhparam generation complete, reloading nginx")
# Make sure the dhparam in use is not the default, pre-generated one
default_checksum = sut_container.exec_run("md5sum /app/dhparam.pem.default").split()
generated_checksum = sut_container.exec_run("md5sum /etc/nginx/dhparam/dhparam.pem").split()
assert default_checksum[0] != generated_checksum[0]

View File

@ -0,0 +1,8 @@
sut:
image: jwilder/nginx-proxy:test
container_name: nginxproxy
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs:ro
environment:
- DHPARAM_BITS=256

View File

@ -0,0 +1,33 @@
import pytest
def test_web1_HSTS_default(docker_compose, nginxproxy):
r = nginxproxy.get("https://web1.nginx-proxy.tld/port", allow_redirects=False)
assert "answer from port 81\n" in r.text
assert "Strict-Transport-Security" in r.headers
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):
r = nginxproxy.get("https://web2.nginx-proxy.tld/port", allow_redirects=False)
assert "answer from port 81\n" in r.text
assert "Strict-Transport-Security" not in r.headers
def test_web3_HSTS_custom(docker_compose, nginxproxy):
r = nginxproxy.get("https://web3.nginx-proxy.tld/port", allow_redirects=False)
assert "answer from port 81\n" in r.text
assert "Strict-Transport-Security" in r.headers
assert "max-age=86400; includeSubDomains; preload" == r.headers["Strict-Transport-Security"]
# Regression test for issue 1080
# https://github.com/jwilder/nginx-proxy/issues/1080
def test_web4_HSTS_off_noredirect(docker_compose, nginxproxy):
r = nginxproxy.get("https://web4.nginx-proxy.tld/port", allow_redirects=False)
assert "answer from port 81\n" in r.text
assert "Strict-Transport-Security" not in r.headers

View File

@ -0,0 +1,42 @@
web1:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: "web1.nginx-proxy.tld"
web2:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: "web2.nginx-proxy.tld"
HSTS: "off"
web3:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: "web3.nginx-proxy.tld"
HSTS: "max-age=86400; includeSubDomains; preload"
web4:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: "web4.nginx-proxy.tld"
HSTS: "off"
HTTPS_METHOD: "noredirect"
sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
- ./certs:/etc/nginx/certs:ro

View File

@ -0,0 +1,18 @@
import pytest
def test_web2_http_is_not_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://web2.nginx-proxy.tld/", allow_redirects=False)
assert r.status_code == 503
def test_web2_https_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("https://web2.nginx-proxy.tld/port", allow_redirects=False)
assert r.status_code == 200
assert "answer from port 82\n" in r.text
def test_web2_HSTS_policy_is_active(docker_compose, nginxproxy):
r = nginxproxy.get("https://web2.nginx-proxy.tld/port", allow_redirects=False)
assert "answer from port 82\n" in r.text
assert "Strict-Transport-Security" in r.headers

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