From ad9ad920d18a1ce31f855098f5951f2b08f959ff Mon Sep 17 00:00:00 2001 From: AnsibleGuy Date: Sun, 5 May 2024 18:40:01 +0200 Subject: [PATCH] cleaned-up route filter-templating --- ExampleAcme.md | 7 +-- ExampleGeoIP.md | 13 +--- ExampleTCP.md | 19 +++--- ExampleWAF.md | 5 +- filter_plugins/utils.py | 65 ++++++++++++++++++++ templates/etc/haproxy/conf.d/frontend.cfg.j2 | 38 ++---------- 6 files changed, 81 insertions(+), 66 deletions(-) diff --git a/ExampleAcme.md b/ExampleAcme.md index 91d107a..ad5bfde 100644 --- a/ExampleAcme.md +++ b/ExampleAcme.md @@ -116,12 +116,7 @@ root@test-ag-haproxy-acme:/# cat /etc/haproxy/conf.d/frontend.cfg > > # BACKEND be_test > acl be_test_domains req.hdr(host) -m str -i app.test.ansibleguy.net -> acl be_test_filter_ip always_true -> acl be_test_filter_not_ip always_false -> -> use_backend be_test if be_test_domains be_test_filter_ip !be_test_filter_not_ip -> -> +> use_backend be_test if be_test_domains > > default_backend be_fallback diff --git a/ExampleGeoIP.md b/ExampleGeoIP.md index fb56edd..de6b133 100644 --- a/ExampleGeoIP.md +++ b/ExampleGeoIP.md @@ -126,25 +126,18 @@ root@test-ag-haproxy-geoip:/# cat /etc/haproxy/conf.d/frontend.cfg > http-request capture req.fhdr(User-Agent) len 200 > > # BACKEND be_test1 -> acl be_test1_domains req.hdr(host) -m str -i app1.test.ansibleguy.net acl be_test1_filter_ip always_true -> acl be_test1_filter_not_ip always_false +> acl be_test1_domains req.hdr(host) -m str -i app1.test.ansibleguy.net > acl be_test1_filter_country var(txn.geoip_country) -m str -i AT -> acl be_test1_filter_not_country always_false > acl be_test1_filter_asn var(txn.geoip_asn) -m int -i 1337 -> acl be_test1_filter_not_asn always_false > -> use_backend be_test1 if be_test1_domains be_test1_filter_ip !be_test1_filter_not_ip be_test1_filter_asn !be_test1_filter_not_asn be_test1_filter_country !be_test1_filter_not_country +> use_backend be_test1 if be_test1_domains be_test1_filter_asn be_test1_filter_country > > # BACKEND be_test2 > acl be_test2_domains req.hdr(host) -m str -i app2.test.ansibleguy.net -> acl be_test2_filter_ip always_true -> acl be_test2_filter_not_ip always_false -> acl be_test2_filter_country always_true > acl be_test2_filter_not_country var(txn.geoip_country) -m str -i CN RU US -> acl be_test2_filter_asn always_true > acl be_test2_filter_not_asn var(txn.geoip_asn) -m int -i 100000 120000 > -> use_backend be_test2 if be_test2_domains be_test2_filter_ip !be_test2_filter_not_ip be_test2_filter_asn !be_test2_filter_not_asn be_test2_filter_country !be_test2_filter_not_country +> use_backend be_test2 if be_test2_domains !be_test2_filter_not_asn !be_test2_filter_not_country > > default_backend be_fallback diff --git a/ExampleTCP.md b/ExampleTCP.md index 24489d1..708461a 100644 --- a/ExampleTCP.md +++ b/ExampleTCP.md @@ -9,6 +9,10 @@ logformat_tcp: "TCP: %ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/% # logformat_http: "HTTP: %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" haproxy: + geoip: + enable: true + token: "" + frontends: fe_mail_smtp: mode: 'tcp' @@ -73,10 +77,7 @@ root@test-ag-haproxy-tcp:/# cat /etc/haproxy/conf.d/frontend.cfg > log-format "TCP: %ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq" > > # BACKEND be_mail_smtp -> acl be_mail_smtp_filter_ip always_true -> acl be_mail_smtp_filter_not_ip always_false -> -> use_backend be_mail_smtp if be_mail_smtp_filter_ip !be_mail_smtp_filter_not_ip +> use_backend be_mail_smtp > > frontend fe_mail_imap > mode tcp @@ -103,14 +104,8 @@ root@test-ag-haproxy-tcp:/# cat /etc/haproxy/conf.d/frontend.cfg > log-format "TCP: %ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq" > > # BACKEND be_mail_imap -> acl be_mail_imap_filter_ip always_true -> acl be_mail_imap_filter_not_ip always_false -> acl be_mail_imap_filter_country var(txn.geoip_country) -m str -i SI -> acl be_mail_imap_filter_not_country always_false -> acl be_mail_imap_filter_asn always_true -> acl be_mail_imap_filter_not_asn always_false -> -> use_backend be_mail_imap if be_mail_imap_filter_ip !be_mail_imap_filter_not_ip be_mail_imap_filter_asn !be_mail_imap_filter_not_asn be_mail_imap_filter_country !be_mail_imap_filter_not_country +> acl be_mail_imap_filter_country var(txn.geoip_country) -m str -i SI +> use_backend be_mail_imap if be_mail_imap_filter_country > > default_backend be_fallback_tcp diff --git a/ExampleWAF.md b/ExampleWAF.md index bb77604..04e2a80 100644 --- a/ExampleWAF.md +++ b/ExampleWAF.md @@ -123,10 +123,7 @@ root@test-ag-haproxy-waf:/# cat /etc/haproxy/conf.d/frontend.cfg > > # BACKEND be_test > acl be_test_domains req.hdr(host) -m str -i app.test.ansibleguy.net -> acl be_test_filter_ip always_true -> acl be_test_filter_not_ip always_false -> -> use_backend be_test if be_test_domains be_test_filter_ip !be_test_filter_not_ip +> use_backend be_test if be_test_domains > > default_backend be_fallback diff --git a/filter_plugins/utils.py b/filter_plugins/utils.py index c6dfce7..c3f2da5 100644 --- a/filter_plugins/utils.py +++ b/filter_plugins/utils.py @@ -10,6 +10,7 @@ def filters(self): "is_dict": self.is_dict, "safe_key": self.safe_key, "ssl_fingerprint_active": self.ssl_fingerprint_active, + "build_route": self.build_route, } @staticmethod @@ -43,3 +44,67 @@ def ssl_fingerprint_active(frontends: dict) -> bool: continue return False + + @staticmethod + def is_truthy(v: (bool, str, int)) -> bool: + return v in [True, 'yes', 'y', 'Yes', 'YES', 'true', 1, '1'] + + @classmethod + def build_route(cls, fe_cnf: dict, be_cnf: dict, be_name: str) -> list: + lines = [] + to_match = [] + var_prefix = f'{be_name}_filter' + + if fe_cnf['mode'] == 'http' and len(be_cnf['domains']) > 0: + lines.append( + f"acl {var_prefix}_domains req.hdr(host) " + f"-m str -i {' '.join(cls.ensure_list(be_cnf['domains']))}" + ) + to_match.append(f'{var_prefix}_domains') + + if len(be_cnf['filter_ip']) > 0: + lines.append(f"acl {var_prefix}_ip src {' '.join(cls.ensure_list(be_cnf['filter_ip']))}") + to_match.append(f'{var_prefix}_ip') + + if len(be_cnf['filter_not_ip']) > 0: + lines.append(f"acl {var_prefix}_not_ip src {' '.join(cls.ensure_list(be_cnf['filter_not_ip']))}") + to_match.append(f'!{var_prefix}_not_ip') + + if cls.is_truthy(fe_cnf['geoip']['enable']): + if cls.is_truthy(fe_cnf['geoip']['country']): + if len(be_cnf['filter_country']) > 0: + lines.append( + f"acl {var_prefix}_country var(txn.geoip_country) " + f"-m str -i {' '.join(cls.ensure_list(be_cnf['filter_country']))}" + ) + to_match.append(f'{var_prefix}_country') + + if len(be_cnf['filter_not_country']) > 0: + lines.append( + f"acl {var_prefix}_not_country var(txn.geoip_country) " + f"-m str -i {' '.join(cls.ensure_list(be_cnf['filter_not_country']))}" + ) + to_match.append(f'!{var_prefix}_not_country') + + if cls.is_truthy(fe_cnf['geoip']['asn']): + if len(be_cnf['filter_asn']) > 0: + lines.append( + f"acl {var_prefix}_asn var(txn.geoip_asn) " + f"-m str -i {' '.join(cls.ensure_list(be_cnf['filter_asn']))}" + ) + to_match.append(f'{var_prefix}_asn') + + if len(be_cnf['filter_not_asn']) > 0: + lines.append( + f"acl {var_prefix}_not_asn var(txn.geoip_asn) " + f"-m str -i {' '.join(cls.ensure_list(be_cnf['filter_not_asn']))}" + ) + to_match.append(f'!{var_prefix}_not_asn') + + if len(to_match) > 0: + lines.append(f"use_backend {be_name} if {' '.join(to_match)}") + + else: + lines.append(f"use_backend {be_name}") + + return lines diff --git a/templates/etc/haproxy/conf.d/frontend.cfg.j2 b/templates/etc/haproxy/conf.d/frontend.cfg.j2 index 0a73afd..2d4abb2 100644 --- a/templates/etc/haproxy/conf.d/frontend.cfg.j2 +++ b/templates/etc/haproxy/conf.d/frontend.cfg.j2 @@ -52,42 +52,12 @@ frontend {{ name }} {% endif %} {% for be_name, be_cnf_user in cnf.routes.items() %} {% set be_cnf = defaults_frontend_route | combine(be_cnf_user, recursive=true) %} +{% set be_route = cnf | build_route(be_cnf, be_name) %} # BACKEND {{ be_name }} -{% if cnf.mode == 'http' %} - acl {{ be_name }}_domains {% if be_cnf.domains | length > 0 %}req.hdr(host) -m str -i {{ be_cnf.domains | ensure_list | join(' ') }}{% else %}always_true{% endif +%} -{% endif %} - acl {{ be_name }}_filter_ip {% if be_cnf.filter_ip | length > 0 %}src {{ be_cnf.filter_ip | ensure_list | join(' ') }}{% else %}always_true{% endif +%} - acl {{ be_name }}_filter_not_ip {% if be_cnf.filter_not_ip | length > 0 %}src {{ be_cnf.filter_not_ip | ensure_list | join(' ') }}{% else %}always_false{% endif +%} -{% if HAPROXY_CONFIG.geoip.enable | bool and cnf.geoip.enable | bool %} -{% if cnf.geoip.country | bool %} - acl {{ be_name }}_filter_country {% if be_cnf.filter_country | length > 0 %}var(txn.geoip_country) -m str -i {{ be_cnf.filter_country | ensure_list | join(' ') }}{% else %}always_true{% endif +%} - acl {{ be_name }}_filter_not_country {% if be_cnf.filter_not_country | length > 0 %}var(txn.geoip_country) -m str -i {{ be_cnf.filter_not_country | ensure_list | join(' ') }}{% else %}always_false{% endif +%} -{% else %} - acl {{ be_name }}_filter_country always_true - acl {{ be_name }}_filter_not_country always_false -{% endif %} -{% if cnf.geoip.asn | bool %} - acl {{ be_name }}_filter_asn {% if be_cnf.filter_asn | length > 0 %}var(txn.geoip_asn) -m int -i {{ be_cnf.filter_asn | ensure_list | join(' ') }}{% else %}always_true{% endif +%} - acl {{ be_name }}_filter_not_asn {% if be_cnf.filter_not_asn | length > 0 %}var(txn.geoip_asn) -m int -i {{ be_cnf.filter_not_asn | ensure_list | join(' ') }}{% else %}always_false{% endif +%} -{% else %} - acl {{ be_name }}_filter_asn always_true - acl {{ be_name }}_filter_not_asn always_false -{% endif %} - -{% if cnf.mode == 'http' %} - use_backend {{ be_name }} if {{ be_name }}_domains {{ be_name }}_filter_ip !{{ be_name }}_filter_not_ip {{ be_name }}_filter_asn !{{ be_name }}_filter_not_asn {{ be_name }}_filter_country !{{ be_name }}_filter_not_country -{% else %} - use_backend {{ be_name }} if {{ be_name }}_filter_ip !{{ be_name }}_filter_not_ip {{ be_name }}_filter_asn !{{ be_name }}_filter_not_asn {{ be_name }}_filter_country !{{ be_name }}_filter_not_country -{% endif %} -{% else %} - -{% if cnf.mode == 'http' %} - use_backend {{ be_name }} if {{ be_name }}_domains {{ be_name }}_filter_ip !{{ be_name }}_filter_not_ip -{% else %} - use_backend {{ be_name }} if {{ be_name }}_filter_ip !{{ be_name }}_filter_not_ip -{% endif %} -{% endif %} +{% for line in be_route %} + {{ line }} +{% endfor %} {% endfor %}