mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<help><![CDATA[Enter one or multiple domains to route via SNI or Host Header. The domain will be added to the "listener_wrapper". Essentially, all traffic that Caddy receives on its frontend listeners (most likely the default HTTP and HTTPS ports) is funnelled through this wrapper to be processed and routed. Wildcard domains are allowed, e.g. "*.example.com". Host wildcards are allowed too, e.g. "*".]]></help>
|
||||
<help><![CDATA[Enter one or multiple domains to route via SNI or Host Header. The domain will be added to the "listener_wrapper". Essentially, all traffic that Caddy receives on its frontend listeners (most likely the default HTTP and HTTPS ports) is funnelled through this wrapper to be processed and routed. Wildcard domains are allowed, e.g. "*.example.com". Host wildcards are allowed too, e.g. "*". Some protocols match all domains and "*" is mandatory.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>layer4.Matchers</id>
|
||||
<label>Matchers</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Match the traffic of the selected domains. The TCP/UDP packets will be routed to the selected upstream domains without terminating TLS or altering the traffic. Only protocols that send a "Client Hello" (like TLS), or a "Host Header" (like HTTP) can be routed here. Routing Precedence: 1. "Host", 2. "SNI", 3. "not SNI", 4. "HTTP Handlers" (hidden default route for all unmatched traffic). The "not" operator will invert the match of the selected domains.]]></help>
|
||||
<help><![CDATA[Match the traffic of the selected domains. The TCP/UDP packets will be routed to the selected upstream domains without terminating TLS or altering the traffic. Only protocols that send a "Client Hello" (like TLS), or a "Host Header" (like HTTP) can be routed here. Routing Precedence: 1. "SSH (or other protocols)", 2. "HTTP (Host Header)", 3. "TLS (SNI)", 4. "TLS (inverted SNI)", 5. "HTTP Handlers" (hidden default route for all unmatched traffic). The "SSH" matcher (and any other matcher that does not evaluate Host Header or SNI), will match any SSH like traffic on the default ports. That means, these protocols will only match once per ruleset and will proxy traffic to one upstream.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>layer4.ToDomain</id>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,9 +405,16 @@
|
||||
<Required>Y</Required>
|
||||
<Default>tlssni</Default>
|
||||
<OptionValues>
|
||||
<httphost>Host (HTTP)</httphost>
|
||||
<tlssni>SNI (TLS)</tlssni>
|
||||
<nottlssni>not SNI (TLS)</nottlssni>
|
||||
<httphost>HTTP (Host Header)</httphost>
|
||||
<postgres>Postgres</postgres>
|
||||
<proxy_protocol>Proxy Protocol</proxy_protocol>
|
||||
<rdp>RDP</rdp>
|
||||
<socks4>SOCKSv4</socks4>
|
||||
<socks5>SOCKSv5</socks5>
|
||||
<ssh>SSH</ssh>
|
||||
<tlssni>TLS (SNI)</tlssni>
|
||||
<nottlssni>TLS (inverted SNI)</nottlssni>
|
||||
<xmpp>XMPP</xmpp>
|
||||
</OptionValues>
|
||||
</Matchers>
|
||||
<ToDomain type="HostnameField">
|
||||
|
||||
@@ -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(',', ' ') }}
|
||||
|
||||
Reference in New Issue
Block a user