diff options
Diffstat (limited to 'config/haproxy-devel')
-rw-r--r-- | config/haproxy-devel/haproxy.inc | 589 | ||||
-rwxr-xr-x | config/haproxy-devel/haproxy_global.php | 122 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_htmllist.inc | 170 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_listeners.php | 34 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_listeners_edit.php | 128 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_pool_edit.php | 209 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_socketinfo.inc | 6 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_utils.inc | 131 |
8 files changed, 1020 insertions, 369 deletions
diff --git a/config/haproxy-devel/haproxy.inc b/config/haproxy-devel/haproxy.inc index 1e403c48..6cfef4fe 100644 --- a/config/haproxy-devel/haproxy.inc +++ b/config/haproxy-devel/haproxy.inc @@ -32,6 +32,7 @@ require_once("functions.inc"); require_once("pkg-utils.inc"); require_once("notices.inc"); +require_once("filter.inc"); require_once("haproxy_utils.inc"); require_once("haproxy_xmlrpcsyncclient.inc"); @@ -39,33 +40,52 @@ $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', +$a_acltypes["host_starts_with"] = array('name' => 'Host starts with:', 'mode' => 'http', 'syntax' => 'hdr_beg(host) -i %1$s'); -$a_acltypes["host_ends_with"] = array('name' => 'Host ends with', +$a_acltypes["host_ends_with"] = array('name' => 'Host ends with:', 'mode' =>'http', 'syntax' => 'hdr_end(host) -i %1$s'); -$a_acltypes["host_matches"] = array('name' => 'Host matches', +$a_acltypes["host_matches"] = array('name' => 'Host matches:', 'mode' =>'http', 'syntax' => 'hdr(host) -i %1$s'); -$a_acltypes["host_regex"] = array('name' => 'Host regex', +$a_acltypes["host_regex"] = array('name' => 'Host regex:', 'mode' =>'http', 'syntax' => 'hdr_reg(host) -i %1$s'); -$a_acltypes["host_contains"] = array('name' => 'Host contains', +$a_acltypes["host_contains"] = array('name' => 'Host contains:', 'mode' => 'http', 'syntax' => 'hdr_dir(host) -i %1$s'); -$a_acltypes["path_starts_with"] = array('name' => 'Path starts with', +$a_acltypes["path_starts_with"] = array('name' => 'Path starts with:', 'mode' => 'http', 'syntax' => 'path_beg -i %1$s'); -$a_acltypes["path_ends_with"] = array('name' => 'Path ends with', +$a_acltypes["path_ends_with"] = array('name' => 'Path ends with:', 'mode' => 'http', 'syntax' => 'path_end -i %1$s'); -$a_acltypes["path_matches"] = array('name' => 'Path matches', +$a_acltypes["path_matches"] = array('name' => 'Path matches:', 'mode' => 'http', 'syntax' => 'path -i %1$s'); -$a_acltypes["path_regex"] = array('name' => 'Path regex', +$a_acltypes["path_regex"] = array('name' => 'Path regex:', 'mode' => 'http', 'syntax' => 'path_reg -i %1$s'); -$a_acltypes["path_contains"] = array('name' => 'Path contains', +$a_acltypes["path_contains"] = array('name' => 'Path contains:', 'mode' => 'http', 'syntax' => 'path_dir -i %1$s'); -$a_acltypes["source_ip"] = array('name' => 'Source IP', +$a_acltypes["ssl_c_verify_code"] = array('name' => 'SSL Client certificate verify error result:', + 'mode' => 'http', 'syntax' => 'ssl_fc_has_crt ssl_c_verify %1$s'); + // ssl_c_verify result codes: https://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS +$a_acltypes["ssl_c_verify"] = array('name' => 'SSL Client certificate valid.', + 'mode' => 'http', 'syntax' => 'ssl_fc_has_crt ssl_c_verify 0 '); +$a_acltypes["ssl_c_ca_commonname"] = array('name' => 'SSL Client issued by CA common-name:', + 'mode' => 'http', 'syntax' => 'ssl_c_i_dn(CN) %1$s'); +$a_acltypes["source_ip"] = array('name' => 'Source IP matches IP or Alias:', 'mode' => '', 'syntax' => 'src %1$s'); -$a_acltypes["backendservercount"] = array('name' => 'Minimum count usable servers', +$a_acltypes["backendservercount"] = array('name' => 'Minimum count usable servers:', 'mode' => '', 'syntax' => 'nbsrv(%2$s) ge %1$d', 'parameters' => 'value,backendname'); +$a_acltypes["traffic_is_http"] = array('name' => 'Traffic is http (no value needed):', 'inspect-delay' => '5', + 'mode' => 'tcp', 'syntax' => 'req.proto_http', 'advancedoptions' => "tcp-request content accept if { req.proto_http }"); +$a_acltypes["traffic_is_ssl"] = array('name' => 'Traffic is ssl (no value needed):', 'inspect-delay' => '5', + 'mode' => 'tcp', 'syntax' => 'req.ssl_ver gt 0', 'advancedoptions' => "tcp-request content accept if { req.ssl_ver gt 0 }"); // 'ssl_sni_matches' was added in HAProxy1.5dev17 -$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 }"); +$a_acltypes["ssl_sni_matches"] = array('name' => 'Server Name Indication TLS extension matches:', 'inspect-delay' => '5', + 'mode' => 'https', 'syntax' => 'req.ssl_sni -i %1$s', 'advancedoptions' => "tcp-request content accept if { req.ssl_hello_type 1 }"); +$a_acltypes["ssl_sni_contains"] = array('name' => 'Server Name Indication TLS extension contains:', 'inspect-delay' => '5', + 'mode' => 'https', 'syntax' => 'req.ssl_sni -m sub -i %1$s', 'advancedoptions' => "tcp-request content accept if { req.ssl_hello_type 1 }"); +$a_acltypes["ssl_sni_starts_with"] = array('name' => 'Server Name Indication TLS extension starts with:', 'inspect-delay' => '5', + 'mode' => 'https', 'syntax' => 'req.ssl_sni -m beg -i %1$s', 'advancedoptions' => "tcp-request content accept if { req.ssl_hello_type 1 }"); +$a_acltypes["ssl_sni_ends_with"] = array('name' => 'Server Name Indication TLS extension ends with:', 'inspect-delay' => '5', + 'mode' => 'https', 'syntax' => 'req.ssl_sni -m end -i %1$s', 'advancedoptions' => "tcp-request content accept if { req.ssl_hello_type 1 }"); +$a_acltypes["custom"] = array('name' => 'Custom acl:', + 'mode' => '', 'syntax' => '%1$s'); global $a_checktypes; $a_checktypes = array(); @@ -78,7 +98,7 @@ $a_checktypes['HTTP'] = array('name' => 'HTTP', 'syntax' => 'httpchk', // 'Agent' was added in HAProxy1.5dev18, and removed in 1.5dev20, in favor of the seperate agent-check option. $a_checktypes['Agent'] = array('name' => 'Agent', 'syntax' => 'lb-agent-chk', 'usedifferenport' => 'yes', 'descr' => 'Use a TCP connection to read an ASCII string of the form 100%,75%,drain,down (others in haproxy manual)', - deprecated => true); + 'deprecated' => true); $a_checktypes['LDAP'] = array('name' => 'LDAP', 'syntax' => 'ldap-check', 'descr' => 'Use LDAPv3 health checks for server testing'); $a_checktypes['MySQL'] = array('name' => 'MySQL', 'syntax' => 'mysql-check', @@ -106,16 +126,18 @@ $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.'); +//$a_closetypes['none'] = array('name' => 'none', 'syntax' => '', +// 'descr' => 'No close headers will be changed.'); +$a_closetypes['http-keep-alive'] = array('name' => 'http-keep-alive (default)', 'syntax' => 'http-keep-alive', + 'descr' => 'By default HAProxy operates in keep-alive mode with regards to persistent connections: for each connection it processes each request and response, and leaves the connection idle on both sides between the end of a response and the start of a new request.'); +$a_closetypes['http-tunnel'] = array('name' => 'http-tunnel', 'syntax' => 'http-tunnel', + 'descr' => 'Option "http-tunnel" disables any HTTP processing past the first request and the first response. This is the mode which was used by default in versions 1.0 to 1.5-dev21. It is the mode with the lowest processing overhead, which is normally not needed anymore unless in very specific cases such as when using an in-house protocol that looks like HTTP but is not compatible, or just to log one request per client in order to reduce log size. Note that everything which works at the HTTP level, including header parsing/addition, cookie processing or content switching will only work for the first request and will be ignored after the first response.'); $a_closetypes['httpclose'] = array('name' => 'httpclose', 'syntax' => 'httpclose', 'descr' => 'The "httpclose" option removes any "Connection" header both ways, and adds a "Connection: close" header in each direction. This makes it easier to disable HTTP keep-alive than the previous 4-rules block.'); $a_closetypes['http-server-close'] = array('name' => 'http-server-close', 'syntax' => 'http-server-close', '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-server-close" enables HTTP connection-close mode on the server side while keeping the ability to support HTTP keep-alive and pipelining on the client side. This provides the lowest latency on the client side (slow network) and the fastest session reuse on the server side to save server resources.'); $a_closetypes['forceclose'] = array('name' => 'forceclose', 'syntax' => 'forceclose', 'descr' => 'Some HTTP servers do not necessarily close the connections when they receive the "Connection: close" set by "option httpclose", and if the client does not close either, then the connection remains open till the timeout expires. This causes high number of simultaneous connections on the servers and shows high global session times in the logs. Note that this option also enables the parsing of the full request and response, which means we can close the connection to the server very quickly, releasing some resources earlier than with httpclose.'); -$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(); @@ -242,6 +264,16 @@ function haproxy_portoralias_to_list($port_or_alias) { return null; } +function haproxy_hostoralias_to_list($host_or_alias) { + if (is_alias($host_or_alias)){ + $result = filter_expand_alias_array($host_or_alias); + } else { + $result = array(); + $result[] = $host_or_alias; + } + return $result; +} + function haproxy_custom_php_deinstall_command() { global $static_output; $static_output .= "HAProxy, running haproxy_custom_php_deinstall_command()\n"; @@ -338,7 +370,10 @@ EOD; $writeconfigupdate = false; /* Do XML upgrade from haproxy 0.31 to haproxy-dev */ if (is_array($config['installedpackages']['haproxy']['ha_servers'])) { - /* We have an old config */ + $static_output .= "HAProxy, Do XML upgrade from haproxy 0.31 to haproxy-dev\n"; + update_output_window($static_output); + + /* We have an old config */ $config['installedpackages']['haproxy']['ha_pools']['item'] = array(); $a_global = &$config['installedpackages']['haproxy']; $a_backends = &$config['installedpackages']['haproxy']['ha_backends']['item']; @@ -383,8 +418,10 @@ EOD; } /* XML update to: pkg v1.3 and 'pool' changed to 'backend_serverpool' because 'pool' was added to listtags() in xmlparse.inc */ - if (is_array($config['installedpackages']['haproxy']['ha_backends']['item'][0]['pool'])) - { + if (is_array($config['installedpackages']['haproxy']['ha_backends']['item'][0]['pool'])) { + $static_output .= "HAProxy, Do XML upgrade, change to backend_serverpool from pool array\n"; + update_output_window($static_output); + foreach($config['installedpackages']['haproxy']['ha_backends']['item'] as &$frontend) { $backend_serverpool = $frontend['pool'][0]; @@ -394,8 +431,9 @@ EOD; $writeconfigupdate = true; } //also move setting for existing 2.0 installations as only the new variable is used - if (isset($config['installedpackages']['haproxy']['ha_backends']['item'][0]['pool'])) - { + if (isset($config['installedpackages']['haproxy']['ha_backends']['item'][0]['pool'])) { + $static_output .= "HAProxy, Do XML upgrade, change to backend_serverpool from pool\n"; + update_output_window($static_output); foreach($config['installedpackages']['haproxy']['ha_backends']['item'] as &$frontend) { $backend_serverpool = $frontend['pool']; @@ -406,6 +444,8 @@ EOD; } // update config to "haproxy-devel 1.5-dev19 pkg v0.5" if(is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { + $static_output .= "HAProxy, Do XML upgrade, update frontend options\n"; + update_output_window($static_output); foreach ($config['installedpackages']['haproxy']['ha_backends']['item'] as &$bind) { if($bind['httpclose'] && $bind['httpclose'] == "yes" ) { $bind['httpclose'] = "httpclose"; @@ -498,7 +538,7 @@ function haproxy_find_acl($name) { } } -function write_backend($fd, $name, $pool, $frontend) { +function write_backend($configpath, $fd, $name, $pool, $frontend) { if(!is_array($pool['ha_servers']['item']) && !$pool['stats_enabled']=='yes') return; global $a_checktypes, $a_cookiemode; @@ -515,8 +555,11 @@ function write_backend($fd, $name, $pool, $frontend) { $backend_mode = $frontendtype; } fwrite ($fd, "\tmode\t\t\t" . $backend_mode . "\n"); - + if ($pool['log-health-checks'] == 'yes') + fwrite ($fd, "\toption\t\t\tlog-health-checks\n"); + if ($frontendtype == "http") { + // actions that read/write http headers only work when 'mode http' is used if ($pool["persist_cookie_enabled"] == "yes") { $cookie_mode = $pool["persist_cookie_mode"]; $cookie_cachable = $pool["persist_cookie_cachable"]; @@ -525,14 +568,52 @@ function write_backend($fd, $name, $pool, $frontend) { $cookie .= $cookie_cachable == "yes" ? "" : " nocache"; fwrite ($fd, "\t" . $cookie . "\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["cookie_attribute_secure"] == 'yes'){ + fwrite ($fd, "\trspirep ^(Set-Cookie:((?!;\\ secure).)*)$ \\1;\ secure if { ssl_fc }\n"); + } + + if($pool['stats_enabled']=='yes') { + fwrite ($fd, "\tstats\t\t\tenable\n"); + if($pool['stats_uri']) + fwrite ($fd, "\tstats\t\t\turi ".$pool['stats_uri']."\n"); + if($pool['stats_realm']) + fwrite ($fd, "\tstats\t\t\trealm " . haproxy_escapestring($pool['stats_realm']) . "\n"); + else + fwrite ($fd, "\tstats\t\t\trealm .\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']) + fwrite ($fd, "\tstats\t\t\tshow-node " . $pool['stats_node'] . "\n"); + if($pool['stats_desc']) + 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"); + } + } + } + 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, "\tacl clienthello req.ssl_hello_type 1\n"); + fwrite ($fd, "\tacl serverhello res.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"); @@ -564,8 +645,7 @@ function write_backend($fd, $name, $pool, $frontend) { unset($checkport); $check_type = $pool['check_type']; - if ($check_type != 'none') - { + if ($check_type != 'none') { $optioncheck = $a_checktypes[$check_type]['syntax']; if ($check_type == "MySQL" || $check_type == "PostgreSQL") $optioncheck .= " user " . $pool['monitor_username']; @@ -599,36 +679,11 @@ function write_backend($fd, $name, $pool, $frontend) { $pool['retries'] = 3; fwrite ($fd, "\tretries\t\t\t" . $pool['retries'] . "\n"); - if ($pool['transparent_clientip']) - fwrite ($fd, "\tsource 0.0.0.0 usesrc clientip\n"); - - if($pool['stats_enabled']=='yes') { - fwrite ($fd, "\tstats\t\t\tenable\n"); - if($pool['stats_uri']) - fwrite ($fd, "\tstats\t\t\turi ".$pool['stats_uri']."\n"); - if($pool['stats_realm']) - fwrite ($fd, "\tstats\t\t\trealm " . haproxy_escapestring($pool['stats_realm']) . "\n"); - else - fwrite ($fd, "\tstats\t\t\trealm .\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']) - fwrite ($fd, "\tstats\t\t\tshow-node " . $pool['stats_node'] . "\n"); - if($pool['stats_desc']) - 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"); - } + if ($pool['transparent_clientip']) { + if (is_ipaddrv4($frontend_ip)) + fwrite ($fd, "\tsource 0.0.0.0 usesrc clientip\n"); + else + fwrite ($fd, "\tsource ipv6@ usesrc clientip\n"); } $uri = $pool['monitor_uri']; @@ -639,10 +694,6 @@ 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'])); @@ -690,15 +741,57 @@ function write_backend($fd, $name, $pool, $frontend) { $isbackup = ""; } $ssl = ""; + $cafile = ""; + $crlfile = ""; + $crtfile = ""; + $verifynone = ""; + $verifyhost = ""; if ($be['ssl'] == 'yes') { $ssl = $frontendtype == "http" ? ' ssl' : ' check-ssl'; + + if ($be['sslserververify'] != 'yes') { + $verifynone = " verify none"; + } else { + $verifyhost = isset($be['verifyhost']) && $be['verifyhost'] != "" ? " verifyhost {$be['verifyhost']}" : ""; + + $ca = $be['ssl-server-ca']; + $filename = "$configpath/ca_$ca.pem"; + haproxy_write_certificate_crt($filename, $ca); + $cafile = " ca-file $filename"; + + $crl = $be['ssl-server-crl']; + if ($crl && $crl != "") { + $filename = "$configpath/crl_$crl.pem"; + haproxy_write_certificate_crl($filename, $crl); + $crlfile = " crl-file $filename"; + } + } + + $server_clientcert = $be['ssl-server-clientcert']; + if ($server_clientcert && $server_clientcert != "") { + $filename = "$configpath/server_clientcert_$server_clientcert.pem"; + haproxy_write_certificate_crt($filename, $server_clientcert, true); + $crtfile = " crt $filename"; + } + } $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{$advanced_txt} {$be['advanced']}\n"); + $maxconn = ""; + if (is_numeric($be['maxconn'])){ + $maxconn = " maxconn " . $be['maxconn']; + } + + if ($be['forwardto'] && $be['forwardto'] != "") { + $server = "/{$be['forwardto']}.socket send-proxy-v2-ssl-cn"; + } else + $server = $be['address'].":" . $be['port']; + + + fwrite ($fd, "\tserver\t\t\t" . $be['name'] . " " . $server . "$ssl$cookie$checkinter$checkport$agentcheck $isbackup$weight$maxconn$cafile$crlfile$verifynone$verifyhost$crtfile{$advanced_txt} {$be['advanced']}\n"); } } fwrite ($fd, "\n"); @@ -735,24 +828,56 @@ function haproxy_check_and_run(&$messages, $reload) { } return $ok; } -function haproxy_write_certificate_file($filename, $certid) { - $cert = lookup_cert($certid); + +function haproxy_lookup_cert($certid) { + $res = lookup_ca($certid); + if (!$res) + $res = lookup_cert($certid); + return $res; +} + +function haproxy_write_certificate_crt($filename, $certid, $include_psk = false, $append = false) { + $cert = haproxy_lookup_cert($certid); + $certcontent = base64_decode($cert['crt']); + if ($include_psk && isset($cert['prv'])) + $certcontent .= "\r\n".base64_decode($cert['prv']); + $flags = $append ? FILE_APPEND : 0; + file_put_contents($filename, $certcontent, $flags); + unset($certcontent); + unset($cert); +} + +function haproxy_write_certificate_crl($filename, $crlid, $append = false) { + $crl = lookup_crl($crlid); + $content = base64_decode($crl['text']); + $flags = $append ? FILE_APPEND : 0; + file_put_contents($filename, $content, $flags); + unset($content); + unset($crl); +} + +function haproxy_write_certificate_fullchain($filename, $certid, $append = false) { + $cert = haproxy_lookup_cert($certid); $certcontent = base64_decode($cert['crt']); - $certcontent .= "\r\n".base64_decode($cert['prv']); + if (isset($cert['prv'])) + $certcontent .= "\r\n".base64_decode($cert['prv']); $certchaincontent = ca_chain($cert); if ($certchaincontent != "") { $certcontent .= "\r\n" . $certchaincontent; } unset($certchaincontent); - file_put_contents($filename, $certcontent); + $flags = $append ? FILE_APPEND : 0; + file_put_contents($filename, $certcontent, $flags); unset($certcontent); unset($cert); } function haproxy_writeconf($configpath) { global $config; + $chroot_dir = "/tmp/haproxy_chroot"; // can contain socket to forward connection from backend to frontend. "/var/empty" + make_dirs($chroot_dir); $configfile = $configpath . "/haproxy.cfg"; @@ -782,10 +907,15 @@ function haproxy_writeconf($configpath) { else $numprocs ="1"; fwrite ($fd, "\tnbproc\t\t\t$numprocs\n"); - fwrite ($fd, "\tchroot\t\t\t/var/empty\n"); + fwrite ($fd, "\tchroot\t\t\t$chroot_dir\n"); fwrite ($fd, "\tdaemon\n"); - fwrite ($fd, "\tssl-server-verify none\n"); + //fwrite ($fd, "\tssl-server-verify none\n"); + if($a_global['ssldefaultdhparam']) + fwrite ($fd, "\ttune.ssl.default-dh-param\t{$a_global['ssldefaultdhparam']}\n"); + if($a_global['log-send-hostname']) + fwrite ($fd, "\tlog-send-hostname\t\t{$a_global['log-send-hostname']}\n"); + // Keep the advanced options on the bottom of the global settings, to allow additional sections to be easely added if($a_global['advanced']) { $adv = explode("\n", base64_decode($a_global['advanced'])); @@ -823,45 +953,34 @@ function haproxy_writeconf($configpath) { continue; $primaryfrontend = get_primaryfrontend($frontend); $bname = get_frontend_ipport($frontend); + if (!is_array($a_bind[$bname])) { + $a_bind[$bname] = array(); + $a_bind[$bname] = $primaryfrontend; + $a_bind[$bname]['config'] = array(); + } //check ssl info 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"; - haproxy_write_certificate_file($filename, $frontend['ssloffloadcert']); + haproxy_write_certificate_fullchain($filename, $frontend['ssloffloadcert']); $subfolder = "$configpath/{$frontend['name']}.{$frontend['port']}"; $certs = $frontend['ha_certificates']['item']; if (is_array($certs)){ if (count($certs) > 0){ make_dirs($subfolder); foreach($certs as $cert){ - haproxy_write_certificate_file("$subfolder/{$cert['ssl_certificate']}.pem", $cert['ssl_certificate']); + haproxy_write_certificate_fullchain("$subfolder/{$cert['ssl_certificate']}.pem", $cert['ssl_certificate']); } $ssl_crt .= " crt $subfolder"; } } }else{ $ssl_crt=""; - unlink_if_exists("var/etc/{$frontend['name']}.{$frontend['port']}.crt"); + unlink_if_exists("var/etc/{$frontend['name']}.{$frontend['port']}.crt");//cleanup for possible old haproxy package version } - if (!is_array($a_bind[$bname])) { - $a_bind[$bname] = array(); - $a_bind[$bname]['config'] = array(); - // Settings which are used only from the primary frontend - $a_bind[$bname]['name'] = $primaryfrontend['name']; - $a_bind[$bname]['extaddr'] = $primaryfrontend['extaddr']; - $a_bind[$bname]['port'] = $primaryfrontend['port']; - $a_bind[$bname]['type'] = $primaryfrontend['type']; - $a_bind[$bname]['forwardfor'] = $primaryfrontend['forwardfor']; - $a_bind[$bname]['httpclose'] = $primaryfrontend['httpclose']; - $a_bind[$bname]['max_connections'] = $primaryfrontend['max_connections']; - $a_bind[$bname]['client_timeout'] = $primaryfrontend['client_timeout']; - $a_bind[$bname]['advanced'] = $primaryfrontend['advanced']; - $a_bind[$bname]['ssloffload'] = $primaryfrontend['ssloffload']; - $a_bind[$bname]['advanced_bind'] = $primaryfrontend['advanced_bind']; - } $b = &$a_bind[$bname]; if (($frontend['secondary'] != 'yes') && ($frontend['name'] != $b['name'])) { @@ -892,13 +1011,34 @@ function haproxy_writeconf($configpath) { $advancedextra = array(); + $ca_file = ""; + $first = true; + if (is_array($bind['clientcert_ca']['item'])){ + foreach($bind['clientcert_ca']['item'] as $ca){ + $filename = "$configpath/clientca_{$bind['name']}.pem"; + haproxy_write_certificate_crt($filename, $ca['cert_ca'], false, !$first); + $first = false; + } + $ca_file = " ca-file $filename verify optional"; + } + $crl_file = ""; + $first = true; + if (is_array($bind['clientcert_crl']['item'])){ + foreach($bind['clientcert_crl']['item'] as $ca){ + $filename = "$configpath/clientcrl_{$bind['name']}.pem"; + haproxy_write_certificate_crl($filename, $ca['cert_crl'], !$first); + $first = false; + } + $crl_file = " crl-file $filename"; + } + // Prepare ports for processing by splitting $portss = "{$bind['port']},"; $ports = split(",", $portss); if($bind['type'] == "http") { // ssl offloading is only possible in http mode. - $ssl_info = $bind['ssl_info']; + $ssl_info = $bind['ssl_info'].$ca_file.$crl_file; $advanced_bind = $bind['advanced_bind']; } else { $ssl_info = ""; @@ -923,6 +1063,9 @@ function haproxy_writeconf($configpath) { fwrite ($fd, "{$frontendinfo}"); fwrite ($fd, "{$listenip}"); + if (use_frontend_as_unixsocket($bind['name'])){ + fwrite ($fd, "\tbind /tmp/haproxy_chroot/{$bind['name']}.socket accept-proxy {$ssl_info} {$advanced_bind}\n"); + } // Advanced pass thru if($bind['advanced']) { @@ -943,7 +1086,19 @@ function haproxy_writeconf($configpath) { fwrite ($fd, "\tmode\t\t\t" . $backend_type . "\n"); fwrite ($fd, "\tlog\t\t\tglobal\n"); - fwrite ($fd, "\toption\t\t\tdontlognull\n"); + + if ($bind['dontlognull'] == 'yes') + fwrite ($fd, "\toption\t\t\tdontlognull\n"); + if ($bind['dontlog-normal'] == 'yes') + fwrite ($fd, "\toption\t\t\tdontlog-normal\n"); + if ($bind['log-separate-errors'] == 'yes') + fwrite ($fd, "\toption\t\t\tlog-separate-errors\n"); + if ($bind['log-detailed'] == 'yes'){ + if ($backend_type == 'http') + fwrite ($fd, "\toption\t\t\thttplog\n"); + else + fwrite ($fd, "\toption\t\t\ttcplog\n"); + } if ($backend_type == 'http') { @@ -970,6 +1125,7 @@ function haproxy_writeconf($configpath) { // Combine the rest of the frontend configs $default_backend = ""; + $inspectdelay = 0; $i = 0; foreach ($bind['config'] as $frontend) { $a_acl = get_frontend_acls($frontend); @@ -1005,13 +1161,24 @@ function haproxy_writeconf($configpath) { // Filter out acls for different modes if ($acl['mode'] != '' && $acl['mode'] != strtolower($bind['type'])) continue; - - $expr = sprintf($acl['syntax'],$entry['value'],$poolname); + if (($entry['expression'] == "source_ip") && is_alias($entry['value'])) { + $filename = "$configpath/ipalias_{$entry['value']}.lst"; + $listitems = haproxy_hostoralias_to_list($entry['value']); + $fd_alias = fopen("$filename", "w"); + foreach($listitems as $item) + fwrite($fd_alias, $item."\r\n"); + fclose($fd_alias); + $expr = "src -f $filename"; + } else + $expr = sprintf($acl['syntax'],$entry['value'],$poolname); $aclname = $i . "_" . $entry['name']; $aclnames .= $aclname." "; fwrite ($fd, "\tacl\t\t\t" . $aclname . "\t" . $expr . "\n"); + if ($acl['inspect-delay'] != '') + $inspectdelay = $acl['inspect-delay']; + if ($acl['advancedoptions'] != '') $advancedextra[$acl['syntax']] = $acl['advancedoptions']."\n"; $i++; @@ -1022,6 +1189,8 @@ function haproxy_writeconf($configpath) { if ($default_backend) fwrite ($fd, "\tdefault_backend\t\t" . $default_backend . "\n"); + if ($inspectdelay > 0) + fwrite ($fd, "\ttcp-request inspect-delay\t" . $inspectdelay . "\n"); foreach($advancedextra as $extra) fwrite ($fd, "\t".$extra."\n"); fwrite ($fd, "\n"); @@ -1032,7 +1201,7 @@ function haproxy_writeconf($configpath) { foreach ($a_pendingpl as $pending) { foreach ($a_backends as $pool) { if ($pending['backend'] == $pool['name']) { - write_backend($fd, $pending['name'], $pool, $pending['frontend']); + write_backend($configpath, $fd, $pending['name'], $pool, $pending['frontend']); } } } @@ -1065,17 +1234,21 @@ function haproxy_is_running() { } function haproxy_load_modules() { - // On FreeBSD 8 ipfw is needed to allow 'transparent' proxying (getting reply's to a non-local ip to pass back to the client-socket).. - // On FreeBSD 9 it is probably possible to do the same with the pf option "divert-reply" + // On FreeBSD 8 ipfw is needed to allow 'transparent' proxying (getting reply's to a non-local ip to pass back to the client-socket). + // On FreeBSD 9 and 10 it should have been possible to do the same with the pf(4) option "divert-reply" however that is not implemented. + // FreeBSD 10 patch proposal: http://lists.freebsd.org/pipermail/freebsd-bugs/2014-April/055823.html + mute_kernel_msgs(); - if (!is_module_loaded("ipfw.ko")) { - mwexec("/sbin/kldload ipfw"); - /* make sure ipfw is not on pfil hooks */ - mwexec("/sbin/sysctl net.inet.ip.pfil.inbound=\"pf\" net.inet6.ip6.pfil.inbound=\"pf\"" . - " net.inet.ip.pfil.outbound=\"pf\" net.inet6.ip6.pfil.outbound=\"pf\""); - } + if (!is_module_loaded("ipfw.ko")) { + mwexec("/sbin/kldload ipfw"); + /* make sure ipfw is not on pfil hooks */ + mwexec("/sbin/sysctl net.inet.ip.pfil.inbound=\"pf\" net.inet6.ip6.pfil.inbound=\"pf\"" . + " net.inet.ip.pfil.outbound=\"pf\" net.inet6.ip6.pfil.outbound=\"pf\""); + } + /* Activate layer2 filtering */ - mwexec("/sbin/sysctl net.link.ether.ipfw=1"); + mwexec("/sbin/sysctl net.link.ether.ipfw=1 /sbin/net.inet.ip.fw.one_pass=1"); + unmute_kernel_msgs(); } @@ -1102,21 +1275,19 @@ function haproxy_get_transparent_backends(){ continue; $real_if = get_real_interface($backend["transparent_interface"]); $a_servers = &$backend['ha_servers']['item']; - foreach($a_servers as $server) { - if (is_array($a_servers)) { - - foreach($a_servers as $be) { - if (!$be['status'] == "inactive") - continue; - if (!is_ipaddr($be['address'])) - continue; - $item = array(); - $item['name'] = $be['name']; - $item['interface'] = $real_if; - $item['address'] = $be['address']; - $item['port'] = $be['port']; - $transparent_backends[] = $item; - } + if (is_array($a_servers)) { + foreach($a_servers as $be) { + if (!$be['status'] == "inactive") + continue; + if (!is_ipaddr($be['address'])) + continue; + $item = array(); + $item['name'] = $be['name']; + $item['interface'] = $real_if; + $item['forwardto'] = $be['forwardto']; + $item['address'] = $be['address']; + $item['port'] = $be['port']; + $transparent_backends[] = $item; } } } @@ -1129,12 +1300,19 @@ function haproxy_generate_rules($type) { $rules = ""; switch($type) { case 'filter': + // Sloppy pf rules are needed because of ipfw is used to 'catch' return traffic, and pf would otherwise terminate the connection after a few packets.. $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. + if (count($transparent_backends) > 0) { $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"; - } + foreach($transparent_backends as $tb){ + if (is_ipaddrv4($tb['address'])) + $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"; + if (is_ipaddrv6($tb['address'])) { + list ($addr, $scope) = explode("%", $tb['address']); + $rules .= "pass out quick on {$tb['interface']} inet6 proto tcp from any to {$addr} port {$tb['port']} flags S/SA keep state ( sloppy ) label \"HAPROXY_transparent_rule_{$tb['name']}\"\n"; + } + } + } break; } return $rules; @@ -1143,7 +1321,11 @@ function haproxy_generate_rules($type) { function load_ipfw_rules() { // On FreeBSD 8 pf does not support "divert-reply" so ipfw is needed. global $g, $config; - $ipfw_zone_haproxy = "haproxy"; + if (haproxy_utils::$pf_version < 2.2) { + $ipfw_zone_haproxy = "haproxy"; + } else { + $ipfw_zone_haproxy = "4000"; // seems that 4000 is a safe zone number to avoid conflicts with captive portal.. and 4095 is the max? + } $a_backends = &$config['installedpackages']['haproxy']['ha_pools']['item']; @@ -1156,22 +1338,39 @@ function load_ipfw_rules() { $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) { - mwexec("/usr/local/sbin/ipfw_context -a $ipfw_zone_haproxy -n $transparent_if", true); + if (haproxy_utils::$pf_version < 2.2) { + // pfSense 2.1 FreeBSD 8.3 + mwexec("/usr/local/sbin/ipfw_context -a $ipfw_zone_haproxy", true); + + foreach($transparent_interfaces as $transparent_if => $value) { + mwexec("/usr/local/sbin/ipfw_context -a $ipfw_zone_haproxy -n $transparent_if", true); + } + } else { + // pfSense 2.2 FreeBSD 10 + mwexec("/sbin/ipfw zone $ipfw_zone_haproxy create", true); + foreach($transparent_interfaces as $transparent_if => $value) { + mwexec("/sbin/ipfw zone $ipfw_zone_haproxy madd $transparent_if", true); + } } $rulenum = 64000; // why that high? captiveportal.inc also does it... $rules = "flush\n"; foreach($transparent_backends as $transparent_be) { - $rules .= "add $rulenum fwd localhost tcp from {$transparent_be["address"]} {$transparent_be["port"]} to any in recv {$transparent_be["interface"]}\n"; + if (is_ipaddrv4($transparent_be["address"])) + $rules .= "add $rulenum fwd localhost tcp from {$transparent_be["address"]} {$transparent_be["port"]} to any in recv {$transparent_be["interface"]}\n"; + else if (is_ipaddrv6($transparent_be["address"])) { + list ($addr, $scope) = explode("%", $transparent_be['address']); + $rules .= "add $rulenum fwd ::1 tcp from {$addr} {$transparent_be["port"]} to any in recv {$transparent_be["interface"]}\n"; + } + $rulenum++; } file_put_contents("{$g['tmp_path']}/ipfw_{$ipfw_zone_haproxy}.haproxy.rules", $rules); - mwexec("/usr/local/sbin/ipfw_context -s $ipfw_zone_haproxy", true); + if (haproxy_utils::$pf_version < 2.2) + mwexec("/usr/local/sbin/ipfw_context -s $ipfw_zone_haproxy", true); mwexec("/sbin/ipfw -x $ipfw_zone_haproxy -q {$g['tmp_path']}/ipfw_{$ipfw_zone_haproxy}.haproxy.rules", true); } @@ -1251,18 +1450,38 @@ function haproxy_check_run($reload) { if(use_transparent_clientip_proxying()) { filter_configure(); load_ipfw_rules(); + } else { + if (haproxy_utils::$pf_version < 2.2) { + mwexec("/usr/local/sbin/ipfw_context -d haproxy", true); + } else { + $ipfw_zone_haproxy = 4000; + mwexec("/sbin/ipfw zone $ipfw_zone_haproxy destroy", true); + } + } + + if (file_exists('/var/run/haproxy.pid')){ + $old_pid = file_get_contents('/var/run/haproxy.pid'); } else - mwexec("/usr/local/sbin/ipfw_context -d haproxy", true); + $old_pid = 'none'; 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 else $sf_st = "-sf";//finish serving existing connections exit when done, and the new process is listening + + syslog(LOG_NOTICE, "haproxy: reload old pid:$old_pid"); exec("/usr/local/sbin/haproxy -f {$configpath}/haproxy.cfg -p /var/run/haproxy.pid $sf_st `cat /var/run/haproxy.pid` 2>&1", $output, $errcode); } else { + syslog(LOG_NOTICE, "haproxy: starting old pid:$old_pid"); exec("/usr/local/sbin/haproxy -f {$configpath}/haproxy.cfg -p /var/run/haproxy.pid -D 2>&1", $output, $errcode); } + if (file_exists('/var/run/haproxy.pid')){ + $new_pid = file_get_contents('/var/run/haproxy.pid'); + } else + $new_pid = 'none'; + syslog(LOG_NOTICE, "haproxy: started new pid:$new_pid"); + foreach($output as $line) $haproxy_run_message .= "<br/>" . htmlspecialchars($line) . "\n"; } else { @@ -1291,13 +1510,16 @@ function killprocesses($processname, $pidfile, $signal = "KILL") { function haproxy_sync_xmlrpc_settings() { global $config; // preserve 'old' sync settings, that should not be overwritten by xmlrpc-sync. + $old_config = $config['installedpackages']['haproxy']; $enable = isset($config['installedpackages']['haproxy']['enablesync']); $config['installedpackages']['haproxy'] = $config['installedpackages']['haproxysyncpkg']; unset($config['installedpackages']['haproxysyncpkg']); + $new_config = &$config['installedpackages']['haproxy']; // restore 'old' settings. $config['installedpackages']['haproxy']['enablesync'] = $enable ? true : false; + $new_config['log-send-hostname'] = $old_config['log-send-hostname']; write_config("haproxy, xmlrpc config synced"); // Write new 'merged' configuration } @@ -1333,22 +1555,28 @@ function haproxy_xmlrpc_sync_configure() { function get_frontend_id($name) { global $config; - $a_backend = &$config['installedpackages']['haproxy']['ha_backends']['item']; + $a_frontend = &$config['installedpackages']['haproxy']['ha_backends']['item']; $i = 0; - foreach($a_backend as $backend) + foreach($a_frontend as $frontend) { - if ($backend['name'] == $name) + if ($frontend['name'] == $name) return $i; $i++; } return null; } +function haproxy_is_frontendname($name) { + if ($name[0] == '!') + $name = substr($name, 1); + return get_frontend_id($name) != null; +} + function get_primaryfrontend($frontend) { global $config; - $a_backend = &$config['installedpackages']['haproxy']['ha_backends']['item']; + $a_frontend = &$config['installedpackages']['haproxy']['ha_backends']['item']; if ($frontend['secondary'] == 'yes') - $mainfrontend = $a_backend[get_frontend_id($frontend['primary_frontend'])]; + $mainfrontend = $a_frontend[get_frontend_id($frontend['primary_frontend'])]; else $mainfrontend = $frontend; return $mainfrontend; @@ -1405,10 +1633,15 @@ function get_haproxy_frontends($excludeitem="") { $result[$frontend['name']]['name'] = "{$frontend['name']} - {$frontend['type']} ({$serveradress})"; $result[$frontend['name']]['ref'] = &$frontend; } - asort($result, SORT_STRING); + uasort($result, haproxy_compareByName); return $result; } +function generate_cert_acl($crt, $defaultport, $nondefaultport){ + // The host header send by a browser will contain the portnumber when a nondefault port is used for the server side. + +} + function get_frontend_acls($frontend) { $mainfrontend = get_primaryfrontend($frontend); $result = array(); @@ -1439,17 +1672,27 @@ function get_frontend_acls($frontend) { $poolname = $frontend['backend_serverpool'] . "_" . strtolower($frontend['type']); $aclname = "SNI_" . $poolname; - if ($frontend['ssloffloadacl']){ + + if (ifset($frontend['ssloffloadacl']) == 'yes' || ifset($frontend['ssloffloadaclnondefault']) == 'yes') { $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); + if (ifset($frontend['ssloffloadacl']) == 'yes' && ifset($frontend['ssloffloadaclnondefault']) == 'yes') { + $acl_item['descr'] = "Certificate ACL match regex: ^{$cert_cn}(:([0-9]){1,5})?$"; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_regex', 'value' => "^{$cert_cn}(:([0-9]){1,5})?$"); + } elseif (ifset($frontend['ssloffloadaclnondefault']) == 'yes') { + $acl_item['descr'] = "Certificate ACL starts with: {$cert_cn}:"; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_starts_with', 'value' => $cert_cn.":"); + } else { + $acl_item['descr'] = "Certificate ACL match: {$cert_cn}"; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_matches', 'value' => $cert_cn); + } $result[] = $acl_item; } - if ($frontend['ssloffloadacladditional']){ + if (ifset($frontend['ssloffloadacladditional']) == 'yes' || ifset($frontend['ssloffloadacladditionalnondefault']) == 'yes') { $certs = $frontend['ha_certificates']['item']; if (is_array($certs)){ foreach($certs as $certref){ @@ -1457,9 +1700,18 @@ function get_frontend_acls($frontend) { $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); + if (ifset($frontend['ssloffloadacladditional']) == 'yes' && ifset($frontend['ssloffloadacladditionalnondefault']) == 'yes') { + $acl_item['descr'] = "Certificate ACL match regex: ^{$cert_cn}(:([0-9]){1,5})?$"; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_regex', 'value' => "^({$cert_cn}(($)|(:.*)))"); + } elseif (ifset($frontend['ssloffloadacladditionalnondefault']) == 'yes') { + $acl_item['descr'] = "Certificate ACL starts with: {$cert_cn}:"; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_starts_with', 'value' => $cert_cn.":"); + } else { + $acl_item['descr'] = "Certificate ACL match: {$cert_cn}"; + $acl_item['ref'] = array('name' => "{$aclname}_{$descr}",'expression' => 'host_matches', 'value' => $cert_cn); + } $result[] = $acl_item; } } @@ -1468,18 +1720,43 @@ function get_frontend_acls($frontend) { return $result; } -function get_backend($name) { +function get_backend_id($name) { global $config; $a_backend = &$config['installedpackages']['haproxy']['ha_pools']['item']; + $i = 0; if(is_array($a_backend)) - foreach($a_backend as $key => $backend) - { + foreach($a_backend as $key => $backend) { if ($backend['name'] == $name) - return $backend; + return $i; + $i++; } return null; } +function get_backend($name) { + global $config; + $a_backend = &$config['installedpackages']['haproxy']['ha_pools']['item']; + $id = get_backend_id($name); + if (is_numeric($id)) + return $a_backend[$id]; + return null; +} + +function use_frontend_as_unixsocket($name) { + global $config; + $a_backends = &$config['installedpackages']['haproxy']['ha_pools']['item']; + foreach ($a_backends as $backend) { + $a_servers = &$backend['ha_servers']['item']; + if (is_array($a_servers)) { + foreach($a_servers as $server) { + if ($server['forwardto'] && $server['forwardto'] == $name) + return true; + } + } + } + return false; +} + function haproxy_escapestring($configurationsting) { $result = str_replace('\\', '\\\\', $configurationsting); $result = str_replace(' ', '\\ ', $result); @@ -1500,13 +1777,17 @@ function haproxy_find_create_certificate($certificatename) { $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); + + $new_cert = array(); + $dn = array( + "organizationName" => "haproxy-pfsense", + "commonName" => "haproxy-pfsense" + ); + $new_cert = array(); + ca_create($new_cert, 1024, 2000, $dn); + $crt = base64_decode($new_cert['crt']); + $prv = base64_decode($new_cert['prv']); + cert_import($cert, $crt, $prv); $a_cert[] = $cert; return $cert; } diff --git a/config/haproxy-devel/haproxy_global.php b/config/haproxy-devel/haproxy_global.php index 50472d9f..cad3795a 100755 --- a/config/haproxy-devel/haproxy_global.php +++ b/config/haproxy-devel/haproxy_global.php @@ -36,7 +36,7 @@ require_once("haproxy_utils.inc"); require_once("globals.inc"); require_once("pkg_haproxy_tabs.inc"); -$simplefields = array('localstats_refreshtime','localstats_sticktable_refreshtime'); +$simplefields = array('localstats_refreshtime','localstats_sticktable_refreshtime','log-send-hostname','ssldefaultdhparam'); if (!is_array($config['installedpackages']['haproxy'])) $config['installedpackages']['haproxy'] = array(); @@ -268,55 +268,6 @@ function enable_change(enable_change) { </tr> <tr> <td valign="top" class="vncell"> - Remote syslog host - </td> - <td class="vtable"> - <input name="remotesyslog" type="text" class="formfld" id="remotesyslog" size="18" value="<?=htmlspecialchars($pconfig['remotesyslog']);?>" /><br/> - To log to the local pfSense systemlog fill the host with the value <b>/var/run/log</b>, however if a lot of messages are generated logging is likely to be incomplete. (Also currently no informational logging gets shown in the systemlog.) - </td> - </tr> - <tr> - <td valign="top" class="vncell"> - Syslog facility - </td> - <td class="vtable"> - <select name="logfacility" class="formfld"> - <?php - $facilities = array("kern", "user", "mail", "daemon", "auth", "syslog", "lpr", - "news", "uucp", "cron", "auth2", "ftp", "ntp", "audit", "alert", "cron2", - "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7"); - foreach ($facilities as $f): - ?> - <option value="<?=$f;?>" <?php if ($f == $pconfig['logfacility']) echo "selected"; ?>> - <?=$f;?> - </option> - <?php - endforeach; - ?> - </select> - </td> - </tr> - <tr> - <td valign="top" class="vncell"> - Syslog level - </td> - <td class="vtable"> - <select name="loglevel" class="formfld"> - <?php - $levels = array("emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"); - foreach ($levels as $l): - ?> - <option value="<?=$l;?>" <?php if ($l == $pconfig['loglevel']) echo "selected"; ?>> - <?=$l;?> - </option> - <?php - endforeach; - ?> - </select> - </td> - </tr> - <tr> - <td valign="top" class="vncell"> Carp monitor </td> <td class="vtable"> @@ -374,6 +325,77 @@ function enable_change(enable_change) { </td> </tr> <tr> + <td colspan="2" valign="top" class="listtopic">Logging</td> + </tr> + <tr> + <td valign="top" class="vncell"> + Remote syslog host + </td> + <td class="vtable"> + <input name="remotesyslog" type="text" class="formfld" id="remotesyslog" size="18" value="<?=htmlspecialchars($pconfig['remotesyslog']);?>" /><br/> + To log to the local pfSense systemlog fill the host with the value <b>/var/run/log</b>, however if a lot of messages are generated logging is likely to be incomplete. (Also currently no informational logging gets shown in the systemlog.) + </td> + </tr> + <tr> + <td valign="top" class="vncell"> + Syslog facility + </td> + <td class="vtable"> + <select name="logfacility" class="formfld"> + <?php + $facilities = array("kern", "user", "mail", "daemon", "auth", "syslog", "lpr", + "news", "uucp", "cron", "auth2", "ftp", "ntp", "audit", "alert", "cron2", + "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7"); + foreach ($facilities as $f): + ?> + <option value="<?=$f;?>" <?php if ($f == $pconfig['logfacility']) echo "selected"; ?>> + <?=$f;?> + </option> + <?php + endforeach; + ?> + </select> + </td> + </tr> + <tr> + <td valign="top" class="vncell"> + Syslog level + </td> + <td class="vtable"> + <select name="loglevel" class="formfld"> + <?php + $levels = array("emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"); + foreach ($levels as $l): + ?> + <option value="<?=$l;?>" <?php if ($l == $pconfig['loglevel']) echo "selected"; ?>> + <?=$l;?> + </option> + <?php + endforeach; + ?> + </select> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell">Log hostname</td> + <td width="78%" class="vtable"> + <input name="log-send-hostname" type="text" <?if(isset($pconfig['log-send-hostname'])) echo "value=\"{$pconfig['log-send-hostname']}\"";?> size="18" maxlength="50" /> EXAMPLE: HaproxyMasterNode<br/>Sets the hostname field in the syslog header. If empty defaults to the system hostname. + </td> + </tr> + <tr> + <td colspan="2" valign="top" class="listtopic">Tuning</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell">Max SSL Diffie-Hellman size</td> + <td width="78%" class="vtable"> + <input name="ssldefaultdhparam" type="text" <?if(isset($pconfig['ssldefaultdhparam'])) echo "value=\"{$pconfig['ssldefaultdhparam']}\"";?> size="10" maxlength="5" /> EXAMPLE: 2048<br/>Sets the maximum size of the Diffie-Hellman parameters used for generating +the ephemeral/temporary Diffie-Hellman key in case of DHE key exchange. +Minimum and default value is: 1024, bigger values might increase CPU usage.<br/> + For more information about the <b>"tune.ssl.default-dh-param"</b> option please see <b><a href='http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#3.2-tune.ssl.default-dh-param' target='_blank'>HAProxy Documentation</a></b><br/> + NOTE: HAProxy will emit a warning when starting when this setting is used but not configured. + </td> + </tr> + <tr> <td colspan="2" valign="top" class="listtopic">Global Advanced pass thru</td> </tr> <tr> diff --git a/config/haproxy-devel/haproxy_htmllist.inc b/config/haproxy-devel/haproxy_htmllist.inc index ae46ffd4..404e9fab 100644 --- a/config/haproxy-devel/haproxy_htmllist.inc +++ b/config/haproxy-devel/haproxy_htmllist.inc @@ -50,7 +50,30 @@ function haproxy_htmllist_get_values($html_list){ return $values; } -function haproxy_htmllist($tablename,$values,$items,$editstate=false){ +function haproxy_htmllist_drawcell($item, $itemvalue, $editable, $itemnamenr = "") { + $itemtype = $item['type']; + if ($editable) { + $itemtype = $item['type']; + if ($itemtype == "select"){ + echo_html_select($itemnamenr, $item['items'], $itemvalue,"","updatevisibility();", "width:{$item['size']}"); + } else + if ($itemtype == "checkbox"){ + $checked = $itemvalue=='yes' ? " checked" : ""; + echo "<input name='$itemnamenr' id='$itemnamenr' type='checkbox'$checked value='yes' size='{$item['size']}' />"; + } else + echo "<input name='$itemnamenr' id='$itemnamenr' type='text' value='{$itemvalue}' size='{$item['size']}' />"; + } else { + if ($itemtype == "select"){ + echo $item['items'][$itemvalue]['name']; + } else + if ($itemtype == "checkbox"){ + echo $itemvalue=='yes' ? gettext('yes') : gettext('no'); + } else + echo $itemvalue; + } +} + +function haproxy_htmllist($tablename,$rowvalues,$items,$editstate=false,$itemdetails=null){ global $g, $counter; echo "<table class='' width='100%' cellpadding='0' cellspacing='0' id='$tablename'> <tr>"; @@ -59,24 +82,20 @@ function haproxy_htmllist($tablename,$values,$items,$editstate=false){ } echo "<td width='5%' class=''></td> </tr>"; - if (is_array($values)){ - foreach($values as $value){ + if (is_array($rowvalues)){ + foreach($rowvalues as $value){ if (!$editstate) { echo "<tr id='tr_view_$counter' ondblclick='editRow($counter); return false;' >"; $leftitem = true; - foreach($items as $item){ + foreach($items as $item) { $tdclass = $leftitem ? "vtable listlr" : "vtable listr"; echo "<td class='$tdclass'>"; $itemname = $item['name']; - $itemtype = $item['type']; $itemvalue = $value[$itemname]; - if ($itemtype == "select"){ - echo $item['items'][$itemvalue]['name']; + if (isset($item['customdrawcell'])) { + $item['customdrawcell']($item, $itemvalue, false); } else - if ($itemtype == "checkbox"){ - echo $itemvalue=='yes' ? gettext('yes') : gettext('no'); - } else - echo $itemvalue; + haproxy_htmllist_drawcell($item, $itemvalue, false); echo "</td>"; $leftitem = false; } @@ -94,24 +113,18 @@ function haproxy_htmllist($tablename,$values,$items,$editstate=false){ </td></tr></table> </td>"; echo "</tr>"; - } + } $displaystyle = $editstate ? "" : "display: none;"; echo "<tr id='tr_edit_$counter' style='$displaystyle'>"; foreach($items as $item){ $itemname = $item['name']; - $itemtype = $item['type']; $itemvalue = $value[$itemname]; - $itemnamenr = $itemname.$counter; + $itemnamenr = $itemname . $counter; echo "<td class='vtable'>"; - if ($itemtype == "select"){ - echo_html_select($itemnamenr, $item['items'], $itemvalue,"","updatevisibility();", "width:{$item['size']}"); + if (isset($item['customdrawcell'])) { + $item['customdrawcell']($item, $itemvalue, true, $itemnamenr); } else - if ($itemtype == "checkbox"){ - $checked = $itemvalue=='yes' ? " checked" : ""; - echo "<input name='$itemnamenr' id='$itemnamenr' type='checkbox'$checked value='yes' size='{$item['size']}' />"; - - } else - echo "<input name='$itemnamenr' id='$itemnamenr' type='text' value='{$itemvalue}' size='{$item['size']}' />"; + haproxy_htmllist_drawcell($item, $itemvalue, true, $itemnamenr); echo "</td>"; } echo " @@ -125,6 +138,78 @@ function haproxy_htmllist($tablename,$values,$items,$editstate=false){ </td></tr></table> </td>"; echo "</tr>"; + if (isset($itemdetails)) { + $colspan = count($items)-1; + echo "<tr id='tr_viewdetail_$counter'>"; + ?> + <td class='vtable listlr' style='border-bottom-width: medium;vertical-align:top;'> + <div style="position:relative;float:right;width:11px;height:11px;"> + <a onclick="htmltable_toggle_details('<?="htmltable_{$tablename}_{$counter}_details"?>')"> + <img id="htmltable_<?="{$tablename}_{$counter}"?>_details_off" alt="Expand advanced server settings" + src="tree/plus.gif" style="clip:rect(19px 13px 30px 2px); top:-19px;position:absolute;"/> + </a> + </div> + </td> + <? + echo "<td class='vtable listr' colspan='$colspan' style='border-bottom-width: medium;'>"; + $itemnr = 0; + echo "<div id='htmltable_{$tablename}_{$counter}_details_view'>"; + $itemcount = count($itemdetails); + foreach($itemdetails as $item) { + echo "<div style='float: left;padding-right: 2px;'>"; + $tdclass = "";//$leftitem ? "vtable listlr" : "vtable listr"; + echo $item['columnheader'] . ": "; + $itemname = $item['name']; + $itemvalue = $value[$itemname]; + if (isset($item['customdrawcell'])) { + $item['customdrawcell']($item, $itemvalue, false); + } else + haproxy_htmllist_drawcell($item, $itemvalue, false); + $leftitem = false; + $itemnr++; + if ($itemcount != $itemnr) + echo ", "; + echo "</div>"; + } + echo "</div>"; + echo "<div id='htmltable_{$tablename}_{$counter}_details_edit' style='display:none;'>"; + echo "<table class='tabcont' style='border-collapse:collapse' border='1' cellspacing='0' >"; + $leftitem = true; + foreach($itemdetails as $item) { + echo "<tr id='tr_edititemdetails_$counter' ondblclick='editRow($counter); return false;'>"; + $tdclass = "";//$leftitem ? "vtable listlr" : "vtable listr"; + echo "<td style='border-right:0' class='$tdclass'>"; + echo "{$item['columnheader']}: "; + echo "</td>"; + echo "<td style='border-left:0' class='$tdclass'>"; + $itemname = $item['name']; + $itemvalue = $value[$itemname]; + $itemnamenr = $itemname . $counter; + echo "{$item['description']}<br/>"; + if (isset($item['customdrawcell'])) { + $item['customdrawcell']($item, $itemvalue, true, $itemnamenr); + } else + haproxy_htmllist_drawcell($item, $itemvalue, true, $itemnamenr); + echo "</td>"; + $leftitem = false; + echo "</tr>"; + } + echo "</table>"; + echo "</div>"; + echo "</td>"; + echo "</tr>"; + } + if (isset($itemdetails)) { + $colspan = count($items)-1; + echo "<tr id='htmltable_{$tablename}_{$counter}_details' style='$displaystyle' >"; + echo "<td class='vtable listlr' style='border-bottom-width: medium;'> </td>"; + echo "<td class='vtable listr' colspan='$colspan' style='border-bottom-width: medium;'>"; + echo "</td>"; + echo "</tr>"; + } + + + $counter++; } } @@ -136,11 +221,11 @@ function haproxy_htmllist($tablename,$values,$items,$editstate=false){ function haproxy_htmllist_js(){ ?><script type="text/javascript"> - function htmllist_get_select_items(tableId) { + function htmllist_get_select_items(prefix,tableId) { var items; var i = tableId.lastIndexOf('_'); - var items_name = "fields_"+tableId.substr(i+1); - items = eval(items_name); + var items_name = prefix+"_"+tableId.substr(i+1); + items = eval("typeof "+items_name+" !== 'undefined' ? "+items_name+" : {}"); return items; } @@ -150,8 +235,7 @@ function haproxy_htmllist_js(){ var btable, btbody, btr, btd; d = document; - items = htmllist_get_select_items(tableId); - seltext = htmllist_get_select_options(tableId); + items = htmllist_get_select_items('fields',tableId); tbody = d.getElementById(tableId).getElementsByTagName("tbody").item(0); tr = d.createElement("tr"); @@ -165,6 +249,7 @@ function haproxy_htmllist_js(){ "' id='" + items[i]['name'] + totalrows + "'><\/input> "; } else if(items[i]['type'] == 'select') { + seltext = htmllist_get_select_options(tableId, items[i]['name']); td.innerHTML="<select style='width:" + items[i]['size'] + "' name='" + items[i]['name'] + totalrows + "' id='" + items[i]['name'] + totalrows + "'>" + seltext + "<\/select> "; @@ -178,6 +263,13 @@ function haproxy_htmllist_js(){ td.rowSpan = "1"; td.setAttribute("class","list"); + items = htmllist_get_select_items('fields_details',tableId); + for (var i in items) { + td.innerHTML=td.innerHTML+"<input type='hidden' name='" + items[i]['name'] + totalrows + + "' id='" + items[i]['name'] + totalrows + + "'><\/input> "; + } + // Recreate the button table. btable = document.createElement("table"); btable.setAttribute("border", "0"); @@ -204,7 +296,7 @@ function haproxy_htmllist_js(){ var dupEl; var newEl; addRowTo(tableId); - items = htmllist_get_select_items(tableId); + items = htmllist_get_select_items('fields',tableId); for (var i in items) { dupEl = document.getElementById(items[i]['name'] + rowId); newEl = document.getElementById(items[i]['name'] + totalrows); @@ -214,6 +306,16 @@ function haproxy_htmllist_js(){ else newEl.value = dupEl.value; } + items = htmllist_get_select_items('fields_details',tableId); + for (var i in items) { + dupEl = document.getElementById(items[i]['name'] + rowId); + newEl = document.getElementById(items[i]['name'] + totalrows); + if (dupEl && newEl) + if(items[i]['type'] == 'checkbox') + newEl.value = dupEl.checked ? 'yes' : ''; + else + newEl.value = dupEl.value; + } } function editRow(num) { var trview = document.getElementById('tr_view_' + num); @@ -223,8 +325,10 @@ function haproxy_htmllist_js(){ } function deleteRow(rowId, tableId) { var view = document.getElementById("tr_view_" + rowId); + var viewdetail = document.getElementById("tr_viewdetail_" + rowId); var edit = document.getElementById("tr_edit_" + rowId); view.parentNode.removeChild(view); + viewdetail.parentNode.removeChild(viewdetail); edit.parentNode.removeChild(edit); } function removeRow(el) { @@ -240,6 +344,16 @@ function haproxy_htmllist_js(){ el.parentNode.removeChild(el); } } + + function htmltable_toggle_details(table_row_detail_id) { + tredit = document.getElementById(table_row_detail_id+'_off'); + trviewdetail = document.getElementById(table_row_detail_id+'_edit'); + treditdetail = document.getElementById(table_row_detail_id+'_view'); + current_on = tredit.style.display == 'none'; + tredit.style.display=current_on ? '' : 'none'; + trviewdetail.style.display=current_on ? 'none' : ''; + treditdetail.style.display=current_on ? '' : 'none'; + } </script><? } diff --git a/config/haproxy-devel/haproxy_listeners.php b/config/haproxy-devel/haproxy_listeners.php index 7022ec34..1deabdbb 100644 --- a/config/haproxy-devel/haproxy_listeners.php +++ b/config/haproxy-devel/haproxy_listeners.php @@ -134,10 +134,12 @@ include("head.inc"); $img_cert = "/themes/{$g['theme']}/images/icons/icon_frmfld_cert.png"; $img_adv = "/themes/{$g['theme']}/images/icons/icon_advanced.gif"; $img_acl = "/themes/{$g['theme']}/images/icons/icon_ts_rule.gif"; + $textgray = ""; + $first = true; $last_frontend_shared = false; foreach ($a_frontend_grouped as $a_frontend) { usort($a_frontend,'sort_sharedfrontends'); - if (count($a_frontend) > 1 || $last_frontend_shared) { + if ((count($a_frontend) > 1 || $last_frontend_shared) && !$first) { ?> <tr class="<?=$textgray?>"><td colspan="7"> </td></tr> <? } $last_frontend_shared = count($a_frontend) > 1; @@ -151,11 +153,19 @@ include("head.inc"); </td> <td class="listlr" ondblclick="document.location='haproxy_listeners_edit.php?id=<?=$frontendname;?>';"> <? + $acls = get_frontend_acls($frontend); + $isaclset = ""; + foreach ($acls as $acl) { + $isaclset .= " " . htmlspecialchars($acl['descr']); + } + if ($isaclset) + echo "<img src=\"$img_acl\" title=\"" . gettext("acl's used") . ": {$isaclset}\" border=\"0\" />"; + if (strtolower($frontend['type']) == "http" && $frontend['ssloffload']) { $cert = lookup_cert($frontend['ssloffloadcert']); $descr = htmlspecialchars($cert['descr']); - $certs = $frontend['ha_certificates']['item']; - if (is_array($certs)){ + if (is_array($frontend['ha_certificates']) && is_array($frontend['ha_certificates']['item'])) { + $certs = $frontend['ha_certificates']['item']; if (count($certs) > 0){ foreach($certs as $certitem){ $cert = lookup_cert($certitem['ssl_certificate']); @@ -166,15 +176,6 @@ include("head.inc"); echo '<img src="'.$img_cert.'" title="SSL offloading cert: '.$descr.'" alt="SSL offloading" border="0" height="16" width="16" />'; } - $acls = get_frontend_acls($frontend); - $isaclset = ""; - foreach ($acls as $acl) { - $isaclset .= " " . htmlspecialchars($acl['descr']); - } - - if ($isaclset) - echo "<img src=\"$img_acl\" title=\"" . gettext("acl's used") . ": {$isaclset}\" border=\"0\" />"; - $isadvset = ""; if ($frontend['advanced_bind']) $isadvset .= "Advanced bind: ".htmlspecialchars($frontend['advanced_bind'])."\r\n"; if ($frontend['advanced']) $isadvset .= "Advanced pass thru setting used\r\n"; @@ -183,12 +184,15 @@ include("head.inc"); $backend_serverpool = $frontend['backend_serverpool']; $backend = get_backend($backend_serverpool ); - if ($backend && is_array($backend['ha_servers']['item'])){ + if ($backend && is_array($backend['ha_servers']) && 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']; + if (isset($server['forwardto']) && $server['forwardto'] != "") + $backend_serverpool_hint .= "\n[".$server['forwardto']."]"; + else + $backend_serverpool_hint .= "\n".$server['address'].":".$server['port']; } } } @@ -208,7 +212,9 @@ include("head.inc"); </td> <td class="listlr" ondblclick="document.location='haproxy_listeners_edit.php?id=<?=$frontendname;?>';"> <div title='<?=$backend_serverpool_hint;?>'> + <a href="haproxy_pool_edit.php?id=<?=$frontend['backend_serverpool']?>"> <?=$frontend['backend_serverpool']?> + </a> </div> </td> <td class="listlr" ondblclick="document.location='haproxy_listeners_edit.php?id=<?=$frontendname;?>';"> diff --git a/config/haproxy-devel/haproxy_listeners_edit.php b/config/haproxy-devel/haproxy_listeners_edit.php index 78423f6d..d243ffb1 100644 --- a/config/haproxy-devel/haproxy_listeners_edit.php +++ b/config/haproxy-devel/haproxy_listeners_edit.php @@ -52,7 +52,7 @@ function haproxy_js_acl_select($mode) { $seltext = ''; foreach ($a_acltypes as $key => $expr) { if ($expr['mode'] == '' || $expr['mode'] == $mode) - $seltext .= "<option value='" . $key . "'>" . $expr['name'] .":<\/option>"; + $seltext .= "<option value='" . $key . "'>" . $expr['name'] ."<\/option>"; } return $seltext; } @@ -63,10 +63,13 @@ if (!is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { $a_backend = &$config['installedpackages']['haproxy']['ha_backends']['item']; $a_pools = &$config['installedpackages']['haproxy']['ha_pools']['item']; +uasort($a_pools, haproxy_compareByName); global $simplefields; $simplefields = array('name','desc','status','secondary','primary_frontend','type','forwardfor','httpclose','extaddr','backend_serverpool', - 'max_connections','client_timeout','port','ssloffloadcert','dcertadv','ssloffload','ssloffloadacl','advanced_bind','ssloffloadacladditional'); + 'max_connections','client_timeout','port','ssloffloadcert','dcertadv','ssloffload','ssloffloadacl','ssloffloadaclnondefault','advanced_bind', + 'ssloffloadacladditional','ssloffloadacladditionalnondefault', + 'dontlognull','dontlog-normal','log-separate-errors','log-detailed'); if (isset($_POST['id'])) $id = $_POST['id']; @@ -84,8 +87,7 @@ if (!is_numeric($id)) $pconfig['ssloffloadacl'] = "yes"; } -$servercerts = get_certificates_server(); - +$servercerts = haproxy_get_certificates('server,user'); $fields_sslCertificates=array(); $fields_sslCertificates[0]['name']="ssl_certificate"; $fields_sslCertificates[0]['columnheader']="Certificates"; @@ -94,6 +96,28 @@ $fields_sslCertificates[0]['type']="select"; $fields_sslCertificates[0]['size']="500px"; $fields_sslCertificates[0]['items']=&$servercerts; +$certs_ca = haproxy_get_certificates('ca'); +$ca_none['']['name']="None"; +$certs_ca = $ca_none + $certs_ca; +$fields_caCertificates=array(); +$fields_caCertificates[0]['name']="cert_ca"; +$fields_caCertificates[0]['columnheader']="Certificates authorities"; +$fields_caCertificates[0]['colwidth']="95%"; +$fields_caCertificates[0]['type']="select"; +$fields_caCertificates[0]['size']="500px"; +$fields_caCertificates[0]['items']=&$certs_ca; + +$certs_crl = haproxy_get_crls(); +$ca_none['']['name']="None"; +$certs_crl = $ca_none + $certs_crl; +$fields_crlCertificates=array(); +$fields_crlCertificates[0]['name']="cert_crl"; +$fields_crlCertificates[0]['columnheader']="Certificate revocation lists"; +$fields_crlCertificates[0]['colwidth']="95%"; +$fields_crlCertificates[0]['type']="select"; +$fields_crlCertificates[0]['size']="500px"; +$fields_crlCertificates[0]['items']=&$certs_crl; + $fields_aclSelectionList=array(); $fields_aclSelectionList[0]['name']="name"; $fields_aclSelectionList[0]['columnheader']="Name"; @@ -114,17 +138,21 @@ $fields_aclSelectionList[2]['colwidth']="35%"; $fields_aclSelectionList[2]['type']="textbox"; $fields_aclSelectionList[2]['size']="35"; - if (isset($id) && $a_backend[$id]) { $pconfig['a_acl']=&$a_backend[$id]['ha_acls']['item']; $pconfig['a_certificates']=&$a_backend[$id]['ha_certificates']['item']; + $pconfig['clientcert_ca']=&$a_backend[$id]['clientcert_ca']['item']; + $pconfig['clientcert_crl']=&$a_backend[$id]['clientcert_crl']['item']; $pconfig['advanced'] = base64_decode($a_backend[$id]['advanced']); foreach($simplefields as $stat) $pconfig[$stat] = $a_backend[$id][$stat]; } -if (isset($_GET['dup'])) +if (isset($_GET['dup'])) { unset($id); + if ($pconfig['secondary'] != 'yes') + $pconfig['primary_frontend'] = $pconfig['name']; +} $changedesc = "Services: HAProxy: Frontend"; $changecount = 0; @@ -135,7 +163,6 @@ if ($_POST) { unset($input_errors); $pconfig = $_POST; - if ($pconfig['secondary'] != "yes") { $reqdfields = explode(" ", "name type port"); $reqdfieldsn = explode(",", "Name,Type,Port"); @@ -173,6 +200,10 @@ if ($_POST) { $a_certificates = haproxy_htmllist_get_values($fields_sslCertificates); $pconfig['a_certificates'] = $a_certificates; + $a_clientcert_ca = haproxy_htmllist_get_values($fields_caCertificates); + $pconfig['a_clientcert_ca'] = $a_clientcert_ca; + $a_clientcert_crl = haproxy_htmllist_get_values($fields_crlCertificates); + $pconfig['a_clientcert_crl'] = $a_clientcert_crl; $a_acl = haproxy_htmllist_get_values($fields_aclSelectionList); $pconfig['a_acl'] = $a_acl; @@ -214,6 +245,8 @@ if ($_POST) { update_if_changed("advanced", $backend['advanced'], base64_encode($_POST['advanced'])); $backend['ha_acls']['item'] = $a_acl; $backend['ha_certificates']['item'] = $a_certificates; + $backend['clientcert_ca']['item'] = $a_clientcert_ca; + $backend['clientcert_crl']['item'] = $a_clientcert_crl; if (isset($id) && $a_backend[$id]) { $a_backend[$id] = $backend; @@ -262,7 +295,7 @@ $interfaces = haproxy_get_bindable_interfaces(); <?php endif; ?> <script type="text/javascript"> - function htmllist_get_select_options(tableId) { + function htmllist_get_select_options(tableId, fieldname) { var seltext; seltext = ""; var type; @@ -290,6 +323,12 @@ $interfaces = haproxy_get_bindable_interfaces(); if (tableId == 'tableA_sslCertificates'){ seltext = "<?=haproxy_js_select_options($servercerts);?>"; } + if (tableId == 'table_clientcert_ca'){ + seltext = "<?=haproxy_js_select_options($certs_ca);?>"; + } + if (tableId == 'table_clientcert_crl'){ + seltext = "<?=haproxy_js_select_options($certs_crl);?>"; + } return seltext; } @@ -511,6 +550,48 @@ $interfaces = haproxy_get_bindable_interfaces(); For more information about ACL's please see <a href='http://haproxy.1wt.eu/download/1.5/doc/configuration.txt' target='_blank'>HAProxy Documentation</a> Section 7 - Using ACL's </td> </tr> + <tr class="haproxy_primary"><td> </td></tr> + <tr class="haproxy_primary"> + <td colspan="2" valign="top" class="listtopic">Logging options</td> + </tr> + <tr class="haproxy_primary" align="left"> + <td width="22%" valign="top" class="vncell">Dont log null</td> + <td width="78%" class="vtable" colspan="2"> + <input id="dontlognull" name="dontlognull" type="checkbox" value="yes" <?php if ($pconfig['dontlognull']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + A connection on which no data has been transferred will not be logged. + <div>To skip logging probes from monitoring systems that otherwise would pollute the logging. (It is generally recommended not to use this option in uncontrolled environments (eg: internet), otherwise scans and other malicious activities would not be logged.)</div> + </td> + </tr> + <tr class="haproxy_primary" align="left"> + <td width="22%" valign="top" class="vncell">Dont log normal</td> + <td width="78%" class="vtable" colspan="2"> + <input id="dontlog-normal" name="dontlog-normal" type="checkbox" value="yes" <?php if ($pconfig['dontlog-normal']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + Don't log connections in which no anomalies are found. + <div>Setting this option ensures that + normal connections, those which experience no error, no timeout, no retry nor + redispatch, will not be logged.</div> + </td> + </tr> + <tr class="haproxy_primary" align="left"> + <td width="22%" valign="top" class="vncell">Raise level for errors</td> + <td width="78%" class="vtable" colspan="2"> + <input id="log-separate-errors" name="log-separate-errors" type="checkbox" value="yes" <?php if ($pconfig['log-separate-errors']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + Change the level changes from "info" to "err" for potentially interesting information. + <div>This option makes haproxy raise the level of logs containing potentially interesting information such + as errors, timeouts, retries, redispatches, or HTTP status codes 5xx. </div> + </td> + </tr> + <tr class="haproxy_primary" align="left"> + <td width="22%" valign="top" class="vncell">Detailed logging</td> + <td width="78%" class="vtable" colspan="2"> + <input id="log-detailed" name="log-detailed" type="checkbox" value="yes" <?php if ($pconfig['log-detailed']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + If checked provides more detailed logging. + <div>Each log line turns into a much richer format including, but + not limited to, the connection timers, the session status, the connections + numbers, the frontend, backend and server name, and of course the source + address and ports. In http mode also the HTTP request and captured headers and cookies will be logged.</div> + </td> + </tr> </table> <br/> <br/> <table class="haproxy_primary" width="100%" border="0" cellpadding="6" cellspacing="0"> @@ -592,7 +673,8 @@ $interfaces = haproxy_get_bindable_interfaces(); <br/> NOTE: choose the cert to use on this frontend. <br/> - <input id="ssloffloadacl" name="ssloffloadacl" type="checkbox" value="yes" <?php if ($pconfig['ssloffloadacl']=='yes') echo "checked";?> onclick="updatevisibility();" />Add ACL for certificate CommonName. + <input id="ssloffloadacl" name="ssloffloadacl" type="checkbox" value="yes" <?php if ($pconfig['ssloffloadacl']=='yes') echo "checked";?> onclick="updatevisibility();" />Add ACL for certificate CommonName. (host header matches 'CN')<br/> + <input id="ssloffloadaclnondefault" name="ssloffloadaclnondefault" type="checkbox" value="yes" <?php if ($pconfig['ssloffloadaclnondefault']=='yes') echo "checked";?> onclick="updatevisibility();" />Add ACL for certificate CommonName for nondefault ports. (host header starts with 'CN:') </td> </tr> <tr class="haproxy_ssloffloading_enabled"> @@ -604,7 +686,8 @@ $interfaces = haproxy_get_bindable_interfaces(); haproxy_htmllist("tableA_sslCertificates", $a_certificates, $fields_sslCertificates); ?> <br/> - <input id="ssloffloadacladditional" name="ssloffloadacladditional" type="checkbox" value="yes" <?php if ($pconfig['ssloffloadacladditional']=='yes') echo "checked";?> onclick="updatevisibility();" />Add ACL for certificate CommonName. + <input id="ssloffloadacladditional" name="ssloffloadacladditional" type="checkbox" value="yes" <?php if ($pconfig['ssloffloadacladditional']=='yes') echo "checked";?> onclick="updatevisibility();" />Add ACL for certificate CommonName. (host header matches 'CN')<br/> + <input id="ssloffloadacladditionalnondefault" name="ssloffloadacladditionalnondefault" type="checkbox" value="yes" <?php if ($pconfig['ssloffloadacladditionalnondefault']=='yes') echo "checked";?> onclick="updatevisibility();" />Add ACL for certificate CommonName for nondefault ports. (host header starts with 'CN:') </td> </tr> <tr class="haproxy_ssloffloading_enabled haproxy_primary" align="left"> @@ -616,6 +699,29 @@ $interfaces = haproxy_get_bindable_interfaces(); some options: force-sslv3, force-tlsv10 force-tlsv11 force-tlsv12 no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets </td> </tr> + <tr class="haproxy_ssloffloading_enabled haproxy_primary"> + <td class="vncell" colspan="2"><b>Client certificate verification options, leave this empty if you do want to ask for a client certificate</b></td> + </tr> + <tr class="haproxy_ssloffloading_enabled haproxy_primary"> + <td width="22%" valign="top" class="vncell">Client verification CA certificates</td> + <td width="78%" class="vtable" colspan="2" valign="top"> + Client certificate will be verified against these CA certificates. + <? + $a_certificates = $pconfig['clientcert_ca']; + haproxy_htmllist("table_clientcert_ca", $a_certificates, $fields_caCertificates); + ?> + </td> + </tr> + <tr class="haproxy_ssloffloading_enabled haproxy_primary"> + <td width="22%" valign="top" class="vncell">Client verification CRL</td> + <td width="78%" class="vtable" colspan="2" valign="top"> + Client certificate will be verified against these CRL revocation lists. + <? + $a_certificates = $pconfig['clientcert_crl']; + haproxy_htmllist("table_clientcert_crl", $a_certificates, $fields_crlCertificates); + ?> + </td> + </tr> <tr> <td> </td> </tr> @@ -645,6 +751,8 @@ $interfaces = haproxy_get_bindable_interfaces(); phparray_to_javascriptarray($primaryfrontends,"primaryfrontends",Array('/*','/*/name','/*/ref','/*/ref/type','/*/ref/ssloffload')); phparray_to_javascriptarray($a_closetypes,"closetypes",Array('/*','/*/name','/*/descr')); phparray_to_javascriptarray($fields_sslCertificates,"fields_sslCertificates",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); + phparray_to_javascriptarray($fields_caCertificates,"fields_ca",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); + phparray_to_javascriptarray($fields_crlCertificates,"fields_crl",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); phparray_to_javascriptarray($fields_aclSelectionList,"fields_acltable",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); ?> </script> diff --git a/config/haproxy-devel/haproxy_pool_edit.php b/config/haproxy-devel/haproxy_pool_edit.php index cabc6e52..30079847 100644 --- a/config/haproxy-devel/haproxy_pool_edit.php +++ b/config/haproxy-devel/haproxy_pool_edit.php @@ -46,6 +46,10 @@ if (isset($_POST['id'])) $id = $_POST['id']; else $id = $_GET['id']; + +$tmp = get_backend_id($id); +if (is_numeric($tmp)) + $id = $tmp; if (isset($_GET['dup'])) $id = $_GET['dup']; @@ -53,57 +57,120 @@ if (isset($_GET['dup'])) global $simplefields; $simplefields = array( "name","balance","transparent_clientip","transparent_interface", -"check_type","checkinter","httpcheck_method","monitor_uri","monitor_httpversion","monitor_username","monitor_domain","monitor_agentport", +"check_type","checkinter","log-health-checks","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_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" +"strict_transport_security","cookie_attribute_secure" ); +$primaryfrontends = get_haproxy_frontends(); +$none = array(); +$none['']['name']="Address+Port:"; +$primaryfrontends = $none + $primaryfrontends; + $fields_servers=array(); $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]['size']="70px"; $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']="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']="20"; -$fields_servers[3]['name']="port"; -$fields_servers[3]['columnheader']="Port"; -$fields_servers[3]['colwidth']="5%"; +$fields_servers[2]['name']="forwardto"; +$fields_servers[2]['columnheader']="Forwardto"; +$fields_servers[2]['colwidth']="15%"; +$fields_servers[2]['type']="select"; +$fields_servers[2]['size']="100px"; +$fields_servers[2]['items']=&$primaryfrontends; +$fields_servers[3]['name']="address"; +$fields_servers[3]['columnheader']="Address"; +$fields_servers[3]['colwidth']="10%"; $fields_servers[3]['type']="textbox"; -$fields_servers[3]['size']="5"; -$fields_servers[4]['name']="ssl"; -$fields_servers[4]['columnheader']="SSL"; +$fields_servers[3]['size']="20"; +$fields_servers[4]['name']="port"; +$fields_servers[4]['columnheader']="Port"; $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[6]['name']="cookie"; -$fields_servers[6]['columnheader']="Cookie"; -$fields_servers[6]['colwidth']="10%"; +$fields_servers[4]['type']="textbox"; +$fields_servers[4]['size']="5"; +$fields_servers[5]['name']="ssl"; +$fields_servers[5]['columnheader']="SSL"; +$fields_servers[5]['colwidth']="5%"; +$fields_servers[5]['type']="checkbox"; +$fields_servers[5]['size']="30"; +$fields_servers[6]['name']="weight"; +$fields_servers[6]['columnheader']="Weight"; +$fields_servers[6]['colwidth']="8%"; $fields_servers[6]['type']="textbox"; -$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"; +$fields_servers[6]['size']="5"; + +$listitem_none['']['name']="None"; + +$certs_ca = haproxy_get_certificates('ca'); +$certs_ca = $listitem_none + $certs_ca; +$certs_client = haproxy_get_certificates('server,user'); +$certs_client = $listitem_none + $certs_client; +$certs_crl = haproxy_get_crls(); +$certs_crl = $listitem_none + $certs_crl; + +$fields_servers_details=array(); +$fields_servers_details[0]['name']="sslserververify"; +$fields_servers_details[0]['columnheader']="Check certificate"; +$fields_servers_details[0]['description']="SSL servers only, The server certificate will be verified against the CA and CRL certificate configured below."; +$fields_servers_details[0]['colwidth']="5%"; +$fields_servers_details[0]['type']="checkbox"; +$fields_servers_details[0]['size']="5"; +$fields_servers_details[1]['name']="verifyhost"; +$fields_servers_details[1]['columnheader']="Certificate check CN"; +$fields_servers_details[1]['description']="SSL servers only, when set, must match the hostnames in the subject and subjectAlternateNames of the certificate provided by the server."; +$fields_servers_details[1]['colwidth']="5%"; +$fields_servers_details[1]['type']="textbox"; +$fields_servers_details[1]['size']="50"; +$fields_servers_details[2]['name']="ssl-server-ca"; +$fields_servers_details[2]['columnheader']="CA"; +$fields_servers_details[2]['description']="SSL servers only, Select the CA authority to check the server certificate against."; +$fields_servers_details[2]['colwidth']="15%"; +$fields_servers_details[2]['type']="select"; +$fields_servers_details[2]['size']="200px"; +$fields_servers_details[2]['items']=$certs_ca; +$fields_servers_details[3]['name']="ssl-server-crl"; +$fields_servers_details[3]['columnheader']="CRL"; +$fields_servers_details[3]['description']="SSL servers only, Select the CRL to check revoked certificates."; +$fields_servers_details[3]['colwidth']="15%"; +$fields_servers_details[3]['type']="select"; +$fields_servers_details[3]['size']="200px"; +$fields_servers_details[3]['items']=$certs_crl; +$fields_servers_details[4]['name']="ssl-server-clientcert"; +$fields_servers_details[4]['columnheader']="Client certificate"; +$fields_servers_details[4]['description']="SSL servers only, This certificate will be sent if the server send a client certificate request."; +$fields_servers_details[4]['colwidth']="15%"; +$fields_servers_details[4]['type']="select"; +$fields_servers_details[4]['size']="200px"; +$fields_servers_details[4]['items']=$certs_client; +$fields_servers_details[5]['name']="cookie"; +$fields_servers_details[5]['columnheader']="Cookie"; +$fields_servers_details[5]['description']="Persistence only, Used to identify server when cookie persistence is configured for the backend."; +$fields_servers_details[5]['colwidth']="10%"; +$fields_servers_details[5]['type']="textbox"; +$fields_servers_details[5]['size']="10"; +$fields_servers_details[6]['name']="maxconn"; +$fields_servers_details[6]['columnheader']="Max conn"; +$fields_servers_details[6]['description']="Tuning, If the number of incoming concurrent requests goes higher than this value, they will be queued"; +$fields_servers_details[6]['colwidth']="15%"; +$fields_servers_details[6]['type']="textbox"; +$fields_servers_details[6]['size']="10"; +$fields_servers_details[7]['name']="advanced"; +$fields_servers_details[7]['columnheader']="Advanced"; +$fields_servers_details[7]['description']="Advanced, Allows for adding custom HAProxy settings to the server. These are passed as written, use escaping where needed."; +$fields_servers_details[7]['colwidth']="15%"; +$fields_servers_details[7]['type']="textbox"; +$fields_servers_details[7]['size']="80"; if (isset($id) && $a_pools[$id]) { $pconfig['advanced'] = base64_decode($a_pools[$id]['advanced']); @@ -129,26 +196,16 @@ if ($_POST) { $reqdfields = explode(" ", "name"); $reqdfieldsn = explode(",", "Name"); - $pf_version=substr(trim(file_get_contents("/etc/version")),0,3); - if ($pf_version < 2.1) - $input_errors = eval('do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); return $input_errors;'); - else - do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); if ($_POST['stats_enabled']) { $reqdfields = explode(" ", "name stats_uri"); $reqdfieldsn = explode(",", "Name,Stats Uri"); - if ($pf_version < 2.1) - $input_errors = eval('do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); return $input_errors;'); - else - do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); if ($_POST['stats_username']) { $reqdfields = explode(" ", "stats_password stats_realm"); $reqdfieldsn = explode(",", "Stats Password,Stats Realm"); - if ($pf_version < 2.1) - $input_errors = eval('do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); return $input_errors;'); - else - do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); } } @@ -183,7 +240,7 @@ if ($_POST) { if (($_POST['name'] == $config['installedpackages']['haproxy']['ha_pools']['item'][$i]['name']) && ($i != $id)) $input_errors[] = "This pool name has already been used. Pool names must be unique."; - $a_servers = haproxy_htmllist_get_values($fields_servers); + $a_servers = haproxy_htmllist_get_values(array_merge($fields_servers,$fields_servers_details)); foreach($a_servers as $server){ $server_name = $server['name']; $server_address = $server['address']; @@ -193,8 +250,14 @@ if ($_POST) { if (preg_match("/[^a-zA-Z0-9\.\-_]/", $server_name)) $input_errors[] = "The field 'Name' contains invalid characters."; - if (!is_ipaddr($server_address) && !is_hostname($server_address)) - $input_errors[] = "The field 'Address' is not a valid ip address or hostname."; + if (!isset($server['forwardto']) || $server['forwardto'] == "") { + if (!is_ipaddr($server_address) && !is_hostname($server_address) && !haproxy_is_frontendname($server_address)) + $input_errors[] = "The field 'Address' for server $server_name is not a valid ip address or hostname." . $server_address; + } else { + if ( ($server_address && $server_address != "") || ($server_port && !is_numeric($server_port))) { + $input_errors[] = "'Address' and 'port' should be empty when a 'Forwardto' frontend is chosen other than 'Address+Port'."; + } + } if (!preg_match("/.{2,}/", $server_name)) $input_errors[] = "The field 'Name' is required (and must be at least 2 characters)."; @@ -209,7 +272,7 @@ if ($_POST) { 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) { +// if (!$input_errors) { $pool = array(); if(isset($id) && $a_pools[$id]) $pool = $a_pools[$id]; @@ -244,7 +307,7 @@ if ($_POST) { } else { $a_pools[] = $pool; } - + if (!isset($input_errors)) { if ($changecount > 0) { touch($d_haproxyconfdirty_path); write_config($changedesc); @@ -261,10 +324,6 @@ if ($_POST) { $pconfig['a_servers']=&$a_pools[$id]['ha_servers']['item']; } -$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); -if ($pf_version < 2.0) - $one_two = true; - $closehead = false; $pgtitle = "HAProxy: Backend server pool: Edit"; include("head.inc"); @@ -276,6 +335,7 @@ foreach($simplefields as $field){ ?> <style type="text/css"> + .tableA_servers_details_visible{display:none;} .haproxy_stats_visible{display:none;} .haproxy_check_enabled{display:none;} .haproxy_check_http{display:none;} @@ -292,8 +352,11 @@ foreach($simplefields as $field){ </head> <body link="#0000CC" vlink="#0000CC" alink="#0000CC"> <script type="text/javascript"> - function htmllist_get_select_options(tableId) { - return "<?=haproxy_js_select_options($a_servermodes);?>"; + function htmllist_get_select_options(tableId, fieldname) { + if (fieldname == 'forwardto') + return "<?=haproxy_js_select_options($primaryfrontends);?>"; + else + return "<?=haproxy_js_select_options($a_servermodes);?>"; } function clearcombo(){ @@ -331,6 +394,7 @@ foreach($simplefields as $field){ function updatevisibility() { d = document; + setCSSdisplay(".tableA_servers_details_visible", server_advanced_options_visible.checked); setCSSdisplay(".haproxy_stats_visible", stats_enabled.checked); setCSSdisplay(".haproxy_cookie_visible", persist_cookie_enabled.checked); @@ -375,10 +439,7 @@ foreach($simplefields as $field){ } </script> <?php include("fbegin.inc"); ?> -<?php if ($input_errors) print_input_errors($input_errors); ?> -<?php if($one_two): ?> -<p class="pgtitle"><?=$pgtitle?></p> -<?php endif; ?> +<?php if (isset($input_errors)) print_input_errors($input_errors); ?> <form action="haproxy_pool_edit.php" method="post" name="iform" id="iform"> <table width="100%" border="0" cellpadding="0" cellspacing="0"> @@ -401,14 +462,14 @@ foreach($simplefields as $field){ </td> </tr> <tr align="left"> - <td class="vncell" colspan="3"><strong>Server list</strong> + <td class="vncell" colspan="3"><strong>Server list</strong><input id="server_advanced_options_visible" name="server_advanced_options_visible" type='checkbox' onclick="updatevisibility();">Show advanced options(servers need to first be saved to configure these settings)</input> <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); + haproxy_htmllist("tableA_servers", $a_servers, $fields_servers, null, $fields_servers_details); ?> <table class="haproxy_help_serverlist" style="border:1px dashed green" cellspacing="0"> <tr><td class="vncell"> @@ -521,10 +582,11 @@ foreach($simplefields as $field){ </div> <br/> Connect transparently to the backend server's so the connection seams to come straight from the client ip address. - 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") + To work properly this requires the reply traffic to pass through pfSense by means of correct routing.<br/> + When using IPv6 only routable ip addresses can be used, host names or link-local addresses (FE80) will not work.<br/> + (uses the option "source 0.0.0.0 usesrc clientip" or "source ipv6@ usesrc clientip") <br/><br/> - 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. + Note : When this is enabled for any backend HAProxy will run as 'root' instead of chrooting to a lower privileged user, this reduces security in case a vulnerability is found. </td> </tr> <tr align="left"> @@ -565,6 +627,15 @@ foreach($simplefields as $field){ <br/>For HTTP/HTTPS defaults to 1000 if left blank. For TCP no check will be performed if left empty. </td> </tr> + <tr align="left" class="haproxy_check_enabled"> + <td width="22%" valign="top" class="vncell">Log checks</td> + <td width="78%" class="vtable" colspan="2"> + <input id="log-health-checks" name="log-health-checks" type="checkbox" value="yes" <?php if ($pconfig['log-health-checks']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + When this option is enabled, any change of the health check status or to the server's health will be logged. + <br/> + By default, failed health check are logged if server is UP and successful health checks are logged if server is DOWN, so the amount of additional information is limited. + </td> + </tr> <tr align="left" class="haproxy_check_http"> <td width="22%" valign="top" class="vncell">Http check method</td> <td width="78%" class="vtable" colspan="2"> @@ -774,6 +845,7 @@ set by the 'retries' parameter.</div> <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();' /> + Enables the haproxy statistics page (only used on 'http' frontends) </td> </tr> <tr class="haproxy_stats_visible" align="left" id='stats_uri_row'> @@ -853,7 +925,7 @@ set by the 'retries' parameter.</div> <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/> + When configured enables "HTTP Strict Transport Security" leave empty to disable. (only used on 'http' frontends)<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. @@ -861,6 +933,14 @@ set by the 'retries' parameter.</div> EXAMPLE: 60 for testing if you are absolutely sure you want this 31536000 (12 months) would be good for production. </td> </tr> + <tr class="" align="left"> + <td width="22%" valign="top" class="vncell">Cookie protection.</td> + <td width="78%" class="vtable" colspan="2"> + <input id="cookie_attribute_secure" name="cookie_attribute_secure" type="checkbox" value="yes" <?php if ($pconfig['cookie_attribute_secure']=='yes') echo "checked"; ?> onclick='updatevisibility();' /> + Set 'secure' attribure on cookies (only used on 'http' frontends)<br/> + This configuration option sets up the Secure attribute on cookies if it has not been setup by the application server while the client was browsing the application over a ciphered connection. + </td> + </tr> <tr><td> </td></tr> <tr align="left"> <td width="22%" valign="top"> </td> @@ -880,6 +960,7 @@ set by the 'retries' parameter.</div> <script type="text/javascript"> <? phparray_to_javascriptarray($fields_servers,"fields_servers",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); + phparray_to_javascriptarray($fields_servers_details,"fields_details_servers",Array('/*','/*/name','/*/type')); 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')); diff --git a/config/haproxy-devel/haproxy_socketinfo.inc b/config/haproxy-devel/haproxy_socketinfo.inc index 6beb17c5..cbfb131b 100644 --- a/config/haproxy-devel/haproxy_socketinfo.inc +++ b/config/haproxy-devel/haproxy_socketinfo.inc @@ -122,6 +122,8 @@ function haproxy_get_clients($show_traffic = false){// "show sess" $clients=array(); $sessions = haproxy_socket_command("show sess"); foreach($sessions as $line) { + if (trim($line) == "") + continue; list($sessid,$proto,$src,$fe,$be,$srv,$ts,$age,$calls,$rq,$rp,$s0,$s1,$exp) = explode(" ", $line); #Retrieve data $sessid = explode(":", $sessid); @@ -149,10 +151,10 @@ function haproxy_get_clients($show_traffic = false){// "show sess" $session_data = haproxy_socket_command("show sess {$client['sessid']}"); $client['session_data'] = $session_data; - $req = explode(" ",$session_data[13]); + $req = explode(" ",$session_data[12]); $x = explode("=",$req[7]); $client['session_datareq'] = $x[1]; - $res = explode(" ",$session_data[16]); + $res = explode(" ",$session_data[15]); $x = explode("=",$res[7]); $client['session_datares'] = $x[1]; } diff --git a/config/haproxy-devel/haproxy_utils.inc b/config/haproxy-devel/haproxy_utils.inc index 03bd434f..8fb89eab 100644 --- a/config/haproxy-devel/haproxy_utils.inc +++ b/config/haproxy-devel/haproxy_utils.inc @@ -33,6 +33,21 @@ require_once("config.inc"); +class haproxy_utils { + public static $pf_version; +} +haproxy_utils::$pf_version = substr(trim(file_get_contents("/etc/version")),0,3); + +if(!function_exists('ifset')){ + function ifset(&$var, $default = ''){ + return isset($var) ? $var : $default; + }; +} + +function haproxy_compareByName($a, $b) { + return strcasecmp($a['name'], $b['name']); +} + function haproxy_interface_ip($interfacebindname,$userfriendly=false){ $list = haproxy_get_bindable_interfaces(); $item = $list[$interfacebindname]; @@ -203,20 +218,11 @@ function haproxy_cert_signed_by($cert, $signedbycert) { return $authoritykeyid == $subjectid; } -function haproxy_get_certificates(){ - global $config; - $allcerts = array(); - foreach($config['cert'] as &$cert) - $allcerts[] = &$cert; - foreach($config['ca'] as &$cert) - $allcerts[] = &$cert; - return $allcerts; -} function haproxy_recalculate_certifcate_chain(){ // and set "selfsigned" for certificates that where used to sign themselves // recalculate the "caref" for all certificates where it is currently unkown. - $allcertificates = haproxy_get_certificates(); + $allcertificates = haproxy_get_certificates('ca,server,user',true); $items_recalculated = 0; foreach($allcertificates as &$cert){ $recalculate=false; @@ -251,7 +257,7 @@ function get_certificat_usage($refid) { $usage = array(); $cert = lookup_cert($refid); if (is_cert_revoked($cert)) - $usage[] = "Revoked"; + $usage[] = "*Revoked"; if (is_webgui_cert($refid)) $usage[] = "webConfigurator"; if (is_user_cert($refid)) @@ -260,55 +266,86 @@ function get_certificat_usage($refid) { $usage[] = "OpenVPN Server"; if (is_openvpn_client_cert($refid)) $usage[] = "OpenVPN Client"; - if (is_ipsec_cert($cert['refid'])) + if (is_ipsec_cert($refid)) $usage[] = "IPsec Tunnel"; if (function_exists("is_captiveportal_cert")) if (is_captiveportal_cert($refid)) $usage[] = "Captive Portal"; - return $usage; } -function get_certificates_server($get_includeWebCert=false) { - // This function (is intended to) provide a uniform way to retrieve a list of server certificates - global $config; - $certificates=array(); - $a_cert = &$config['cert']; - foreach ($a_cert as $cert) - { - if ($get_ca == false && is_webgui_cert($cert['refid'])) - continue; - $purpose = cert_get_purpose($cert['crt']); - //$certserverpurpose = $purpose['server'] == 'Yes' ? " [Server certificate]" : ""; - $certserverpurpose = ""; +function haproxy_get_certificate_descriptivename($cert) { + $usage = get_certificat_usage($cert['crt']); + foreach($usage as $use){ + $usagestr .= " " . $use; + } + if ($usagestr != "") + $usagestr = " (".trim($usagestr).")"; - $selected = ""; - $caname = ""; - $inuse = ""; - $revoked = ""; - $ca = lookup_ca($cert['caref']); - if ($ca) - $caname = " (CA: {$ca['descr']})"; - if ($pconfig['certref'] == $cert['refid']) - $selected = "selected"; - if (cert_in_use($cert['refid'])) - $inuse = " *In Use"; - if (is_cert_revoked($cert)) - $revoked = " *Revoked"; + $purpose = cert_get_purpose($cert['crt']); + $certserverpurpose = $purpose['server'] == 'Yes' ? " [Server cert]" : ""; - $usagestr=""; - $usage = get_certificat_usage($cert['refid']); - foreach($usage as $use){ - $usagestr .= " " . $use; - } - if ($usagestr != "") - $usagestr = " (".trim($usagestr).")"; - - $certificates[$cert['refid']]['name'] = $cert['descr'] . $caname . $certserverpurpose . $inuse . $revoked . $usagestr; + $caname = ""; + $ca = lookup_ca($cert['caref']); + if ($ca) + $caname = " (CA: {$ca['descr']})"; + + return $cert['descr'] . $caname . $certserverpurpose . $usagestr; +} + +function haproxy_get_certificates($type = 'server,user', $get_includeWebCert=false) { + // $type one or multiple of these separated by a comma: ca,server,user + // $get_includeWebCert if the webgui certificate may be included. + + // This function (is intended to) provide a uniform way to retrieve a list of server certificates + global $config; + $type = ",$type,"; + $certificates = array(); + if (strpos($type,',server,') !== false || strpos($type,',user,') !== false ) { + if (is_array($config['cert'])) { + $a_cert = &$config['cert']; + foreach ($a_cert as $cert) { + $purpose = cert_get_purpose($cert['crt']); + + $ok = false; + $ok |= stristr($type,',server,') && $purpose['server'] == 'Yes'; + $ok |= stristr($type,',user,') && $purpose['server'] != 'Yes'; + if (!$ok) + continue; + if ($get_includeWebCert == false && is_webgui_cert($cert['refid'])) + continue; + $certificates[$cert['refid']]['name'] = haproxy_get_certificate_descriptivename($cert); + } + } } + if (strpos($type,',ca,') !== false) { + if (is_array($config['ca'])) { + $a_cert = &$config['ca']; + foreach ($a_cert as $cert) { + $certificates[$cert['refid']]['name'] = haproxy_get_certificate_descriptivename($cert); + } + } + } + uasort($certificates, haproxy_compareByName); return $certificates; } +function haproxy_get_crls() { + global $config; + $certificates=array(); + if (is_array($config['crl'])) { + foreach ($config['crl'] as $crl) { + $caname = ""; + $ca = lookup_ca($crl['caref']); + if ($ca) + $caname = " (CA: {$ca['descr']})"; + + $certificates[$crl['refid']]['name'] = $crl['descr'] . $caname; + } + } + uasort($certificates, haproxy_compareByName); + return $certificates; +} function phparray_to_javascriptarray_recursive($nestID, $path, $items, $nodeName, $includeitems) { $offset = str_repeat(' ',$nestID); |