veza/ansible/roles/haproxy/templates/haproxy.cfg
2025-12-03 22:56:50 +01:00

222 lines
8.7 KiB
INI

# {{ 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