From b8699cde8cf210e864e4bfab7329dcaaf40ec995 Mon Sep 17 00:00:00 2001 From: Monviech <79600909+Monviech@users.noreply.github.com> Date: Sun, 22 Sep 2024 09:53:55 +0200 Subject: [PATCH] www/caddy: Allow access lists in handlers (#4245) * www/caddy: Allow access lists in handlers. The new unique_suffix constructs the named matcher so that it is always unique. Also small bug that the invert did not render was fixed. * www/caddy: Create inline access list logic that matches in the scope of a handler before the reverse_proxy directive, effectively blocking connections on matched IPs before they process further. * www/caddy: Rework access list feature to use a single macro and work the same in domains, subdomains and handlers. Remove abort statement from general settings since the new logic makes it mandatory. * www/caddy: Actually add the new template to the commit * www/caddy: Hide Access List Response Code in advanced mode, since abort is now the standard action. Add changelog. * www/caddy: Remove default values from Access List Response, since abort is the default. Add it as hint to dialogAccessList. Fix two small bugs: Layer4 ports do not render since the generalSettings was missing, Subdomains do not have ports, so replace with description. --- www/caddy/pkg-descr | 6 ++ .../OPNsense/Caddy/forms/dialogAccessList.xml | 7 +- .../OPNsense/Caddy/forms/dialogHandle.xml | 7 ++ .../OPNsense/Caddy/forms/general.xml | 6 -- .../mvc/app/models/OPNsense/Caddy/Caddy.xml | 13 +++- .../views/OPNsense/Caddy/reverse_proxy.volt | 1 + .../templates/OPNsense/Caddy/Caddyfile | 77 +++++++------------ 7 files changed, 58 insertions(+), 59 deletions(-) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index 8f4f1679f..6585ef223 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -16,9 +16,15 @@ Plugin Changelog 1.7.1 * Add: Frontend HTTP Version can be selected in General Settings, can be used to disable QUIC protocol +* Add: Access Lists can now be defined per HTTP Handler in advanced mode * Change: Caddy Domains widget will now open links to managed websites in new browser tabs +* Change: To select tls_insecure_skip_verify in a HTTP Handler, the protocol https:// must be chosen +* Change: Abort from General Settings has been removed, it is now automatically added to all Access Lists +* Cleanup: Caddyfile template logic for Access Lists has been rewritten * Cleanup: TLS checkboxes have been converted to dropdowns with http/https for clarity * Cleanup: Layer4, Domain and Handle dialogues have been cleaned up, some options are now hidden in advanced mode +* Fix: Layer4 default ports did not render due to regression in previous version +* Fix: Invert in Access Lists did not render due to regression in previous version 1.7.0 diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogAccessList.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogAccessList.xml index b38f500fd..50c2718ab 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogAccessList.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogAccessList.xml @@ -23,13 +23,16 @@ accesslist.HttpResponseCode text - + abort + + true accesslist.HttpResponseMessage text - + + true accesslist.description diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogHandle.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogHandle.xml index b8402ecca..6cbba6939 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogHandle.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogHandle.xml @@ -53,6 +53,13 @@ true + + handle.accesslist + + dropdown + + true + handle.ForwardAuth 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 09ef4f2ab..3b87e2cc1 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 @@ -59,12 +59,6 @@ dropdown - - caddy.general.abort - - checkbox - - caddy.general.GracePeriod 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 e489d2f6e..d04bda3df 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 @@ -84,7 +84,6 @@ - Y 0 @@ -288,7 +287,7 @@ OPNsense.Caddy.Caddy reverseproxy.subdomain - FromDomain,FromPort + FromDomain,description %s %s @@ -305,6 +304,16 @@ /^(\/.*)?$/u Please enter a valid Path that starts with '/'. + + + + OPNsense.Caddy.Caddy + reverseproxy.accesslist + accesslistName,description + %s %s + + +
diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/reverse_proxy.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/reverse_proxy.volt index 6d3aca02f..2473da5bf 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/reverse_proxy.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/reverse_proxy.volt @@ -474,6 +474,7 @@ {{ lang._('Upstream Path') }} {{ lang._('Upstream Fail Duration') }} {{ lang._('Forward Auth') }} + {{ lang._('Access List') }} {{ lang._('HTTP Version') }} {{ lang._('HTTP Keepalive') }} {{ lang._('TLS Trust Pool') }} diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile index be973608a..a710e61ed 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile @@ -234,7 +234,7 @@ # There are no regressions to set these ports up. #} -{% if enableLayer4|default("0") == "1" %} +{% if generalSettings.EnableLayer4|default("0") == "1" %} # Layer4 default HTTP port {% if httpPort %} :{{ httpPort }} { @@ -379,6 +379,8 @@ http://{{ domain }} { #} {% macro reverse_proxy_configuration(handle) %} {{ handle.HandleType }} {{ handle.HandlePath|default("") }} { + {{ handle_accesslist(handle.accesslist) }} + {# All IPs not matched by accesslist will continue processing #} {% if handle.ForwardAuth|default("0") == "1" %} {% include "OPNsense/Caddy/includeAuthProvider" %} {% endif %} @@ -433,25 +435,29 @@ http://{{ domain }} { {# # Macro: handle_accesslist -# Purpose: Manages the logic for access lists, including configuration and handling. +# Purpose: Manages the logic for access lists, used for handlers, domains and subdomains # Parameters: # @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 handle_accesslist(accesslist, content, invert=false) %} +{% macro handle_accesslist(accesslist) %} + {# Access list logic for dropping connections based on IP #} {% 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 }} + {% if accesslist_obj %} + {% set client_ips = accesslist_obj.clientIps.split(',') | join(' ') %} + @{{ accesslist_obj['@uuid'] }} { + {# Non-inverted access lists have "not" as default, inverted ones '' #} + {{ 'not' if accesslist_obj.accesslistInvert|default("0") == "0" else '' }} client_ip {{ client_ips }} + } + {# When IP is matched, abort or send response code. This will end processing and drop the request #} + handle @{{ accesslist_obj['@uuid'] }} { + {% if accesslist_obj.HttpResponseCode %} + respond {{ '"' + accesslist_obj.HttpResponseMessage + '"' if accesslist_obj.HttpResponseMessage else '' }} {{ accesslist_obj.HttpResponseCode }} + {% else %} + abort + {% endif %} + } + {% endif %} {% endif %} {% endmacro %} @@ -474,25 +480,6 @@ http://{{ domain }} { {% 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), @@ -567,25 +554,17 @@ http://{{ domain }} { } 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) }} - - {% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', subdomain.accesslist) | first %} - {{ handle_response(accesslist, Pischem.caddy.general.abort|default("0")) }} + {{ handle_accesslist(subdomain.accesslist) }} + {# All IPs not matched by accesslist will continue processing #} + {{ render_handles(subdomain_handles, subdomain.basicauth) }} } {% endif %} {% endfor %} - {% 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 %} - {{ handle_response(accesslist, Pischem.caddy.general.abort|default("0")) }} + {% set domain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %} + {{ handle_accesslist(reverse.accesslist) }} + {# All IPs not matched by accesslist will continue processing #} + {{ render_handles(domain_handles, reverse.basicauth) }} } {% endif %} {% endfor %}