diff --git a/README.md b/README.md index 2aa38ff..8533d51 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,31 @@ Note: providing a port number in `VIRTUAL_HOST` isn't suported, please see [virt The nginx-proxy images are available in two flavors. #### Debian based version +### Virtual Host Aliases + +You can add aliases that will redirect (301) to the first entry in `VIRTUAL_HOST` by adding the `VIRTUAL_HOST_ALIAS` env var: + + $ docker run -e VIRTUAL_HOST=example.com -e VIRTUAL_HOST_ALIAS=www.example.com,old.example.com ... + +This will setup the following redirects: +- `http://www.example.com` → `http://example.com` +- `http://old.example.com` → `http://example.com` + +If you are using [letsencrypt-nginx-proxy-companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion) for SSL support, then you would run: + + $ docker run -e VIRTUAL_HOST=example.com \ + -e VIRTUAL_HOST_ALIAS=www.example.com,old.example.com + -e LETSENCRYPT_HOST=example.com,www.example.com,old.example.com + ... + +This will setup the following redirects: + - `http://example.com` → `https://example.com` + - `http://www.example.com` → `https://example.com` + - `http://old.example.com` → `https://example.com` + - `https://www.example.com` → `https://example.com` + - `https://old.example.com` → `https://example.com` + + This image is based on the nginx:mainline image, itself based on the debian slim image. diff --git a/nginx.tmpl b/nginx.tmpl index 14e30ca..0f9e67b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1041,5 +1041,153 @@ server { return {{ $globals.config.default_root_response }}; } {{- end }} + return 500; + + ssl_certificate /etc/nginx/certs/default.crt; + ssl_certificate_key /etc/nginx/certs/default.key; +} +{{ end }} + +{{ end }} +{{ end }} + +# VIRTUAL_HOST_ALIAS +{{ range $host_alias, $containers := groupBy $ "Env.VIRTUAL_HOST_ALIAS" }} +{{ $host_alias := replace $host_alias "," " " 99 }} + +{{ $first_host := (first (groupByKeys $containers "Env.VIRTUAL_HOST")) }} +{{ $first_host := trim $first_host }} + +# First Host {{ $first_host }} + +#Alias: {{ $host_alias }} + +{{ $default_host := or ($.Env.DEFAULT_HOST) "" }} +{{ $default_server := index (dict $host_alias "" $default_host "default_server") $host_alias }} + +{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} +{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} + +{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} +{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} + +{{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}} +{{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} + +{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} +{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }} + +{{/* Get the first cert name defined by containers w/ the same vhost */}} +{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} + +{{/* Get the best matching cert by name for the vhost. */}} +{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host_alias))}} + +{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} +{{ $vhostCert := trimSuffix ".crt" $vhostCert }} +{{ $vhostCert := trimSuffix ".key" $vhostCert }} + +{{/* Use the cert specified on the container or fallback to the best vhost match */}} +{{ $cert := (coalesce $certName $vhostCert) }} + +{{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} + +{{ if $is_https }} + +server { + server_name {{ $host_alias }}; + {{ if eq $https_method "redirect" }} + listen {{ $external_http_port }} {{ $default_server }}; + {{ end }} + listen {{ $external_https_port }} ssl http2 {{ $default_server }}; + {{ if $enable_ipv6 }} + {{ if eq $https_method "redirect" }} + listen [::]:{{ $external_http_port }} {{ $default_server }}; + {{ end }} + listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }}; + {{ end }} + access_log /var/log/nginx/access.log vhost; + + {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }} + + 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) }}; + + {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} + ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; + {{ end }} + + {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; + {{ end }} + + {{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }} + add_header Strict-Transport-Security "{{ trim $hsts }}" always; + {{ end }} + + {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host_alias)) }} + include {{ printf "/etc/nginx/vhost.d/%s" $host_alias }}; + {{ else if (exists "/etc/nginx/vhost.d/default") }} + include /etc/nginx/vhost.d/default; + {{ end }} + + location / { + return 301 https://{{ $first_host }}$request_uri; + } +} + +{{ end }} + +{{ if or (not $is_https) (eq $https_method "noredirect") }} + +server { + server_name {{ $host_alias }}; + listen {{ $external_http_port }} {{ $default_server }}; + {{ if $enable_ipv6 }} + listen [::]:80 {{ $default_server }}; + {{ end }} + access_log /var/log/nginx/access.log vhost; + + {{ if eq $network_tag "internal" }} + # Only allow traffic from internal clients + include /etc/nginx/network_internal.conf; + {{ end }} + + {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host_alias)) }} + include {{ printf "/etc/nginx/vhost.d/%s" $host_alias }}; + {{ else if (exists "/etc/nginx/vhost.d/default") }} + include /etc/nginx/vhost.d/default; + {{ end }} + + {{ if not (exists "/etc/nginx/vhost.d/default") }} + # Do not HTTPS redirect Let'sEncrypt ACME challenge + location /.well-known/acme-challenge/ { + auth_basic off; + allow all; + root /usr/share/nginx/html; + try_files $uri =404; + break; + } + {{ end }} + + location / { + return 301 https://{{ $first_host }}$request_uri; + } +} + +{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +server { + server_name {{ $host_alias }}; + listen {{ $external_https_port }} ssl http2 {{ $default_server }}; + {{ if $enable_ipv6 }} + listen [::]:443 ssl http2 {{ $default_server }}; + {{ end }} + access_log /var/log/nginx/access.log vhost; } {{- end }}