Merge pull request #31 from fraenki/haproxy_25

net/haproxy: SSL fixes & more – version 1.4
This commit is contained in:
Franco Fichtner
2016-08-03 18:07:25 +02:00
committed by GitHub
8 changed files with 166 additions and 27 deletions
+1 -2
View File
@@ -1,6 +1,5 @@
PLUGIN_NAME= haproxy
PLUGIN_VERSION= 1.3
PLUGIN_REVISION= 1
PLUGIN_VERSION= 1.4
PLUGIN_COMMENT= Reliable, high performance TCP/HTTP load balancer
PLUGIN_DEPENDS= haproxy
PLUGIN_MAINTAINER= opnsense@moov.de
@@ -26,6 +26,13 @@
<help><![CDATA[Configure listen addresses for this frontend, i.e. 127.0.0.1:8080 or www.example.com:443. Use TAB key to complete typing a listen address.]]></help>
<hint>Enter address:port here. Finish with TAB.</hint>
</field>
<field>
<id>frontend.bindOptions</id>
<label>Advanced Bind Options</label>
<type>text</type>
<help><![CDATA[A list of parameters that will be appended to every Listen Address line.<br/>Example: accept-proxy npn http/1.1<br/><div class="text-info"><b>NOTE:</b> The syntax will not be checked, use at your own risk!</div>]]></help>
<advanced>true</advanced>
</field>
<field>
<id>frontend.mode</id>
<label>Type</label>
@@ -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>
@@ -37,6 +37,13 @@
<type>text</type>
<help><![CDATA[Sets the maximum number of concurrent connections per HAProxy process.<br/><div class="text-info"><b>NOTE:</b> HAProxy will not be able to allocate enough memory if you set this value too high. Consider raising the settings for kern.maxfiles and kern.maxfilesperproc if you need to specify a non-default value.</div>]]></help>
</field>
<field>
<id>haproxy.general.tuning.sslServerVerify</id>
<label>Verify SSL Server Certificates</label>
<type>dropdown</type>
<help><![CDATA[This enforces a certain behavior for SSL verify on servers, ignoring per-server settings. If set to 'enforce verify', server certificates are verified. If set to 'disable verify', server certificates are not verified. The default is 'no preference' to only use per-server configurations and not enforce a global default for all servers.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>haproxy.general.tuning.maxDHSize</id>
<label>Maximum SSL DH Size</label>
@@ -27,6 +27,15 @@
<ValidationMessage>Please specify a value between 1 and 128.</ValidationMessage>
<Required>Y</Required>
</nbproc>
<sslServerVerify type="OptionField">
<Required>Y</Required>
<default>ignore</default>
<OptionValues>
<ignore>no preference [default]</ignore>
<required>enforce verify</required>
<none>disable verify</none>
</OptionValues>
</sslServerVerify>
<maxDHSize type="IntegerField">
<default>1024</default>
<MinimumValue>1024</MinimumValue>
@@ -243,6 +252,9 @@
<ChangeCase>lower</ChangeCase>
<ValidationMessage>Please provide a valid listen address, i.e. 127.0.0.1:8080 or www.example.com:443. Port range as start-end, i.e. 127.0.0.1:1220-1240.</ValidationMessage>
</bind>
<bindOptions type="TextField">
<Required>N</Required>
</bindOptions>
<mode type="OptionField">
<Required>Y</Required>
<default>http</default>
@@ -558,13 +570,31 @@
<active>active [default]</active>
<backup>backup</backup>
<disabled>disabled</disabled>
<inactive>inactive</inactive>
</OptionValues>
</mode>
<ssl type="BooleanField">
<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";
}
}
}
}
}
}
}
@@ -1,5 +1,5 @@
name: opnsense-haproxy
version: 1.2
version: 1.4
origin: opnsense/haproxy
comment: load balancer
desc: Reliable, high performance TCP/HTTP load balancer
@@ -43,7 +43,12 @@
{% for acl in action_data.linkedAcls.split(",") %}
{% set acl_data = helpers.getUUID(acl) %}
{% set acl_enabled = '1' %}
{% do action_acls.append('acl_' ~ acl_data.id) %}
{# # first check if this ACL condition should be negated #}
{% if acl_data.negate|default("") == '1' %}
{% do action_acls.append('!acl_' ~ acl_data.id) if acl_data.negate|default("") == '1' %}
{% else %}
{% do action_acls.append('acl_' ~ acl_data.id) %}
{% endif %}
{# # check if this ACL was already defined in this scope #}
{% if acl_data.id in acls_seen %}
{# # DEBUG: ignoring duplicate ACL {{acl_data.name}} #}
@@ -456,6 +461,11 @@ global
{% if helpers.exists('OPNsense.HAProxy.general.tuning.maxDHSize') %}
tune.ssl.default-dh-param {{OPNsense.HAProxy.general.tuning.maxDHSize}}
{% endif %}
{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') %}
{% if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %}
ssl-server-verify {{OPNsense.HAProxy.general.tuning.sslServerVerify}}
{% endif %}
{% endif %}
{% if OPNsense.HAProxy.general.tuning.spreadChecks|default("") != "" %}
spread-checks {{OPNsense.HAProxy.general.tuning.spreadChecks}}
{% endif %}
@@ -554,7 +564,7 @@ frontend {{frontend.name}}
{# # bind/listen configuration #}
{% if frontend.bind|default("") != "" %}
{% for bind in frontend.bind.split(",") %}
bind {{bind}} name {{bind}} {% if frontend.ssl_enabled == '1' and ssl_certs|default("") != "" %}ssl {{ ssl_options }}{{ssl_certs|join(' ')}} {% endif %}
bind {{bind}} name {{bind}} {% if frontend.bindOptions|default("") != "" %}{{ frontend.bindOptions }} {% endif %}{% if frontend.ssl_enabled == '1' and ssl_certs|default("") != "" %}ssl {{ ssl_options }}{{ssl_certs|join(' ')}} {% endif %}
{% endfor %}
{% endif %}
@@ -778,7 +788,40 @@ backend {{backend.name}}
{# # server weight #}
{% do server_options.append('weight ' ~ server_data.weight) if server_data.weight|default("") != "" %}
{# # server role/mode #}
{% do server_options.append(server_data.mode) if server_data.mode|default("") != "active" %}
{% if server_data.mode|default("") != 'active' %}
{% do server_options.append(server_data.mode) %}
{% endif %}
{# # server ssl communication #}
{% if server_data.ssl|default("") == '1' %}
{% do server_options.append('ssl') %}
{# # get status of ssl verification #}
{% set ssl_verify_enabled = '0' %}
{% if helpers.exists('OPNsense.HAProxy.general.tuning.sslServerVerify') and OPNsense.HAProxy.general.tuning.sslServerVerify|default("") != 'ignore' %}
{# # NOTE: Global parameter overrides per-server configuration. #}
{% set ssl_verify_enabled = '1' if OPNsense.HAProxy.general.tuning.sslServerVerify|default("") == 'required' %}
{% elif server_data.sslVerify|default("") == '1' %}
{% set ssl_verify_enabled = '1' %}
{% endif %}
{# # configure ssl verification #}
{% if ssl_verify_enabled == '1' %}
{# # enable SSL verification #}
{% do server_options.append('verify required') %}
{# # check for SSL CA #}
{% if server_data.sslCA|default("") != "" %}
{% do server_options.append('ca-file /var/etc/haproxy/ssl/' ~ server_data.sslCA ~ '.pem') %}
{% endif %}
{# # check for SSL CRL #}
{% if server_data.sslCRL|default("") != "" %}
{% do server_options.append('crl-file /var/etc/haproxy/ssl/' ~ server_data.sslCRL ~ '.pem') %}
{% endif %}
{# # check for SSL client cert #}
{% if server_data.sslClientCertificate|default("") != "" %}
{% do server_options.append('crt /var/etc/haproxy/ssl/' ~ server_data.sslClientCertificate ~ '.pem') %}
{% endif %}
{% else %}
{% do server_options.append('verify none') %}
{% endif %}
{% endif %}
{# # source address #}
{% if backend.source|default("") != "" %}
{# # prefer backend configuration #}