# {{ ansible_managed }} {% if haproxy_userlist is defined %} {% for userlist, users in haproxy_userlist.items() %} userlist {{ userlist }} {% for user in users %} user {{ user }} insecure-password {{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['haproxy_basicauth_' + user + '_password'] | mandatory }} {% endfor %} {% endfor %} {% endif %} # BEGIN GLOBAL AND DEFAULTS global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners stats socket /run/haproxy/monitoring.sock mode 666 level user stats timeout 30s user haproxy group haproxy daemon maxconn {{ haproxy_maxconn }} ca-base /etc/ssl/certs crt-base /etc/ssl/private # Current TLS profile: {{ haproxy_tls_profile }} ssl-default-bind-curves X25519:prime256v1:secp384r1 {% if tls_ciphers is defined %} ssl-default-bind-ciphers {{ tls_ciphers|join(':') }} ssl-default-server-ciphers {{ tls_ciphers|join(':') }} {% endif %} ssl-default-bind-ciphersuites {{ tls_ciphersuites|join(':') }} ssl-default-server-ciphersuites {{ tls_ciphersuites|join(':') }} ssl-default-bind-options {{ tls_options|join(' ') }} ssl-default-server-options {{ tls_options|join(' ') }} {% if haproxy_tls_profile != "modern" %} ssl-dh-param-file /usr/local/etc/tls/dh2048.pem {% endif %} defaults log global mode http option httplog option dontlognull timeout connect {{ haproxy_timeout_connect | default('5s') }} timeout client {{ haproxy_timeout_client | default('50s') }} timeout server {{ haproxy_timeout_server | default('50s') }} timeout http-request {{ haproxy_timeout_http_request | default('5s') }} timeout client-fin {{ haproxy_timeout_client_fin | default('30s') }} timeout tunnel {{ haproxy_timeout_tunnel | default('1h') }} 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 # END GLOBAL AND DEFAULTS => BEGIN FRONTENDS {% if haproxy_default_frontend %} frontend https filter compression compression algo gzip compression type {{ haproxy_compression_type|join(' ') }} mode http {% if haproxy_frontend['bind_list'] is defined %} {% for bind in haproxy_frontend['bind_list'] %} bind {{ bind }} {% endfor %} {% else %} bind :443,:::443 v6only ssl crt /usr/local/etc/tls/haproxy alpn h2,http/1.1 bind :80,:::80 v6only {% endif %} http-request set-header X-Forwarded-Proto https if { ssl_fc } {% if haproxy_iis %} http-request set-header Front-End-Https On if { ssl_fc } {% endif %} redirect scheme https code 301 if !{ ssl_fc } option forwardfor default_backend {{ haproxy_frontend['default_backend'] | default('error404') }} # add an header to know on which haproxy we are http-response set-header x-proxy-id {{ ansible_hostname }} # HSTS for 1 year http-response set-header Strict-Transport-Security "max-age=31536000; preload" # block access to any git paths acl git path,url_dec -m sub /.git use_backend error404 if git # block access to path begining by "/manager" except from 10.0.0.0/8 acl internal_network src 10.0.0.0/8 acl manager path,url_dec -m beg /manager use_backend error404 if manager !internal_network # redirect multiple traling slash to one slash acl has_multiple_slash path_reg /{2,} http-request set-path %[path,regsub(/+,/,g)] if has_multiple_slash acl is_robots_txt path /robots.txt {% if haproxy_robotstxt %} use_backend robotstxt if is_robots_txt {% endif %} {% if haproxy_letsencrypt %} acl acme-challenge path_beg -i /.well-known/acme-challenge use_backend letsencrypt if acme-challenge {% endif %} {% if haproxy_coraza is defined and haproxy_coraza and haproxy_waf_sample_percent is defined%} acl openvas src 185.14.128.171 2a03:a240:0:1dea::a2 {% if haproxy_waf_sample_percent | int < 100 %} acl waf_trigger rand(100) lt {{ haproxy_waf_sample_percent }} {% endif %} http-request set-var(txn.coraza.app) str(haproxy_waf) filter spoe engine coraza config /etc/haproxy/coraza.cfg {% if haproxy_waf_sample_percent | int < 100 %} http-request send-spoe-group coraza coraza-req if waf_trigger !openvas {% else %} http-request send-spoe-group coraza coraza-req !openvas {% endif %} http-request redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } http-response redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } http-request deny deny_status 403 hdr waf-block "request" if { var(txn.coraza.action) -m str deny } http-response deny deny_status 403 hdr waf-block "response" if { var(txn.coraza.action) -m str deny } http-request silent-drop if { var(txn.coraza.action) -m str drop } http-response silent-drop if { var(txn.coraza.action) -m str drop } http-request deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } http-response deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } {% endif %} {% if haproxy_frontend_raw_config is defined %} {{ haproxy_frontend_raw_config|indent(8, True) }} {% endif %} {% endif %} {% if haproxy_frontend_list is defined %} {% for frontend in haproxy_frontend_list %} frontend {{ frontend['name'] }} mode {{ frontend['mode'] | default('http') }} {% for bind in frontend['bind_list'] %} bind {{ bind }} {% endfor %} {% if frontend['mode'] is defined and frontend['mode'] == 'tcp' %} option tcplog {% endif %} {% if frontend['use_backend'] is defined %} use_backend {{ frontend['use_backend'] }} {% endif %} {% if haproxy_coraza is defined and haproxy_coraza and frontend['waf_sample_percent'] %} acl openvas src 185.14.128.171 2a03:a240:0:1dea::a2 {% if frontend['waf_sample_percent'] | int < 100 %} acl waf_trigger rand(100) lt {{ frontend['waf_sample_percent'] }} {% endif %} http-request set-var(txn.coraza.app) str(haproxy_waf) filter spoe engine coraza config /etc/haproxy/coraza.cfg {% if frontend['waf_sample_percent'] | int < 100 %} http-request send-spoe-group coraza coraza-req if waf_trigger !openvas {% else %} http-request send-spoe-group coraza coraza-req !openvas {% endif %} http-request redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } http-response redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } http-request deny deny_status 403 hdr waf-block "request" if { var(txn.coraza.action) -m str deny } http-response deny deny_status 403 hdr waf-block "response" if { var(txn.coraza.action) -m str deny } http-request silent-drop if { var(txn.coraza.action) -m str drop } http-response silent-drop if { var(txn.coraza.action) -m str drop } http-request deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } http-response deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } {% endif %} {% if frontend['config'] is defined %} {{ frontend['config']|indent(8, True) }} {% endif %} {% endfor %} {% endif %} # END FRONTENDS => BEGIN BACKENDS backend error404 mode http errorfile 503 /etc/haproxy/errors/404.http {% if haproxy_letsencrypt %} backend letsencrypt http-request set-path %[path,regsub(/.well-known/acme-challenge/,/)] server localhost 127.0.0.1:8888 {% endif %} {% if haproxy_coraza is defined and haproxy_coraza %} backend coraza-spoa mode tcp server coraza_spoa 127.0.0.1:9000 {% endif %} backend robotstxt mode http http-request return status 200 content-type "text/plain" file "/etc/haproxy/static/robots.txt" hdr "cache-control" "no-cache" {% if haproxy_backend is defined %} {% for backend in haproxy_backend %} backend {{ backend['name'] }} {% if backend['mode'] is defined %} mode {{ backend['mode'] }} {% endif %} {% if backend['raw_config'] is defined %} {{ backend['raw_config']|indent(8, True) }}{% endif %} {% if backend['balance'] is defined %} balance {{ backend['balance'] }} {% endif %} {% if backend['source'] is defined %} source {{ backend['source'] }} {% endif %} {% if backend['server'] is defined %} {% for server in backend['server'] %} server {{ server['name'] | default(server['fqdn']) }} {{ server['fqdn'] | default(server['name']) }}:{{ server['port'] | default('80') }}{% if server['proto'] is defined %} proto {{ server['proto'] }}{% endif %} {{ server['check'] | default('check') }}{% if server['options'] is defined %} {{ server['options'] }}{% endif %} {% endfor %} {% endif %} {% if backend['server'] is defined and (backend['mode'] is undefined or backend['mode'] == 'http') %} filter compression compression algo gzip compression type {{ haproxy_compression_type|join(' ') }} {% endif %} {% endfor %} {% endif %} # END BACKENDS