From 6bde751bc97b7929af6c5b859f85a4335003bdb6 Mon Sep 17 00:00:00 2001 From: Monviech <79600909+Monviech@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:15:58 +0100 Subject: [PATCH] www/caddy: Add Load Balancing options to Reverse Proxy and Layer4 Proxy (#4379) * www/caddy: Add Load Balancing options to Reverse Proxy and Layer4 Proxy * www/caddy: Add Load Balancing options to Reverse Proxy and Layer4 Proxy * Update www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/forms/dialogHandle.xml Co-authored-by: Franco Fichtner * www/caddy: Add changelog --------- Co-authored-by: Franco Fichtner --- www/caddy/pkg-descr | 1 + .../OPNsense/Caddy/forms/dialogHandle.xml | 93 +++++++++++++++++-- .../OPNsense/Caddy/forms/dialogLayer4.xml | 38 ++++++-- .../mvc/app/models/OPNsense/Caddy/Caddy.xml | 69 ++++++++++++-- .../mvc/app/views/OPNsense/Caddy/layer4.volt | 5 +- .../views/OPNsense/Caddy/reverse_proxy.volt | 10 +- .../templates/OPNsense/Caddy/Caddyfile | 24 +++++ .../templates/OPNsense/Caddy/includeLayer4 | 36 ++++--- 8 files changed, 234 insertions(+), 42 deletions(-) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index 378f12b86..023352a4a 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -15,6 +15,7 @@ Plugin Changelog 1.7.5 +* Add: Load Balancing options to Layer 4 Proxy and HTTP Handle * Add: Layer4 TLS Termination * Add: h2c protocol to HTTP Handler * Cleanup: Caddy Certificate widget hides unused automatic certificates 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 68714b31c..3ada90d0b 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 @@ -136,14 +136,6 @@ true - - handle.PassiveHealthFailDuration - - text - - - true - handle.HttpTlsInsecureSkipVerify @@ -172,4 +164,89 @@ + + header + + true + + + handle.lb_policy + + dropdown + + + true + + + handle.lb_retries + + text + + off + + true + + + handle.lb_try_duration + + text + + 0 + + true + + + handle.lb_try_interval + + text + + 250 + + true + + + handle.PassiveHealthFailDuration + + text + + off + + true + + + handle.PassiveHealthMaxFails + + text + + 1 + + true + + + handle.PassiveHealthUnhealthyStatus + + text + + off + + true + + + handle.PassiveHealthUnhealthyLatency + + text + + off + + true + + + handle.PassiveHealthUnhealthyRequestCount + + text + + off + + true + 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 a3ebb43f9..8fa4fdcae 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 @@ -107,13 +107,6 @@ text - - layer4.PassiveHealthFailDuration - - text - - true - layer4.ProxyProtocol @@ -121,6 +114,37 @@ true + + header + + true + + + layer4.lb_policy + + dropdown + + + true + + + layer4.PassiveHealthFailDuration + + text + + off + + true + + + layer4.PassiveHealthMaxFails + + text + + 1 + + true + header 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 dc0f8de5e..f7da86b03 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 @@ -344,11 +344,6 @@ /^(\/.*)?$/u Please enter a valid Path that starts with '/'. - - 1 - 100 - Please enter a value between 1 to 100. - Y @@ -392,6 +387,46 @@ ca + + random + + first + round_robin + least_conn + ip_hash + client_ip_hash + uri_hash + + + + 1 + Please enter a minimum value of 1 or leave empty for defaults. + + + 1 + Please enter a minimum value of 1 or leave empty for defaults. + + + + 1 + Please enter a minimum value of 1 or leave empty for defaults. + + + 2 + Please enter a minimum value of 2 or leave empty for defaults. + + + /^(100|[1-5][0-9]{2}|[1-5]xx)$/u + Please enter a valid HTTP response code like 404 a status code class like 4xx. + + + 1 + Please enter a minimum value of 1 or leave empty for defaults. + + + 1 + Please enter a minimum value of 1 or leave empty for defaults. + @@ -548,11 +583,6 @@ Y - - 1 - 100 - Please enter a value between 1 to 100. - Off (default) @@ -560,6 +590,25 @@ v2 + + random + + first + round_robin + least_conn + ip_hash + client_ip_hash + uri_hash + + + + 1 + Please enter a minimum value of 1 or leave empty for defaults. + + + 2 + Please enter a minimum value of 2 or leave empty for defaults. + , Y diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt index 7e41ee417..f1c57229c 100644 --- a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt +++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/layer4.volt @@ -148,13 +148,16 @@ {{ lang._('Local Port') }} {{ lang._('Matchers') }} {{ lang._('Invert Matchers') }} + {{ lang._('Terminate TLS') }} {{ lang._('Domain') }} {{ lang._('OpenVPN Modes') }} {{ lang._('OpenVPN Static Key') }} {{ lang._('Upstream Domain') }} {{ lang._('Upstream Port') }} {{ lang._('Remote IP') }} - {{ lang._('Fail Duration') }} + {{ lang._('Load Balance Policy') }} + {{ lang._('Passive Health Fail Duration') }} + {{ lang._('Passive Health Max Fails') }} {{ lang._('Proxy Protocol') }} {{ lang._('Description') }} {{ lang._('Commands') }} 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 2840c9b83..42036d604 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 @@ -435,7 +435,6 @@ {{ lang._('Upstream Domain') }} {{ lang._('Upstream Port') }} {{ lang._('Upstream Path') }} - {{ lang._('Upstream Fail Duration') }} {{ lang._('Forward Auth') }} {{ lang._('Access List') }} {{ lang._('HTTP Version') }} @@ -444,6 +443,15 @@ {{ lang._('TLS Server Name') }} {{ lang._('NTLM') }} {{ lang._('TLS Insecure Skip Verify') }} + {{ lang._('Load Balance Policy') }} + {{ lang._('Load Balance Retries') }} + {{ lang._('Load Balance Try Duration') }} + {{ lang._('Load Balance Try Interval') }} + {{ lang._('Passive Health Fail Duration') }} + {{ lang._('Passive Health Max Fails') }} + {{ lang._('Passive Health Unhealthy Status') }} + {{ lang._('Passive Health Unhealthy Latency') }} + {{ lang._('Passive Health Unhealthy Request Count') }} {{ lang._('Description') }} {{ lang._('Commands') }} diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile index ba29d7e26..90543e2fd 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/Caddyfile @@ -412,9 +412,33 @@ http://{{ domain }} { {% if handle.HandleDirective == "reverse_proxy" %} {{ handle.HandleDirective }} {{ formatted_domains | join(' ') }} { {{ header_manipulation(handle) }} + {% if handle.lb_policy|default("") %} + lb_policy {{ handle.lb_policy }} + {% endif %} + {% if handle.lb_retries|default("") %} + lb_retries {{ handle.lb_retries }} + {% endif %} + {% if handle.lb_try_duration|default("") %} + lb_try_duration {{ handle.lb_try_duration }}s + {% endif %} + {% if handle.lb_try_interval|default("") %} + lb_try_interval {{ handle.lb_try_interval }}ms + {% endif %} {% if handle.PassiveHealthFailDuration|default("") %} fail_duration {{ handle.PassiveHealthFailDuration }}s {% endif %} + {% if handle.PassiveHealthMaxFails|default("") %} + max_fails {{ handle.PassiveHealthMaxFails }} + {% endif %} + {% if handle.PassiveHealthUnhealthyStatus|default("") %} + unhealthy_status {{ handle.PassiveHealthUnhealthyStatus }} + {% endif %} + {% if handle.PassiveHealthUnhealthyLatency|default("") %} + unhealthy_latency {{ handle.PassiveHealthUnhealthyLatency }}ms + {% endif %} + {% if handle.PassiveHealthUnhealthyRequestCount|default("") %} + unhealthy_request_count {{ handle.PassiveHealthUnhealthyRequestCount }} + {% endif %} {% set has_transport_options = handle.HttpVersion or handle.HttpKeepalive or diff --git a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 index 0029c1ba6..92d638759 100644 --- a/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 +++ b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeLayer4 @@ -22,35 +22,41 @@ {# 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) %} +{% macro define_proxy(layer4) %} {% if layer4.TerminateTls|default("0") == "1" %} tls {% endif %} - proxy {% for domain in to_domains.split(',') %} + proxy {% for domain in layer4.ToDomain.split(',') %} {% set is_ipv6 = (':' in domain) %} - {{ layer4.Protocol }}/{{ '[' if is_ipv6 }}{{ domain }}{{ ']' if is_ipv6 }}:{{ to_port }}{% if not loop.last %} {% endif %} + {{ layer4.Protocol }}/{{ '[' if is_ipv6 }}{{ domain }}{{ ']' if is_ipv6 }}:{{ layer4.ToPort }}{% if not loop.last %} {% endif %} {% endfor %} { - {% if fail_duration %} - fail_duration {{ fail_duration }}s + {% if layer4.lb_policy|default("") %} + lb_policy {{ layer4.lb_policy }} {% endif %} - {% if proxy_protocol %} - proxy_protocol {{ proxy_protocol }} + {% if layer4.PassiveHealthFailDuration|default("") %} + fail_duration {{ layer4.PassiveHealthFailDuration }}s + {% endif %} + {% if layer4.PassiveHealthMaxFails|default("") %} + max_fails {{ layer4.PassiveHealthMaxFails }} + {% endif %} + {% if layer4.ProxyProtocol %} + proxy_protocol {{ layer4.ProxyProtocol }} {% endif %} } {% endmacro %} -{% macro configure_proxy(layer4, to_domains, to_port, remote_ips, fail_duration, proxy_protocol) %} +{% macro configure_proxy(layer4) %} {% set content %} - {% if remote_ips %} - {% set ip_list = remote_ips.split(',') %} + {% if layer4.RemoteIp %} + {% set ip_list = layer4.RemoteIp.split(',') %} subroute { @allowed_ips remote_ip {{ ip_list|join(' ') }} route @allowed_ips { - {{ define_proxy(layer4, to_domains, to_port, fail_duration, proxy_protocol) }} + {{ define_proxy(layer4) }} } } {% else %} - {{ define_proxy(layer4, to_domains, to_port, fail_duration, proxy_protocol) }} + {{ define_proxy(layer4) }} {% endif %} {% endset %} {{ content|trim }} @@ -144,7 +150,7 @@ {% 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) }} + {{ configure_proxy(layer4) }} } {% endif %} {% endif %} @@ -157,11 +163,11 @@ {% 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) }} + {{ configure_proxy(layer4) }} } {% else %} route { - {{ configure_proxy(layer4, layer4.ToDomain, layer4.ToPort, layer4.RemoteIp, layer4.PassiveHealthFailDuration, layer4.ProxyProtocol) }} + {{ configure_proxy(layer4) }} } {% endif %} {% endif %}