From ad98d3c6d28634aab1a2e7e07b2ea160252e8fa4 Mon Sep 17 00:00:00 2001 From: Paul Lecuq Date: Sat, 4 Feb 2023 19:51:20 +0100 Subject: [PATCH] updated haproxy state --- states/haproxy/defaults.yaml | 6 +-- states/haproxy/map.jinja | 18 ++++++- states/haproxy/scripts/collector.lua | 10 ++-- .../scripts/{hello_world.lua => hello.lua} | 0 .../{weight_by_latency.lua => weight.lua} | 0 states/haproxy/templates/haproxy.cfg.j2 | 50 +++++++++++++++---- states/haproxy/templates/spoe.cfg.j2 | 33 ++++++------ 7 files changed, 79 insertions(+), 38 deletions(-) rename states/haproxy/scripts/{hello_world.lua => hello.lua} (100%) rename states/haproxy/scripts/{weight_by_latency.lua => weight.lua} (100%) diff --git a/states/haproxy/defaults.yaml b/states/haproxy/defaults.yaml index 84c242e..4afc712 100644 --- a/states/haproxy/defaults.yaml +++ b/states/haproxy/defaults.yaml @@ -11,19 +11,18 @@ haproxy: config: dir: /etc/haproxy configfile: haproxy.cfg + peers: dirs: - maps - scripts - mods - errors scripts: - - name: scripts/http.lua - lib: true - name: scripts/json.lua lib: true - name: scripts/collector.lua lib: false - - name: scripts/weight_by_latency.lua + - name: scripts/weight.lua lib: false namespace: paulbsd user: haproxy @@ -75,6 +74,7 @@ haproxy: ddos: timeperiod: 10s maxrequests: 200 + size: 1m domains: {} vhosts: {} services: {} diff --git a/states/haproxy/map.jinja b/states/haproxy/map.jinja index 6595cc5..75eef84 100644 --- a/states/haproxy/map.jinja +++ b/states/haproxy/map.jinja @@ -3,4 +3,20 @@ {%- set haproxy = salt['pillar.get']('haproxy', default=defaults.haproxy, merge=True) -%} {%- set users = salt['pillar.get']('htpasswds') -%} -{%- set net = salt['pillar.get']('net') -%} \ No newline at end of file +{%- set net = salt['pillar.get']('net') -%} + +{% set peers = salt['mine.get']( + tgt='G@lb:true', + fun='internal_ip_addrs', + tgt_type='compound') %} + +{% set peers_ip = [] %} +{%- for k,v in peers.items() %} +{%- if k != salt['grains.get']('fqdn') %} +{%- for i in v %} +{% set _ = peers_ip.append([k,i]) %} +{%- endfor %} +{%- endif %} +{%- endfor %} + +{% do haproxy.config.update({"peers": peers_ip }) %} diff --git a/states/haproxy/scripts/collector.lua b/states/haproxy/scripts/collector.lua index 80c402a..f14a945 100644 --- a/states/haproxy/scripts/collector.lua +++ b/states/haproxy/scripts/collector.lua @@ -1,9 +1,6 @@ ---require = GLOBAL.require ---require("http.request") -local request = require("http") local json = require("json") -core.register_action("test", { "http-req" }, function(txn) +core.register_action("collector", { "http-req" }, function(txn) local info = {} local headers = {} @@ -27,9 +24,8 @@ core.register_action("test", { "http-req" }, function(txn) info["path"] = reqpath local infojson = json.encode(info) - --local req = request.new_from_uri("https://ipbl.paulbsd.com") - --local headers, stream = req:go() - --local body = assert(stream:get_body_as_string()) + -- httpclient is a haproxy 2.5 class + local req = httpclient:post{url="https://ipbl.paulbsd.com", body=infojson} txn.Info(txn, infojson) end diff --git a/states/haproxy/scripts/hello_world.lua b/states/haproxy/scripts/hello.lua similarity index 100% rename from states/haproxy/scripts/hello_world.lua rename to states/haproxy/scripts/hello.lua diff --git a/states/haproxy/scripts/weight_by_latency.lua b/states/haproxy/scripts/weight.lua similarity index 100% rename from states/haproxy/scripts/weight_by_latency.lua rename to states/haproxy/scripts/weight.lua diff --git a/states/haproxy/templates/haproxy.cfg.j2 b/states/haproxy/templates/haproxy.cfg.j2 index 361dbcb..c9e11cf 100644 --- a/states/haproxy/templates/haproxy.cfg.j2 +++ b/states/haproxy/templates/haproxy.cfg.j2 @@ -2,20 +2,20 @@ {%- from "haproxy/map.jinja" import haproxy,certs with context %} {%- set fqdn = salt["grains.get"]("fqdn") %} -{%- set default_backend = "test" %} {%- set ns = namespace(default_backend='notdefined') %} {%- for name, values in haproxy.config.vhosts.items() %}{% if values.default_backend|default(false) %}{% set ns.default_backend = name %}{% endif %}{% endfor %} {%- macro internal() -%} acl internal src -f {{ haproxy.config.dir }}/maps/access - http-response return status 403 content-type text/html string "403 forbidden" if ! internal + http-response return status 403 content-type text/html string "403 forbidden" if !internal {%- endmacro -%} {%- macro head() -%} - http-request return status 200 if { method -i HEAD } + http-request return status 200 if METH_HEAD {%- endmacro -%} {%- macro statusresponses() -%} + http-response return status 403 content-type text/html string "403 forbidden" if { status 403 } http-response return status 404 content-type text/html string "404 not found" if { status 404 } {%- endmacro -%} @@ -89,20 +89,33 @@ defaults {{ haproxy.config.namespace }} {{ key }} {{ value }} {%- endfor %} +{% if haproxy.config.peers|length > 0 -%} +peers paulbsd + bind *:4096 ssl crt {{ haproxy.config.acme_fullchains_dir }} + default-server ssl verify none + server {{ salt['grains.get']('fqdn') }} +{%- for peer in haproxy.config.peers %} + server {{ peer[0] }} {{ peer[1] }}:4096 +{%- endfor %} +{%- endif %} + # Cache cache static total-max-size 64 - max-object-size {{ haproxy.config.cache.size|default(50000) }} + max-object-size {{ haproxy.config.cache.size|default(5000) }} max-age 120 # Per IP rates stick table backend per_ip_rates from {{ haproxy.config.namespace }} - stick-table type string size 1m expire {{ haproxy.config.ddos.timeperiod|default("10s") }} store http_req_rate({{ haproxy.config.ddos.timeperiod|default("10s")}}) + 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 # Default HTTP frontend frontend http from {{ haproxy.config.namespace }} bind *:{{ haproxy.config.http_port }},:::{{ haproxy.config.http_port }} v4v6 name http mode http +{% for name, service in haproxy.config.spoe.items() %} + filter spoe engine {{ name }} config {{ haproxy.config.dir }}/spoe.cfg +{%- endfor %} ## ACLs acl http ssl_fc,not @@ -126,14 +139,17 @@ frontend https from {{ haproxy.config.namespace }} #bind quic4@*:{{ haproxy.config.https_port }},quic6@:::{{ haproxy.config.https_port }} v4v6 ssl crt {{ haproxy.config.acme_fullchains_dir }}{% if haproxy.config.http2 %} alpn h2,http/1.1{% endif %} mode http option httplog +{% for name, service in haproxy.config.spoe.items() %} + filter spoe engine {{ name }} config {{ haproxy.config.dir }}/spoe.cfg +{%- endfor %} ## ACLs acl internal src -f {{ haproxy.config.dir }}/maps/access acl domains req.hdr(Host),map_dom({{ haproxy.config.dir }}/maps/domains) -m found req.hdr(host) -m str %H acl robots_txt path /robots.txt + acl max_req_rate sc_http_req_rate(0) gt {{ haproxy.config.ddos.maxrequests|default(200) }} acl self_host req.hdr(Host) {{ fqdn }} acl path_root path / - acl path_admin path_beg /; acl path_host path_dir /host acl path_date path_dir /date acl path_srchash path /srchash @@ -153,7 +169,7 @@ frontend https from {{ haproxy.config.namespace }} http-request capture sc_http_req_rate(0) len 4 ## DDoS - http-request deny deny_status 429 if { sc_http_req_rate(0) gt {{ haproxy.config.ddos.maxrequests|default(200)}} } !internal + http-request deny deny_status 429 if max_req_rate !internal ## Returns http-request return status 200 content-type text/plain string "User-agent: *\r\nDisallow: /" if robots_txt @@ -170,15 +186,17 @@ frontend https from {{ haproxy.config.namespace }} http-response set-header X-Random "%[var(txn.random)]" 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" + 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 } http-request deny deny_status 404 unless domains {%- if haproxy.config.admin %} - use_backend admin if self_host internal path_admin + use_backend admin if self_host internal {%- endif %} use_backend %[req.hdr(Host),lower,map({{ haproxy.config.dir }}/maps/vhosts,nginx)] monitor-uri /dead_or_alive default_backend {{ ns.default_backend }} + # HTTP Backends {%- for name, values in haproxy.config.vhosts.items() %} {%- if not values.redirect|default(False) %} @@ -191,6 +209,7 @@ backend {{ name }} from {{ haproxy.config.namespace }} {%- for step in values.check_steps|default([]) %} http-check {{ step }} {%- endfor %} + {{ statusresponses() }} {%- endif %} {%- if values.head|default(False) %} {{ head() }} @@ -217,9 +236,14 @@ listen {{ name }} from {{ haproxy.config.namespace }} bind *:{{ values.port }},:::{{ values.port }} v4v6 name {{ name }} mode tcp option tcplog + option tcpka {%- if values.type == "postgres" %} option pgsql-check user repmgr - option tcpka +{%- endif %} +{%- if values.type == "spoe" %} + timeout connect 1s + timeout server 1s + option spop-check {%- endif %} default-server inter 3s fall 3 {{- tcpendpoints(servers=values.servers, check=values.check|default(haproxy.config.check)) }} @@ -227,8 +251,12 @@ listen {{ name }} from {{ haproxy.config.namespace }} # SPOE Agents {%- for name, values in haproxy.config.spoe.items() %} -listen {{ name }} from {{ haproxy.config.namespace }} - bind *:{{ values.port }},:::{{ values.port }} v4v6 +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)) }} {% endfor %} {%- if haproxy.config.admin %} diff --git a/states/haproxy/templates/spoe.cfg.j2 b/states/haproxy/templates/spoe.cfg.j2 index ac4ffa3..98112ee 100644 --- a/states/haproxy/templates/spoe.cfg.j2 +++ b/states/haproxy/templates/spoe.cfg.j2 @@ -1,23 +1,24 @@ ## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} {%- from "haproxy/map.jinja" import haproxy with context %} -{%- for name, values in haproxy.config.spoe.items() %} +{% for name, values in haproxy.config.spoe.items() -%} [{{ name }}] spoe-agent {{ name }} - messages {{ " ".join(values.messages.keys()) }} option var-prefix {{ name }} - timeout hello 2s - timeout idle 2m - timeout processing 50ms - use-backend {{ name }} + messages {{ " ".join(values.messages.keys()) }} + option var-prefix {{ name }} log global + timeout hello {{ values.timeout_hello|default("2s") }} + timeout idle {{ values.timeout_idle|default("2m") }} + timeout processing {{ values.timeout_processing|default("300ms") }} + use-backend {{ name }} -#{%- for m, m_values in values.messages.items() %} -#spoe-message {{ m }} -# {%- if m_values.args.keys() > 0 %} -# args {# " ".join(["%s=%s".format(i,v) for (i, v) in m_values.args.items()]) #} -# {%- endif %} -# {%- if m_values.event %} -# event {{ m_values.event }} -# {%- endif %} -#{% endfor %} -#{% endfor -%} +{% for m, m_values in values.messages.items() -%} +spoe-message {{ m }} + {%- if m_values.args.keys()|length > 0 %} + args {% for i,v in m_values.args.items() %}{{ "{}={}".format(i,v) }} {% endfor %} + {%- endif %} + {%- if m_values.event %} + event {{ m_values.event }} + {%- endif %} +{% endfor -%} +{% endfor -%}