ansibleguy.infra_haproxy
<a href="https://www.haproxy.com">
<img src="https://www.haproxy.com/assets/legal/web-logo.png" alt="HAProxy Logo" width="300"/>
</a>
# Ansible Role - HAProxy Community (with ACME, GeoIP, and some WAF Features)
This role sets up HAProxy (specifically the Community Version).
I find the structure of `frontend` => `route` => `backend` used in this role very user-friendly. Please [share your feedback](https://github.com/ansibleguy/infra_haproxy/discussions)!
<a href='https://ko-fi.com/ansible0guy' target='_blank'><img height='35' style='border:0px;height:46px;' src='https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0' alt='Buy me a coffee' />
[](https://github.com/ansibleguy/_meta_cicd/blob/latest/templates/usr/local/bin/cicd/molecule.sh.j2)
[](https://github.com/ansibleguy/_meta_cicd/blob/latest/templates/usr/local/bin/cicd/yamllint.sh.j2)
[](https://github.com/ansibleguy/_meta_cicd/blob/latest/templates/usr/local/bin/cicd/pylint.sh.j2)
[](https://github.com/ansibleguy/_meta_cicd/blob/latest/templates/usr/local/bin/cicd/ansiblelint.sh.j2)
[](https://galaxy.ansible.com/ui/standalone/roles/ansibleguy/infra_haproxy)
Molecule Logs: [Short](https://badges.ansibleguy.net/log/molecule_infra_haproxy_test_short.log), [Full](https://badges.ansibleguy.net/log/molecule_infra_haproxy_test.log)
**Tested on:**
* Debian 12
----
## Install
```bash
# latest version
ansible-galaxy role install git+https://github.com/ansibleguy/infra_haproxy
# from Ansible Galaxy
ansible-galaxy install ansibleguy.infra_haproxy
# or install to a custom role path
ansible-galaxy install ansibleguy.infra_haproxy --roles-path ./roles
Roadmap
- Security features
- Basic rate limiting for GET/HEAD and POST/PUT/DELETE methods
- Generic client fingerprinting
- Interface to convert Dict to Map-File easily
- Option to download and integrate IP lists (like Tor exit nodes) easily
- Simple way to change default error pages
Usage
Want a simple Ansible GUI? Check out my Ansible WebUI
Examples
Here are some detailed configuration examples and their results:
Configuration
Minimal example
haproxy:
acme:
enable: true
email: '[email protected]'
frontends:
fe_web:
bind: ['[::]:80 v4v6', '[::]:443 v4v6 ssl']
acme:
enable: true
routes:
be_intern:
domains: ['app.template.ansibleguy.net']
default_backend: 'be_fallback'
backends:
be_intern:
servers:
- 'srv-1 192.168.10.11:80'
- 'srv-2 192.168.10.12:80'
be_fallback:
lines: 'http-request redirect code 302 location https://github.com/ansibleguy'
Define the configuration as needed:
haproxy:
version: '2.8'
acme:
enable: true
email: '[email protected]'
# FRONTENDS
frontends:
fe_web:
bind: ['[::]:80 v4v6', '[::]:443 v4v6 ssl']
acme:
enable: true
domains: ['app.template.ansibleguy.net'] # domains from routes will also be added
routes:
be_app01:
domains: ['app01.template.ansibleguy.net', 'hello.template.ansibleguy.net']
# define raw config sections/lines to add
lines:
section1:
- ...
default_backend: 'be_fallback'
fe_dbs:
mode: 'tcp'
default_backend: 'be_db'
fe_restricted:
bind: ['[::]:8080 v4v6', '[::]:8443 v4v6 ssl crt /etc/myapp/mycert.pem']
geoip:
enable: true
security:
restrict_methods: true
allow_only_methods: ['HEAD', 'GET', 'POST']
fingerprint_ssl: true # create and log the JA3 SSL fingerprint of clients
# very basic filtering of bad bots based on user-agent matching
block_script_bots: true
block_bad_crawler_bots: true
routes:
be_app02:
filter_country: ['AT', 'DE', 'CH']
# filter_ip: ['10.0.0.0/8']
domains: ['app01.template.ansibleguy.net', 'hello.template.ansibleguy.net']
# define raw config sections/lines to add
lines:
section1:
- ...
default_backend: 'be_fallback'
# BACKENDS
backends:
be_app01:
servers:
- 'app01-1 10.0.1.1:80'
- 'app01-2 10.0.1.2:80'
check_uri: '/health'
check_expect: 'status 200'
be_app02:
security:
# very basic filtering of bad bots based on user-agent matching
block_script_bots: true
block_bad_crawler_bots: true
ssl: true
ssl_verify: 'none' # default; example: 'required ca-file /etc/ssl/certs/my_ca.crt verifyhost host01.intern'
servers:
- 'app02-1 10.0.1.1:443'
- 'app02-2 10.0.1.2:443'
be_db:
mode: 'tcp'
balance: 'roundrobin'
# define raw config sections/lines to add
lines:
section1:
- 'option mysql-check user haproxy_check'
servers:
- 'mysql-1 10.0.0.1:3306'
- 'mysql-2 10.0.0.2:3306'
be_fallback:
lines:
default: 'http-request redirect code 302 location https://github.com/ansibleguy'
# GENERAL
stats:
enable: true # enable stats http listener
bind: '127.0.0.1:8404' # default
geoip:
enable: true
provider: 'ipinfo' # or 'maxmind'
token: '<YOUR-TOKEN>'
# define globals/defaults as key/value pairs (multi-value lists)
global:
ca-base: '/etc/ssl/certs'
defaults:
mode: 'http'
'timeout connect': 3000
'timeout server': 5000
'timeout client': 5000
You may want to use 'ansible-vault' to encrypt your passwords:
ansible-vault encrypt_string
Functionality
Package installation
- Necessary repositories (minimal)
- HAProxy
- GeoIP
- ACME
- Dependencies
- Lightweight Nginx for handling challenge-response
Configuration
Default config:
- Global and default settings similar to standard installations
Default opt-ins:
- Frontend configurations
- HTTP mode
- Redirect non-SSL traffic to SSL
- Log User-Agent
- Set basic security headers
- Block TRACE & CONNECT methods
- HTTP mode
- Frontend configurations
Default opt-outs:
Stats HTTP listener
Frontend options
- ACME/LetsEncrypt
- GeoIP Lookups
- Block known script-bots
- SSL Fingerprinting (JA3)
Backend options
- Sticky sessions
- Block TRACE & CONNECT methods
Info
Note: This role currently supports only Debian-based systems.
Note: Many of the role's features can be enabled or disabled.
For all options available, see the default configuration in the main defaults file!
Warning: Not all settings you provide will be validated. Incorrect configuration may cause the role to fail!
Info: You can filter access to backends using the
filter
andfilter_not
settings:filter_ip
,filter_not_ip
,filter_country
,filter_not_country
,filter_asn
,filter_not_asn
Info: You can enable basic user-agent based script and bad-crawler bot blocking for frontends and backends. Check the defaults for the list of bots that are blocked.
Info: You can restrict allowed HTTP methods on a specific frontend or backend by enabling
security.restrict_methods
and specifyingsecurity.allow_only_methods
.Info: Check the Fingerprinting Docs for more information on tracking clients.
Info: If you use Graylog Server to analyze your logs, make sure to split your HAProxy logs into fields using pipeline rules. Example: HAProxy Community - Graylog Pipeline Rule
Tip: Increase the number of available
track-sc
by setting the global option tune.stick-counters. Useful in complex environments with rate-limits.
GeoIP
Warning: If you use the automatically provided GeoIP databases, ensure compliance with their license:
IPinfo: Information, CC4 License (allows commercial use - attribution is required)
Attribution:
<p>IP address data powered by <a href="https://ipinfo.io">IPinfo</a></p>
MaxMind: Information, EULA (allows limited commercial use - attribution is required)
Attribution:
This product includes GeoLite2 data created by MaxMind, available from <a href="https://www.maxmind.com">https://www.maxmind.com</a>.
Info: Create a free account to access GeoIP Tokens:
- IPInfo: Login/Register
- MaxMind: Login/Register - Set
token
to<ACCOUNT>:<LICENSE>
Info: For self-managed GeoIP databases (not recommended), they should be placed at
/var/local/lib/geoip
and namedasn.mmdb
&country.mmdb
.Info: Test the GeoIP Lookup Microservice using curl:
curl 'http://127.0.0.1:10069/?lookup=country&ip=1.1.1.1'
WAF
Note: The WAF/security features in this role are not as comprehensive as those available in HAProxy Enterprise by default. If budget allows, consider using that version.
Tip: Use
security.flag_bots
to enforce stricter rules for suspected bots.Examples:
Lower rate limit for bots:
http-request deny deny_status 429 if !{ var(txn.bot) -m int 0 } { sc_http_req_rate(0) gt 50 }
Prevent bots from registering accounts:
http-request deny deny_status 400 if !{ var(txn.bot) -m int 0 } { method POST } { path_sub -m str -i /register/ }
Pass the bot flag to your application for user-friendly error messages:
http-request add-header X-Bot %[var(txn.bot)]
Note: Before using
security.block_script_kiddies
, review the block-list in the defaults and modify as needed.
TCP
Info: To dynamically capture data, implement
tcp-request content capture
.You must enable logging of captured data by modifying the log format:
{% raw %}<DEFAULT LOG FORMAT HERE> {%[capture.req.hdr(0)]|%[capture.req.hdr(1)]}{% endraw %}
This allows logging of SNI or GeoIP information.
Execution
Run the playbook:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
Useful tags are available:
- install
- config => update configuration and SSL certificates
- ssl or acme
- geoip
- lua
To debug errors, set the 'debug' variable during runtime:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml -e debug=yes
```
Provision HAProxy Community (with ACME, GeoIP and some WAF-Features)
ansible-galaxy install ansibleguy.infra_haproxy