From 99f205e7b9e09246e669778f7d1d7dbe505ff5cf Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Sun, 14 Jul 2019 22:52:39 +0200 Subject: [PATCH 1/4] net/haproxy: allow backends without servers, closes #1304 --- .../templates/OPNsense/HAProxy/haproxy.conf | 216 +++++++++--------- 1 file changed, 111 insertions(+), 105 deletions(-) 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..938493cdb 100644 --- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf +++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf @@ -1131,10 +1131,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 +1286,119 @@ 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 %} +{# # 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}}) From 62128b41651071302ef9267aba00750a0e6a78d3 Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Sun, 14 Jul 2019 22:53:05 +0200 Subject: [PATCH 2/4] net/haproxy: bump version --- net/haproxy/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From e08ddd214417af0dbdb7936b501a0047b03f917a Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Sun, 14 Jul 2019 23:33:45 +0200 Subject: [PATCH 3/4] net/haproxy: support deciphered SNI check, improve wording, closes #1365 --- .../OPNsense/HAProxy/forms/dialogAcl.xml | 11 +++++++++++ .../app/models/OPNsense/HAProxy/HAProxy.xml | 19 ++++++++++++------- .../templates/OPNsense/HAProxy/haproxy.conf | 11 +++++++++-- 3 files changed, 32 insertions(+), 9 deletions(-) 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/models/OPNsense/HAProxy/HAProxy.xml b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml index 33d431a46..2499cecde 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 @@ -1290,7 +1290,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 +1316,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 +1621,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 938493cdb..94cf13f07 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) %} From 9c748353940983a84a1e92baf8c5b4e52599a289 Mon Sep 17 00:00:00 2001 From: Frank Wall Date: Mon, 15 Jul 2019 00:15:18 +0200 Subject: [PATCH 4/4] net/haproxy: allow to force SSL for health checks, closes #1282 --- .../OPNsense/HAProxy/forms/dialogHealthcheck.xml | 6 ++++++ .../mvc/app/controllers/OPNsense/HAProxy/forms/main.xml | 1 - .../opnsense/mvc/app/models/OPNsense/HAProxy/HAProxy.xml | 4 ++++ .../service/templates/OPNsense/HAProxy/haproxy.conf | 4 ++++ 4 files changed, 14 insertions(+), 1 deletion(-) 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 2499cecde..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 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 94cf13f07..9332ba734 100644 --- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf +++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf @@ -1336,6 +1336,10 @@ backend {{backend.name}} {% 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 %}