Merge branch 'master' into weight

This commit is contained in:
Niklas Keller 2016-10-19 09:16:30 +02:00
commit 20a8ba0ba4
20 changed files with 671 additions and 202 deletions

View file

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

22
.travis.yml Normal file
View file

@ -0,0 +1,22 @@
sudo: required
services:
- docker
env:
global:
- DOCKER_VERSION=1.12.1-0~trusty
before_install:
# list docker-engine versions
- apt-cache madison docker-engine
# upgrade docker-engine to specific version
- sudo apt-get -o Dpkg::Options::="--force-confnew" install -y docker-engine=${DOCKER_VERSION}
- docker version
- docker info
- sudo add-apt-repository ppa:duggan/bats --yes
- sudo apt-get update -qq
- sudo apt-get install -qq bats
- make update-dependencies
script:
- make test

View file

@ -1,5 +1,5 @@
FROM nginx:1.9.5
MAINTAINER Jason Wilder jwilder@litl.com
FROM nginx:1.11.3
MAINTAINER Jason Wilder mail@jasonwilder.com
# Install wget and install/updates certificates
RUN apt-get update \
@ -14,10 +14,10 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /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.3
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 \

14
Makefile Normal file
View file

@ -0,0 +1,14 @@
.SILENT :
.PHONY : test
update-dependencies:
docker pull jwilder/docker-gen:0.7.3
docker pull nginx:1.11.3
docker pull python:3
docker pull rancher/socat-docker:latest
docker pull appropriate/curl:latest
docker pull docker:1.10
test:
docker build -t jwilder/nginx-proxy:bats .
bats test

View file

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

View file

