HAProxy SSL Offloading with Self-Signed Backend Certificates: Secure Configuration Guide


4 views

When implementing HAProxy as an SSL terminator, many administrators face the dilemma of maintaining security between HAProxy and backend servers while avoiding the overhead of managed certificates. The optimal solution balances encryption strength with operational simplicity.

Self-signed certificates offer these advantages for backend communication:

  • No certificate authority costs
  • Long expiration periods (10+ years)
  • Simple regeneration process
  • No DNS validation requirements

The key to security lies in properly configuring certificate verification. Instead of using verify none, we should create a CA bundle:

# Generate CA certificate for self-signed certs
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt

# On each backend server:
openssl genrsa -out backend.key 2048
openssl req -new -key backend.key -out backend.csr
openssl x509 -req -days 3650 -in backend.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out backend.crt

Here's the proper backend configuration with certificate pinning:

backend secure-backend
    balance leastconn
    server web1 1.1.1.1:443 check ssl ca-file /etc/ssl/ca.crt verify required
    server web2 2.2.2.2:443 check ssl ca-file /etc/ssl/ca.crt verify required
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }

This approach provides equivalent security to commercial certificates for backend communication because:

  • The private CA key never leaves your infrastructure
  • Only servers with certificates signed by your CA are trusted
  • Certificate verification prevents MITM attacks
  • You maintain control over certificate lifetimes

For additional security, implement mutual TLS where both sides authenticate:

backend mTLS-backend
    server web1 1.1.1.1:443 check ssl ca-file /etc/ssl/ca.crt verify required crt /etc/ssl/haproxy.pem

This requires each backend server to be configured with the HAProxy client certificate in its trust store.

When using SSL to backends, consider these optimizations:

backend optimized-backend
    server web1 1.1.1.1:443 check ssl ca-file /etc/ssl/ca.crt verify required alpn h2,http/1.1 no-tls-tickets

When implementing HAProxy as a reverse proxy, many administrators face the dilemma of securing backend connections without the overhead of managing multiple CA-signed certificates. The typical setup involves:

frontend public
    bind *:443 ssl crt /etc/ssl/cert.pem
    default_backend secured_servers

While using verify none with self-signed certs works, it's not ideal for security. Here's a better way to configure trusted self-signed certificates:

backend secured_servers
    balance roundrobin
    server web1 192.168.1.10:443 ssl ca-file /etc/ssl/backend-ca.pem verify required
    server web2 192.168.1.11:443 ssl ca-file /etc/ssl/backend-ca.pem verify required

1. Generate self-signed certificates for your backend servers:

openssl req -x509 -newkey rsa:4096 -sha256 -nodes \
    -keyout backend.key -out backend.crt -subj "/CN=backend.example.com" \
    -days 3650

2. Create a CA bundle containing all your backend certificates:

cat backend1.crt backend2.crt > /etc/ssl/backend-ca.pem

For additional security, consider these HAProxy settings:

backend secured_servers
    server web1 192.168.1.10:443 ssl \
        ca-file /etc/ssl/backend-ca.pem \
        crt /etc/ssl/backend.pem \
        verify required \
        ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384 \
        no-sslv3 no-tlsv10 no-tlsv11

Check your setup with OpenSSL:

openssl s_client -connect backend:443 -showcerts -CAfile /etc/ssl/backend-ca.pem

Look for the "Verify return code: 0 (ok)" message to confirm proper validation.

For larger deployments, consider setting up an internal CA:

# Create CA
openssl genrsa -aes256 -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt

# Sign backend certificates
openssl x509 -req -in backend.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out backend.crt -days 365 -sha256

Then configure HAProxy to trust your internal CA:

global
    ssl-server-verify none
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

backend secure_backend
    server node1 10.0.0.1:443 ssl ca-file internal-ca.crt verify required