Skip to content

Commit 2a8f455

Browse files
committed
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
2 parents 7d05f0d + 6186fbf commit 2a8f455

11 files changed

+246
-21
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ services:
44

55
env:
66
global:
7-
- DOCKER_VERSION=1.12.1-0~trusty
7+
- DOCKER_VERSION=1.12.3-0~trusty
88

99
before_install:
1010
# list docker-engine versions
1111
- apt-cache madison docker-engine
1212
# upgrade docker-engine to specific version
13-
- sudo apt-get -o Dpkg::Options::="--force-confnew" install -y docker-engine=${DOCKER_VERSION}
13+
- sudo apt-get -o Dpkg::Options::="--force-confnew" install -y --force-yes docker-engine=${DOCKER_VERSION}
1414
- docker version
1515
- docker info
1616
- sudo add-apt-repository ppa:duggan/bats --yes

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM nginx:1.11.3
1+
FROM nginx:1.11.6
22
MAINTAINER Jason Wilder [email protected]
33

44
# Install wget and install/updates certificates

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
![nginx 1.11.3](https://img.shields.io/badge/nginx-1.11.3-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')
1+
![nginx 1.11.6](https://img.shields.io/badge/nginx-1.11.6-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')
22

33

44
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.
@@ -42,7 +42,7 @@ services:
4242
```shell
4343
$ docker-compose up
4444
$ curl -H "Host: whoami.local" localhost
45-
I''m 5b129ab83266
45+
I'm 5b129ab83266
4646
```
4747

4848
### Multiple Ports
@@ -76,7 +76,13 @@ In this example, the `my-nginx-proxy` container will be connected to `my-network
7676

7777
### SSL Backends
7878

79-
If you would like to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container.
79+
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.
80+
81+
### uWSGI Backends
82+
83+
If you would like to connect to uWSGI backend, set `VIRTUAL_PROTO=uwsgi` on the
84+
backend container. Your backend container should than listen on a port rather
85+
than a socket and expose that port.
8086

8187
### Default Host
8288

@@ -92,6 +98,14 @@ image and the official [nginx](https://registry.hub.docker.com/_/nginx/) image.
9298

9399
You may want to do this to prevent having the docker socket bound to a publicly exposed container service.
94100

101+
You can demo this pattern with docker-compose:
102+
103+
```console
104+
$ docker-compose --file docker-compose-separate-containers.yml up
105+
$ curl -H "Host: whoami.local" localhost
106+
I'm 5b129ab83266
107+
```
108+
95109
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.
96110

97111
First start nginx with a volume:
@@ -126,6 +140,10 @@ hosts in use. The certificate and keys should be named after the virtual host w
126140
`.key` extension. For example, a container with `VIRTUAL_HOST=foo.bar.com` should have a
127141
`foo.bar.com.crt` and `foo.bar.com.key` file in the certs directory.
128142

143+
If you are running the container in a virtualized environment (Hyper-V, VirtualBox, etc...),
144+
/path/to/certs must exist in that environment or be made accessible to that environment.
145+
By default, Docker is not able to mount directories on the host machine to containers running in a virtual machine.
146+
129147
#### Diffie-Hellman Groups
130148

131149
If you have Diffie-Hellman groups enabled, the files should be named after the virtual host with a
@@ -205,6 +223,7 @@ proxy_set_header Connection $proxy_connection;
205223
proxy_set_header X-Real-IP $remote_addr;
206224
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
207225
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
226+
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
208227
209228
# Mitigate httpoxy attack (see README for details)
210229
proxy_set_header Proxy "";
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
version: '2'
2+
services:
3+
nginx:
4+
image: nginx
5+
container_name: nginx
6+
ports:
7+
- "80:80"
8+
volumes:
9+
- /etc/nginx/conf.d
10+
11+
dockergen:
12+
image: jwilder/docker-gen
13+
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
14+
volumes_from:
15+
- nginx
16+
volumes:
17+
- /var/run/docker.sock:/tmp/docker.sock:ro
18+
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
19+
20+
whoami:
21+
image: jwilder/whoami
22+
environment:
23+
- VIRTUAL_HOST=whoami.local

nginx.tmpl

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
2424
'' $scheme;
2525
}
2626

27+
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
28+
# server port the client connected to
29+
map $http_x_forwarded_port $proxy_x_forwarded_port {
30+
default $http_x_forwarded_port;
31+
'' $server_port;
32+
}
33+
2734
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
2835
# Connection header that may have been passed to this server
2936
map $http_upgrade $proxy_connection {
@@ -51,6 +58,7 @@ proxy_set_header Connection $proxy_connection;
5158
proxy_set_header X-Real-IP $remote_addr;
5259
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
5360
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
61+
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
5462

5563
# Mitigate httpoxy attack (see README for details)
5664
proxy_set_header Proxy "";
@@ -70,6 +78,7 @@ server {
7078
access_log /var/log/nginx/access.log vhost;
7179
return 503;
7280

81+
ssl_session_tickets off;
7382
ssl_certificate /etc/nginx/certs/default.crt;
7483
ssl_certificate_key /etc/nginx/certs/default.key;
7584
}
@@ -118,8 +127,8 @@ upstream {{ $host }} {
118127
{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}}
119128

120129
{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}}
121-
{{ $vhostCert := replace $vhostCert ".crt" "" -1 }}
122-
{{ $vhostCert := replace $vhostCert ".key" "" -1 }}
130+
{{ $vhostCert := trimSuffix ".crt" $vhostCert }}
131+
{{ $vhostCert := trimSuffix ".key" $vhostCert }}
123132

124133
{{/* Use the cert specified on the container or fallback to the best vhost match */}}
125134
{{ $cert := (coalesce $certName $vhostCert) }}
@@ -158,7 +167,7 @@ server {
158167
{{ end }}
159168

160169
{{ if (ne $https_method "noredirect") }}
161-
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
170+
add_header Strict-Transport-Security "max-age=31536000";
162171
{{ end }}
163172

164173
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
@@ -168,7 +177,12 @@ server {
168177
{{ end }}
169178

170179
location / {
180+
{{ if eq $proto "uwsgi" }}
181+
include uwsgi_params;
182+
uwsgi_pass {{ trim $proto }}://{{ trim $host }};
183+
{{ else }}
171184
proxy_pass {{ trim $proto }}://{{ trim $host }};
185+
{{ end }}
172186
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
173187
auth_basic "Restricted {{ $host }}";
174188
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
@@ -197,7 +211,12 @@ server {
197211
{{ end }}
198212

199213
location / {
214+
{{ if eq $proto "uwsgi" }}
215+
include uwsgi_params;
216+
uwsgi_pass {{ trim $proto }}://{{ trim $host }};
217+
{{ else }}
200218
proxy_pass {{ trim $proto }}://{{ trim $host }};
219+
{{ end }}
201220
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
202221
auth_basic "Restricted {{ $host }}";
203222
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};

test/docker.bats

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,13 @@ function assert_nginxproxy_behaves {
111111
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
112112

113113
# Querying the proxy with Host header → 200
114-
run curl_container $container /data --header "Host: web1.bats"
114+
run curl_container $container /port --header "Host: web1.bats"
115115
assert_output "answer from port 81"
116116

117-
run curl_container $container /data --header "Host: web2.bats"
117+
run curl_container $container /port --header "Host: web2.bats"
118118
assert_output "answer from port 82"
119119

120120
# Querying the proxy with unknown Host header → 503
121-
run curl_container $container /data --header "Host: webFOO.bats" --head
121+
run curl_container $container /port --header "Host: webFOO.bats" --head
122122
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
123123
}

test/headers.bats

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#!/usr/bin/env bats
2+
load test_helpers
3+
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
4+
5+
function setup {
6+
# make sure to stop any web container before each test so we don't
7+
# have any unexpected container running with VIRTUAL_HOST or VIRUTAL_PORT set
8+
stop_bats_containers web
9+
}
10+
11+
12+
@test "[$TEST_FILE] start a nginx-proxy container" {
13+
# GIVEN
14+
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
15+
assert_success
16+
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
17+
}
18+
19+
@test "[$TEST_FILE] nginx-proxy passes arbitrary header" {
20+
# WHEN
21+
prepare_web_container bats-host-1 80 -e VIRTUAL_HOST=web.bats
22+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-1
23+
sleep 1
24+
25+
# THEN
26+
run curl_container $SUT_CONTAINER /headers -H "Foo: Bar" -H "Host: web.bats"
27+
assert_output -l 'Foo: Bar'
28+
}
29+
30+
##### Testing the handling of X-Forwarded-For #####
31+
32+
@test "[$TEST_FILE] nginx-proxy generates X-Forwarded-For" {
33+
# WHEN
34+
prepare_web_container bats-host-2 80 -e VIRTUAL_HOST=web.bats
35+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-2
36+
sleep 1
37+
38+
# THEN
39+
run curl_container $SUT_CONTAINER /headers -H "Host: web.bats"
40+
assert_output -p 'X-Forwarded-For:'
41+
}
42+
43+
@test "[$TEST_FILE] nginx-proxy passes X-Forwarded-For" {
44+
# WHEN
45+
prepare_web_container bats-host-3 80 -e VIRTUAL_HOST=web.bats
46+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-3
47+
sleep 1
48+
49+
# THEN
50+
run curl_container $SUT_CONTAINER /headers -H "X-Forwarded-For: 1.2.3.4" -H "Host: web.bats"
51+
assert_output -p 'X-Forwarded-For: 1.2.3.4, '
52+
}
53+
54+
##### Testing the handling of X-Forwarded-Proto #####
55+
56+
@test "[$TEST_FILE] nginx-proxy generates X-Forwarded-Proto" {
57+
# WHEN
58+
prepare_web_container bats-host-4 80 -e VIRTUAL_HOST=web.bats
59+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-4
60+
sleep 1
61+
62+
# THEN
63+
run curl_container $SUT_CONTAINER /headers -H "Host: web.bats"
64+
assert_output -l 'X-Forwarded-Proto: http'
65+
}
66+
67+
@test "[$TEST_FILE] nginx-proxy passes X-Forwarded-Proto" {
68+
# WHEN
69+
prepare_web_container bats-host-5 80 -e VIRTUAL_HOST=web.bats
70+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-5
71+
sleep 1
72+
73+
# THEN
74+
run curl_container $SUT_CONTAINER /headers -H "X-Forwarded-Proto: https" -H "Host: web.bats"
75+
assert_output -l 'X-Forwarded-Proto: https'
76+
}
77+
78+
##### Testing the handling of X-Forwarded-Port #####
79+
80+
@test "[$TEST_FILE] nginx-proxy generates X-Forwarded-Port" {
81+
# WHEN
82+
prepare_web_container bats-host-6 80 -e VIRTUAL_HOST=web.bats
83+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-6
84+
sleep 1
85+
86+
# THEN
87+
run curl_container $SUT_CONTAINER /headers -H "Host: web.bats"
88+
assert_output -l 'X-Forwarded-Port: 80'
89+
}
90+
91+
@test "[$TEST_FILE] nginx-proxy passes X-Forwarded-Port" {
92+
# WHEN
93+
prepare_web_container bats-host-7 80 -e VIRTUAL_HOST=web.bats
94+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-7
95+
sleep 1
96+
97+
# THEN
98+
run curl_container $SUT_CONTAINER /headers -H "X-Forwarded-Port: 1234" -H "Host: web.bats"
99+
assert_output -l 'X-Forwarded-Port: 1234'
100+
}
101+
102+
##### Other headers
103+
104+
@test "[$TEST_FILE] nginx-proxy generates X-Real-IP" {
105+
# WHEN
106+
prepare_web_container bats-host-8 80 -e VIRTUAL_HOST=web.bats
107+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-8
108+
sleep 1
109+
110+
# THEN
111+
run curl_container $SUT_CONTAINER /headers -H "Host: web.bats"
112+
assert_output -p 'X-Real-IP: '
113+
}
114+
115+
@test "[$TEST_FILE] nginx-proxy passes Host" {
116+
# WHEN
117+
prepare_web_container bats-host-9 80 -e VIRTUAL_HOST=web.bats
118+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-9
119+
sleep 1
120+
121+
# THEN
122+
run curl_container $SUT_CONTAINER /headers -H "Host: web.bats"
123+
assert_output -l 'Host: web.bats'
124+
}
125+
126+
@test "[$TEST_FILE] nginx-proxy supresses Proxy for httpoxy protection" {
127+
# WHEN
128+
prepare_web_container bats-host-10 80 -e VIRTUAL_HOST=web.bats
129+
dockergen_wait_for_event $SUT_CONTAINER start bats-host-10
130+
sleep 1
131+
132+
# THEN
133+
run curl_container $SUT_CONTAINER /headers -H "Proxy: tcp://foo.com" -H "Host: web.bats"
134+
refute_output -l 'Proxy: tcp://foo.com'
135+
}
136+
137+
@test "[$TEST_FILE] stop all bats containers" {
138+
stop_bats_containers
139+
}

test/multiple-hosts.bats

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ function setup {
2626
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
2727

2828
# THEN querying the proxy with unknown Host header → 503
29-
run curl_container $SUT_CONTAINER /data --header "Host: webFOO.bats" --head
29+
run curl_container $SUT_CONTAINER /port --header "Host: webFOO.bats" --head
3030
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
3131

3232
# THEN
33-
run curl_container $SUT_CONTAINER /data --header 'Host: multiple-hosts-1-A.bats'
33+
run curl_container $SUT_CONTAINER /port --header 'Host: multiple-hosts-1-A.bats'
3434
assert_output "answer from port 80"
3535

3636
# THEN
37-
run curl_container $SUT_CONTAINER /data --header 'Host: multiple-hosts-1-B.bats'
37+
run curl_container $SUT_CONTAINER /port --header 'Host: multiple-hosts-1-B.bats'
3838
assert_output "answer from port 80"
3939
}
4040

test/multiple-ports.bats

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function setup {
5858
# $1 port we are expecting an response from
5959
function assert_response_is_from_port {
6060
local -r port=$1
61-
run curl_container $SUT_CONTAINER /data --header "Host: web.bats"
61+
run curl_container $SUT_CONTAINER /port --header "Host: web.bats"
6262
assert_output "answer from port $port"
6363
}
6464

test/test_helpers.bash

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,15 @@ function prepare_web_container {
124124
--name $container_name \
125125
$expose_option \
126126
-w /var/www/ \
127+
-v $DIR/web_helpers:/var/www:ro \
127128
$options \
128129
-e PYTHON_PORTS="$ports" \
129130
python:3 bash -c "
130131
trap '[ \${#PIDS[@]} -gt 0 ] && kill -TERM \${PIDS[@]}' TERM
131132
declare -a PIDS
132133
for port in \$PYTHON_PORTS; do
133134
echo starting a web server listening on port \$port;
134-
mkdir /var/www/\$port
135-
cd /var/www/\$port
136-
echo \"answer from port \$port\" > data
137-
python -m http.server \$port &
135+
./webserver.py \$port &
138136
PIDS+=(\$!)
139137
done
140138
wait \${PIDS[@]}
@@ -146,7 +144,7 @@ function prepare_web_container {
146144
# THEN querying directly port works
147145
IFS=$' \t\n' # See https://github.com/sstephenson/bats/issues/89
148146
for port in $ports; do
149-
run retry 5 1s docker run --label bats-type="curl" appropriate/curl --silent --fail http://$(docker_ip $container_name):$port/data
147+
run retry 5 1s docker run --label bats-type="curl" appropriate/curl --silent --fail http://$(docker_ip $container_name):$port/port
150148
assert_output "answer from port $port"
151149
done
152150
}

0 commit comments

Comments
 (0)