Merge pull request #2214 from fraenki/haproxy_300

net/haproxy: release 3.0
This commit is contained in:
Frank Wall 2021-02-23 17:13:38 +01:00 committed by GitHub
commit a7d182a65d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1372 additions and 230 deletions

View file

@ -1,6 +1,5 @@
PLUGIN_NAME= haproxy
PLUGIN_VERSION= 2.26
PLUGIN_REVISION= 1
PLUGIN_VERSION= 3.0
PLUGIN_COMMENT= Reliable, high performance TCP/HTTP load balancer
PLUGIN_DEPENDS= haproxy
PLUGIN_MAINTAINER= opnsense@moov.de

View file

@ -6,6 +6,40 @@ very high loads while needing persistence or Layer7 processing.
Plugin Changelog
================
3.0
Added:
* new feature to change server state and weight on-the-fly (#2213)
* add new SSL bind option: prefer-client-ciphers
* add global option to enable old buggy behaviour for PROXY v2 connections
* add support for HTTP/2 in health checks
* add config export (#2035)
* add config diff
* guard against broken config by using a staging config file
* add basic OCSP stapling support (#1430)
* add support for e-mail alerts and mailers (#1669)
* add support for custom header checks (#1907)
* add support for server templates (#1975)
* add support for additional resolver options (#1975)
* add support for resolve-prefer option (#1975)
Fixed:
* fix maintenance page (python error: 'list' object has no attribute 'strip')
* prevent service outage by aborting "Apply" when configtest fails
* fix direct links to individual statistics tabs
Changed:
* change default SSL version to TLSv1.2 (ssl-min-ver)
* remove weak ciphers from (default) SSL settings
* remove default SSL bind options that would conflict with ssl-min-ver
* move SSL bind options below other SSL settings, they are rarely used nowadays
* change default for tune.ssl.default-dh-param from 1024 to 2048
* use new "http-check send" command for HTTP health checks
* change default for spreadChecks from 0 to 2
* no longer overwrite live config file when running a syntax check
* make restart/reload commands usable in cron jobs
* relax GUI input validation for servers, move validation to jinja template (#1975)
2.26
Fixed:

View file

@ -0,0 +1,94 @@
<?php
/**
* Copyright (C) 2021 Frank Wall
* Copyright (C) 2015 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.
*
*/
namespace OPNsense\HAProxy\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\Core\Backend;
use OPNsense\HAProxy\HAProxy;
/**
* Class StatisticsController
* @package OPNsense\HAProxy
*/
class ExportController extends ApiControllerBase
{
/**
* get config
* @return string
*/
public function configAction()
{
$backend = new Backend();
$response = $backend->configdRun("haproxy showconf");
return array("response" => $response);
}
/**
* get config diff
* @return string
*/
public function diffAction()
{
$backend = new Backend();
$response = $backend->configdRun("haproxy configdiff");
return array("response" => $response);
}
/**
* download config file or config archive
* @return array|mixed
*/
public function downloadAction($type)
{
$backend = new Backend();
if ($type == 'config') {
$result = $backend->configdRun("haproxy showconf");
$filename = 'haproxy.conf';
$filetype = 'text/plain';
$content = $result;
} else {
$result = $backend->configdRun("haproxy exportall");
$filename = 'haproxy_config_export.zip';
$filetype = 'application/zip';
$content = file_get_contents('/tmp/haproxy_config_export.zip');
}
$response = array(
'result' => $result,
'filename' => $filename,
'filetype' => $filetype,
'content' => base64_encode($content),
);
return $response;
}
}

View file

@ -132,7 +132,7 @@ class SettingsController extends ApiMutableModelControllerBase
public function searchServersAction()
{
return $this->searchBase('servers.server', array('enabled', 'name', 'address', 'port', 'description'), 'name');
return $this->searchBase('servers.server', array('enabled', 'name', 'type', 'address', 'port', 'description'), 'name');
}
public function getHealthcheckAction($uuid = null)
@ -409,4 +409,34 @@ class SettingsController extends ApiMutableModelControllerBase
{
return $this->searchBase('resolvers.resolver', array('enabled', 'name', 'nameservers'), 'name');
}
public function getmailerAction($uuid = null)
{
return $this->getBase('mailer', 'mailers.mailer', $uuid);
}
public function setmailerAction($uuid)
{
return $this->setBase('mailer', 'mailers.mailer', $uuid);
}
public function addmailerAction()
{
return $this->addBase('mailer', 'mailers.mailer');
}
public function delmailerAction($uuid)
{
return $this->delBase('mailers.mailer', $uuid);
}
public function togglemailerAction($uuid, $enabled = null)
{
return $this->toggleBase('mailers.mailer', $uuid);
}
public function searchmailersAction()
{
return $this->searchBase('mailers.mailer', array('enabled', 'name', 'mailservers', 'sender', 'recipient'), 'name');
}
}

View file

@ -0,0 +1,45 @@
<?php
/**
* Copyright (C) 2021 Frank Wall
* Copyright (C) 2015 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.
*
*/
namespace OPNsense\HAProxy;
/**
* Class ExportController
* @package OPNsense\HAProxy
*/
class ExportController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
// choose template
$this->view->pick('OPNsense/HAProxy/export');
}
}

View file

@ -53,6 +53,7 @@ class IndexController extends \OPNsense\Base\IndexController
$this->view->formDialogGroup = $this->getForm("dialogGroup");
$this->view->formDialogHealthcheck = $this->getForm("dialogHealthcheck");
$this->view->formDialogLua = $this->getForm("dialogLua");
$this->view->formDialogMailer = $this->getForm("dialogMailer");
$this->view->formDialogMapfile = $this->getForm("dialogMapfile");
$this->view->formDialogResolver = $this->getForm("dialogResolver");
$this->view->formDialogServer = $this->getForm("dialogServer");

View file

@ -148,6 +148,91 @@
<type>text</type>
<help><![CDATA[HTTP request URL path contains string (substring match)]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>
<style>expression_table table_cust_hdr_beg</style>
</field>
<field>
<id>acl.cust_hdr_beg_name</id>
<label>Header Name</label>
<type>text</type>
<help><![CDATA[The name of the HTTP Header.]]></help>
</field>
<field>
<id>acl.cust_hdr_beg</id>
<label>Header Prefix</label>
<type>text</type>
<help><![CDATA[HTTP Header starts with string (prefix match)]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>
<style>expression_table table_cust_hdr_end</style>
</field>
<field>
<id>acl.cust_hdr_end_name</id>
<label>Header Name</label>
<type>text</type>
<help><![CDATA[The name of the HTTP Header.]]></help>
</field>
<field>
<id>acl.cust_hdr_end</id>
<label>Header Suffix</label>
<type>text</type>
<help><![CDATA[HTTP Header ends with string (suffix match)]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>
<style>expression_table table_cust_hdr</style>
</field>
<field>
<id>acl.cust_hdr_name</id>
<label>Header Name</label>
<type>text</type>
<help><![CDATA[The name of the HTTP Header.]]></help>
</field>
<field>
<id>acl.cust_hdr</id>
<label>Header Matches</label>
<type>text</type>
<help><![CDATA[HTTP Header matches exact string]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>
<style>expression_table table_cust_hdr_reg</style>
</field>
<field>
<id>acl.cust_hdr_reg_name</id>
<label>Header Name</label>
<type>text</type>
<help><![CDATA[The name of the HTTP Header.]]></help>
</field>
<field>
<id>acl.cust_hdr_reg</id>
<label>Header Regex</label>
<type>text</type>
<help><![CDATA[HTTP Header matches regular expression]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>
<style>expression_table table_cust_hdr_sub</style>
</field>
<field>
<id>acl.cust_hdr_sub_name</id>
<label>Header Name</label>
<type>text</type>
<help><![CDATA[The name of the HTTP Header.]]></help>
</field>
<field>
<id>acl.cust_hdr_sub</id>
<label>Header Contains</label>
<type>text</type>
<help><![CDATA[HTTP Header contains string (substring match)]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>

View file

@ -89,7 +89,7 @@
<id>action.http_request_redirect</id>
<label>HTTP Redirect</label>
<type>text</type>
<help><![CDATA[Use HAProxy's redirect function to return a HTTP redirection. See <a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#redirect">HAProxy's documentation</a> for further details and examples.]]></help>
<help><![CDATA[Use HAProxy's redirect function to return a HTTP redirection. See <a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#redirect">HAProxy's documentation</a> for further details and examples.]]></help>
</field>
<field>
<label>Parameters</label>
@ -128,7 +128,7 @@
<id>action.http_request_add_header_content</id>
<label>Header Content</label>
<type>text</type>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it is possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it is possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
</field>
<field>
<label>Parameters</label>
@ -145,7 +145,7 @@
<id>action.http_request_set_header_content</id>
<label>Header Content</label>
<type>text</type>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it's possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it's possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
</field>
<field>
<label>Parameters</label>
@ -251,7 +251,7 @@
<id>action.http_response_add_header_content</id>
<label>Header Content</label>
<type>text</type>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it's possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it's possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
</field>
<field>
<label>Parameters</label>
@ -268,7 +268,7 @@
<id>action.http_response_set_header_content</id>
<label>Header Content</label>
<type>text</type>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it's possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
<help><![CDATA[The value that should be set for the specified HTTP header. Note that it's possible to use pre-defined variables, see <a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#8.2.4">HAProxy's documentation</a> for further details and examples.]]></help>
</field>
<field>
<label>Parameters</label>

View file

@ -28,7 +28,7 @@
<id>backend.algorithm</id>
<label>Balancing Algorithm</label>
<type>dropdown</type>
<help><![CDATA[Define the load balancing algorithm to be used in a Backend Pool. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#balance">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[Define the load balancing algorithm to be used in a Backend Pool. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#balance">HAProxy documentation</a> for a full description.]]></help>
<hint>Choose a load balancing algorithm.</hint>
</field>
<field>
@ -42,7 +42,7 @@
<id>backend.proxyProtocol</id>
<label>Proxy Protocol</label>
<type>dropdown</type>
<help><![CDATA[Enforces use of the PROXY protocol over any connection established to the configured servers. The PROXY protocol informs the other end about the layer 3/4 addresses of the incoming connection, so that it can know the client's address or the public address it accessed to, whatever the upper layer protocol. This setting must not be used if the servers are not aware of the PROXY protocol. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#send-proxy">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[Enforces use of the PROXY protocol over any connection established to the configured servers. The PROXY protocol informs the other end about the layer 3/4 addresses of the incoming connection, so that it can know the client's address or the public address it accessed to, whatever the upper layer protocol. This setting must not be used if the servers are not aware of the PROXY protocol. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#send-proxy">HAProxy documentation</a> for a full description.]]></help>
<advanced>true</advanced>
</field>
<field>
@ -61,6 +61,22 @@
<help><![CDATA[Select the custom resolver configuration that should be used for all servers in this backend.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>backend.resolverOpts</id>
<label>Resolver Options</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help><![CDATA[Add resolver options. Use TAB key to complete typing.]]></help>
<hint>Type option name or choose from list.</hint>
</field>
<field>
<id>backend.resolvePrefer</id>
<label>Prefer IP Family</label>
<type>dropdown</type>
<help><![CDATA[When DNS resolution is enabled for a server and multiple IP addresses from different families are returned, HAProxy will prefer using an IP address from the selected family.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>backend.source</id>
<label>Source address</label>
@ -119,6 +135,12 @@
<help><![CDATA[The number of consecutive successful health checks before a server is considered as available.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>backend.linkedMailer</id>
<label>E-Mail Alert</label>
<type>dropdown</type>
<help><![CDATA[Select an e-mail alert configuration. An e-mail is sent when the state of a server changes.]]></help>
</field>
<field>
<label>HTTP(S) settings</label>
<type>header</type>
@ -164,7 +186,7 @@
<id>backend.persistence_cookiemode</id>
<label>Cookie handling</label>
<type>dropdown</type>
<help><![CDATA[Usually it is better to reuse an existing cookie. In this case HAProxy prefixes the cookie with the required information. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4.2-cookie">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[Usually it is better to reuse an existing cookie. In this case HAProxy prefixes the cookie with the required information. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4.2-cookie">HAProxy documentation</a> for a full description.]]></help>
</field>
<field>
<id>backend.persistence_cookiename</id>
@ -186,14 +208,14 @@
<id>backend.stickiness_pattern</id>
<label>Table type</label>
<type>dropdown</type>
<help><![CDATA[Choose a request pattern to associate a user to a server. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#stick on">HAProxy documentation</a> for a full description.<br/><div class="text-info"><b>NOTE:</b> Consider not using this feature in multi-process mode, it can result in random behaviours.</div>]]></help>
<help><![CDATA[Choose a request pattern to associate a user to a server. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#stick on">HAProxy documentation</a> for a full description.<br/><div class="text-info"><b>NOTE:</b> Consider not using this feature in multi-process mode, it can result in random behaviours.</div>]]></help>
<hint>Choose a persistence type.</hint>
</field>
<field>
<id>backend.stickiness_dataTypes</id>
<label>Stored data types</label>
<type>select_multiple</type>
<help><![CDATA[This is used to store additional information in the stick-table. It may be used by ACLs in order to control various criteria related to the activity of the client matching the stick-table. Note that this directly impacts memory usage. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#stick-table">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[This is used to store additional information in the stick-table. It may be used by ACLs in order to control various criteria related to the activity of the client matching the stick-table. Note that this directly impacts memory usage. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#stick-table">HAProxy documentation</a> for a full description.]]></help>
</field>
<field>
<id>backend.stickiness_expire</id>

View file

@ -89,15 +89,6 @@
<type>header</type>
<style>mode_table table_http table_ssl table_ssl_advanced table_ssl_advanced_true</style>
</field>
<field>
<id>frontend.ssl_bindOptions</id>
<label>Bind options</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Used to enforce or disable certain SSL options.]]></help>
</field>
<field>
<id>frontend.ssl_minVersion</id>
<label>Minimum SSL Version</label>
@ -146,6 +137,15 @@
<type>text</type>
<help><![CDATA[Future requests to the domain should use only HTTPS for the specified time (in seconds): 15768000 = 6 months]]></help>
</field>
<field>
<id>frontend.ssl_bindOptions</id>
<label>Bind options</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Used to enforce or disable certain SSL options.]]></help>
</field>
<field>
<label>Client Certificate Auth</label>
<type>header</type>
@ -322,14 +322,14 @@
<id>frontend.stickiness_pattern</id>
<label>Table type</label>
<type>dropdown</type>
<help><![CDATA[Choose the type of data that should be stored in this stick-table. Note that this stick-table cannot be used for session persistence, it is only used to store additional per-connection data (select below). See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#stick-table">HAProxy documentation</a> for further information.]]></help>
<help><![CDATA[Choose the type of data that should be stored in this stick-table. Note that this stick-table cannot be used for session persistence, it is only used to store additional per-connection data (select below). See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#stick-table">HAProxy documentation</a> for further information.]]></help>
<hint>Choose a stick-table type.</hint>
</field>
<field>
<id>frontend.stickiness_dataTypes</id>
<label>Stored data types</label>
<type>select_multiple</type>
<help><![CDATA[This is used to store additional information in the stick-table. It may be used by ACLs in order to control various criteria related to the activity of the client matching the stick-table. Note that this directly impacts memory usage. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#stick-table">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[This is used to store additional information in the stick-table. It may be used by ACLs in order to control various criteria related to the activity of the client matching the stick-table. Note that this directly impacts memory usage. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#stick-table">HAProxy documentation</a> for a full description.]]></help>
</field>
<field>
<id>frontend.stickiness_expire</id>
@ -356,7 +356,7 @@
<id>frontend.stickiness_counter_key</id>
<label>Sticky counter key</label>
<type>text</type>
<help><![CDATA[It describes what elements of the incoming request or connection will be analyzed, extracted, combined, and used to select which table entry to update the counters. Defaults to "src" to track elements of the source IP. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#tcp-request connection">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[It describes what elements of the incoming request or connection will be analyzed, extracted, combined, and used to select which table entry to update the counters. Defaults to "src" to track elements of the source IP. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#tcp-request connection">HAProxy documentation</a> for a full description.]]></help>
<advanced>true</advanced>
</field>
<field>

View file

@ -0,0 +1,61 @@
<form>
<field>
<id>mailer.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this mailer configuration.</help>
</field>
<field>
<id>mailer.name</id>
<label>Name</label>
<type>text</type>
<help>Choose a name for this mailer configuration.</help>
</field>
<field>
<id>mailer.description</id>
<label>Description</label>
<type>text</type>
<help>Choose a optional description for this mailer configuration.</help>
</field>
<field>
<id>mailer.mailservers</id>
<label>Mail Servers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Add mailservers to this mailer configuration, i.e. 192.168.1.1:25. Use TAB key to complete typing.]]></help>
<hint>Enter ip:port here. Finish with TAB.</hint>
</field>
<field>
<id>mailer.sender</id>
<label>Mail Sender</label>
<type>text</type>
<help><![CDATA[Declare the from email address to be used in both the envelope and header of email alerts.]]></help>
</field>
<field>
<id>mailer.recipient</id>
<label>Mail Recipient</label>
<type>text</type>
<help><![CDATA[Declare both the recipient address in the envelope and to address in the header of email alerts.]]></help>
</field>
<field>
<id>mailer.loglevel</id>
<label>Alert Log Level</label>
<type>dropdown</type>
<help><![CDATA[Declare the maximum log level of messages for which email alerts will be sent. This acts as a filter on the sending of email alerts.]]></help>
</field>
<field>
<id>mailer.timeout</id>
<label>Timeout</label>
<type>text</type>
<help><![CDATA[Defines the time (in seconds) available for a mail/connection to be made and send to the mail server.]]></help>
</field>
<field>
<id>mailer.hostname</id>
<label>Hostname</label>
<type>text</type>
<help><![CDATA[Declare the to hostname address to be used when communicating with mailers.]]></help>
<advanced>true</advanced>
</field>
</form>

View file

@ -15,6 +15,6 @@
<id>mapfile.content</id>
<label>Content</label>
<type>textbox</type>
<help><![CDATA[Paste the content of your map file here. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#map">HAProxy documentation</a> for a full description.]]></help>
<help><![CDATA[Paste the content of your map file here. See the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#map">HAProxy documentation</a> for a full description.]]></help>
</field>
</form>

View file

@ -7,9 +7,9 @@
</field>
<field>
<id>server.name</id>
<label>Name</label>
<label>Name or Prefix</label>
<type>text</type>
<help>Name to identify this server.</help>
<help>Name to identify a static server. When creating a server template, then this prefix is used for the server names to be built.</help>
</field>
<field>
<id>server.description</id>
@ -17,6 +17,17 @@
<type>text</type>
<help>Description for this server.</help>
</field>
<field>
<id>server.type</id>
<label>Type</label>
<type>dropdown</type>
<help>Either configure a static server or a template to initialize multiple servers with shared parameters.</help>
</field>
<field>
<label>Static Server</label>
<type>header</type>
<style>table_server_type table_server_type_static</style>
</field>
<field>
<id>server.address</id>
<label>FQDN or IP</label>
@ -24,6 +35,42 @@
<help><![CDATA[Provide either the FQDN or the IP address of this server.]]></help>
<hint>Enter server address.</hint>
</field>
<field>
<label>Server Template</label>
<type>header</type>
<style>table_server_type table_server_type_template</style>
</field>
<field>
<id>server.serviceName</id>
<label>Service Name or FQDN</label>
<type>text</type>
<help><![CDATA[Provide either the FQDN for all the servers this template initializes or a service name to discover the available services via DNS SRV records.]]></help>
</field>
<field>
<id>server.number</id>
<label>Number of Servers</label>
<type>text</type>
<help><![CDATA[The number of servers this template initializes, i.e. 5 or 1-5.]]></help>
</field>
<field>
<id>server.linkedResolver</id>
<label>Resolvers</label>
<type>dropdown</type>
<help><![CDATA[Specify the resolver that the server template should look at to discover available services via DNS.]]></help>
</field>
<field>
<id>server.resolverOpts</id>
<label>Resolver Options</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help><![CDATA[Add resolver options. Use TAB key to complete typing.]]></help>
<hint>Type option name or choose from list.</hint>
</field>
<field>
<label>Common Options</label>
<type>header</type>
</field>
<field>
<id>server.port</id>
<label>Port</label>
@ -36,6 +83,13 @@
<type>dropdown</type>
<help><![CDATA[Sets the operation mode to use for this server.]]></help>
</field>
<field>
<id>server.resolvePrefer</id>
<label>Prefer IP Family</label>
<type>dropdown</type>
<help><![CDATA[When DNS resolution is enabled for a server and multiple IP addresses from different families are returned, HAProxy will prefer using an IP address from the selected family.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>server.ssl</id>
<label>SSL</label>

View file

@ -21,6 +21,12 @@
<type>checkbox</type>
<help><![CDATA[HAProxy will handle service restarts in a way that no connections are dropped. This is the best restart mode, because it has no impact on user experience. That being said, there might be edge cases where seamless reloads lead to unexpected behaviour.]]></help>
</field>
<field>
<id>haproxy.general.storeOcsp</id>
<label>Store OCSP responses</label>
<type>checkbox</type>
<help><![CDATA[Retrieve OCSP data everytime when starting or restarting HAProxy. For every certificate, the OCSP response will be fetched and stored in filesystem, and automatically picked-up by HAProxy on startup. However, depending on the number of certificates and other circumstances, this may noticeably increase the time required to start/restart the HAProxy service. Note that this only updates the OCSP responses once during start/restart, you need to setup a cron job to periodically update this data too.]]></help>
</field>
<field>
<id>haproxy.general.showIntro</id>
<label>Show introduction pages</label>

View file

@ -68,6 +68,12 @@
<type>text</type>
<help><![CDATA[Add some randomness in the check interval between 0 and +/- 50%. A value between 2 and 5 seems to show good results. The default value is 0 (disabled).]]></help>
</field>
<field>
<id>haproxy.general.tuning.bogusProxyEnabled</id>
<label>Enable old bogus PROXY v2 implementation</label>
<type>checkbox</type>
<help><![CDATA[A bug in the PROXY protocol v2 implementation was present in HAProxy up to version 2.1. Enabling this option reverts this old buggy behaviour.]]></help>
</field>
<field>
<id>haproxy.general.tuning.customOptions</id>
<label>Custom options</label>
@ -85,15 +91,6 @@
<type>checkbox</type>
<help><![CDATA[Enable global SSL default values.]]></help>
</field>
<field>
<id>haproxy.general.tuning.ssl_bindOptions</id>
<label>Bind options</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Used to enforce or disable certain SSL options.]]></help>
</field>
<field>
<id>haproxy.general.tuning.ssl_minVersion</id>
<label>Minimum SSL Version</label>
@ -118,4 +115,13 @@
<type>text</type>
<help><![CDATA[It sets the default string describing the list of cipher algorithms ("cipher suite") that are negotiated during the SSL/TLS handshake for TLSv1.3.]]></help>
</field>
<field>
<id>haproxy.general.tuning.ssl_bindOptions</id>
<label>Bind options</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Used to enforce or disable certain SSL options.]]></help>
</field>
</form>

View file

@ -1,6 +1,6 @@
<model>
<mount>//OPNsense/HAProxy</mount>
<version>2.10.0</version>
<version>3.0.0</version>
<description>the HAProxy load balancer</description>
<items>
<general>
@ -16,6 +16,10 @@
<default>0</default>
<Required>Y</Required>
</seamlessReload>
<storeOcsp type="BooleanField">
<default>0</default>
<Required>N</Required>
</storeOcsp>
<showIntro type="BooleanField">
<default>1</default>
</showIntro>
@ -86,7 +90,7 @@
</OptionValues>
</sslServerVerify>
<maxDHSize type="IntegerField">
<default>1024</default>
<default>2048</default>
<MinimumValue>1024</MinimumValue>
<MaximumValue>16384</MaximumValue>
<ValidationMessage>Please specify a value between 1024 and 16384.</ValidationMessage>
@ -107,12 +111,16 @@
<Required>N</Required>
</checkBufferSize>
<spreadChecks type="IntegerField">
<default>0</default>
<default>2</default>
<MinimumValue>0</MinimumValue>
<MaximumValue>50</MaximumValue>
<ValidationMessage>Please specify a value between 0 and 50.</ValidationMessage>
<Required>Y</Required>
</spreadChecks>
<bogusProxyEnabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</bogusProxyEnabled>
<luaMaxMem type="IntegerField">
<default>0</default>
<MinimumValue>0</MinimumValue>
@ -129,7 +137,7 @@
</ssl_defaultsEnabled>
<ssl_bindOptions type="OptionField">
<Required>N</Required>
<default>no-sslv3,no-tlsv10,no-tls-tickets</default>
<default>prefer-client-ciphers</default>
<Sorted>Y</Sorted>
<Multiple>Y</Multiple>
<OptionValues>
@ -144,11 +152,13 @@
<force-tlsv11>force-tlsv11</force-tlsv11>
<force-tlsv12>force-tlsv12</force-tlsv12>
<force-tlsv13>force-tlsv13</force-tlsv13>
<prefer-client-ciphers>prefer-client-ciphers</prefer-client-ciphers>
<strict-sni>strict-sni</strict-sni>
</OptionValues>
</ssl_bindOptions>
<ssl_minVersion type="OptionField">
<Required>N</Required>
<default>TLSv1.2</default>
<OptionValues>
<SSLv3>SSLv3</SSLv3>
<TLSv1.0>TLSv1.0</TLSv1.0>
@ -168,11 +178,11 @@
</OptionValues>
</ssl_maxVersion>
<ssl_cipherList type="TextField">
<default>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256</default>
<default>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256</default>
<Required>N</Required>
</ssl_cipherList>
<ssl_cipherSuites type="TextField">
<default>TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256</default>
<default>TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256</default>
<Required>N</Required>
</ssl_cipherSuites>
</tuning>
@ -461,7 +471,7 @@
</ssl_advancedEnabled>
<ssl_bindOptions type="OptionField">
<Required>N</Required>
<default>no-sslv3,no-tlsv10,no-tls-tickets</default>
<default>prefer-client-ciphers</default>
<Sorted>Y</Sorted>
<Multiple>Y</Multiple>
<OptionValues>
@ -476,11 +486,13 @@
<force-tlsv11>force-tlsv11</force-tlsv11>
<force-tlsv12>force-tlsv12</force-tlsv12>
<force-tlsv13>force-tlsv13</force-tlsv13>
<prefer-client-ciphers>prefer-client-ciphers</prefer-client-ciphers>
<strict-sni>strict-sni</strict-sni>
</OptionValues>
</ssl_bindOptions>
<ssl_minVersion type="OptionField">
<Required>N</Required>
<default>TLSv1.2</default>
<OptionValues>
<SSLv3>SSLv3</SSLv3>
<TLSv1.0>TLSv1.0</TLSv1.0>
@ -500,11 +512,11 @@
</OptionValues>
</ssl_maxVersion>
<ssl_cipherList type="TextField">
<default>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256</default>
<default>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256</default>
<Required>N</Required>
</ssl_cipherList>
<ssl_cipherSuites type="TextField">
<default>TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256</default>
<default>TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256</default>
<Required>N</Required>
</ssl_cipherSuites>
<ssl_hstsEnabled type="BooleanField">
@ -868,6 +880,24 @@
<multiple>N</multiple>
<Required>N</Required>
</linkedResolver>
<resolverOpts type="OptionField">
<Required>N</Required>
<Sorted>Y</Sorted>
<Multiple>Y</Multiple>
<OptionValues>
<allow-dup-ip>allow-dup-ip</allow-dup-ip>
<ignore-weight>ignore-weight</ignore-weight>
<prevent-dup-ip>prevent-dup-ip</prevent-dup-ip>
</OptionValues>
</resolverOpts>
<resolvePrefer type="OptionField">
<Required>N</Required>
<Multiple>N</Multiple>
<OptionValues>
<ipv4>prefer IPv4</ipv4>
<ipv6>prefer IPv6 [default]</ipv6>
</OptionValues>
</resolvePrefer>
<source type="TextField">
<mask>/^((([0-9a-zA-Z._\-\*:]+)))*/u</mask>
<ChangeCase>lower</ChangeCase>
@ -916,6 +946,18 @@
<ValidationMessage>Please specify a value between 1 and 100.</ValidationMessage>
<Required>N</Required>
</healthCheckRise>
<linkedMailer type="ModelRelationField">
<Model>
<template>
<source>OPNsense.HAProxy.HAProxy</source>
<items>mailers.mailer</items>
<display>name</display>
</template>
</Model>
<ValidationMessage>Related mailer not found</ValidationMessage>
<multiple>N</multiple>
<Required>N</Required>
</linkedMailer>
<http2Enabled type="BooleanField">
<default>0</default>
<Required>N</Required>
@ -1173,7 +1215,7 @@
<address type="TextField">
<mask>/^([0-9a-zA-Z\.,_\-:]){0,1024}$/u</mask>
<ValidationMessage>Please specify a valid servername or IP address.</ValidationMessage>
<Required>Y</Required>
<Required>N</Required>
</address>
<port type="IntegerField">
<MinimumValue>1</MinimumValue>
@ -1189,7 +1231,7 @@
<Required>N</Required>
</checkport>
<mode type="OptionField">
<Required>Y</Required>
<Required>N</Required>
<default>active</default>
<OptionValues>
<active>active [default]</active>
@ -1197,6 +1239,54 @@
<disabled>disabled</disabled>
</OptionValues>
</mode>
<type type="OptionField">
<Required>Y</Required>
<default>static</default>
<OptionValues>
<static>static</static>
<template>template</template>
</OptionValues>
</type>
<serviceName type="TextField">
<mask>/^([0-9a-zA-Z\.,_\-:]){0,1024}$/u</mask>
<ValidationMessage>Please specify a valid service name.</ValidationMessage>
<Required>N</Required>
</serviceName>
<number type="TextField">
<mask>/^[0-9]+(-[0-9]+)?/u</mask>
<ValidationMessage>Please specify a valid number or range.</ValidationMessage>
<Required>N</Required>
</number>
<linkedResolver type="ModelRelationField">
<Model>
<template>
<source>OPNsense.HAProxy.HAProxy</source>
<items>resolvers.resolver</items>
<display>name</display>
</template>
</Model>
<ValidationMessage>Related resolver not found</ValidationMessage>
<multiple>N</multiple>
<Required>N</Required>
</linkedResolver>
<resolverOpts type="OptionField">
<Required>N</Required>
<Sorted>Y</Sorted>
<Multiple>Y</Multiple>
<OptionValues>
<allow-dup-ip>allow-dup-ip</allow-dup-ip>
<ignore-weight>ignore-weight</ignore-weight>
<prevent-dup-ip>prevent-dup-ip</prevent-dup-ip>
</OptionValues>
</resolverOpts>
<resolvePrefer type="OptionField">
<Required>N</Required>
<Multiple>N</Multiple>
<OptionValues>
<ipv4>prefer IPv4</ipv4>
<ipv6>prefer IPv6 [default]</ipv6>
</OptionValues>
</resolvePrefer>
<ssl type="BooleanField">
<default>0</default>
<Required>Y</Required>
@ -1318,6 +1408,7 @@
<OptionValues>
<http10>HTTP/1.0 [default]</http10>
<http11>HTTP/1.1</http11>
<http2>HTTP/2</http2>
</OptionValues>
</http_version>
<http_host type="TextField">
@ -1441,6 +1532,11 @@
<path_reg>Path regex</path_reg>
<path_dir>Path contains subdir</path_dir>
<path_sub>Path contains string</path_sub>
<cust_hdr_beg>HTTP Header starts with</cust_hdr_beg>
<cust_hdr_end>HTTP Header ends with</cust_hdr_end>
<cust_hdr>HTTP Header matches</cust_hdr>
<cust_hdr_reg>HTTP Header regex</cust_hdr_reg>
<cust_hdr_sub>HTTP Header contains</cust_hdr_sub>
<url_param>URL parameter contains</url_param>
<ssl_c_verify>SSL Client certificate is valid</ssl_c_verify>
<ssl_c_verify_code>SSL Client certificate verify error result</ssl_c_verify_code>
@ -1538,6 +1634,56 @@
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</path_sub>
<cust_hdr_beg_name type="TextField">
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_beg_name>
<cust_hdr_beg type="TextField">
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_beg>
<cust_hdr_end_name type="TextField">
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_end_name>
<cust_hdr_end type="TextField">
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_end>
<cust_hdr_name type="TextField">
<mask>/^.{1,1024}$/u</mask>
<ValidationMessage>Should be a string between 1 and 1024 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_name>
<cust_hdr type="TextField">
<mask>/^.{1,1024}$/u</mask>
<ValidationMessage>Should be a string between 1 and 1024 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr>
<cust_hdr_reg_name type="TextField">
<mask>/^.{1,1024}$/u</mask>
<ValidationMessage>Should be a string between 1 and 1024 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_reg_name>
<cust_hdr_reg type="TextField">
<mask>/^.{1,1024}$/u</mask>
<ValidationMessage>Should be a string between 1 and 1024 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_reg>
<cust_hdr_sub_name type="TextField">
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_sub_name>
<cust_hdr_sub type="TextField">
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</cust_hdr_sub>
<url_param type="TextField">
<mask>/^.{1,4096}$/u</mask>
<Required>N</Required>
@ -2610,5 +2756,64 @@
</timeout_retry>
</resolver>
</resolvers>
<mailers>
<mailer type="ArrayField">
<id type="UniqueIdField">
<Required>Y</Required>
</id>
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<name type="TextField">
<mask>/^[^\t^,^;^\.^\[^\]^\{^\}]{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>Y</Required>
</name>
<description type="TextField">
<Required>N</Required>
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
</description>
<mailservers type="CSVListField">
<Required>Y</Required>
<Sorted>Y</Sorted>
<multiple>Y</multiple>
<mask>/^((([0-9a-zA-Z._\-\*:\[\]]+:[0-9]+(-[0-9]+)?)([,]){0,1}))*/u</mask>
<ChangeCase>lower</ChangeCase>
<ValidationMessage>Please provide mailserver addresses, i.e. 192.168.1.1:25.</ValidationMessage>
</mailservers>
<sender type="EmailField">
<Required>Y</Required>
</sender>
<recipient type="EmailField">
<Required>Y</Required>
</recipient>
<loglevel type="OptionField">
<Required>Y</Required>
<default>alert</default>
<OptionValues>
<emerg>emerg</emerg>
<alert>alert</alert>
<crit>crit</crit>
<err>err</err>
<warning>warning</warning>
<notice>notice</notice>
<info>info</info>
<debug>debug</debug>
</OptionValues>
</loglevel>
<timeout type="TextField">
<default>30</default>
<MinimumValue>4</MinimumValue>
<MaximumValue>10000</MaximumValue>
<ValidationMessage>Please specify a value between 4 and 10000 seconds.</ValidationMessage>
<Required>Y</Required>
</timeout>
<hostname type="HostnameField">
<Required>N</Required>
</hostname>
</mailer>
</mailers>
</items>
</model>

View file

@ -21,6 +21,7 @@
<Errorfiles VisibleName="Error Files" url="/ui/haproxy#errorfiles"/>
<Mapfiles VisibleName="Map Files" url="/ui/haproxy#mapfiles"/>
<Resolvers VisibleName="Resolvers" url="/ui/haproxy#resolvers"/>
<Mailers VisibleName="E-Mail Alerts" url="/ui/haproxy#mailers"/>
</Settings>
<Statistics order="20" url="/ui/haproxy/statistics">
<Overview VisibleName="Overview" url="/ui/haproxy/statistics#info"/>
@ -32,6 +33,7 @@
<Server VisibleName="Server" url="/ui/haproxy/maintenance#server"/>
</Maintenance>
<Log VisibleName="Log File" order="40" url="/ui/diagnostics/log/core/haproxy"/>
<Export VisibleName="Config Export" order="50" url="/ui/haproxy/export"/>
</HAProxy>
</Services>
</menu>

View file

@ -0,0 +1,44 @@
<?php
/**
* Copyright (C) 2021 Frank Wall
*
* 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.
*
*/
namespace OPNsense\HAProxy\Migrations;
use OPNsense\Base\BaseModelMigration;
class M3_0_0 extends BaseModelMigration
{
public function run($model)
{
// Servers have a 'type' field now
foreach ($model->getNodeByReference('servers.server')->iterateItems() as $server) {
$server->type = 'static';
}
}
}

View file

@ -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.
#}
<script>
$( document ).ready(function() {
/**
* show HAProxy config
*/
function update_showconf() {
ajaxCall(url="/api/haproxy/export/config/", sendData={}, callback=function(data,status) {
if (data['response'] && data['response'].trim()) {
$("#showconf").text(data['response']);
} else {
conf_help = "<br><span style=\"color: #000000; white-space: pre-wrap; font-family: monospace;\"> {{ lang._('Config file not found. Run a syntax check to create it.') }}</span><br>";
$("#showconfempty").append(conf_help);
$("#showconf").hide();
}
});
}
update_showconf();
/**
* show HAProxy config diff
*/
function update_showdiff() {
ajaxCall(url="/api/haproxy/export/diff/", sendData={}, callback=function(data,status) {
diff = '';
if (data['response'] && data['response'].trim()) {
var lines = data['response'].split("\n");
$.each(lines, function(n, line) {
switch(line.substring(0,1)) {
case '+':
color = '#3bbb33';
break;
case '-':
color = '#c13928';
break;
case '@':
color = '#3bb9c3';
break;
default:
color = '#000000';
}
diff += '<span style="color: ' + color + '; white-space: pre-wrap; font-family: monospace;">' + line + '</span><br>';
});
} else {
diff = "<br><span style=\"color: #000000; white-space: pre-wrap; font-family: monospace;\"> {{ lang._('New and old config files are identical.') }}</span><br>";
}
$("#showdiff").append(diff);
});
}
update_showdiff();
/**
* download HAProxy config
*/
$('[id*="exportbtn"]').each(function(){
$(this).click(function(){
var type = $(this).data("type");
ajaxGet("/api/haproxy/export/download/"+type+"/", {}, function(data, status){
if (data.filename !== undefined) {
var link = $('<a></a>')
.attr('href','data:'+data.filetype+';base64,' + data.content)
.attr('download', data.filename)
.appendTo('body');
link.ready(function() {
link.get(0).click();
link.empty();
});
}
});
});
});
// 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()
});
});
</script>
<ul class="nav nav-tabs" role="tablist" id="maintabs">
<li class="active"><a data-toggle="tab" href="#export"><b>{{ lang._('Config Export') }}</b></a></li>
<li><a data-toggle="tab" href="#diff">{{ lang._('Config Diff') }}</a></li>
</ul>
<div class="content-box tab-content">
<div id="export" class="tab-pane fade in active">
<div class="content-box" style="padding-bottom: 1.5em;">
<div id="showconfempty"></div>
<pre id="showconf"></pre>
<div class="col-md-12">
<hr />
<button class="btn btn-primary" id="exportbtn" data-type="config" type="button"><b>{{ lang._('Download Config') }}</b></button>
<button class="btn btn-primary" id="exportbtn" data-type="all" type="button"><b>{{ lang._('Download All') }}</b></button>
</div>
</div>
</div>
<div id="diff" class="tab-pane fade in">
<div class="content-box" style="padding-bottom: 1.5em;">
<div id="showdiff"></div>
</div>
</div>
</div>
{{ partial("layout_partials/base_dialog_processing") }}

View file

@ -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('<a href=\"/ui/haproxy/export#diff\">','</a>') }}",
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.
<li><a data-toggle="tab" href="#mapfiles">{{ lang._('Map Files') }}</a></li>
<li><a data-toggle="tab" href="#cpus">{{ lang._('CPU Affinity Rules') }}</a></li>
<li><a data-toggle="tab" href="#resolvers">{{ lang._('Resolvers') }}</a></li>
<li><a data-toggle="tab" href="#mailers">{{ lang._('E-Mail Alerts') }}</a></li>
</ul>
</li>
</ul>
@ -645,7 +647,7 @@ POSSIBILITY OF SUCH DAMAGE.
<li>{{ lang._('Lastly, enable HAProxy using the %sService%s settings page.') | format('<b>', '</b>') }}</li>
</ul>
<p>{{ lang._('Please be aware that you need to %smanually%s add the required firewall rules for all configured services.') | format('<b>', '</b>') }}</p>
<p>{{ 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('<a href="https://docs.opnsense.org/manual/how-tos/haproxy.html" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html" target="_blank">', '</a>', '<a href="https://github.com/opnsense/plugins/issues/" target="_blank">', '</a>') }}</p>
<p>{{ 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('<a href="https://docs.opnsense.org/manual/how-tos/haproxy.html" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html" target="_blank">', '</a>', '<a href="https://github.com/opnsense/plugins/issues/" target="_blank">', '</a>') }}</p>
<br/>
</div>
</div>
@ -653,12 +655,12 @@ POSSIBILITY OF SUCH DAMAGE.
<div id="subtab_haproxy-real-servers-introduction" class="tab-pane fade">
<div class="col-md-12">
<h1>{{ lang._('Real Servers') }}</h1>
<p>{{ lang._('HAProxy needs to know which servers should be used to serve content. The following minimum information must be provided for each server:') }}</p>
<p>{{ 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:') }}</p>
<ul>
<li>{{ lang._('%sFQDN or IP:%s The IP address or fully-qualified domain name that should be used when communicating with your server.') | format('<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>') }}</li>
</ul>
<p>{{ 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.") }}</p>
<p>{{ 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.") }}</p>
<p>{{ 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('<b>', '</b>') }}</p>
<br/>
</div>
@ -687,7 +689,7 @@ POSSIBILITY OF SUCH DAMAGE.
<li>{{ 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('<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>', '<b>', '</b>', '<b>', '</b>', '<b>', '</b>') }}</li>
</ul>
<p>{{ lang._("For more information on HAProxy's %sACL feature%s see the %sofficial documentation%s.") | format('<b>', '</b>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#7" target="_blank">', '</a>') }}</p>
<p>{{ lang._("For more information on HAProxy's %sACL feature%s see the %sofficial documentation%s.") | format('<b>', '</b>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7" target="_blank">', '</a>') }}</p>
<p>{{ 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('<b>', '</b>') }}</p>
<br/>
</div>
@ -702,7 +704,7 @@ POSSIBILITY OF SUCH DAMAGE.
<li>{{ 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('<b>', '</b>') }}</li>
</ul>
<p>{{ 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.') }}</p>
<p>{{ lang._("For more information on HAProxy's %suser/group management%s see the %sofficial documentation%s.") | format('<b>', '</b>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#3.4" target="_blank">', '</a>') }}</p>
<p>{{ lang._("For more information on HAProxy's %suser/group management%s see the %sofficial documentation%s.") | format('<b>', '</b>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#3.4" target="_blank">', '</a>') }}</p>
<br/>
</div>
</div>
@ -720,7 +722,7 @@ POSSIBILITY OF SUCH DAMAGE.
<li>{{ 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('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>', '<b>', '</b>') }}</li>
</ul>
<p>{{ lang._("For more details visit HAProxy's official documentation regarding the %sStatistics%s, %sCache%s and %sPeers%s features.") | format('<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#stats%20enable" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#10" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#3.5" target="_blank">', '</a>') }}</p>
<p>{{ lang._("For more details visit HAProxy's official documentation regarding the %sStatistics%s, %sCache%s and %sPeers%s features.") | format('<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#stats%20enable" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#10" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#3.5" target="_blank">', '</a>') }}</p>
<br/>
</div>
</div>
@ -735,8 +737,9 @@ POSSIBILITY OF SUCH DAMAGE.
<li>{{ 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('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ 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('<b>', '</b>', '<b>', '</b>') }}</li>
</ul>
<p>{{ 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('<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-errorfile" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#lua-load" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#cpu-map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#bind-process" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#process" target="_blank">', '</a>','<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#5.3.2" target="_blank">', '</a>') }}</p>
<p>{{ 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('<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4-errorfile" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#lua-load" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#cpu-map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#bind-process" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#process" target="_blank">', '</a>','<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.3.2" target="_blank">', '</a>') }}</p>
<br/>
</div>
</div>
@ -814,6 +817,7 @@ POSSIBILITY OF SUCH DAMAGE.
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="serverid" data-type="number" data-visible="false">{{ lang._('Server ID') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Server Name') }}</th>
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="address" data-type="string">{{ lang._('Server Address') }}</th>
<th data-column-id="port" data-type="string">{{ lang._('Server Port') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
@ -1169,6 +1173,40 @@ POSSIBILITY OF SUCH DAMAGE.
</div>
</div>
<div id="mailers" class="tab-pane fade">
<table id="grid-mailers" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogMailer">
<thead>
<tr>
<th data-column-id="mailerid" data-type="number" data-visible="false">{{ lang._('Mailer ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="Sender" data-type="string">{{ lang._('Sender') }}</th>
<th data-column-id="Recipient" data-type="string">{{ lang._('Recipient') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureAct-mailers" type="button"><b>{{ lang._('Apply') }}</b><i id="reconfigureAct_progress" class=""></i></button>
<button class="btn btn-primary" id="configtestAct-mailers" type="button"><b>{{ lang._('Test syntax') }}</b><i id="configtestAct_progress" class=""></i></button>
<br/>
<br/>
</div>
</div>
<!-- subtabs for general "Settings" tab below -->
<div id="general-settings" class="tab-pane fade">
<div class="content-box" style="padding-bottom: 1.5em;">
@ -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')])}}

View file

@ -45,10 +45,10 @@ POSSIBILITY OF SUCH DAMAGE.
formatters: {
"commands": function (column, row) {
buttons = ""
buttons += "<button type=\"button\" title=\"Set administrative state to ready. Puts the server in normal mode.\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"ready\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-check\"></span></button>"
buttons += " <button type=\"button\" title=\"Set administrative state to drain. Removes the server from load balancing but still allows it to be health checked and to accept new persistent connections\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"drain\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-sort-amount-desc\"></span></button>"
buttons += " <button type=\"button\" title=\"Set administrative state to maintenance. Disables any traffic to the server as well as any health checks.\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"maint\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-wrench\"></span></button>"
buttons += " <button type=\"button\" title=\"Change a server's weight.\" class=\"btn btn-xs btn-default command-set-weight\" data-weight=\"" + row.weight + "\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-balance-scale\"></span></button>"
buttons += "<button type=\"button\" title=\"{{ lang._('Set administrative state to ready. Puts the server in normal mode.') }}\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"ready\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-check\"></span></button>"
buttons += " <button type=\"button\" title=\"{{ lang._('Set administrative state to drain. Removes the server from load balancing but still allows it to be health checked and to accept new persistent connections') }}\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"drain\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-sort-amount-desc\"></span></button>"
buttons += " <button type=\"button\" title=\"{{ lang._('Set administrative state to maintenance. Disables any traffic to the server as well as any health checks.') }}\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"maint\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-wrench\"></span></button>"
buttons += " <button type=\"button\" title=\"{{ lang._('Change server weight.') }}\" class=\"btn btn-xs btn-default command-set-weight\" data-weight=\"" + row.weight + "\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-balance-scale\"></span></button>"
return buttons;
},
},
@ -272,10 +272,10 @@ POSSIBILITY OF SUCH DAMAGE.
<tr>
<td></td>
<td>
<button data-action="setStateBulk" title="Set administrative state to ready for all selected items." data-state="ready" type="button" class="btn btn-xs btn-default"><span class="fa fa-check"></span></button>
<button data-action="setStateBulk" title="Set administrative state to drain for all selected items." data-state="drain" type="button" class="btn btn-xs btn-default"><span class="fa fa-sort-amount-desc"></span></button>
<button data-action="setStateBulk" title="Set administrative state to maintenance for all selected items." data-state="maint" type="button" class="btn btn-xs btn-default"><span class="fa fa-wrench"></span></button>
<button data-action="setWeightBulk" data-weight="" type="button" class="btn btn-xs btn-default"><span class="fa fa-balance-scale"></span></button>
<button data-action="setStateBulk" title="{{ lang._('Set administrative state to ready for all selected items.') }}" data-state="ready" type="button" class="btn btn-xs btn-default"><span class="fa fa-check"></span></button>
<button data-action="setStateBulk" title="{{ lang._('Set administrative state to drain for all selected items.') }}" data-state="drain" type="button" class="btn btn-xs btn-default"><span class="fa fa-sort-amount-desc"></span></button>
<button data-action="setStateBulk" title="{{ lang._('Set administrative state to maintenance for all selected items.') }}" data-state="maint" type="button" class="btn btn-xs btn-default"><span class="fa fa-wrench"></span></button>
<button data-action="setWeightBulk" title="{{ lang._('Change server weight for all selected items.') }}" data-weight="" type="button" class="btn btn-xs btn-default"><span class="fa fa-balance-scale"></span></button>
</td>
</tr>
</tfoot>

View file

@ -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()
});
});
</script>

View file

@ -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

View file

@ -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

View file

@ -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}")

View file

@ -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 <https://www.gnu.org/licenses/>.
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

View file

@ -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

View file

@ -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

View file

@ -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 %}

View file

@ -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 %}