# 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 with SSL externaly
    bind *:8080 ssl crt /etc/haproxy/ssl/
    # Bind without SSL locally
    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 self     hdr(host) -i my-hostname my-hostname.domain.tld
    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; ssl-frontend"    if  { ssl_fc }
    http-response add-header X-Boost-Step1 "haproxy-external; no-ssl-frontend" 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 self
    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

    # 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_com_maintenance_ips acl before)
    ### use_backend example_com_maintenance if example_com_domains !example_com_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-backend"    if  { ssl_bc }
    http-response add-header X-Boost-Step3 "haproxy-internal; no-ssl-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_com_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
