From 8bc3779eeb304bc8d338b374e56d42c941a7d51b Mon Sep 17 00:00:00 2001 From: Monviech <79600909+Monviech@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:50:39 +0200 Subject: [PATCH] www/caddy: Add remote_ip matcher for access list functionality in Layer4 proxy (#4147) --- www/caddy/pkg-descr | 1 + .../OPNsense/Caddy/forms/dialogLayer4.xml | 13 +++++++++ .../mvc/app/models/OPNsense/Caddy/Caddy.xml | 6 ++++ .../views/OPNsense/Caddy/reverse_proxy.volt | 1 + .../templates/OPNsense/Caddy/includeLayer4 | 29 +++++++++++++++---- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index 4edfff9d1..df004fa8c 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -33,6 +33,7 @@ Plugin Changelog * Add: Authentik as authentication provider (contributed by Tim-Sc) * Add: Feature Preview - Layer4 routing to proxy traffic without TLS termination. Can be enabled in advanced mode of "General Settings" * Add: Layer4 protocols: HTTP, Postgres, Proxy Protocol, RDP, SOCKS4, SOCKS5, SSH, TLS, XMPP +* Add: Layer4 Remote IP matcher to restrict access to proxied services 1.6.1 diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml index 307937560..bbc7d6b30 100644 --- a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml +++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogLayer4.xml @@ -61,4 +61,17 @@ dropdown + + header + + true + + + layer4.RemoteIp + + select_multiple + + true + + 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 b27125460..d3c0d2ed2 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 @@ -438,6 +438,12 @@ v2 + + , + Y + Y + Please enter one or multiple valid IP addresses or networks. + 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 4df185cc7..f16163beb 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 @@ -561,6 +561,7 @@ {{ lang._('Matcher') }} {{ lang._('Upstream Domain') }} {{ lang._('Upstream Port') }} + {{ lang._('Remote IP') }} {{ lang._('Fail Duration') }} {{ lang._('Proxy Protocol') }} {{ lang._('Description') }} diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 index c6a20dab4..7eb367b6b 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 @@ -5,8 +5,8 @@ #} {% set layer4_configs = helpers.toList('Pischem.caddy.reverseproxy.layer4') %} -{# Define a macro for setting up the proxy #} -{% macro setup_proxy(to_domains, to_port, fail_duration, proxy_protocol) %} +{# Nested Macro for proxy definition #} +{% macro define_proxy(to_domains, to_port, fail_duration, proxy_protocol) %} proxy {% for domain in to_domains.split(',') %} {% set is_ipv6 = (':' in domain) %} {# Check if the domain contains a colon, typical in IPv6 addresses #} {{ '[' if is_ipv6 }}{{ domain }}{{ ']' if is_ipv6 }}:{{ to_port }}{% if not loop.last %} {% endif %} @@ -20,6 +20,23 @@ } {% endmacro %} +{# Macro for configuring the proxy with additional remote IP access list #} +{% macro configure_proxy(to_domains, to_port, remote_ips, fail_duration, proxy_protocol) %} + {% if remote_ips %} + {% set ip_list = remote_ips.split(',') %} + subroute { + @allowed_ips remote_ip {{ ip_list|join(' ') }} + route @allowed_ips { + {# Call Nested Macro #} + {{ define_proxy(to_domains, to_port, fail_duration, proxy_protocol) }} + } + } + {% else %} + {# Call Nested Macro #} + {{ define_proxy(to_domains, to_port, fail_duration, proxy_protocol) }} + {% endif %} +{% endmacro %} + {# Set up Layer4 App #} layer4 { import /usr/local/etc/caddy/caddy.d/*.layer4 @@ -28,7 +45,7 @@ layer4 { {% if layer4.enabled == "1" and layer4.Matchers not in ['httphost', 'tlssni', 'nottlssni'] %} @{{ layer4['@uuid'] }} {{ layer4.Matchers }} route @{{ layer4['@uuid'] }} { - {{ setup_proxy(layer4.ToDomain, layer4.ToPort, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} + {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} } {% endif %} {% endfor %} @@ -37,7 +54,7 @@ layer4 { {% if layer4.enabled == "1" and layer4.Matchers == 'httphost' %} @{{ layer4['@uuid'] }} http host {{ layer4.FromDomain.replace(',', ' ') }} route @{{ layer4['@uuid'] }} { - {{ setup_proxy(layer4.ToDomain, layer4.ToPort, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} + {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} } {% endif %} {% endfor %} @@ -46,7 +63,7 @@ layer4 { {% if layer4.enabled == "1" and layer4.Matchers == 'tlssni' %} @{{ layer4['@uuid'] }} tls sni {{ layer4.FromDomain.replace(',', ' ') }} route @{{ layer4['@uuid'] }} { - {{ setup_proxy(layer4.ToDomain, layer4.ToPort, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} + {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} } {% endif %} {% endfor %} @@ -55,7 +72,7 @@ layer4 { {% if layer4.enabled == "1" and layer4.Matchers == 'nottlssni' %} @{{ layer4['@uuid'] }} not tls sni {{ layer4.FromDomain.replace(',', ' ') }} route @{{ layer4['@uuid'] }} { - {{ setup_proxy(layer4.ToDomain, layer4.ToPort, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} + {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} } {% endif %} {% endfor %}