diff --git a/README.md b/README.md index b5ee411..d7d65d0 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,9 @@ ansible-vault encrypt_string * Pass the flag to your application to show a pretty error: `http-request add-header X-Bot %[var(txn.bot)]` + +* **Note**: If you want to use `security.block_script_kiddies` make sure you check out the block-list in the [defaults](https://github.com/ansibleguy/infra_haproxy/blob/latest/defaults/main/2_waf.yml) and add excludes as needed. + ### TCP * **Info**: If you want to capture data dynamically, you can use `tcp-request content capture`. diff --git a/defaults/main/0_hardcoded.yml b/defaults/main/0_hardcoded.yml index 819384f..d8c213d 100644 --- a/defaults/main/0_hardcoded.yml +++ b/defaults/main/0_hardcoded.yml @@ -10,6 +10,7 @@ HAPROXY_HC: path: config: '/etc/haproxy/conf.d' map: '/etc/haproxy/map' + lst: '/etc/haproxy/lst' lua: '/etc/haproxy/lua' geoip_bin: '/usr/local/bin/geoip-lookup' geoip_bin_src: "geoip-lookup-linux-{{ cpu_arch }}-CGO0" @@ -27,6 +28,17 @@ HAPROXY_HC: acme_challenges: '/var/www/haproxy_acme' acme_config: '/etc/dehydrated' + file: + lst: + script_kiddy_beg: 'waf-script-kiddy-path-beg.lst' + script_kiddy_end: 'waf-script-kiddy-path-end.lst' + script_kiddy_sub: 'waf-script-kiddy-path-sub.lst' + crawler_full: 'waf-crawler-ua-full.lst' + crawler_sub: 'waf-crawler-ua-sub.lst' + bot_sub: 'waf-bot-ua-sub.lst' + bad_bot_sub: 'waf-badbot-ua-sub.lst' + bad_bot_full: 'waf-badbot-ua-full.lst' + user: 'haproxy' group: 'haproxy' diff --git a/defaults/main/2_waf.yml b/defaults/main/2_waf.yml index 1e6975d..60ea38d 100644 --- a/defaults/main/2_waf.yml +++ b/defaults/main/2_waf.yml @@ -141,7 +141,6 @@ defaults_waf: path_beg: - '/cgi-bin/' - - '/icons/' - '/manager/' - '/php' - '/program/' @@ -159,13 +158,48 @@ defaults_waf: - '/xmlrpc' - '/%' - '/.' + - '//' - '/securityRealm/' - '/magmi/' - - '/menu/' - - '/etc/' - '/HNAP1' + - '/vendor/' + - '/login.action' + - 'google.com:443' # test CONNECT method + - '/actuator/' + - '/telescope/requests' + - '/geoserver/' + # information gathering + - '/server-status' + - '/FPURL.xml' + - '/.vscode/' + - '/.idea/' + - '/config.json' + # data extraction + - '/backup' + - '/old' + - '/new' + - '/home' + - '/main' + - '/www/' + - '/etc/' + - '/config/aws.yml' + - '/aws.yml' + - '/aws-secret.yaml' + - '/.aws/' + # MS Exchange + - '/ecp/' + - '/ews/' + - '/autodiscover/' + # Wordpress + - '/wp' + - '/wordpress' path_end: + - 'login' + - 'login.jsp' + - 'logon.htm' + - 'logon.html' + - 'logincheck' # scripts etc - '.php' - '.asp' @@ -180,8 +214,13 @@ defaults_waf: - '.cs' - '.application' - '.exe' + - '.cfm' + - '.pl' + - '.shtml' + - '.jsp' + - '.jsa' + - '.mwsl' # information gathering - - '.env' - '.git/config' - '.git/HEAD' - '.git/index' @@ -189,6 +228,7 @@ defaults_waf: - '.aws/config' - '.config' - '.settings' + - '.properties' # data extraction - '.zip' - '.tar' @@ -200,6 +240,28 @@ defaults_waf: - '.sql' - '.sqlite3' - '.bak' + - '.yml' + - '.yaml' + - '/credentials' + - '/config' path_sub: - '/../' + - '/.%2e/' + - '/%%32%65%%32%65/' + - '/.env' + - 'eval' + - 'shell' + - '/.git/' + - '/;' + - '/bin/' + - 'phpinfo' + - '/debug' + - '/admin/' + + # todo: query/body + # * filter hex/hashes + # also in this format: 'C%97%85%EC%86%8C%F0%9F%90%89%EC%97' + # * curl/wget/chmod/chown + # * filter sqli + # AAA%20or%201=0%20&username=admin&password=shagenda--%20- diff --git a/tasks/debian/config.yml b/tasks/debian/config.yml index 7b874d3..7121519 100644 --- a/tasks/debian/config.yml +++ b/tasks/debian/config.yml @@ -1,5 +1,23 @@ --- +- name: HAProxy | Config | WAF Lists + ansible.builtin.template: + src: "templates/etc/haproxy/lst/{{ item }}.j2" + dest: "{{ HAPROXY_HC.path.lst }}/{{ item }}" + owner: 'root' + group: 'haproxy' + mode: 0640 + notify: HAProxy-reload + loop: + - '{{ HAPROXY_HC.file.lst.script_kiddy_beg }}' + - '{{ HAPROXY_HC.file.lst.script_kiddy_end }}' + - "{{ HAPROXY_HC.file.lst.script_kiddy_sub }}" + - "{{ HAPROXY_HC.file.lst.crawler_full }}" + - "{{ HAPROXY_HC.file.lst.crawler_sub }}" + - "{{ HAPROXY_HC.file.lst.bot_sub }}" + - "{{ HAPROXY_HC.file.lst.bad_bot_sub }}" + - "{{ HAPROXY_HC.file.lst.bad_bot_full }}" + - name: HAProxy | Config | Globals/Defaults ansible.builtin.template: src: 'templates/etc/haproxy/haproxy.cfg.j2' diff --git a/tasks/debian/geoip.yml b/tasks/debian/geoip.yml index 5efe4c3..713f529 100644 --- a/tasks/debian/geoip.yml +++ b/tasks/debian/geoip.yml @@ -15,6 +15,7 @@ mode: 0750 loop: - "{{ HAPROXY_HC.path.map }}" + - "{{ HAPROXY_HC.path.lst }}" - "{{ HAPROXY_HC.path.lua }}" - '/tmp/haproxy' diff --git a/templates/etc/haproxy/conf.d/inc/security.j2 b/templates/etc/haproxy/conf.d/inc/security.j2 index 2188c78..bebcb9d 100644 --- a/templates/etc/haproxy/conf.d/inc/security.j2 +++ b/templates/etc/haproxy/conf.d/inc/security.j2 @@ -8,31 +8,31 @@ {% if cnf.security.block_script_bots | bool %} # block well-known script-bots {% if HAPROXY_WAF.user_agents.script.full | length > 0 %} - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m str -i {{ HAPROXY_WAF.user_agents.script.full | ensure_list | join(' ') }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m str -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.bad_bot_full }} } {% endif %} {% if HAPROXY_WAF.user_agents.script.sub | length > 0 %} - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m sub -i {{ HAPROXY_WAF.user_agents.script.sub | ensure_list | join(' ') }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m sub -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.bad_bot_sub }} } {% endif %} {% endif %} {% if cnf.security.block_bad_crawler_bots | bool %} # block well-known bad-crawler-bots {% if HAPROXY_WAF.user_agents.bad_crawlers.full | length > 0 %} - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m str -i {{ HAPROXY_WAF.user_agents.bad_crawlers.full | ensure_list | join(' ') }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m str -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.crawler_full }} } {% endif %} {% if HAPROXY_WAF.user_agents.bad_crawlers.sub | length > 0 %} - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m sub -i {{ HAPROXY_WAF.user_agents.bad_crawlers.sub | ensure_list | join(' ') }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { req.fhdr(User-Agent) -m sub -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.crawler_sub}} } {% endif %} {% endif %} {% if cnf.security.block_script_kiddies | bool %} # block script-kiddy requests - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path_beg -i {{ HAPROXY_WAF.script_kiddy.path_beg | join_w_excludes(HAPROXY_WAF.script_kiddy.excludes) }} } - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path_end -i {{ HAPROXY_WAF.script_kiddy.path_end | join_w_excludes(HAPROXY_WAF.script_kiddy.excludes) }} } - http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path_sub -i {{ HAPROXY_WAF.script_kiddy.path_sub | join_w_excludes(HAPROXY_WAF.script_kiddy.excludes) }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path -m beg -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.script_kiddy_beg }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path -m end -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.script_kiddy_end }} } + http-request deny status {{ HAPROXY_WAF.block_code }} {{ BLOCK_ERRORFILE }} if { path -m sub -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.script_kiddy_sub }} } {% endif %} {% if cnf.security.flag_bots | bool %} # FLAG BOTS ## flag bots by common user-agent substrings - http-request set-var(txn.bot) int(1) if !{ var(txn.bot) -m found } { req.fhdr(User-Agent) -m sub -i {{ HAPROXY_WAF.user_agents.any | ensure_list | join(' ') }} } + http-request set-var(txn.bot) int(1) if !{ var(txn.bot) -m found } { req.fhdr(User-Agent) -m sub -i -f {{ HAPROXY_HC.path.lst }}/{{ HAPROXY_HC.file.lst.bot_sub }} } {% if not cnf.security.block_script_bots | bool %} ## flag well-known script-bots diff --git a/templates/etc/haproxy/lst/waf-badbot-ua-full.lst.j2 b/templates/etc/haproxy/lst/waf-badbot-ua-full.lst.j2 new file mode 100644 index 0000000..7b7c24d --- /dev/null +++ b/templates/etc/haproxy/lst/waf-badbot-ua-full.lst.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for ua in HAPROXY_WAF.user_agents.script.full %} +{{ ua }} +{% endfor %} diff --git a/templates/etc/haproxy/lst/waf-badbot-ua-sub.lst.j2 b/templates/etc/haproxy/lst/waf-badbot-ua-sub.lst.j2 new file mode 100644 index 0000000..65aac87 --- /dev/null +++ b/templates/etc/haproxy/lst/waf-badbot-ua-sub.lst.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for ua in HAPROXY_WAF.user_agents.script.sub %} +{{ ua }} +{% endfor %} diff --git a/templates/etc/haproxy/lst/waf-bot-ua-sub.lst.j2 b/templates/etc/haproxy/lst/waf-bot-ua-sub.lst.j2 new file mode 100644 index 0000000..67e6b0c --- /dev/null +++ b/templates/etc/haproxy/lst/waf-bot-ua-sub.lst.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for ua in HAPROXY_WAF.user_agents.any %} +{{ ua }} +{% endfor %} diff --git a/templates/etc/haproxy/lst/waf-crawler-ua-full.lst.j2 b/templates/etc/haproxy/lst/waf-crawler-ua-full.lst.j2 new file mode 100644 index 0000000..8f33c68 --- /dev/null +++ b/templates/etc/haproxy/lst/waf-crawler-ua-full.lst.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for ua in HAPROXY_WAF.user_agents.bad_crawlers.full %} +{{ ua }} +{% endfor %} diff --git a/templates/etc/haproxy/lst/waf-crawler-ua-sub.lst.j2 b/templates/etc/haproxy/lst/waf-crawler-ua-sub.lst.j2 new file mode 100644 index 0000000..be5bd3a --- /dev/null +++ b/templates/etc/haproxy/lst/waf-crawler-ua-sub.lst.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +{% for ua in HAPROXY_WAF.user_agents.bad_crawlers.sub %} +{{ ua }} +{% endfor %} diff --git a/templates/etc/haproxy/lst/waf-script-kiddy-path-beg.lst.j2 b/templates/etc/haproxy/lst/waf-script-kiddy-path-beg.lst.j2 new file mode 100644 index 0000000..51c6075 --- /dev/null +++ b/templates/etc/haproxy/lst/waf-script-kiddy-path-beg.lst.j2 @@ -0,0 +1,8 @@ +# {{ ansible_managed }} + +{% for path in HAPROXY_WAF.script_kiddy.path_beg %} +{% if path not in HAPROXY_WAF.script_kiddy.excludes %} +{{ path | lower }} +{% endif %} +{% endfor %} + diff --git a/templates/etc/haproxy/lst/waf-script-kiddy-path-end.lst.j2 b/templates/etc/haproxy/lst/waf-script-kiddy-path-end.lst.j2 new file mode 100644 index 0000000..7abd9da --- /dev/null +++ b/templates/etc/haproxy/lst/waf-script-kiddy-path-end.lst.j2 @@ -0,0 +1,8 @@ +# {{ ansible_managed }} + +{% for path in HAPROXY_WAF.script_kiddy.path_end %} +{% if path not in HAPROXY_WAF.script_kiddy.excludes %} +{{ path | lower }} +{% endif %} +{% endfor %} + diff --git a/templates/etc/haproxy/lst/waf-script-kiddy-path-sub.lst.j2 b/templates/etc/haproxy/lst/waf-script-kiddy-path-sub.lst.j2 new file mode 100644 index 0000000..dfe2b19 --- /dev/null +++ b/templates/etc/haproxy/lst/waf-script-kiddy-path-sub.lst.j2 @@ -0,0 +1,8 @@ +# {{ ansible_managed }} + +{% for path in HAPROXY_WAF.script_kiddy.path_sub %} +{% if path not in HAPROXY_WAF.script_kiddy.excludes %} +{{ path | lower }} +{% endif %} +{% endfor %} +