From 8adc2eb146787ba237ef5135050d147d27c92aa2 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sat, 11 Jan 2025 00:27:45 +0100 Subject: [PATCH] nginx.tmpl: use closest vhost.d and htpasswd files (#1309) Add logic to find the closest matching files for vhost.d, vhost.d _include and htpasswd files. This allows multiple hosts to share these files, similar to wildcard certificate files, but not limited to just 1 subdomain depth. The match is anchored to the end of the string filename, so a largest suffix match is found. For example, 'vhost.d/b.c' will match a 'a.b.c' virtual host. But 'a.b' will not match 'a.b.c'. This is stricter than the "closest" match function that certificate files used in the past, which would also match in the 2nd case. This is a WIP, unfinished sections are marked with TODO. --- nginx.tmpl | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index d18b2ab..71982e5 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -280,6 +280,7 @@ {{- define "location" }} {{- $vpath := .VPath }} + {{- /* TODO: largest suffix match location_override (same as for include_file and htpasswd_file) */}} {{- $override := printf "/etc/nginx/vhost.d/%s_%s_location_override" .Host (sha1 .Path) }} {{- if and (eq .Path "/") (not (exists $override)) }} {{- $override = printf "/etc/nginx/vhost.d/%s_location_override" .Host }} @@ -322,14 +323,16 @@ set $upstream_keepalive {{ if ne $keepalive "disabled" }}true{{ else }}false{{ end }}; {{- end }} + {{- /* TODO: largest suffix match for host_sha1path too? */}} {{- if (exists (printf "/etc/nginx/htpasswd/%s_%s" .Host (sha1 .Path) )) }} auth_basic "Restricted {{ .Host }}{{ .Path }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s_%s" .Host (sha1 .Path)) }}; - {{- else if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }} + {{- else if (ne .HtpasswdFile "") }} auth_basic "Restricted {{ .HostIsRegexp | ternary "access" .Host }}"; - auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" .Host) }}; + auth_basic_user_file {{ .HtpasswdFile }}; {{- end }} + {{- /* TODO: largest suffix match location_override (same as for include_file and htpasswd_file) */}} {{- if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }} include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }}; {{- else if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} @@ -739,6 +742,36 @@ proxy_set_header Proxy ""; {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} {{- $vhost_root := groupByKeys $vhost_containers "Env.VIRTUAL_ROOT" | first | default "/var/www/public" }} + {{- /* Get the best matching vhost.d include file */}} + {{- $include_file := "" }} + {{- range (dir "/etc/nginx/vhost.d") }} + {{- if (and (hasSuffix . $hostname) (gt (len .) (len $include_file))) }} + {{- $include_file = . }} + {{- end }} + {{- end }} + {{- $include_file = when (ne $include_file "") (print "/etc/nginx/vhost.d/" $include_file) "" }} + {{- $include_file = when (exists $include_file) $include_file "" }} + + {{- /* Get the best matching vhost.d location include file */}} + {{- $location_file := "" }} + {{- range (dir "/etc/nginx/vhost.d") }} + {{- if (and (hasSuffix . (print $hostname "_location")) (gt (len .) (len $location_file))) }} + {{- $location_file = . }} + {{- end }} + {{- end }} + {{- $location_file = when (ne $location_file "") (print "/etc/nginx/vhost.d/" $location_file) "" }} + {{- $location_file = when (exists $location_file) $location_file "" }} + + {{- /* Get the best matching htpasswd file */}} + {{- $htpasswd_file := "" }} + {{- range (dir "/etc/nginx/htpasswd") }} + {{- if (and (hasSuffix . $hostname) (gt (len .) (len $htpasswd_file))) }} + {{- $htpasswd_file = . }} + {{- end }} + {{- end }} + {{- $htpasswd_file = when (ne $htpasswd_file "") (print "/etc/nginx/htpasswd/" $htpasswd_file) "" }} + {{- $htpasswd_file = when (exists $htpasswd_file) $htpasswd_file "" }} + {{- $vhost_data = merge $vhost_data (dict "cert" $cert "cert_ok" $cert_ok @@ -756,6 +789,9 @@ proxy_set_header Proxy ""; "trust_default_cert" $trust_default_cert "upstream_name" $upstream_name "vhost_root" $vhost_root + "include_file" $include_file + "location_file" $location_file + "htpasswd_file" $htpasswd_file ) }} {{- $_ := set $globals.vhosts $hostname $vhost_data }} {{- end }} @@ -897,6 +933,7 @@ server { server { {{- if $vhost.is_regexp }} + {{- /* TODO: largest suffix match? what does this if-block even do? */}} {{- if or (printf "/etc/nginx/vhost.d/%s" $hostname | exists) (printf "/etc/nginx/vhost.d/%s_location" $hostname | exists) @@ -988,10 +1025,10 @@ server { {{- end }} {{- end }} - {{- $vhostFileName := $vhost.is_regexp | ternary (sha1 $hostname) $hostname }} + {{- $vhostFileName := $vhost.is_regexp | ternary (print "/etc/nginx/vhost.d/" (sha1 $hostname)) $vhost.include_file }} - {{- if (exists (printf "/etc/nginx/vhost.d/%s" $vhostFileName)) }} - include {{ printf "/etc/nginx/vhost.d/%s" $vhostFileName }}; + {{- if (ne $vhostFileName "") }} + include {{ $vhostFileName }}; {{- else if (exists "/etc/nginx/vhost.d/default") }} include /etc/nginx/vhost.d/default; {{- end }} @@ -1003,10 +1040,12 @@ server { {{- range $path, $vpath := $vhost.paths }} {{- template "location" (dict "Path" $path - "Host" $vhostFileName + "Host" ($vhost.is_regexp | ternary (sha1 $hostname) $hostname) "HostIsRegexp" $vhost.is_regexp "VhostRoot" $vhost.vhost_root "VPath" $vpath + "LocationFile" $vhost.location_file + "HtpasswdFile" $vhost.htpasswd_file ) }} {{- end }}