# Inspired by https://gist.github.com/haproxytechblog/dc5c3b5e2801d36b79e00f07b2309c14 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 node my-hostname server-state-file /var/lib/haproxy/server_state.txt maxconn 60000 # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # generated 2021-05-03, Mozilla Guideline v5.6, HAProxy 2.2, OpenSSL 1.1.1d, intermediate configuration # https://ssl-config.mozilla.org/#server=haproxy&version=2.2&config=intermediate&openssl=1.1.1d&guideline=5.6 ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/ssl/dhparam-haproxy ssl-dh-param-file /etc/ssl/dhparam-haproxy defaults log global mode http option httplog option dontlognull option log-separate-errors option httpclose option abortonclose option http-server-close option redispatch timeout connect 5s timeout client 300s timeout server 300s timeout queue 60s timeout http-request 15s load-server-state-from-file global maxconn 50000 # Default options for "server" directive in backends default-server maxconn 2000 on-error fail-check slowstart 60s inter 5s fastinter 1s downinter 1s weight 100 fall 3 rise 2 http-errors boost-default-errors errorfile 400 /etc/haproxy/errors/400.http errorfile 403 /etc/haproxy/errors/403.http errorfile 408 /etc/haproxy/errors/408.http errorfile 500 /etc/haproxy/errors/500.http errorfile 502 /etc/haproxy/errors/502.http errorfile 503 /etc/haproxy/errors/503.http errorfile 504 /etc/haproxy/errors/504.http resolvers local_dns parse-resolv-conf resolve_retries 3 timeout resolve 1s timeout retry 1s hold other 30s hold refused 30s hold nx 30s hold timeout 30s hold valid 10s hold obsolete 30s userlist example-dev_team user dev1 insecure-password ItyecpotHeHagAcukfoGrynPeawdEyb2 user dev2 insecure-password 8owEkecharhiedyarOwcebcheimeObBa listen stats mode http bind *:8080 ssl crt /etc/haproxy/ssl/ bind 127.0.0.1:8081 acl stats_access_ips src -f /etc/haproxy/stats_access_ips acl stats_admin_ips src -f /etc/haproxy/stats_admin_ips stats enable stats refresh 10s stats uri / stats show-legends stats show-node stats admin if stats_admin_ips stats http-request deny if !stats_access_ips http-request set-log-level silent frontend external bind 0.0.0.0:80,:::80 bind 0.0.0.0:443,:::443 ssl strict-sni alpn h2,http/1.1 crt /etc/haproxy/ssl capture request header Host len 32 option forwardfor errorfiles boost-default-errors # Is the request coming for the server itself (stats…) acl server_hostname hdr(host) -i my-hostname acl munin hdr(host) -i munin # List of IP that will not go the maintenance backend acl maintenance_ips src -f /etc/haproxy/maintenance_ips # Detect Let's Encrypt challenge requests acl letsencrypt path_dir -i /.well-known/acme-challenge # Determine if the request is routable to Varnish acl varnish_available nbsrv(varnish) gt 0 acl varnish_detected res.hdr(X-Varnish) -m found acl varnish_http_verb method GET HEAD PURGE # Reject the request at the TCP level if source is in the denylist tcp-request connection reject if { src -f /etc/haproxy/deny_ips } http-request set-header X-Forwarded-Port %[dst_port] # Set header if not coming from Varnish http-request set-header X-Forwarded-Proto http if !{ ssl_fc } http-request set-header X-Forwarded-Proto https if { ssl_fc } # BEGIN HTTP tagging http-request set-header X-Unique-ID %[uuid()] unless { hdr(X-Unique-ID) -m found } http-request add-header X-Boost-Step1 haproxy-external http-response add-header X-Boost-Step1 "haproxy-external; client-https" if { ssl_fc } http-response add-header X-Boost-Step1 "haproxy-external; client-http" if !{ ssl_fc } http-response set-header X-Boost-Server my-hostname # Debug: Enable this to add a full log line in the response ### http-response add-header X-Haproxy-Log-external "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r" # END HTTP tagging # Global maintenance mode (### -> uncomment) ### use_backend maintenance unless maintenance_ips use_backend local if server_hostname use_backend local if munin # "letsencrypt" must stay after "local" use_backend letsencrypt if letsencrypt # BEGIN frontend_external section for site 'example' acl example_com_domains hdr(host) -i example.com acl example_com_domains2 hdr(host) -i example.org www.example.org ### acl example_maintenance_ips src -f /etc/haproxy/example/maintenance_ips # Redirect to HTTPS without Let's Encrypt certificate ### redirect scheme https code 301 if example_com_domains !{ ssl_fc } # Redirect to HTTPS with Let's Encrypt certificate (exclude LE challenge from redirection) ### redirect scheme https code 301 if example_com_domains !{ ssl_fc } !letsencrypt # Redirect a domain to another without Let's encrypt certificate ### redirect prefix https://example-to.org code 301 if { hdr(host) -i example-from.org } # Redirect a domain to another with a Let's encrypt certificate (exclude Let's Enrypt challenge from redirection) ### redirect prefix https://example-to.org code 301 if { hdr(host) -i example-from.org } !letsencrypt # HSTS (31536000 seconds = 1 year) ### http-response set-header Strict-Transport-Security max-age=31536000 if example_com_domains # custom directives, all conditionned by at least the "example_com_domains" ACL ### http-request deny if example_com_domains { src 1.2.3.4 } # routing directives, all conditionned by (at least) the "example_com_domains" ACL # Maintenance mode (### -> uncomment BUT define example_maintenance_ips acl before) ### use_backend example_maintenance if example_com_domains !example_maintenance_ips !maintenance_ips # Use Varnish if available use_backend varnish if example_com_domains varnish_available varnish_http_verb # … or use normal backend use_backend example_com if example_com_domains # END frontend_external section for site 'example' default_backend goto_internal # This is used as a fallback to go to the internal frontend # This is not supposed to happen backend goto_internal server haproxy-internal /run/haproxy-frontend-default.sock send-proxy-v2 # This frontend is only used when returning from varnish frontend internal bind /run/haproxy-frontend-default.sock user root mode 666 accept-proxy capture request header Host len 32 option forwardfor # Check URL (used by Varnish) monitor-uri /haproxycheck acl varnish_from hdr(X-Varnish) -m found acl forwarded_proto hdr(x-forwarded-proto) -m found # Keep header if present and coming from Varnish http-request set-header X-Forwarded-Proto %[hdr(x-forwarded-proto)] if forwarded_proto varnish_from # BEGIN HTTP tagging http-request add-header X-Boost-Step3 haproxy-internal http-response add-header X-Boost-Step3 "haproxy-internal; SSL to backend" if { ssl_bc } http-response add-header X-Boost-Step3 "haproxy-internal; no SSL to backend" if !{ ssl_bc } # Debug: Enable this to add a full log line in the response ### http-response add-header X-Haproxy-Log-Internal "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r" # END HTTP tagging # BEGIN frontend_internal section for site 'example' acl example_com_domains hdr(host) -i example.com # routing directives, all conditionned by (at least) the "example_com_domains" ACL use_backend example_com if example_com_domains # END frontend_internal section for site 'example' backend varnish option httpchk HEAD /varnishcheck server varnish_sock /run/varnish.sock check observe layer7 maxconn 3000 inter 1s send-proxy-v2 # BEGIN backend section for site 'example' backend example_com errorfile 503 /etc/haproxy/sites/example/maintenance.http http-response set-header X-Boost-Proto https if { ssl_bc } http-response set-header X-Boost-Proto http if !{ ssl_bc } server example-hostname 1.2.3.4:443 check observe layer4 ssl verify none backend example_maintenance http-request set-log-level silent errorfile 503 /etc/haproxy/sites/example/maintenance.http # END backend section for site 'example' backend letsencrypt # Use this if the challenge is managed locally server localhost 127.0.0.1:81 send-proxy-v2 maxconn 10 # Use this if the challenge is managed remotely ### server my-certbot-challenge-manager 192.168.2.1:80 maxconn 10 backend local option httpchk HEAD /haproxy-check server localhost 127.0.0.1:81 send-proxy-v2 maxconn 10 backend maintenance http-request set-log-level silent errorfile 503 /etc/haproxy/errors/maintenance.http