From 90b49ceca34d037fd3073622d807f27e28b27d29 Mon Sep 17 00:00:00 2001
From: Monviech <79600909+Monviech@users.noreply.github.com>
Date: Wed, 31 Jul 2024 11:42:40 +0200
Subject: [PATCH] www/caddy: Add remaining compatible Layer4 traffic matchers
(#4133)
* www/caddy: Add Layer4 SSH traffic matcher.
* www/caddy: Restructure Layer4 protocol names.
* www/caddy: Also change the names in the help text.
* www/caddy: Generalize additional protocol handling, add remaining supported protocols for layer4 routing.
---
www/caddy/pkg-descr | 1 +
.../OPNsense/Caddy/forms/dialogLayer4.xml | 4 +--
.../mvc/app/models/OPNsense/Caddy/Caddy.php | 34 +++++++++++++++++++
.../mvc/app/models/OPNsense/Caddy/Caddy.xml | 13 +++++--
.../templates/OPNsense/Caddy/includeLayer4 | 15 ++++++--
5 files changed, 59 insertions(+), 8 deletions(-)
diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr
index d8a139e4c..4edfff9d1 100644
--- a/www/caddy/pkg-descr
+++ b/www/caddy/pkg-descr
@@ -32,6 +32,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
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 8abb5e9fd..307937560 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
@@ -11,13 +11,13 @@
select_multiple
true
-
+
layer4.Matchers
dropdown
-
+
layer4.ToDomain
diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php
index beca19c9c..c493ae9b6 100644
--- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php
+++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Caddy.php
@@ -266,6 +266,37 @@ class Caddy extends BaseModel
}
}
+ /**
+ * 6. Check that when certain Layer4 matchers are selected, only "*" is valid as FromDomain.
+ * This happens because they cannot be matched by host header or SNI, so they match all traffic.
+ * The "*" shows the user that all traffic will be matched, and that creating multiple
+ * matchers will not result in more routes for the same traffic type to work.
+ */
+ private function checkLayer4Matchers($messages)
+ {
+ foreach ($this->reverseproxy->layer4->iterateItems() as $item) {
+ $matchers = (string) $item->Matchers;
+ $fromDomain = (string) $item->FromDomain;
+
+ // Check if matchers is not in the list of specific values
+ $isNotInSpecificMatchers = !in_array($matchers, ['httphost', 'tlssni', 'nottlssni']);
+ $isInvalidFromDomain = $fromDomain !== '*';
+
+ if ($isNotInSpecificMatchers && $isInvalidFromDomain) {
+ $key = $item->__reference;
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When "%s" matcher is selected, the only valid entry in Domain is "*".'
+ ),
+ $matchers
+ ),
+ $key . ".FromDomain"
+ ));
+ }
+ }
+ }
+
// Perform the actual validation
public function performValidation($validateFullModel = false)
{
@@ -296,6 +327,9 @@ class Caddy extends BaseModel
// 6. Check DisableSuperuser Port conflicts
$this->checkSuperuserPorts($messages);
+ // 7. Check Layer4 matchers
+ $this->checkLayer4Matchers($messages);
+
return $messages;
}
}
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 2ce870d44..b27125460 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
@@ -405,9 +405,16 @@
Y
tlssni
- Host (HTTP)
- SNI (TLS)
- not SNI (TLS)
+ HTTP (Host Header)
+ Postgres
+ Proxy Protocol
+ RDP
+ SOCKSv4
+ SOCKSv5
+ SSH
+ TLS (SNI)
+ TLS (inverted SNI)
+ XMPP
diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4
index a74eeeafd..c6a20dab4 100644
--- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4
+++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4
@@ -23,7 +23,16 @@
{# Set up Layer4 App #}
layer4 {
import /usr/local/etc/caddy/caddy.d/*.layer4
- {# 1. loop to handle http host matchers #}
+ {# 1. loop to handle any traffic matchers that can only be added once since they match all specific protocol traffic. #}
+ {% for layer4 in layer4_configs %}
+ {% 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) }}
+ }
+ {% endif %}
+ {% endfor %}
+ {# 2. loop to handle http host matchers #}
{% for layer4 in layer4_configs %}
{% if layer4.enabled == "1" and layer4.Matchers == 'httphost' %}
@{{ layer4['@uuid'] }} http host {{ layer4.FromDomain.replace(',', ' ') }}
@@ -32,7 +41,7 @@ layer4 {
}
{% endif %}
{% endfor %}
- {# 2. loop to handle tls sni matchers #}
+ {# 3. loop to handle tls sni matchers #}
{% for layer4 in layer4_configs %}
{% if layer4.enabled == "1" and layer4.Matchers == 'tlssni' %}
@{{ layer4['@uuid'] }} tls sni {{ layer4.FromDomain.replace(',', ' ') }}
@@ -41,7 +50,7 @@ layer4 {
}
{% endif %}
{% endfor %}
- {# 3. loop to handle not tls sni matchers #}
+ {# 4. loop to handle not tls sni matchers #}
{% for layer4 in layer4_configs %}
{% if layer4.enabled == "1" and layer4.Matchers == 'nottlssni' %}
@{{ layer4['@uuid'] }} not tls sni {{ layer4.FromDomain.replace(',', ' ') }}