#
# https://www.haproxy.com/blog/haproxy-on-docker-swarm-load-balancing-and-dns-service-discovery/
#
global
    log          fd@2 local2
    chroot       /var/lib/haproxy
    pidfile      /var/run/haproxy.pid
    maxconn      {{ haproxy_maxconns }}
    user         haproxy
    group        haproxy
    stats socket /var/lib/haproxy/stats expose-fd listeners
    master-worker
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private
    # https://ssl-config.mozilla.org/#server=haproxy&version=2.2&config=intermediate&openssl=1.1.1d&guideline=5.6   
    tune.ssl.default-dh-param 2048
    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

resolvers docker
    nameserver dns1 127.0.0.11:53
    resolve_retries 3
    timeout resolve 1s
    timeout retry   1s
    hold other      10s
    hold refused    10s
    hold nx         10s
    hold timeout    10s
    hold valid      10s
    hold obsolete   10s

defaults
    timeout connect 10s
    timeout client 30s
    timeout server 30s
    log global
    monitor-uri /_haproxy_health_check
    timeout http-keep-alive {{ haproxy_global_keepalive_timeout }}
    timeout connect {{ haproxy_connect_timeout }}
    timeout client  {{ haproxy_client_timeout }}
    timeout server  {{ haproxy_server_timeout }}
    timeout check  {{ haproxy_check_timeout }}
    timeout http-request 10s  # slowloris protection
    default-server inter 3s fall 2 rise 2 slowstart 60s

# Needed to preserve the stick tables
peers mypeers
    peer local_haproxy 127.0.0.1:1024

listen stats
    bind *:{{ haproxy_admin_port }} ssl crt {{ haproxy_cert_dir }} alpn h2,http/1.1
    mode http
    option httplog
    stats enable
    stats uri     /
    stats realm   HAProxy\ Statistics
    stats auth    admin:{{ haproxy_admin_pwd }}
    stats refresh 15s
    stats show-legends
    stats show-node

listen local_stats
    bind 127.0.0.1:8881
    mode http
    option httplog
    stats enable
    stats uri     /
    stats realm   HAProxy\ Statistics

frontend http
    bind *:{{ https_port }} ssl crt {{ haproxy_cert_dir }} alpn h2,http/1.1  {% if docker_swarm_haproxy_installation_type == 'global' %}accept-proxy{% endif %}
    bind *:{{ haproxy_default_port }} {% if docker_swarm_haproxy_installation_type == 'global' %}accept-proxy{% endif %}
    mode http
    option httplog
    option http-keep-alive
    http-request add-header X-Forwarded-Proto https
    # HSTS (63072000 seconds)
    http-response set-header Strict-Transport-Security max-age=63072000
{% if docker_swarm_cluster_portainer_install %}
    acl portainer_srv hdr_dom(host) -i {{ docker_swarm_portainer_hostname }}
    use_backend portainer_bck if portainer_srv
{% endif %}
{% if docker_swarm_expose_api_via_haproxy %}
    acl swarm_api hdr_dom(host) -i {{ docker_swarm_expose_api_hostname }}
    acl swarm_api_allowed_nets src {% for net in docker_swarm_api_networks_acl %} {{ net }}{% endfor %}

    http-request deny if swarm_api !swarm_api_allowed_nets
    use_backend swarm_api_bck if swarm_api
{% endif %}
{% for srv in docker_swarm_haproxy_additional_services %}
    acl {{ srv.acl_name }} {{ srv.acl_rule }}
{% if srv.allowed_networks is defined %}
    acl {{ srv.acl_name }}_nets src {% for net in srv.allowed_networks %} {{ net }}{% endfor %}

    http-request deny if {{ srv.acl_name }} !{{ srv.acl_name }}_nets
{% endif %}
    use_backend {{ srv.acl_name }}_bck if {{ srv.acl_name }}
{% endfor %}
    redirect scheme https code 301 if !{ ssl_fc }

#
# Backends
#

{% if docker_swarm_expose_api_via_haproxy %}
# swarm API
backend swarm_api_bck
    mode http
    option httplog
    balance roundrobin
    server swarm 127.0.0.1:{{ docker_api_port }}
{% endif %}

{% if docker_swarm_cluster_portainer_install %}
backend portainer_bck
    mode http
    option httplog
    option httpchk
    http-check send meth HEAD uri / ver HTTP/1.1 hdr Host localhost
    http-check expect rstatus (2|3)[0-9][0-9]
    balance roundrobin
    server-template portainer- 1 portainer:{{ docker_swarm_portainer_http_port }} check resolvers docker init-addr libc,none
{% endif %}

{% for srv in docker_swarm_haproxy_additional_services %}
backend {{ srv.acl_name }}_bck
    mode http
    option httplog
    option httpchk
    http-check send meth HEAD uri / ver HTTP/1.1 hdr Host localhost
    http-check expect rstatus (2|3)[0-9][0-9]
    balance roundrobin
    server-template {{ srv.service_name }}- {{ srv.service_replica_num }} {{ srv.service_name }}:{{ srv.service_port }} check resolvers docker init-addr libc,none
{% endfor %}