net/haproxy: add options for advanced SSL verification, refs #27

This commit is contained in:
Frank Wall
2016-08-01 22:36:27 +02:00
parent 1878899f68
commit 606246c912
4 changed files with 124 additions and 23 deletions
@@ -37,6 +37,35 @@
<type>checkbox</type>
<help><![CDATA[Enable or disable SSL communication with this server.]]></help>
</field>
<field>
<id>server.sslVerify</id>
<label>Verify SSL Certificate</label>
<type>checkbox</type>
<help><![CDATA[If disabled, server certificate is not verified. Otherwise the certificate provided by the server is verified using CAs and optional CRLs. <b>Please note that this setting can be overriden by the global configuration.</b><br/><div class="text-info"><b>NOTE:</b> It is critically important to verify server certificates when using SSL to connect to servers, otherwise the communication is prone to trivial man-in-the-middle attacks rendering SSL totally useless.</div>]]></help>
</field>
<field>
<id>server.sslCA</id>
<label>SSL Verify CA</label>
<type>dropdown</type>
<help><![CDATA[This certificate authority will be used to verify server's certificate. <br/>To import additional CAs, go to <a href="/system_certmanager.php">Certificate Manager</a>.]]></help>
<hint>Type CA name or choose from list.</hint>
</field>
<field>
<id>server.sslCRL</id>
<label>SSL Verify CRL</label>
<type>dropdown</type>
<help><![CDATA[This certificate revocation list will be used to verify server's certificate. <br/>To import additional CRLs, go to <a href="/system_certmanager.php">Certificate Manager</a>.]]></help>
<hint>Type CRL name or choose from list.</hint>
<advanced>true</advanced>
</field>
<field>
<id>server.sslClientCertificate</id>
<label>SSL Client Certificate</label>
<type>dropdown</type>
<help><![CDATA[This certificate will be sent if the server send a client certificate request.<br/>To import additional certificates, go to <a href="/system_certmanager.php">Certificate Manager</a>.]]></help>
<hint>Type certificate name or choose from list.</hint>
<advanced>true</advanced>
</field>
<field>
<id>server.weight</id>
<label>Weight</label>
@@ -574,6 +574,25 @@
<default>0</default>
<Required>Y</Required>
</ssl>
<sslVerify type="BooleanField">
<default>1</default>
<Required>Y</Required>
</sslVerify>
<sslCA type="CertificateField">
<Required>N</Required>
<Type>ca</Type>
<ValidationMessage>Please select a valid CA from the list.</ValidationMessage>
</sslCA>
<sslCRL type="CertificateField">
<Required>N</Required>
<Type>crl</Type>
<ValidationMessage>Please select a valid CRL from the list.</ValidationMessage>
</sslCRL>
<sslClientCertificate type="CertificateField">
<Required>N</Required>
<Type>cert</Type>
<ValidationMessage>Please select a valid certificate from the list.</ValidationMessage>
</sslClientCertificate>
<weight type="IntegerField">
<MinimumValue>0</MinimumValue>
<MaximumValue>256</MaximumValue>
@@ -37,27 +37,51 @@ require_once("legacy_bindings.inc");
use OPNsense\Core\Config;
global $config;
// traverse HAProxy frontends
// configure ssl elements
$configNodes = [
'frontends' => ['ssl_certificates'],
'servers' => ['sslCA', 'sslCRL', 'sslClientCertificate'],
];
$certTypes = ['cert', 'ca', 'crl'];
// traverse HAProxy configuration
$configObj = Config::getInstance()->object();
if (isset($configObj->OPNsense->HAProxy->frontends)) {
foreach ($configObj->OPNsense->HAProxy->frontends->children() as $frontend) {
if (!isset($frontend->ssl_enabled)) {
continue;
}
// multiple comma-separated values are possible
$certs = explode(',', $frontend->ssl_certificates);
foreach ($certs as $cert_refid) {
// if the frontend has a cert attached, search for its contents
if ($cert_refid != "") {
foreach ($configObj->cert as $cert) {
if ($cert_refid == (string)$cert->refid) {
// generate cert pem file
$pem_content = str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->crt)));
$pem_content .= "\n" . str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->prv)));
$output_pem_filename = "/var/etc/haproxy/ssl/" . $cert_refid . ".pem" ;
file_put_contents($output_pem_filename, $pem_content);
chmod($output_pem_filename, 0600);
echo "certificate exported to " . $output_pem_filename . "\n";
foreach ($configNodes as $key => $value) {
// lookup all config nodes
if (isset($configObj->OPNsense->HAProxy->$key)) {
foreach ($configObj->OPNsense->HAProxy->$key->children() as $child) {
// search in all matching child elements for ssl data
foreach ($configNodes[$key] as $sslchild) {
if (isset($child->$sslchild)) {
// multiple comma-separated values are possible
$certs = explode(',', $child->$sslchild);
foreach ($certs as $cert_refid) {
// if the element has a cert attached, search for its contents
if ($cert_refid != "") {
// check all known cert types
foreach ($certTypes as $type) {
// search for cert (type) in config
foreach ($configObj->$type as $cert) {
if ($cert_refid == (string)$cert->refid) {
$pem_content = '';
// CRLs require special export
if ( $type == 'crl' ) {
$crl =& lookup_crl($cert_refid);
crl_update($crl);
$pem_content = base64_decode($crl['text']);
} else {
$pem_content = str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->crt)));
$pem_content .= "\n" . str_replace("\n\n", "\n", str_replace("\r", "", base64_decode((string)$cert->prv)));
}
// generate pem file
$output_pem_filename = "/var/etc/haproxy/ssl/" . $cert_refid . ".pem" ;
file_put_contents($output_pem_filename, $pem_content);
chmod($output_pem_filename, 0600);
echo "exported $type to " . $output_pem_filename . "\n";
}
}
}
}
}
}
}
@@ -457,7 +457,7 @@ global
tune.ssl.default-dh-param {{OPNsense.HAProxy.general.tuning.maxDHSize}}
{% endif %}
{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') %}
{% if OPNsense.HAProxy.general.tuning.spreadChecks|default("") != 'ignore' %}
{% if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %}
ssl-server-verify {{OPNsense.HAProxy.general.tuning.sslServerVerify}}
{% endif %}
{% endif %}
@@ -784,8 +784,37 @@ backend {{backend.name}}
{% do server_options.append('weight ' ~ server_data.weight) if server_data.weight|default("") != "" %}
{# # server role/mode #}
{% do server_options.append(server_data.mode) if server_data.mode|default("") != "active" %}
{# # server ssl support #}
{% do server_options.append('ssl ') if server_data.ssl|default("") == '1' %}
{# # server ssl communication #}
{% if server_data.ssl|default("") == '1' %}
{% do server_options.append('ssl') %}
{# # get status of ssl verification #}
{% set ssl_verify_enabled = '0' %}
{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') and OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %}
{# # NOTE: Global parameter overrides per-server configuration. #}
{% set ssl_verify_enabled = '1' if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") == 'required' %}
{% elif server_data.sslVerify|default("") == '1' %}
{% set ssl_verify_enabled = '1' %}
{% endif %}
{# # configure ssl verification #}
{% if ssl_verify_enabled == '1' %}
{# # enable SSL verification #}
{% do server_options.append('verify required') %}
{# # check for SSL CA #}
{% if server_data.sslCA|default("") != "" %}
{% do server_options.append('ca-file /var/etc/haproxy/ssl/' ~ server_data.sslCA ~ '.pem') %}
{% endif %}
{# # check for SSL CRL #}
{% if server_data.sslCRL|default("") != "" %}
{% do server_options.append('crl-file /var/etc/haproxy/ssl/' ~ server_data.sslCRL ~ '.pem') %}
{% endif %}
{# # check for SSL client cert #}
{% if server_data.sslClientCertificate|default("") != "" %}
{% do server_options.append('crt /var/etc/haproxy/ssl/' ~ server_data.sslClientCertificate ~ '.pem') %}
{% endif %}
{% else %}
{% do server_options.append('verify none') %}
{% endif %}
{% endif %}
{# # source address #}
{% if backend.source|default("") != "" %}
{# # prefer backend configuration #}