diff --git a/net/haproxy/Makefile b/net/haproxy/Makefile
index 04205848a..475c2897a 100644
--- a/net/haproxy/Makefile
+++ b/net/haproxy/Makefile
@@ -1,5 +1,5 @@
PLUGIN_NAME= haproxy
-PLUGIN_VERSION= 2.16
+PLUGIN_VERSION= 2.17
PLUGIN_COMMENT= Reliable, high performance TCP/HTTP load balancer
PLUGIN_DEPENDS= haproxy18
PLUGIN_MAINTAINER= opnsense@moov.de
diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogAcl.xml b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogAcl.xml
index f56065c94..ee8bb0283 100644
--- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogAcl.xml
+++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogAcl.xml
@@ -423,6 +423,17 @@
dropdown
+
+
+ header
+
+
+
+ acl.ssl_fc_sni
+
+ text
+
+
header
diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogHealthcheck.xml b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogHealthcheck.xml
index 2c1e8e200..a3d23ffbc 100644
--- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogHealthcheck.xml
+++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogHealthcheck.xml
@@ -23,6 +23,12 @@
text
+
+ healthcheck.force_ssl
+
+ checkbox
+
+
healthcheck.checkport
diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/main.xml b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/main.xml
index 48eb6193c..87acdf10d 100644
--- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/main.xml
+++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/main.xml
@@ -114,7 +114,6 @@
dropdown
- true
haproxy.general.tuning.maxDHSize
diff --git a/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml
index 33d431a46..296262e91 100644
--- a/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml
+++ b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml
@@ -1134,6 +1134,10 @@
Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".
Y
+
+ 0
+ N
+
1
@@ -1290,7 +1294,6 @@
Path contains subdir
Path contains string
URL parameter contains
- SSL/TLS connection established
SSL Client certificate is valid
SSL Client certificate verify error result
SSL Client certificate issued by CA common-name
@@ -1317,12 +1320,14 @@
Source IP: session rate
Minimum number of usable servers in backend
Traffic is HTTP
- Traffic is SSL
- SNI TLS extension matches
- SNI TLS extension contains
- SNI TLS extension starts with
- SNI TLS extension ends with
- SNI TLS extension regex
+ Traffic is SSL (TCP request content inspection)
+ Traffic is SSL (locally deciphered)
+ SNI TLS extension matches (locally deciphered)
+ SNI TLS extension matches (TCP request content inspection)
+ SNI TLS extension contains (TCP request content inspection)
+ SNI TLS extension starts with (TCP request content inspection)
+ SNI TLS extension ends with (TCP request content inspection)
+ SNI TLS extension regex (TCP request content inspection)
Custom condition (option pass-through)
@@ -1620,6 +1625,10 @@
Related backend item not found
N
+
+ /^.{1,4096}$/u
+ N
+
/^.{1,4096}$/u
N
diff --git a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
index 76a446013..9332ba734 100644
--- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
+++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
@@ -182,8 +182,6 @@
{% set acl_enabled = '0' %}
# ERROR: missing parameters
{% endif %}
-{% elif acl_data.expression == 'ssl_fc' %}
-{% do acl_options.append('ssl_fc') %}
{% elif acl_data.expression == 'src' %}
{% if acl_data.src|default("") != "" %}
{% do acl_options.append('src ' ~ acl_data.src) %}
@@ -238,6 +236,15 @@
{% do acl_options.append('req.proto_http') %}
{% elif acl_data.expression == 'traffic_is_ssl' %}
{% do acl_options.append('req.ssl_ver gt 0') %}
+{% elif acl_data.expression == 'ssl_fc' %}
+{% do acl_options.append('ssl_fc') %}
+{% elif acl_data.expression == 'ssl_fc_sni' %}
+{% if acl_data.ssl_fc_sni|default("") != "" %}
+{% do acl_options.append('ssl_fc_sni ' ~ acl_data.ssl_fc_sni) %}
+{% else %}
+{% set acl_enabled = '0' %}
+ # ERROR: missing parameters
+{% endif %}
{% elif acl_data.expression == 'ssl_sni' %}
{% if acl_data.ssl_sni|default("") != "" %}
{% do acl_options.append('req.ssl_sni -i ' ~ acl_data.ssl_sni) %}
@@ -1131,10 +1138,13 @@ frontend {{frontend.name}}
{%- if helpers.exists('OPNsense.HAProxy.backends') %}
{% for backend in helpers.toList('OPNsense.HAProxy.backends.backend') %}
-{# # ignore disabled backends and those without a server #}
-{% if backend.enabled == '1' and backend.linkedServers|default("") != "" %}
+{# # ignore disabled backends #}
+{% if backend.enabled == '1' %}
# Backend: {{backend.name}} ({{backend.description}})
backend {{backend.name}}
+{% if backend.linkedServers|default("") == "" %}
+ # HINT: no servers configured for this backend.
+{% endif %}
{# # store additional parameters for the "server" entries #}
{% set healthcheck_additions = [] %}
{% if backend.healthCheck|default("") != "" and backend.healthCheckEnabled == '1' %}
@@ -1283,116 +1293,123 @@ backend {{backend.name}}
{% if backend.tuning_defaultserver|default("") != "" %}
default-server {{backend.tuning_defaultserver}}
{% endif %}
-{% for server in backend.linkedServers.split(",") %}
-{% set server_data = helpers.getUUID(server) %}
-{# # check if this server can be found in configuration #}
-{% if server_data == {} %}
+{# # check if this backend has any servers configured #}
+{% if backend.linkedServers|default("") != "" %}
+{% for server in backend.linkedServers.split(",") %}
+{% set server_data = helpers.getUUID(server) %}
+{# # check if this server can be found in configuration #}
+{% if server_data == {} %}
# ERROR: server data not found ({{server}})
-{% else %}
-{# # collect optional server parameters #}
-{% set server_options = [] %}
-{# # check if health check is enabled #}
-{% if healthcheck_enabled == '1' %}
-{% do server_options.append('check') %}
-{# # This can be configured in multiple places. #}
-{# # Priority for which value is used: backend > server > health check #}
-{% if backend.checkInterval|default("") != "" %}
-{% do server_options.append('inter ' ~ backend.checkInterval) %}
-{% elif server_data.checkInterval|default("") != "" %}
-{% do server_options.append('inter ' ~ server_data.checkInterval) %}
-{% elif healthcheck_data.interval|default("") != "" %}
-{% do server_options.append('inter ' ~ healthcheck_data.interval) %}
-{% endif %}
-{# # use a different interval when server is in DOWN state #}
-{% if backend.checkDownInterval|default("") != "" %}
-{% do server_options.append('downinter ' ~ backend.checkDownInterval) %}
-{% elif server_data.checkDownInterval|default("") != "" %}
-{% do server_options.append('downinter ' ~ server_data.checkDownInterval) %}
-{% endif %}
-{# # unhealthy threshold #}
-{% if backend.healthCheckFall|default("") != "" %}
-{% do server_options.append('fall ' ~ backend.healthCheckFall) %}
-{% endif %}
-{# # healthy threshold #}
-{% if backend.healthCheckRise|default("") != "" %}
-{% do server_options.append('rise ' ~ backend.healthCheckRise) %}
-{% endif %}
-{# # use a different port for health check #}
-{% if healthcheck_data.checkport|default("") != "" %}
-{# # prefer port from health check template #}
-{% do server_options.append('port ' ~ healthcheck_data.checkport) %}
-{% elif server_data.checkport|default("") != "" %}
-{% do server_options.append('port ' ~ server_data.checkport) %}
-{% endif %}
-{# # add all additions from healthchecks here #}
-{% do server_options.append(healthcheck_additions|join(' ')) if healthcheck_additions.length != '0' %}
-{% endif %}
-{# # server weight #}
-{% do server_options.append('weight ' ~ server_data.weight) if server_data.weight|default("") != "" %}
-{# # server role/mode #}
-{% if server_data.mode|default("") != 'active' %}
-{% do server_options.append(server_data.mode) %}
-{% endif %}
-{# # server ssl communication #}
-{% if server_data.ssl|default("") == '1' %}
-{% do server_options.append('ssl') %}
-{# # get status of ssl verification #}
-{% set ssl_verify_enabled = '0' %}
-{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') and OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %}
-{# # NOTE: Global parameter overrides per-server configuration. #}
-{% set ssl_verify_enabled = '1' if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") == 'required' %}
-{% elif server_data.sslVerify|default("") == '1' %}
-{% set ssl_verify_enabled = '1' %}
-{% endif %}
-{# # configure ssl verification #}
-{% if ssl_verify_enabled == '1' %}
-{# # enable SSL verification #}
-{% do server_options.append('verify required') %}
-{# # check for SSL CA #}
-{% if server_data.sslCA|default("") != "" %}
-{% do server_options.append('ca-file /tmp/haproxy/ssl/' ~ server_data.id ~ '.calist') %}
+{% else %}
+{# # collect optional server parameters #}
+{% set server_options = [] %}
+{# # check if health check is enabled #}
+{% if healthcheck_enabled == '1' %}
+{% do server_options.append('check') %}
+{# # This can be configured in multiple places. #}
+{# # Priority for which value is used: backend > server > health check #}
+{% if backend.checkInterval|default("") != "" %}
+{% do server_options.append('inter ' ~ backend.checkInterval) %}
+{% elif server_data.checkInterval|default("") != "" %}
+{% do server_options.append('inter ' ~ server_data.checkInterval) %}
+{% elif healthcheck_data.interval|default("") != "" %}
+{% do server_options.append('inter ' ~ healthcheck_data.interval) %}
{% endif %}
-{# # check for SSL CRL #}
-{% if server_data.sslCRL|default("") != "" %}
-{% do server_options.append('crl-file /tmp/haproxy/ssl/' ~ server_data.sslCRL ~ '.pem') %}
+{# # use a different interval when server is in DOWN state #}
+{% if backend.checkDownInterval|default("") != "" %}
+{% do server_options.append('downinter ' ~ backend.checkDownInterval) %}
+{% elif server_data.checkDownInterval|default("") != "" %}
+{% do server_options.append('downinter ' ~ server_data.checkDownInterval) %}
{% endif %}
-{# # check for SSL client cert #}
-{% if server_data.sslClientCertificate|default("") != "" %}
-{% do server_options.append('crt /tmp/haproxy/ssl/' ~ server_data.sslClientCertificate ~ '.pem') %}
+{# # unhealthy threshold #}
+{% if backend.healthCheckFall|default("") != "" %}
+{% do server_options.append('fall ' ~ backend.healthCheckFall) %}
{% endif %}
-{% else %}
-{% do server_options.append('verify none') %}
+{# # healthy threshold #}
+{% if backend.healthCheckRise|default("") != "" %}
+{% do server_options.append('rise ' ~ backend.healthCheckRise) %}
+{% endif %}
+{# # use a different port for health check #}
+{% if healthcheck_data.checkport|default("") != "" %}
+{# # prefer port from health check template #}
+{% do server_options.append('port ' ~ healthcheck_data.checkport) %}
+{% elif server_data.checkport|default("") != "" %}
+{% do server_options.append('port ' ~ server_data.checkport) %}
+{% endif %}
+{# # force SSL encryption for health checks #}
+{% if healthcheck_data.force_ssl|default('') == '1' %}
+{% do server_options.append('check-ssl ') %}
+{% endif %}
+{# # add all additions from healthchecks here #}
+{% do server_options.append(healthcheck_additions|join(' ')) if healthcheck_additions.length != '0' %}
{% endif %}
-{% endif %}
-{# # source address #}
-{% if backend.source|default("") != "" %}
-{# # prefer backend configuration #}
-{% do server_options.append('source ' ~ backend.source) %}
-{% elif server_data.source|default("") != "" %}
-{% do server_options.append('source ' ~ server_data.source) %}
-{% endif %}
-{# # PROXY protocol #}
-{% if backend.proxyProtocol|default("") == "v1" %}
-{% do server_options.append('send-proxy') %}
-{% do server_options.append('check-send-proxy') %}
-{% elif backend.proxyProtocol|default("") == "v2" %}
-{% do server_options.append('send-proxy-v2') %}
-{% do server_options.append('check-send-proxy') %}
-{% endif %}
-{# # cookie-based persistence #}
-{% if backend.persistence|default("") == "cookie" %}
-{% do server_options.append('cookie ' ~ server_data.id|replace(".", "")) %}
-{% endif %}
-{# # server advanced options #}
-{% if server_data.advanced|default("") != "" %}
-{% do server_options.append(server_data.advanced) %}
-{% endif %}
-{# # server enabled? #}
-{% if server_data.enabled == '1' %}
+{# # server weight #}
+{% do server_options.append('weight ' ~ server_data.weight) if server_data.weight|default("") != "" %}
+{# # server role/mode #}
+{% if server_data.mode|default("") != 'active' %}
+{% do server_options.append(server_data.mode) %}
+{% endif %}
+{# # server ssl communication #}
+{% if server_data.ssl|default("") == '1' %}
+{% do server_options.append('ssl') %}
+{# # get status of ssl verification #}
+{% set ssl_verify_enabled = '0' %}
+{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') and OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %}
+{# # NOTE: Global parameter overrides per-server configuration. #}
+{% set ssl_verify_enabled = '1' if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") == 'required' %}
+{% elif server_data.sslVerify|default("") == '1' %}
+{% set ssl_verify_enabled = '1' %}
+{% endif %}
+{# # configure ssl verification #}
+{% if ssl_verify_enabled == '1' %}
+{# # enable SSL verification #}
+{% do server_options.append('verify required') %}
+{# # check for SSL CA #}
+{% if server_data.sslCA|default("") != "" %}
+{% do server_options.append('ca-file /tmp/haproxy/ssl/' ~ server_data.id ~ '.calist') %}
+{% endif %}
+{# # check for SSL CRL #}
+{% if server_data.sslCRL|default("") != "" %}
+{% do server_options.append('crl-file /tmp/haproxy/ssl/' ~ server_data.sslCRL ~ '.pem') %}
+{% endif %}
+{# # check for SSL client cert #}
+{% if server_data.sslClientCertificate|default("") != "" %}
+{% do server_options.append('crt /tmp/haproxy/ssl/' ~ server_data.sslClientCertificate ~ '.pem') %}
+{% endif %}
+{% else %}
+{% do server_options.append('verify none') %}
+{% endif %}
+{% endif %}
+{# # source address #}
+{% if backend.source|default("") != "" %}
+{# # prefer backend configuration #}
+{% do server_options.append('source ' ~ backend.source) %}
+{% elif server_data.source|default("") != "" %}
+{% do server_options.append('source ' ~ server_data.source) %}
+{% endif %}
+{# # PROXY protocol #}
+{% if backend.proxyProtocol|default("") == "v1" %}
+{% do server_options.append('send-proxy') %}
+{% do server_options.append('check-send-proxy') %}
+{% elif backend.proxyProtocol|default("") == "v2" %}
+{% do server_options.append('send-proxy-v2') %}
+{% do server_options.append('check-send-proxy') %}
+{% endif %}
+{# # cookie-based persistence #}
+{% if backend.persistence|default("") == "cookie" %}
+{% do server_options.append('cookie ' ~ server_data.id|replace(".", "")) %}
+{% endif %}
+{# # server advanced options #}
+{% if server_data.advanced|default("") != "" %}
+{% do server_options.append(server_data.advanced) %}
+{% endif %}
+{# # server enabled? #}
+{% if server_data.enabled == '1' %}
server {{server_data.name}} {{server_data.address}}:{% if backend.tuning_noport != '1' %}{% if server_data.port|default("") != "" %}{{server_data.port}}{% endif %}{% endif %} {{server_options|join(' ')}}
+{% endif %}
{% endif %}
-{% endif %}
-{% endfor %}
+{% endfor %}
+{% endif %}
{% else %}
# Backend (DISABLED): {{backend.name}} ({{backend.description}})