aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiBa-NL <pba_2k3@yahoo.com>2013-11-21 23:14:30 +0100
committerPiBa-NL <pba_2k3@yahoo.com>2013-11-21 23:14:30 +0100
commit9824bac2ea71404e673d11fafbfd37f9a44dccc8 (patch)
treef727fb1c6da67f957f6fb3ac9afc6b814625b540
parent82f495970898105fba33c472a6bc29799e361755 (diff)
downloadpfsense-packages-9824bac2ea71404e673d11fafbfd37f9a44dccc8.tar.gz
pfsense-packages-9824bac2ea71404e673d11fafbfd37f9a44dccc8.tar.bz2
pfsense-packages-9824bac2ea71404e673d11fafbfd37f9a44dccc8.zip
haproxy-devel
-better IPv6 support -use certificate chains where available -new interface selections to listen on instead of only wan,VIPs,any,local -option to recalculate certificate chain links -show shared frontend option only when another primary frontend is present
-rw-r--r--config/haproxy-devel/haproxy.inc80
-rw-r--r--config/haproxy-devel/haproxy.xml10
-rwxr-xr-xconfig/haproxy-devel/haproxy_global.php18
-rw-r--r--config/haproxy-devel/haproxy_listeners.php7
-rw-r--r--config/haproxy-devel/haproxy_listeners_edit.php29
-rw-r--r--config/haproxy-devel/haproxy_pool_edit.php21
-rw-r--r--config/haproxy-devel/haproxy_utils.inc244
-rw-r--r--config/haproxy-devel/isCertSigner.inc361
8 files changed, 676 insertions, 94 deletions
diff --git a/config/haproxy-devel/haproxy.inc b/config/haproxy-devel/haproxy.inc
index 912f1fb3..4da961de 100644
--- a/config/haproxy-devel/haproxy.inc
+++ b/config/haproxy-devel/haproxy.inc
@@ -31,6 +31,7 @@
require_once("functions.inc");
require_once("pkg-utils.inc");
require_once("notices.inc");
+require_once("haproxy_utils.inc");
require_once("haproxy_xmlrpcsyncclient.inc");
$d_haproxyconfdirty_path = $g['varrun_path'] . "/haproxy.conf.dirty";
@@ -107,7 +108,7 @@ $a_closetypes['forceclose'] = array('name' => 'forceclose', 'syntax' => 'forcecl
function haproxy_custom_php_deinstall_command() {
exec("cd /var/db/pkg && pkg_delete `ls | grep haproxy`");
- exec("rm /usr/local/pkg/haproxy.inc");
+ exec("rm /usr/local/pkg/haproxy*");
exec("rm /usr/local/www/haproxy*");
exec("rm /usr/local/etc/rc.d/haproxy.sh");
exec("rm /etc/devd/haproxy.conf");
@@ -592,6 +593,13 @@ function haproxy_writeconf($configfile) {
$ssl_crt=" crt /var/etc/{$backend['name']}.{$backend['port']}.crt";
$cert = lookup_cert($backend['ssloffloadcert']);
$certcontent = base64_decode($cert['crt'])."\r\n".base64_decode($cert['prv']);
+
+ $certchaincontent = ca_chain($cert);
+ if ($certchaincontent != "") {
+ $certcontent .= "\r\n" . $certchaincontent;
+ }
+ unset($certchaincontent);
+
file_put_contents("/var/etc/{$backend['name']}.{$backend['port']}.crt", $certcontent);
unset($certcontent);
}else{
@@ -657,14 +665,12 @@ function haproxy_writeconf($configfile) {
$listenip = "";
// Process and add bind directives for ports
- foreach($ports as $port) {
- if($port) {
- if($bind['extaddr'] == "any")
- $listenip .= "\tbind\t\t\t0.0.0.0:{$port} {$ssl_info} {$advanced_bind}\n";
- elseif($bind['extaddr'])
- $listenip .= "\tbind\t\t\t{$bind['extaddr']}:{$port} {$ssl_info} {$advanced_bind}\n";
- else
- $listenip .= "\tbind\t\t\t" . get_current_wan_address('wan') . ":{$port} {$ssl_info} {$advanced_bind}\n";
+ $ip = haproxy_interface_ip($bind['extaddr']);
+ if ($ip){
+ foreach($ports as $port) {
+ if($port) {
+ $listenip .= "\tbind\t\t\t$ip:{$port} {$ssl_info} {$advanced_bind}\n";
+ }
}
}
@@ -1039,16 +1045,11 @@ function get_primaryfrontend($frontend) {
return $mainfrontend;
}
-function get_frontend_ipport($frontend) {
+function get_frontend_ipport($frontend,$userfriendly=false) {
$mainfrontend = get_primaryfrontend($frontend);
- if($mainfrontend['extaddr'] == "any")
- $result = "0.0.0.0";
- elseif ($mainfrontend['extaddr'] == "localhost")
- $result = "127.0.0.1";
- elseif($mainfrontend['extaddr'])
- $result = $mainfrontend['extaddr'];
- else
- $result = get_current_wan_address('wan');
+ $result = haproxy_interface_ip($mainfrontend['extaddr'],$userfriendly);
+ if ($userfriendly and is_ipaddrv6($result))
+ $result = "[{$result}]";
return $result . ":" . $mainfrontend['port'];
}
@@ -1099,7 +1100,7 @@ function get_haproxy_frontends($excludeitem="") {
return $result;
}
-function get_frontent_acls($frontend) {
+function get_frontend_acls($frontend) {
$result = array();
$a_acl = &$frontend['ha_acls']['item'];
if (is_array($a_acl))
@@ -1123,51 +1124,10 @@ function get_frontent_acls($frontend) {
return $result;
}
-function phparray_to_javascriptarray_recursive($nestID, $path, $items, $nodeName, $includeitems) {
- $offset = str_repeat(' ',$nestID);
- $itemName = "item$nestID";
- echo "{$offset}$nodeName = {};\n";
- if (is_array($items))
- foreach ($items as $key => $item)
- {
- if (in_array($path.'/'.$key, $includeitems))
- $subpath = $path.'/'.$key;
- else
- $subpath = $path.'/*';
- if (in_array($subpath, $includeitems) || in_array($path.'/*', $includeitems)) {
- if (is_array($item)) {
- $subNodeName = "item$nestID";
- phparray_to_javascriptarray_recursive($nestID+1, $subpath, $items[$key], $subNodeName, $includeitems);
- echo "{$offset}{$nodeName}['{$key}'] = $itemName;\n";
- } else
- echo "{$offset}{$nodeName}['$key'] = '$item';\n";
- }
- }
-}
-
-function phparray_to_javascriptarray($items, $javaMapName, $includeitems) {
- phparray_to_javascriptarray_recursive(1,'',$items, $javaMapName, $includeitems);
-}
-
function haproxy_escapestring($configurationsting) {
$result = str_replace('\\', '\\\\', $configurationsting);
$result = str_replace(' ', '\\ ', $result);
return str_replace('#', '\\#', $result);
}
-function echo_html_select($name, $keyvaluelist, $selected, $listEmptyMessage="", $onchangeEvent="") {
- if (count($keyvaluelist)>0){
- if ($onchangeEvent != "")
- $onchangeEvent .= " onchange=$onchangeEvent";
- echo "<select name=\"$name\" id=\"$name\" class=\"formselect\"$onchangeEvent>";
- foreach($keyvaluelist as $key => $desc){
- $selectedhtml = $key == $selected ? "selected" : "";
- echo "<option value=\"{$key}\" {$selectedhtml}>{$desc['name']}</option>";
- }
- echo "</select>";
- } else {
- echo $listEmptyMessage;
- }
-}
-
?>
diff --git a/config/haproxy-devel/haproxy.xml b/config/haproxy-devel/haproxy.xml
index bfd7f437..f7bac704 100644
--- a/config/haproxy-devel/haproxy.xml
+++ b/config/haproxy-devel/haproxy.xml
@@ -100,6 +100,16 @@
<item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_xmlrpcsyncclient.inc</item>
</additional_files_needed>
<additional_files_needed>
+ <prefix>/usr/local/pkg/</prefix>
+ <chmod>077</chmod>
+ <item>http://www.pfsense.com/packages/config/haproxy-devel/isCertSigner.inc</item>
+ </additional_files_needed>
+ <additional_files_needed>
+ <prefix>/usr/local/pkg/</prefix>
+ <chmod>077</chmod>
+ <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_utils.inc</item>
+ </additional_files_needed>
+ <additional_files_needed>
<prefix>/usr/local/www/widgets/widgets/</prefix>
<chmod>077</chmod>
<item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy.widget.php</item>
diff --git a/config/haproxy-devel/haproxy_global.php b/config/haproxy-devel/haproxy_global.php
index ff8d1280..0ff0e10e 100755
--- a/config/haproxy-devel/haproxy_global.php
+++ b/config/haproxy-devel/haproxy_global.php
@@ -31,6 +31,7 @@
require_once("guiconfig.inc");
require_once("haproxy.inc");
+require_once("haproxy_utils.inc");
require_once("globals.inc");
if (!is_array($config['installedpackages']['haproxy']))
@@ -41,6 +42,9 @@ if ($_POST) {
unset($input_errors);
$pconfig = $_POST;
+ if ($_POST['calculate_certificate_chain']) {
+ haproxy_recalculate_certifcate_chain();
+ } else
if ($_POST['apply']) {
$result = haproxy_check_and_run($savemsg, true);
if ($result)
@@ -154,6 +158,20 @@ function enable_change(enable_change) {
<div id="mainarea">
<table class="tabcont" width="100%" border="0" cellpadding="6" cellspacing="0">
<tr>
+ <td colspan="2" valign="top" class="listtopic">Recalculate certificate chain.</td>
+ </tr>
+ <tr>
+ <td width="22%" valign="top" class="vncell">&nbsp;</td>
+ <td width="78%" class="vtable">
+ <input type="hidden" name="calculate_certificate_chain" id="calculate_certificate_chain">
+ <input type="button" class="formbtn" value="Recalculate certificate chains" onclick="$('calculate_certificate_chain').value='true';document.iform.submit();">
+ <br/>
+ This can be required after certificates have been created or imported. As pfSense 2.1.0 currently does not
+ always keep track of these dependencies which might be required to create a proper certificate chain when using SSLoffloading.
+ </td>
+ </tr>
+
+ <tr>
<td colspan="2" valign="top" class="listtopic">General settings</td>
</tr>
<tr>
diff --git a/config/haproxy-devel/haproxy_listeners.php b/config/haproxy-devel/haproxy_listeners.php
index 6d9c9dc1..397cef5c 100644
--- a/config/haproxy-devel/haproxy_listeners.php
+++ b/config/haproxy-devel/haproxy_listeners.php
@@ -32,6 +32,7 @@
require_once("guiconfig.inc");
require_once("haproxy.inc");
require_once("certs.inc");
+require_once("haproxy_utils.inc");
if (!is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) {
$config['installedpackages']['haproxy']['ha_backends']['item'] = array();
@@ -125,7 +126,7 @@ include("head.inc");
$a_frontend_grouped = array();
foreach($a_frontend as &$frontend2) {
- $ipport = get_frontend_ipport($frontend2);
+ $ipport = get_frontend_ipport($frontend2, true);
$frontend2['ipport'] = $ipport;
$a_frontend_grouped[$ipport][] = $frontend2;
}
@@ -156,7 +157,7 @@ include("head.inc");
echo '<img src="'.$img_cert.'" title="SSL offloading cert: '.$cert['descr'].'" alt="SSL offloading" border="0" height="16" width="16" />';
}
- $acls = get_frontent_acls($frontend);
+ $acls = get_frontend_acls($frontend);
$isaclset = "";
foreach ($acls as $acl) {
$isaclset .= "&#10;" . $acl['descr'];
@@ -179,7 +180,7 @@ include("head.inc");
<?=$frontend['desc'];?>
</td>
<td class="listlr" ondblclick="document.location='haproxy_listeners_edit.php?id=<?=$frontendname;?>';">
- <?=$frontend['ipport'];?>
+ <?=str_replace(" ","&nbsp;",$frontend['ipport']);?>
</td>
<td class="listlr" ondblclick="document.location='haproxy_listeners_edit.php?id=<?=$frontendname;?>';">
<?=$frontend['type']?>
diff --git a/config/haproxy-devel/haproxy_listeners_edit.php b/config/haproxy-devel/haproxy_listeners_edit.php
index d37444c0..98fba74a 100644
--- a/config/haproxy-devel/haproxy_listeners_edit.php
+++ b/config/haproxy-devel/haproxy_listeners_edit.php
@@ -32,6 +32,7 @@
require("guiconfig.inc");
require_once("haproxy.inc");
+require_once("haproxy_utils.inc");
/* Compatibility function for pfSense 2.0 */
if (!function_exists("cert_get_purpose")) {
@@ -274,6 +275,9 @@ if (!$id)
$pgtitle = "HAProxy: Frontend: Edit";
include("head.inc");
+
+$primaryfrontends = get_haproxy_frontends($pconfig['name']);
+$interfaces = haproxy_get_bindable_interfaces();
?>
<body link="#0000CC" vlink="#0000CC" alink="#0000CC">
@@ -526,11 +530,16 @@ include("head.inc");
<option value="disabled"<?php if($pconfig['status'] == "disabled") echo " SELECTED"; ?>>Disabled</option>
</select>
</td>
- </tr>
+ </tr>
<tr align="left">
<td width="22%" valign="top" class="vncell">Shared Frontend</td>
<td width="78%" class="vtable" colspan="2">
+ <?if (count($primaryfrontends)==0){ ?>
+ <b>At least 1 primary frontend is needed.</b><br/><br/>
+ <? } else{ ?>
<input id="secondary" name="secondary" type="checkbox" value="yes" <?php if ($pconfig['secondary']=='yes') echo "checked"; ?> onclick="updatevisibility();"/>
+ <? } ?>
+ This can be used to host a second or more website on the same IP:Port combination.<br/>
Use this setting to configure multiple backends/accesslists for a single frontend.<br/>
All settings of which only 1 can exist will be hidden.<br/>
The frontend settings will be merged into 1 set of frontend configuration.
@@ -540,7 +549,6 @@ include("head.inc");
<td width="22%" valign="top" class="vncellreq">Primary frontend</td>
<td width="78%" class="vtable" colspan="2">
<?
- $primaryfrontends = get_haproxy_frontends($pconfig['name']);
echo_html_select('primary_frontend',$primaryfrontends, $pconfig['primary_frontend'],"You must first create a 'primary' frontend.","updatevisibility();");
?>
</td>
@@ -548,22 +556,9 @@ include("head.inc");
<tr class="haproxy_primary">
<td width="22%" valign="top" class="vncellreq">External address</td>
<td width="78%" class="vtable">
- <select name="extaddr" class="formfld">
- <option value="" <?php if (!$pconfig['extaddr']) echo "selected"; ?>>Interface address</option>
- <option value="localhost" <?php if ('localhost' == $pconfig['extaddr']) echo "selected"; ?>>Localhost</option>
- <?php
- if (is_array($config['virtualip']['vip'])):
- foreach ($config['virtualip']['vip'] as $sn):
- ?>
- <option value="<?=$sn['subnet'];?>" <?php if ($sn['subnet'] == $pconfig['extaddr']) echo "selected"; ?>>
- <?=htmlspecialchars("{$sn['subnet']} ({$sn['descr']})");?>
- </option>
- <?php
- endforeach;
- endif;
+ <?
+ echo_html_select('extaddr', $interfaces, $pconfig['extaddr']);
?>
- <option value="any" <?php if($pconfig['extaddr'] == "any") echo "selected"; ?>>any</option>
- </select>
<br />
<span class="vexpl">
If you want this rule to apply to another IP address than the IP address of the interface chosen above,
diff --git a/config/haproxy-devel/haproxy_pool_edit.php b/config/haproxy-devel/haproxy_pool_edit.php
index a7a56b1c..92f4177e 100644
--- a/config/haproxy-devel/haproxy_pool_edit.php
+++ b/config/haproxy-devel/haproxy_pool_edit.php
@@ -31,6 +31,7 @@
require("guiconfig.inc");
require_once("haproxy.inc");
+require_once("haproxy_utils.inc");
$d_haproxyconfdirty_path = $g['varrun_path'] . "/haproxy.conf.dirty";
@@ -137,7 +138,7 @@ if ($_POST) {
if (preg_match("/[^a-zA-Z0-9\.\-_]/", $server_name))
$input_errors[] = "The field 'Name' contains invalid characters.";
- if (preg_match("/[^a-zA-Z0-9\.\-_]/", $server_address))
+ if (!is_ipaddr($server_address))
$input_errors[] = "The field 'Address' contains invalid characters.";
if (!preg_match("/.{2,}/", $server_name))
@@ -350,7 +351,7 @@ foreach($simplefields as $field){
<td width="5%" class="listhdrr">Port</td>
<td width="5%" class="listhdrr">SSL</td>
<td width="8%" class="listhdrr">Weight</td>
- <td width="5%" class="listhdr">Backup</td>
+ <td width="5%" class="listhdrr">Mode</td>
<td width="15%" class="listhdr">Advanced</td>
<td width="4%" class=""></td>
</tr>
@@ -534,10 +535,7 @@ foreach($simplefields as $field){
NOTE: paste text into this box that you would like to pass thru. Applied to the backend section.
</td>
</tr>
-
- </table>
- <br/>
- <table width="100%" border="0" cellpadding="6" cellspacing="0">
+ <tr><td>&nbsp;</td></tr>
<tr>
<td colspan="2" valign="top" class="listtopic">Health checking</td>
</tr>
@@ -611,9 +609,7 @@ FLUSH PRIVILEGES;</pre>
Fill in the TCP portnumber the healthcheck should be performed on.
</td>
</tr>
- </table>
- <br/>
- <table width="100%" border="0" cellpadding="6" cellspacing="0">
+ <tr><td>&nbsp;</td></tr>
<tr>
<td colspan="2" valign="top" class="listtopic">Advanced settings</td>
</tr>
@@ -641,9 +637,7 @@ want the clients to see the failures. The number of attempts to reconnect is
set by the 'retries' parameter.</div>
</td>
</tr>
- </table>
- <br/>&nbsp;<br/>
- <table width="100%" border="0" cellpadding="6" cellspacing="0">
+ <tr><td>&nbsp;</td></tr>
<tr>
<td colspan="2" valign="top" class="listtopic">Statistics</td>
</tr>
@@ -719,8 +713,7 @@ set by the 'retries' parameter.</div>
Specify the refresh rate of the stats page in seconds, or specified time unit (us, ms, s, m, h, d).
</td>
</tr>
- </table>
- <table width="100%" border="0" cellpadding="6" cellspacing="0">
+ <tr><td>&nbsp;</td></tr>
<tr align="left">
<td width="22%" valign="top">&nbsp;</td>
<td width="78%">
diff --git a/config/haproxy-devel/haproxy_utils.inc b/config/haproxy-devel/haproxy_utils.inc
new file mode 100644
index 00000000..e826f530
--- /dev/null
+++ b/config/haproxy-devel/haproxy_utils.inc
@@ -0,0 +1,244 @@
+<?php
+/*
+ haproxy_utils.php
+ part of pfSense (http://www.pfsense.com/)
+ Copyright (C) 2013 PiBa-NL
+ 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, 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.
+*/
+/*
+ This file contains functions which are NOT specific to HAProxy and may/could/should
+ be moved to the general pfSense php library for possible easy use by other parts of pfSense
+*/
+
+function haproxy_interface_ip($interfacebindname,$userfriendly=false){
+ $list = haproxy_get_bindable_interfaces();
+ $item = $list[$interfacebindname];
+ $result = $item['ip'];
+ if ($userfriendly && !$result)
+ $result = $item['name'];
+ return $result;
+}
+function haproxy_get_bindable_interfaces($ipv="ipv4,ipv6", $interfacetype="any,localhost,real,carp,ipalias"){
+ // returns a list of ALL interface/IPs that can be used to bind a service to.
+ // filtered by the conditions given in the two filter parameters.
+ // result array includes:
+ // $bindable[key] can be stored and compared with previous setings
+ // $bindable[key]['ip'] the current IP (possibly changes for dhcp enabled interfaces..)
+ // $bindable[key]['description'] can be shown to user in a selection box
+
+ global $config;
+ $ipverions = split(',',$ipv);
+ $interfacetypes= split(',',$interfacetype);
+
+ $bindable = array();
+ if (in_array("ipv4",$ipverions)){
+ if (in_array('any',$interfacetypes)){
+ $item = array();
+ $item[ip] = '0.0.0.0';
+ $item[name] = 'any (IPv4)';
+ $bindable['any_ipv4'] = $item;
+ }
+ if (in_array('localhost',$interfacetypes)){
+ $item = array();
+ $item[ip] = '127.0.0.1';
+ $item[name] = 'localhost (IPv4)';
+ $bindable['localhost_ipv4'] = $item;
+ }
+ if (in_array('real',$interfacetypes)){
+ foreach($config['interfaces'] as $if => $ifdetail) {
+ if (!isset($ifdetail['enable']))
+ continue;
+ if (!isset($ifdetail['ipaddr']))
+ continue;
+ $item = array();
+ $item[ip] = get_interface_ip($if);
+ $item[name] = $ifdetail['descr'].' address (IPv4)';
+ $bindable[$if.'_ipv4'] = $item;
+ }
+ }
+ if (in_array('carp',$interfacetypes)){
+ $carplist = get_configured_carp_interface_list();
+ foreach ($carplist as $carpif => $carpip){
+ if (is_ipaddrv4($carpip)){
+ $item = array();
+ $item['ip'] = $carpip;
+ $item['name'] = $carpip." (".get_vip_descr($carpip).")";
+ $bindable[$carpip] = $item;
+ }
+ }
+
+ }
+ if (in_array('ipalias',$interfacetypes)){
+ $aliaslist = get_configured_ip_aliases_list();
+ foreach ($aliaslist as $aliasip => $aliasif){
+ if (is_ipaddrv4($aliasip)){
+ $item = array();
+ $item['ip'] = $aliasip;
+ $item['name'] = $aliasip." (".get_vip_descr($aliasip).")";
+ $bindable[$aliasip.'_ipv4'] = $item;
+ }
+ }
+ }
+ }
+ if (in_array("ipv6",$ipverions)){
+ if (in_array('any',$interfacetypes)){
+ $item = array();
+ $item[ip] = '::';
+ $item[name] = 'any (IPv6)';
+ $bindable['any_ipv6'] = $item;
+ }
+ if (in_array('localhost',$interfacetypes)){
+ $item = array();
+ $item[ip] = '::1';
+ $item[name] = 'localhost (IPv6)';
+ $bindable['localhost_ipv6'] = $item;
+ }
+ if (in_array('real',$interfacetypes)){
+ foreach($config['interfaces'] as $if => $ifdetail) {
+ if (!isset($ifdetail['enable']))
+ continue;
+ if (!isset($ifdetail['ipaddrv6']))
+ continue;
+ $item = array();
+ $item[ip] = get_interface_ipv6($if);
+ $item[name] = $ifdetail['descr'].' address (IPv6)';
+ $bindable[$if.'_ipv6'] = $item;
+ }
+ }
+ if (in_array('carp',$interfacetypes)){
+ $carplist = get_configured_carp_interface_list();
+ foreach ($carplist as $carpif => $carpip){
+ if (is_ipaddrv6($carpip)){
+ $item = array();
+ $item['ip'] = $carpip;
+ $item['name'] = $carpip." (".get_vip_descr($carpip).")";
+ $bindable[$carpip] = $item;
+ }
+ }
+
+ }
+ if (in_array('ipalias',$interfacetypes)){
+ $aliaslist = get_configured_ip_aliases_list();
+ foreach ($aliaslist as $aliasip => $aliasif){
+ if (is_ipaddrv6($aliasip)){
+ $item = array();
+ $item['ip'] = $aliasip;
+ $item['name'] = $aliasip." (".get_vip_descr($aliasip).")";
+ $bindable[$aliasip] = $item;
+ }
+ }
+ }
+ }
+ return $bindable;
+}
+
+function haproxy_cert_signed_by($cert, $signedbycert) {
+ // uses function isCertSigner(a,b) from isCertSigner.inc to check if $cert was signed by $signedbycert
+ // returns true if it is
+ return isCertSigner(base64_decode($cert['crt']), base64_decode($signedbycert['crt']));
+}
+function haproxy_get_certificates(){
+ global $config;
+ $allcerts = array();
+ foreach($config['cert'] as &$cert)
+ $allcerts[] = &$cert;
+ foreach($config['ca'] as &$cert)
+ $allcerts[] = &$cert;
+ return $allcerts;
+}
+function haproxy_recalculate_certifcate_chain(){
+ // and set "selfsigned" for certificates that where used to sign themselves
+ // recalculate the "caref" for all certificates where it is currently unkown.
+
+ $allcertificates = haproxy_get_certificates();
+ $items_recalculated = 0;
+ foreach($allcertificates as &$cert){
+ $recalculate=false;
+ if (!isset($cert['selfsigned'])){
+ if (!isset($cert['caref']))
+ $recalculate=true;
+ else {
+ $ca = lookup_ca($cert['caref']);
+ if (!$ca)
+ $recalculate=true;
+ }
+ }
+ if ($recalculate){
+ foreach($allcertificates as &$signedbycert){
+ if(haproxy_cert_signed_by($cert, $signedbycert)){
+ if ($cert['refid'] == $signedbycert['refid']){
+ $cert['selfsigned'] = true;
+ } else {
+ $cert['caref'] = $signedbycert['refid'];
+ }
+ $items_recalculated++;
+ }
+ }
+ }
+ }
+ if ($items_recalculated > 0)
+ write_config("Recalculated $items_recalculated certificate chains.");
+}
+
+function phparray_to_javascriptarray_recursive($nestID, $path, $items, $nodeName, $includeitems) {
+ $offset = str_repeat(' ',$nestID);
+ $itemName = "item$nestID";
+ echo "{$offset}$nodeName = {};\n";
+ if (is_array($items))
+ foreach ($items as $key => $item)
+ {
+ if (in_array($path.'/'.$key, $includeitems))
+ $subpath = $path.'/'.$key;
+ else
+ $subpath = $path.'/*';
+ if (in_array($subpath, $includeitems) || in_array($path.'/*', $includeitems)) {
+ if (is_array($item)) {
+ $subNodeName = "item$nestID";
+ phparray_to_javascriptarray_recursive($nestID+1, $subpath, $items[$key], $subNodeName, $includeitems);
+ echo "{$offset}{$nodeName}['{$key}'] = $itemName;\n";
+ } else
+ echo "{$offset}{$nodeName}['$key'] = '$item';\n";
+ }
+ }
+}
+function phparray_to_javascriptarray($items, $javaMapName, $includeitems) {
+ phparray_to_javascriptarray_recursive(1,'',$items, $javaMapName, $includeitems);
+}
+
+function echo_html_select($name, $keyvaluelist, $selected, $listEmptyMessage="", $onchangeEvent="") {
+ if (count($keyvaluelist)>0){
+ if ($onchangeEvent != "")
+ $onchangeEvent .= " onchange=$onchangeEvent";
+ echo "<select name=\"$name\" id=\"$name\" class=\"formselect\"$onchangeEvent>";
+ foreach($keyvaluelist as $key => $desc){
+ $selectedhtml = $key == $selected ? "selected" : "";
+ echo "<option value=\"{$key}\" {$selectedhtml}>{$desc['name']}</option>";
+ }
+ echo "</select>";
+ } else {
+ echo $listEmptyMessage;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/config/haproxy-devel/isCertSigner.inc b/config/haproxy-devel/isCertSigner.inc
new file mode 100644
index 00000000..d964129f
--- /dev/null
+++ b/config/haproxy-devel/isCertSigner.inc
@@ -0,0 +1,361 @@
+<?php
+/**
+ * Is one pem encoded certificate the signer of another?
+ *
+ * The PHP openssl functionality is severely limited by the lack of a stable
+ * api and documentation that might as well have been encrypted itself.
+ * In particular the documention on openssl_verify() never explains where
+ * to get the actual signature to verify. The isCertSigner() function below
+ * will accept two PEM encoded certs as arguments and will return true if
+ * one certificate was used to sign the other. It only relies on the
+ * openssl_pkey_get_public() and openssl_public_decrypt() openssl functions,
+ * which should stay fairly stable. The ASN parsing code snippets were mostly
+ * borrowed from the horde project's smime.php.
+ *
+ * @author Mike Green <mikey at badpenguins dot com>
+ * @copyright Copyright (c) 2010, Mike Green
+ * @license http://opensource.org/licenses/gpl-2.0.php GPLv2
+ */
+
+/**
+ * If viewSource is in the request string, show the source, luke.
+ */
+if (isset($_REQUEST['viewSource'])) {
+ die(highlight_file(__FILE__));
+ }
+
+/**
+ * Extract signature from der encoded cert.
+ * Expects x509 der encoded certificate consisting of a section container
+ * containing 2 sections and a bitstream. The bitstream contains the
+ * original encrypted signature, encrypted by the public key of the issuing
+ * signer.
+ * @param string $der
+ * @return string on success
+ * @return bool false on failures
+ */
+function extractSignature($der=false) {
+ if (strlen($der) < 5) { return false; }
+ // skip container sequence
+ $der = substr($der,4);
+ // now burn through two sequences and the return the final bitstream
+ while(strlen($der) > 1) {
+ $class = ord($der[0]);
+ $classHex = dechex($class);
+ switch($class) {
+ // BITSTREAM
+ case 0x03:
+ $len = ord($der[1]);
+ $bytes = 0;
+ if ($len & 0x80) {
+ $bytes = $len & 0x0f;
+ $len = 0;
+ for ($i = 0; $i < $bytes; $i++) {
+ $len = ($len << 8) | ord($der[$i + 2]);
+ }
+ }
+ return substr($der,3 + $bytes, $len);
+ break;
+ // SEQUENCE
+ case 0x30:
+ $len = ord($der[1]);
+ $bytes = 0;
+ if ($len & 0x80) {
+ $bytes = $len & 0x0f;
+ $len = 0;
+ for($i = 0; $i < $bytes; $i++) {
+ $len = ($len << 8) | ord($der[$i + 2]);
+ }
+ }
+ $contents = substr($der, 2 + $bytes, $len);
+ $der = substr($der,2 + $bytes + $len);
+ break;
+ default:
+ return false;
+ break;
+ }
+ }
+ return false;
+ }
+
+/**
+ * Get signature algorithm oid from der encoded signature data.
+ * Expects decrypted signature data from a certificate in der format.
+ * This ASN1 data should contain the following structure:
+ * SEQUENCE
+ * SEQUENCE
+ * OID (signature algorithm)
+ * NULL
+ * OCTET STRING (signature hash)
+ * @return bool false on failures
+ * @return string oid
+ */
+function getSignatureAlgorithmOid($der=null) {
+ // Validate this is the der we need...
+ if (!is_string($der) or strlen($der) < 5) { return false; }
+ $bit_seq1 = 0;
+ $bit_seq2 = 2;
+ $bit_oid = 4;
+ if (ord($der[$bit_seq1]) !== 0x30) {
+ die('Invalid DER passed to getSignatureAlgorithmOid()');
+ }
+ if (ord($der[$bit_seq2]) !== 0x30) {
+ die('Invalid DER passed to getSignatureAlgorithmOid()');
+ }
+ if (ord($der[$bit_oid]) !== 0x06) {
+ die('Invalid DER passed to getSignatureAlgorithmOid');
+ }
+ // strip out what we don't need and get the oid
+ $der = substr($der,$bit_oid);
+ // Get the oid
+ $len = ord($der[1]);
+ $bytes = 0;
+ if ($len & 0x80) {
+ $bytes = $len & 0x0f;
+ $len = 0;
+ for ($i = 0; $i < $bytes; $i++) {
+ $len = ($len << 8) | ord($der[$i + 2]);
+ }
+ }
+ $oid_data = substr($der, 2 + $bytes, $len);
+ // Unpack the OID
+ $oid = floor(ord($oid_data[0]) / 40);
+ $oid .= '.' . ord($oid_data[0]) % 40;
+ $value = 0;
+ $i = 1;
+ while ($i < strlen($oid_data)) {
+ $value = $value << 7;
+ $value = $value | (ord($oid_data[$i]) & 0x7f);
+ if (!(ord($oid_data[$i]) & 0x80)) {
+ $oid .= '.' . $value;
+ $value = 0;
+ }
+ $i++;
+ }
+ return $oid;
+ }
+
+/**
+ * Get signature hash from der encoded signature data.
+ * Expects decrypted signature data from a certificate in der format.
+ * This ASN1 data should contain the following structure:
+ * SEQUENCE
+ * SEQUENCE
+ * OID (signature algorithm)
+ * NULL
+ * OCTET STRING (signature hash)
+ * @return bool false on failures
+ * @return string hash
+ */
+function getSignatureHash($der=null) {
+ // Validate this is the der we need...
+ if (!is_string($der) or strlen($der) < 5) { return false; }
+ if (ord($der[0]) !== 0x30) {
+ die('Invalid DER passed to getSignatureHash()');
+ }
+ // strip out the container sequence
+ $der = substr($der,2);
+ if (ord($der[0]) !== 0x30) {
+ die('Invalid DER passed to getSignatureHash()');
+ }
+ // Get the length of the first sequence so we can strip it out.
+ $len = ord($der[1]);
+ $bytes = 0;
+ if ($len & 0x80) {
+ $bytes = $len & 0x0f;
+ $len = 0;
+ for ($i = 0; $i < $bytes; $i++) {
+ $len = ($len << 8) | ord($der[$i + 2]);
+ }
+ }
+ $der = substr($der, 2 + $bytes + $len);
+ // Now we should have an octet string
+ if (ord($der[0]) !== 0x04) {
+ die('Invalid DER passed to getSignatureHash()');
+ }
+ $len = ord($der[1]);
+ $bytes = 0;
+ if ($len & 0x80) {
+ $bytes = $len & 0x0f;
+ $len = 0;
+ for ($i = 0; $i < $bytes; $i++) {
+ $len = ($len << 8) | ord($der[$i + 2]);
+ }
+ }
+ return bin2hex(substr($der, 2 + $bytes, $len));
+ }
+
+/**
+ * Determine if one cert was used to sign another
+ * Note that more than one CA cert can give a positive result, some certs
+ * re-issue signing certs after having only changed the expiration dates.
+ * @param string $cert - PEM encoded cert
+ * @param string $caCert - PEM encoded cert that possibly signed $cert
+ * @return bool
+ */
+function isCertSigner($certPem=null,$caCertPem=null) {
+ if (!function_exists('openssl_pkey_get_public')) {
+ die('Need the openssl_pkey_get_public() function.');
+ }
+ if (!function_exists('openssl_public_decrypt')) {
+ die('Need the openssl_public_decrypt() function.');
+ }
+ if (!function_exists('hash')) {
+ die('Need the php hash() function.');
+ }
+ if (empty($certPem) or empty($caCertPem)) { return false; }
+ // Convert the cert to der for feeding to extractSignature.
+ $certDer = pemToDer($certPem);
+ if (!is_string($certDer)) { die('invalid certPem'); }
+ // Grab the encrypted signature from the der encoded cert.
+ $encryptedSig = extractSignature($certDer);
+ if (!is_string($encryptedSig)) {
+ die('Failed to extract encrypted signature from certPem.');
+ }
+ // Extract the public key from the ca cert, which is what has
+ // been used to encrypt the signature in the cert.
+ $pubKey = openssl_pkey_get_public($caCertPem);
+ if ($pubKey === false) {
+ die('Failed to extract the public key from the ca cert.');
+ }
+ // Attempt to decrypt the encrypted signature using the CA's public
+ // key, returning the decrypted signature in $decryptedSig. If
+ // it can't be decrypted, this ca was not used to sign it for sure...
+ $rc = openssl_public_decrypt($encryptedSig,$decryptedSig,$pubKey);
+ if ($rc === false) { return false; }
+ // We now have the decrypted signature, which is der encoded
+ // asn1 data containing the signature algorithm and signature hash.
+ // Now we need what was originally hashed by the issuer, which is
+ // the original DER encoded certificate without the issuer and
+ // signature information.
+ $origCert = stripSignerAsn($certDer);
+ if ($origCert === false) {
+ die('Failed to extract unsigned cert.');
+ }
+ // Get the oid of the signature hash algorithm, which is required
+ // to generate our own hash of the original cert. This hash is
+ // what will be compared to the issuers hash.
+ $oid = getSignatureAlgorithmOid($decryptedSig);
+ if ($oid === false) {
+ die('Failed to determine the signature algorithm.');
+ }
+ switch($oid) {
+ case '1.2.840.113549.2.2': $algo = 'md2'; break;
+ case '1.2.840.113549.2.4': $algo = 'md4'; break;
+ case '1.2.840.113549.2.5': $algo = 'md5'; break;
+ case '1.3.14.3.2.18': $algo = 'sha'; break;
+ case '1.3.14.3.2.26': $algo = 'sha1'; break;
+ case '2.16.840.1.101.3.4.2.1': $algo = 'sha256'; break;
+ case '2.16.840.1.101.3.4.2.2': $algo = 'sha384'; break;
+ case '2.16.840.1.101.3.4.2.3': $algo = 'sha512'; break;
+ default:
+ die('Unknown signature hash algorithm oid: ' . $oid);
+ break;
+ }
+ // Get the issuer generated hash from the decrypted signature.
+ $decryptedHash = getSignatureHash($decryptedSig);
+ // Ok, hash the original unsigned cert with the same algorithm
+ // and if it matches $decryptedHash we have a winner.
+ $certHash = hash($algo,$origCert);
+ return ($decryptedHash === $certHash);
+ }
+
+/**
+ * Convert pem encoded certificate to DER encoding
+ * @return string $derEncoded on success
+ * @return bool false on failures
+ */
+function pemToDer($pem=null) {
+ if (!is_string($pem)) { return false; }
+ $cert_split = preg_split('/(-----((BEGIN)|(END)) CERTIFICATE-----)/',$pem);
+ if (!isset($cert_split[1])) { return false; }
+ return base64_decode($cert_split[1]);
+ }
+
+/**
+ * Obtain der cert with issuer and signature sections stripped.
+ * @param string $der - der encoded certificate
+ * @return string $der on success
+ * @return bool false on failures.
+ */
+function stripSignerAsn($der=null) {
+ if (!is_string($der) or strlen($der) < 8) { return false; }
+ $bit = 4;
+ $len = ord($der[($bit + 1)]);
+ $bytes = 0;
+ if ($len & 0x80) {
+ $bytes = $len & 0x0f;
+ $len = 0;
+ for($i = 0; $i < $bytes; $i++) {
+ $len = ($len << 8) | ord($der[$bit + $i + 2]);
+ }
+ }
+ return substr($der,4,$len + 4);
+ }
+/*
+/ **
+ * HTML form starts here...
+ * /
+
+$answer = 'Enter PEM Encoded Certificates for the Issuer and Subject '
+ . 'and click Submit. Include the entire certificates, including '
+ . 'the BEGIN CERTIFICATE and END CERTIFICATE lines.';
+
+if (isset($_POST['subjectPem']) and isset($_POST['issuerPem'])) {
+ if (strlen($_POST['subjectPem']) > 0 and strlen($_POST['issuerPem']) > 0) {
+ $rc = isCertSigner($_POST['subjectPem'],$_POST['issuerPem']);
+ if ($rc === true) {
+ $answer = 'The issuer cert DID sign the subject cert.';
+ } else {
+ $answer = 'The issuer cert DID NOT sign the subject cert.';
+ }
+ }
+ }
+?>
+<HTML>
+<BODY BGCOLOR="white">
+<HEAD>
+<TITLE>Is This The Cert Signer?</TITLE>
+<STYLE TYPE="text/css">
+textarea {
+ font-size: 11px;
+ }
+</STYLE>
+</HEAD>
+<BODY>
+<DIV ALIGN="center">
+<FORM NAME="check_certs" METHOD="post" action="<?= $_SERVER['PHP_SELF']; ?>">
+<TABLE BORDER="1" WIDTH="500px">
+ <TR>
+ <TD><?= $answer; ?></TD>
+ </TR>
+ <TR>
+ <TH>Issuer Certificate (<A HREF="#" ONCLICK="javascript:document.check_certs.issuerPem.value='';">clear</A>)</TH>
+ </TR>
+ <TR>
+ <TD>
+ <TEXTAREA NAME="issuerPem" ROWS="20" COLS="70"><?= (isset($_POST['issuerPem'])) ? $_POST['issuerPem'] : ''; ?></TEXTAREA>
+ </TD>
+ </TR>
+ <TR>
+ <TH>Subject Certificate (<A HREF="#" ONCLICK="javascript:document.check_certs.subjectPem.value='';">clear</A>)</TH>
+ </TR>
+ <TR>
+ <TD>
+ <TEXTAREA NAME="subjectPem" ROWS="20" COLS="70"><?= (isset($_POST['subjectPem'])) ? $_POST['subjectPem'] : ''; ?></TEXTAREA>
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">
+ (<A HREF="<?= $_SERVER['PHP_SELF']; ?>?viewSource">view source</A>)
+ <INPUT TYPE="submit" NAME="submit" VALUE="submit">
+ </TD>
+ </TR>
+</TABLE>
+</FORM>
+</DIV>
+</BODY>
+</HTML>
+
+*/
+?> \ No newline at end of file