diff --git a/ExampleAcme.md b/ExampleAcme.md index 9d262bc..49c3104 100644 --- a/ExampleAcme.md +++ b/ExampleAcme.md @@ -36,7 +36,6 @@ haproxy: ## Result ```bash - root@test-ag-haproxy-acme:/# ls -l /etc/dehydrated/ > -rw-r----- 1 root haproxy-acme 478 May 3 15:44 config > -rw-r----- 1 root haproxy-acme 898 May 4 13:29 domains.txt diff --git a/ExampleGeoIP.md b/ExampleGeoIP.md index d1cef3d..6840feb 100644 --- a/ExampleGeoIP.md +++ b/ExampleGeoIP.md @@ -46,7 +46,7 @@ haproxy: ```bash root@test-ag-haproxy-geoip:/# journalctl -u haproxy.service -f -> May 04 18:58:57 test-ag-haproxy-geoip haproxy[84265]: ::ffff:140.82.115.47:33494 [04/May/2024:18:58:57.790] fe_web~ be_test2/srv2 0/0/26/26/52 200 1778 - - ---- 2/2/0/0/0 0/0 {US|36459|github-camo (4b76e509)} "GET /infra_haproxy.pylint.svg HTTP/1.1" +> May 04 18:58:57 test-ag-haproxy-geoip haproxy[84265]: ::ffff:140.82.115.0:33494 [04/May/2024:18:58:57.790] fe_web~ be_test2/srv2 0/0/26/26/52 200 1778 - - ---- 2/2/0/0/0 0/0 {US|36459|github-camo (4b76e509)} "GET /infra_haproxy.pylint.svg HTTP/1.1" root@test-ag-haproxy-geoip:/#ls -l /var/local/lib/geoip/ > -rw-r--r-- 1 haproxy-geoip haproxy-geoip 11813924 May 2 18:24 asn.mmdb diff --git a/ExampleTCP.md b/ExampleTCP.md new file mode 100644 index 0000000..64ba42b --- /dev/null +++ b/ExampleTCP.md @@ -0,0 +1,142 @@ +# Basic TCP Example with GeoIP + +## Config + +```yaml +# you may want to add a prefix to the logs, so you can easily filter them in your log-processing system +# see also: https://www.haproxy.com/blog/haproxy-log-customization +logformat_tcp: "TCP: %ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq" +# 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: + frontends: + fe_mail_smtp: + mode: 'tcp' + bind: ['[::]:25 v4v6'] + + routes: + be_mail_smtp: + + lines: + - "log-format \"{{ logformat_tcp }}\"" + + fe_mail_imap: + mode: 'tcp' + bind: ['[::]:993 v4v6'] + + geoip: + enable: true + + routes: + be_mail_imap: + filter_country: ['SI'] + + lines: + - "log-format \"{{ logformat_tcp }}\"" + + default_backend: 'be_fallback_tcp' + + backends: + be_mail_smtp: + mode: 'tcp' + servers: 'mail-gateway 192.168.0.10:25' + + be_mail_imap: + mode: 'tcp' + servers: 'mail-server 192.168.0.11:993' + + be_fallback_tcp: + mode: 'tcp' + lines: 'tcp-request content reject' +``` + + +---- + +## Result + +For services and `haproxy.cfg` see [Example GeoIP](https://github.com/ansibleguy/infra_haproxy/blob/latest/ExampleGeoIP.md) + +```bash +# logs +root@test-ag-haproxy-tcp:/# journalctl -u haproxy -n 200 | grep TCP +> May 05 15:55:57 lb01 haproxy[99127]: TCP: ::ffff:193.222.96.0:57424 [05/May/2024:15:55:57.548] fe_mail_smtp be_mail_smtp/mail-gateway 1/25/274 297 -- 3/1/0/0/0 0/0 + +root@test-ag-haproxy-tcp:/# cat /etc/haproxy/conf.d/frontend.cfg +> # Ansible managed: Do NOT edit this file manually! +> # ansibleguy.infra_haproxy +> +> frontend fe_mail_smtp +> mode tcp +> bind [::]:25 v4v6 +> +> 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 +> +> frontend fe_mail_imap +> mode tcp +> bind [::]:993 v4v6 +> +> # GEOIP +> acl private_nets src 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.0/8 ::1 +> http-request set-var(txn.geoip_country) str(00) if private_nets +> +> ## GEOIP COUNTRY +> acl geoip_country_in_map src,ipmask(24,48),map_ip(/etc/haproxy/map/geoip_country.map) -m found +> http-request set-var(txn.geoip_country) src,ipmask(24,48),map(/etc/haproxy/map/geoip_country.map) if !private_nets geoip_country_in_map +> http-request lua.lookup_geoip_country if !{ var(txn.geoip_country) -m found } +> http-request set-map(/etc/haproxy/map/geoip_country.map) %[src,ipmask(24,48)] %[var(txn.geoip_country)] if !private_nets !geoip_country_in_map +> http-request capture var(txn.geoip_country) len 2 +> +> ## GEOIP ASN +> acl geoip_asn_in_map src,ipmask(24,48),map_ip(/etc/haproxy/map/geoip_asn.map) -m found +> http-request set-var(txn.geoip_asn) src,ipmask(24,48),map(/etc/haproxy/map/geoip_asn.map) if !private_nets geoip_asn_in_map +> http-request lua.lookup_geoip_asn if !{ var(txn.geoip_asn) -m found } +> http-request set-map(/etc/haproxy/map/geoip_asn.map) %[src,ipmask(24,48)] %[var(txn.geoip_asn)] if !private_nets !geoip_asn_in_map +> http-request capture var(txn.geoip_asn) len 10 +> +> 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 +> +> default_backend be_fallback_tcp + +root@test-ag-haproxy-tcp:/# cat /etc/haproxy/conf.d/backend.cfg +> # Ansible managed: Do NOT edit this file manually! +> # ansibleguy.infra_haproxy +> +> backend be_mail_smtp +> mode tcp +> balance leastconn +> +> server mail-gateway 192.168.0.10:25 check +> +> backend be_mail_imap +> mode tcp +> balance leastconn +> +> server mail-server 192.168.0.11:993 check +> +> backend be_fallback_tcp +> mode tcp +> balance leastconn +> +> tcp-request content reject +> +> backend be_haproxy_geoip +> server haproxy_geoip 127.0.0.1:8406 check + +``` diff --git a/ExampleWAF.md b/ExampleWAF.md index ede6a44..4ff649e 100644 --- a/ExampleWAF.md +++ b/ExampleWAF.md @@ -1,4 +1,4 @@ -# Basic Example with WAF +# Basic WAF Example There are still some basic WAF features to be implemented. diff --git a/README.md b/README.md index 631a775..74296e5 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Here some detailed config examples and their results: * [Example ACME](https://github.com/ansibleguy/infra_haproxy/blob/latest/ExampleAcme.md) * [Example GeoIP](https://github.com/ansibleguy/infra_haproxy/blob/latest/ExampleGeoIP.md) * [Example WAF](https://github.com/ansibleguy/infra_haproxy/blob/latest/ExampleWAF.md) +* [Example TCP](https://github.com/ansibleguy/infra_haproxy/blob/latest/ExampleTCP.md) ### Config diff --git a/defaults/main/1_main.yml b/defaults/main/1_main.yml index dcdf480..92c1e8e 100644 --- a/defaults/main/1_main.yml +++ b/defaults/main/1_main.yml @@ -21,6 +21,7 @@ defaults_haproxy: user: 'haproxy' pwd: 'monitor' realm: 'Authorized Personal Only' + log: true # allows you to disable logging of stats access acme: # see also: https://github.com/dehydrated-io/dehydrated/blob/master/docs/examples/config enable: false diff --git a/templates/etc/haproxy/conf.d/frontend.cfg.j2 b/templates/etc/haproxy/conf.d/frontend.cfg.j2 index 8c37cde..0a73afd 100644 --- a/templates/etc/haproxy/conf.d/frontend.cfg.j2 +++ b/templates/etc/haproxy/conf.d/frontend.cfg.j2 @@ -20,18 +20,18 @@ frontend {{ name }} {% endif %} {% endif %} {% if cnf.geoip.enable | bool %} -{% include "inc/geoip.j2" %} +{% include "inc/geoip.j2" %} {% endif %} {% if cnf.mode == 'http' %} {% include "inc/security.j2" %} {% include "inc/security_only_fe.j2" %} -{% endif %} {% if cnf.log.user_agent | bool %} http-request capture req.fhdr(User-Agent) len 200 {% endif %} +{% endif %} {% if cnf.lines | is_dict %} {% for section, lines in cnf.lines.items() %} # SECTION: {{ section }} @@ -54,30 +54,40 @@ frontend {{ name }} {% set be_cnf = defaults_frontend_route | combine(be_cnf_user, recursive=true) %} # 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 %} +{% 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 %} +{% else %} acl {{ be_name }}_filter_country always_true acl {{ be_name }}_filter_not_country always_false -{% endif %} -{% if cnf.geoip.asn | bool %} +{% 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 %} +{% else %} acl {{ be_name }}_filter_asn always_true acl {{ be_name }}_filter_not_asn always_false -{% endif %} +{% 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 %} {% endfor %} diff --git a/templates/etc/haproxy/conf.d/stats.cfg.j2 b/templates/etc/haproxy/conf.d/stats.cfg.j2 index 54b1ffc..7c47f31 100644 --- a/templates/etc/haproxy/conf.d/stats.cfg.j2 +++ b/templates/etc/haproxy/conf.d/stats.cfg.j2 @@ -12,6 +12,10 @@ frontend stats redirect code 301 location /stats if { path -i / } +{% if not HAPROXY_CONFIG.stats.log | bool %} + no log +{% endif %} + {% if HAPROXY_CONFIG.stats.admin | bool %} stats admin{% if HAPROXY_CONFIG.stats.admin_if | default(none, true) is not none %} if {{ HAPROXY_CONFIG.stats.admin_if }}{% endif %} {% endif %}