@ -1,4 +1,5 @@
![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) ![Build](https://circleci.com/gh/jwilder/nginx-proxy.svg?&style=shield&circle-token=2da3ee844076a47371bd45da81cf27409ca7306a)
![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')
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.
@ -18,6 +19,32 @@ The containers being proxied must [expose](https://docs.docker.com/reference/run
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.
### Docker Compose
```yaml
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
container_name: whoami
environment:
- VIRTUAL_HOST=whoami.local
```
```shell
$ docker-compose up
$ curl -H "Host: whoami.local" localhost
I'm 5b129ab83266
```
### Multiple Ports
If your container exposes multiple ports, nginx-proxy will default to the service running on port 80. If you need to specify a different port, you can set a VIRTUAL_PORT env var to select a different one. If your container only exposes one port and it has a VIRTUAL_HOST env var set, that port will be selected.
@ -33,10 +60,30 @@ 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.
### SSL Backends
If you would like to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container.
### uWSGI Backends
If you would like to connect to uWSGI backend, set `VIRTUAL_PROTO=uwsgi` on the
backend container. Your backend container should than listen on a port rather
than a socket and expose that port.
### Default Host
To set the default host for nginx use the env var `DEFAULT_HOST=foo.bar.com` for example
@ -51,6 +98,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,7 +119,7 @@ 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.
@ -93,7 +148,7 @@ should have a `foo.bar.com.dhparam.pem` file in the certs directory.
#### 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
@ -110,7 +165,7 @@ should provide compatibility with clients back to Firefox 1, Chrome 1, IE 7, Ope
Windows XP IE8, Android 2.3, Java 7. The configuration also enables HSTS, and SSL
session caches.
The behavior for the proxy when port 80 and 443 are exposed is as follows:
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.
@ -121,6 +176,15 @@ to establish a connection. A self-signed or generic cert named `default.crt` an
will allow a client browser to make a SSL connection (likely w/ a warning) and subsequently receive
a 503.
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`. `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.
### Basic Authentication Support
In order to be able to secure your virtual host, you have to create a file named as its equivalent VIRTUAL_HOST variable on directory
@ -155,10 +219,15 @@ 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;
# 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`.
@ -218,3 +287,12 @@ If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=e
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.
### 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'll need to install [bats 0.4.0](https://github.com/sstephenson/bats).
make test

View file

@ -1,21 +0,0 @@
machine:
pre:
# install docker 1.7.1
- sudo curl -L -o /usr/bin/docker 'https://s3-external-1.amazonaws.com/circle-downloads/docker-1.7.1-circleci'; sudo chmod 0755 /usr/bin/docker; true
services:
- docker
dependencies:
override:
- sudo add-apt-repository ppa:duggan/bats --yes
- sudo apt-get update -qq
- sudo apt-get install -qq bats
- docker pull jwilder/docker-gen
- docker pull nginx
- docker pull python:3
- docker pull rancher/socat-docker
test:
override:
- docker build -t jwilder/nginx-proxy:bats .
- bats test

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

15
docker-compose.yml Normal file
View file

@ -0,0 +1,15 @@
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

@ -1,3 +1,5 @@
{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
{{ 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 +7,13 @@
# {{ .Container.Node.Name }}/{{ .Container.Name }}
server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }} weight={{ .Weight }};
{{/* 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 }} weight={{ .Weight }};
server {{ .Network.IP }}:{{ .Address.Port }} weight={{ .Weight }};
{{ end }}
{{ else }}
{{ else if .Network }}
# {{ .Container.Name }}
server {{ .Container.IP }} down;
server {{ .Network.IP }} down;
{{ end }}
{{ end }}
@ -49,6 +51,9 @@ 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;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
{{ end }}
server {
@ -65,6 +70,7 @@ server {
access_log /var/log/nginx/access.log vhost;
return 503;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/default.crt;
ssl_certificate_key /etc/nginx/certs/default.key;
}
@ -76,15 +82,24 @@ upstream {{ $host }} {
{{ range $container := $containers }}
{{ $weight := or ($container.Env.VIRTUAL_WEIGHT) "1" }}
{{ $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 "Weight" $weight) }}
{{/* 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 "Weight" $weight) }}
{{ range $knownNetwork := $CurrentContainer.Networks }}
{{ range $containerNetwork := $container.Networks }}
{{ if eq $knownNetwork.Name $containerNetwork.Name }}
## Can be connect 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 "Weight" $weight) }}
{{/* 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 "Weight" $weight) }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
}
@ -95,6 +110,9 @@ upstream {{ $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" }}
{{/* 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 first cert name defined by containers w/ the same vhost */}}
{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
@ -102,20 +120,24 @@ 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 $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;
}
{{ end }}
server {
server_name {{ $host }};
@ -123,11 +145,12 @@ server {
access_log /var/log/nginx/access.log vhost;
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;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
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) }};
@ -136,7 +159,9 @@ server {
ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
{{ end }}
{{ if (ne $https_method "noredirect") }}
add_header Strict-Transport-Security "max-age=31536000";
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
@ -145,7 +170,12 @@ server {
{{ end }}
location / {
proxy_pass {{ $proto }}://{{ $host }};
{{ if eq $proto "uwsgi" }}
include uwsgi_params;
uwsgi_pass {{ trim $proto }}://{{ trim $host }};
{{ else }}
proxy_pass {{ trim $proto }}://{{ trim $host }};
{{ end }}
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
@ -157,7 +187,10 @@ server {
{{ end }}
}
}
{{ else }}
{{ end }}
{{ if or (not $is_https) (eq $https_method "noredirect") }}
server {
server_name {{ $host }};
@ -171,7 +204,12 @@ server {
{{ end }}
location / {
proxy_pass {{ $proto }}://{{ $host }};
{{ if eq $proto "uwsgi" }}
include uwsgi_params;
uwsgi_pass {{ trim $proto }}://{{ trim $host }};
{{ else }}
proxy_pass {{ trim $proto }}://{{ trim $host }};
{{ end }}
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
@ -184,12 +222,12 @@ server {
}
}
{{ 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;
return 500;
ssl_certificate /etc/nginx/certs/default.crt;
ssl_certificate_key /etc/nginx/certs/default.key;

View file

@ -4,7 +4,7 @@ load test_helpers
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
docker ps -q --filter "label=bats-type=web" | xargs -r docker stop >&2
stop_bats_containers web
}
@ -17,7 +17,7 @@ function setup {
# WHEN nginx-proxy runs with DEFAULT_HOST set to web.bats
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro -e DEFAULT_HOST=web.bats
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN querying the proxy without Host header → 200
run curl_container $SUT_CONTAINER / --head
@ -27,3 +27,7 @@ function setup {
run curl_container $SUT_CONTAINER / --head --header "Host: something.I.just.made.up"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}

View file

@ -11,10 +11,10 @@ load test_helpers
@test "[$TEST_FILE] -v /var/run/docker.sock:/tmp/docker.sock:ro" {
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1
# WHEN nginx-proxy runs on our docker host using the default unix socket
# WHEN nginx-proxy runs on our docker host using the default unix socket
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN
assert_nginxproxy_behaves $SUT_CONTAINER
@ -24,10 +24,10 @@ load test_helpers
@test "[$TEST_FILE] -v /var/run/docker.sock:/f00.sock:ro -e DOCKER_HOST=unix:///f00.sock" {
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-2
# WHEN nginx-proxy runs on our docker host using a custom unix socket
# WHEN nginx-proxy runs on our docker host using a custom unix socket
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/f00.sock:ro -e DOCKER_HOST=unix:///f00.sock
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN
assert_nginxproxy_behaves $SUT_CONTAINER
@ -44,8 +44,8 @@ load test_helpers
# WHEN nginx-proxy runs on our docker host using tcp to connect to our docker host
run nginxproxy $SUT_CONTAINER -e DOCKER_HOST="tcp://bats-docker-tcp:2375" --link bats-docker-tcp:bats-docker-tcp
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN
assert_nginxproxy_behaves $SUT_CONTAINER
}
@ -54,33 +54,36 @@ load test_helpers
@test "[$TEST_FILE] separated containers (nginx + docker-gen + nginx.tmpl)" {
docker_clean bats-nginx
docker_clean bats-docker-gen
# GIVEN a simple nginx container
run docker run -d \
--label bats-type="nginx" \
--name bats-nginx \
-v /etc/nginx/conf.d/ \
-v /etc/nginx/certs/ \
nginx:latest
assert_success
run retry 5 1s curl --silent --fail --head http://$(docker_ip bats-nginx)/
run retry 5 1s docker run --label bats-type="curl" appropriate/curl --silent --fail --head http://$(docker_ip bats-nginx)/
assert_output -l 0 $'HTTP/1.1 200 OK\r'
# WHEN docker-gen runs on our docker host
run docker run -d \
--label bats-type="docker-gen" \
--name bats-docker-gen \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
-v $BATS_TEST_DIRNAME/../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro \
--volumes-from bats-nginx \
jwilder/docker-gen:latest \
--expose 80 \
jwilder/docker-gen:0.7.3 \
-notify-sighup bats-nginx \
-watch \
-only-exposed \
/etc/docker-gen/templates/nginx.tmpl \
/etc/nginx/conf.d/default.conf
assert_success
docker_wait_for_log bats-docker-gen 6 "Watching docker events"
# Give some time to the docker-gen container to notify bats-nginx so it
docker_wait_for_log bats-docker-gen 9 "Watching docker events"
# Give some time to the docker-gen container to notify bats-nginx so it
# reloads its config
sleep 2s
@ -89,11 +92,15 @@ load test_helpers
docker logs bats-docker-gen
false
} >&2
# THEN
assert_nginxproxy_behaves bats-nginx
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# $1 nginx-proxy container
function assert_nginxproxy_behaves {
@ -109,9 +116,8 @@ function assert_nginxproxy_behaves {
run curl_container $container /data --header "Host: web2.bats"
assert_output "answer from port 82"
# Querying the proxy with unknown Host header → 503
run curl_container $container /data --header "Host: webFOO.bats" --head
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}

View file

@ -13,6 +13,11 @@ function docker_ip {
docker inspect --format '{{ .NetworkSettings.IPAddress }}' $1
}
# get the ip of docker container $1
function docker_id {
docker inspect --format '{{ .ID }}' $1
}
# get the running state of container $1
# → true/false
# fails if the container does not exist
@ -52,9 +57,10 @@ function docker_tcp {
local container_name="$1"
docker_clean $container_name
docker run -d \
--label bats-type="socat" \
--name $container_name \
--expose 2375 \
-v /var/run/docker.sock:/var/run/docker.sock \
rancher/socat-docker
docker -H tcp://$(docker_ip $container_name):2375 version
docker run --label bats-type="docker" --link "$container_name:docker" docker:1.10 version
}

View file

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID7TCCAtWgAwIBAgIJAOGkf5EnexJVMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD
VQQGEwJVUzERMA8GA1UECAwIVmlyZ2luaWExDzANBgNVBAcMBlJlc3RvbjERMA8G
A1UECgwIRmFrZSBPcmcxGzAZBgNVBAMMEioubmdpbngtcHJveHkuYmF0czEpMCcG
CSqGSIb3DQEJARYad2VibWFzdGVyQG5naW54LXByb3h5LmJhdHMwHhcNMTYwNDIw
MTUzOTUxWhcNMjYwNDE4MTUzOTUxWjCBjDELMAkGA1UEBhMCVVMxETAPBgNVBAgM
CFZpcmdpbmlhMQ8wDQYDVQQHDAZSZXN0b24xETAPBgNVBAoMCEZha2UgT3JnMRsw
GQYDVQQDDBIqLm5naW54LXByb3h5LmJhdHMxKTAnBgkqhkiG9w0BCQEWGndlYm1h
c3RlckBuZ2lueC1wcm94eS5iYXRzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA0Amkj3iaQn8Z2CW6n24zSuWu2OoLCkHZAk8eprkI4kKoPBvjusynkm8E
phq65jebToHoldfuQ0wM61DzhD15bHwS3x9CrOVbShsmdnGALz+wdR0/4Likx50I
YZdecTOAlkoZudnX5FZ4ngOxjqcym7p5T8TrSS97a0fx99gitZY0p+Nu2tip4o3t
WBMs+SoPWTlQ1SrSmL8chC8O2knyBl/w1nHmDnMuR6FGcHdhLncApw9t5spgfv7p
OrMF4tQxJQNk10TnflmEMkGmy+pfk2e0cQ1Kwp3Nmzm7ECkggxxyjU3ihKiFK+09
8aSCi7gDAY925+mV6LZ5oLMpO3KJvQIDAQABo1AwTjAdBgNVHQ4EFgQU+NvFo37z
9Dyq8Mu82SPtV7q1gYQwHwYDVR0jBBgwFoAU+NvFo37z9Dyq8Mu82SPtV7q1gYQw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAI1ityDV0UsCVHSpB2LN+
QXlk8XS0ACIJ8Q0hbOj3BmYrdAVglG4P6upDEueaaxwsaBTagkTP8nxZ9dhfZHyZ
5YLNwYsiG5iqb8e0ecHx3uJT/0YiXn/8rBvxEZna4Fl8seGdp7BjOWUAS2Nv8tn4
EJJvRdfX/O8XgPc95DM4lwQ/dvyWmavMI4lnl0n1IQV9WPGaIQhYPU9WEQK6iMUB
o1kx8YbOJQD0ZBRfqpriNt1/8ylkkSYYav8QT9JFvQFCWEvaX71QF+cuOwC7ZYBH
4ElXwEUrYBHKiPo0q0VsTtMvLh7h/T5czrIhG/NpfVJPtQOk8aVwNScL3/n+TGU8
6g==
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQCaSPeJpCfxnY
JbqfbjNK5a7Y6gsKQdkCTx6muQjiQqg8G+O6zKeSbwSmGrrmN5tOgeiV1+5DTAzr
UPOEPXlsfBLfH0Ks5VtKGyZ2cYAvP7B1HT/guKTHnQhhl15xM4CWShm52dfkVnie
A7GOpzKbunlPxOtJL3trR/H32CK1ljSn427a2Knije1YEyz5Kg9ZOVDVKtKYvxyE
Lw7aSfIGX/DWceYOcy5HoUZwd2EudwCnD23mymB+/uk6swXi1DElA2TXROd+WYQy
QabL6l+TZ7RxDUrCnc2bObsQKSCDHHKNTeKEqIUr7T3xpIKLuAMBj3bn6ZXotnmg
syk7com9AgMBAAECggEAa7wCp3XqVPNjW+c1/ShhkbDeWmDhtL8i9aopkmeSbTHd
07sRtQQU56Vsf+Sp010KpZ5q52Z6cglpS1eRtHLtdbvPPhL/QXBJVVg4E/B1VIKk
DBJIqUSVuPXeiEOOWgs01R+ssO1ae1o4foQlKF33vGPWPPQacL0RKh6I9TPNzcD7
n4rujlHk72N/bNydyK2rnyKB4vAI5TbZPLps+Xe123CmgZnW3JClcWV9B4foRmiu
a5Iq1WYAK2GYKbYwgqDRyYBC27m91a7U31pE4GQD+xQdlz6kcOlCU5hAcPK3h7j0
fLQqn8g+YAtc0nBKKB4NZe3QEzTiVMorT0VitxI71QKBgQDnirardZaXOFzYGzB3
j+FGB9BUW54hnHr5BxOYrfmEJ5umJjJWaGupfYrQsPArrJP1//WbqVZIPvdQParD
mQhLmSp1r/VNzGB6pISmzU1ZGDHsmBxYseh366om5YBQUFU2vmbil9VkrkM4fsJG
tcS9V/nVY/EM7Yp3PzjfLlhC1wKBgQDmA1YJmnZvIbLp3PoKqM69QiCLKztVm7nX
xpu3b3qbXEzXkt2sP5PHmr+s13hOPQFKRJ2hk4UN9WqpnFoHw5E5eWWhSa/peUZm
r10Y5XspiFtRHHiu6ABXB49eB4fen+vHEZHKyRJ4rFthKjjBHdNPC8bmwnT3jE85
/8a26FLZiwKBgQDXEi8JZslBn9YF2oOTm28KCLoHka551AsaA+u892T8z3mxxGsf
fhD7N6TYonIEb2Jkr6OpOortwqcgvpc+5oghCJ27AX2fDUdUxDp/YdYF+wZsmQJD
lMW1lo7PYIBmmaf9mLCiq5xIz+GauYul+LNNmUl0YEgI1SC4EV63WCodswKBgDMX
GJxHd/kVViVGFTAa8NjvAEWJU8OfNHduQRZMp8IsjVDw6VYiRRP4Fo0wyyMtv8Sc
WxsRpmNEWO3VsdW5pd9LTLy3nmBQtMeIOjiWeHXwOMBaf5/yHmk2X6z2JULY6Mkt
6OFPKlAtkJqTg0m58z7Ckeqd1NdLjimG27+y+PwjAoGAFt0cbC1Ust2BE6YEspSX
ofpAnJsyKrbF9iVUyXDUP99sdqYQfPJ5uqPGkP59lJGkTLtebuitqi6FCyrsT6Fq
AWLiExbqebAqcuAZw2S+iuK27S4rrkjVGF53J7vH3rOzCBUXaRx6GKfTjUqedHdg
9Kw+LP6IFnMTb+EGLo+GqHs=
-----END PRIVATE KEY-----

View file

@ -5,19 +5,21 @@ SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
docker ps -q --filter "label=bats-type=web" | xargs -r docker stop >&2
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] nginx-proxy forwards requests for 2 hosts" {
# WHEN a container runs a web server with VIRTUAL_HOST set for multiple hosts
prepare_web_container bats-multiple-hosts-1 80 -e VIRTUAL_HOST=multiple-hosts-1-A.bats,multiple-hosts-1-B.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-multiple-hosts-1
sleep 1
# THEN querying the proxy without Host header → 503
run curl_container $SUT_CONTAINER / --head
@ -35,3 +37,7 @@ function setup {
run curl_container $SUT_CONTAINER /data --header 'Host: multiple-hosts-1-B.bats'
assert_output "answer from port 80"
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}

View file

@ -1,54 +1,64 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
docker ps -q --filter "label=bats-type=web" | xargs -r docker stop >&2
}
@test "[$TEST_FILE] start a nginx-proxy container" {
# GIVEN nginx-proxy
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
}
@test "[$TEST_FILE] nginx-proxy defaults to the service running on port 80" {
# WHEN
prepare_web_container bats-web-${TEST_FILE}-1 "80 90" -e VIRTUAL_HOST=web.bats
# THEN
assert_response_is_from_port 80
}
@test "[$TEST_FILE] VIRTUAL_PORT=90 while port 80 is also exposed" {
# GIVEN
prepare_web_container bats-web-${TEST_FILE}-2 "80 90" -e VIRTUAL_HOST=web.bats -e VIRTUAL_PORT=90
# THEN
assert_response_is_from_port 90
}
@test "[$TEST_FILE] single exposed port != 80" {
# GIVEN
prepare_web_container bats-web-${TEST_FILE}-3 1234 -e VIRTUAL_HOST=web.bats
# THEN
assert_response_is_from_port 1234
}
# assert querying nginx-proxy provides a response from the expected port of the web container
# $1 port we are expecting an response from
function assert_response_is_from_port {
local -r port=$1
run curl_container $SUT_CONTAINER /data --header "Host: web.bats"
assert_output "answer from port $port"
}
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
# GIVEN nginx-proxy
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] nginx-proxy defaults to the service running on port 80" {
# WHEN
prepare_web_container bats-web-${TEST_FILE}-1 "80 90" -e VIRTUAL_HOST=web.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-web-${TEST_FILE}-1
sleep 1
# THEN
assert_response_is_from_port 80
}
@test "[$TEST_FILE] VIRTUAL_PORT=90 while port 80 is also exposed" {
# GIVEN
prepare_web_container bats-web-${TEST_FILE}-2 "80 90" -e VIRTUAL_HOST=web.bats -e VIRTUAL_PORT=90
dockergen_wait_for_event $SUT_CONTAINER start bats-web-${TEST_FILE}-2
sleep 1
# THEN
assert_response_is_from_port 90
}
@test "[$TEST_FILE] single exposed port != 80" {
# GIVEN
prepare_web_container bats-web-${TEST_FILE}-3 1234 -e VIRTUAL_HOST=web.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-web-${TEST_FILE}-3
sleep 1
# THEN
assert_response_is_from_port 1234
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# assert querying nginx-proxy provides a response from the expected port of the web container
# $1 port we are expecting an response from
function assert_response_is_from_port {
local -r port=$1
run curl_container $SUT_CONTAINER /data --header "Host: web.bats"
assert_output "answer from port $port"
}

146
test/ssl.bats Normal file
View file

@ -0,0 +1,146 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro -v ${DIR}/lib/ssl:/etc/nginx/certs:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] test SSL for VIRTUAL_HOST=*.nginx-proxy.bats" {
# WHEN
prepare_web_container bats-ssl-hosts-1 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-1
sleep 1
# THEN
assert_301 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
}
@test "[$TEST_FILE] test HTTPS_METHOD=nohttp" {
# WHEN
prepare_web_container bats-ssl-hosts-2 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats \
-e HTTPS_METHOD=nohttp
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-2
sleep 1
# THEN
assert_503 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
}
@test "[$TEST_FILE] test HTTPS_METHOD=noredirect" {
# WHEN
prepare_web_container bats-ssl-hosts-3 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats \
-e HTTPS_METHOD=noredirect
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-3
sleep 1
# THEN
assert_200 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
}
@test "[$TEST_FILE] test SSL Strict-Transport-Security" {
# WHEN
prepare_web_container bats-ssl-hosts-4 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-1
sleep 1
# THEN
assert_301 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
assert_output -p "Strict-Transport-Security: max-age=31536000"
}
@test "[$TEST_FILE] test HTTPS_METHOD=noredirect disables Strict-Transport-Security" {
# WHEN
prepare_web_container bats-ssl-hosts-5 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats \
-e HTTPS_METHOD=noredirect
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-3
sleep 1
# THEN
assert_200 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
refute_output -p "Strict-Transport-Security: max-age=31536000"
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_301 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 301 Moved Permanently\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200_https {
local -r host=$1
run curl_container_https $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503_https {
local -r host=$1
run curl_container_https $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_301_https {
local -r host=$1
run curl_container_https $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 301 Moved Permanently\r'
}

View file

@ -1,7 +1,6 @@
# Test if requirements are met
(
type docker &>/dev/null || ( echo "docker is not available"; exit 1 )
type curl &>/dev/null || ( echo "curl is not available"; exit 1 )
)>&2
@ -34,6 +33,7 @@ function nginxproxy {
shift
docker_clean $container_name \
&& docker run -d \
--label bats-type="nginx-proxy" \
--name $container_name \
"$@" \
$SUT_IMAGE \
@ -67,13 +67,30 @@ function curl_container {
local -r container=$1
local -r path=$2
shift 2
curl --silent \
docker run --label bats-type="curl" appropriate/curl --silent \
--connect-timeout 5 \
--max-time 20 \
"$@" \
http://$(docker_ip $container)${path}
}
# Send a HTTPS request to container $1 for path $2 and
# Additional curl options can be passed as $@
#
# $1 container name
# $2 HTTPS path to query
# $@ additional options to pass to the curl command
function curl_container_https {
local -r container=$1
local -r path=$2
shift 2
docker run --label bats-type="curl" appropriate/curl --silent \
--connect-timeout 5 \
--max-time 20 \
--insecure \
"$@" \
https://$(docker_ip $container)${path}
}
# start a container running (one or multiple) webservers listening on given ports
#
@ -87,6 +104,7 @@ function prepare_web_container {
local -r options="$@"
local expose_option=""
IFS=$' \t\n' # See https://github.com/sstephenson/bats/issues/89
for port in $ports; do
expose_option="${expose_option}--expose=$port "
done
@ -108,21 +126,59 @@ function prepare_web_container {
-w /var/www/ \
$options \
-e PYTHON_PORTS="$ports" \
python:3 sh -c "
python:3 bash -c "
trap '[ \${#PIDS[@]} -gt 0 ] && kill -TERM \${PIDS[@]}' TERM
declare -a PIDS
for port in \$PYTHON_PORTS; do
echo starting a web server listening on port \$port;
mkdir /var/www/\$port
cd /var/www/\$port
echo \"answer from port \$port\" > data
python -m http.server \$port &
PIDS+=(\$!)
done
wait
wait \${PIDS[@]}
trap - TERM
wait \${PIDS[@]}
"
assert_success
# THEN querying directly port works
IFS=$' \t\n' # See https://github.com/sstephenson/bats/issues/89
for port in $ports; do
run retry 5 1s curl --silent --fail http://$(docker_ip $container_name):$port/data
run retry 5 1s docker run --label bats-type="curl" appropriate/curl --silent --fail http://$(docker_ip $container_name):$port/data
assert_output "answer from port $port"
done
}
}
# stop all containers with the "bats-type" label (matching the optionally supplied value)
#
# $1 optional label value
function stop_bats_containers {
local -r value=$1
if [ -z "$value" ]; then
CIDS=( $(docker ps -q --filter "label=bats-type") )
else
CIDS=( $(docker ps -q --filter "label=bats-type=$value") )
fi
if [ ${#CIDS[@]} -gt 0 ]; then
docker stop ${CIDS[@]} >&2
fi
}
# wait for a docker-gen container to receive a specified event from a
# container with the specified ID/name
#
# $1 docker-gen container name
# $2 event
# $3 ID/name of container to receive event from
function dockergen_wait_for_event {
local -r container=$1
local -r event=$2
local -r other=$3
local -r did=$(docker_id "$other")
docker_wait_for_log "$container" 9 "Received event $event for container ${did:0:12}"
}

View file

@ -1,68 +1,78 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
docker ps -q --filter "label=bats-type=web" | xargs -r docker stop >&2
}
@test "[$TEST_FILE] start a nginx-proxy container" {
# GIVEN
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 3 "Watching docker events"
}
@test "[$TEST_FILE] VIRTUAL_HOST=*.wildcard.bats" {
# WHEN
prepare_web_container bats-wildcard-hosts-1 80 -e VIRTUAL_HOST=*.wildcard.bats
# THEN
assert_200 f00.wildcard.bats
assert_200 bar.wildcard.bats
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] VIRTUAL_HOST=wildcard.bats.*" {
# WHEN
prepare_web_container bats-wildcard-hosts-2 80 -e VIRTUAL_HOST=wildcard.bats.*
# THEN
assert_200 wildcard.bats.f00
assert_200 wildcard.bats.bar
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] VIRTUAL_HOST=~^foo\.bar\..*\.bats" {
# WHEN
prepare_web_container bats-wildcard-hosts-2 80 -e VIRTUAL_HOST=~^foo\.bar\..*\.bats
# THEN
assert_200 foo.bar.whatever.bats
assert_200 foo.bar.why.not.bats
assert_503 unexpected.host.bats
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
# GIVEN
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] VIRTUAL_HOST=*.wildcard.bats" {
# WHEN
prepare_web_container bats-wildcard-hosts-1 80 -e VIRTUAL_HOST=*.wildcard.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-wildcard-hosts-1
sleep 1
# THEN
assert_200 f00.wildcard.bats
assert_200 bar.wildcard.bats
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] VIRTUAL_HOST=wildcard.bats.*" {
# WHEN
prepare_web_container bats-wildcard-hosts-2 80 -e VIRTUAL_HOST=wildcard.bats.*
dockergen_wait_for_event $SUT_CONTAINER start bats-wildcard-hosts-2
sleep 1
# THEN
assert_200 wildcard.bats.f00
assert_200 wildcard.bats.bar
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] VIRTUAL_HOST=~^foo\.bar\..*\.bats" {
# WHEN
prepare_web_container bats-wildcard-hosts-2 80 -e VIRTUAL_HOST=~^foo\.bar\..*\.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-wildcard-hosts-2
sleep 1
# THEN
assert_200 foo.bar.whatever.bats
assert_200 foo.bar.why.not.bats
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}