├── .gitignore ├── LICENSE ├── README.md ├── docker-compose ├── v1 │ └── simple-site │ │ └── docker-compose.yml └── v2 │ └── simple-site │ └── docker-compose.yml ├── docker-run └── simple-site │ └── docker-run.sh └── volumes ├── examples └── simple-site │ └── conf.d │ └── site.example.com.conf └── proxy └── templates ├── nginx-compose-v2.tmpl └── nginx.tmpl /.gitignore: -------------------------------------------------------------------------------- 1 | ### Miscellaneous 2 | .Spotlight-V100 3 | .Trashes 4 | .DS_Store 5 | .DS_Store? 6 | ehthumbs.db 7 | Thumbs.db 8 | 9 | ### IDE 10 | .idea/ 11 | *.cache 12 | 13 | volumes/proxy/certs/* 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Karl Fathi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-letsencrypt-nginx-proxy-companion-examples 2 | 3 | This repository is meant to be a starting point for working with [nginx-proxy](https://github.com/jwilder/nginx-proxy), [docker-gen](https://github.com/jwilder/docker-gen) and [docker-letsencrypt-nginx-proxy-companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion) by providing basic working bootstrapped examples that combines them. 4 | 5 | ## Choosing how to run things 6 | 7 | Running docker containers can be done either using the [command line interface](https://docs.docker.com/engine/reference/commandline/cli/) (CLI), or using [docker-compose](https://docs.docker.com/compose/overview/) 8 | 9 | ## docker run 10 | 11 | Replace all the default "site.example.com" and "mail@example.com" occurrences in docker-run/simple-site/docker-run.sh for your publicly accessible domain redirecting to the server running the script. 12 | 13 | ``` 14 | $ ./docker-run.sh 15 | ``` 16 | 17 | ## docker-compose v1 18 | 19 | While docker-compose v1 file version [isn't advised for starting new project](https://docs.docker.com/compose/compose-file/#upgrading), docker-gen hasn't yet officially resolved [the issue](https://github.com/jwilder/nginx-proxy/issues/304) raised by new networking in docker-compose v2, you might find it easier to stick with v1. 20 | 21 | Replace all the default "site.example.com" and "mail@example.com" occurrences in docker-compose/v1/simple-site/docker-compose.yml for your publicly accessible domain redirecting to the server running the script. 22 | 23 | ``` 24 | $ docker-compose up 25 | ``` 26 | 27 | ## docker-compose v2 28 | 29 | V2 support for docker-gen is being done through @almereyda's [unofficial solution](https://github.com/jwilder/nginx-proxy/issues/304#issuecomment-189775983). See above for compose v1. 30 | 31 | Replace all the default "site.example.com" and "mail@example.com" occurrences in docker-compose/v2/simple-site/docker-compose.yml for your publicly accessible domain redirecting to the server running the script. 32 | 33 | First, you'll need to create an external docker network named 'nginx-proxy' (or change the name in compose file). 34 | 35 | ``` 36 | docker network create -d bridge nginx-proxy 37 | ``` 38 | 39 | You'll only need to do this once. 40 | 41 | ``` 42 | $ docker-compose up 43 | ``` 44 | 45 | ## Debugging 46 | 47 | You should use [docker logs](https://docs.docker.com/engine/reference/commandline/logs/) to see the output of your daemonized containers. 48 | 49 | ``` 50 | $ docker logs nginx 51 | ``` 52 | 53 | You can also checkout the docker-gen generated "default.conf" file using [docker exec](https://docs.docker.com/engine/reference/commandline/exec/) 54 | 55 | ``` 56 | $ docker exec -it nginx-gen cat /etc/nginx/conf.d/default.conf 57 | ``` 58 | 59 | # Notes 60 | 61 | If you want to help out the community, feel free to submit a PR with other examples for popular site/apps such as Wordpress, ghost... 62 | 63 | I'll try to add some myself provided this repo gets enough stars. 64 | -------------------------------------------------------------------------------- /docker-compose/v1/simple-site/docker-compose.yml: -------------------------------------------------------------------------------- 1 | nginx: 2 | image: nginx 3 | container_name: nginx 4 | ports: 5 | - "80:80" 6 | - "443:443" 7 | volumes: 8 | - "/etc/nginx/conf.d" 9 | - "/etc/nginx/vhost.d" 10 | - "/usr/share/nginx/html" 11 | - "../../../volumes/proxy/certs:/etc/nginx/certs:ro" 12 | nginx-gen: 13 | image: jwilder/docker-gen 14 | container_name: nginx-gen 15 | volumes: 16 | - "/var/run/docker.sock:/tmp/docker.sock:ro" 17 | - "../../../volumes/proxy/templates/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro" 18 | volumes_from: 19 | - nginx 20 | entrypoint: /usr/local/bin/docker-gen -notify-sighup nginx -watch -only-exposed -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 21 | letsencrypt-nginx-proxy-companion: 22 | image: jrcs/letsencrypt-nginx-proxy-companion 23 | container_name: letsencrypt-nginx-proxy-companion 24 | volumes_from: 25 | - nginx 26 | volumes: 27 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 28 | - "../../../volumes/proxy/certs:/etc/nginx/certs:rw" 29 | environment: 30 | - NGINX_DOCKER_GEN_CONTAINER=nginx-gen 31 | 32 | simple-site: 33 | image: nginx 34 | container_name: simple-site 35 | volumes: 36 | - "../../../volumes/examples/simple-site/conf.d/:/etc/nginx/conf.d" 37 | environment: 38 | - VIRTUAL_HOST=site.example.com 39 | - LETSENCRYPT_HOST=site.example.com 40 | - LETSENCRYPT_EMAIL=email@example.com 41 | 42 | -------------------------------------------------------------------------------- /docker-compose/v2/simple-site/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | nginx: 5 | image: nginx 6 | container_name: nginx 7 | ports: 8 | - "80:80" 9 | - "443:443" 10 | volumes: 11 | - "/etc/nginx/conf.d" 12 | - "/etc/nginx/vhost.d" 13 | - "/usr/share/nginx/html" 14 | - "../../../volumes/proxy/certs:/etc/nginx/certs:ro" 15 | networks: 16 | - proxy-tier 17 | nginx-gen: 18 | image: jwilder/docker-gen 19 | container_name: nginx-gen 20 | volumes: 21 | - "/var/run/docker.sock:/tmp/docker.sock:ro" 22 | - "../../../volumes/proxy/templates/nginx-compose-v2.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro" 23 | volumes_from: 24 | - nginx 25 | entrypoint: /usr/local/bin/docker-gen -notify-sighup nginx -watch -only-exposed -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 26 | letsencrypt-nginx-proxy-companion: 27 | image: jrcs/letsencrypt-nginx-proxy-companion 28 | container_name: letsencrypt-nginx-proxy-companion 29 | volumes_from: 30 | - nginx 31 | volumes: 32 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 33 | - "../../../volumes/proxy/certs:/etc/nginx/certs:rw" 34 | environment: 35 | - NGINX_DOCKER_GEN_CONTAINER=nginx-gen 36 | 37 | simple-site: 38 | image: nginx 39 | container_name: simple-site 40 | volumes: 41 | - "../../../volumes/examples/simple-site/conf.d/:/etc/nginx/conf.d" 42 | environment: 43 | - VIRTUAL_HOST=site.example.com 44 | - VIRTUAL_NETWORK=nginx-proxy 45 | - VIRTUAL_PORT=80 46 | - LETSENCRYPT_HOST=site.example.com 47 | - LETSENCRYPT_EMAIL=email@example.com 48 | networks: 49 | - proxy-tier 50 | 51 | networks: 52 | proxy-tier: 53 | external: 54 | name: nginx-proxy 55 | -------------------------------------------------------------------------------- /docker-run/simple-site/docker-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This example will run a basic nginx server provisionned with an index.html file 3 | # Make sure to replace "site.example.com" with a public accessible domain poiting to the server you will run this on. 4 | 5 | # This nginx container will get a configuration generated by the docker-gen instance and act as a reverse-proxy 6 | echo "Starting nginx instance..." 7 | docker run -d -p 80:80 -p 443:443 \ 8 | --name nginx \ 9 | -v /etc/nginx/conf.d \ 10 | -v /etc/nginx/vhost.d \ 11 | -v /usr/share/nginx/html \ 12 | -v $(pwd)/../../volumes/proxy/certs:/etc/nginx/certs:ro \ 13 | nginx 14 | 15 | # This nginx-gen container using the docker-gen image will generate a 'default.conf' file from the 'nginx.tmpl' located in volumes/proxy/templates. 16 | echo "Starting docker-gen instance..." 17 | docker run -d \ 18 | --name nginx-gen \ 19 | --volumes-from nginx \ 20 | -v $(pwd)/../../volumes/proxy/templates/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro \ 21 | -v /var/run/docker.sock:/tmp/docker.sock:ro \ 22 | jwilder/docker-gen \ 23 | -notify-sighup nginx -watch -only-exposed -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 24 | 25 | 26 | echo "Starting letsencrypt-nginx-proxy-companion..." 27 | docker run -d \ 28 | -e "NGINX_DOCKER_GEN_CONTAINER=nginx-gen" \ 29 | --volumes-from nginx \ 30 | -v $(pwd)/../../volumes/proxy/certs:/etc/nginx/certs:rw \ 31 | -v /var/run/docker.sock:/var/run/docker.sock:ro \ 32 | jrcs/letsencrypt-nginx-proxy-companion 33 | 34 | 35 | # This an example service that will get picked up and served by the reverse proxy. 36 | # Make sure you change all the default values in this file and in volumes/examples/simple-site 37 | echo "Starting simple-site nginx example..." 38 | docker run -d \ 39 | --name simple-site \ 40 | -e "VIRTUAL_HOST=site.example.com" \ 41 | -e "LETSENCRYPT_HOST=site.example.com" \ 42 | -e "LETSENCRYPT_EMAIL=mail@example.com" \ 43 | -v $(pwd)/../../volumes/examples/simple-site/conf.d/:/etc/nginx/conf.d \ 44 | nginx 45 | -------------------------------------------------------------------------------- /volumes/examples/simple-site/conf.d/site.example.com.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name site.example.com; 4 | root /usr/share/nginx/html; 5 | } 6 | -------------------------------------------------------------------------------- /volumes/proxy/templates/nginx-compose-v2.tmpl: -------------------------------------------------------------------------------- 1 | {{ define "upstream" }} 2 | {{ if .Address }} 3 | {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} 4 | {{ if and .Container.Node.ID .Address.HostPort }} 5 | # {{ .Container.Node.Name }}/{{ .Container.Name }} 6 | server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; 7 | {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} 8 | {{ else }} 9 | # {{ .Container.Name }} 10 | server {{ .Address.IP }}:{{ .Address.Port }}; 11 | {{ end }} 12 | {{ else }} 13 | # {{ .Container.Name }} 14 | server {{ .Container.IP }} down; 15 | {{ end }} 16 | {{ end }} 17 | 18 | # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the 19 | # scheme used to connect to this server 20 | map $http_x_forwarded_proto $proxy_x_forwarded_proto { 21 | default $http_x_forwarded_proto; 22 | '' $scheme; 23 | } 24 | 25 | # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any 26 | # Connection header that may have been passed to this server 27 | map $http_upgrade $proxy_connection { 28 | default upgrade; 29 | '' close; 30 | } 31 | 32 | gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 33 | 34 | log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 35 | '"$request" $status $body_bytes_sent ' 36 | '"$http_referer" "$http_user_agent"'; 37 | 38 | access_log off; 39 | 40 | {{ if (exists "/etc/nginx/proxy.conf") }} 41 | include /etc/nginx/proxy.conf; 42 | {{ else }} 43 | # HTTP 1.1 support 44 | proxy_http_version 1.1; 45 | proxy_buffering off; 46 | proxy_set_header Host $http_host; 47 | proxy_set_header Upgrade $http_upgrade; 48 | proxy_set_header Connection $proxy_connection; 49 | proxy_set_header X-Real-IP $remote_addr; 50 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 51 | proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; 52 | {{ end }} 53 | 54 | server { 55 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 56 | listen 80; 57 | access_log /var/log/nginx/access.log vhost; 58 | return 503; 59 | } 60 | 61 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 62 | server { 63 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 64 | listen 443 ssl http2; 65 | access_log /var/log/nginx/access.log vhost; 66 | return 503; 67 | 68 | ssl_certificate /etc/nginx/certs/default.crt; 69 | ssl_certificate_key /etc/nginx/certs/default.key; 70 | } 71 | {{ end }} 72 | 73 | {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} 74 | 75 | upstream {{ $host }} { 76 | 77 | {{ range $index, $value := $containers }} 78 | 79 | {{ $addrLen := len $value.Addresses }} 80 | {{/* If only 1 port exposed, use that */}} 81 | {{ if eq $addrLen 1 }} 82 | {{ with $address := index $value.Addresses 0 }} 83 | # {{$value.Name}} 84 | server {{ $address.IP }}:{{ $address.Port }}; 85 | {{ end }} 86 | 87 | {{/* If a VIRTUAL_NETWORK is specified use use its IP */}} 88 | {{ else if $value.Env.VIRTUAL_NETWORK }} 89 | {{ range $i, $network := $value.Networks }} 90 | {{ if eq $network.Name $value.Env.VIRTUAL_NETWORK }} 91 | # Container: {{$value.Name}}@{{$network.Name}} 92 | server {{ $network.IP }}:{{ $value.Env.VIRTUAL_PORT }}; 93 | {{ end }} 94 | {{ end }} 95 | 96 | {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var */}} 97 | {{ else if $value.Env.VIRTUAL_PORT }} 98 | {{ range $i, $address := $value.Addresses }} 99 | {{ if eq $address.Port $value.Env.VIRTUAL_PORT }} 100 | # {{$value.Name}} 101 | server {{ $address.IP }}:{{ $address.Port }}; 102 | {{ end }} 103 | {{ end }} 104 | 105 | {{/* Else default to standard web port 80 */}} 106 | {{ else }} 107 | {{ range $i, $address := $value.Addresses }} 108 | {{ if eq $address.Port "80" }} 109 | # {{$value.Name}} 110 | server {{ $address.IP }}:{{ $address.Port }}; 111 | {{ end }} 112 | {{ end }} 113 | {{ end }} 114 | {{ end }} 115 | } 116 | 117 | {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} 118 | {{ $default_server := index (dict $host "" $default_host "default_server") $host }} 119 | 120 | {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} 121 | {{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }} 122 | 123 | {{/* Get the first cert name defined by containers w/ the same vhost */}} 124 | {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} 125 | 126 | {{/* Get the best matching cert by name for the vhost. */}} 127 | {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} 128 | 129 | {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} 130 | {{ $vhostCert := replace $vhostCert ".crt" "" -1 }} 131 | {{ $vhostCert := replace $vhostCert ".key" "" -1 }} 132 | 133 | {{/* Use the cert specifid on the container or fallback to the best vhost match */}} 134 | {{ $cert := (coalesce $certName $vhostCert) }} 135 | 136 | {{ if (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} 137 | 138 | server { 139 | server_name {{ $host }}; 140 | listen 80 {{ $default_server }}; 141 | access_log /var/log/nginx/access.log vhost; 142 | return 301 https://$host$request_uri; 143 | } 144 | 145 | server { 146 | server_name {{ $host }}; 147 | listen 443 ssl http2 {{ $default_server }}; 148 | access_log /var/log/nginx/access.log vhost; 149 | 150 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 151 | 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; 152 | 153 | ssl_prefer_server_ciphers on; 154 | ssl_session_timeout 5m; 155 | ssl_session_cache shared:SSL:50m; 156 | 157 | ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; 158 | ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; 159 | 160 | {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} 161 | ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; 162 | {{ end }} 163 | 164 | add_header Strict-Transport-Security "max-age=31536000"; 165 | 166 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 167 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 168 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 169 | include /etc/nginx/vhost.d/default; 170 | {{ end }} 171 | 172 | location / { 173 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 174 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 175 | auth_basic "Restricted {{ $host }}"; 176 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 177 | {{ end }} 178 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 179 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 180 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 181 | include /etc/nginx/vhost.d/default_location; 182 | {{ end }} 183 | } 184 | } 185 | {{ else }} 186 | 187 | server { 188 | server_name {{ $host }}; 189 | listen 80 {{ $default_server }}; 190 | access_log /var/log/nginx/access.log vhost; 191 | 192 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 193 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 194 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 195 | include /etc/nginx/vhost.d/default; 196 | {{ end }} 197 | 198 | location / { 199 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 200 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 201 | auth_basic "Restricted {{ $host }}"; 202 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 203 | {{ end }} 204 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 205 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 206 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 207 | include /etc/nginx/vhost.d/default_location; 208 | {{ end }} 209 | } 210 | } 211 | 212 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 213 | server { 214 | server_name {{ $host }}; 215 | listen 443 ssl http2 {{ $default_server }}; 216 | access_log /var/log/nginx/access.log vhost; 217 | return 503; 218 | 219 | ssl_certificate /etc/nginx/certs/default.crt; 220 | ssl_certificate_key /etc/nginx/certs/default.key; 221 | } 222 | {{ end }} 223 | 224 | {{ end }} 225 | {{ end }} 226 | -------------------------------------------------------------------------------- /volumes/proxy/templates/nginx.tmpl: -------------------------------------------------------------------------------- 1 | {{ define "upstream" }} 2 | {{ if .Address }} 3 | {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} 4 | {{ if and .Container.Node.ID .Address.HostPort }} 5 | # {{ .Container.Node.Name }}/{{ .Container.Name }} 6 | server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; 7 | {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} 8 | {{ else }} 9 | # {{ .Container.Name }} 10 | server {{ .Address.IP }}:{{ .Address.Port }}; 11 | {{ end }} 12 | {{ else }} 13 | # {{ .Container.Name }} 14 | server {{ .Container.IP }} down; 15 | {{ end }} 16 | {{ end }} 17 | 18 | # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the 19 | # scheme used to connect to this server 20 | map $http_x_forwarded_proto $proxy_x_forwarded_proto { 21 | default $http_x_forwarded_proto; 22 | '' $scheme; 23 | } 24 | 25 | # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any 26 | # Connection header that may have been passed to this server 27 | map $http_upgrade $proxy_connection { 28 | default upgrade; 29 | '' close; 30 | } 31 | 32 | gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 33 | 34 | log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 35 | '"$request" $status $body_bytes_sent ' 36 | '"$http_referer" "$http_user_agent"'; 37 | 38 | access_log off; 39 | 40 | {{ if (exists "/etc/nginx/proxy.conf") }} 41 | include /etc/nginx/proxy.conf; 42 | {{ else }} 43 | # HTTP 1.1 support 44 | proxy_http_version 1.1; 45 | proxy_buffering off; 46 | proxy_set_header Host $http_host; 47 | proxy_set_header Upgrade $http_upgrade; 48 | proxy_set_header Connection $proxy_connection; 49 | proxy_set_header X-Real-IP $remote_addr; 50 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 51 | proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; 52 | {{ end }} 53 | 54 | server { 55 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 56 | listen 80; 57 | access_log /var/log/nginx/access.log vhost; 58 | return 503; 59 | } 60 | 61 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 62 | server { 63 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 64 | listen 443 ssl http2; 65 | access_log /var/log/nginx/access.log vhost; 66 | return 503; 67 | 68 | ssl_certificate /etc/nginx/certs/default.crt; 69 | ssl_certificate_key /etc/nginx/certs/default.key; 70 | } 71 | {{ end }} 72 | 73 | {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} 74 | 75 | upstream {{ $host }} { 76 | {{ range $container := $containers }} 77 | {{ $addrLen := len $container.Addresses }} 78 | {{/* If only 1 port exposed, use that */}} 79 | {{ if eq $addrLen 1 }} 80 | {{ $address := index $container.Addresses 0 }} 81 | {{ template "upstream" (dict "Container" $container "Address" $address) }} 82 | {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}} 83 | {{ else }} 84 | {{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }} 85 | {{ $address := where $container.Addresses "Port" $port | first }} 86 | {{ template "upstream" (dict "Container" $container "Address" $address) }} 87 | {{ end }} 88 | {{ end }} 89 | } 90 | 91 | {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} 92 | {{ $default_server := index (dict $host "" $default_host "default_server") $host }} 93 | 94 | {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} 95 | {{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }} 96 | 97 | {{/* Get the first cert name defined by containers w/ the same vhost */}} 98 | {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} 99 | 100 | {{/* Get the best matching cert by name for the vhost. */}} 101 | {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} 102 | 103 | {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} 104 | {{ $vhostCert := replace $vhostCert ".crt" "" -1 }} 105 | {{ $vhostCert := replace $vhostCert ".key" "" -1 }} 106 | 107 | {{/* Use the cert specifid on the container or fallback to the best vhost match */}} 108 | {{ $cert := (coalesce $certName $vhostCert) }} 109 | 110 | {{ if (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} 111 | 112 | server { 113 | server_name {{ $host }}; 114 | listen 80 {{ $default_server }}; 115 | access_log /var/log/nginx/access.log vhost; 116 | return 301 https://$host$request_uri; 117 | } 118 | 119 | server { 120 | server_name {{ $host }}; 121 | listen 443 ssl http2 {{ $default_server }}; 122 | access_log /var/log/nginx/access.log vhost; 123 | 124 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 125 | 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; 126 | 127 | ssl_prefer_server_ciphers on; 128 | ssl_session_timeout 5m; 129 | ssl_session_cache shared:SSL:50m; 130 | 131 | ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; 132 | ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; 133 | 134 | {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} 135 | ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; 136 | {{ end }} 137 | 138 | add_header Strict-Transport-Security "max-age=31536000"; 139 | 140 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 141 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 142 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 143 | include /etc/nginx/vhost.d/default; 144 | {{ end }} 145 | 146 | location / { 147 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 148 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 149 | auth_basic "Restricted {{ $host }}"; 150 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 151 | {{ end }} 152 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 153 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 154 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 155 | include /etc/nginx/vhost.d/default_location; 156 | {{ end }} 157 | } 158 | } 159 | {{ else }} 160 | 161 | server { 162 | server_name {{ $host }}; 163 | listen 80 {{ $default_server }}; 164 | access_log /var/log/nginx/access.log vhost; 165 | 166 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 167 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 168 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 169 | include /etc/nginx/vhost.d/default; 170 | {{ end }} 171 | 172 | location / { 173 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 174 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 175 | auth_basic "Restricted {{ $host }}"; 176 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 177 | {{ end }} 178 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 179 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 180 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 181 | include /etc/nginx/vhost.d/default_location; 182 | {{ end }} 183 | } 184 | } 185 | 186 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 187 | server { 188 | server_name {{ $host }}; 189 | listen 443 ssl http2 {{ $default_server }}; 190 | access_log /var/log/nginx/access.log vhost; 191 | return 503; 192 | 193 | ssl_certificate /etc/nginx/certs/default.crt; 194 | ssl_certificate_key /etc/nginx/certs/default.key; 195 | } 196 | {{ end }} 197 | 198 | {{ end }} 199 | {{ end }} 200 | --------------------------------------------------------------------------------