diff --git a/net/haproxy/Makefile b/net/haproxy/Makefile index b742ec62a..c02238883 100644 --- a/net/haproxy/Makefile +++ b/net/haproxy/Makefile @@ -1,6 +1,5 @@ PLUGIN_NAME= haproxy -PLUGIN_VERSION= 1.3 -PLUGIN_REVISION= 1 +PLUGIN_VERSION= 1.4 PLUGIN_COMMENT= Reliable, high performance TCP/HTTP load balancer PLUGIN_DEPENDS= haproxy PLUGIN_MAINTAINER= opnsense@moov.de diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml index 9f04d755c..e090d101c 100644 --- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml +++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogFrontend.xml @@ -26,6 +26,13 @@ Enter address:port here. Finish with TAB. + + frontend.bindOptions + + text + Example: accept-proxy npn http/1.1
NOTE: The syntax will not be checked, use at your own risk!
]]>
+ true +
frontend.mode diff --git a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogServer.xml b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogServer.xml index 7fceff403..bfa340571 100644 --- a/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogServer.xml +++ b/net/haproxy/src/opnsense/mvc/app/controllers/OPNsense/HAProxy/forms/dialogServer.xml @@ -37,6 +37,35 @@ checkbox + + server.sslVerify + + checkbox + Please note that this setting can be overriden by the global configuration.
NOTE: It is critically important to verify server certificates when using SSL to connect to servers, otherwise the communication is prone to trivial man-in-the-middle attacks rendering SSL totally useless.
]]>
+
+ + server.sslCA + + dropdown + To import additional CAs, go to Certificate Manager.]]> + Type CA name or choose from list. + + + server.sslCRL + + dropdown + To import additional CRLs, go to Certificate Manager.]]> + Type CRL name or choose from list. + true + + + server.sslClientCertificate + + dropdown + To import additional certificates, go to Certificate Manager.]]> + Type certificate name or choose from list. + true + server.weight 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 886eed24d..3866e557e 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 @@ -37,6 +37,13 @@ text
NOTE: HAProxy will not be able to allocate enough memory if you set this value too high. Consider raising the settings for kern.maxfiles and kern.maxfilesperproc if you need to specify a non-default value.
]]>
+ + haproxy.general.tuning.sslServerVerify + + 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 584645996..c1b55c270 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 @@ -27,6 +27,15 @@ Please specify a value between 1 and 128. Y + + Y + ignore + + no preference [default] + enforce verify + disable verify + + 1024 1024 @@ -243,6 +252,9 @@ lower Please provide a valid listen address, i.e. 127.0.0.1:8080 or www.example.com:443. Port range as start-end, i.e. 127.0.0.1:1220-1240. + + N + Y http @@ -558,13 +570,31 @@ active [default] backup disabled - inactive 0 Y + + 1 + Y + + + N + ca + Please select a valid CA from the list. + + + N + crl + Please select a valid CRL from the list. + + + N + cert + Please select a valid certificate from the list. + 0 256 diff --git a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php index 96f03ee43..02e70a1f7 100755 --- a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php +++ b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php @@ -37,27 +37,51 @@ require_once("legacy_bindings.inc"); use OPNsense\Core\Config; global $config; -// traverse HAProxy frontends +// configure ssl elements +$configNodes = [ + 'frontends' => ['ssl_certificates'], + 'servers' => ['sslCA', 'sslCRL', 'sslClientCertificate'], +]; +$certTypes = ['cert', 'ca', 'crl']; + +// traverse HAProxy configuration $configObj = Config::getInstance()->object(); -if (isset($configObj->OPNsense->HAProxy->frontends)) { - foreach ($configObj->OPNsense->HAProxy->frontends->children() as $frontend) { - if (!isset($frontend->ssl_enabled)) { - continue; - } - // multiple comma-separated values are possible - $certs = explode(',', $frontend->ssl_certificates); - foreach ($certs as $cert_refid) { - // if the frontend has a cert attached, search for its contents - if ($cert_refid != "") { - foreach ($configObj->cert as $cert) { - if ($cert_refid == (string)$cert->refid) { - // generate cert pem file - $pem_content = str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->crt))); - $pem_content .= "\n" . str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->prv))); - $output_pem_filename = "/var/etc/haproxy/ssl/" . $cert_refid . ".pem" ; - file_put_contents($output_pem_filename, $pem_content); - chmod($output_pem_filename, 0600); - echo "certificate exported to " . $output_pem_filename . "\n"; +foreach ($configNodes as $key => $value) { + // lookup all config nodes + if (isset($configObj->OPNsense->HAProxy->$key)) { + foreach ($configObj->OPNsense->HAProxy->$key->children() as $child) { + // search in all matching child elements for ssl data + foreach ($configNodes[$key] as $sslchild) { + if (isset($child->$sslchild)) { + // multiple comma-separated values are possible + $certs = explode(',', $child->$sslchild); + foreach ($certs as $cert_refid) { + // if the element has a cert attached, search for its contents + if ($cert_refid != "") { + // check all known cert types + foreach ($certTypes as $type) { + // search for cert (type) in config + foreach ($configObj->$type as $cert) { + if ($cert_refid == (string)$cert->refid) { + $pem_content = ''; + // CRLs require special export + if ( $type == 'crl' ) { + $crl =& lookup_crl($cert_refid); + crl_update($crl); + $pem_content = base64_decode($crl['text']); + } else { + $pem_content = str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->crt))); + $pem_content .= "\n" . str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->prv))); + } + // generate pem file + $output_pem_filename = "/var/etc/haproxy/ssl/" . $cert_refid . ".pem" ; + file_put_contents($output_pem_filename, $pem_content); + chmod($output_pem_filename, 0600); + echo "exported $type to " . $output_pem_filename . "\n"; + } + } + } + } } } } diff --git a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+MANIFEST b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+MANIFEST index b8ece5deb..e6f0c8899 100644 --- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+MANIFEST +++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+MANIFEST @@ -1,5 +1,5 @@ name: opnsense-haproxy -version: 1.2 +version: 1.4 origin: opnsense/haproxy comment: load balancer desc: Reliable, high performance TCP/HTTP load balancer 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 1161cae06..53559cab0 100644 --- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf +++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf @@ -43,7 +43,12 @@ {% for acl in action_data.linkedAcls.split(",") %} {% set acl_data = helpers.getUUID(acl) %} {% set acl_enabled = '1' %} -{% do action_acls.append('acl_' ~ acl_data.id) %} +{# # first check if this ACL condition should be negated #} +{% if acl_data.negate|default("") == '1' %} +{% do action_acls.append('!acl_' ~ acl_data.id) if acl_data.negate|default("") == '1' %} +{% else %} +{% do action_acls.append('acl_' ~ acl_data.id) %} +{% endif %} {# # check if this ACL was already defined in this scope #} {% if acl_data.id in acls_seen %} {# # DEBUG: ignoring duplicate ACL {{acl_data.name}} #} @@ -456,6 +461,11 @@ global {% if helpers.exists('OPNsense.HAProxy.general.tuning.maxDHSize') %} tune.ssl.default-dh-param {{OPNsense.HAProxy.general.tuning.maxDHSize}} {% endif %} +{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') %} +{% if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %} + ssl-server-verify {{OPNsense.HAProxy.general.tuning.sslServerVerify}} +{% endif %} +{% endif %} {% if OPNsense.HAProxy.general.tuning.spreadChecks|default("") != "" %} spread-checks {{OPNsense.HAProxy.general.tuning.spreadChecks}} {% endif %} @@ -554,7 +564,7 @@ frontend {{frontend.name}} {# # bind/listen configuration #} {% if frontend.bind|default("") != "" %} {% for bind in frontend.bind.split(",") %} - bind {{bind}} name {{bind}} {% if frontend.ssl_enabled == '1' and ssl_certs|default("") != "" %}ssl {{ ssl_options }}{{ssl_certs|join(' ')}} {% endif %} + bind {{bind}} name {{bind}} {% if frontend.bindOptions|default("") != "" %}{{ frontend.bindOptions }} {% endif %}{% if frontend.ssl_enabled == '1' and ssl_certs|default("") != "" %}ssl {{ ssl_options }}{{ssl_certs|join(' ')}} {% endif %} {% endfor %} {% endif %} @@ -778,7 +788,40 @@ backend {{backend.name}} {# # server weight #} {% do server_options.append('weight ' ~ server_data.weight) if server_data.weight|default("") != "" %} {# # server role/mode #} -{% do server_options.append(server_data.mode) if server_data.mode|default("") != "active" %} +{% 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 /var/etc/haproxy/ssl/' ~ server_data.sslCA ~ '.pem') %} +{% endif %} +{# # check for SSL CRL #} +{% if server_data.sslCRL|default("") != "" %} +{% do server_options.append('crl-file /var/etc/haproxy/ssl/' ~ server_data.sslCRL ~ '.pem') %} +{% endif %} +{# # check for SSL client cert #} +{% if server_data.sslClientCertificate|default("") != "" %} +{% do server_options.append('crt /var/etc/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 #}