2022-10-01 20:06:19 +02:00
## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }}
2023-03-10 00:02:52 +01:00
{%- from "haproxy/map.jinja" import haproxy,certs,fqdn with context %}
2022-10-01 20:06:19 +02:00
2023-01-16 12:00:54 +01:00
{%- set ns = namespace(default_backend='notdefined') %}
2023-01-04 23:32:56 +01:00
{%- for name, values in haproxy.config.vhosts.items() %}{% if values.default_backend|default(false) %}{% set ns.default_backend = name %}{% endif %}{% endfor %}
2022-10-23 16:37:53 +02:00
{%- macro internal() -%}
acl internal src -f {{ haproxy.config.dir }}/maps/access
2023-07-24 23:37:11 +02:00
http-response return status 403 content-type text/html string "<h1>403 forbidden</h1>" if !internal
2022-10-01 20:06:19 +02:00
{%- endmacro -%}
2022-10-23 16:37:53 +02:00
{%- macro head() -%}
2023-02-04 19:51:20 +01:00
http-request return status 200 if METH_HEAD
2022-10-01 20:06:19 +02:00
{%- endmacro -%}
2022-12-03 00:07:39 +01:00
{%- macro statusresponses() -%}
2023-07-24 23:37:11 +02:00
http-response return status 403 content-type text/html string "<h1>403 forbidden</h1>" if { status 403 }
http-response return status 404 content-type text/html string "<h1>404 not found</h1>" if { status 404 }
2022-12-03 00:07:39 +01:00
{%- endmacro -%}
2023-08-07 16:03:04 +02:00
{%- macro httpcheckrules(layer="layer7",inter="1s",fall=5,rise=5) -%}check observe {{ layer }} inter {{ inter }} fall {{ fall }} rise {{ rise }}{%- endmacro -%}
2022-12-09 23:47:23 +01:00
2023-08-07 16:03:04 +02:00
{%- macro httpsslrules(h2=False) -%}ssl verify none{{ " alpn h2,http/1.1" if h2 }}{%- endmacro -%}
2022-11-13 20:48:27 +01:00
2023-04-13 23:13:34 +02:00
{%- macro httpendpoints(servers=[], check=True, disabled=False) -%}
2023-10-10 09:07:44 +02:00
{%- if servers -%}
2022-11-13 20:48:27 +01:00
{%- for server in servers %}
2023-09-14 16:45:28 +02:00
{% if server.name in haproxy.servers.keys() -%}
{%- set addr = haproxy.servers[server.name][0] -%}
{%- else -%}
{%- set addr = server.name -%}
{%- endif -%}
server {{ server.name }} {{ addr }}:{{ server.port }}{{ " " + httpcheckrules(inter=server.inter|default("1s"), fall=server.fall|default(5), rise=server.rise|default(5)) if check }}{{ " " + httpsslrules(server.h2|default(False)) if server.ssl|default(False) }}{{ " disabled" if server.disabled|default(False) }}{{ " send-proxy" if server.proxy|default(False) }}{{ " on-marked-down shutdown-sessions on-marked-up shutdown-backup-sessions" if server.killsessions|default(False) }}
2022-11-13 20:48:27 +01:00
{%- endfor %}
2023-10-10 09:07:44 +02:00
{%- endif -%}
2022-11-13 20:48:27 +01:00
{%- endmacro -%}
{%- macro tcpendpoints(servers=[], check=True) -%}
2023-10-10 09:07:44 +02:00
{%- if servers -%}
2022-10-23 16:37:53 +02:00
{%- for server in servers %}
2023-09-14 16:45:28 +02:00
{% if server.name in haproxy.servers.keys() -%}
{%- set addr = haproxy.servers[server.name][0] -%}
{%- else -%}
{%- set addr = server.name -%}
{%- endif -%}
server {{ server.name }} {{ addr }}:{{ server.port }}{{ " check" if check }}{{ " backup" if server.backup|default(False) }} port {{ server.port }}{{ " on-marked-down shutdown-sessions on-marked-up shutdown-backup-sessions" if server.killsessions|default(False) }}
2022-10-01 20:06:19 +02:00
{%- endfor %}
2023-10-10 09:07:44 +02:00
{%- endif -%}
2022-10-01 20:06:19 +02:00
{%- endmacro -%}
2022-10-23 16:37:53 +02:00
{%- macro cache() -%}
2022-12-24 17:49:54 +01:00
http-request cache-use static if { path_end {{ haproxy.config.cache.file_types|join(" ") }} }
2022-12-11 16:34:27 +01:00
http-response cache-store static
2022-10-23 16:37:53 +02:00
{%- endmacro -%}
{%- macro compression() -%}
compression algo gzip
2023-05-17 13:20:14 +02:00
compression type {{ haproxy.config.compression_mime_types|join(' ') }}
2022-10-23 16:37:53 +02:00
{%- endmacro -%}
2022-10-01 20:06:19 +02:00
{%- macro admin() -%}
2022-12-09 23:47:23 +01:00
# Stats Backend
2022-12-26 18:13:39 +01:00
backend admin from {{ haproxy.config.namespace }}
2022-10-01 20:06:19 +02:00
mode http
stats enable
2022-12-03 00:07:39 +01:00
stats admin if TRUE
2023-06-06 09:23:53 +02:00
stats refresh 10s
2022-12-03 00:07:39 +01:00
stats show-modules
stats show-legends
2022-12-24 17:49:54 +01:00
stats uri /
2022-10-01 20:06:19 +02:00
{%- endmacro -%}
{%- macro api() -%}
2022-11-08 11:04:39 +01:00
# Runtime API
stats socket {{ haproxy.config.api.tcpsocket }} level admin
stats socket {{ haproxy.config.api.filesocket }} mode 666 level admin
2022-10-01 20:06:19 +02:00
{%- endmacro %}
2022-11-08 11:04:39 +01:00
# Global config
2022-10-01 20:06:19 +02:00
global
2023-06-06 09:23:53 +02:00
master-worker
server-state-file /var/run/haproxy.state
mworker-max-reloads 2
2023-04-13 23:13:34 +02:00
maxconn 1000
2023-01-04 23:32:56 +01:00
lua-prepend-path {{ haproxy.config.dir }}/mods/?.so cpath
2022-10-23 16:37:53 +02:00
lua-prepend-path {{ haproxy.config.dir }}/scripts/?.lua
2023-06-06 09:23:53 +02:00
{%- for script in haproxy.config.scripts %}
{%- if not script.lib and script.enabled|default(true) %}
lua-load {{ haproxy.config.dir }}/{{ script.name }} {% if "args" in script.keys() %}{{ script.args|join(" ") }}{% endif %}
2022-10-23 16:37:53 +02:00
{%- endif %}
2022-10-10 10:16:32 +02:00
{%- endfor %}
2022-10-23 16:37:53 +02:00
{%- if haproxy.config.api.enable %}
{{ api() }}
{%- endif %}
2023-04-13 23:13:34 +02:00
crt-base {{ haproxy.config.acme_fullchains_dir }}
ssl-dh-param-file {{ haproxy.config.acme_dh_dir }}/dh.pem
2022-10-01 20:06:19 +02:00
ssl-default-bind-ciphers {{ haproxy.config.ssl_ciphers|join(":") }}
ssl-default-bind-options {{ haproxy.config.ssl_options|join(" ") }}
ssl-default-server-ciphers {{ haproxy.config.ssl_ciphers|join(":") }}
ssl-default-server-options {{ haproxy.config.ssl_options|join(" ") }}
2023-04-13 23:13:34 +02:00
tune.lua.maxmem {{ haproxy.config.lua_max_mem }}
expose-experimental-directives
2023-12-01 16:45:53 +01:00
.if feature(QUIC)
limited-quic
.endif
2022-10-01 20:06:19 +02:00
2022-11-08 11:04:39 +01:00
# Defaults values
2022-12-26 18:13:39 +01:00
defaults {{ haproxy.config.namespace }}
2022-10-23 16:37:53 +02:00
{%- for key, value in haproxy.config.defaults.items() %}
{{ key }} {{ value }}
2022-10-01 20:06:19 +02:00
{%- endfor %}
2023-03-21 19:42:51 +01:00
{% if haproxy.config.peers.hosts -%}
2023-02-04 19:51:20 +01:00
peers paulbsd
2023-03-21 19:42:51 +01:00
bind *:{{ haproxy.config.peers.port }} ssl crt {{ haproxy.config.acme_fullchains_dir }}
2023-02-04 19:51:20 +01:00
default-server ssl verify none
server {{ salt['grains.get']('fqdn') }}
2023-03-21 19:42:51 +01:00
{%- for host in haproxy.config.peers.hosts %}
server {{ host[0] }} {{ host[1] }}:{{ haproxy.config.peers.port }}
2023-02-04 19:51:20 +01:00
{%- endfor %}
{%- endif %}
2022-11-08 11:04:39 +01:00
# Cache
2022-10-10 10:16:32 +02:00
cache static
2023-06-23 08:47:25 +02:00
total-max-size {{ haproxy.config.cache.total|default(64) }}
max-object-size {{ (haproxy.config.cache.size|default(8))*1024*1024 }}
max-age {{ haproxy.config.cache.age|default(3600) }}
2022-10-10 10:16:32 +02:00
2022-11-08 11:04:39 +01:00
# Per IP rates stick table
2022-12-26 18:13:39 +01:00
backend per_ip_rates from {{ haproxy.config.namespace }}
2023-03-21 19:42:51 +01:00
stick-table type string size {{ haproxy.config.ddos.size|default("1m") }} expire {{ haproxy.config.ddos.timeperiod|default("10s") }} store http_req_rate({{ haproxy.config.ddos.timeperiod|default("10s")}}) {{ "peers paulbsd" if haproxy.config.peers.hosts }}
2022-10-23 16:37:53 +02:00
2022-11-08 11:04:39 +01:00
# Default HTTP frontend
2023-06-23 08:47:25 +02:00
frontend fe_http from {{ haproxy.config.namespace }}
2023-01-16 12:00:54 +01:00
bind *:{{ haproxy.config.http_port }},:::{{ haproxy.config.http_port }} v4v6 name http
2022-10-01 20:06:19 +02:00
mode http
2023-02-04 19:51:20 +01:00
{% for name, service in haproxy.config.spoe.items() %}
filter spoe engine {{ name }} config {{ haproxy.config.dir }}/spoe.cfg
{%- endfor %}
2023-01-04 23:32:56 +01:00
## ACLs
2022-10-01 20:06:19 +02:00
acl http ssl_fc,not
2023-01-04 23:32:56 +01:00
acl self_host req.hdr(Host) {{ fqdn }}
2023-01-07 21:10:56 +01:00
acl path_root path /
acl path_host path_dir /host
acl path_date path_dir /date
2023-04-13 23:13:34 +02:00
acl path_srchash path_dir /srchash
2023-01-04 23:32:56 +01:00
## Basic rules
http-request set-var(txn.srchash) src,crc32,mod(100)
http-request set-var(txn.httpdate) date,http_date()
2023-04-13 23:13:34 +02:00
2023-01-04 23:32:56 +01:00
http-request return status 200 content-type text/html lf-string "%H\n" if self_host path_host
http-request return status 200 content-type text/html lf-string "%[var(txn.httpdate)]\n" if self_host path_date
http-request return status 200 content-type text/html lf-string "%[var(txn.srchash)]\n" if self_host path_srchash
2022-10-01 20:06:19 +02:00
http-request redirect scheme https if http
2022-11-08 11:04:39 +01:00
# Default HTTPS frontend
2023-06-23 08:47:25 +02:00
frontend fe_https from {{ haproxy.config.namespace }}
2023-02-19 12:21:59 +01:00
.if feature(QUIC)
bind quic4@*:{{ haproxy.config.https_port }},quic6@:::{{ haproxy.config.https_port }} v4v6 ssl crt {{ haproxy.config.acme_fullchains_dir }}{% if haproxy.config.http2 %} alpn h3,h2,http/1.1{% endif %} name https
.endif
2023-12-01 16:45:53 +01:00
bind *:{{ haproxy.config.https_port }},:::{{ haproxy.config.https_port }} v4v6 ssl crt {{ haproxy.config.acme_fullchains_dir }}{% if haproxy.config.http2 %} alpn h2,http/1.1{% endif %} name https
2022-10-23 16:37:53 +02:00
mode http
option httplog
2023-06-10 10:02:59 +02:00
option http-buffer-request
2023-02-04 19:51:20 +01:00
{% for name, service in haproxy.config.spoe.items() %}
filter spoe engine {{ name }} config {{ haproxy.config.dir }}/spoe.cfg
{%- endfor %}
2023-01-04 23:32:56 +01:00
## ACLs
2022-11-08 11:04:39 +01:00
acl internal src -f {{ haproxy.config.dir }}/maps/access
2023-04-13 23:13:34 +02:00
acl domains req.hdr(Host),map_dom({{ haproxy.config.dir }}/maps/domains) -m found
2023-07-15 19:54:00 +02:00
acl ua req.hdr(User-Agent),map_beg(/etc/haproxy/maps/ua) -m found
2023-03-21 19:42:51 +01:00
acl security_txt path /.well-known/security.txt
2022-12-03 00:07:39 +01:00
acl robots_txt path /robots.txt
2023-02-04 19:51:20 +01:00
acl max_req_rate sc_http_req_rate(0) gt {{ haproxy.config.ddos.maxrequests|default(200) }}
2023-01-04 23:32:56 +01:00
acl self_host req.hdr(Host) {{ fqdn }}
acl path_root path /
2023-01-07 21:10:56 +01:00
acl path_host path_dir /host
acl path_date path_dir /date
2023-01-04 23:32:56 +01:00
acl path_srchash path /srchash
2023-04-28 18:11:56 +02:00
acl path_location path /location
2023-01-04 23:32:56 +01:00
## Basic rules
http-request set-var(txn.random) rand,mul(5)
http-request set-var(txn.httpdate) date,http_date()
http-request set-var(txn.srchash) src,crc32,mod(100)
2022-11-08 11:04:39 +01:00
http-request set-var(req.src) src
2022-12-24 17:49:54 +01:00
http-request set-var(req.host) req.hdr(Host)
2022-11-08 11:04:39 +01:00
http-request set-var(req.accesshash) str(),concat(,req.src,),concat(-,req.host,)
2023-04-13 23:13:34 +02:00
2022-11-08 11:04:39 +01:00
http-request track-sc0 var(req.accesshash) table per_ip_rates
2022-12-09 23:47:23 +01:00
2023-12-01 16:45:53 +01:00
http-request capture req.hdr(User-Agent) len 200
2023-04-13 23:13:34 +02:00
http-request capture req.hdr(Content-Type) len 50
2022-10-23 16:37:53 +02:00
http-request capture sc_http_req_rate(0) len 4
2022-12-09 23:47:23 +01:00
2023-06-06 09:23:53 +02:00
## Silent drop all external requests with no host header
http-request silent-drop if !domains !internal
2022-12-09 23:47:23 +01:00
## DDoS
2023-02-04 19:51:20 +01:00
http-request deny deny_status 429 if max_req_rate !internal
2022-12-09 23:47:23 +01:00
2023-04-13 23:13:34 +02:00
{%- if haproxy.config.geoip.enabled %}
# GeoIP
http-request set-var(txn.country) lua.country(req.src)
2023-08-26 09:48:17 +02:00
http-request return status 200 content-type "text/html; charset=utf-8" lf-string "<html><body><p>ip: %[var(req.src)]</p><p>country: %[lua.country(req.src)]</p><p>city: %[lua.city(req.src)]<p></body></html>" if self_host path_location
2023-04-13 23:13:34 +02:00
acl allowed_country var(txn.country),map_str(/etc/haproxy/maps/countries,OK) OK
http-response deny deny_status 451 content-type text/plain string "Denied" if !allowed_country !internal
{%- endif %}
2022-12-09 23:47:23 +01:00
## Returns
2023-03-21 19:42:51 +01:00
http-request return status 200 content-type text/plain string "User-agent: *\r\nAllow: /" if robots_txt
2023-04-13 23:13:34 +02:00
http-request return status 200 content-type text/plain string "Contact: mailto:{{ haproxy.config.syscontact }}" if security_txt
2023-01-04 23:32:56 +01:00
http-request return status 200 content-type text/html lf-string "%H\n" if self_host path_host
http-request return status 200 content-type text/html lf-string "%[var(txn.httpdate)]\n" if self_host path_date
http-request return status 200 content-type text/html lf-string "%[var(txn.srchash)]\n" if self_host path_srchash
2022-12-09 23:47:23 +01:00
## Headers
2022-12-24 17:49:54 +01:00
http-request set-header X-Proxy-Id "%H"
2022-11-08 11:04:39 +01:00
http-request set-header X-Proto https if { ssl_fc }
2022-12-24 17:49:54 +01:00
2023-01-04 23:32:56 +01:00
http-response set-header Date "%[var(txn.httpdate)]"
2022-12-26 18:13:39 +01:00
http-response set-header Server "{{ haproxy.config.servername }}"
2023-01-04 23:32:56 +01:00
http-response set-header X-Random "%[var(txn.random)]"
2023-12-01 16:45:53 +01:00
.if feature(QUIC)
http-response set-header Alt-Svc "h3=\":443\"; ma=3600"
.endif
2022-12-24 17:49:54 +01:00
http-request redirect location %[req.hdr(Host),map_dom({{ haproxy.config.dir }}/maps/redirects)] code 301 if { req.hdr(Host),map_dom({{ haproxy.config.dir }}/maps/redirects) -m found }
2023-07-15 19:54:00 +02:00
http-request deny deny_status 404 unless domains || ua
2023-01-16 12:00:54 +01:00
{%- if haproxy.config.admin %}
2023-02-04 19:51:20 +01:00
use_backend admin if self_host internal
2022-12-09 23:47:23 +01:00
{%- endif %}
2023-07-15 19:54:00 +02:00
use_backend %[req.hdr(Host),lower,map({{ haproxy.config.dir }}/maps/vhosts)] if domains
use_backend %[req.hdr(User-Agent),map_beg({{ haproxy.config.dir }}/maps/ua)] if ua
2023-01-04 23:32:56 +01:00
default_backend {{ ns.default_backend }}
2022-10-01 20:06:19 +02:00
2023-04-13 23:13:34 +02:00
monitor-uri /dead_or_alive
log-format "%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"
2023-02-04 19:51:20 +01:00
2022-11-08 11:04:39 +01:00
# HTTP Backends
2022-12-03 00:07:39 +01:00
{%- for name, values in haproxy.config.vhosts.items() %}
2022-11-13 20:48:27 +01:00
{%- if not values.redirect|default(False) %}
2022-12-26 18:13:39 +01:00
backend {{ name }} from {{ haproxy.config.namespace }}
2022-11-13 20:48:27 +01:00
balance {{ values.balance|default(haproxy.config.balance) }}
2022-10-23 16:37:53 +02:00
mode http
option forwardfor
2022-11-13 20:48:27 +01:00
{%- if values.check|default(haproxy.config.check) %}
2022-11-08 11:04:39 +01:00
option httpchk
{%- for step in values.check_steps|default([]) %}
http-check {{ step }}
{%- endfor %}
{%- endif %}
2023-03-10 00:02:52 +01:00
{%- if values.overrides|default([]) %}
{%- for override in values.overrides %}
{{ override }}
{%- endfor %}
{%- endif %}
2022-11-13 20:48:27 +01:00
{%- if values.head|default(False) %}
2022-10-23 16:37:53 +02:00
{{ head() }}
2022-10-01 20:06:19 +02:00
{%- endif %}
2022-10-23 16:37:53 +02:00
2022-11-13 20:48:27 +01:00
{%- if values.compression|default(True) %}
2022-10-23 16:37:53 +02:00
{{ compression() }}
{%- endif %}
2022-11-13 20:48:27 +01:00
{%- if values.usecache|default(True) %}
2022-10-23 16:37:53 +02:00
{{ cache() }}
{%- endif %}
2022-11-13 20:48:27 +01:00
{%- if values.internal|default(False) %}
2022-10-23 16:37:53 +02:00
{{ internal() }}
2022-10-01 20:06:19 +02:00
{%- endif %}
2023-10-10 09:07:44 +02:00
{{- httpendpoints(servers=values.servers|default([]), check=values.check|default(haproxy.config.check)) }}
2022-11-08 11:04:39 +01:00
{%- endif %}
{% endfor %}
2022-10-01 20:06:19 +02:00
2022-11-08 11:04:39 +01:00
# TCP services
2022-12-03 00:07:39 +01:00
{%- for name, values in haproxy.config.services.items() %}
2022-12-26 18:13:39 +01:00
listen {{ name }} from {{ haproxy.config.namespace }}
2023-01-16 12:00:54 +01:00
bind *:{{ values.port }},:::{{ values.port }} v4v6 name {{ name }}
2022-10-01 20:06:19 +02:00
mode tcp
2022-10-23 16:37:53 +02:00
option tcplog
2023-02-04 19:51:20 +01:00
option tcpka
2022-10-01 20:06:19 +02:00
{%- if values.type == "postgres" %}
2022-12-09 21:56:25 +01:00
option pgsql-check user repmgr
2023-02-04 19:51:20 +01:00
{%- endif %}
{%- if values.type == "spoe" %}
timeout connect 1s
timeout server 1s
option spop-check
2022-10-01 20:06:19 +02:00
{%- endif %}
2023-08-30 13:58:10 +02:00
default-server inter 1s fall 5
2023-10-10 09:07:44 +02:00
{{- tcpendpoints(servers=values.servers|default([]), check=values.check|default(haproxy.config.check)) }}
2022-12-17 23:26:58 +01:00
{% endfor %}
2022-12-09 23:47:23 +01:00
2022-12-11 16:34:27 +01:00
# SPOE Agents
{%- for name, values in haproxy.config.spoe.items() %}
2023-02-04 19:51:20 +01:00
backend {{ name }} from {{ haproxy.config.namespace }}
mode tcp
timeout connect 5s
timeout server 3m
option spop-check
{{- tcpendpoints(servers=values.servers, check=values.check|default(haproxy.config.check)) }}
2022-12-17 23:26:58 +01:00
{% endfor %}
2022-12-11 16:34:27 +01:00
2023-01-16 12:00:54 +01:00
{%- if haproxy.config.admin %}
2023-04-13 23:13:34 +02:00
{{ admin() if haproxy.config.admin }}
2022-12-09 23:47:23 +01:00
{%- endif %}