talas-group/04_INFRA_DEPLOIEMENT/Notes_Operations/waf_haproxy.txt
senke 66471934af Initial commit: Talas Group project management & documentation
Knowledge base of ~80+ markdown files across 14 domains (00-13),
Logseq graph, hardware design files (KiCAD), infrastructure configs,
and talas-wiki static site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:10:41 +02:00

268 lines
10 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

HAProxy SPOE doc:
https://www.haproxy.com/blog/extending-haproxy-with-the-stream-processing-offload-engine
https://github.com/haproxy/haproxy/blob/master/doc/SPOE.txt
Coraza tutorial :
https://coraza.io/connectors/coraza-spoa/
External Documentation (unreliable) :
https://www.alldiscoveries.com/installation-and-configuration-haproxy-v2-4-22-with-waf-coraza-spoa-on-ubuntu-server-22-04-lts/
Structure implémenté :
- srv1 (10.184.116.168) : host HAProxy & Coraza SPOA
- srv2 (10.184.116.20) : le serveur à protéger (ici apache2, config par défault)
Fichiers de configurations importants :
- /etc/haproxy/haproxy.cfg - HAProxy Main Configuration
- /etc/haproxy/coraza.cfg - HAProxy SPOE Configuration
- /etc/coraza-spoa/config.yml - Coraza SPOA Main Configuration
- /etc/coraza-spoa/coraza.conf - Coraza Engine Configuration
On srv1 side :
apt update && apt install -y haproxy git wget
git clone https://github.com/corazawaf/coraza-spoa.git
# install go to build coraza-spoa
wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz
sudo echo "export PATH=$PATH:/usr/local/go/bin" >> /root/.profile
source /root/.profile
# build coraza-spoa
cd coraza-spoa/
go run mage.go build
# create coraza user and group
addgroup --quiet --system coraza-spoa
adduser --quiet --system --ingroup coraza-spoa --no-create-home --home /nonexistent --disabled-password coraza-spoa
mkdir -p /etc/coraza-spoa
cp build/coraza-spoa /usr/bin/coraza-spoa
chmod 755 /usr/bin/coraza-spoa
cp example/coraza-spoa.yaml /etc/coraza-spoa/config.yaml
vim /etc/coraza-spoa/config.yaml
change : bind: 0.0.0.0:9000
for : bind: 127.0.0.1:9000
change : - name: sample_app
for : - name: haporxy_waf
wget https://raw.githubusercontent.com/corazawaf/coraza/main/coraza.conf-recommended -O /etc/coraza-spoa/coraza.conf
vim /etc/coraza-spoa/coraza.conf
change : SecRuleEngine DetectionOnly
for : SecRuleEngine On
mkdir coraza-crs
cd coraza-crs
git clone https://github.com/coreruleset/coreruleset
cp coreruleset/crs-setup.conf.example /etc/coraza-spoa/crs-setup.conf
cp -R coreruleset/rules /etc/coraza-spoa
cp -R coreruleset/plugins /etc/coraza-spoa
# update files permissions
chown -R coraza-spoa:coraza-spoa /etc/coraza-spoa/
chmod 755 /etc/coraza-spoa
chmod -R 600 /etc/coraza-spoa/*
chmod 700 /etc/coraza-spoa/rules
chmod 700 /etc/coraza-spoa/plugins
cd ..
cp contrib/coraza-spoa.service /lib/systemd/system/coraza-spoa.service
systemctl daemon-reload
systemctl enable coraza-spoa.service
cp example/haproxy/coraza.cfg /etc/haproxy/coraza.cfg
cp example/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg
vim /etc/haproxy/haproxy.cfg
change : http-request set-var(txn.coraza.app) str(sample_app)
for : http-request set-var(txn.coraza.app) str(haproxy_waf)
change : filter spoe engine coraza config /usr/local/etc/haproxy/coraza.cfg
for : filter spoe engine coraza config /etc/haproxy/coraza.cfg
change : server backend $BACKEND_HOST
for : server backend 10.184.116.20:80 check
chnage : server coraza_spoa coraza-spoa:9000
for : server coraza_spoa 127.0.0.1:9000
systemctl restart haproxy
systemctl restart coraza-spoa
On srv2 side :
apt update && apt install -y apache2
systemctl status apache2
Check installation :
curl -I http://10.184.116.168/index.php?f=/etc/passwd
should return :
HTTP/1.1 403 Forbidden
waf-block: request
content-length: 0
((========================================================================================================================================================================================))
((=========================================================================================LARSEN=========================================================================================))
((========================================================================================================================================================================================))
Optimisations possibles:*
Regarder quelle règles sont utilisé souvent :
journalctl -u coraza-spoa --no-pager | grep "id"
dans /etc/coraza-spoa/coraza.conf enlver les regle non essentielles:
par exemple SecRuleRemoveById 920350
Ou si incertain mettre la règle en mode DetectionOnly:
SecRuleUpdateActionById 920350 "phase:2,log,pass"
appliquer coraza uniquement sur les requêtes critiques:
acl is_sensitive path_beg /admin /api /login /cart
filter spoe engine coraza config /etc/haproxy/coraza.cfg if is_sensitive
utiliser le mode détection pour les pages non sensibles:
dans /etc/coraza-spoa/coraza.conf :
SecRuleEngine On
SecRule REQUEST_URI "@beginsWith /public" "id:1000,phase:1,pass"
dans /etc/coraza-spoa/config.yaml on peut gerer l'allocation de ram et cpu:
memory_limit: 256m
worker_threads: 4
dans /etc/coraza-spoa/coraza.conf on peut limiter les logs aux req bloquées :
SecAuditEngine RelevantOnly
ou exclure les log tr
On peut égallementy utiliser la directive SecAuditLogParts pour définir le logging de coraza (internez à modsecurity) :
A: Audit log header (mandatory).
B: Request headers.
C: Request body (present only if the request body exists and Coraza is configured to intercept it. This would require SecRequestBodyAccess to be set to on).
D: Reserved for intermediary response headers; not implemented yet.
E: Intermediary response body (present only if Coraza is configured to intercept response bodies, and if the audit log engine is configured to record it. Intercepting response bodies requires SecResponseBodyAccess to be enabled). Intermediary response body is the same as the actual response body unless Coraza intercepts the intermediary response body, in which case the actual response body will contain the error message.
F: Final response headers.
G: Reserved for the actual response body; not implemented yet.
H: Audit log trailer.
I: This part is a replacement for part C. It will log the same data as C in all cases except when multipart/form-data encoding in used. In this case, it will log a fake application/x-www-form-urlencoded body that contains the information about parameters but not about the files. This is handy if you dont want to have (often large) files stored in your audit logs.
J: This part contains information about the files uploaded using multipart/form-data encoding.
K: This part contains a full list of every rule that matched (one per line) in the order they were matched. The rules are fully qualified and will thus show inherited actions and default operators.
Z: Final boundary, signifies the end of the entry (mandatory).
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/z-ciso.pem
mode http
option httplog
acl api_path path_beg /api/
use_backend backend_ciso if api_path
acl waf_trigger rand(100) lt 10
http-request set-var(txn.coraza.app) str(haproxy_waf)
filter spoe engine coraza config /etc/haproxy/coraza.cfg
http-request send-spoe-group coraza coraza-req if waf_trigger
http-request redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect }
http-response redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect }
http-request deny deny_status 403 hdr waf-block "request" if { var(txn.coraza.action) -m str deny }
http-response deny deny_status 403 hdr waf-block "response" if { var(txn.coraza.action) -m str deny }
http-request silent-drop if { var(txn.coraza.action) -m str drop }
http-response silent-drop if { var(txn.coraza.action) -m str drop }
http-request deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 }
http-response deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 }
use_backend frontend_ciso
#default_backend frontend_ciso
backend coraza-spoa
mode tcp
server coraza_spoa 127.0.0.1:9000
acl skip_openvas_ipv4 src 10.184.116.20
acl skip_openvas_ipv6 src fd42:4cb:ac26:e6e1:216:3eff:fef6:99a8
http-request deny if skip_openvas_ipv4
http-request deny if skip_openvas_ipv6
185.14.128.171
2a03:a240:0:1dea::a2
# ansible local config
# documentation: https://docs.ansible.com/ansible/latest/reference_appendices/config.html#ansible-configuration-settings
[defaults]
retry_files_enabled = False
# this deactivate the network gather process to speed up the fact gathering process
gather_subset = hardware,!network,virtual,ohai,facter
remote_user = adm-nmilovanovic
roles_path = ~/git/cosium/IT/ansible/roles
host_key_checking = False
forks = 10
# use for playbook 'virtual_ip_proxy.yml' with host 'center_proxy_gen_3'
# default value: 104448
max_diff_size = 4177920000
display_args_to_stdout = True
# enable timestamps when I run a playbook : profile_tasks
# https://docs.ansible.com/ansible/latest/collections/ansible/posix/profile_tasks_callback.html
callbacks_enabled = ansible.posix.profile_tasks,ansible.posix.timer,ansible.posix.profile_roles,community.general.counter_enabled
stdout_callback=yaml
# Callback settings
# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/default_callback.html
show_task_path_on_failure = True # When a task fails, display the path to the file containing the failed task and the line number.
check_mode_markers = True # Toggle to control displaying markers when running in check mode.
show_custom_stats = True # This adds the custom stats set via the set_stats plugin to the play recap
display_failed_stderr = True # Toggle to control whether failed and unreachable tasks are displayed to STDERR (vs. STDOUT)
#result_format = yaml
#pretty_results = true
[ssh_connection]
pipelining = True
[privilege_escalation]
become = True
[inventory]
enable_plugins = constructed,ini
[persistent_connection]
connect_timeout = 100
command_timeout = 100
TASK [zabbix_agent : zabbix_agentd.conf src=zabbix_agentd.conf, dest={{ zabbix_path_config }}, backup=True] ********************************************************************************************************************************************************************************************************************************************************************************************************************************
Wednesday 21 May 2025 17:47:41 +0200 (0:00:00.034) 0:00:03.419 *********
Wednesday 21 May 2025 17:47:41 +0200 (0:00:00.034) 0:00:03.419 *********
ok: [pad-rp-1]