@@ -32,6 +33,7 @@
+
diff --git a/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/Migrations/M3_0_0.php b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/Migrations/M3_0_0.php
new file mode 100644
index 000000000..fff7515d3
--- /dev/null
+++ b/net/haproxy/src/opnsense/mvc/app/models/OPNsense/HAProxy/Migrations/M3_0_0.php
@@ -0,0 +1,44 @@
+getNodeByReference('servers.server')->iterateItems() as $server) {
+ $server->type = 'static';
+ }
+ }
+}
diff --git a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/export.volt b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/export.volt
new file mode 100644
index 000000000..19c31de7a
--- /dev/null
+++ b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/export.volt
@@ -0,0 +1,143 @@
+{#
+
+Copyright (C) 2021 Frank Wall
+OPNsense® is Copyright © 2014 – 2016 by Deciso B.V.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+#}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Download Config') }}
+ {{ lang._('Download All') }}
+
+
+
+
+
+
+
+
+{{ partial("layout_partials/base_dialog_processing") }}
diff --git a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/index.volt b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/index.volt
index 59adaa892..63ff12973 100644
--- a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/index.volt
+++ b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/index.volt
@@ -212,12 +212,24 @@ POSSIBILITY OF SUCH DAMAGE.
}
);
+ $("#grid-mailers").UIBootgrid(
+ { search:'/api/haproxy/settings/searchMailers',
+ get:'/api/haproxy/settings/getMailer/',
+ set:'/api/haproxy/settings/setMailer/',
+ add:'/api/haproxy/settings/addMailer/',
+ del:'/api/haproxy/settings/delMailer/',
+ toggle:'/api/haproxy/settings/toggleMailer/',
+ options: {
+ rowCount:[10,25,50,100,500,1000]
+ }
+ }
+ );
+
// hook into on-show event for dialog to extend layout.
$('#DialogAcl').on('shown.bs.modal', function (e) {
$("#acl\\.expression").change(function(){
var service_id = 'table_' + $(this).val();
$(".expression_table").hide();
- // $(".table_"+$(this).val()).show();
$("."+service_id).show();
});
$("#acl\\.expression").change();
@@ -228,7 +240,6 @@ POSSIBILITY OF SUCH DAMAGE.
$("#action\\.type").change(function(){
var service_id = 'table_' + $(this).val();
$(".type_table").hide();
- // $(".table_"+$(this).val()).show();
$("."+service_id).show();
});
$("#action\\.type").change();
@@ -295,6 +306,16 @@ POSSIBILITY OF SUCH DAMAGE.
$("#healthcheck\\.type").change();
})
+ // hook into on-show event for dialog to extend layout.
+ $('#DialogServer').on('shown.bs.modal', function (e) {
+ $("#server\\.type").change(function(){
+ var service_id = 'table_server_type_' + $(this).val();
+ $(".table_server_type").hide();
+ $("."+service_id).show();
+ });
+ $("#server\\.type").change();
+ })
+
/***********************************************************************
* Commands
**********************************************************************/
@@ -313,29 +334,9 @@ POSSIBILITY OF SUCH DAMAGE.
if (data['result'].indexOf('ALERT') > -1) {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_DANGER,
- title: "{{ lang._('HAProxy config contains critical errors') }}",
- message: "{{ lang._('The HAProxy service may not be able to start due to critical errors. Try anyway?') }}",
+ title: "{{ lang._('HAProxy configtest found critical errors') }}",
+ message: "{{ lang._('The HAProxy service may not be able to start due to critical errors. Run syntax check for further details or review the changes in the %sConfiguration Diff%s.')|format('',' ') }}",
buttons: [{
- label: '{{ lang._('Continue') }}',
- cssClass: 'btn-primary',
- action: function(dlg){
- ajaxCall(url="/api/haproxy/service/reconfigure", sendData={}, callback=function(data,status) {
- if (status != "success" || data['status'] != 'ok') {
- BootstrapDialog.show({
- type: BootstrapDialog.TYPE_WARNING,
- title: "{{ lang._('Error reconfiguring HAProxy') }}",
- message: data['status'],
- draggable: true
- });
- }
- });
- // when done, disable progress animation
- $('[id*="reconfigureAct_progress"]').each(function(){
- $(this).removeClass("fa fa-spinner fa-pulse");
- });
- dlg.close();
- }
- }, {
icon: 'fa fa-trash-o',
label: '{{ lang._('Abort') }}',
action: function(dlg){
@@ -385,21 +386,21 @@ POSSIBILITY OF SUCH DAMAGE.
if (data['result'].indexOf('ALERT') > -1) {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_DANGER,
- title: "{{ lang._('HAProxy config contains critical errors') }}",
+ title: "{{ lang._('HAProxy configtest found critical errors') }}",
message: data['result'],
draggable: true
});
} else if (data['result'].indexOf('WARNING') > -1) {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
- title: "{{ lang._('HAProxy config contains minor errors') }}",
+ title: "{{ lang._('HAProxy configtest found minor errors') }}",
message: data['result'],
draggable: true
});
} else {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
- title: "{{ lang._('HAProxy config test result') }}",
+ title: "{{ lang._('HAProxy configtest result') }}",
message: "{{ lang._('Your HAProxy config contains no errors.') }}",
draggable: true
});
@@ -628,6 +629,7 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._('Map Files') }}
{{ lang._('CPU Affinity Rules') }}
{{ lang._('Resolvers') }}
+ {{ lang._('E-Mail Alerts') }}
@@ -645,7 +647,7 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._('Lastly, enable HAProxy using the %sService%s settings page.') | format('', ' ') }}
{{ lang._('Please be aware that you need to %smanually%s add the required firewall rules for all configured services.') | format('', ' ') }}
- {{ lang._('Further information is available in our %sHAProxy plugin documentation%s and of course in the %sofficial HAProxy documentation%s. Be sure to report bugs and request features on our %sGitHub issue page%s. Code contributions are also very welcome!') | format('', ' ', '', ' ', '', ' ') }}
+ {{ lang._('Further information is available in our %sHAProxy plugin documentation%s and of course in the %sofficial HAProxy documentation%s. Be sure to report bugs and request features on our %sGitHub issue page%s. Code contributions are also very welcome!') | format('', ' ', '', ' ', '', ' ') }}
@@ -653,12 +655,12 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._('Real Servers') }}
-
{{ lang._('HAProxy needs to know which servers should be used to serve content. The following minimum information must be provided for each server:') }}
+
{{ lang._('HAProxy needs to know which servers should be used to serve content. Either add a static server configuration or use a template to initialize multiple servers at once. The latter one can also be used to discover the available services via DNS SRV records. The following minimum information must be provided for each server:') }}
- {{ lang._('%sFQDN or IP:%s The IP address or fully-qualified domain name that should be used when communicating with your server.') | format('', ' ') }}
- {{ lang._('%sPort:%s The TCP or UDP port that should be used. If unset, the same port the client connected to will be used.') | format('', ' ') }}
+ {{ lang._('%sStatic Servers:%s The IP address or fully-qualified domain name that should be used when communicating with your server. Additionally the TCP or UDP port that should be used. If unset, the same port the client connected to will be used.') | format('', ' ') }}
+ {{ lang._('%sServer Templates:%s A prefix is required to build the server names. Additionally a service name or FQDN is required to identify the servers this template initializes') | format('', ' ') }}
-
{{ lang._("Please note that advanced mode settings allow you to disable a certain server or to configure it as a backup server in a Backend Pool. Another neat option is the possibility to adjust a server's weight relative to other servers in the same Backend Pool.") }}
+
{{ lang._("Please note that advanced mode settings allow you to adjust a server's weight relative to other servers in the same Backend Pool, in addition to fine-grained health check options.") }}
{{ lang._('Note that it is possible to directly add options to the HAProxy configuration by using the "option pass-through", a setting that is available for several configuration items. It allows you to implement configurations that are currently not officially supported by this plugin. It is strongly discouraged to rely on this feature. Please report missing features on our GitHub page!') | format('', ' ') }}
@@ -687,7 +689,7 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._('%sConditions:%s HAProxy is capable of extracting data from requests, responses and other connection data and match it against predefined patterns. Use these powerful patterns to compose a condition that may be used in multiple Rules.') | format('', ' ') }}
{{ lang._('%sRules:%s Perform a large set of actions if one or more %sConditions%s match. These Rules may be used in %sBackend Pools%s as well as %sPublic Services%s.') | format('', ' ', '', ' ', '', ' ', '', ' ') }}
-
{{ lang._("For more information on HAProxy's %sACL feature%s see the %sofficial documentation%s.") | format('', ' ', '', ' ') }}
+
{{ lang._("For more information on HAProxy's %sACL feature%s see the %sofficial documentation%s.") | format('', ' ', '', ' ') }}
{{ lang._('Note that it is possible to directly add options to the HAProxy configuration by using the "option pass-through", a setting that is available for several configuration items. It allows you to implement configurations that are currently not officially supported by this plugin. It is strongly discouraged to rely on this feature. Please report missing features on our GitHub page!') | format('', ' ') }}
@@ -702,7 +704,7 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._('%sGroup:%s A optional list containing one or more users. Groups usually make it easier to manage permissions for a large number of users') | format('', ' ') }}
{{ lang._('Note that users and groups must be selected from the Backend Pool or Public Service configuration in order to be used for authentication. In addition to this users and groups may also be used in Rules/Conditions.') }}
- {{ lang._("For more information on HAProxy's %suser/group management%s see the %sofficial documentation%s.") | format('', ' ', '', ' ') }}
+ {{ lang._("For more information on HAProxy's %suser/group management%s see the %sofficial documentation%s.") | format('', ' ', '', ' ') }}
@@ -720,7 +722,7 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._("%sCache:%s HAProxy's cache which was designed to perform cache on small objects (favicon, css, etc.). This is a minimalist low-maintenance cache which runs in RAM.") | format('', ' ', '', ' ') }}
{{ lang._("%sPeers:%s Configure a communication channel between two HAProxy instances. This will propagate entries of any data-types in stick-tables between these HAProxy instances over TCP connections in a multi-master fashion. Useful when aiming for a seamless failover in a HA setup.") | format('', ' ', '', ' ') }}
- {{ lang._("For more details visit HAProxy's official documentation regarding the %sStatistics%s, %sCache%s and %sPeers%s features.") | format('', ' ', '', ' ', '', ' ') }}
+ {{ lang._("For more details visit HAProxy's official documentation regarding the %sStatistics%s, %sCache%s and %sPeers%s features.") | format('', ' ', '', ' ', '', ' ') }}
@@ -735,8 +737,9 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._("%sMap Files:%s A map allows to map a data in input to an other one on output. For example, this makes it possible to map a large number of domains to backend pools without using the GUI. Map files need to be used in %sRules%s, otherwise they are ignored.") | format('', ' ', '', ' ') }}
{{ lang._("%sCPU Affinity Rules:%s This feature makes it possible to bind HAProxy's processes/threads to a specific CPU (or a CPU set). Furthermore it is possible to select CPU Affinity Rules in %sPublic Services%s to restrict them to a certain set of processes/threads/CPUs.") | format('', ' ', '', ' ') }}
{{ lang._("%sResolvers:%s This feature allows in-depth configuration of how HAProxy handles name resolution and interacts with name resolvers (DNS). Each resolver configuration can be used in %sBackend Pools%s to apply individual name resolution configurations.") | format('', ' ', '', ' ') }}
+ {{ lang._("%sE-Mail Alerts:%s It is possible to send email alerts when the state of servers changes. Each configuration can be used in %sBackend Pools%s to send e-mail alerts to the configured recipient.") | format('', ' ', '', ' ') }}
- {{ lang._("For more details visit HAProxy's official documentation regarding the %sError Messages%s, %sLua Script%s and the %sMap Files%s features. More information on HAProxy's CPU Affinity is also available %shere%s, %shere%s and %shere%s. A detailed explanation of the resolvers feature can be found %shere%s.") | format('', ' ', '', ' ', '', ' ' ,'', ' ' ,'', ' ' ,'', ' ','', ' ') }}
+ {{ lang._("For more details visit HAProxy's official documentation regarding the %sError Messages%s, %sLua Script%s and the %sMap Files%s features. More information on HAProxy's CPU Affinity is also available %shere%s, %shere%s and %shere%s. A detailed explanation of the resolvers feature can be found %shere%s.") | format('', ' ', '', ' ', '', ' ' ,'', ' ' ,'', ' ' ,'', ' ','', ' ') }}
@@ -814,6 +817,7 @@ POSSIBILITY OF SUCH DAMAGE.
{{ lang._('Enabled') }}
{{ lang._('Server ID') }}
{{ lang._('Server Name') }}
+ {{ lang._('Type') }}
{{ lang._('Server Address') }}
{{ lang._('Server Port') }}
{{ lang._('Description') }}
@@ -1169,6 +1173,40 @@ POSSIBILITY OF SUCH DAMAGE.
+
+
+
+
+ {{ lang._('Mailer ID') }}
+ {{ lang._('Enabled') }}
+ {{ lang._('Name') }}
+ {{ lang._('Sender') }}
+ {{ lang._('Recipient') }}
+ {{ lang._('Commands') }}
+ {{ lang._('ID') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Apply') }}
+ {{ lang._('Test syntax') }}
+
+
+
+
+
@@ -1262,3 +1300,4 @@ POSSIBILITY OF SUCH DAMAGE.
{{ partial("layout_partials/base_dialog",['fields':formDialogMapfile,'id':'DialogMapfile','label':lang._('Edit Map File')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogCpu,'id':'DialogCpu','label':lang._('Edit CPU Affinity Rule')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogResolver,'id':'DialogResolver','label':lang._('Edit Resolver')])}}
+{{ partial("layout_partials/base_dialog",['fields':formDialogMailer,'id':'DialogMailer','label':lang._('Edit E-Mail Alert')])}}
diff --git a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt
index 33332c27b..63c870e6d 100644
--- a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt
+++ b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/maintenance.volt
@@ -45,10 +45,10 @@ POSSIBILITY OF SUCH DAMAGE.
formatters: {
"commands": function (column, row) {
buttons = ""
- buttons += " "
- buttons += " "
- buttons += " "
- buttons += " "
+ buttons += " "
+ buttons += " "
+ buttons += " "
+ buttons += " "
return buttons;
},
},
@@ -272,10 +272,10 @@ POSSIBILITY OF SUCH DAMAGE.
-
-
-
-
+
+
+
+
diff --git a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/statistics.volt b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/statistics.volt
index 52be520da..1f2c4085d 100644
--- a/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/statistics.volt
+++ b/net/haproxy/src/opnsense/mvc/app/views/OPNsense/HAProxy/statistics.volt
@@ -157,6 +157,17 @@ POSSIBILITY OF SUCH DAMAGE.
$("#update-status").click();
$("#update-counters").click();
$("#update-tables").click();
+
+ // update history on tab state and implement navigation
+ if(window.location.hash != "") {
+ $('a[href="' + window.location.hash + '"]').click()
+ }
+ $('.nav-tabs a').on('shown.bs.tab', function (e) {
+ history.pushState(null, null, e.target.hash);
+ });
+ $(window).on('hashchange', function(e) {
+ $('a[href="' + window.location.hash + '"]').click()
+ });
});
diff --git a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php
index ea4c086f5..7bfa26819 100755
--- a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php
+++ b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/exportCerts.php
@@ -80,7 +80,13 @@ foreach ($configNodes as $key => $value) {
if (!empty((string)$cert->caref)) {
$cert = (array)$cert;
$ca = ca_chain($cert);
+ // append the CA to the certificate data
$pem_content .= "\n" . $ca;
+ // additionally export CA to it's own file,
+ // not required for HAProxy, but makes OCSP handling easier
+ $output_ca_filename = $export_path . $cert_refid . ".issuer";
+ file_put_contents($output_ca_filename, $ca);
+ chmod($output_ca_filename, 0600);
}
}
// generate pem file for individual certs
diff --git a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/setup.sh b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/setup.sh
index cb88ee64d..8dc393461 100755
--- a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/setup.sh
+++ b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/setup.sh
@@ -1,5 +1,9 @@
#!/bin/sh
+if [ -f /etc/rc.conf.d/haproxy ]; then
+. /etc/rc.conf.d/haproxy
+fi
+
# NOTE: Keep /var/haproxy on this list, see GH issue opnsense/plugins #39.
HAPROXY_DIRS="/var/haproxy /var/haproxy/var/run /tmp/haproxy /tmp/haproxy/ssl /tmp/haproxy/lua /tmp/haproxy/errorfiles /tmp/haproxy/mapfiles"
@@ -18,4 +22,19 @@ find /var/haproxy -type d -exec chmod 550 {} \;
/usr/local/opnsense/scripts/OPNsense/HAProxy/exportErrorFiles.php > /dev/null 2>&1
/usr/local/opnsense/scripts/OPNsense/HAProxy/exportMapFiles.php > /dev/null 2>&1
+# update OCSP data
+if [ "${haproxy_ocsp}" == "YES" ]; then
+ /usr/local/opnsense/scripts/OPNsense/HAProxy/updateOcsp.sh > /dev/null 2>&1
+fi
+
+# deploy new config
+case "$1" in
+deploy)
+ # run syntax check against newly generated config
+ if /usr/local/sbin/haproxy -c -f /usr/local/etc/haproxy.conf.staging > /dev/null 2>&1; then
+ cp /usr/local/etc/haproxy.conf.staging /usr/local/etc/haproxy.conf
+ fi
+ ;;
+esac
+
exit 0
diff --git a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/socketCommand.py b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/socketCommand.py
index fd9b438c0..554db684a 100755
--- a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/socketCommand.py
+++ b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/socketCommand.py
@@ -148,7 +148,7 @@ try:
if con:
result = con.sendCmd(command_class(**command_args), objectify=False)
if result:
- print(result.strip())
+ print(result)
else:
print(f"Could not open socket {SOCKET}")
diff --git a/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/updateOcsp.sh b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/updateOcsp.sh
new file mode 100755
index 000000000..12c4c8724
--- /dev/null
+++ b/net/haproxy/src/opnsense/scripts/OPNsense/HAProxy/updateOcsp.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+# This file is based on:
+# https://github.com/acmesh-official/acme.sh/blob/master/deploy/haproxy.sh
+#
+# Copyright (C) 2021 Neil Pang
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+HAPROXY_DIR="/tmp/haproxy/ssl"
+
+for _pem in "$HAPROXY_DIR"/*.pem; do
+ cert_file="$(basename "$_pem")"
+ _issuer="${HAPROXY_DIR}/${cert_file%.pem}.issuer"
+ _ocsp="${_pem}.ocsp"
+ cert_cn="$(openssl x509 -in "$_pem" -noout -text | sed -nE 's/.*Subject:.*CN = ([^,]*)(,.*)?$/\1/p')"
+
+ if [ ! -f "$_issuer" ]; then
+ continue
+ fi
+
+ if [ -r "${_issuer}" ]; then
+ _ocsp_url="$(openssl x509 -noout -ocsp_uri -in "$_pem")"
+ if [ -n "$_ocsp_url" ]; then
+ _ocsp_host="$(echo "$_ocsp_url" | cut -d/ -f3)"
+ subjectdn="$(openssl x509 -in "$_issuer" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)"
+ issuerdn="$(openssl x509 -in "$_issuer" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)"
+ if [ "$subjectdn" = "$issuerdn" ]; then
+ _cafile_argument="-CAfile \"${_issuer}\""
+ else
+ _cafile_argument=""
+ fi
+ _openssl_version=$(openssl version | cut -d' ' -f2)
+ _openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
+ _openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
+ if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
+ _header_sep="="
+ else
+ _header_sep=" "
+ fi
+
+ _openssl_ocsp_cmd="openssl ocsp \
+ -issuer \"${_issuer}\" \
+ -cert \"${_pem}\" \
+ -url \"${_ocsp_url}\" \
+ -header Host${_header_sep}\"${_ocsp_host}\" \
+ -respout \"${_ocsp}\" \
+ -verify_other \"${_issuer}\" \
+ ${_cafile_argument} \
+ | grep -q \"${_pem}: good\""
+
+ eval "${_openssl_ocsp_cmd}"
+ _ret=$?
+
+ if [ "${_ret}" != "0" ]; then
+ echo "Updating OCSP stapling failed with return code ${_ret}"
+ fi
+ fi
+ fi
+done
diff --git a/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf b/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf
index 48bcc629d..1e45bc954 100644
--- a/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf
+++ b/net/haproxy/src/opnsense/service/conf/actions.d/actions_haproxy.conf
@@ -5,7 +5,7 @@ type:script_output
message:setup haproxy service requirements
[start]
-command:/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh; /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh start
+command:/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh deploy; /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh start
parameters:
type:script
message:starting haproxy
@@ -17,19 +17,21 @@ type:script
message:stopping haproxy
[restart]
-command:/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh; /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh restart
+command:/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh deploy; /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh restart
parameters:
type:script
+description:Restart HAProxy service
message:restarting haproxy
[reload]
-command:/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh; /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh reload || /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh restart
+command:/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh deploy; /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh reload || /usr/local/opnsense/scripts/OPNsense/HAProxy/rc-wrapper.sh restart
parameters:
type:script
+description:Reload HAProxy service
message:reloading haproxy
[configtest]
-command:/usr/local/etc/rc.d/haproxy configtest 2>&1 || exit 0
+command:/usr/local/sbin/haproxy -c -f /usr/local/etc/haproxy.conf.staging 2>&1 || exit 0
parameters:
type:script_output
message:testing haproxy configuration
@@ -100,3 +102,29 @@ parameters:
type:script_output
message:Sync ssl certificates into HAProxy memory for all frontends
description:Sync ssl certificates changes into HAProxy memory
+
+[showconf]
+command:test -f /usr/local/etc/haproxy.conf.staging && cat /usr/local/etc/haproxy.conf.staging
+parameters:
+type:script_output
+message:show haproxy config
+
+[exportall]
+command:/usr/local/bin/zip -r /tmp/haproxy_config_export.zip /tmp/haproxy /usr/local/etc/haproxy.conf.staging
+parameters:
+type:script_output
+message:show haproxy config
+
+[configdiff]
+command:/usr/bin/diff -Naur /usr/local/etc/haproxy.conf /usr/local/etc/haproxy.conf.staging; exit 0
+parameters:
+type:script_output
+message:diff haproxy config
+
+[update_ocsp]
+command:/usr/local/opnsense/scripts/OPNsense/HAProxy/updateOcsp.sh
+parameters:
+type:script_output
+description:Update HAProxy OCSP data
+message:update haproxy ocsp data
+
diff --git a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+TARGETS b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+TARGETS
index a8fa7728c..389191f4d 100644
--- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+TARGETS
+++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/+TARGETS
@@ -1,3 +1,3 @@
-haproxy.conf:/usr/local/etc/haproxy.conf
+haproxy.conf:/usr/local/etc/haproxy.conf.staging
rc.conf.d:/etc/rc.conf.d/haproxy
sslCerts.yaml:/usr/local/etc/haproxy/sslCerts.yaml
\ No newline at end of file
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 1796018a4..091d1d4d0 100644
--- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
+++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/haproxy.conf
@@ -158,6 +158,41 @@
{% set acl_enabled = '0' %}
# ERROR: missing parameters
{% endif %}
+{% elif acl_data.expression == 'cust_hdr_beg' %}
+{% if acl_data.cust_hdr_beg|default("") != "" and acl_data.cust_hdr_beg_name|default("") != "" %}
+{% do acl_options.append('hdr_beg(' ~ acl_data.cust_hdr_beg_name ~ ') -i ' ~ acl_data.cust_hdr_beg) %}
+{% else %}
+{% set acl_enabled = '0' %}
+ # ERROR: missing parameters
+{% endif %}
+{% elif acl_data.expression == 'cust_hdr_end' %}
+{% if acl_data.cust_hdr_end|default("") != "" and acl_data.cust_hdr_end_name|default("") %}
+{% do acl_options.append('hdr_end(' ~ acl_data.cust_hdr_end_name ~ ') -i ' ~ acl_data.cust_hdr_end) %}
+{% else %}
+{% set acl_enabled = '0' %}
+ # ERROR: missing parameters
+{% endif %}
+{% elif acl_data.expression == 'cust_hdr' %}
+{% if acl_data.cust_hdr|default("") != "" and acl_data.cust_hdr_name|default("") != "" %}
+{% do acl_options.append('hdr(' ~ acl_data.cust_hdr_name ~ ') -i ' ~ acl_data.cust_hdr) %}
+{% else %}
+{% set acl_enabled = '0' %}
+ # ERROR: missing parameters
+{% endif %}
+{% elif acl_data.expression == 'cust_hdr_reg' %}
+{% if acl_data.cust_hdr_reg|default("") != "" and acl_data.cust_hdr_reg_name|default("") != "" %}
+{% do acl_options.append('hdr_reg(' ~ acl_data.cust_hdr_reg_name ~ ') -i ' ~ acl_data.cust_hdr_reg) %}
+{% else %}
+{% set acl_enabled = '0' %}
+ # ERROR: missing parameters
+{% endif %}
+{% elif acl_data.expression == 'cust_hdr_sub' %}
+{% if acl_data.cust_hdr_sub|default("") != "" and acl_data.cust_hdr_sub_name|default("") != "" %}
+{% do acl_options.append('hdr_sub(' ~ acl_data.cust_hdr_sub_name ~ ') -i ' ~ acl_data.cust_hdr_sub) %}
+{% else %}
+{% set acl_enabled = '0' %}
+ # ERROR: missing parameters
+{% endif %}
{% elif acl_data.expression == 'url_param' %}
{% if acl_data.url_param_value|default("") != "" and acl_data.url_param|default("") != "" %}
{% do acl_options.append('url_param(' ~ acl_data.url_param ~ ') -i ' ~ acl_data.url_param_value) %}
@@ -838,6 +873,9 @@ global
{% if OPNsense.HAProxy.general.tuning.spreadChecks|default("") != "" %}
spread-checks {{OPNsense.HAProxy.general.tuning.spreadChecks}}
{% endif %}
+{% if OPNsense.HAProxy.general.tuning.bogusProxyEnabled|default("") == '1' %}
+ pp2-never-send-local
+{% endif %}
{% if OPNsense.HAProxy.general.tuning.checkBufferSize|default("") != "" %}
tune.chksize {{OPNsense.HAProxy.general.tuning.checkBufferSize}}
{% endif %}
@@ -1046,6 +1084,45 @@ resolvers {{resolver.id}}
{% endfor %}
{%- endif -%}
+{# ############################### #}
+{# MAILERS #}
+{# ############################### #}
+
+{% if helpers.exists('OPNsense.HAProxy.mailers') %}
+{% for mailer in helpers.toList('OPNsense.HAProxy.mailers.mailer') %}
+{% if mailer.enabled == '1' %}
+{# # check if mailer is configured in a backend #}
+{% set ns = namespace(mailer_found=false) %}
+{% if helpers.exists('OPNsense.HAProxy.backends') %}
+{% for backend in helpers.toList('OPNsense.HAProxy.backends.backend') %}
+{# # backend must be enabled #}
+{% if backend.enabled == '1' and backend.linkedMailer|default('') == mailer['@uuid'] %}
+{% set ns.mailer_found = True %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{# # only add mailers that are in use to avoid config test warnings #}
+{% if ns.mailer_found %}
+# Mailer: {{mailer.name}}
+mailers {{mailer.id}}
+ timeout mail {{mailer.timeout}}s
+{% if mailer.mailservers|default("") != "" %}
+{% for mailserver in mailer.mailservers.split(",") %}
+ mailer {{mailserver}} {{mailserver}}
+{% endfor %}
+{% endif %}
+
+{% else %}
+# NOTE: Mailer {{mailer.name}} ignored: not configured in any backend
+
+{% endif %}
+{% else %}
+# Mailer (DISABLED): {{mailer.name}}
+
+{% endif %}
+{% endfor %}
+{%- endif -%}
+
{# ############################### #}
{# FRONTENDS #}
{# ############################### #}
@@ -1285,15 +1362,20 @@ backend {{backend.name}}
{% endif %}
{% endif %}
{% elif healthcheck_data.type == 'http' %}
-{% do healthcheck_options.append('httpchk') %}
+ option httpchk
{# # HTTP method must be uppercase #}
+{% do healthcheck_options.append('send meth') %}
{% do healthcheck_options.append(healthcheck_data.http_method|upper) %}
+{% do healthcheck_options.append('uri') %}
{% do healthcheck_options.append(healthcheck_data.http_uri) %}
-{% do healthcheck_options.append('HTTP/1.0') if healthcheck_data.http_version == 'http10' %}
{# # HTTP Host header requires HTTP 1.1 #}
-{% do healthcheck_options.append('HTTP/1.1') if healthcheck_data.http_version == 'http11' and healthcheck_data.http_host|default("") == "" %}
-{% do healthcheck_options.append('HTTP/1.1\\r\\nHost:\ ' ~ healthcheck_data.http_host) if healthcheck_data.http_version == 'http11' and healthcheck_data.http_host|default("") != "" %}
- option {{healthcheck_options|join(' ')}}
+{% if (healthcheck_data.http_version == 'http11' or healthcheck_data.http_version == 'http2') and healthcheck_data.http_host|default('') != '' %}
+{% do healthcheck_options.append('ver HTTP/1.1 hdr Host ' ~ healthcheck_data.http_host) if healthcheck_data.http_version == 'http11' %}
+{% do healthcheck_options.append('ver HTTP/2 hdr Host ' ~ healthcheck_data.http_host) if healthcheck_data.http_version == 'http2' %}
+{% elif healthcheck_data.http_version == 'http10' %}
+{% do healthcheck_options.append('ver HTTP/1.0') %}
+{% endif %}
+ http-check {{healthcheck_options|join(' ')}}
{# # custom HTTP health check option #}
{% if healthcheck_data.http_expressionEnabled|default("") == '1' %}
{# # validate options #}
@@ -1345,6 +1427,23 @@ backend {{backend.name}}
# health checking is DISABLED
{% set healthcheck_enabled = '0' %}
{% endif %}
+{# # mailer #}
+{% if backend.linkedMailer|default("") != "" %}
+{% set mailer_data = helpers.getUUID(backend.linkedMailer) %}
+{% if mailer_data == {} %}
+# ERROR: mailer data not found ({{backend.linkedMailer}})
+{% elif mailer_data.enabled == '0' %}
+# NOTE: specified mailer is disabled ({{mailer_data.name}})
+{% else %}
+ email-alert mailers {{mailer_data.id}}
+ email-alert from {{mailer_data.sender}}
+ email-alert to {{mailer_data.recipient}}
+ email-alert level {{mailer_data.loglevel}}
+{% if mailer_data.hostname|default("") != "" %}
+ email-alert myhostname {{mailer_data.hostname}}
+{% endif %}
+{% endif %}
+{% endif %}
{# # NOTE: Usually the frontend and the backend are in the same mode, #}
{# # but we have no way to know what frontend uses this backend. #}
{# # Hence we can't automatically set the mode and thus need a #}
@@ -1413,135 +1512,169 @@ backend {{backend.name}}
{% 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 %}
-{# # 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 %}
-{# # 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') %}
-{# # HTTP/2 #}
-{% if backend.http2Enabled|default("") == '1' and backend.ba_advertised_protocols|default("") != "" %}
-{# # convert protocols to HAProxy-compatible format #}
-{% set alpn_options = backend.ba_advertised_protocols|replace('http10', 'http/1.0')|replace('http11', 'http/1.1') %}
-{% do server_options.append('alpn ' ~ alpn_options) %}
-{% endif %}
-{# # HTTP/2 without TLS #}
-{% elif backend.http2Enabled|default("") == '1' and backend.http2Enabled_nontls|default("") == '1' %}
-{% do server_options.append('proto h2') %}
-{% endif %}
-{# # ssl verification can be enabled for two reasons: #}
-{# # 1. in server settings: to verify *all* communication to this server #}
-{# # 2. in health checks: to verify *only* health check communication to this server #}
-{# # When 1. is enabled, health checks are automatically secured. #}
-{# # Use-case for 2: when using TCP for server communication, but HTTPS for health checks. #}
-{% if server_data.ssl|default("") == '1' or (healthcheck_enabled == '1' and healthcheck_data.force_ssl|default('') == '1') %}
-{# # 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 %}
-{# # fallback to system CA Root Certificates #}
-{% do server_options.append('ca-file /etc/ssl/cert.pem') %}
-{% 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 %}
+{# # check if all required server parameters are set #}
+{% if (server_data.type|default("") == 'static' and server_data.address|default("") == '') or (server_data.type|default("") == 'template' and (server_data.serviceName|default("") == '' or server_data.number|default("") == '')) %}
+# ERROR: server is invalid, required parameters not set ({{server_data.name}})
+{% else %}
+{# # server type #}
+{% set server_basics = [] %}
+{% if server_data.type|default("") == 'static' %}
+{% do server_basics.append('server ' ~ server_data.name ~ ' ' ~ server_data.address) %}
{% else %}
-{% do server_options.append('verify none') %}
+{% do server_basics.append('server-template ' ~ server_data.name ~ ' ' ~ server_data.number ~ ' ' ~ server_data.serviceName) %}
+{% endif %}
+{# # 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 %}
+{# # 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 %}
+{# # 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') %}
+{# # HTTP/2 #}
+{% if backend.http2Enabled|default("") == '1' and backend.ba_advertised_protocols|default("") != "" %}
+{# # convert protocols to HAProxy-compatible format #}
+{% set alpn_options = backend.ba_advertised_protocols|replace('http10', 'http/1.0')|replace('http11', 'http/1.1') %}
+{% do server_options.append('alpn ' ~ alpn_options) %}
+{% endif %}
+{# # HTTP/2 without TLS #}
+{% elif backend.http2Enabled|default("") == '1' and backend.http2Enabled_nontls|default("") == '1' %}
+{% do server_options.append('proto h2') %}
+{% endif %}
+{# # ssl verification can be enabled for two reasons: #}
+{# # 1. in server settings: to verify *all* communication to this server #}
+{# # 2. in health checks: to verify *only* health check communication to this server #}
+{# # When 1. is enabled, health checks are automatically secured. #}
+{# # Use-case for 2: when using TCP for server communication, but HTTPS for health checks. #}
+{% if server_data.ssl|default("") == '1' or (healthcheck_enabled == '1' and healthcheck_data.force_ssl|default('') == '1') %}
+{# # 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 %}
+{# # fallback to system CA Root Certificates #}
+{% do server_options.append('ca-file /etc/ssl/cert.pem') %}
+{% 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 %}
+{# # resolver #}
+{% set resolver_id = '' %}
+{% set resolver_opts = '' %}
+{% if backend.linkedResolver|default("") != "" %}
+{# # prefer backend configuration #}
+{% set resolver_id = backend.linkedResolver %}
+{% set resolver_opts = backend.resolverOpts %}
+{% elif server_data.linkedResolver|default("") != "" and server_data.type|default("") == 'template' %}
+{# # use resolver for server template #}
+{% set resolver_id = server_data.linkedResolver %}
+{% set resolver_opts = server_data.resolverOpts %}
+{% endif %}
+{% if resolver_id != '' %}
+{% set resolver_data = helpers.getUUID(resolver_id) %}
+{% do server_options.append('resolvers ' ~ resolver_data.id) %}
+{# # additional resolver options #}
+{% if resolver_opts != '' %}
+{% do server_options.append('resolve-opts ' ~ resolver_opts) %}
+{% endif %}
+{% endif %}
+{# # prefer selected IP family for DNS resolution #}
+{% if backend.resolvePrefer|default("") != "" %}
+{# # prefer backend configuration #}
+{% do server_options.append('resolve-prefer ' ~ backend.resolvePrefer) %}
+{% elif server_data.linkedResolver|default("") != "" %}
+{% do server_options.append('resolve-prefer ' ~ server_data.resolvePrefer) %}
+{% 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_basics|join(' ')}}{% if backend.tuning_noport != '1' %}{% if server_data.port|default("") != "" %}:{{server_data.port}}{% endif %}{% endif %} {{server_options|join(' ')}}
{% endif %}
-{% endif %}
-{# # resolver #}
-{% if backend.linkedResolver|default("") != "" %}
-{% set resolver_data = helpers.getUUID(backend.linkedResolver) %}
-{% do server_options.append('resolvers ' ~ resolver_data.id) %}
-{% 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 %}
{% endfor %}
diff --git a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/rc.conf.d b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/rc.conf.d
index 50cee173c..261881284 100644
--- a/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/rc.conf.d
+++ b/net/haproxy/src/opnsense/service/templates/OPNsense/HAProxy/rc.conf.d
@@ -3,6 +3,11 @@ haproxy_enable=YES
haproxy_var_script="/usr/local/opnsense/scripts/OPNsense/HAProxy/setup.sh"
haproxy_pidfile="/var/run/haproxy.pid"
haproxy_config="/usr/local/etc/haproxy.conf"
+{% if helpers.exists('OPNsense.HAProxy.general.storeOcsp') and OPNsense.HAProxy.general.storeOcsp|default("0") == "1" %}
+haproxy_ocsp=YES
+{% else %}
+haproxy_ocsp=NO
+{% endif %}
{% if helpers.exists('OPNsense.HAProxy.general.gracefulStop') and OPNsense.HAProxy.general.gracefulStop|default("0") == "1" %}
haproxy_hardstop=NO
{% else %}