=========================================================
3. Simple HTTP/HTTPS load-balancing with cookie insertion
=========================================================
This is the same context as in example 1 above, but the web
server uses HTTPS.
+-------+
|clients| clients
+---+---+
|
-+-----+--------+----
| _|_db
+--+--+ (___)
| SSL | (___)
| web | (___)
+-----+
192.168.1.1 192.168.1.2
Since haproxy does not handle SSL, this part will have to be extracted from the
servers (freeing even more ressources) and installed on the load-balancer
itself. Install haproxy and apache+mod_ssl on the old box which will spread the
load between the new boxes. Apache will work in SSL reverse-proxy-cache. If the
application is correctly developped, it might even lower its load. However,
since there now is a cache between the clients and haproxy, some security
measures must be taken to ensure that inserted cookies will not be cached.
192.168.1.1 192.168.1.11-192.168.1.14 192.168.1.2
-------+-----------+-----+-----+-----+--------+----
| | | | | _|_db
+--+--+ +-+-+ +-+-+ +-+-+ +-+-+ (___)
| LB1 | | A | | B | | C | | D | (___)
+-----+ +---+ +---+ +---+ +---+ (___)
apache 4 cheap web servers
mod_ssl
haproxy
Config on haproxy (LB1) :
-------------------------
listen 127.0.0.1:8000
mode http
balance roundrobin
cookie SERVERID insert indirect nocache
option httpchk HEAD /index.html HTTP/1.0
server webA 192.168.1.11:80 cookie A check
server webB 192.168.1.12:80 cookie B check
server webC 192.168.1.13:80 cookie C check
server webD 192.168.1.14:80 cookie D check
Description :
-------------
- apache on LB1 will receive clients requests on port 443
- it forwards it to haproxy bound to 127.0.0.1:8000
- if a request does not contain a cookie, it will be forwarded to a valid
server
- in return, a cookie "SERVERID" will be inserted in the response holding the
server name (eg: "A"), and a "Cache-control: private" header will be added
so that the apache does not cache any page containing such cookie.
- when the client comes again with the cookie "SERVERID=A", LB1 will know that
it must be forwarded to server A. The cookie will be removed so that the
server does not see it.
- if server "webA" dies, the requests will be sent to another valid server
and a cookie will be reassigned.
Notes :
-------
- if the cookie works in "prefix" mode, there is no need to add the "nocache"
option because it is an application cookie which will be modified, and the
application flags will be preserved.
- if apache 1.3 is used as a front-end before haproxy, it always disables
HTTP keep-alive on the back-end, so there is no need for the "httpclose"
option on haproxy.
- configure apache to set the X-Forwarded-For header itself, and do not do
it on haproxy if you need the application to know about the client's IP.
Flows :
-------
(apache) (haproxy) (server A)
>-- GET /URI1 HTTP/1.0 ------------> |
( no cookie, haproxy forwards in load-balancing mode. )
| >-- GET /URI1 HTTP/1.0 ---------->
| <-- HTTP/1.0 200 OK -------------<
( the proxy now adds the server cookie in return )
<-- HTTP/1.0 200 OK ---------------< |
Set-Cookie: SERVERID=A |
Cache-Control: private |
>-- GET /URI2 HTTP/1.0 ------------> |
Cookie: SERVERID=A |
( the proxy sees the cookie. it forwards to server A and deletes it )
| >-- GET /URI2 HTTP/1.0 ---------->
| <-- HTTP/1.0 200 OK -------------<
( the proxy does not add the cookie in return because the client knows it )
<-- HTTP/1.0 200 OK ---------------< |
>-- GET /URI3 HTTP/1.0 ------------> |
Cookie: SERVERID=A |
( ... )
========================================
3.1. Alternate solution using Stunnel
========================================
When only SSL is required and cache is not needed, stunnel is a cheaper
solution than Apache+mod_ssl. By default, stunnel does not process HTTP and
does not add any X-Forwarded-For header, but there is a patch on the official
haproxy site to provide this feature to recent stunnel versions.
This time, stunnel will only process HTTPS and not HTTP. This means that
haproxy will get all HTTP traffic, so haproxy will have to add the
X-Forwarded-For header for HTTP traffic, but not for HTTPS traffic since
stunnel will already have done it. We will use the "except" keyword to tell
haproxy that connections from local host already have a valid header.
192.168.1.1 192.168.1.11-192.168.1.14 192.168.1.2
-------+-----------+-----+-----+-----+--------+----
| | | | | _|_db
+--+--+ +-+-+ +-+-+ +-+-+ +-+-+ (___)
| LB1 | | A | | B | | C | | D | (___)
+-----+ +---+ +---+ +---+ +---+ (___)
stunnel 4 cheap web servers
haproxy
Config on stunnel (LB1) :
-------------------------
cert=/etc/stunnel/stunnel.pem
setuid=stunnel
setgid=proxy
socket=l:TCP_NODELAY=1
socket=r:TCP_NODELAY=1
[https]
accept=192.168.1.1:443
connect=192.168.1.1:80
xforwardedfor=yes
Config on haproxy (LB1) :
-------------------------
listen 192.168.1.1:80
mode http
balance roundrobin
option forwardfor except 192.168.1.1
cookie SERVERID insert indirect nocache
option httpchk HEAD /index.html HTTP/1.0
server webA 192.168.1.11:80 cookie A check
server webB 192.168.1.12:80 cookie B check
server webC 192.168.1.13:80 cookie C check
server webD 192.168.1.14:80 cookie D check
Description :
-------------
- stunnel on LB1 will receive clients requests on port 443
- it forwards them to haproxy bound to port 80
- haproxy will receive HTTP client requests on port 80 and decrypted SSL
requests from Stunnel on the same port.
- stunnel will add the X-Forwarded-For header
- haproxy will add the X-Forwarded-For header for everyone except the local
address (stunnel).
========================================