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.HttpResponseCodetext
-
+ abort
+
+ trueaccesslist.HttpResponseMessagetext
-
+
+ trueaccesslist.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 @@
- Y0
@@ -288,7 +287,7 @@
OPNsense.Caddy.Caddyreverseproxy.subdomain
- FromDomain,FromPort
+ FromDomain,description%s %s
@@ -305,6 +304,16 @@
/^(\/.*)?$/uPlease 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 %}