From e4476ce6a6cedab8609bfe86b5646d15b47390cf Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Tue, 10 Jan 2017 14:20:07 +0100 Subject: [PATCH 1/7] Revert "Fix SSL setup for haproxy 1.4 release" We have haproxy 1.5.x now, so we can use persistence for SSL sessions. This matters as if we want to allow people to not use "source" as balance algorithm, then we need to make sure that sessions keep going to the same backend to avoid breakages. This reverts commit 32323b0a4b17d64489735d307b9fe65b8bb4ed31. (cherry picked from commit ba2fd30d429a74c237606fff1b7c72e348700aa5) --- .../haproxy/templates/default/haproxy.cfg.erb | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index 1ba918be..0a3e4ebe 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -40,9 +40,30 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats <% content = node[:haproxy][:sections][type][name] -%> <%= type %> <%= name %> bind <%= content[:address] %>:<%= content[:port] %> - <% if content[:use_ssl] -%> - mode tcp - balance source + <% if content[:use_ssl] # http://blog.exceliance.fr/2011/07/04/maintain-affinity-based-on-ssl-session-id/ -%> + mode tcp + + # maximum SSL session ID length is 32 bytes. + stick-table type binary len 32 size 30k expire 30m + + acl clienthello req_ssl_hello_type 1 + acl serverhello rep_ssl_hello_type 2 + + # use tcp content accepts to detects ssl client and server hello. + tcp-request inspect-delay 5s + tcp-request content accept if clienthello + + # no timeout on response inspect delay by default. + tcp-response content accept if serverhello + + # SSL session ID (SSLID) may be present on a client or server hello. + # Its length is coded on 1 byte at offset 43 and its value starts + # at offset 44. + # Match and learn on request if client hello. + stick on payload_lv(43,1) if clienthello + + # Learn on response if server hello. + stick store-response payload_lv(43,1) if serverhello <% else -%> mode <%= content[:mode] %> <% end -%> From 8b0f2d303a9231c8acaff3fdceff291294d617ab Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Tue, 10 Jan 2017 14:21:00 +0100 Subject: [PATCH 2/7] haproxy: Do not enforce mode as tcp for ssl twice This is done in the LWRP and in the template; let's just do it in the LWRP to simplify the template. (cherry picked from commit ba3d9fd0b9d8748d359a2c4d7d0bc5d8a92f1f5e) --- chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index 0a3e4ebe..97a3b2e0 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -40,9 +40,9 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats <% content = node[:haproxy][:sections][type][name] -%> <%= type %> <%= name %> bind <%= content[:address] %>:<%= content[:port] %> - <% if content[:use_ssl] # http://blog.exceliance.fr/2011/07/04/maintain-affinity-based-on-ssl-session-id/ -%> - mode tcp + mode <%= content[:mode] %> + <% if content[:use_ssl] # http://blog.exceliance.fr/2011/07/04/maintain-affinity-based-on-ssl-session-id/ -%> # maximum SSL session ID length is 32 bytes. stick-table type binary len 32 size 30k expire 30m @@ -64,8 +64,6 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats # Learn on response if server hello. stick store-response payload_lv(43,1) if serverhello - <% else -%> - mode <%= content[:mode] %> <% end -%> <% content[:options].each do |option| -%> From 65d0ee6f648bee865631785f48298722239b7d81 Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Wed, 11 Jan 2017 15:50:45 +0100 Subject: [PATCH 3/7] haproxy: Add ability to configure stickiness based on cookies This is useful to achieve persistence for web apps which have a session, which is important in order to allow using a different algorithm than "source" for balancing without breaking sessions. (cherry picked from commit 74115353ff1a9f8a6eeb9f5f36c5b6ebb9ff04cf) --- chef/cookbooks/haproxy/providers/loadbalancer.rb | 4 ++++ chef/cookbooks/haproxy/resources/loadbalancer.rb | 1 + .../haproxy/templates/default/haproxy.cfg.erb | 13 ++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/chef/cookbooks/haproxy/providers/loadbalancer.rb b/chef/cookbooks/haproxy/providers/loadbalancer.rb index ada975dd..8d0cc888 100644 --- a/chef/cookbooks/haproxy/providers/loadbalancer.rb +++ b/chef/cookbooks/haproxy/providers/loadbalancer.rb @@ -47,6 +47,10 @@ else section["mode"] = new_resource.mode end + + section["stick"] = new_resource.stick + section["stick"]["expire"] ||= "30m" + section["options"] = new_resource.options || [] if section["options"].empty? || section["options"].include?("defaults") section["options"].delete("defaults") diff --git a/chef/cookbooks/haproxy/resources/loadbalancer.rb b/chef/cookbooks/haproxy/resources/loadbalancer.rb index 64e498fc..536a7bcd 100644 --- a/chef/cookbooks/haproxy/resources/loadbalancer.rb +++ b/chef/cookbooks/haproxy/resources/loadbalancer.rb @@ -26,5 +26,6 @@ attribute :port, kind_of: Integer, default: 0 attribute :mode, kind_of: String, default: "http", equal_to: ["http", "tcp", "health"] attribute :use_ssl, kind_of: [TrueClass, FalseClass], default: false +attribute :stick, kind_of: Hash, default: {} attribute :options, kind_of: Array, default: [] attribute :servers, kind_of: Array, default: [] diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index 97a3b2e0..5df86bf6 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -44,7 +44,7 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats <% if content[:use_ssl] # http://blog.exceliance.fr/2011/07/04/maintain-affinity-based-on-ssl-session-id/ -%> # maximum SSL session ID length is 32 bytes. - stick-table type binary len 32 size 30k expire 30m + stick-table type binary len 32 size 30k expire <%= content[:stick][:expire] %> acl clienthello req_ssl_hello_type 1 acl serverhello rep_ssl_hello_type 2 @@ -64,6 +64,17 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats # Learn on response if server hello. stick store-response payload_lv(43,1) if serverhello + <% elsif content[:mode] == "http" && content[:stick] && content[:stick][:cookie] + # There are various options here, described in: + # http://stackoverflow.com/questions/27094501/haproxy-1-5-8-how-do-i-configure-cookie-based-stickiness + # We go with the stick-table to avoid no-cache and exposing backends + # through cookies. + # Note that appsession is easier, but deprecated: + # http://serverfault.com/questions/550910/haproxy-appsession-vs-cookie-precedence + -%> + stick-table type string len 64 size 100k expire <%= content[:stick][:expire] %> + stick store-response res.cook(<%= content[:stick][:cookie] %>) + stick match req.cook(<%= content[:stick][:cookie] %>) <% end -%> <% content[:options].each do |option| -%> From 4d3d927948563d1bc11d5e56f55f55a4d27221e4 Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Wed, 8 Feb 2017 13:34:30 +0100 Subject: [PATCH 4/7] haproxy: Add ability to do stickiness based on more than one cookie This allows achieving persistence for a normal session, but also for the login form where there's usually a CSRF token (and which is not associated to a real session in the web app). (cherry picked from commit 8c3fdbf703395ff577d071ea0f0d2241ea095837) --- chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index 5df86bf6..e52f107b 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -64,7 +64,8 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats # Learn on response if server hello. stick store-response payload_lv(43,1) if serverhello - <% elsif content[:mode] == "http" && content[:stick] && content[:stick][:cookie] + <% elsif content[:mode] == "http" && content[:stick] && + content[:stick][:cookies] && !content[:stick][:cookies].empty? # There are various options here, described in: # http://stackoverflow.com/questions/27094501/haproxy-1-5-8-how-do-i-configure-cookie-based-stickiness # We go with the stick-table to avoid no-cache and exposing backends @@ -73,8 +74,10 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats # http://serverfault.com/questions/550910/haproxy-appsession-vs-cookie-precedence -%> stick-table type string len 64 size 100k expire <%= content[:stick][:expire] %> - stick store-response res.cook(<%= content[:stick][:cookie] %>) - stick match req.cook(<%= content[:stick][:cookie] %>) + <% content[:stick][:cookies].each do |cookie| -%> + stick store-response res.cook(<%= cookie %>) + stick match req.cook(<%= cookie %>) + <% end -%> <% end -%> <% content[:options].each do |option| -%> From 53012ff8b684a0f6f0e661b90a20bc96e7cd8403 Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Tue, 10 Jan 2017 14:58:42 +0100 Subject: [PATCH 5/7] haproxy: Add balance attribute to haproxy_loadbalancer resource This enables customization of the balance strategy for each service. (cherry picked from commit 1c6dbf8db507eb9b121e0c545b57f6645ffd4311) --- chef/cookbooks/haproxy/providers/loadbalancer.rb | 3 +++ chef/cookbooks/haproxy/resources/loadbalancer.rb | 1 + chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb | 3 +++ 3 files changed, 7 insertions(+) diff --git a/chef/cookbooks/haproxy/providers/loadbalancer.rb b/chef/cookbooks/haproxy/providers/loadbalancer.rb index 8d0cc888..e25d108f 100644 --- a/chef/cookbooks/haproxy/providers/loadbalancer.rb +++ b/chef/cookbooks/haproxy/providers/loadbalancer.rb @@ -47,6 +47,9 @@ else section["mode"] = new_resource.mode end + unless new_resource.balance.empty? + section["balance"] = new_resource.balance + end section["stick"] = new_resource.stick section["stick"]["expire"] ||= "30m" diff --git a/chef/cookbooks/haproxy/resources/loadbalancer.rb b/chef/cookbooks/haproxy/resources/loadbalancer.rb index 536a7bcd..1ee82346 100644 --- a/chef/cookbooks/haproxy/resources/loadbalancer.rb +++ b/chef/cookbooks/haproxy/resources/loadbalancer.rb @@ -25,6 +25,7 @@ attribute :address, kind_of: String, default: "0.0.0.0" attribute :port, kind_of: Integer, default: 0 attribute :mode, kind_of: String, default: "http", equal_to: ["http", "tcp", "health"] +attribute :balance, kind_of: String, default: "", equal_to: ["", "roundrobin", "static-rr", "leastconn", "first", "source"] attribute :use_ssl, kind_of: [TrueClass, FalseClass], default: false attribute :stick, kind_of: Hash, default: {} attribute :options, kind_of: Array, default: [] diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index e52f107b..877c7ca5 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -41,6 +41,9 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats <%= type %> <%= name %> bind <%= content[:address] %>:<%= content[:port] %> mode <%= content[:mode] %> + <% unless content[:balance].nil? -%> + balance <%= content[:balance] %> + <% end -%> <% if content[:use_ssl] # http://blog.exceliance.fr/2011/07/04/maintain-affinity-based-on-ssl-session-id/ -%> # maximum SSL session ID length is 32 bytes. From 72df165dca10facab0944af6348512b4c1a87104 Mon Sep 17 00:00:00 2001 From: Vincent Untz Date: Wed, 8 Feb 2017 11:23:41 +0100 Subject: [PATCH 6/7] crowbar-pacemaker: Stop changing default haproxy balance strategy The default in the haproxy cookbook is roundrobin, and this should actually work fine. In cases where this may be troublesome (like web apps), we can now configure stickiness to avoid issues. With roundrobin, we spread the load accross the various backends, which results in much improved performance. (cherry picked from commit 592b5d9e47e795c3ca7c73e068ceedf6e64fd755) --- chef/cookbooks/crowbar-pacemaker/recipes/haproxy.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/chef/cookbooks/crowbar-pacemaker/recipes/haproxy.rb b/chef/cookbooks/crowbar-pacemaker/recipes/haproxy.rb index 382efef2..7357c030 100644 --- a/chef/cookbooks/crowbar-pacemaker/recipes/haproxy.rb +++ b/chef/cookbooks/crowbar-pacemaker/recipes/haproxy.rb @@ -20,11 +20,6 @@ #FIXME: delete group when it's not needed anymore #FIXME: need to find/write OCF for haproxy -# Recommendation from the OpenStack HA guide is to use "source" as balance -# algorithm. This obviously is less useful for load balancing, but we care more -# about HA and things working than about load balancing. -node.default["haproxy"]["defaults"]["balance"] = "source" - # With the default bufsize, getting a keystone PKI token from its ID doesn't # work, because the URI path is too long for haproxy node.default["haproxy"]["global"]["bufsize"] = 32768 From 5fd490b70358f330e93f4d64b659fffda33c8474 Mon Sep 17 00:00:00 2001 From: Mate Lakat Date: Thu, 13 Jul 2017 10:43:52 +0200 Subject: [PATCH 7/7] Address style issues and review comments (cherry picked from commit c07c2556608a3a2350e6f1acf294855c5e29bd40) --- chef/cookbooks/haproxy/providers/loadbalancer.rb | 4 +--- chef/cookbooks/haproxy/resources/loadbalancer.rb | 3 ++- chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/chef/cookbooks/haproxy/providers/loadbalancer.rb b/chef/cookbooks/haproxy/providers/loadbalancer.rb index e25d108f..5b8d71f2 100644 --- a/chef/cookbooks/haproxy/providers/loadbalancer.rb +++ b/chef/cookbooks/haproxy/providers/loadbalancer.rb @@ -47,9 +47,7 @@ else section["mode"] = new_resource.mode end - unless new_resource.balance.empty? - section["balance"] = new_resource.balance - end + section["balance"] = new_resource.balance unless new_resource.balance.empty? section["stick"] = new_resource.stick section["stick"]["expire"] ||= "30m" diff --git a/chef/cookbooks/haproxy/resources/loadbalancer.rb b/chef/cookbooks/haproxy/resources/loadbalancer.rb index 1ee82346..438d4162 100644 --- a/chef/cookbooks/haproxy/resources/loadbalancer.rb +++ b/chef/cookbooks/haproxy/resources/loadbalancer.rb @@ -25,7 +25,8 @@ attribute :address, kind_of: String, default: "0.0.0.0" attribute :port, kind_of: Integer, default: 0 attribute :mode, kind_of: String, default: "http", equal_to: ["http", "tcp", "health"] -attribute :balance, kind_of: String, default: "", equal_to: ["", "roundrobin", "static-rr", "leastconn", "first", "source"] +attribute :balance, kind_of: String, default: "", + equal_to: ["", "roundrobin", "static-rr", "leastconn", "first", "source"] attribute :use_ssl, kind_of: [TrueClass, FalseClass], default: false attribute :stick, kind_of: Hash, default: {} attribute :options, kind_of: Array, default: [] diff --git a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb index 877c7ca5..b5a90f92 100644 --- a/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb +++ b/chef/cookbooks/haproxy/templates/default/haproxy.cfg.erb @@ -45,7 +45,7 @@ listen admin-stats <%= node[:haproxy][:stats][:enabled] ? node[:haproxy][:stats balance <%= content[:balance] %> <% end -%> - <% if content[:use_ssl] # http://blog.exceliance.fr/2011/07/04/maintain-affinity-based-on-ssl-session-id/ -%> + <% if content[:use_ssl] # http://www.haproxy.com/blog/maintain-affinity-based-on-ssl-session-id/ -%> # maximum SSL session ID length is 32 bytes. stick-table type binary len 32 size 30k expire <%= content[:stick][:expire] %>