|
@@ -11,6 +11,7 @@
|
|
{{- $_ := set $globals "Env" $.Env }}
|
|
{{- $_ := set $globals "Env" $.Env }}
|
|
{{- $_ := set $globals "Docker" $.Docker }}
|
|
{{- $_ := set $globals "Docker" $.Docker }}
|
|
{{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }}
|
|
{{- $_ := set $globals "CurrentContainer" (where $globals.containers "ID" $globals.Docker.CurrentContainerID | first) }}
|
|
|
|
+{{- $_ := set $globals "default_cert_ok" (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
|
|
{{- $_ := set $globals "external_http_port" (coalesce $globals.Env.HTTP_PORT "80") }}
|
|
{{- $_ := set $globals "external_http_port" (coalesce $globals.Env.HTTP_PORT "80") }}
|
|
{{- $_ := set $globals "external_https_port" (coalesce $globals.Env.HTTPS_PORT "443") }}
|
|
{{- $_ := set $globals "external_https_port" (coalesce $globals.Env.HTTPS_PORT "443") }}
|
|
{{- $_ := set $globals "sha1_upstream_name" (parseBool (coalesce $globals.Env.SHA1_UPSTREAM_NAME "false")) }}
|
|
{{- $_ := set $globals "sha1_upstream_name" (parseBool (coalesce $globals.Env.SHA1_UPSTREAM_NAME "false")) }}
|
|
@@ -19,6 +20,7 @@
|
|
{{- $_ := set $globals "access_log" (or (and (not $globals.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }}
|
|
{{- $_ := set $globals "access_log" (or (and (not $globals.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }}
|
|
{{- $_ := set $globals "enable_ipv6" (parseBool (coalesce $globals.Env.ENABLE_IPV6 "false")) }}
|
|
{{- $_ := set $globals "enable_ipv6" (parseBool (coalesce $globals.Env.ENABLE_IPV6 "false")) }}
|
|
{{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }}
|
|
{{- $_ := set $globals "ssl_policy" (or ($globals.Env.SSL_POLICY) "Mozilla-Intermediate") }}
|
|
|
|
+{{- $_ := set $globals "vhosts" (dict) }}
|
|
{{- $_ := set $globals "networks" (dict) }}
|
|
{{- $_ := set $globals "networks" (dict) }}
|
|
# Networks available to the container running docker-gen (which are assumed to
|
|
# Networks available to the container running docker-gen (which are assumed to
|
|
# match the networks available to the container running nginx):
|
|
# match the networks available to the container running nginx):
|
|
@@ -345,36 +347,103 @@ proxy_set_header X-Original-URI $request_uri;
|
|
proxy_set_header Proxy "";
|
|
proxy_set_header Proxy "";
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
|
|
|
|
+{{- /*
|
|
|
|
+ * Precompute some information about each vhost. This is done early because
|
|
|
|
+ * the creation of fallback servers depends on DEFAULT_HOST, HTTPS_METHOD,
|
|
|
|
+ * and whether there are any missing certs.
|
|
|
|
+ */}}
|
|
|
|
+{{- range $vhost, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }}
|
|
|
|
+ {{- $vhost := trim $vhost }}
|
|
|
|
+ {{- if not $vhost }}
|
|
|
|
+ {{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}}
|
|
|
|
+ {{- continue }}
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- $certName := first (groupByKeys $containers "Env.CERT_NAME") }}
|
|
|
|
+ {{- $vhostCert := closest (dir "/etc/nginx/certs") (printf "%s.crt" $vhost) }}
|
|
|
|
+ {{- $vhostCert = trimSuffix ".crt" $vhostCert }}
|
|
|
|
+ {{- $vhostCert = trimSuffix ".key" $vhostCert }}
|
|
|
|
+ {{- $cert := or $certName $vhostCert }}
|
|
|
|
+ {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }}
|
|
|
|
+ {{- $default := eq $globals.Env.DEFAULT_HOST $vhost }}
|
|
|
|
+ {{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) $globals.Env.HTTPS_METHOD "redirect" }}
|
|
|
|
+ {{- $_ := set $globals.vhosts $vhost (dict "cert" $cert "cert_ok" $cert_ok "containers" $containers "default" $default "https_method" $https_method) }}
|
|
|
|
+{{- end }}
|
|
|
|
+
|
|
|
|
+{{- /*
|
|
|
|
+ * If needed, create a catch-all fallback server to send an error code to
|
|
|
|
+ * clients that request something from an unknown vhost.
|
|
|
|
+ */}}
|
|
|
|
+{{- block "fallback_server" $globals }}
|
|
|
|
+ {{- $globals := . }}
|
|
|
|
+ {{- $http_exists := false }}
|
|
|
|
+ {{- $https_exists := false }}
|
|
|
|
+ {{- $default_http_exists := false }}
|
|
|
|
+ {{- $default_https_exists := false }}
|
|
|
|
+ {{- range $vhost := $globals.vhosts }}
|
|
|
|
+ {{- $http := or (ne $vhost.https_method "nohttp") (not $vhost.cert_ok) }}
|
|
|
|
+ {{- $https := ne $vhost.https_method "nohttps" }}
|
|
|
|
+ {{- $http_exists = or $http_exists $http }}
|
|
|
|
+ {{- $https_exists = or $https_exists $https }}
|
|
|
|
+ {{- $default_http_exists = or $default_http_exists (and $http $vhost.default) }}
|
|
|
|
+ {{- $default_https_exists = or $default_https_exists (and $https $vhost.default) }}
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- $fallback_http := and $http_exists (not $default_http_exists) }}
|
|
|
|
+ {{- $fallback_https := and $https_exists (not $default_https_exists) }}
|
|
|
|
+ {{- /*
|
|
|
|
+ * If there are no vhosts at all, create fallbacks for both plain http
|
|
|
|
+ * and https so that clients get something more useful than a connection
|
|
|
|
+ * refused error.
|
|
|
|
+ */}}
|
|
|
|
+ {{- if and (not $http_exists) (not $https_exists) }}
|
|
|
|
+ {{- $fallback_http = true }}
|
|
|
|
+ {{- $fallback_https = true }}
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- if or $fallback_http $fallback_https }}
|
|
server {
|
|
server {
|
|
server_name _; # This is just an invalid value which will never trigger on a real hostname.
|
|
server_name _; # This is just an invalid value which will never trigger on a real hostname.
|
|
server_tokens off;
|
|
server_tokens off;
|
|
- listen {{ $globals.external_http_port }};
|
|
|
|
-{{- if $globals.enable_ipv6 }}
|
|
|
|
- listen [::]:{{ $globals.external_http_port }};
|
|
|
|
-{{- end }}
|
|
|
|
|
|
+ {{- if $fallback_http }}
|
|
|
|
+ listen {{ $globals.external_http_port }} default_server;
|
|
|
|
+ {{- if $globals.enable_ipv6 }}
|
|
|
|
+ listen [::]:{{ $globals.external_http_port }} default_server;
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- if $fallback_https }}
|
|
|
|
+ listen {{ $globals.external_https_port }} ssl http2 default_server;
|
|
|
|
+ {{- if $globals.enable_ipv6 }}
|
|
|
|
+ listen [::]:{{ $globals.external_https_port }} ssl http2 default_server;
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- end }}
|
|
{{ $globals.access_log }}
|
|
{{ $globals.access_log }}
|
|
- return 503;
|
|
|
|
-
|
|
|
|
-{{- if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
|
|
|
|
- listen {{ $globals.external_https_port }} ssl http2;
|
|
|
|
- {{- if $globals.enable_ipv6 }}
|
|
|
|
- listen [::]:{{ $globals.external_https_port }} ssl http2;
|
|
|
|
- {{- end }}
|
|
|
|
-
|
|
|
|
|
|
+ {{- if $globals.default_cert_ok }}
|
|
ssl_session_cache shared:SSL:50m;
|
|
ssl_session_cache shared:SSL:50m;
|
|
ssl_session_tickets off;
|
|
ssl_session_tickets off;
|
|
ssl_certificate /etc/nginx/certs/default.crt;
|
|
ssl_certificate /etc/nginx/certs/default.crt;
|
|
ssl_certificate_key /etc/nginx/certs/default.key;
|
|
ssl_certificate_key /etc/nginx/certs/default.key;
|
|
-{{- end }}
|
|
|
|
|
|
+ {{- else }}
|
|
|
|
+ # No default.crt certificate found for this vhost, so force nginx to emit a
|
|
|
|
+ # TLS error if the client connects via https.
|
|
|
|
+ {{- /* See the comment in the main `server` directive for rationale. */}}
|
|
|
|
+ ssl_ciphers aNULL;
|
|
|
|
+ set $empty "";
|
|
|
|
+ ssl_certificate data:$empty;
|
|
|
|
+ ssl_certificate_key data:$empty;
|
|
|
|
+ if ($https) {
|
|
|
|
+ return 444;
|
|
|
|
+ }
|
|
|
|
+ {{- end }}
|
|
|
|
+ return 503;
|
|
}
|
|
}
|
|
|
|
+ {{- end }}
|
|
|
|
+{{- end }}
|
|
|
|
|
|
-{{- range $host, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }}
|
|
|
|
|
|
+{{- range $host, $vhost := $globals.vhosts }}
|
|
|
|
+ {{- $cert := $vhost.cert }}
|
|
|
|
+ {{- $cert_ok := $vhost.cert_ok }}
|
|
|
|
+ {{- $containers := $vhost.containers }}
|
|
|
|
+ {{- $default_server := when $vhost.default "default_server" "" }}
|
|
|
|
+ {{- $https_method := $vhost.https_method }}
|
|
|
|
|
|
- {{- $host := trim $host }}
|
|
|
|
- {{- if not $host }}
|
|
|
|
- {{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}}
|
|
|
|
- {{- continue }}
|
|
|
|
- {{- end }}
|
|
|
|
{{- $is_regexp := hasPrefix "~" $host }}
|
|
{{- $is_regexp := hasPrefix "~" $host }}
|
|
{{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }}
|
|
{{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $host) $host }}
|
|
|
|
|
|
@@ -394,22 +463,12 @@ server {
|
|
{{ template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers) }}
|
|
{{ template "upstream" (dict "globals" $globals "Upstream" $upstream "Containers" $containers) }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
|
|
- {{- $default_host := or ($globals.Env.DEFAULT_HOST) "" }}
|
|
|
|
- {{- $default_server := index (dict $host "" $default_host "default_server") $host }}
|
|
|
|
-
|
|
|
|
{{- /*
|
|
{{- /*
|
|
* Get the SERVER_TOKENS defined by containers w/ the same vhost,
|
|
* Get the SERVER_TOKENS defined by containers w/ the same vhost,
|
|
* falling back to "".
|
|
* falling back to "".
|
|
*/}}
|
|
*/}}
|
|
{{- $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }}
|
|
{{- $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }}
|
|
|
|
|
|
-
|
|
|
|
- {{- /*
|
|
|
|
- * Get the HTTPS_METHOD defined by containers w/ the same vhost, falling
|
|
|
|
- * back to "redirect".
|
|
|
|
- */}}
|
|
|
|
- {{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $globals.Env.HTTPS_METHOD "redirect") }}
|
|
|
|
-
|
|
|
|
{{- /*
|
|
{{- /*
|
|
* Get the SSL_POLICY defined by containers w/ the same vhost, falling
|
|
* Get the SSL_POLICY defined by containers w/ the same vhost, falling
|
|
* back to empty string (use default).
|
|
* back to empty string (use default).
|
|
@@ -425,29 +484,7 @@ server {
|
|
{{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
|
|
{{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
|
|
{{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
|
|
{{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
|
|
|
|
|
|
-
|
|
|
|
- {{- /* Get the first cert name defined by containers w/ the same vhost */}}
|
|
|
|
- {{- $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
|
|
|
|
-
|
|
|
|
- {{- /* Get the best matching cert by name for the vhost. */}}
|
|
|
|
- {{- $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 := 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 and $is_https (eq $https_method "redirect") }}
|
|
|
|
|
|
+ {{- if and $cert_ok (eq $https_method "redirect") }}
|
|
server {
|
|
server {
|
|
server_name {{ $host }};
|
|
server_name {{ $host }};
|
|
{{- if $server_tokens }}
|
|
{{- if $server_tokens }}
|
|
@@ -485,19 +522,20 @@ server {
|
|
server_tokens {{ $server_tokens }};
|
|
server_tokens {{ $server_tokens }};
|
|
{{- end }}
|
|
{{- end }}
|
|
{{ $globals.access_log }}
|
|
{{ $globals.access_log }}
|
|
- {{- if or (not $is_https) (eq $https_method "noredirect") }}
|
|
|
|
|
|
+ {{- if or (eq $https_method "nohttps") (not $cert_ok) (eq $https_method "noredirect") }}
|
|
listen {{ $globals.external_http_port }} {{ $default_server }};
|
|
listen {{ $globals.external_http_port }} {{ $default_server }};
|
|
{{- if $globals.enable_ipv6 }}
|
|
{{- if $globals.enable_ipv6 }}
|
|
listen [::]:{{ $globals.external_http_port }} {{ $default_server }};
|
|
listen [::]:{{ $globals.external_http_port }} {{ $default_server }};
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
- {{- if $is_https }}
|
|
|
|
|
|
+ {{- if ne $https_method "nohttps" }}
|
|
listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }};
|
|
listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }};
|
|
{{- if $globals.enable_ipv6 }}
|
|
{{- if $globals.enable_ipv6 }}
|
|
listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }};
|
|
listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }};
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
|
|
- {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
|
|
|
|
|
|
+ {{- if $cert_ok }}
|
|
|
|
+ {{- template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
|
|
|
|
|
|
ssl_session_timeout 5m;
|
|
ssl_session_timeout 5m;
|
|
ssl_session_cache shared:SSL:50m;
|
|
ssl_session_cache shared:SSL:50m;
|
|
@@ -506,22 +544,50 @@ server {
|
|
ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }};
|
|
ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }};
|
|
ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }};
|
|
ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }};
|
|
|
|
|
|
- {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
|
|
|
|
|
|
+ {{- if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
|
|
ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
|
|
ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
|
|
- {{- end }}
|
|
|
|
|
|
+ {{- end }}
|
|
|
|
|
|
- {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
|
|
|
|
|
|
+ {{- if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
|
|
ssl_stapling on;
|
|
ssl_stapling on;
|
|
ssl_stapling_verify on;
|
|
ssl_stapling_verify on;
|
|
ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
|
|
ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
|
|
- {{- end }}
|
|
|
|
|
|
+ {{- end }}
|
|
|
|
|
|
- {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }}
|
|
|
|
|
|
+ {{- if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }}
|
|
set $sts_header "";
|
|
set $sts_header "";
|
|
if ($https) {
|
|
if ($https) {
|
|
set $sts_header "{{ trim $hsts }}";
|
|
set $sts_header "{{ trim $hsts }}";
|
|
}
|
|
}
|
|
add_header Strict-Transport-Security $sts_header always;
|
|
add_header Strict-Transport-Security $sts_header always;
|
|
|
|
+ {{- end }}
|
|
|
|
+ {{- else if $globals.default_cert_ok }}
|
|
|
|
+ # No certificate found for this vhost, so use the default certificate and
|
|
|
|
+ # return an error code if the user connects via https.
|
|
|
|
+ ssl_certificate /etc/nginx/certs/default.crt;
|
|
|
|
+ ssl_certificate_key /etc/nginx/certs/default.key;
|
|
|
|
+ if ($https) {
|
|
|
|
+ return 500;
|
|
|
|
+ }
|
|
|
|
+ {{- else }}
|
|
|
|
+ # No certificate found for this vhost, so force nginx to emit a TLS error if
|
|
|
|
+ # the client connects via https.
|
|
|
|
+ {{- /*
|
|
|
|
+ * The alternative is to not provide an https server for this
|
|
|
|
+ * vhost, which would either cause the user to see the wrong
|
|
|
|
+ * vhost (if there is another vhost with a certificate) or a
|
|
|
|
+ * connection refused error (if there is no other vhost with a
|
|
|
|
+ * certificate). A TLS error is easier to troubleshoot, and is
|
|
|
|
+ * safer than serving the wrong vhost. Also see
|
|
|
|
+ * <https://serverfault.com/a/1044022>.
|
|
|
|
+ */}}
|
|
|
|
+ ssl_ciphers aNULL;
|
|
|
|
+ set $empty "";
|
|
|
|
+ ssl_certificate data:$empty;
|
|
|
|
+ ssl_certificate_key data:$empty;
|
|
|
|
+ if ($https) {
|
|
|
|
+ return 444;
|
|
|
|
+ }
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
|
|
@@ -558,23 +624,4 @@ server {
|
|
}
|
|
}
|
|
{{- end }}
|
|
{{- end }}
|
|
}
|
|
}
|
|
-
|
|
|
|
- {{- if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
|
|
|
|
-server {
|
|
|
|
- server_name {{ $host }};
|
|
|
|
- {{- if $server_tokens }}
|
|
|
|
- server_tokens {{ $server_tokens }};
|
|
|
|
- {{- end }}
|
|
|
|
- listen {{ $globals.external_https_port }} ssl http2 {{ $default_server }};
|
|
|
|
- {{- if $globals.enable_ipv6 }}
|
|
|
|
- listen [::]:{{ $globals.external_https_port }} ssl http2 {{ $default_server }};
|
|
|
|
- {{- end }}
|
|
|
|
- {{ $globals.access_log }}
|
|
|
|
- return 500;
|
|
|
|
-
|
|
|
|
- ssl_certificate /etc/nginx/certs/default.crt;
|
|
|
|
- ssl_certificate_key /etc/nginx/certs/default.key;
|
|
|
|
-}
|
|
|
|
- {{- end }}
|
|
|
|
-
|
|
|
|
{{- end }}
|
|
{{- end }}
|