diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogSubdomain.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogSubdomain.xml index 558c702a2..ccfc1c2ba 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogSubdomain.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogSubdomain.xml @@ -81,4 +81,23 @@ false + + subdomain.ClientAuthTrustPool + + select_multiple + + + false + + + + subdomain.ClientAuthMode + + dropdown + true + + + false + + diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml index b6f0c86d1..2d5a1167b 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/general.xml @@ -180,6 +180,16 @@ 0 + + header + + + + caddy.general.TlsDnsEchDomain + + text + + diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml index 3e1fdc217..7e9d03c24 100644 --- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml +++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.xml @@ -109,6 +109,9 @@ N + + N + @@ -337,6 +340,18 @@ + + require_and_verify + + request + require + verify_if_given + + + + ca + Y + diff --git a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php index a6494eb97..0c14379c4 100755 --- a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php +++ b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_certs.php @@ -100,6 +100,19 @@ foreach ((new Caddy())->reverseproxy->reverse->iterateItems() as $reverseItem) { } } +foreach ((new Caddy())->reverseproxy->subdomain->iterateItems() as $subdomainItem) { + $caCertField = (string)$subdomainItem->ClientAuthTrustPool; + + if (!empty($caCertField)) { + $refs = array_map('trim', explode(',', $caCertField)); + foreach ($refs as $ref) { + if (!empty($ref)) { + $caCertRefs[] = $ref; + } + } + } +} + $caCertRefs = array_unique($caCertRefs); foreach ((new Ca())->ca->iterateItems() as $caItem) { diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile index 8e027500b..acbf13bc6 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile @@ -1,5 +1,5 @@ {# -# Copyright (c) 2023-2024 Cedrik Pischem +# Copyright (c) 2023-2025 Cedrik Pischem # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -220,6 +220,19 @@ } {% endif %} + {# + # Section: Encrypted ClientHello (ECH) Configuration + # https://caddyserver.com/docs/caddyfile/options#ech + #} + {% if generalSettings.TlsDnsEchDomain|default("") and dnsProvider %} + dns {{ dnsProvider }} {% if dnsProvider not in dnsProviderSpecialConfig %}{{ dnsApiKey }}{% else %}{ + {% set context_var = 'dnsProviderSpecialLogic' %} + {% include "OPNsense/Caddy/includeDnsProvider" %} + } + {% endif +%} + ech {{ generalSettings.TlsDnsEchDomain }} + {% endif %} + {# # Section: ACME Email, Auto HTTPS selection and global import statement # Purpose: The ACME email is optional for receiving certificate notices. @@ -304,21 +317,21 @@ http://{{ domain }} { # certificates with the DNS-01 challenge. Refer to Dynamic DNS section for more details. #} {% macro tls_configuration( - customCert, - dnsChallenge, - clientAuthTrustPool, - clientAuthMode, - dnsProvider, - dnsApiKey, - dnsSecretApiKey, - tlsDnsOptionalField1, - tlsDnsOptionalField2, - tlsDnsOptionalField3, - tlsDnsOptionalField4, - tlsDnsPropagationTimeout, - tlsDnsPropagationTimeoutPeriod, - tlsDnsPropagationDelay, - tlsDnsPropagationResolvers + customCert="", + dnsChallenge="0", + clientAuthTrustPool="", + clientAuthMode="", + dnsProvider="", + dnsApiKey="", + dnsSecretApiKey="", + tlsDnsOptionalField1="", + tlsDnsOptionalField2="", + tlsDnsOptionalField3="", + tlsDnsOptionalField4="", + tlsDnsPropagationTimeout="", + tlsDnsPropagationTimeoutPeriod="", + tlsDnsPropagationDelay="", + tlsDnsPropagationResolvers="" ) %} {% if customCert or (dnsChallenge == "1" and dnsProvider) or clientAuthTrustPool %} tls {% if customCert %}/var/db/caddy/data/caddy/certificates/temp/{{ customCert }}.pem /var/db/caddy/data/caddy/certificates/temp/{{ customCert }}.key{% endif %} { @@ -586,13 +599,10 @@ http://{{ domain }} { # - 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 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'] }}" {% 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" %} @@ -606,44 +616,58 @@ http://{{ domain }} { {% endif %} {% endif %} {{ tls_configuration( - reverse.CustomCertificate|default(""), - reverse.DnsChallenge|default("0"), - reverse.ClientAuthTrustPool|default(""), - reverse.ClientAuthMode|default(""), - generalSettings.TlsDnsProvider, - generalSettings.TlsDnsApiKey, - generalSettings.TlsDnsSecretApiKey, - generalSettings.TlsDnsOptionalField1, - generalSettings.TlsDnsOptionalField2, - generalSettings.TlsDnsOptionalField3, - generalSettings.TlsDnsOptionalField4, - generalSettings.TlsDnsPropagationTimeout, - generalSettings.TlsDnsPropagationTimeoutPeriod, - generalSettings.TlsDnsPropagationDelay, - generalSettings.TlsDnsPropagationResolvers + customCert=reverse.CustomCertificate|default(""), + dnsChallenge=reverse.DnsChallenge|default("0"), + clientAuthTrustPool=reverse.ClientAuthTrustPool|default(""), + clientAuthMode=reverse.ClientAuthMode|default(""), + dnsProvider=generalSettings.TlsDnsProvider, + dnsApiKey=generalSettings.TlsDnsApiKey, + dnsSecretApiKey=generalSettings.TlsDnsSecretApiKey, + tlsDnsOptionalField1=generalSettings.TlsDnsOptionalField1, + tlsDnsOptionalField2=generalSettings.TlsDnsOptionalField2, + tlsDnsOptionalField3=generalSettings.TlsDnsOptionalField3, + tlsDnsOptionalField4=generalSettings.TlsDnsOptionalField4, + tlsDnsPropagationTimeout=generalSettings.TlsDnsPropagationTimeout, + tlsDnsPropagationTimeoutPeriod=generalSettings.TlsDnsPropagationTimeoutPeriod, + tlsDnsPropagationDelay=generalSettings.TlsDnsPropagationDelay, + tlsDnsPropagationResolvers=generalSettings.TlsDnsPropagationResolvers ) }} - - {% 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 %} - {{ handle_accesslist(subdomain.accesslist, subdomain.FromDomain) }} - {# All IPs not matched by accesslist will continue processing #} - {{ render_basic_auth(subdomain.basicauth) }} - {{ render_handles(subdomain_handles) }} - } - {% endif %} - {% endfor %} - {% set domain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %} {{ handle_accesslist(reverse.accesslist, reverse.FromDomain) }} - {# All IPs not matched by accesslist will continue processing #} {{ render_basic_auth(reverse.basicauth) }} {{ render_handles(domain_handles) }} } + + {% endif %} +{% endfor %} + +{% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %} + {% if subdomain.enabled|default("0") == "1" %} + {% set reverse = helpers.toList('Pischem.caddy.reverseproxy.reverse') | selectattr('@uuid', 'equalto', subdomain.reverse) | first %} + {% if reverse and reverse.enabled|default("0") == "1" %} + {% if reverse.DisableTls|default("0") == "1" %}http://{% endif %}{{ subdomain.FromDomain|default("") }}{% if reverse.FromPort %}:{{ reverse.FromPort }}{% endif %} { + {% if reverse.AccessLog|default("0") == "1" %} + {% if generalSettings.LogAccessPlain|default("0") == "0" %} + log {{ subdomain['@uuid'] }} + {% else %} + log { + output file /var/log/caddy/access/{{ subdomain['@uuid'] }}.log { + roll_keep_for {{ generalSettings.LogAccessPlainKeep|default("10") }}d + } + } + {% endif %} + {% endif %} + {{ tls_configuration( + clientAuthTrustPool=subdomain.ClientAuthTrustPool|default(""), + clientAuthMode=subdomain.ClientAuthMode|default("") + ) }} + {% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %} + {{ handle_accesslist(subdomain.accesslist, subdomain.FromDomain) }} + {{ render_basic_auth(subdomain.basicauth) }} + {{ render_handles(subdomain_handles) }} + } + + {% endif %} {% endif %} {% endfor %}