From 30a1d4796eb8a2db3b22baadd2bac2ed4109ce11 Mon Sep 17 00:00:00 2001
From: Monviech <79600909+Monviech@users.noreply.github.com>
Date: Tue, 8 Oct 2024 13:00:43 +0200
Subject: [PATCH] www/caddy: Complete Layer4 routing feature (#4281)
* www/caddy: Improve Layer 4 Routes to allow configurations outside the context of listener_wrappers, streaming and loadbalancing any TCP/UDP traffic on custom ports.
* www/caddy: Add validations for Layer 4 Routes.
* www/caddy: Control visibility of FromDomain in Layer4 based on Matcher. Remove need to always select * even when matcher does not support domains. Remove requirement for FromDomain, it is now validated based on selected Matcher.
* www/caddy: Cleanup validations, add isFieldChanged where applicable, removed weird old subdomain validation.
* www/caddy: Introduce InvertMatchers to invert any matchers, replacing the not tls sni matcher. Add HTTP and TLS without domain requirement as additional matchers. Add validations to ensure these matchers can not be chosen for listener_wrappers. Improve validations to ensure domain is empty when not using HTTP Host Header or TLS SNI Client Hello matchers.
* www/caddy: Bump model version. Add changelog.
* www/caddy: Since the prior validation demanded * and required FromDomain to be filled out, the new validation has to allow this too to avoid migration issues.
* www/caddy: Implement sequence number so rules can be sorted and be processed in custom order.
---
www/caddy/pkg-descr | 4 +
.../OPNsense/Caddy/forms/dialogLayer4.xml | 61 +++++-
.../mvc/app/models/OPNsense/Caddy/Caddy.php | 200 +++++++++---------
.../mvc/app/models/OPNsense/Caddy/Caddy.xml | 39 +++-
.../views/OPNsense/Caddy/reverse_proxy.volt | 15 +-
.../templates/OPNsense/Caddy/Caddyfile | 20 +-
.../templates/OPNsense/Caddy/includeLayer4 | 145 +++++++------
7 files changed, 312 insertions(+), 172 deletions(-)
diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr
index 01962ce7a..d2c94173f 100644
--- a/www/caddy/pkg-descr
+++ b/www/caddy/pkg-descr
@@ -16,6 +16,10 @@ Plugin Changelog
1.7.2
* Add: Directive in HTTP Handler can be chosen, "reverse_proxy" and "redir"
+* Add: Layer4 routes feature. Routing Type "global" or "listener_wrapper" can be chosen
+* Add: Any Layer4 TCP/UDP traffic can be proxied without choosing a Layer 7 protocol matcher
+* Change: Layer4 routes are not ordered automatically anymore
+* Change: Layer4 "not tls sni" matcher has been replaced by generalized "Invert Matchers" checkbox
* Build: Update Caddy Layer4 module, fixes TLS matcher in chromium based browsers
* Fix: When Apply takes longer than 20 seconds, Caddy will be forcefully restarted
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 acac47c8c..a2c73f6a0 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
@@ -5,6 +5,12 @@
checkbox
+
+ layer4.Sequence
+
+ text
+
+
layer4.description
@@ -13,21 +19,59 @@
header
-
+
+ true
- layer4.FromDomain
-
- select_multiple
-
- true
-
+ layer4.Type
+
+ dropdown
+
+ true
+
+
+ header
+
+ true
+
+
+ layer4.Protocol
+
+ dropdown
+
+ true
+
+
+ layer4.FromPort
+
+ text
+
+ true
+
+
+ header
+
layer4.Matchers
dropdown
-
+
+
+
+ layer4.FromDomain
+
+ select_multiple
+
+ true
+
+
+
+ layer4.InvertMatchers
+
+ checkbox
+
+ true
header
@@ -64,7 +108,6 @@
header
- true
layer4.RemoteIp
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 ddb6f919f..aefc67b9b 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
@@ -37,11 +37,11 @@ use OPNsense\Core\Config;
class Caddy extends BaseModel
{
// Check domain-port combinations
- private function checkForUniquePortCombos($items, $messages)
+ private function checkForUniquePortCombos($messages)
{
$combos = [];
- foreach ($items as $item) {
- $key = $item->__reference; // Dynamic key based on item reference
+ foreach ($this->reverseproxy->reverse->iterateItems() as $item) {
+ $key = $item->__reference;
$fromDomain = (string) $item->FromDomain;
$fromPort = (string) $item->FromPort;
@@ -52,12 +52,9 @@ class Caddy extends BaseModel
}
foreach ($defaultPorts as $port) {
- // Create a unique key for domain-port combination
$comboKey = $fromDomain . ':' . $port;
- // Check for duplicate combinations
if (isset($combos[$comboKey])) {
- // Use dynamic $key for message referencing
$messages->appendMessage(new Message(
sprintf(
gettext(
@@ -76,48 +73,6 @@ class Caddy extends BaseModel
}
}
- // Check that subdomains are under a wildcard or exact domain
- private function checkSubdomainsAgainstDomains($subdomains, $domains, $messages)
- {
- $wildcardDomainList = [];
- foreach ($domains as $domain) {
- if ((string) $domain->enabled === '1') {
- $domainName = (string) $domain->FromDomain;
- if (str_starts_with($domainName, '*.')) {
- $wildcardBase = substr($domainName, 2);
- $wildcardDomainList[$wildcardBase] = $domainName;
- }
- }
- }
-
- foreach ($subdomains as $subdomain) {
- if ((string) $subdomain->enabled === '1') {
- $subdomainName = (string) $subdomain->FromDomain;
- $isValid = false;
- foreach ($wildcardDomainList as $baseDomain => $wildcardDomain) {
- if (str_ends_with($subdomainName, $baseDomain)) {
- $isValid = true;
- break;
- }
- }
-
- if (!$isValid) {
- $key = $subdomain->__reference; // Dynamic key based on subdomain reference
- $messages->appendMessage(new Message(
- sprintf(
- gettext(
- 'Invalid subdomain configuration: %s does not fall ' .
- 'under any configured wildcard domain.'
- ),
- $subdomainName
- ),
- $key . ".FromDomain"
- ));
- }
- }
- }
- }
-
// Get the current OPNsense WebGUI ports and check for conflicts with Caddy
private function getWebGuiPorts()
{
@@ -172,8 +127,7 @@ class Caddy extends BaseModel
private function checkDisableTlsConflicts($messages)
{
foreach ($this->reverseproxy->reverse->iterateItems() as $item) {
- // First check if the DisableTls field has been changed
- if ($item->isFieldChanged('DisableTls')) {
+ if ($item->isFieldChanged()) {
if ((string) $item->DisableTls === '1') {
$conflictChecks = [
'DnsChallenge' => (string) $item->DnsChallenge === '1',
@@ -214,7 +168,7 @@ class Caddy extends BaseModel
if ($httpPort < 1024) {
$messages->appendMessage(new Message(
gettext(
- 'Superuser is disabled, HTTP port must not be empty and must be 1024 or above.'
+ 'www user is active, HTTP port must not be empty and must be 1024 or above.'
),
"general.HttpPort"
));
@@ -224,7 +178,7 @@ class Caddy extends BaseModel
if ($httpsPort < 1024) {
$messages->appendMessage(new Message(
gettext(
- 'Superuser is disabled, HTTPS port must not be empty and must be 1024 or above.'
+ 'www user is active, HTTPS port must not be empty and must be 1024 or above.'
),
"general.HttpsPort"
));
@@ -237,7 +191,7 @@ class Caddy extends BaseModel
if ($fromPort !== null && $fromPort < 1024) {
$messages->appendMessage(new Message(
gettext(
- 'Superuser is disabled, port must be empty or must be 1024 or above.'
+ 'www user is active, port must be empty or must be 1024 or above.'
),
$item->__reference . ".FromPort"
));
@@ -249,36 +203,111 @@ class Caddy extends BaseModel
));
}
}
+
+ foreach ($this->reverseproxy->layer4->iterateItems() as $item) {
+ $fromPort = !empty((string)$item->FromPort) ? (string)$item->FromPort : null;
+
+ if ($fromPort !== null && $fromPort < 1024) {
+ $messages->appendMessage(new Message(
+ gettext(
+ 'www user is active, port must be empty or must be 1024 or above.'
+ ),
+ $item->__reference . ".FromPort"
+ ));
+ $messages->appendMessage(new Message(
+ gettext(
+ 'Ports in "Reverse Proxy - Layer4 Routes" must be empty or must be 1024 or above.'
+ ),
+ "general.DisableSuperuser"
+ ));
+ }
+ }
}
}
- /**
- * 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) {
+ if ($item->isFieldChanged()) {
$key = $item->__reference;
- $messages->appendMessage(new Message(
- sprintf(
- gettext(
- 'When "%s" matcher is selected, the only valid entry in Domain is "*".'
+ if (in_array((string)$item->Matchers, ['httphost', 'tlssni']) && empty((string)$item->FromDomain)) {
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When "%s" matcher is selected, domain is required.'
+ ),
+ $item->Matchers
),
- $matchers
- ),
- $key . ".FromDomain"
- ));
+ $key . ".FromDomain"
+ ));
+ } elseif (
+ !in_array((string)$item->Matchers, ['httphost', 'tlssni']) &&
+ (
+ !empty((string)$item->FromDomain) &&
+ (string)$item->FromDomain != '*'
+ )
+ ) {
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When "%s" matcher is selected, domain must be empty or *.'
+ ),
+ $item->Matchers
+ ),
+ $key . ".FromDomain"
+ ));
+ }
+
+ if ((string)$item->Type === 'global' && empty((string)$item->FromPort)) {
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When routing type is "%s", port is required.'
+ ),
+ $item->Type
+ ),
+ $key . ".FromPort"
+ ));
+ } elseif ((string)$item->Type !== 'global' && !empty((string)$item->FromPort)) {
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When routing type is "%s", port must be empty.'
+ ),
+ $item->Type
+ ),
+ $key . ".FromPort"
+ ));
+ }
+
+ if ((string)$item->Type !== 'global' && ((string)$item->Protocol !== 'tcp')) {
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When routing type is "%s", protocol must be TCP.'
+ ),
+ $item->Type
+ ),
+ $key . ".Protocol"
+ ));
+ }
+
+ if ((string)$item->Type !== 'global' &&
+ (
+ (string)$item->Matchers == 'tls' ||
+ (string)$item->Matchers == 'http'
+ )
+ ) {
+ $messages->appendMessage(new Message(
+ sprintf(
+ gettext(
+ 'When routing type is "%s", matchers "HTTP" or "TLS" cannot be chosen.'
+ ),
+ $item->Type
+ ),
+ $key . ".Matchers"
+ ));
+ }
}
}
}
@@ -288,29 +317,10 @@ class Caddy extends BaseModel
{
$messages = parent::performValidation($validateFullModel);
- // Check domain-port combinations
- $this->checkForUniquePortCombos(
- $this->reverseproxy->reverse->iterateItems(),
- $messages
- );
-
- // Check that subdomains are under a wildcard or exact domain
- $this->checkSubdomainsAgainstDomains(
- $this->reverseproxy->subdomain->iterateItems(),
- $this->reverseproxy->reverse->iterateItems(),
- $messages
- );
-
- // Check WebGUI conflicts
+ $this->checkForUniquePortCombos($messages);
$this->checkWebGuiSettings($messages);
-
- // Check for TLS conflicts in Domain
$this->checkDisableTlsConflicts($messages);
-
- // Check DisableSuperuser Port conflicts
$this->checkSuperuserPorts($messages);
-
- // 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 c663b3960..8e357ad8e 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
@@ -1,7 +1,7 @@
//Pischem/caddy
Caddy Reverse Proxy
- 1.3.2
+ 1.3.3
@@ -453,8 +453,35 @@
1
Y
-
+
+ 1
+ 99999
+ Please enter a value between 1 and 99999 or leave empty.
+
+
+ Sequence value has to be unique or empty.
+ UniqueConstraint
+
+
+
+
Y
+ listener_wrappers
+
+ listener_wrappers
+ global
+
+
+
+ Y
+ tcp
+
+ TCP
+ UDP
+
+
+
+
N
Y
Y
@@ -466,7 +493,9 @@
Y
tlssni
+ ANY
DNS
+ HTTP
HTTP (Host Header)
Postgres
Proxy Protocol
@@ -474,11 +503,13 @@
SOCKSv4
SOCKSv5
SSH
- TLS (SNI)
- TLS (inverted SNI)
+ TLS
+ TLS (SNI Client Hello)
+ Wireguard
XMPP
+
Y
,
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 ee1b2f5ab..cd7cb0dc3 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
@@ -339,6 +339,14 @@
}
});
+ $("#layer4\\.Matchers").change(function() {
+ if ($(this).val() !== "tlssni" && $(this).val() !== "httphost") {
+ $(".style_matchers").closest('tr').hide();
+ } else {
+ $(".style_matchers").closest('tr').show();
+ }
+ });
+
// Initialize tabs, service control and filter selectpicker
initializeTabs();
updateServiceControlUI('caddy');
@@ -617,8 +625,13 @@
| {{ lang._('ID') }} |
{{ lang._('Enabled') }} |
+ {{ lang._('Sequence') }} |
+ {{ lang._('Routing Type') }} |
+ {{ lang._('Protocol') }} |
+ {{ lang._('Local Port') }} |
{{ lang._('Domain') }} |
- {{ lang._('Matcher') }} |
+ {{ lang._('Matchers') }} |
+ {{ lang._('Invert Matchers') }} |
{{ lang._('Upstream Domain') }} |
{{ lang._('Upstream Port') }} |
{{ lang._('Remote IP') }} |
diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile
index 0fd8fc9fd..62f47f65d 100644
--- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile
+++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile
@@ -90,13 +90,27 @@
{% endif %}
{% if generalSettings.EnableLayer4|default("0") == "1" %}
listener_wrappers {
- {# Plug the Layer 4 template in #}
- {% include "OPNsense/Caddy/includeLayer4" %}
-
+ layer4 {
+ import /usr/local/etc/caddy/caddy.d/*.layer4listener
+ {% set context_var = "listener_wrappers" %}
+ {% include "OPNsense/Caddy/includeLayer4" %}
+ {# Empty Route that catches all other traffic #}
+ route
+ }
+ {# Route all other traffic to HTTP App #}
+ tls
}
{% endif %}
}
+ {% if generalSettings.EnableLayer4|default("0") == "1" %}
+ layer4 {
+ import /usr/local/etc/caddy/caddy.d/*.layer4global
+ {% set context_var = "global" %}
+ {% include "OPNsense/Caddy/includeLayer4" %}
+ }
+ {% endif %}
+
{#
# Section: Dynamic DNS Global Configuration
# Purpose: Sets up global configuration for Dynamic DNS. Caddy needs to be compiled with
diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4
index 7eb367b6b..defaf2528 100644
--- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4
+++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4
@@ -1,15 +1,31 @@
{#
-# This file sets up the listener_wrapper for layer 4 routing support.
-# - Section: Servers Global Configuration
-# Also allows for custom configurations with the import statement.
+# This file sets up layer4 routing support.
+# There are two contexts: "listener_wrappers" and "global"
+#
+# "listener_wrappers" multiplexes on OSI Layer 7 on the default HTTP and HTTPS ports and requires a traffic matcher since
+# otherwise the "reverse_proxy" would stop receiving any requests. The "any" Layer 7 matcher is not allowed here.
+# This context allows for matching domains via SNI and route them without terminating TLS.
+#
+# "global" can set up custom ports and also route any OSI Layer 4 TCP/UDP traffic without a matcher.
+# They will be grouped under the same protocol/port combination
+# to allow multiple Layer 7 matchers inside the scope of the same Layer 4 matcher.
+# This context is for advanced usecases where raw TCP/UDP traffic on custom ports should be proxied or load balanced.
#}
-{% set layer4_configs = helpers.toList('Pischem.caddy.reverseproxy.layer4') %}
-{# Nested Macro for proxy definition #}
-{% macro define_proxy(to_domains, to_port, fail_duration, proxy_protocol) %}
+{% set unsorted_layer4_configs = helpers.toList('Pischem.caddy.reverseproxy.layer4') %}
+
+{# Ensure that 'Sequence' is present and converted to an integer in each item #}
+{% for item in unsorted_layer4_configs %}
+ {% set _ = item.update({'Sequence': item.get('Sequence', '0') | int}) %}
+{% endfor %}
+
+{# Sort the configurations based on 'Sequence' #}
+{% set layer4_configs = unsorted_layer4_configs | sort(attribute='Sequence') %}
+
+{% macro define_proxy(layer4, 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 %}
+ {% set is_ipv6 = (':' in domain) %}
+ {{ layer4.Protocol }}/{{ '[' if is_ipv6 }}{{ domain }}{{ ']' if is_ipv6 }}:{{ to_port }}{% if not loop.last %} {% endif %}
{% endfor %} {
{% if fail_duration %}
fail_duration {{ fail_duration }}s
@@ -20,64 +36,73 @@
}
{% 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) }}
+{% macro configure_proxy(layer4, to_domains, to_port, remote_ips, fail_duration, proxy_protocol) %}
+ {% set content %}
+ {% if remote_ips %}
+ {% set ip_list = remote_ips.split(',') %}
+ subroute {
+ @allowed_ips remote_ip {{ ip_list|join(' ') }}
+ route @allowed_ips {
+ {{ define_proxy(layer4, to_domains, to_port, fail_duration, proxy_protocol) }}
+ }
}
- }
+ {% else %}
+ {{ define_proxy(layer4, to_domains, to_port, fail_duration, proxy_protocol) }}
+ {% endif %}
+ {% endset %}
+ {{ content|trim }}
+{% endmacro %}
+
+{% set grouped_configs = {} %}
+{% for layer4 in layer4_configs %}
+ {% if layer4.FromPort and layer4.Protocol and layer4.enabled == "1" %}
+ {% set key = layer4.Protocol ~ '/:' ~ layer4.FromPort %}
+ {% if not key in grouped_configs %}
+ {% set _ = grouped_configs.update({key: []}) %}
+ {% endif %}
+ {% set _ = grouped_configs[key].append(layer4) %}
+ {% endif %}
+{% endfor %}
+
+{% macro handle_special_matchers(layer4) %}
+ {% set invert_prefix = 'not ' if layer4.InvertMatchers == '1' else '' %}
+ {% if layer4.Matchers == 'httphost' %}
+ {{ invert_prefix }}http host {{ layer4.FromDomain.replace(',', ' ') }}
+ {% elif layer4.Matchers == 'tlssni' %}
+ {{ invert_prefix }}tls sni {{ layer4.FromDomain.replace(',', ' ') }}
{% else %}
- {# Call Nested Macro #}
- {{ define_proxy(to_domains, to_port, fail_duration, proxy_protocol) }}
+ {{ invert_prefix }}{{ layer4.Matchers }}
{% endif %}
{% endmacro %}
-{# Set up Layer4 App #}
-layer4 {
- import /usr/local/etc/caddy/caddy.d/*.layer4
- {# 1. loop to handle any traffic matchers that can only be added once since they match all specific protocol traffic. #}
+{% if context_var == 'listener_wrappers' %}
{% for layer4 in layer4_configs %}
- {% if layer4.enabled == "1" and layer4.Matchers not in ['httphost', 'tlssni', 'nottlssni'] %}
- @{{ layer4['@uuid'] }} {{ layer4.Matchers }}
- route @{{ layer4['@uuid'] }} {
- {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
- }
+ {% if layer4.enabled == "1" and layer4.Type == 'listener_wrappers' %}
+ {% if layer4.Matchers != 'any' %}
+ @{{ layer4['@uuid'] }} {{ handle_special_matchers(layer4) }}
+ route @{{ layer4['@uuid'] }} {
+ {{ configure_proxy(layer4, layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
+ }
+ {% endif %}
{% 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(',', ' ') }}
- route @{{ layer4['@uuid'] }} {
- {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
- }
- {% endif %}
+{% elif context_var == 'global' %}
+ {% for key, layers in grouped_configs.items() %}
+ {{ key }} {
+ {% for layer4 in layers %}
+ {% if layer4.enabled == "1" and layer4.Type == 'global' %}
+ {% if layer4.Matchers != 'any' %}
+ @{{ layer4['@uuid'] }} {{ handle_special_matchers(layer4) }}
+ route @{{ layer4['@uuid'] }} {
+ {{ configure_proxy(layer4, layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
+ }
+ {% else %}
+ route {
+ {{ configure_proxy(layer4, layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
+ }
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+ }
{% endfor %}
- {# 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(',', ' ') }}
- route @{{ layer4['@uuid'] }} {
- {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
- }
- {% endif %}
- {% endfor %}
- {# 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(',', ' ') }}
- route @{{ layer4['@uuid'] }} {
- {{ configure_proxy(layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }}
- }
- {% endif %}
- {% endfor %}
- {# Empty Route that catches all other traffic #}
- route
-}
-{# Route all other traffic to HTTP App #}
-tls
+{% endif %}