www/caddy: Refactor Caddyfile template to make it more dry (#4211)

* www/caddy: Refactor Caddyfile to make it more dry. All prior functionality remains the same.

* www/caddy: Fix typo in TlsInsecureSkipVerify
This commit is contained in:
Monviech
2024-09-03 07:08:28 +02:00
committed by GitHub
parent fc0bf2d9f8
commit 0e03ffbd6f
@@ -275,34 +275,28 @@
{#
# Section: HTTP-01 Challenge Redirection
# Purpose: A small premade reverse_proxy section
# that can redirect the HTTP-01 challenge to a different webserver.
# Purpose: Redirects HTTP-01 challenges to a different webserver for reverse proxies and subdomains.
#}
{% macro http01_challenge_redirection(domain, acme_passthrough) %}
http://{{ domain }} {
handle /.well-known/acme-challenge/* {
reverse_proxy {{ acme_passthrough }}
}
handle {
redir https://{host}{uri} 308
}
}
{% endmacro %}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
{% if reverse.enabled|default("0") == "1" and reverse.AcmePassthrough %}
# HTTP-01 challenge redirection for domain: "{{ reverse['@uuid'] }}"
http://{{ reverse.FromDomain|default("") }} {
handle /.well-known/acme-challenge/* {
reverse_proxy {{ reverse.AcmePassthrough }}
}
handle {
redir https://{host}{uri} 308
}
}
{{ http01_challenge_redirection(reverse.FromDomain|default(""), reverse.AcmePassthrough) }}
{% endif %}
{% endfor %}
{# Process redirection for subdomains afterwards, since a redirection of a wildcard domain has to match before them. #}
{% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
{% if subdomain.enabled|default("0") == "1" and subdomain.AcmePassthrough %}
# HTTP-01 challenge redirection for subdomain: "{{ subdomain['@uuid'] }}"
http://{{ subdomain.FromDomain|default("") }} {
handle /.well-known/acme-challenge/* {
reverse_proxy {{ subdomain.AcmePassthrough }}
}
handle {
redir https://{host}{uri} 308
}
}
{{ http01_challenge_redirection(subdomain.FromDomain|default(""), subdomain.AcmePassthrough) }}
{% endif %}
{% endfor %}
@@ -399,24 +393,9 @@
{#
# Macro: reverse_proxy_configuration
# Purpose: Sets up the handle with the reverse proxy configurations. The TLS Settings are generated here for the Upstream.
# Integrated Macros: header_manipulation
# Purpose: Sets up the handle with the reverse proxy configurations.
# Parameters:
# @param handle (object):
# - @uuid (string)
# - HandleType (string): Specifies the handling strategy.
# - HandlePath (string, optional): The path the handle should match on.
# - ToDomain (string): Target domain for the reverse proxy.
# - ToPort (string, optional): Target port on the ToDomain.
# - ToPath (string, optional): Destination path on the ToDomain.
# - HttpTls (boolean, optional): Enable TLS for the connection.
# - HttpNtlm (boolean, optional): Enable NTLM authentication for the connection. Not all HTTP options apply to NTLM.
# - HttpTlsInsecureSkipVerify (boolean, optional): If true, the server's SSL certificate is not verified.
# - HttpTlsTrustedCaCerts (string, optional): The config extracted name of a CA certificate.
# - HttpTlsServerName (string, optional): Specifies the server name for the TLS handshake.
# - PassiveHealthFailDuration (integer, optional): Enables passive health checks when set > 0.
# - HttpVersion (string, optional): Choose HTTP version. Empty (default) is 1.1 and 2.
# - HttpKeepalive (string, optional): Keeaplive is either off (0) or a value. Empty (default) 120s.
# @param handle (object)
#}
{% macro reverse_proxy_configuration(handle) %}
{{ handle.HandleType }} {{ handle.HandlePath|default("") }} {
@@ -436,27 +415,15 @@
{% if handle.PassiveHealthFailDuration|default("") %}
fail_duration {{ handle.PassiveHealthFailDuration }}s
{% endif %}
{% if handle.HttpTls|default("0") == "1" or handle.HttpTlsInsecureSkipVerify|default("0") == "1" or handle.HttpTlsTrustedCaCerts or handle.HttpTlsServerName or handle.HttpVersion or handle.HttpKeepalive %}
{% set has_transport_options = handle.HttpVersion or handle.HttpKeepalive or handle.HttpTls|default("0") == "1" or handle.HttpTlsesecureSkipVerify|default("0") == "1" or handle.HttpTlsTrustedCaCerts or handle.HttpTlsServerName %}
{% if has_transport_options %}
{% if handle.HttpNtlm|default("0") == "1" %}
transport http_ntlm {
{% if handle.HttpTls|default("0") == "1" %}
tls
{% endif %}
{% if handle.HttpTlsInsecureSkipVerify|default("0") == "1" %}
tls_insecure_skip_verify
{% endif %}
{% if handle.HttpTlsTrustedCaCerts %}
tls_trust_pool file /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
{% endif %}
{% if handle.HttpTlsServerName %}
tls_server_name {{ handle.HttpTlsServerName }}
{% endif %}
}
{% else %}
transport http {
{# The model does not allow to set a single number as option directly, so we have to map them. #}
{% set version_map = {'http1': 1.1, 'http2': 2, 'http3': 3} %}
{% endif %}
{% if handle.HttpVersion %}
{% set version_map = {'http1': 1.1, 'http2': 2, 'http3': 3} %}
versions {{ version_map[handle.HttpVersion] }}
{% endif %}
{% if handle.HttpKeepalive %}
@@ -479,43 +446,42 @@
tls_server_name {{ handle.HttpTlsServerName }}
{% endif %}
}
{% endif %}
{% endif %}
}
}
{% endmacro %}
{#
# Macro: access_list_configuration
# Purpose: Defines access lists based on client IP addresses. The standard logic is "allow these IP addresses, deny all others."
# A handle with an @ matcher is created that will put the reverse_proxy_configuration inside. That means, the traffic will
# only get to the reverse proxy, when the access list matches. Invert is also possible, to explicitely deny IPs.
# The assembly is handled by the "Section: Reverse Proxy Configurations".
# Macro: handle_accesslist
# Purpose: Manages the logic for access lists, including configuration and handling.
# Parameters:
# @param accesslist (object):
# - uuid (string)
# - clientIps (string): A comma-separated list of client IP addresses
# - invert (boolean): A flag that inverts the logic of the access list
# @param accesslist (string): The UUID of the access list to be applied.
# @param content (string): The content to be wrapped in the access list handler.
# @param invert (boolean): A flag that inverts the logic of the access list.
#}
{% macro access_list_configuration(accesslist, invert) %}
{% set client_ips = accesslist.clientIps.split(',') %}
{% set client_ips_space_separated = client_ips | join(' ') %}
@{{ accesslist['@uuid'] }} {
{{ 'not' if invert else '' }} client_ip {{ client_ips_space_separated }}
}
{% macro handle_accesslist(accesslist, content, invert=false) %}
{% if accesslist %}
{% set accesslist_obj = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', accesslist) | first %}
{% set client_ips = accesslist_obj.clientIps.split(',') %}
{% set client_ips_space_separated = client_ips | join(' ') %}
@{{ accesslist_obj['@uuid'] }} {
{{ 'not' if invert or accesslist_obj.accesslistenvert|default("0") == "1" else '' }} client_ip {{ client_ips_space_separated }}
}
handle @{{ accesslist_obj['@uuid'] }} {
{{ content }}
}
{% else %}
{{ content }}
{% endif %}
{% endmacro %}
{#
# Macro: basicauth_configuration
# Purpose: Implements basic authentication with a username and password for access.
# Macro: render_basic_auth
# Purpose: Renders the basic authentication configuration.
# Parameters:
# @param basicauth_uuids (string): A comma-separated list of UUIDs, each UUID corresponding to
# a specific user credentials (username and password).
# - @uuid (string)
# - basicauthuser (string): The username required for authentication.
# - basicauthpass (string): The password associated with the username.
# @param basicauth_uuids (string): A comma-separated list of UUIDs for basic authentication.
#}
{% macro basicauth_configuration(basicauth_uuids) %}
{% macro render_basic_auth(basicauth_uuids) %}
{% if basicauth_uuids %}
basic_auth {
{% for uuid in basicauth_uuids.split(',') %}
@@ -528,24 +494,64 @@
{% endif %}
{% endmacro %}
{#
# Macro: handle_response
# Purpose: Manages the response logic for access lists and aborts.
# Parameters:
# @param accesslist (object): The access list object containing response settings.
# @param general_abort (string): The global abort setting.
#}
{% macro handle_response(accesslist, general_abort) %}
{% if accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif general_abort == "1" %}
abort
{% endif %}
{% elif general_abort == "1" %}
abort
{% endif %}
{% endmacro %}
{#
# Macro: render_handles
# Purpose: Renders the handles in the correct order (path-specific first, then catch-all),
# including basic authentication configuration.
# Parameters:
# @param handles (list): A list of handle objects to be rendered.
# @param basicauth_uuids (string): A comma-separated list of UUIDs for basic authentication.
#}
{% macro render_handles(handles, basicauth_uuids=None) %}
{{ render_basic_auth(basicauth_uuids) }}
{% for handle in handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endmacro %}
{#
# Section: Reverse Proxy Configurations
# Purpose: Assembles reverse proxy configurations using predefined macros.
# This is the main logic of the whole template, handle with care.
# Macros Used:
# - tls_configuration
# - basicauth_configuration
# - access_list_configuration
# - reverse_proxy_configuration
# - indirect: header_manipulation
# - tls_configuration: Configures TLS settings for the domain.
# - handle_accesslist: Manages the logic for access lists, including configuration.
# - render_handles: Renders the handles in the correct order, including basic authentication.
# - reverse_proxy_configuration: Sets up the handle with reverse proxy configurations.
# - handle_response: Manages the response logic for access lists and aborts.
# Important Details:
# - Order of Path specific Handles - Prioritizes order of specific path handles over catch-all handles.
# - Order of Path specific Handles: Prioritizes order of specific path handles over catch-all handles.
# - Order of Wildcard Domains and Subdomains: Handles for wildcard domains come after all subdomains.
#}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
{% if reverse.enabled|default("0") == "1" %}
# Reverse Proxy Domain: "{{ reverse['@uuid'] }}"
{# The default are encrypted connections, uncencrypted connections have to render http:// #}
{% if reverse.DisableTls|default("0") == "1" %}http://{% endif %}{{ reverse.FromDomain|default("") }}{% if reverse.FromPort %}:{{ reverse.FromPort }}{% endif %} {
{% if reverse.AccessLog|default("0") == "1" %}
{% if generalSettings.LogAccessPlain|default("0") == "0" %}
@@ -560,8 +566,6 @@
{% endif %}
{% set customCert = reverse.CustomCertificate|default("") %}
{% set dnsChallenge = reverse.DnsChallenge|default("0") %}
{% set tlsDnsPropagationTimeout = generalSettings.TlsDnsPropagationTimeout %}
{% set tlsDnsPropagationResolvers = generalSettings.TlsDnsPropagationResolvers %}
{{ tls_configuration(
customCert,
dnsChallenge,
@@ -572,122 +576,36 @@
tlsDnsOptionalField2,
tlsDnsOptionalField3,
tlsDnsOptionalField4,
tlsDnsPropagationTimeout,
tlsDnsPropagationResolvers
generalSettings.TlsDnsPropagationTimeout,
generalSettings.TlsDnsPropagationResolvers
) }}
{% if not reverse.accesslist %}
{% set basicauth_uuids = reverse.basicauth %}
{{ basicauth_configuration(basicauth_uuids) }}
{% endif %}
{% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
{% if subdomain.enabled|default("0") == "1" and subdomain.reverse == reverse['@uuid'] %}
@{{ subdomain['@uuid'] }} {
host {{ subdomain.FromDomain }}
}
handle @{{ subdomain['@uuid'] }} {
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% set subdomain_content %}
{{ render_handles(subdomain_handles, subdomain.basicauth) }}
{% endset %}
{{ handle_accesslist(subdomain.accesslist, subdomain_content) }}
{% if not subdomain.accesslist %}
{% set subdomain_basicauth_uuids = subdomain.basicauth %}
{{ basicauth_configuration(subdomain_basicauth_uuids) }}
{% endif %}
{% if subdomain.accesslist %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', subdomain.accesslist) | first %}
{{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
handle @{{ accesslist['@uuid'] }} {
{% set subdomain_basicauth_uuids = subdomain.basicauth %}
{{ basicauth_configuration(subdomain_basicauth_uuids) }}
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
}
{% else %}
{% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in subdomain_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endif %}
{% if subdomain.accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% else %}
{% if Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% endif %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', subdomain.accesslist) | first %}
{{ handle_response(accesslist, Pischem.caddy.general.abort|default("0")) }}
}
{% endif %}
{% endfor %}
{% if reverse.accesslist %}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
{{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
handle @{{ accesslist['@uuid'] }} {
{% set basicauth_uuids = reverse.basicauth %}
{{ basicauth_configuration(basicauth_uuids) }}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
}
{% else %}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% for handle in wildcard_handles %}
{% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
{{ reverse_proxy_configuration(handle) }}
{% endif %}
{% endfor %}
{% endif %}
{% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
{% set wildcard_content %}
{{ render_handles(wildcard_handles, reverse.basicauth) }}
{% endset %}
{{ handle_accesslist(reverse.accesslist, wildcard_content) }}
{% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
{% if accesslist %}
{% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
{% elif Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% else %}
{% if Pischem.caddy.general.abort|default("0") == "1" %}
abort
{% endif %}
{% endif %}
{{ handle_response(accesslist, Pischem.caddy.general.abort|default("0")) }}
}
{% endif %}
{% endfor %}