Hardened SSL Ciphers Using AWS ELB and HAProxy

I stumbled across this post on /r/netsec and saw this user's comment, looking for a way to deploy a hardened SSL cipher suite with ELB.

Having just finished setting this up for Mattress, I figured I would share our implementation. As a warning, we are doing SSL termination on our frontend instances, so if you require SSL termination to happen at the ELB, you are still out of luck.

Our basic setup looks like:

 -----      ---------      ------------
| ELB | -> | HAProxy | -> | Web Server |
 -----      ---------      ------------

The elastic load balancer proxies requests to our frontend servers, which run HAProxy for SSL termination and Unicorn. By enabling the PROXY protocol role on the ELB, the frontend servers also have access to the real remote client ip.

Enabling PROXY Protocol Support on the ELB

  • Setup a tcp listener on port 443 on your ELB (the instance port can be whatever you want)
  • Make sure you have the elb-tools installed [a simple brew install elb-tools works]

Run the following to create the policy:

elb-create-lb-policy LOADBALANCER_NAME --policy-name EnableProxyProtocol --policy-type ProxyProtocolPolicyType  --attribute "name=ProxyProtocol, value=true"

Then enable the policy for the specific instance port:

elb-set-lb-policies-for-backend-server LOADBALANCER_NAME --instance-port INSTANCE_PORT --policy-names EnableProxyProtocol

Configure HAProxy for Hardened SSL Termination

In the relevant frontend portion of your HAProxy config, modify the following to your liking.

# Bind on port 443. NOTE: accept-proxy is required here
# since the ELB is using the PROXY protocol. Also, the
# ciphers came from:
# http://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
bind 0.0.0.0:443 accept-proxy ssl crt /etc/cert.pem ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!ADH:!AECDH:!MD5:!DSS

# Add X-Forwarded-Proto header
reqadd X-Forwarded-Proto:\ https

# This option will set X-Forwarded-For header to the ip
# passed along from the ELB
option forwardfor

That's all there is to it. If you have any questions, I'm @JudStephenson on Twitter.