diff options
Diffstat (limited to 'config/haproxy-devel')
-rw-r--r-- | config/haproxy-devel/haproxy.inc | 565 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy.widget.php | 2 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy.xml | 39 | ||||
-rwxr-xr-x | config/haproxy-devel/haproxy_global.php | 115 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_htmllist.inc | 2 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_listeners.php | 36 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_listeners_edit.php | 66 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_pool_edit.php | 362 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_pools.php | 14 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_socketinfo.inc | 27 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_stats.php | 257 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_utils.inc | 2 | ||||
-rw-r--r-- | config/haproxy-devel/pkg_haproxy_tabs.inc | 25 |
13 files changed, 1159 insertions, 353 deletions
diff --git a/config/haproxy-devel/haproxy.inc b/config/haproxy-devel/haproxy.inc index 5e798dc2..1d85cc51 100644 --- a/config/haproxy-devel/haproxy.inc +++ b/config/haproxy-devel/haproxy.inc @@ -37,6 +37,7 @@ require_once("haproxy_xmlrpcsyncclient.inc"); $d_haproxyconfdirty_path = $g['varrun_path'] . "/haproxy.conf.dirty"; +global $a_acltypes; $a_acltypes = array(); $a_acltypes["host_starts_with"] = array('name' => 'Host starts with', 'mode' => 'http', 'syntax' => 'hdr_beg(host) -i %1$s'); @@ -66,6 +67,7 @@ $a_acltypes["backendservercount"] = array('name' => 'Minimum count usable server $a_acltypes["ssl_sni_matches"] = array('name' => 'Server Name Indication TLS extension matches', 'mode' => 'https', 'syntax' => 'req_ssl_sni -i %1$s', 'advancedoptions' => "tcp-request inspect-delay 5s\n\ttcp-request content accept if { req_ssl_hello_type 1 }"); +global $a_checktypes; $a_checktypes = array(); $a_checktypes['none'] = array('name' => 'none', 'syntax' => '', 'descr' => 'No health checks will be performed.'); @@ -92,6 +94,7 @@ $a_checktypes['ESMTP'] = array('name' => 'ESMTP', 'syntax' => 'smtpchk EHLO', $a_checktypes['SSL'] = array('name' => 'SSL', 'syntax' => 'ssl-hello-chk', 'descr' => 'Use SSLv3 client hello health checks for server testing.'); +global $a_httpcheck_method; $a_httpcheck_method = array(); $a_httpcheck_method['OPTIONS'] = array('name' => 'OPTIONS', 'syntax' => 'OPTIONS'); $a_httpcheck_method['HEAD'] = array('name' => 'HEAD', 'syntax' => 'HEAD'); @@ -101,6 +104,7 @@ $a_httpcheck_method['PUT'] = array('name' => 'PUT', 'syntax' => 'PUT'); $a_httpcheck_method['DELETE'] = array('name' => 'DELETE', 'syntax' => 'DELETE'); $a_httpcheck_method['TRACE'] = array('name' => 'TRACE', 'syntax' => 'TRACE'); +global $a_closetypes; $a_closetypes = array(); $a_closetypes['none'] = array('name' => 'none', 'syntax' => '', 'descr' => 'No close headers will be changed.'); @@ -113,32 +117,156 @@ $a_closetypes['forceclose'] = array('name' => 'forceclose', 'syntax' => 'forcecl $a_closetypes['http-keep-alive'] = array('name' => 'http-keep-alive', 'syntax' => 'http-keep-alive', 'descr' => 'By default, when a client communicates with a server, HAProxy will only analyze, log, and process the first request of each connection. Setting "option http-keep-alive" enables HTTP keep-alive mode on the client- and server- sides. This provides the lowest latency on the client side (slow network) and the fastest session reuse on the server side at the expense of maintaining idle connections to the servers. In general, it is possible with this option to achieve approximately twice the request rate that the "http-server-close" option achieves on small objects. There are mainly two situations where this option may be useful : - when the server is non-HTTP compliant and authenticates the connection instead of requests (eg: NTLM authentication) - when the cost of establishing the connection to the server is significant compared to the cost of retrieving the associated object from the server.'); +global $a_servermodes; $a_servermodes = array(); $a_servermodes["active"]['name'] = "active"; $a_servermodes["backup"]['name'] = "backup"; $a_servermodes["disabled"]['name'] = "disabled"; $a_servermodes["inactive"]['name'] = "inactive"; +// http://www.exceliance.fr/sites/default/files/biblio/aloha_load_balancer_haproxy_cookie_persistence_methods_memo.pdf +global $a_cookiemode; +$a_cookiemode = array(); +$a_cookiemode['passive'] = array('name' => 'Passive', 'syntax' => 'cookie <cookie name>', + 'descr' => 'Cookie is analysed on incoming request to choose server. HAProxy does not perform any insertion update or deletion on the Cookie or Set-Cookie. If the Cookie is not set, then the load-balancing algorithm is applied.'); +$a_cookiemode['passive-silent'] = array('name' => 'Passive-silent', 'syntax' => 'cookie <cookie name> indirect', + 'descr' => 'Cookie is analysed on incoming request to choose server. HAProxy does not perform any insertion, update or deletion on the Cookie. Set-Cookie is removed from response if not required. If the Cookie is not set, then HAProxy applies the load-balancing algorithm.'); +$a_cookiemode['reset'] = array('name' => 'Reset', 'syntax' => 'cookie <cookie name> rewrite', + 'descr' => 'Cookie is analysed on incoming request to choose server and Set-Cookie value is overwritten in response if present. If the Set-Cookie isn\'t sent by the server, then HAProxy won\'t set it.'); +$a_cookiemode['set'] = array('name' => 'Insert', 'syntax' => 'cookie <cookie name> insert', + 'descr' => 'Cookie is analyzed on incoming request to choose server and Set-Cookie value is overwritten if present and set to an unknown value or inserted in response if not present.'); +$a_cookiemode['set-silent'] = array('name' => 'Insert-silent', 'syntax' => 'cookie <cookie name> insert indirect', + 'descr' => 'Cookie is analyzed on incoming request to choose server and Set-Cookie value is overwritten if present, inserted in response if needed and removed if a valid Cookie was provided.'); +$a_cookiemode['insert-only'] = array('name' => 'Insert-preserve', 'syntax' => 'cookie <cookie name> preserve insert', + 'descr' => 'Cookie is analyzed on incoming request to choose server. Set-Cookie value is set only if the server does not provide one or if the client came without the Cookie.'); +$a_cookiemode['insert-only-silent'] = array('name' => 'Insert-preserve-silent', 'syntax' => 'cookie <cookie name> preserve insert indirect', + 'descr' => 'Cookie is analyzed on incoming request to choose server and Set-Cookie value is left untouched if present, inserted in response if needed or removed if not needed.'); +$a_cookiemode['session-prefix'] = array('name' => 'Session-prefix', 'syntax' => 'cookie <cookie name> prefix', + 'descr' => 'Cookie is analyzed on incoming request to choose server whose Cookie Name prefix matches. Set Cookie value is prefixed using server line Cookie ID in response. Cookie is modified only between HAProxy and the client only'); +$a_cookiemode['passive-session-prefix'] = array('name' => 'Passive-session-prefix', 'syntax' => 'cookie <cookie name> preserve prefix indirect', + 'descr' => 'Cookie is analysed on incoming request to choose server whose Cookie ID prefix matches.'); +foreach($a_cookiemode as &$cookiemode) + $cookiemode['descr'] = $cookiemode['descr'] . "\n\n" . $cookiemode['syntax'] . ""; + +global $a_sticky_type; +$a_sticky_type = array(); +$a_sticky_type['none'] = array('name' => 'none', + 'descr' => "No stick-table will be used"); +$a_sticky_type['stick_sslsessionid'] = array('name' => 'Stick on SSL-Session-ID', + 'descr' => "Only used on https frontends. Uses the SSL-Session-ID to persist clients to a server."); +$a_sticky_type['stick_sourceipv4'] = array('name' => 'Stick on SourceIP IPv4', + 'descr' => "Stick on the client ip, drawback is that multiple clients behind a natted public ip will be balanced to the same server."); +$a_sticky_type['stick_sourceipv6'] = array('name' => 'Stick on SourceIP IPv6', + 'descr' => "Stick on the client ip, drawback is that multiple clients behind a natted public ip will be balanced to the same server."); +$a_sticky_type['stick_cookie_value'] = array('name' => 'Stick on existing Cookie value', + 'descr' => "Stick on the value of a session cookie", + 'cookiedescr' => "Enables SSL-session-id based persistence. (only use on 'https' and 'tcp' frontends that use SSL)<br/>EXAMPLE: JSESSIONID PHPSESSIONID ASP.NET_SessionId"); +$a_sticky_type['stick_rdp_cookie'] = array('name' => 'Stick on RDP-cookie', + 'descr' => "Uses a RDP-Cookie send by the mstsc client, note that not all clients send this.", + 'cookiedescr' => 'EXAMPLE: msts or mstshash'); + +if(!function_exists('group_ports')){ +// function group_ports() is present in pfSense 2.2 in util.inc +/* create ranges of sequential port numbers (200:215) and remove duplicates */ +function group_ports($ports) { + if (!is_array($ports) || empty($ports)) + return; + + $uniq = array(); + foreach ($ports as $port) { + if (is_portrange($port)) { + list($begin, $end) = explode(":", $port); + if ($begin > $end) { + $aux = $begin; + $begin = $end; + $end = $aux; + } + for ($i = $begin; $i <= $end; $i++) + if (!in_array($i, $uniq)) + $uniq[] = $i; + } else if (is_port($port)) { + if (!in_array($port, $uniq)) + $uniq[] = $port; + } + } + sort($uniq, SORT_NUMERIC); + + $result = array(); + foreach ($uniq as $idx => $port) { + if ($idx == 0) { + $result[] = $port; + continue; + } + + $last = end($result); + if (is_portrange($last)) + list($begin, $end) = explode(":", $last); + else + $begin = $end = $last; + + if ($port == ($end+1)) { + $end++; + $result[count($result)-1] = "{$begin}:{$end}"; + } else { + $result[] = $port; + } + } + + return $result; +} +} + +function haproxy_portoralias_to_list($port_or_alias) { + // input: a port or aliasname: 80 https MyPortAlias + // returns: a array of ports and portranges 80 443 8000:8010 + global $aliastable; + $portresult = array(); + if (alias_get_type($port_or_alias) == "port") { + $aliasports = $aliastable[$port_or_alias]; + $ports = explode(' ',$aliasports); + foreach($ports as $port) { + $portresults = haproxy_portoralias_to_list($port); + $portresult = array_merge($portresult, $portresults); + } + return $portresult; + } else if (is_portrange($port_or_alias)) { + return (array)$port_or_alias; + } else if (is_port($port_or_alias)) { + if (getservbyname($port_or_alias, "tcp")) + return (array)getservbyname($port_or_alias, "tcp"); + if (getservbyname($port_or_alias, "udp")) + return (array)getservbyname($port_or_alias, "udp"); + return (array)$port_or_alias; + } + else + return null; +} + function haproxy_custom_php_deinstall_command() { - exec("cd /var/db/pkg && pkg_delete `ls | grep haproxy`"); - exec("rm /usr/local/pkg/haproxy*"); - exec("rm /usr/local/www/haproxy*"); + global $static_output; + $static_output .= "HAProxy, running haproxy_custom_php_deinstall_command()\n"; + update_output_window($static_output); + $static_output .= "HAProxy, deleting haproxy webgui\n"; + update_output_window($static_output); exec("rm /usr/local/etc/rc.d/haproxy.sh"); - exec("rm /etc/devd/haproxy.conf"); - exec("/etc/rc.d/devd restart"); + $static_output .= "HAProxy, installing cron job if needed\n"; + update_output_window($static_output); haproxy_install_cron(false); + $static_output .= "HAProxy, running haproxy_custom_php_deinstall_command() DONE\n"; + update_output_window($static_output); } function haproxy_custom_php_install_command() { - global $g, $config; + global $g, $config, $static_output; + $static_output .= "HAProxy, running haproxy_custom_php_install_command()\n"; + update_output_window($static_output); + + $static_output .= "HAProxy, conf_mount_rw\n"; + update_output_window($static_output); conf_mount_rw(); - - $freebsd_version = substr(trim(`uname -r`), 0, 1); - if(!file_exists("/usr/bin/limits")) { - exec("fetch -q -o /usr/bin/limits http://files.pfsense.org/extras/{$freebsd_version}/limits"); - exec("chmod a+rx /usr/bin/limits"); - } + $static_output .= "HAProxy, create '/usr/local/etc/rc.d/haproxy.sh'\n"; + update_output_window($static_output); $haproxy = <<<EOD #!/bin/sh @@ -150,7 +278,7 @@ function haproxy_custom_php_install_command() { name="haproxy" rcvar=`set_rcvar` -command="/usr/local/bin/haproxy" +command="/usr/pbi/haproxy-devel-`uname -m`/sbin/haproxy" haproxy_enable=\${haproxy-"YES"} start_cmd="haproxy_start" @@ -202,27 +330,11 @@ EOD; fclose($fd); exec("chmod a+rx /usr/local/etc/rc.d/haproxy.sh"); - $devd = <<<EOD -notify 0 { - match "system" "IFNET"; - match "subsystem" "carp[0-9]+"; - match "type" "LINK_UP"; - action "/usr/local/etc/rc.d/haproxy.sh check"; -}; -notify 0 { - match "system" "IFNET"; - match "subsystem" "carp[0-9]+"; - match "type" "LINK_DOWN"; - action "/usr/local/etc/rc.d/haproxy.sh check"; -}; -EOD; - exec("mkdir -p /etc/devd"); - $fd = fopen("/etc/devd/haproxy.conf", "w"); - fwrite($fd, $devd); - fclose($fd); - exec("/etc/rc.d/devd restart"); + $static_output .= "HAProxy, update configuration\n"; + update_output_window($static_output); + $writeconfigupdate = false; /* Do XML upgrade from haproxy 0.31 to haproxy-dev */ if (is_array($config['installedpackages']['haproxy']['ha_servers'])) { @@ -293,9 +405,8 @@ EOD; $writeconfigupdate = true; } // update config to "haproxy-devel 1.5-dev19 pkg v0.5" - $a_backends = &$config['installedpackages']['haproxy']['ha_backends']['item']; - if(is_array($a_backends)) { - foreach ($a_backends as &$bind) { + if(is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { + foreach ($config['installedpackages']['haproxy']['ha_backends']['item'] as &$bind) { if($bind['httpclose'] && $bind['httpclose'] == "yes" ) { $bind['httpclose'] = "httpclose"; $writeconfigupdate = true; @@ -314,12 +425,22 @@ EOD; } } } - if ($writeconfigupdate) - write_config("haproxy, update xml config version"); + if ($writeconfigupdate) { + $static_output .= "HAProxy, write updated config\n"; + update_output_window($static_output); + write_config("HAProxy, update xml config version"); + } + $static_output .= "HAProxy, conf_mount_ro\n"; + update_output_window($static_output); conf_mount_ro(); - exec("/usr/local/etc/rc.d/haproxy.sh start"); + $static_output .= "HAProxy, starting haproxy (if previously enabled)\n"; + update_output_window($static_output); + haproxy_check_run(1); + + $static_output .= "HAProxy, running haproxy_custom_php_install_command() DONE\n"; + update_output_window($static_output); } function haproxy_install_cron($should_install) { @@ -380,33 +501,66 @@ function haproxy_find_acl($name) { function write_backend($fd, $name, $pool, $frontend) { if(!is_array($pool['ha_servers']['item']) && !$pool['stats_enabled']=='yes') return; - global $a_checktypes; + global $a_checktypes, $a_cookiemode; $a_servers = &$pool['ha_servers']['item']; - - unset($sslserverpresent); - if (is_array($a_servers)) - { - foreach($a_servers as $be) { - if (!$be['status'] == "inactive") - continue; - if ($be['ssl']) - $sslserverpresent = true; - } - } + $frontendtype = $frontend['type']; + $frontend_ip = haproxy_interface_ip($frontend['extaddr']); fwrite ($fd, "backend " . $name . "\n"); - if($pool['cookie_name'] && strtolower($frontend['type']) == "http") - fwrite ($fd, "\tcookie\t\t\t" . $pool['cookie_name'] . " insert indirect\n"); - - // https is an alias for tcp for clarity purpouses - if(strtolower($frontend['type']) == "https") { - $backend_type = "tcp"; + // https is an alias for tcp for clarity purposes + if($frontendtype == "https") { + $backend_mode = "tcp"; } else { - $backend_type = $frontend['type']; + $backend_mode = $frontendtype; + } + fwrite ($fd, "\tmode\t\t\t" . $backend_mode . "\n"); + + if ($frontendtype == "http") { + if ($pool["persist_cookie_enabled"] == "yes") { + $cookie_mode = $pool["persist_cookie_mode"]; + $cookie_cachable = $pool["persist_cookie_cachable"]; + $cookiesyntax = $a_cookiemode[$cookie_mode]["syntax"]; + $cookie = str_replace("<cookie name>", $pool["persist_cookie_name"], $cookiesyntax); + $cookie .= $cookie_cachable == "yes" ? "" : " nocache"; + fwrite ($fd, "\t" . $cookie . "\n"); + } + } + switch($pool["persist_sticky_type"]) { + case 'stick_sslsessionid': + if ($frontendtype == "https") { + fwrite ($fd, "\ttcp-request inspect-delay 5s\n"); + fwrite ($fd, "\tstick-table type binary len 32 size ".$pool["persist_stick_tablesize"]." expire ".$pool["persist_stick_expire"]."\n"); + fwrite ($fd, "\tacl clienthello req_ssl_hello_type 1\n"); + fwrite ($fd, "\tacl serverhello rep_ssl_hello_type 2\n"); + fwrite ($fd, "\ttcp-request content accept if clienthello\n"); + fwrite ($fd, "\ttcp-response content accept if serverhello\n"); + fwrite ($fd, "\tstick on payload_lv(43,1) if clienthello\n"); + fwrite ($fd, "\tstick store-response payload_lv(43,1) if serverhello\n"); + } + break; + case 'stick_rdp_cookie': + //tcp-request content accept if RDP_COOKIE + //fwrite ($fd, "\tstick on req.rdp_cookie(msts)\n"); + fwrite ($fd, "\tstick-table type binary len 32 size ".$pool["persist_stick_tablesize"]." expire ".$pool["persist_stick_expire"]."\n"); + fwrite ($fd, "\tstick on req.rdp_cookie(mstshash)\n"); + break; + case 'stick_sourceipv4': + fwrite ($fd, "\tstick-table type ip size ".$pool["persist_stick_tablesize"]." expire ".$pool["persist_stick_expire"]."\n"); + fwrite ($fd, "\tstick on src\n"); + break; + case 'stick_sourceipv6': + fwrite ($fd, "\tstick-table type ip size ".$pool["persist_stick_tablesize"]." expire ".$pool["persist_stick_expire"]."\n"); + fwrite ($fd, "\tstick on src\n"); + break; + case 'stick_cookie_value': + if ($frontendtype == "http") { + fwrite ($fd, "\tstick-table type string len {$pool["persist_stick_length"]} size ".$pool["persist_stick_tablesize"]." expire ".$pool["persist_stick_expire"]."\n"); + fwrite ($fd, "\tstick store-response res.cook({$pool["persist_stick_cookiename"]})\n"); + fwrite ($fd, "\tstick on req.cook({$pool["persist_stick_cookiename"]})\n"); + } + break; } - - fwrite ($fd, "\tmode\t\t\t" . $backend_type . "\n"); unset($checkport); $check_type = $pool['check_type']; @@ -456,17 +610,25 @@ function write_backend($fd, $name, $pool, $frontend) { fwrite ($fd, "\tstats\t\t\trealm " . haproxy_escapestring($pool['stats_realm']) . "\n"); else fwrite ($fd, "\tstats\t\t\trealm .\n"); - fwrite ($fd, "\tstats\t\t\tauth " . haproxy_escapestring($pool['stats_username']).":". haproxy_escapestring($pool['stats_password'])."\n"); + + if ($pool['stats_username'] && $pool['stats_password']) + fwrite ($fd, "\tstats\t\t\tauth " . haproxy_escapestring($pool['stats_username']).":". haproxy_escapestring($pool['stats_password'])."\n"); if($pool['stats_admin']=='yes') fwrite ($fd, "\tstats\t\t\tadmin if TRUE" . "\n"); - if($pool['stats_node_enabled']=='yes') + if($pool['stats_node']) fwrite ($fd, "\tstats\t\t\tshow-node " . $pool['stats_node'] . "\n"); if($pool['stats_desc']) - fwrite ($fd, "\tstats\t\t\tshow-desc " . $pool['stats_desc'] . "\n"); + fwrite ($fd, "\tstats\t\t\tshow-desc " . haproxy_escapestring($pool['stats_desc']) . "\n"); if($pool['stats_refresh']) fwrite ($fd, "\tstats\t\t\trefresh " . $pool['stats_refresh'] . "\n"); + + if ($pool['stats_scope']) { + $scope_items = explode(",", $pool['stats_scope']); + foreach($scope_items as $scope_item) + fwrite ($fd, "\tstats\t\t\tscope " . $scope_item . "\n"); + } } $uri = $pool['monitor_uri']; @@ -478,6 +640,10 @@ function write_backend($fd, $name, $pool, $frontend) { if ($optioncheck) fwrite ($fd, "\toption\t\t\t{$optioncheck}\n"); + if ($pool["strict_transport_security"] && is_numeric($pool["strict_transport_security"])){ + fwrite ($fd, "\trspadd Strict-Transport-Security:\ max-age={$pool["strict_transport_security"]};\n"); + } + if ($pool['advanced_backend']) { $adv_be = explode("\n", base64_decode($pool['advanced_backend'])); foreach($adv_be as $adv_line) { @@ -487,10 +653,6 @@ function write_backend($fd, $name, $pool, $frontend) { } } - if($pool['cookie'] && strtolower($frontend['type']) == "http") - $cookie = " cookie {$pool['cookie']} "; - else - $cookie = ""; if($pool['advanced']) { $advanced = base64_decode($pool['advanced']); $advanced_txt = " " . $advanced; @@ -501,9 +663,9 @@ function write_backend($fd, $name, $pool, $frontend) { if ($check_type != 'none') { if($pool['checkinter']) - $checkinter = "check inter {$pool['checkinter']}"; + $checkinter = " check inter {$pool['checkinter']}"; else - $checkinter = "check inter 1000"; + $checkinter = " check inter 1000"; } //agent-check requires at least haproxy v1.5dev20 @@ -515,6 +677,10 @@ function write_backend($fd, $name, $pool, $frontend) { foreach($a_servers as $be) { if ($be['status'] == "inactive") continue; + if($be['cookie'] && $frontendtype == "http") + $cookie = " cookie {$be['cookie']}"; + else + $cookie = ""; if (!$be['name']) $be['name'] = $be['address']; @@ -526,9 +692,13 @@ function write_backend($fd, $name, $pool, $frontend) { $ssl = ""; if ($be['ssl'] == 'yes') { - $ssl = $backend_type == "http" ? ' ssl' : ' check-ssl'; + $ssl = $frontendtype == "http" ? ' ssl' : ' check-ssl'; + } + $weight = ""; + if (is_numeric($be['weight'])){ + $weight = " weight " . $be['weight']; } - fwrite ($fd, "\tserver\t\t\t" . $be['name'] . " " . $be['address'].":" . $be['port'] . "$ssl $cookie $checkinter$checkport$agentcheck $isbackup weight " . $be['weight'] . "{$advanced_txt} {$be['advanced']}\n"); + fwrite ($fd, "\tserver\t\t\t" . $be['name'] . " " . $be['address'].":" . $be['port'] . "$ssl$cookie$checkinter$checkport$agentcheck $isbackup$weight{$advanced_txt} {$be['advanced']}\n"); } } fwrite ($fd, "\n"); @@ -537,13 +707,11 @@ function write_backend($fd, $name, $pool, $frontend) { function haproxy_configure() { global $g; // reload haproxy - haproxy_writeconf("{$g['varetc_path']}/haproxy"); return haproxy_check_run(1); } function haproxy_check_and_run(&$messages, $reload) { global $g; - $configpath = "{$g['varetc_path']}/haproxy"; $testpath = "{$g['varetc_path']}/haproxy_test"; haproxy_writeconf($testpath); $retval = exec("haproxy -c -V -f $testpath/haproxy.cfg 2>&1", $output, $err); @@ -561,7 +729,6 @@ function haproxy_check_and_run(&$messages, $reload) { $ok = strstr($retval, "Configuration file is valid"); if ($ok && $reload) { global $haproxy_run_message; - haproxy_writeconf($configpath); rmdir_recursive($testpath); $ok = haproxy_check_run(1) == 0; $messages = $haproxy_run_message; @@ -617,6 +784,7 @@ function haproxy_writeconf($configpath) { fwrite ($fd, "\tnbproc\t\t\t$numprocs\n"); fwrite ($fd, "\tchroot\t\t\t/var/empty\n"); fwrite ($fd, "\tdaemon\n"); + fwrite ($fd, "\tssl-server-verify none\n"); // Keep the advanced options on the bottom of the global settings, to allow additional sections to be easely added if($a_global['advanced']) { @@ -627,6 +795,22 @@ function haproxy_writeconf($configpath) { } } fwrite ($fd, "\n"); + + $localstatsport = $a_global['localstatsport']; + if ($localstatsport){ + fwrite ($fd, "listen HAProxyLocalStats\n"); + fwrite ($fd, "\tbind 127.0.0.1:$localstatsport\n"); + fwrite ($fd, "\tmode http\n"); + fwrite ($fd, "\tstats enable\n"); + if (is_numeric($a_global['localstats_refreshtime'])) + fwrite ($fd, "\tstats refresh {$a_global['localstats_refreshtime']}\n"); + fwrite ($fd, "\tstats admin if TRUE\n"); + fwrite ($fd, "\tstats uri /haproxy_stats.php?haproxystats=1\n"); + fwrite ($fd, "\ttimeout client 5000\n"); + fwrite ($fd, "\ttimeout connect 5000\n"); + fwrite ($fd, "\ttimeout server 5000\n"); + fwrite ($fd, "\n"); + } } // Try and get a unique array for address:port as frontends can duplicate @@ -643,11 +827,11 @@ function haproxy_writeconf($configpath) { unlink_if_exists("var/etc/{$frontend['name']}.{$frontend['port']}.crt"); continue; } - + $primaryfrontend = get_primaryfrontend($frontend); $bname = get_frontend_ipport($frontend); //check ssl info - if (strtolower($frontend['type']) == "http" && $frontend['ssloffload']){ + if (strtolower($primaryfrontend['type']) == "http" && $frontend['ssloffload']){ //ssl crt ./server.pem ca-file ./ca.crt verify optional crt-ignore-err all crl-file ./ca_crl.pem $filename = "$configpath/{$frontend['name']}.{$frontend['port']}.pem"; $ssl_crt = " crt $filename"; @@ -672,7 +856,6 @@ function haproxy_writeconf($configpath) { $a_bind[$bname] = array(); $a_bind[$bname]['config'] = array(); // Settings which are used only from the primary frontend - $primaryfrontend = get_primaryfrontend($frontend); $a_bind[$bname]['name'] = $primaryfrontend['name']; $a_bind[$bname]['extaddr'] = $primaryfrontend['extaddr']; $a_bind[$bname]['port'] = $primaryfrontend['port']; @@ -689,7 +872,7 @@ function haproxy_writeconf($configpath) { if (($frontend['secondary'] != 'yes') && ($frontend['name'] != $b['name'])) { // only 1 frontend can be the primary for a set of frontends that share 1 address:port. - $input_errors[] = "Multiple primary frondends for $bname"; + $input_errors[] = "Multiple primary frontends for $bname use the 'Shared Frontend' option instead"; } if ($ssl_crt != "") { @@ -718,17 +901,28 @@ function haproxy_writeconf($configpath) { // Prepare ports for processing by splitting $portss = "{$bind['port']},"; $ports = split(",", $portss); - $ssl_info = $bind['ssl_info']; - $advanced_bind = $bind['advanced_bind']; + + if($bind['type'] == "http") { + // ssl offloading is only possible in http mode. + $ssl_info = $bind['ssl_info']; + $advanced_bind = $bind['advanced_bind']; + } else { + $ssl_info = ""; + $advanced_bind = ""; + } // Initialize variable $listenip = ""; // Process and add bind directives for ports $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"; + foreach($ports as $alias_or_port) { + if($alias_or_port) { + $portsnumeric = group_ports(haproxy_portoralias_to_list($alias_or_port)); + foreach($portsnumeric as $portnumeric) { + $portnumeric = str_replace(":","-",$portnumeric); + $listenip .= "\tbind\t\t\t$ip:{$portnumeric} {$ssl_info} {$advanced_bind}\n"; + } } } } @@ -746,7 +940,7 @@ function haproxy_writeconf($configpath) { } } - // https is an alias for tcp for clarity purpouses + // https is an alias for tcp for clarity purposes if($bind['type'] == "https") { $backend_type = "tcp"; } else { @@ -784,45 +978,15 @@ function haproxy_writeconf($configpath) { $default_backend = ""; $i = 0; foreach ($bind['config'] as $frontend) { - $a_acl=&$frontend['ha_acls']['item']; - if(!is_array($a_acl)) - $a_acl=array(); - - $poolname = $frontend['backend_serverpool'] . "_" . strtolower($frontend['type']); + $a_acl = get_frontend_acls($frontend); - // Create different pools if the svrport is set - if ($frontend['svrport'] > 0) - $poolname .= "_" . $frontend['svrport']; + $poolname = $frontend['backend_serverpool'] . "_" . strtolower($bind['type']); if (!isset($a_pendingpl[$poolname])) { $a_pendingpl[$poolname] = array(); $a_pendingpl[$poolname]['name'] = $poolname; - $a_pendingpl[$poolname]['frontend'] = $frontend; - } - - if (strtolower($bind['type']) == "http" && $frontend['ssloffload']) { - $aclname = "SNI_" . $poolname; - if ($frontend['ssloffloadacl']){ - $cert = lookup_cert($frontend['ssloffloadcert']); - $cert_cn = cert_get_cn($cert['crt']); - $descr = haproxy_escape_acl_name($cert['descr']); - $a_acl[] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_matches', 'value' => $cert_cn); - unset($cert); - } - if ($frontend['ssloffloadacladditional']){ - $certs = $frontend['ha_certificates']['item']; - if (is_array($certs)){ - if (count($certs) > 0){ - foreach($certs as $certref){ - $cert = lookup_cert($certref['ssl_certificate']); - $cert_cn = cert_get_cn($cert['crt']); - $descr = haproxy_escape_acl_name($cert['descr']); - $a_acl[] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_matches', 'value' => $cert_cn); - unset($cert); - } - } - } - } + $a_pendingpl[$poolname]['backend'] = $frontend['backend_serverpool']; + $a_pendingpl[$poolname]['frontend'] = $bind; } // Write this out once, and must be before any backend config text @@ -833,8 +997,8 @@ function haproxy_writeconf($configpath) { // combine acl's with same name to allow for 'combined checks' to check for example hostname and fileextension together.. $a_acl_combine = array(); foreach ($a_acl as $entry) { - $name = $entry['name']; - $a_acl_combine[$name][] = $entry; + $name = $entry['ref']['name']; + $a_acl_combine[$name][] = $entry['ref']; } foreach ($a_acl_combine as $a_usebackend) { @@ -873,7 +1037,7 @@ function haproxy_writeconf($configpath) { if (is_array($a_pendingpl) && is_array($a_backends)) { foreach ($a_pendingpl as $pending) { foreach ($a_backends as $pool) { - if ($pending['frontend']['backend_serverpool'] == $pool['name']) { + if ($pending['backend'] == $pool['name']) { write_backend($fd, $pending['name'], $pool, $pending['frontend']); } } @@ -935,16 +1099,9 @@ function use_transparent_clientip_proxying() { return false; } -function load_ipfw_rules() { - // On FreeBSD 8 pf does not support "divert-reply" so ipfw is needed. - global $g, $config; - $ipfw_zone_haproxy = "haproxy"; - +function haproxy_get_transparent_backends(){ + global $config; $a_backends = &$config['installedpackages']['haproxy']['ha_pools']['item']; - - haproxy_load_modules(); - - $transparent_interfaces = array(); $transparent_backends = array(); foreach ($a_backends as $backend) { if ($backend["transparent_clientip"] != 'yes') @@ -960,15 +1117,51 @@ function load_ipfw_rules() { if (!is_ipaddr($be['address'])) continue; $item = array(); + $item['name'] = $be['name']; + $item['interface'] = $real_if; $item['address'] = $be['address']; $item['port'] = $be['port']; - $item['interface'] = $real_if; $transparent_backends[] = $item; - $transparent_interfaces[$real_if] = 1; } } } } + return $transparent_backends; +} + +function haproxy_generate_rules($type) { + // called by filter.inc when pfSense rules generation happens + global $g, $config; + $rules = ""; + switch($type) { + case 'filter': + $transparent_backends = haproxy_get_transparent_backends(); + foreach($transparent_backends as $tb){ + // This sloppy rule is needed because of ipfw is used to 'catch' return traffic. + $rules .= "# allow HAProxy transparent traffic\n"; + $rules .= "pass out quick on {$tb['interface']} inet proto tcp from any to {$tb['address']} port {$tb['port']} flags S/SA keep state ( sloppy ) label \"HAPROXY_transparent_rule_{$tb['name']}\"\n"; + } + break; + } + return $rules; +} + +function load_ipfw_rules() { + // On FreeBSD 8 pf does not support "divert-reply" so ipfw is needed. + global $g, $config; + $ipfw_zone_haproxy = "haproxy"; + + $a_backends = &$config['installedpackages']['haproxy']['ha_pools']['item']; + + haproxy_load_modules(); + + $transparent_backends = haproxy_get_transparent_backends(); + + $transparent_interfaces = array(); + foreach($transparent_backends as $transparent_backend){ + $interface = $transparent_backend['interface']; + $transparent_interfaces[$interface] = 1; + } mwexec("/usr/local/sbin/ipfw_context -a $ipfw_zone_haproxy", true); foreach($transparent_interfaces as $transparent_if => $value) { @@ -988,19 +1181,27 @@ function load_ipfw_rules() { mwexec("/sbin/ipfw -x $ipfw_zone_haproxy -q {$g['tmp_path']}/ipfw_{$ipfw_zone_haproxy}.haproxy.rules", true); } +function haproxy_plugin_carp($pluginparams) { + // called by pfSense when a CARP interface changes its state (called multiple times when multiple interfaces change state) + // $pluginparams['type'] always 'carp' + // $pluginparams['event'] either 'rc.carpmaster' or 'rc.carpbackup' + // $pluginparams['interface'] contains the affected interface + $type = $pluginparams['type']; + $event = $pluginparams['event']; + $interface = $pluginparams['interface']; + haproxy_check_run(0); +} + function haproxy_check_run($reload) { global $config, $g, $haproxy_run_message; + $haproxylock = lock("haproxy", LOCK_EX); $a_global = &$config['installedpackages']['haproxy']; $configpath = "{$g['varetc_path']}/haproxy"; - - exec("/usr/bin/limits -n 300014"); - - if(use_transparent_clientip_proxying()) - load_ipfw_rules(); - else - mwexec("/usr/local/sbin/ipfw_context -d haproxy", true); + if ($reload) + haproxy_writeconf($configpath); + if(isset($a_global['enable'])) { if (isset($a_global['carpdev'])) { $status = get_carp_interface_status($a_global['carpdev']); @@ -1010,15 +1211,25 @@ function haproxy_check_run($reload) { //exec("/bin/pkill -F /var/run/haproxy.pid haproxy");//doesnt work for multiple pid's in a pidfile haproxy_kill(); } + unlock($haproxylock); return (0); } else if (haproxy_is_running() && $reload == 0) { + unlock($haproxylock); return (0); } log_error("Starting haproxy on CARP master."); /* fallthrough */ - } else if ($reload == 0) + } else if ($reload == 0){ + unlock($haproxylock); return (0); + } + if(use_transparent_clientip_proxying()) { + filter_configure(); + load_ipfw_rules(); + } else + mwexec("/usr/local/sbin/ipfw_context -d haproxy", true); + if (haproxy_is_running()) { if (isset($a_global['terminate_on_reload'])) $sf_st = "-st";//terminate old process as soon as the new process is listening @@ -1030,14 +1241,15 @@ function haproxy_check_run($reload) { } foreach($output as $line) $haproxy_run_message .= "<br/>" . htmlspecialchars($line) . "\n"; - return ($errcode); } else { if ($reload && haproxy_is_running()) { //exec("/bin/pkill -F /var/run/haproxy.pid haproxy");//doesnt work for multiple pid's in a pidfile haproxy_kill(); } - return (0); + $errcode = 0; } + unlock($haproxylock); + return ($errcode); } function haproxy_kill($killimmediately = true) { @@ -1120,7 +1332,7 @@ function get_primaryfrontend($frontend) { function get_frontend_ipport($frontend,$userfriendly=false) { $mainfrontend = get_primaryfrontend($frontend); - $result = haproxy_interface_ip($mainfrontend['extaddr'],$userfriendly); + $result = haproxy_interface_ip($mainfrontend['extaddr'], $userfriendly); if ($userfriendly and is_ipaddrv6($result)) $result = "[{$result}]"; return $result . ":" . $mainfrontend['port']; @@ -1174,6 +1386,7 @@ function get_haproxy_frontends($excludeitem="") { } function get_frontend_acls($frontend) { + $mainfrontend = get_primaryfrontend($frontend); $result = array(); $a_acl = &$frontend['ha_acls']['item']; if (is_array($a_acl)) @@ -1184,7 +1397,7 @@ function get_frontend_acls($frontend) { continue; // Filter out acls for different modes - if ($acl['mode'] != '' && $acl['mode'] != strtolower($frontend['type'])) + if ($acl['mode'] != '' && $acl['mode'] != strtolower($mainfrontend['type'])) continue; $acl_item = array(); @@ -1194,17 +1407,52 @@ function get_frontend_acls($frontend) { $result[] = $acl_item; } } + + if (strtolower($mainfrontend['type']) == "http" && $mainfrontend['ssloffload']) { + $a_acl = &$frontend['ha_acls']['item']; + if(!is_array($a_acl)) + $a_acl=array(); + + $poolname = $frontend['backend_serverpool'] . "_" . strtolower($frontend['type']); + $aclname = "SNI_" . $poolname; + if ($frontend['ssloffloadacl']){ + $cert = lookup_cert($frontend['ssloffloadcert']); + $cert_cn = cert_get_cn($cert['crt']); + $descr = haproxy_escape_acl_name($cert['descr']); + unset($cert); + $acl_item = array(); + $acl_item['descr'] = "Certificate ACL ".$cert_cn; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_matches', 'value' => $cert_cn); + $result[] = $acl_item; + } + if ($frontend['ssloffloadacladditional']){ + $certs = $frontend['ha_certificates']['item']; + if (is_array($certs)){ + foreach($certs as $certref){ + $cert = lookup_cert($certref['ssl_certificate']); + $cert_cn = cert_get_cn($cert['crt']); + $descr = haproxy_escape_acl_name($cert['descr']); + unset($cert); + $acl_item = array(); + $acl_item['descr'] = "Additional certificate ACLs: ".$cert_cn; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_matches', 'value' => $cert_cn); + $result[] = $acl_item; + } + } + } + } return $result; } function get_backend($name) { global $config; $a_backend = &$config['installedpackages']['haproxy']['ha_pools']['item']; - foreach($a_backend as $key => $backend) - { - if ($backend['name'] == $name) - return $backend; - } + if(is_array($a_backend)) + foreach($a_backend as $key => $backend) + { + if ($backend['name'] == $name) + return $backend; + } return null; } @@ -1218,4 +1466,25 @@ function haproxy_escape_acl_name($aclname) { return preg_replace_callback('([^A-Za-z0-9\._\-\:])', function($match){return "_".dechex(ord($match[0]));}, $aclname); } +function haproxy_find_create_certificate($certificatename) { + global $g; + $cert = lookup_cert_by_name($certificatename); + if (is_array($cert)) + return $cert; + global $config; + $a_cert =& $config['cert']; + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = gettext($certificatename); + mwexec("/usr/local/bin/openssl genrsa 1024 > {$g['tmp_path']}/ssl.key"); + mwexec("/usr/local/bin/openssl req -new -x509 -nodes -sha256 -days 2000 -key {$g['tmp_path']}/ssl.key > {$g['tmp_path']}/ssl.crt"); + $crt = file_get_contents("{$g['tmp_path']}/ssl.crt"); + $key = file_get_contents("{$g['tmp_path']}/ssl.key"); + unlink("{$g['tmp_path']}/ssl.key"); + unlink("{$g['tmp_path']}/ssl.crt"); + cert_import($cert, $crt, $key); + $a_cert[] = $cert; + return $cert; +} + ?> diff --git a/config/haproxy-devel/haproxy.widget.php b/config/haproxy-devel/haproxy.widget.php index 7954e404..5d664e81 100644 --- a/config/haproxy-devel/haproxy.widget.php +++ b/config/haproxy-devel/haproxy.widget.php @@ -3,7 +3,7 @@ Copyright (C) 2013 PiBa-NL Copyright 2011 Thomas Schaefer - Tomschaefer.org Copyright 2011 Marcello Coutinho - Part of pfSense widgets (www.pfsense.com) + Part of pfSense widgets (www.pfsense.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/config/haproxy-devel/haproxy.xml b/config/haproxy-devel/haproxy.xml index 6b25dd46..5c534522 100644 --- a/config/haproxy-devel/haproxy.xml +++ b/config/haproxy-devel/haproxy.xml @@ -58,66 +58,81 @@ <executable>haproxy</executable> <description>The Reliable, High Performance TCP/HTTP Load Balancer</description> </service> + <plugins> + <item> + <type>plugin_carp</type> + </item> + </plugins> <configpath>installedpackages->haproxy->config</configpath> <additional_files_needed> <prefix>/usr/local/pkg/</prefix> <chmod>077</chmod> - <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy.inc</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy.inc</item> </additional_files_needed> <additional_files_needed> <prefix>/usr/local/www/</prefix> <chmod>077</chmod> - <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_listeners.php</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_listeners.php</item> </additional_files_needed> <additional_files_needed> <prefix>/usr/local/www/</prefix> <chmod>077</chmod> - <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_listeners_edit.php</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_listeners_edit.php</item> </additional_files_needed> <additional_files_needed> <prefix>/usr/local/www/</prefix> <chmod>077</chmod> - <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_global.php</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_global.php</item> </additional_files_needed> <additional_files_needed> <prefix>/usr/local/www/</prefix> <chmod>077</chmod> - <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_pools.php</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_pools.php</item> </additional_files_needed> <additional_files_needed> <prefix>/usr/local/www/</prefix> <chmod>077</chmod> - <item>http://www.pfsense.com/packages/config/haproxy-devel/haproxy_pool_edit.php</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_pool_edit.php</item> + </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/www/</prefix> + <chmod>077</chmod> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_stats.php</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_socketinfo.inc</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_socketinfo.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_xmlrpcsyncclient.inc</item> + <item>https://packages.pfsense.org/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/haproxy_htmllist.inc</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_htmllist.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> + <item>https://packages.pfsense.org/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> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy.widget.php</item> </additional_files_needed> <additional_files_needed> <prefix>/usr/local/www/shortcuts/</prefix> <chmod>0755</chmod> - <item>http://www.pfsense.org/packages/config/haproxy-devel/pkg_haproxy.inc</item> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/pkg_haproxy.inc</item> + </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/pkg/</prefix> + <chmod>077</chmod> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/pkg_haproxy_tabs.inc</item> </additional_files_needed> <custom_delete_php_command> </custom_delete_php_command> diff --git a/config/haproxy-devel/haproxy_global.php b/config/haproxy-devel/haproxy_global.php index 0a92cde7..50472d9f 100755 --- a/config/haproxy-devel/haproxy_global.php +++ b/config/haproxy-devel/haproxy_global.php @@ -2,7 +2,7 @@ /* $Id: load_balancer_pool.php,v 1.5.2.6 2007/03/02 23:48:32 smos Exp $ */ /* haproxy_global.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2013 PiBa-NL Copyright (C) 2009 Scott Ullrich <sullrich@pfsense.com> Copyright (C) 2008 Remco Hoef <remcoverhoef@pfsense.com> @@ -34,6 +34,9 @@ require_once("guiconfig.inc"); require_once("haproxy.inc"); require_once("haproxy_utils.inc"); require_once("globals.inc"); +require_once("pkg_haproxy_tabs.inc"); + +$simplefields = array('localstats_refreshtime','localstats_sticktable_refreshtime'); if (!is_array($config['installedpackages']['haproxy'])) $config['installedpackages']['haproxy'] = array(); @@ -65,6 +68,15 @@ if ($_POST) { if ($_POST['maxconn'] && (!is_numeric($_POST['maxconn']))) $input_errors[] = "The maximum number of connections should be numeric."; + + if ($_POST['localstatsport'] && (!is_numeric($_POST['localstatsport']))) + $input_errors[] = "The local stats port should be numeric or empty."; + + if ($_POST['localstats_refreshtime'] && (!is_numeric($_POST['localstats_refreshtime']))) + $input_errors[] = "The local stats refresh time should be numeric or empty."; + + if ($_POST['localstats_sticktable_refreshtime'] && (!is_numeric($_POST['localstats_sticktable_refreshtime']))) + $input_errors[] = "The local stats sticktable refresh time should be numeric or empty."; /*if($_POST['synchost1'] && !is_ipaddr($_POST['synchost1'])) $input_errors[] = "Synchost1 needs to be an IPAddress."; @@ -86,8 +98,11 @@ if ($_POST) { $config['installedpackages']['haproxy']['loglevel'] = $_POST['loglevel'] ? $_POST['loglevel'] : false; $config['installedpackages']['haproxy']['carpdev'] = $_POST['carpdev'] ? $_POST['carpdev'] : false; //$config['installedpackages']['haproxy']['syncpassword'] = $_POST['syncpassword'] ? $_POST['syncpassword'] : false; + $config['installedpackages']['haproxy']['localstatsport'] = $_POST['localstatsport'] ? $_POST['localstatsport'] : false; $config['installedpackages']['haproxy']['advanced'] = $_POST['advanced'] ? base64_encode($_POST['advanced']) : false; $config['installedpackages']['haproxy']['nbproc'] = $_POST['nbproc'] ? $_POST['nbproc'] : false; + foreach($simplefields as $stat) + $config['installedpackages']['haproxy'][$stat] = $_POST[$stat]; touch($d_haproxyconfdirty_path); write_config(); } @@ -106,8 +121,11 @@ $pconfig['remotesyslog'] = $config['installedpackages']['haproxy']['remotesyslog $pconfig['logfacility'] = $config['installedpackages']['haproxy']['logfacility']; $pconfig['loglevel'] = $config['installedpackages']['haproxy']['loglevel']; $pconfig['carpdev'] = $config['installedpackages']['haproxy']['carpdev']; +$pconfig['localstatsport'] = $config['installedpackages']['haproxy']['localstatsport']; $pconfig['advanced'] = base64_decode($config['installedpackages']['haproxy']['advanced']); $pconfig['nbproc'] = $config['installedpackages']['haproxy']['nbproc']; +foreach($simplefields as $stat) + $pconfig[$stat] = $config['installedpackages']['haproxy'][$stat]; // defaults if (!$pconfig['logfacility']) @@ -115,8 +133,8 @@ if (!$pconfig['logfacility']) if (!$pconfig['loglevel']) $pconfig['loglevel'] = 'info'; -$pfSversion = str_replace("\n", "", file_get_contents("/etc/version")); -if(strstr($pfSversion, "1.2")) +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); +if ($pf_version < 2.0) $one_two = true; $pgtitle = "Services: HAProxy: Settings"; @@ -148,12 +166,7 @@ function enable_change(enable_change) { <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr><td class="tabnavtbl"> <?php - /* active tabs */ - $tab_array = array(); - $tab_array[] = array("Settings", true, "haproxy_global.php"); - $tab_array[] = array("Frontend", false, "haproxy_listeners.php"); - $tab_array[] = array("Backend", false, "haproxy_pools.php"); - display_top_tabs($tab_array); + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "settings"); ?> </td></tr> <tr> @@ -161,20 +174,6 @@ 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"> </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> @@ -198,11 +197,17 @@ function enable_change(enable_change) { </table> Sets the maximum per-process number of concurrent connections to X.<br/> <strong>NOTE:</strong> setting this value too high will result in HAProxy not being able to allocate enough memory.<br/> + <p> <?php $memusage = trim(`ps auxw | grep haproxy | grep -v grep | awk '{ print $5 }'`); if($memusage) - echo "<p>Current memory usage: {$memusage} K.</p>"; + echo "Current memory usage: <b>{$memusage} kB.</b><br/>"; ?> + Current <a href='/system_advanced_sysctl.php'>'System Tunables'</a> settings.<br/> + 'kern.maxfiles': <b><?=`sysctl kern.maxfiles | awk '{ print $2 }'`?></b><br/> + 'kern.maxfilesperproc': <b><?=`sysctl kern.maxfilesperproc | awk '{ print $2 }'`?></b><br/> + </p> + Full memory usage will only show after all connections have actually been used. </td><td> <table style="border: 1px solid #000;"> <tr> @@ -215,23 +220,29 @@ function enable_change(enable_change) { </td> </tr> <tr> - <td align="right"><font size=-1>999</font></td> - <td><font size=-1>1888K</font></td> + <td align="right"><font size=-1>1</font></td> + <td><font size=-1>50 kB</font></td> </tr> <tr> - <td align="right"><font size=-1>99999</font></td> - <td><font size=-1>8032K</font></td> + <td align="right"><font size=-1>1.000</font></td> + <td><font size=-1>48 MB</font></td> </tr> <tr> - <td align="right"><font size=-1>999999</font></td> - <td><font size=-1>50016K</font></td> + <td align="right"><font size=-1>10.000</font></td> + <td><font size=-1>488 MB</font></td> </tr> <tr> - <td align="right"><font size=-1>9999999</font></td> - <td><font size=-1>467M</font></td> + <td align="right"><font size=-1>100.000</font></td> + <td><font size=-1>4,8 GB</font></td> + </tr> + <tr> + <td colspan="2" style="white-space: nowrap"><font size=-2>Calculated for plain HTTP connections,<br/>using ssl offloading will increase this.</font></td> </tr> </table> </td></tr></table> + When setting a high amount of allowed simultaneous connections you will need to add and or increase the following two <b><a href='/system_advanced_sysctl.php'>'System Tunables'</a></b> kern.maxfiles and kern.maxfilesperproc. + For HAProxy alone set these to at least the number of allowed connections * 2 + 31. So for 100.000 connections these need to be 200.031 or more to avoid trouble, take into account that handles are also used by other processes when setting kern.maxfiles. + <br/> </td> </tr> <tr> @@ -339,12 +350,37 @@ function enable_change(enable_change) { </td> </tr> <tr> + <td colspan="2" valign="top" class="listtopic">Stats tab, 'internal' stats port</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell">Internal stats port</td> + <td class="vtable"> + <input name="localstatsport" type="text" <?if(isset($pconfig['localstatsport'])) echo "value=\"{$pconfig['localstatsport']}\"";?> size="10" maxlength="5" /> EXAMPLE: 2200<br/> + Sets the internal port to be used for the stats tab. + This is bound to 127.0.0.1 so will not be directly exposed on any LAN/WAN/other interface. It is used to internally pass through the stats page. + Leave this setting empty to remove the "HAProxyLocalStats" item from the stats page and save a little on recources. + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell">Internal stats refresh rate</td> + <td class="vtable"> + <input name="localstats_refreshtime" type="text" <?if(isset($pconfig['localstats_refreshtime'])) echo "value=\"{$pconfig['localstats_refreshtime']}\"";?> size="10" maxlength="5" /> Seconds, Leave this setting empty to not refresh the page automatically. EXAMPLE: 10 + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell">Sticktable page refresh rate</td> + <td class="vtable"> + <input name="localstats_sticktable_refreshtime" type="text" <?if(isset($pconfig['localstats_sticktable_refreshtime'])) echo "value=\"{$pconfig['localstats_sticktable_refreshtime']}\"";?> size="10" maxlength="5" /> Seconds, Leave this setting empty to not refresh the page automatically. EXAMPLE: 10 + </td> + </tr> + <tr> <td colspan="2" valign="top" class="listtopic">Global Advanced pass thru</td> </tr> <tr> <td width="22%" valign="top" class="vncell"> </td> <td width="78%" class="vtable"> - <textarea name='advanced' rows="4" cols="70" id='advanced'><?php echo $pconfig['advanced']; ?></textarea> + <? $textrowcount = max(substr_count($pconfig['advanced'],"\n"), 2) + 2; ?> + <textarea name='advanced' rows="<?=$textrowcount;?>" cols="70" id='advanced'><?php echo $pconfig['advanced']; ?></textarea> <br/> NOTE: paste text into this box that you would like to pass thru in the global settings area. </td> @@ -355,6 +391,19 @@ function enable_change(enable_change) { </td> </tr> <tr> + <td colspan="2" valign="top" class="listtopic">Recalculate certificate chain.</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"> </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();" />(Other changes on this page will be lost) + <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">Configuration synchronization</td> </tr> <tr> diff --git a/config/haproxy-devel/haproxy_htmllist.inc b/config/haproxy-devel/haproxy_htmllist.inc index 2e93ca2a..ae46ffd4 100644 --- a/config/haproxy-devel/haproxy_htmllist.inc +++ b/config/haproxy-devel/haproxy_htmllist.inc @@ -1,7 +1,7 @@ <?php /* haproxy_htmllist.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2013 PiBa-NL All rights reserved. diff --git a/config/haproxy-devel/haproxy_listeners.php b/config/haproxy-devel/haproxy_listeners.php index 3ff53cea..7022ec34 100644 --- a/config/haproxy-devel/haproxy_listeners.php +++ b/config/haproxy-devel/haproxy_listeners.php @@ -2,7 +2,7 @@ /* $Id: load_balancer_virtual_server.php,v 1.6.2.1 2006/01/02 23:46:24 sullrich Exp $ */ /* haproxy_listeners.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2013 PiBa-NL Copyright (C) 2009 Scott Ullrich <sullrich@pfsense.com> Copyright (C) 2008 Remco Hoef <remcoverhoef@pfsense.com> @@ -34,6 +34,7 @@ require_once("guiconfig.inc"); require_once("haproxy.inc"); require_once("certs.inc"); require_once("haproxy_utils.inc"); +require_once("pkg_haproxy_tabs.inc"); if (!is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { $config['installedpackages']['haproxy']['ha_backends']['item'] = array(); @@ -69,8 +70,8 @@ if ($_GET['act'] == "del") { } } -$pfSversion = str_replace("\n", "", file_get_contents("/etc/version")); -if(strstr($pfSversion, "1.2")) +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); +if ($pf_version < 2.0) $one_two = true; $pgtitle = "Services: HAProxy: Frontends"; @@ -91,12 +92,7 @@ include("head.inc"); <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr><td class="tabnavtbl"> <?php - /* active tabs */ - $tab_array = array(); - $tab_array[] = array("Settings", false, "haproxy_global.php"); - $tab_array[] = array("Frontend", true, "haproxy_listeners.php"); - $tab_array[] = array("Backend", false, "haproxy_pools.php"); - display_top_tabs($tab_array); + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "frontend"); ?> </td></tr> <tr> @@ -127,8 +123,10 @@ include("head.inc"); $a_frontend_grouped = array(); foreach($a_frontend as &$frontend2) { + $mainfrontend = get_primaryfrontend($frontend2); $ipport = get_frontend_ipport($frontend2, true); $frontend2['ipport'] = $ipport; + $frontend2['type'] = $mainfrontend['type']; $a_frontend_grouped[$ipport][] = $frontend2; } ksort($a_frontend_grouped); @@ -171,29 +169,27 @@ include("head.inc"); $acls = get_frontend_acls($frontend); $isaclset = ""; foreach ($acls as $acl) { - $isaclset .= " " . $acl['descr']; + $isaclset .= " " . htmlspecialchars($acl['descr']); } - if ($frontend['ssloffloadacl']) - $isaclset .= " " . "Certificate ACL"; - if ($frontend['ssloffloadacladditional']) - $isaclset .= " " . "Additional certificate ACLs"; if ($isaclset) echo "<img src=\"$img_acl\" title=\"" . gettext("acl's used") . ": {$isaclset}\" border=\"0\" />"; $isadvset = ""; - if ($frontend['advanced_bind']) $isadvset .= "Advanced bind: {$frontend['advanced_bind']}\r\n"; + if ($frontend['advanced_bind']) $isadvset .= "Advanced bind: ".htmlspecialchars($frontend['advanced_bind'])."\r\n"; if ($frontend['advanced']) $isadvset .= "Advanced pass thru setting used\r\n"; if ($isadvset) echo "<img src=\"$img_adv\" title=\"" . gettext("Advanced settings set") . ": {$isadvset}\" border=\"0\" />"; $backend_serverpool = $frontend['backend_serverpool']; $backend = get_backend($backend_serverpool ); - $servers = $backend['ha_servers']['item']; - $backend_serverpool_hint = gettext("Servers in pool:"); - if (is_array($servers)){ - foreach($servers as $server){ - $backend_serverpool_hint .= "\n".$server['address'].":".$server['port']; + if ($backend && is_array($backend['ha_servers']['item'])){ + $servers = $backend['ha_servers']['item']; + $backend_serverpool_hint = gettext("Servers in pool:"); + if (is_array($servers)){ + foreach($servers as $server){ + $backend_serverpool_hint .= "\n".$server['address'].":".$server['port']; + } } } ?> diff --git a/config/haproxy-devel/haproxy_listeners_edit.php b/config/haproxy-devel/haproxy_listeners_edit.php index 3c62ec3a..6731731d 100644 --- a/config/haproxy-devel/haproxy_listeners_edit.php +++ b/config/haproxy-devel/haproxy_listeners_edit.php @@ -2,7 +2,7 @@ /* $Id: load_balancer_pool_edit.php,v 1.24.2.23 2007/03/03 00:07:09 smos Exp $ */ /* haproxy_listeners_edit.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2009 Scott Ullrich <sullrich@pfsense.com> Copyright (C) 2008 Remco Hoef <remcoverhoef@pfsense.com> Copyright (C) 2013 PiBa-NL merging (some of the) "haproxy-devel" changes from: Marcello Coutinho <marcellocoutinho@gmail.com> @@ -34,6 +34,7 @@ require("guiconfig.inc"); require_once("haproxy.inc"); require_once("haproxy_utils.inc"); require_once("haproxy_htmllist.inc"); +require_once("pkg_haproxy_tabs.inc"); /* Compatibility function for pfSense 2.0 */ if (!function_exists("cert_get_purpose")) { @@ -56,8 +57,6 @@ function haproxy_js_acl_select($mode) { return $seltext; } -$d_haproxyconfdirty_path = $g['varrun_path'] . "/haproxy.conf.dirty"; - if (!is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { $config['installedpackages']['haproxy']['ha_backends']['item'] = array(); } @@ -79,6 +78,12 @@ if (isset($_GET['dup'])) $id = get_frontend_id($id); +if (!is_numeric($id)) +{ + //default value for new items. + $pconfig['ssloffloadacl'] = "yes"; +} + $servercerts = get_certificates_server(); $fields_sslCertificates=array(); @@ -150,8 +155,8 @@ if ($_POST) { $ports = split(",", $_POST['port'] . ","); foreach($ports as $port) - if ($port && !is_numeric($port)) - $input_errors[] = "The field 'Port' value is not a number."; + if ($port && !is_numeric($port) && !is_portoralias($port)) + $input_errors[] = "The field 'Port' value '".htmlspecialchars($port)."' is not a number or alias thereof."; if ($_POST['client_timeout'] !== "" && !is_numeric($_POST['client_timeout'])) $input_errors[] = "The field 'Client timeout' value is not a number."; @@ -222,21 +227,17 @@ if ($_POST) { } } -$pfSversion = str_replace("\n", "", file_get_contents("/etc/version")); -if(strstr($pfSversion, "1.2")) +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); +if ($pf_version < 2.0) $one_two = true; -if (!$id) -{ - //default value for new items. - $pconfig['ssloffloadacl'] = "yes"; -} - $closehead = false; $pgtitle = "HAProxy: Frontend: Edit"; include("head.inc"); -$primaryfrontends = get_haproxy_frontends($pconfig['name']); +if (!isset($_GET['dup'])) + $excludefrontend = $pconfig['name']; +$primaryfrontends = get_haproxy_frontends($excludefrontend); $interfaces = haproxy_get_bindable_interfaces(); ?> @@ -246,6 +247,8 @@ $interfaces = haproxy_get_bindable_interfaces(); .haproxy_primary{} .haproxy_secondary{display:none;} </style> + <script type="text/javascript" src="/javascript/suggestions.js"></script> + <script type="text/javascript" src="/javascript/autosuggest.js"></script> </head> <body link="#0000CC" vlink="#0000CC" alink="#0000CC"> @@ -254,12 +257,18 @@ $interfaces = haproxy_get_bindable_interfaces(); <script type="text/javascript" src="/javascript/scriptaculous/scriptaculous.js"></script> <?php endif; ?> - <script type="text/javascript"> function htmllist_get_select_options(tableId) { var seltext; seltext = ""; - var type = d.getElementById("type").value; + var type; + var secondary = d.getElementById("secondary"); + var primary_frontend = d.getElementById("primary_frontend"); + if ((secondary !== null) && (secondary.checked)) + type = primaryfrontends[primary_frontend.value]['ref']['type']; + else + type = d.getElementById("type").value; + if (tableId == 'tableA_acltable'){ if (type == 'health') seltext = "<?php echo haproxy_js_acl_select('health');?>"; @@ -295,10 +304,10 @@ $interfaces = haproxy_get_bindable_interfaces(); function updatevisibility() { d = document; ssloffload = d.getElementById("ssloffload"); - type = d.getElementById("type"); - secondary = d.getElementById("secondary"); - primary_frontend = d.getElementById("primary_frontend"); + var type; + var secondary = d.getElementById("secondary"); + var primary_frontend = d.getElementById("primary_frontend"); if ((secondary !== null) && (secondary.checked)) type = primaryfrontends[primary_frontend.value]['ref']['type']; else @@ -373,12 +382,7 @@ $interfaces = haproxy_get_bindable_interfaces(); <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr><td class="tabnavtbl"> <?php - /* active tabs */ - $tab_array = array(); - $tab_array[] = array("Settings", false, "haproxy_global.php"); - $tab_array[] = array("Frontend", true, "haproxy_listeners.php"); - $tab_array[] = array("Backend", false, "haproxy_pools.php"); - display_top_tabs($tab_array); + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "frontend"); ?> </td></tr> <tr> @@ -448,8 +452,8 @@ $interfaces = haproxy_get_bindable_interfaces(); <tr class="haproxy_primary" align="left"> <td width="22%" valign="top" class="vncellreq">External port</td> <td width="78%" class="vtable" colspan="2"> - <input name="port" type="text" <?if(isset($pconfig['port'])) echo "value=\"{$pconfig['port']}\"";?> size="10" maxlength="500" /> - <div>The port to listen to. To specify multiple ports, separate with a comma (,). EXAMPLE: 80,443</div> + <input name="port" id="port" type="text" <?if(isset($pconfig['port'])) echo "value=\"{$pconfig['port']}\"";?> size="10" maxlength="500" /> + <div>The port to listen to. To specify multiple ports, separate with a comma (,). EXAMPLE: 80,8000</div> </td> </tr> <tr class="haproxy_primary" align="left"> @@ -550,7 +554,8 @@ $interfaces = haproxy_get_bindable_interfaces(); <tr align="left"> <td width="22%" valign="top" class="vncell">Advanced pass thru</td> <td width="78%" class="vtable" colspan="2"> - <textarea name='advanced' rows="4" cols="70" id='advanced'><?php echo htmlspecialchars($pconfig['advanced']); ?></textarea> + <? $textrowcount = max(substr_count($pconfig['advanced'],"\n"), 2) + 2; ?> + <textarea name='advanced' rows="<?=$textrowcount;?>" cols="70" id='advanced'><?php echo htmlspecialchars($pconfig['advanced']); ?></textarea> <br/> NOTE: paste text into this box that you would like to pass thru. </td> @@ -601,7 +606,7 @@ $interfaces = haproxy_get_bindable_interfaces(); <tr class="haproxy_ssloffloading_enabled haproxy_primary" align="left"> <td width="22%" valign="top" class="vncell">Advanced ssl options</td> <td width="78%" class="vtable" colspan="2"> - <input type='text' name='dcertadv' size="64" id='dcertadv' <?if(isset($pconfig['dcertadv'])) echo "value=\"{$pconfig['dcertadv']}\"";?> maxlength="64" /> + <input type='text' name='dcertadv' size="64" id='dcertadv' <?if(isset($pconfig['dcertadv'])) echo 'value="'.htmlspecialchars($pconfig['dcertadv']).'"';?> /> <br/> NOTE: Paste additional ssl options(without commas) to include on ssl listening options.<br/> some options: force-sslv3, force-tlsv10 force-tlsv11 force-tlsv12 no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets @@ -642,6 +647,9 @@ $interfaces = haproxy_get_bindable_interfaces(); <script type="text/javascript"> totalrows = <?php echo $counter; ?>; updatevisibility(); + + var customarray = <?= json_encode(get_alias_list(array("port", "url_ports", "urltable_ports"))) ?>; + var oTextbox1 = new AutoSuggestControl(document.getElementById("port"), new StateSuggestions(customarray)); </script> <?php haproxy_htmllist_js(); diff --git a/config/haproxy-devel/haproxy_pool_edit.php b/config/haproxy-devel/haproxy_pool_edit.php index 93fa20dc..49eb4271 100644 --- a/config/haproxy-devel/haproxy_pool_edit.php +++ b/config/haproxy-devel/haproxy_pool_edit.php @@ -2,7 +2,7 @@ /* $Id: load_balancer_pool_edit.php,v 1.24.2.23 2007/03/03 00:07:09 smos Exp $ */ /* haproxy_pool_edit.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2013 PiBa-NL Copyright (C) 2009 Scott Ullrich <sullrich@pfsense.com> Copyright (C) 2008 Remco Hoef <remcoverhoef@pfsense.com> @@ -34,8 +34,7 @@ require("guiconfig.inc"); require_once("haproxy.inc"); require_once("haproxy_utils.inc"); require_once("haproxy_htmllist.inc"); - -$d_haproxyconfdirty_path = $g['varrun_path'] . "/haproxy.conf.dirty"; +require_once("pkg_haproxy_tabs.inc"); if (!is_array($config['installedpackages']['haproxy']['ha_pools']['item'])) { $config['installedpackages']['haproxy']['ha_pools']['item'] = array(); @@ -53,49 +52,58 @@ if (isset($_GET['dup'])) global $simplefields; $simplefields = array( -"name","cookie","balance","transparent_clientip","transparent_interface", +"name","balance","transparent_clientip","transparent_interface", "check_type","checkinter","httpcheck_method","monitor_uri","monitor_httpversion","monitor_username","monitor_domain","monitor_agentport", "agent_check","agent_port","agent_inter", "connection_timeout","server_timeout","retries", -"stats_enabled","stats_username","stats_password","stats_uri","stats_realm","stats_admin","stats_node_enabled","stats_node","stats_desc","stats_refresh"); +"stats_enabled","stats_username","stats_password","stats_uri","stats_scope","stats_realm","stats_admin","stats_node","stats_desc","stats_refresh", +"persist_stick_expire","persist_stick_tablesize","persist_stick_length","persist_stick_cookiename","persist_sticky_type", +"persist_cookie_enabled","persist_cookie_name","persist_cookie_mode","persist_cookie_cachable", +"strict_transport_security" +); $fields_servers=array(); -$fields_servers[0]['name']="name"; -$fields_servers[0]['columnheader']="Name"; -$fields_servers[0]['colwidth']="20%"; -$fields_servers[0]['type']="textbox"; -$fields_servers[0]['size']="30"; -$fields_servers[1]['name']="address"; -$fields_servers[1]['columnheader']="Address"; -$fields_servers[1]['colwidth']="10%"; +$fields_servers[0]['name']="status"; +$fields_servers[0]['columnheader']="Mode"; +$fields_servers[0]['colwidth']="5%"; +$fields_servers[0]['type']="select"; +$fields_servers[0]['size']="5"; +$fields_servers[0]['items']=&$a_servermodes; +$fields_servers[1]['name']="name"; +$fields_servers[1]['columnheader']="Name"; +$fields_servers[1]['colwidth']="20%"; $fields_servers[1]['type']="textbox"; -$fields_servers[1]['size']="20"; -$fields_servers[2]['name']="port"; -$fields_servers[2]['columnheader']="Port"; -$fields_servers[2]['colwidth']="5%"; +$fields_servers[1]['size']="30"; +$fields_servers[2]['name']="address"; +$fields_servers[2]['columnheader']="Address"; +$fields_servers[2]['colwidth']="10%"; $fields_servers[2]['type']="textbox"; -$fields_servers[2]['size']="5"; -$fields_servers[3]['name']="ssl"; -$fields_servers[3]['columnheader']="SSL"; +$fields_servers[2]['size']="20"; +$fields_servers[3]['name']="port"; +$fields_servers[3]['columnheader']="Port"; $fields_servers[3]['colwidth']="5%"; -$fields_servers[3]['type']="checkbox"; -$fields_servers[3]['size']="30"; -$fields_servers[4]['name']="weight"; -$fields_servers[4]['columnheader']="Weight"; -$fields_servers[4]['colwidth']="8%"; -$fields_servers[4]['type']="textbox"; -$fields_servers[4]['size']="5"; -$fields_servers[5]['name']="status"; -$fields_servers[5]['columnheader']="Mode"; -$fields_servers[5]['colwidth']="5%"; -$fields_servers[5]['type']="select"; +$fields_servers[3]['type']="textbox"; +$fields_servers[3]['size']="5"; +$fields_servers[4]['name']="ssl"; +$fields_servers[4]['columnheader']="SSL"; +$fields_servers[4]['colwidth']="5%"; +$fields_servers[4]['type']="checkbox"; +$fields_servers[4]['size']="30"; +$fields_servers[5]['name']="weight"; +$fields_servers[5]['columnheader']="Weight"; +$fields_servers[5]['colwidth']="8%"; +$fields_servers[5]['type']="textbox"; $fields_servers[5]['size']="5"; -$fields_servers[5]['items']=&$a_servermodes; -$fields_servers[6]['name']="advanced"; -$fields_servers[6]['columnheader']="Advanced"; -$fields_servers[6]['colwidth']="15%"; +$fields_servers[6]['name']="cookie"; +$fields_servers[6]['columnheader']="Cookie"; +$fields_servers[6]['colwidth']="10%"; $fields_servers[6]['type']="textbox"; -$fields_servers[6]['size']="20"; +$fields_servers[6]['size']="10"; +$fields_servers[7]['name']="advanced"; +$fields_servers[7]['columnheader']="Advanced"; +$fields_servers[7]['colwidth']="15%"; +$fields_servers[7]['type']="textbox"; +$fields_servers[7]['size']="20"; if (isset($id) && $a_pools[$id]) { $pconfig['advanced'] = base64_decode($a_pools[$id]['advanced']); @@ -123,9 +131,14 @@ if ($_POST) { do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); if ($_POST['stats_enabled']) { - $reqdfields = explode(" ", "name stats_username stats_password stats_uri stats_realm"); - $reqdfieldsn = explode(",", "Name,Stats Username,Stats Password,Stats Uri,Stats Realm"); + $reqdfields = explode(" ", "name stats_uri"); + $reqdfieldsn = explode(",", "Name,Stats Uri"); do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); + if ($_POST['stats_username']) { + $reqdfields = explode(" ", "stats_password stats_realm"); + $reqdfieldsn = explode(",", "Stats Password,Stats Realm"); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); + } } if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['name'])) @@ -151,6 +164,9 @@ if ($_POST) { if (preg_match("/[^a-zA-Z0-9!-~ ]/", $_POST['stats_password'])) $input_errors[] = "The field 'Stats Password' contains invalid characters."; + if (preg_match("/[^a-zA-Z0-9\-_]/", $_POST['stats_node'])) + $input_errors[] = "The field 'Stats Node' contains invalid characters. Should be a string with digits(0-9), letters(A-Z, a-z), hyphen(-) or underscode(_)"; + /* Ensure that our pool names are unique */ for ($i=0; isset($config['installedpackages']['haproxy']['ha_pools']['item'][$i]); $i++) if (($_POST['name'] == $config['installedpackages']['haproxy']['ha_pools']['item'][$i]['name']) && ($i != $id)) @@ -162,23 +178,25 @@ if ($_POST) { $server_address = $server['address']; $server_port = $server['port']; $server_weight = $server['weight']; + if (preg_match("/[^a-zA-Z0-9\.\-_]/", $server_name)) $input_errors[] = "The field 'Name' contains invalid characters."; - if (!is_ipaddr($server_address)) - $input_errors[] = "The field 'Address' is not a valid ip address."; + + if (!is_ipaddr($server_address) && !is_hostname($server_address)) + $input_errors[] = "The field 'Address' is not a valid ip address or hostname."; if (!preg_match("/.{2,}/", $server_name)) $input_errors[] = "The field 'Name' is required (and must be at least 2 characters)."; - if (!preg_match("/.{2,}/", $server_address)) - $input_errors[] = "The field 'Address' is required (and must be at least 2 characters)."; - - if (!is_numeric($server_weight)) + if ($server_weight && !is_numeric($server_weight)) $input_errors[] = "The field 'Weight' value is not a number."; if ($server_port && !is_numeric($server_port)) $input_errors[] = "The field 'Port' value is not a number."; } + + if ($_POST['strict_transport_security'] !== "" && !is_numeric($_POST['strict_transport_security'])) + $input_errors[] = "The field 'Strict-Transport-Security' is not empty or a number."; if (!$input_errors) { $pool = array(); @@ -203,12 +221,8 @@ if ($_POST) { $pool['ha_servers']['item']=$a_servers; - update_if_changed("name", $pool['name'], $_POST['name']); - update_if_changed("cookie", $pool['cookie'], $_POST['cookie']); update_if_changed("advanced", $pool['advanced'], base64_encode($_POST['advanced'])); update_if_changed("advanced_backend", $pool['advanced_backend'], base64_encode($_POST['advanced_backend'])); - update_if_changed("checkinter", $pool['checkinter'], $_POST['checkinter']); - update_if_changed("monitor_uri", $pool['monitor_uri'], $_POST['monitor_uri']); global $simplefields; foreach($simplefields as $stat) @@ -236,8 +250,8 @@ if ($_POST) { $pconfig['a_servers']=&$a_pools[$id]['ha_servers']['item']; } -$pfSversion = str_replace("\n", "", file_get_contents("/etc/version")); -if(strstr($pfSversion, "1.2")) +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); +if ($pf_version < 2.0) $one_two = true; $closehead = false; @@ -259,6 +273,10 @@ foreach($simplefields as $field){ .haproxy_transparent_clientip{display:none;} .haproxy_check_agent{display:none;} .haproxy_agent_check{display:none;} + .haproxy_stick_cookiename{display:none;} + .haproxy_stick_tableused{display:none;} + .haproxy_cookie_visible{display:none;} + .haproxy_help_serverlist{display:none;} </style> </head> <body link="#0000CC" vlink="#0000CC" alink="#0000CC"> @@ -286,15 +304,35 @@ foreach($simplefields as $field){ } } } + function toggleCSSdisplay(cssID) + { + var ss = document.styleSheets; + for (var i=0; i<ss.length; i++) { + var rules = ss[i].cssRules || ss[i].rules; + for (var j=0; j<rules.length; j++) { + if (rules[j].selectorText === cssID) { + rules[j].style.display = rules[j].style.display == "none" ? "" : "none"; + } + } + } + } function updatevisibility() { d = document; setCSSdisplay(".haproxy_stats_visible", stats_enabled.checked); + setCSSdisplay(".haproxy_cookie_visible", persist_cookie_enabled.checked); check_type = d.getElementById("check_type").value; check_type_description = d.getElementById("check_type_description"); check_type_description.innerHTML=checktypes[check_type]["descr"]; + + persist_cookie_mode = d.getElementById("persist_cookie_mode").value; + persist_cookie_mode_description = d.getElementById("persist_cookie_mode_description"); + persist_cookie_mode_description.innerHTML=cookiemode[persist_cookie_mode]["descr"]; + persist_cookie_mode_description.setAttribute('style','padding:5px; border:1px dashed #990000; background-color: #ffffff; color: #000000; font-size: 8pt; height:30px'); + persist_cookie_mode_description.setAttribute('style','padding:5px; border:1px dashed #990000; background-color: #ffffff; color: #000000; font-size: 8pt; height:'+persist_cookie_mode_description.scrollHeight+'px'); + setCSSdisplay(".haproxy_check_enabled", check_type != 'none'); setCSSdisplay(".haproxy_check_http", check_type == 'HTTP'); setCSSdisplay(".haproxy_check_username", check_type == 'MySQL' || check_type == 'PostgreSQL'); @@ -306,6 +344,16 @@ foreach($simplefields as $field){ transparent_clientip = d.getElementById("transparent_clientip"); setCSSdisplay(".haproxy_transparent_clientip", transparent_clientip.checked); + + persist_sticky_type = d.getElementById("persist_sticky_type").value; + setCSSdisplay(".haproxy_stick_tableused", persist_sticky_type != 'none'); + setCSSdisplay(".haproxy_stick_cookiename", persist_sticky_type == 'stick_rdp_cookie' || persist_sticky_type == 'stick_cookie_value'); + + cookie_example = sticky_type[persist_sticky_type]['cookiedescr']; + stick_cookiename_description = d.getElementById("stick_cookiename_description"); + stick_cookiename_description.innerHTML = cookie_example; + sticky_type_description.innerHTML = sticky_type[persist_sticky_type]['descr']; + monitor_username = d.getElementById("monitor_username"); sqlcheckusername = d.getElementById("sqlcheckusername"); if(!browser_InnerText_support){ @@ -325,12 +373,7 @@ foreach($simplefields as $field){ <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr><td class="tabnavtbl"> <?php - /* active tabs */ - $tab_array = array(); - $tab_array[] = array("Settings", false, "haproxy_global.php"); - $tab_array[] = array("Frontend", false, "haproxy_listeners.php"); - $tab_array[] = array("Backend", true, "haproxy_pools.php"); - display_top_tabs($tab_array); + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "backend"); ?> </td></tr> <tr> @@ -347,25 +390,37 @@ foreach($simplefields as $field){ </td> </tr> <tr align="left"> - <td width="22%" valign="top" class="vncell">Cookie</td> - <td width="78%" class="vtable" colspan="2"> - <input name="cookie" type="text" <?if(isset($pconfig['cookie'])) echo "value=\"{$pconfig['cookie']}\"";?>size="64" /><br/> - This value will be checked in incoming requests, and the first - operational pool possessing the same value will be selected. In return, in - cookie insertion or rewrite modes, this value will be assigned to the cookie - sent to the client. There is nothing wrong in having several servers sharing - the same cookie value, and it is in fact somewhat common between normal and - backup servers. See also the "cookie" keyword in backend section. - - </td> - </tr> - <tr align="left"> <td class="vncell" colspan="3"><strong>Server list</strong> + <span style="float:right;"> + Toggle serverlist help. <a onclick="toggleCSSdisplay('.haproxy_help_serverlist');" title="<?php echo gettext("Help"); ?>"><img style="vertical-align:middle" src="/themes/<?php echo $g['theme']; ?>/images/icons/icon_help.gif" border="0" alt="help" /></a> + </span> <? $counter=0; $a_servers = $pconfig['a_servers']; haproxy_htmllist("tableA_servers", $a_servers, $fields_servers); ?> + <table class="haproxy_help_serverlist" style="border:1px dashed green" cellspacing="0"> + <tr><td class="vncell"> + Mode: </td><td class="vncell">Active: server will be used normally<br/> + Backup: server is only used in load balancing when all other non-backup servers are unavailable<br/> + Disabled: server is marked down in maintenance mode<br/> + Inactive: server will not be available for use + </td></tr><tr><td class="vncell"> + Name: </td><td class="vncell">Used to as a name for the server in for example the stats<br/>EXAMPLE: MyWebServer + </td></tr><tr><td class="vncell"> + Address: </td><td class="vncell">IP or hostname(only resolved on start-up.)<br/>EXAMPLE: 192.168.1.22 , fe80::1000:2000:3000:4000%em0 , WebServer1.localdomain + </td></tr><tr><td class="vncell"> + Port: </td><td class="vncell">The port of the backend.<br/>EXAMPLE: 80 or 443<br/> + </td></tr><tr><td class="vncell"> + SSL: </td><td class="vncell">Is the backend using SSL (commonly with port 443)<br/> + </td></tr><tr><td class="vncell"> + Weight: </td><td class="vncell">A weight between 0 and 256, this setting can be used when multiple servers on different hardware need to be balanced with with a different part the traffic. A server with weight 0 wont get new traffic. Default if empty: 1 + </td></tr><tr><td class="vncell"> + Cookie: </td><td class="vncell">the value of the cookie used to identify a server (only when cookie-persistence is enabled below) + </td></tr><tr><td class="vncell"> + Advanced: </td><td class="vncell">More advanced settings like rise,fall,error-limit,send-proxy and others can be configured here.<br/>For a full list of options see the <a target="_blank" href="http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.2">HAProxy manual: Server and default-server options</a> + </td></tr> + </table> </td> </tr> <tr align="left"> @@ -437,6 +492,7 @@ foreach($simplefields as $field){ <tr align="left"> <td width="22%" valign="top" class="vncell">Transparent ClientIP</td> <td width="78%" class="vtable" colspan="2"> + WARNING Activating this option will load rules in IPFW and might interfere with CaptivePortal and possibly other services due to the way server return traffic must be 'captured' with a automatically created fwd rule. This also breaks directly accessing the (web)server on the ports configured above. Also a automatic sloppy pf rule is made to allow HAProxy to server traffic.<br/> <input id="transparent_clientip" name="transparent_clientip" type="checkbox" value="yes" <?php if ($pconfig['transparent_clientip']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> Use Client-IP to connect to backend servers. <div class="haproxy_transparent_clientip"> @@ -457,13 +513,13 @@ foreach($simplefields as $field){ For proper workings this requires the reply's traffic to pass through pfSense by means of correct routing. (uses the option "source 0.0.0.0 usesrc clientip") <br/><br/> - Note : When this is enabled for a single backend HAProxy will run as 'root', which reduces security. + Note : When this is enabled for a single backend HAProxy will run as 'root' instead of chrooting to a lower privileged user, this reduces security in case of a a bit. </td> </tr> <tr align="left"> <td width="22%" valign="top" class="vncell">Per server pass thru</td> <td width="78%" class="vtable" colspan="2"> - <input type="text" name='advanced' id='advanced' value='<?php echo $pconfig['advanced']; ?>' size="64" /> + <input type="text" name='advanced' id='advanced' value='<?php echo htmlspecialchars($pconfig['advanced']); ?>' size="64" /> <br/> NOTE: paste text into this box that you would like to pass thru. Applied to each 'server' line. </td> @@ -472,7 +528,8 @@ foreach($simplefields as $field){ <tr align="left"> <td width="22%" valign="top" class="vncell">Backend pass thru</td> <td width="78%" class="vtable" colspan="2"> - <textarea rows="4" cols="70" name='advanced_backend' id='advanced_backend'><?php echo $pconfig['advanced_backend']; ?></textarea> + <? $textrowcount = max(substr_count($pconfig['advanced_backend'],"\n"), 2) + 2; ?> + <textarea rows="<?=$textrowcount;?>" cols="70" name='advanced_backend' id='advanced_backend'><?php echo htmlspecialchars($pconfig['advanced_backend']); ?></textarea> <br/> NOTE: paste text into this box that you would like to pass thru. Applied to the backend section. </td> @@ -608,71 +665,167 @@ set by the 'retries' parameter.</div> </tr> <tr><td> </td></tr> <tr> - <td colspan="2" valign="top" class="listtopic">Statistics</td> + <td colspan="2" valign="top" class="listtopic">Cookie persistence</td> </tr> <tr align="left"> - <td width="22%" valign="top" class="vncell">Stats Enabled</td> + <td width="22%" valign="top" class="vncell">Cookie Enabled</td> <td width="78%" class="vtable" colspan="2"> - <input id="stats_enabled" name="stats_enabled" type="checkbox" value="yes" <?php if ($pconfig['stats_enabled']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + <input id="persist_cookie_enabled" name="persist_cookie_enabled" type="checkbox" value="yes" <?php if ($pconfig['persist_cookie_enabled']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + Enables cookie based persistence. (only used on 'http' frontends) </td> </tr> - <tr class="haproxy_stats_visible" align="left" id='stats_realm_row'> - <td width="22%" valign="top" class="vncellreq">Stats Realm</td> + <tr class="haproxy_cookie_visible" align="left"> + <td width="22%" valign="top" class="vncellreq">Server Cookies</td> <td width="78%" class="vtable" colspan="2"> - <input id="stats_realm" name="stats_realm" type="text" <?if(isset($pconfig['stats_realm'])) echo "value=\"{$pconfig['stats_realm']}\"";?> size="64" /><br/> - EXAMPLE: haproxystats + <b>Make sure to configure a different cookie on every server in this backend.<b/> + </td> + </tr> + <tr class="haproxy_cookie_visible" align="left"> + <td width="22%" valign="top" class="vncellreq">Cookie Name</td> + <td width="78%" class="vtable" colspan="2"> + <input id="persist_cookie_name" name="persist_cookie_name" type="text" <?if(isset($pconfig['persist_cookie_name'])) echo "value=\"{$pconfig['persist_cookie_name']}\"";?> size="64" /><br/> + The string name to track in Set-Cookie and Cookie HTTP headers.<br/> + EXAMPLE: MyLoadBalanceCookie JSESSIONID PHPSESSIONID ASP.NET_SessionId + </td> + </tr> + <tr class="haproxy_cookie_visible" align="left"> + <td width="22%" valign="top" class="vncellreq">Cookie Mode</td> + <td width="78%" class="vtable" colspan="2"> + <? + echo_html_select("persist_cookie_mode",$a_cookiemode,$pconfig['persist_cookie_mode'],"","updatevisibility();"); + ?> + Determines how HAProxy inserts/prefixes/replaces or examines cookie and set-cookie headers.<br/> + EXAMPLE: with an existing PHPSESSIONID you can for example use "Session-prefix" or to create a new cookie use "Insert-silent".<br/> + <br/> + <textarea readonly="yes" cols="60" rows="2" id="persist_cookie_mode_description" name="persist_cookie_mode_description" style="padding:5px; border:1px dashed #990000; background-color: #ffffff; color: #000000; font-size: 8pt;"></textarea> + </td> + </tr> + <tr class="haproxy_cookie_visible" align="left"> + <td width="22%" valign="top" class="vncell">Cookie Cachable</td> + <td width="78%" class="vtable" colspan="2"> + <input id="persist_cookie_cachable" name="persist_cookie_cachable" type="checkbox" value="yes" <?php if ($pconfig['persist_cookie_cachable']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + Allows shared caches to cache the server response. + </td> + </tr> + <tr><td> </td></tr> + <tr> + <td colspan="2" valign="top" class="listtopic">Stick-table persistence</td> + </tr> + <tr><td class="vncell"></td><td class="vncell">These options are used to make sure seperate requests from a single client go to the same backend. This can be required for servers that keep track of for example a shopping cart.</td></tr> + <tr align="left"> + <td width="22%" valign="top" class="vncell">Stick tables</td> + <td width="78%" class="vtable" colspan="2"> + <? + echo_html_select("persist_sticky_type",$a_sticky_type,$pconfig['persist_sticky_type'],"","updatevisibility();"); + ?> + Sticktables that are kept in memory, and when matched make sure the same server will be used.<br/> + <textarea readonly="yes" cols="60" rows="2" id="sticky_type_description" name="sticky_type_description" style="padding:5px; border:1px dashed #990000; background-color: #ffffff; color: #000000; font-size: 8pt;"></textarea> + </td> + </tr> + <tr align="left" class="haproxy_stick_cookiename"> + <td width="22%" valign="top" class="vncellreq">Stick cookie name</td> + <td width="78%" class="vtable" colspan="2"> + <input name="persist_stick_cookiename" type="text" <?if(isset($pconfig['persist_stick_cookiename'])) echo "value=\"{$pconfig['persist_stick_cookiename']}\"";?> size="20" /> + Cookiename to use for sticktable<br/> + <span id="stick_cookiename_description"></span> + </td> + </tr> + <tr align="left" class="haproxy_stick_cookiename"> + <td width="22%" valign="top" class="vncellreq">Stick cookie length</td> + <td width="78%" class="vtable" colspan="2"> + <input name="persist_stick_length" type="text" <?if(isset($pconfig['persist_stick_length'])) echo "value=\"{$pconfig['persist_stick_length']}\"";?> size="20" /> + The maximum number of characters that will be stored in a "string" type stick-table<br/> + <span id="stick_cookiename_description"></span> + </td> + </tr> + <tr align="left" class="haproxy_stick_tableused"> + <td width="22%" valign="top" class="vncellreq">stick-table expire</td> + <td width="78%" class="vtable" colspan="2"> + <input name="persist_stick_expire" type="text" <?if(isset($pconfig['persist_stick_expire'])) echo "value=\"{$pconfig['persist_stick_expire']}\"";?> size="20" /> d=days h=hour m=minute s=seconds ms=miliseconds(default)<br/> + Defines the maximum duration of an entry in the stick-table since it was last created, refreshed or matched.<br/> + EXAMPLE: 30m + </td> + </tr> + <tr align="left" class="haproxy_stick_tableused"> + <td width="22%" valign="top" class="vncellreq">stick-table size</td> + <td width="78%" class="vtable" colspan="2"> + <input name="persist_stick_tablesize" type="text" <?if(isset($pconfig['persist_stick_tablesize'])) echo "value=\"{$pconfig['persist_stick_tablesize']}\"";?> size="20" /> maximum number of entries supports suffixes "k", "m", "g" for 2^10, 2^20 and 2^30 factors.<br/> + Is the maximum number of entries that can fit in the table. This value directly impacts memory usage. Count approximately + 50 bytes per entry, plus the size of a string if any.<br/> + EXAMPLE: 50k + </td> + </tr> + <tr><td> </td></tr> + <tr> + <td colspan="2" valign="top" class="listtopic">Statistics</td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncell">Stats Enabled</td> + <td width="78%" class="vtable" colspan="2"> + <input id="stats_enabled" name="stats_enabled" type="checkbox" value="yes" <?php if ($pconfig['stats_enabled']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_uri_row'> <td width="22%" valign="top" class="vncellreq">Stats Uri</td> <td width="78%" class="vtable" colspan="2"> <input id="stats_uri" name="stats_uri" type="text" <?if(isset($pconfig['stats_uri'])) echo "value=\"{$pconfig['stats_uri']}\"";?> size="64" /><br/> - EXAMPLE: /haproxy?stats + This url can be used when this same backend is used for passing connections to backends<br/> + EXAMPLE: / or /haproxy?stats + </td> + </tr> + <tr class="haproxy_stats_visible" align="left" id='stats_scope_row'> + <td width="22%" valign="top" class="vncell">Stats Scope</td> + <td width="78%" class="vtable" colspan="2"> + <input id="stats_scope" name="stats_scope" type="text" <?if(isset($pconfig['stats_scope'])) echo "value=\"{$pconfig['stats_scope']}\"";?> size="64" /><br/> + Determines which frontends and backends are shown, leave empty to show all.<br/> + EXAMPLE: frontendA,backend1,backend2 + </td> + </tr> + <tr class="haproxy_stats_visible" align="left" id='stats_realm_row'> + <td width="22%" valign="top" class="vncell">Stats Realm</td> + <td width="78%" class="vtable" colspan="2"> + <input id="stats_realm" name="stats_realm" type="text" <?if(isset($pconfig['stats_realm'])) echo "value=\"{$pconfig['stats_realm']}\"";?> size="64" /><br/> + The realm is shown when authentication is requested by haproxy.<br/> + EXAMPLE: haproxystats </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_username_row'> - <td width="22%" valign="top" class="vncellreq">Stats Username</td> + <td width="22%" valign="top" class="vncell">Stats Username</td> <td width="78%" class="vtable" colspan="2"> <input id="stats_username" name="stats_username" type="text" <?if(isset($pconfig['stats_username'])) echo "value=\"".$pconfig['stats_username']."\"";?> size="64" /> + EXAMPLE: admin </td> </tr> - <tr class="haproxy_stats_visible" align="left" id='stats_password_row'> - <td width="22%" valign="top" class="vncellreq">Stats Password</td> + <td width="22%" valign="top" class="vncell">Stats Password</td> <td width="78%" class="vtable" colspan="2"> <input id="stats_password" name="stats_password" type="password" <? if(isset($pconfig['stats_password'])) echo "value=\"".$pconfig['stats_password']."\""; ?> size="64" /> - <br/> + EXAMPLE: 1Your2Secret3P@ssword </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_node_admin_row'> <td width="22%" valign="top" class="vncell">Stats Admin</td> <td width="78%" class="vtable" colspan="2"> <input id="stats_admin" name="stats_admin" type="checkbox" value="yes" <?php if ($pconfig['stats_admin']=='yes') echo "checked"; ?> /> - <br/> - </td> - </tr> - <tr class="haproxy_stats_visible" align="left" id='stats_node_enabled_row'> - <td width="22%" valign="top" class="vncell">Stats Enable Node Name</td> - <td width="78%" class="vtable" colspan="2"> - <input id="stats_node_enabled" name="stats_node_enabled" type="checkbox" value="yes" <?php if ($pconfig['stats_node_enabled']=='yes') echo "checked"; ?> /> - <br/> + Makes available the options disable/enable/softstop/softstart/killsessions from the stats page.<br/> + Note: This is not persisted when haproxy restarts. For publicly visible stats pages this should be disabled. </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_node_row'> - <td width="22%" valign="top" class="vncell">Stats Node</td> + <td width="22%" valign="top" class="vncell">Stats Nodename</td> <td width="78%" class="vtable" colspan="2"> <input id="stats_node" name="stats_node" type="text" <?if(isset($pconfig['stats_node'])) echo "value=\"{$pconfig['stats_node']}\"";?> size="64" /><br/> - The node name is displayed in the stats and helps to differentiate which server in a cluster is actually serving clients.<br/> - Leave blank to use the system name. + The short name is displayed in the stats and helps to differentiate which server in a cluster is actually serving clients. </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_desc_row'> <td width="22%" valign="top" class="vncell">Stats Description</td> <td width="78%" class="vtable" colspan="2"> - <input id="stats_desc" name="stats_desc" type="text" <?if(isset($pconfig['stats_node'])) echo "value=\"{$pconfig['stats_desc']}\"";?> size="64" /><br/> + <input id="stats_desc" name="stats_desc" type="text" <?if(isset($pconfig['stats_desc'])) echo "value=\"{$pconfig['stats_desc']}\"";?> size="64" /><br/><br/> + The description is displayed behind the Nodename set above. </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_refresh_row'> @@ -683,6 +836,21 @@ set by the 'retries' parameter.</div> </td> </tr> <tr><td> </td></tr> + <tr> + <td colspan="2" valign="top" class="listtopic">Advanced</td> + </tr> + <tr class="" align="left" id='Strict-Transport-Security'> + <td width="22%" valign="top" class="vncell">Strict-Transport-Security</td> + <td width="78%" class="vtable" colspan="2"> + When configured enables "HTTP Strict Transport Security" leave empty to disable.<br/> + <b>WARNING! the domain will only work over https with a valid certificate!</b><br/> + <input id="strict_transport_security" name="strict_transport_security" type="text" <?if(isset($pconfig['strict_transport_security'])) echo "value=\"{$pconfig['strict_transport_security']}\"";?> size="20" /> Seconds<br/> + If configured clients that requested the page with this setting active will not be able to visit this domain over a unencrypted http connection. + So make sure you understand the consequence of this setting or start with a really low value.<br/> + EXAMPLE: 60 for testing if you are absolutely sure you want this 31536000 (12 months) would be good for production. + </td> + </tr> + <tr><td> </td></tr> <tr align="left"> <td width="22%" valign="top"> </td> <td width="78%"> @@ -702,6 +870,8 @@ set by the 'retries' parameter.</div> <? phparray_to_javascriptarray($fields_servers,"fields_servers",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); phparray_to_javascriptarray($a_checktypes,"checktypes",Array('/*','/*/name','/*/descr')); + phparray_to_javascriptarray($a_cookiemode,"cookiemode",Array('/*','/*/name','/*/descr')); + phparray_to_javascriptarray($a_sticky_type,"sticky_type",Array('/*','/*/descr','/*/cookiedescr')); ?> browser_InnerText_support = (document.getElementsByTagName("body")[0].innerText != undefined) ? true : false; diff --git a/config/haproxy-devel/haproxy_pools.php b/config/haproxy-devel/haproxy_pools.php index 39009633..92235933 100644 --- a/config/haproxy-devel/haproxy_pools.php +++ b/config/haproxy-devel/haproxy_pools.php @@ -2,7 +2,7 @@ /* $Id: load_balancer_virtual_server.php,v 1.6.2.1 2006/01/02 23:46:24 sullrich Exp $ */ /* haproxy_pools.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2013 PiBa-NL Copyright (C) 2009 Scott Ullrich <sullrich@pfsense.com> Copyright (C) 2008 Remco Hoef <remcoverhoef@pfsense.com> @@ -32,6 +32,7 @@ $shortcut_section = "haproxy"; require_once("guiconfig.inc"); require_once("haproxy.inc"); +require_once("pkg_haproxy_tabs.inc"); if (!is_array($config['installedpackages']['haproxy']['ha_pools']['item'])) { @@ -64,8 +65,8 @@ if ($_GET['act'] == "del") { exit; } -$pfSversion = str_replace("\n", "", file_get_contents("/etc/version")); -if(strstr($pfSversion, "1.2")) +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); +if ($pf_version < 2.0) $one_two = true; $pgtitle = "Services: HAProxy: Backend server pools"; @@ -86,12 +87,7 @@ include("head.inc"); <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr><td class="tabnavtbl"> <?php - /* active tabs */ - $tab_array = array(); - $tab_array[] = array("Settings", false, "haproxy_global.php"); - $tab_array[] = array("Frontend", false, "haproxy_listeners.php"); - $tab_array[] = array("Backend", true, "haproxy_pools.php"); - display_top_tabs($tab_array); + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "backend"); ?> </td></tr> <tr> diff --git a/config/haproxy-devel/haproxy_socketinfo.inc b/config/haproxy-devel/haproxy_socketinfo.inc index eeaba8b6..6beb17c5 100644 --- a/config/haproxy-devel/haproxy_socketinfo.inc +++ b/config/haproxy-devel/haproxy_socketinfo.inc @@ -3,7 +3,7 @@ Copyright (C) 2013 PiBa-NL Copyright 2011 Thomas Schaefer - Tomschaefer.org Copyright 2011 Marcello Coutinho - Part of pfSense widgets (www.pfsense.com) + Part of pfSense widgets (www.pfsense.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -57,15 +57,36 @@ function haproxy_set_server_enabled($backend, $server, $enable) {//"enable be/se return haproxy_socket_command("$enablecommand server $backend/$server"); } +function haproxy_get_tables(){// "show table" + $result = array(); + $cmdresult = haproxy_socket_command("show table"); + foreach($cmdresult as $line) { + if (trim($line) == "") + continue; + list($table,$type,$size,$used) = explode(",", $line); + $table = explode(":", $table); + $type = explode(":", $type); + $size = explode(":", $size); + $used = explode(":", $used); + $newtable = array(); + $tablename = trim($table[1]); + $newtable['type'] = trim($type[1]); + $newtable['size'] = $size[1]; + $newtable['used'] = $used[1]; + $result[$tablename] = $newtable; + } + return $result; +} + function haproxy_get_statistics(){// "show stat" $result = array(); $frontends=array(); $backends=array(); $servers=array(); - $result = haproxy_socket_command("show stat"); + $cmdresult = haproxy_socket_command("show stat"); - foreach($result as $line) { + foreach($cmdresult as $line) { list($pxname,$svname,$qcur,$qmax,$scur,$smax,$slim,$stot,$bin,$bout,$dreq,$dresp,$ereq,$econ,$eresp,$wretr,$wredis,$status,$weight,$act,$bck,$chkfail,$chkdown,$lastchg,$downtime,$qlimit,$pid,$iid,$sid,$throttle,$lbtot,$tracked,$type,$rate,$rate_lim,$rate_max,$check_status,$check_code,$check_duration,$hrsp_1xx,$hrsp_2xx,$hrsp_3xx,$hrsp_4xx,$hrsp_5xx,$hrsp_other,$hanafail,$req_rate,$req_rate_max,$req_tot,$cli_abrt,$srv_abrt,$comp_in,$comp_out,$comp_byp,$comp_rsp) = explode(",", $line); #Retrieve data switch ($svname) { diff --git a/config/haproxy-devel/haproxy_stats.php b/config/haproxy-devel/haproxy_stats.php new file mode 100644 index 00000000..10dd136a --- /dev/null +++ b/config/haproxy-devel/haproxy_stats.php @@ -0,0 +1,257 @@ +<?php +/* + haproxy_stats.php + part of pfSense (https://www.pfsense.org/) + 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, 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. +*/ +require_once("authgui.inc"); +require_once("config.inc"); +require_once("haproxy_socketinfo.inc"); + +$pconfig = $config['installedpackages']['haproxy']; +if (isset($_GET['haproxystats']) || isset($_GET['scope']) || (isset($_POST) && isset($_POST['action']))){ + $fail = false; + try{ + $request = ""; + if (is_array($_GET)){ + foreach($_GET as $key => $arg) + $request .= ";$key=$arg"; + } + $options = array( + 'http'=>array( + 'method'=>"POST", + 'header'=>"Accept-language: en\r\n". + "Content-type: application/x-www-form-urlencoded\r\n", + 'content'=>http_build_query($_POST) + )); + $context = stream_context_create($options); + $response = file_get_contents("http://127.0.0.1:{$pconfig['localstatsport']}/haproxy_stats.php?haproxystats=1".$request, false, $context); + if (is_array($http_response_header)){ + foreach($http_response_header as $header){ + if (strpos($header,"Refresh: ") == 0) + header($header); + } + } + $fail = $response === false; + } catch (Exception $e) { + $fail = true; + } + if ($fail) + $response = "<br/><br/>Make sure HAProxy settings are applied and HAProxy is enabled and running"; + echo $response; + exit(0); +} +require_once("guiconfig.inc"); +if (isset($_GET['showsticktablecontent'])){ + if (is_numeric($pconfig['localstats_sticktable_refreshtime'])) + header("Refresh: {$pconfig['localstats_sticktable_refreshtime']}"); +} +$shortcut_section = "haproxy"; +require_once("haproxy.inc"); +require_once("certs.inc"); +require_once("haproxy_utils.inc"); +require_once("pkg_haproxy_tabs.inc"); + +if (!is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { + $config['installedpackages']['haproxy']['ha_backends']['item'] = array(); +} +$a_frontend = &$config['installedpackages']['haproxy']['ha_backends']['item']; + +function haproxy_add_stats_example() { + global $config, $d_haproxyconfdirty_path; + $a_backends = &$config['installedpackages']['haproxy']['ha_pools']['item']; + $a_frontends = &$config['installedpackages']['haproxy']['ha_backends']['item']; + $webcert = haproxy_find_create_certificate("HAProxy stats default"); + + $backend = array(); + $backend["name"] = "HAProxy_stats_ssl_backend"; + $backend["stats_enabled"] = "yes"; + $backend["stats_uri"] = "/"; + $backend["stats_refresh"] = "10"; + $a_backends[] = $backend; + $changecount++; + + $frontend = array(); + $frontend["name"] = "HAProxy_stats_ssl_frontend"; + $frontend["status"] = "active"; + $frontend["type"] = "http"; + $frontend["port"] = "444"; + $frontend["extaddr"] = "lan_ipv4"; + $frontend["ssloffload"] = "yes"; + $frontend["ssloffloadcert"] = $webcert['refid']; + $frontend["backend_serverpool"] = $backend["name"]; + $a_frontends[] = $frontend; + $changecount++; + $changedesc = "add new HAProxy stats example"; + + if ($changecount > 0) { + echo "touching: $d_haproxyconfdirty_path"; + touch($d_haproxyconfdirty_path); + write_config($changedesc); + } +} + +if (isset($_GET['add_stats_example'])) { + haproxy_add_stats_example(); + write_config(); + touch($d_haproxyconfdirty_path); + header("Location: haproxy_listeners.php"); + exit; +} + +if ($_POST) { + if ($_POST['apply']) { + $result = haproxy_check_and_run($savemsg, true); + if ($result) + unlink_if_exists($d_haproxyconfdirty_path); + } +} + +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); +if ($pf_version < 2.0) + $one_two = true; + +$pgtitle = "Services: HAProxy: Stats"; +include("head.inc"); + +?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<form action="haproxy_stats.php" method="post"> +<?php if($one_two): ?> +<p class="pgtitle"><?=$pgtitle?></p> +<?php endif; ?> +<?php if ($input_errors) print_input_errors($input_errors); ?> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php if (file_exists($d_haproxyconfdirty_path)): ?> +<?php print_info_box_np("The haproxy configuration has been changed.<br/>You must apply the changes in order for them to take effect.");?><br/> +<?php endif; ?> +</form> +<table width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr><td class="tabnavtbl"> + <?php + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "stats"); + ?> + </td></tr> + <tr> + <td> + <div id="mainarea"> + <table class="tabcont" width="100%" height="100%" cellspacing="0"> + <tr> + <? +if (isset($_GET['showsticktablecontent'])){ + $sticktablename = $_GET['showsticktablecontent']; +echo "<td colspan='2'>"; + echo "Contents of the sticktable: $sticktablename<br/>"; + $res = haproxy_socket_command("show table $sticktablename"); + foreach($res as $line){ + echo "<br/>".print_r($line,true); + } +echo "</td>"; +} else { +?> + <td colspan="2"> + This page contains a 'stats' page available from haproxy accessible through the pfSense gui.<br/> + <br/> + As the page is forwarded through the pfSense gui, this might cause some functionality to not work.<br/> + Though the normal haproxy stats page can be tweaked more, and doesn't use a user/pass from pfSense itself.<br/> + Some examples are configurable automatic page refresh,<br/> + only showing certain servers, not providing admin options, and can be accessed from wherever the associated frontend is accessible.(as long as rules permit access)<br/> + To use this or for simply an example how to use SSL-offloading configure stats on either a real backend while utilizing the 'stats uri'. + Or create a backend specifically for serving stats, for that you can start with the 'stats example' template below.<br/> + </td> + </tr> + <tr> + <td> </td> + </tr> + <tr> + <td colspan="2" valign="top" class="listtopic">Stats example template</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell">Example</td> + <td class="vtable"> + As an basic example you can use the link below to create a 'stats' frontend/backend page which offers with more options like setting user/password and 'admin mode' when you go to the backend settings.<br/> + <a href="haproxy_stats.php?add_stats_example=1">TEMPLATE: Create stats example configuration using a frontend/backend combination with ssl</a><br/> + <br/> + After applying the changes made by the template use this link to visit the stats page: <a target="_blank" href="https://<?=get_interface_ip("lan");?>:444">https://pfSense-LAN-ip:444/</a> + </td> + </tr> + <tr> + <td> </td> + </tr> + <tr> + <td colspan="2" valign="top" class="listtopic">HAProxy stick-tables</td> + </tr> + <tr> + <td colspan="2" valign="top" class="vncell"> + These tables are used to store information for session persistence and can be used with ssl-session-id information, application-cookies, or other information that is used to persist a user to a server. + <table class="tabcont sortable" id="sortabletable" width="100%" cellspacing="0" cellpadding="6" border="0"> + <head> + <td class="listhdrr">Stick-table</td> + <td class="listhdrr">Type</td> + <td class="listhdrr">Size</td> + <td class="listhdrr">Used</td> + </head> + <? $tables = haproxy_get_tables(); + foreach($tables as $key => $table) { ?> + <tr> + <td class="listlr"><a href="/haproxy_stats.php?showsticktablecontent=<?=$key;?>"><?=$key;?></td> + <td class="listr"><?=$table['type'];?></td> + <td class="listr"><?=$table['size'];?></td> + <td class="listr"><?=$table['used'];?></td> + </tr> + <? } ?> + </table> + </td> + </tr> + <tr> + <td> </td> + </tr> + <tr> + <td colspan="2" valign="top" class="listtopic">HAProxy stats</td> + </tr> + <tr> + <td colspan="2" valign="top" class="vncell"><a href="/haproxy_stats.php?haproxystats=1" target="_blank">Fullscreen stats page</a></td> + </tr> + <tr> + <td colspan="2" class="listlr"> + <? if (isset($pconfig['enable']) && $pconfig['localstatsport'] && is_numeric($pconfig['localstatsport'])){?> + <iframe id="frame_haproxy_stats" width="1000px" height="1500px" seamless=1 src="/haproxy_stats.php?haproxystats=1<?=$request;?>"></iframe> + <? } else { ?> + <br/> + In the "Settings" configure a internal stats port and enable haproxy for this to be functional.<br/> + <br/> + <? } ?> +<?}?> + </td> + </tr> + </table> + </div> + </td> + </tr> +</table> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/config/haproxy-devel/haproxy_utils.inc b/config/haproxy-devel/haproxy_utils.inc index 058efc98..03bd434f 100644 --- a/config/haproxy-devel/haproxy_utils.inc +++ b/config/haproxy-devel/haproxy_utils.inc @@ -1,7 +1,7 @@ <?php /* haproxy_utils.php - part of pfSense (http://www.pfsense.com/) + part of pfSense (https://www.pfsense.org/) Copyright (C) 2013 PiBa-NL All rights reserved. diff --git a/config/haproxy-devel/pkg_haproxy_tabs.inc b/config/haproxy-devel/pkg_haproxy_tabs.inc new file mode 100644 index 00000000..8cb280f8 --- /dev/null +++ b/config/haproxy-devel/pkg_haproxy_tabs.inc @@ -0,0 +1,25 @@ +<?php + +//require_once("guiconfig.inc"); DO NOT REQUIRE THIS! + +// DO NOT REQUIRE guiconfig.inc HERE! though it contains the function display_top_tabs needed below. +// however if included it will hang filter rule generation, and might cause pf to not load any rules. +// This happens when /usr/local/pkg/*.inc files are dynamically loaded during package generation from filter.inc with function discover_pkg_rules(x). + +global $haproxy_tab_array; + +$haproxy_tab_array['haproxy'] = array(); +$haproxy_tab_array['haproxy']['settings'] = Array(name => "Settings", url => "haproxy_global.php"); +$haproxy_tab_array['haproxy']['frontend'] = Array(name => "Frontend", url => "haproxy_listeners.php"); +$haproxy_tab_array['haproxy']['backend'] = Array(name => "Backend", url => "haproxy_pools.php"); +$haproxy_tab_array['haproxy']['stats'] = Array(name => "Stats", url => "haproxy_stats.php"); + +function haproxy_display_top_tabs_active($top_tabs, $activetab) { + $tab_array = array(); + foreach($top_tabs as $key => $tab_item){ + $tab_array[] = array($tab_item['name'], $key == $activetab, $tab_item['url']); + } + display_top_tabs($tab_array); +} + +?> |