HTTP/2 SSL Offloading with Haproxy and Nginx
HTTP/2 SSL Offloading with Haproxy and Nginx
After HTTP/2 becoming more an more prominent regarding SSL enforcement, i will show you in this post how to setup HTTP/2 SSL Offloading with Haproxy and Nginx in few easy steps.
Prerequisites:
- A working Haproxy 1.7.1.1+ Setup which Supports ALPN H2 and PROXY Protocol
- OpenSSL 1.0.2+ that supports ALPN
- Functional Nginx Setup behind Haproxy which supports H2 and PROXY Protocol
- Texteditor (vi, joe, nano) of your choice
1. First of all we start with the Haproxy Global SSL Ciphers and tunings
Open your haproxy.cfg and if not already done we first set secure SSL Ciphers and SSL Options to our Global block which will give you an Grade A rating at SSLLabs:
tune.ssl.default-dh-param 2048 ssl-default-bind-options no-sslv3 no-tls-tickets ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
The no-sslv3
Option prevents POODLE attacks while no-tls-tickets
enables Session Resumption (caching)
2. Now we add our new HTTP/2 frontend to our Haproxy config
frontend http2-ssl bind 10.1.1.1:443 ssl crt /etc/haproxy/certs/combined.yourcert.pem alpn h2,http/1.1 mode tcp log global option dontlog-normal maxconn 6000 timeout client 2m use_backend nginx-pool-http2 if { ssl_fc_alpn -i h2 } default_backend nginx-pool-http2-fallback
Notes: Change the Bind IP, Cert Location, Maxconn, Timeout e.g. that it matches your Setup.
3. Next one is to setup our Haproxy Backends
backend nginx-pool-http2 mode tcp balance roundrobin option tcpka timeout connect 2m timeout server 2m retries 3 server ngx-01 10.0.0.21:81 check send-proxy inter 2500 maxconn 2000 server ngx-02 10.0.0.22:81 check send-proxy inter 2500 maxconn 2000 server ngx-03 10.0.0.23:81 check send-proxy inter 2000 maxconn 2000 server ngx-04 10.0.0.24:81 check send-proxy inter 2000 maxconn 2000 server ngx-05 10.0.0.25:81 check send-proxy inter 1500 maxconn 2000 server ngx-06 10.0.0.26:81 check send-proxy inter 1500 maxconn 2000 backend nginx-pool-http2-fallback mode tcp balance roundrobin option tcpka timeout connect 2m timeout server 2m retries 3 server ngx-01 10.0.0.21:82 check send-proxy inter 2500 maxconn 2000 server ngx-02 10.0.0.22:82 check send-proxy inter 2500 maxconn 2000 server ngx-03 10.0.0.23:82 check send-proxy inter 2000 maxconn 2000 server ngx-04 10.0.0.24:82 check send-proxy inter 2000 maxconn 2000 server ngx-05 10.0.0.25:82 check send-proxy inter 1500 maxconn 2000 server ngx-06 10.0.0.26:82 check send-proxy inter 1500 maxconn 2000
Note: As you see, we set one Backend for HTTP/2 and another one for HTTP/1.1 and lower Protocol Versions as Fallback, which is important to support Clients which cannot speak HTTP/2.
The two Backends are very important and mandatory due to the fact that we are using TCP behind SSL Offloading, because Nginx cannot detect which HTTP Protocol (if its HTTP/2 or HTTP/1.0) the Client is initially using.
Change the backends server IPs that they will fit your loadbalancer setup.
4. In the final step we will prepare Nginx to speak the PROXY and HTTP/2 Protocol
In the Server Block of your Nginx sites-config create a new server block like shown bellow:
server { listen 81 http2 proxy_protocol; # haproxy SSL termination + HTTP/2 listen 82 proxy_protocol; # haproxy SSL termination for HTTP/1.1 and lower add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload'; # We want SSL A+ rating so we enable HSTS also. set_real_ip_from 10.1.0.0/24; # Set IP or subnet of your Loadbalancers here real_ip_recursive on; real_ip_header proxy_protocol; # This is mandatory to get Real IP from Proxy Protocol (other server block on port: 80 can continue use real_ip module with header: X-Forwarded-For)
.... and here comes your access_log, location settings e.g.
Note: Be careful with the “includeSubDomains” and “preload” response header. Set this only if you are hundered percent sure that you have SSL Certs for all your subdomains or you will get into trouble, because Browser which supports HSTS will force preload all your Domains and Subdomains by SSL if enabled and it is very hard to get removed from HSTS Preload List if activated once. If you want to get added/removed, you can visit the: HSTS Preload List Submission Page
Restart your Haproxy and Nginx and check your Logs for Errors.
Watch your Nginx Access Log, Connections with HTTP/2 should contain “HTTP/2.0” and Fallback Clients: HTTP/1.1 now.
If all looks fine enjoy your new HTTP/2 capability!
Is it possible to apply ACLs (e.g. url whitelisting) for HTTP2 to HTTP2 traffic?
No, since TCP mode is very straight forward Application Layer (layer 7) and doesn’t inspect the content of its packets.
The Transport Layer 4 (http mode) is required for ACLs, but since http mode doesn’t support HTTP/2 yet,
you cant apply routes like that.