Many modern architectures require SSL termination at the load balancer while maintaining encrypted connections to backend servers. Here's how to configure HAProxy for this dual-SSL scenario while handling advanced routing requirements.
global
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
tune.ssl.default-dh-param 2048
defaults
mode http
timeout connect 10s
timeout client 30s
timeout server 30s
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https
# Cookie-based stickiness
stick-table type string len 32 size 30k expire 30m
stick store-request req.cook(JSESSIONID)
stick match resp.cook(JSESSIONID)
# URL rewriting example
reqrep ^([^\ ]*)\ /oldpath/(.*) \1\ /newpath/\2
# SSL termination with re-encryption
use_backend secure_servers if { hdr(host) -i app.example.com }
backend secure_servers
server s1 192.168.1.10:443 ssl ca-file /etc/haproxy/certs/backend-ca.pem verify required
server s2 192.168.1.11:443 ssl ca-file /etc/haproxy/certs/backend-ca.pem verify required
http-request set-header X-Forwarded-For %[src]
The configuration achieves several critical objectives:
- Dual SSL Handling: Terminates client SSL at HAProxy then re-encrypts using
ssl
parameter in backend server definitions - Header Preservation:
X-Forwarded-For
maintains original client IP through the encrypted backend hop - Session Stickiness: Cookie-based persistence using
stick-table
tracks sessions via JSESSIONID
For backend verification, you'll need:
openssl s_client -connect backend:443 -showcerts < /dev/null | \
awk '/BEGIN CERT/,/END CERT/ { print $0 }' > backend-ca.pem
When debugging SSL handshakes:
echo "quit" | openssl s_client -connect backend:443 -status -debug -msg -state
Check HAProxy logs for SSL errors with tail -f /var/log/haproxy.log | grep ssl
SSL re-encryption adds CPU overhead. Consider:
- Using EC certificates for faster handshakes
- Implementing SSL session reuse with
ssl-default-server-options no-sslv3 no-tls-tickets
- Monitoring SSL handshake rates with
echo "show info" | socat /var/run/haproxy.sock -
Many modern architectures require SSL termination at the load balancer while maintaining backend security. HAProxy provides powerful tools for this scenario, but the configuration requires careful attention to detail.
To implement this properly, we need to handle:
- Frontend SSL termination
- HTTP header manipulation
- URL rewriting
- Backend SSL re-encryption
- Session stickiness
Here's a working configuration that addresses all requirements:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
tune.ssl.default-dh-param 2048
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend https-in
bind *:443 ssl crt /etc/ssl/certs/frontend.pem
mode http
option forwardfor
# Extract real client IP
http-request set-header X-Forwarded-For %[src]
# URL rewriting example
acl is_legacy path_beg /oldpath
http-request redirect code 301 location /newpath%[path] if is_legacy
# Cookie-based session stickiness
cookie SERVERID insert indirect nocache secure
use_backend %[req.cook(SERVERID)]
backend secure_servers
mode http
balance roundrobin
cookie SERVERID prefix
server s1 192.168.1.10:443 ssl verify none check cookie s1
server s2 192.168.1.11:443 ssl verify none check cookie s2
# Re-encrypt traffic to backend
option ssl-hello-chk
http-request set-header X-Forwarded-Proto https if { ssl_fc }
SSL Termination and Re-encryption: The frontend handles SSL termination while the backend servers are configured with 'ssl' parameter for re-encryption.
Header Manipulation: We use http-request set-header
directives to properly set X-Forwarded-For and X-Forwarded-Proto headers.
Session Stickiness: The cookie-based persistence ensures clients stick to the same backend server.
For more complex URL manipulation:
# Rewrite /api/v1/ to /v2/ while preserving query strings
acl is_v1 path_beg /api/v1/
http-request set-path /v2/%[path,regsub(^/api/v1/,/)] if is_v1
http-request replace-header Host (.*) v2.example.com if is_v1
When implementing SSL re-encryption:
- Enable SSL session caching in global section:
ssl-default-server-cache 200000
- Consider using TLS tickets for reduced handshake overhead
- Monitor CPU usage as SSL operations are computationally intensive
If you encounter issues:
- Check logs with
tail -f /var/log/haproxy.log
- Verify SSL certificates with
openssl s_client -connect backend:443
- Test header forwarding with
curl -v -H "Host: example.com" https://haproxy