net/haproxy: add SNI support to servers and health checks (#2388)

This commit is contained in:
Frank Wall 2021-12-24 23:56:41 +01:00
parent da2004e414
commit 8b856ddc1d
6 changed files with 115 additions and 9 deletions

View file

@ -6,6 +6,14 @@ very high loads while needing persistence or Layer7 processing.
Plugin Changelog
================
3.9
Added:
* add SSL SNI setting to servers and health checks (#2388)
Changed:
* replace "force SSL" setting with "SSL preferences" in health checks (#2388)
3.8
Added:

View file

@ -17,18 +17,24 @@
<type>dropdown</type>
<help><![CDATA[Select type of health check.]]></help>
</field>
<field>
<id>healthcheck.ssl</id>
<label>SSL preferences</label>
<type>dropdown</type>
<help><![CDATA[This option forces encryption of all health checks over SSL, regardless of whether the server uses SSL or not for the normal traffic.]]></help>
</field>
<field>
<id>healthcheck.sslSNI</id>
<label>SSL SNI</label>
<type>text</type>
<help><![CDATA[The host name sent in the SNI TLS extension to the server. Overrides the server's SNI setting for health checks.]]></help>
</field>
<field>
<id>healthcheck.interval</id>
<label>Check interval</label>
<type>text</type>
<help><![CDATA[Select interval (in milliseconds) between two consecutive health checks. This value can be overriden in backend pool and real server configuration.]]></help>
</field>
<field>
<id>healthcheck.force_ssl</id>
<label>Force SSL</label>
<type>checkbox</type>
<help><![CDATA[This option forces encryption of all health checks over SSL, regardless of whether the server uses SSL or not for the normal traffic.]]></help>
</field>
<field>
<id>healthcheck.checkport</id>
<label>Port to check</label>

View file

@ -108,6 +108,12 @@
<type>checkbox</type>
<help><![CDATA[Enable or disable SSL communication with this server.]]></help>
</field>
<field>
<id>server.sslSNI</id>
<label>SSL SNI</label>
<type>text</type>
<help><![CDATA[The host name sent in the SNI TLS extension to the server.]]></help>
</field>
<field>
<id>server.sslVerify</id>
<label>Verify SSL Certificate</label>

View file

@ -1,6 +1,6 @@
<model>
<mount>//OPNsense/HAProxy</mount>
<version>3.4.0</version>
<version>3.5.0</version>
<description>the HAProxy load balancer</description>
<items>
<general>
@ -1304,6 +1304,11 @@
<default>0</default>
<Required>Y</Required>
</ssl>
<sslSNI type="TextField">
<mask>/^([0-9a-zA-Z._\-]){1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</sslSNI>
<sslVerify type="BooleanField">
<default>1</default>
<Required>Y</Required>
@ -1406,6 +1411,22 @@
<ValidationMessage>Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".</ValidationMessage>
<Required>Y</Required>
</interval>
<ssl type="OptionField">
<Required>N</Required>
<default>nopref</default>
<OptionValues>
<nopref>Use server settings</nopref>
<ssl>Force SSL for health checks</ssl>
<sslsni>Force SSL+SNI for health checks</sslsni>
<nossl>Force no SSL for health checks</nossl>
</OptionValues>
</ssl>
<sslSNI type="TextField">
<mask>/^([0-9a-zA-Z._\-]){1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>N</Required>
</sslSNI >
<!-- XXX: old value, required for model migration to 3.5.0 -->
<force_ssl type="BooleanField">
<default>0</default>
<Required>N</Required>

View file

@ -0,0 +1,50 @@
<?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_5_0 extends BaseModelMigration
{
public function run($model)
{
foreach ($model->getNodeByReference('healthchecks.healthcheck')->iterateItems() as $healthcheck) {
switch ((string)$healthcheck->force_ssl) {
case '0':
$healthcheck->ssl = 'nopref';
break;
case '1':
$healthcheck->ssl = 'ssl';
break;
}
}
}
}

View file

@ -1722,8 +1722,19 @@ backend {{backend.name}}
{% 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 ') %}
{% if healthcheck_data.ssl|default('') == 'ssl' or healthcheck_data.ssl|default('') == 'sslsni' %}
{% do server_options.append('check-ssl') %}
{% elif healthcheck_data.ssl|default('') == 'nossl' %}
{% do server_options.append('no-check-ssl') %}
{% endif %}
{# # add SNI header for health checks #}
{% if healthcheck_data.ssl|default('') == 'sslsni' and healthcheck_data.sslSNI|default('') != '' %}
{% do server_options.append('check-sni ' ~ healthcheck_data.sslSNI) %}
{# # fallback to server SNI value #}
{% elif healthcheck_data.ssl|default('') == 'sslsni' and server_data.sslSNI|default('') != '' %}
{% do server_options.append('check-sni ' ~ server_data.sslSNI) %}
{% elif healthcheck_data.ssl|default('') == 'sslsni' and (healthcheck_data.sslSNI|default('') == '' and server_data.sslSNI|default('') == '') %}
# ERROR: missing SSL SNI value
{% endif %}
{# # add all additions from healthchecks here #}
{% do server_options.append(healthcheck_additions|join(' ')) if healthcheck_additions.length != '0' %}
@ -1739,6 +1750,10 @@ backend {{backend.name}}
{# # server ssl communication #}
{% if server_data.ssl|default("") == '1' %}
{% do server_options.append('ssl') %}
{# # SNI #}
{% if server_data.sslSNI|default('') != '' %}
{% do server_options.append('sni str(' ~ server_data.sslSNI ~ ')') %}
{% endif %}
{# # HTTP/2 #}
{% if backend.http2Enabled|default("") == '1' and backend.ba_advertised_protocols|default("") != "" %}
{# # convert protocols to HAProxy-compatible format #}