На этом же хосте, что держит shiro.su, перед бэкендом стоит Coraza — WAF с движком, совместимым по правилам с ModSecurity, и набором правил OWASP Core Rule Set (CRS). Интеграция с HAProxy идёт не через модуль, а через отдельный демон coraza-spoa, который общается с HAProxy по протоколу SPOP (Stream Processing Offload Protocol). Разберём, как это собрано и как работает.
Схема
HAProxy на каждый запрос отправляет данные (метод, путь, заголовки, тело) во внешний агент через SPOE-фильтр. Агент — это coraza-spoa, отдельный процесс, который прогоняет запрос через движок Coraza с правилами CRS и возвращает вердикт (allow/deny и anomaly score). HAProxy уже сам решает, блокировать запрос или пропускать, на основе переменных, которые выставил агент.
клиент → HAProxy (frontend) → [SPOE: coraza-spoa] → решение → backend (shiro-blog) / 403
Плюс такой схемы: WAF живёт в отдельном процессе с собственным непривилегированным пользователем и минимальными правами, а не встроен в worker HAProxy.
Демон coraza-spoa
Собран из исходников через mage (github.com/corazawaf/coraza-spoa, версия v0.7.2, Go 1.25):
version v0.7.2
go go1.25.7
dep github.com/corazawaf/coraza/v3 v3.7.0
dep github.com/corazawaf/coraza-coreruleset/v4 v4.25.0
Правила CRS вкомпилированы в бинарь как Go-модуль (coraza-coreruleset/v4) — отдельно тащить и обновлять файлы правил на сервере не нужно, они обновляются вместе с зависимостью при пересборке.
Собранный бинарь лежит в /usr/bin/coraza-spoa, конфиг — в /etc/coraza-spoa/config.yaml:
bind: 127.0.0.1:9000
log_level: info
log_file: /var/log/coraza-spoa/coraza-spoa.log
log_format: console
default_application: shiro
applications:
- name: shiro
directives: |
Include @coraza.conf-recommended
Include @crs-setup.conf.example
Include @owasp_crs/*.conf
SecRuleEngine On
response_check: false
transaction_ttl_ms: 60000
log_level: info
log_file: /var/log/coraza-spoa/shiro-app.log
log_format: console
name: shiro в applications — это тег приложения, на него ссылается HAProxy при отправке запроса (app=var(txn.coraza.app)), так можно на одном агенте держать разные наборы правил под разные сайты. SecRuleEngine On включает блокировку сразу; для обкатки на боевом трафике логичнее сперва подержать SecRuleEngine DetectionOnly и посмотреть на срабатывания в логе, прежде чем включать блокировку.
Systemd-сервис
Юнит не штатный из репозитория — в него руками добавлен ExecReload, без него systemctl reload не перечитывает конфиг:
[Service]
ExecStart=/usr/bin/coraza-spoa -config=/etc/coraza-spoa/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
User=coraza-spoa
Group=coraza-spoa
Restart=always
SIGHUP проверен вживую — конфиг перечитывается без разрыва уже открытых соединений.
Дальше — плотный sandboxing systemd: своя система/юзер без домашней папки, ProtectSystem=strict, PrivateUsers=true, MemoryDenyWriteExecute=yes, длинный список InaccessiblePaths (от /usr/bin/bash до /usr/bin/ssh), и главное —
RestrictAddressFamilies=AF_INET AF_INET6
IPAddressDeny=any
IPAddressAllow=localhost
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
то есть демону разрешено общаться только с localhost и запрещено вообще всё остальное сетевое взаимодействие. Даже если в самом WAF найдут RCE, вырваться наружу из этой песочницы или достучаться до внешней сети процессу будет некуда.
Конфигурация HAProxy
SPOE-конфиг (/etc/haproxy/coraza.cfg) — стандартный пример из репозитория coraza-spoa, без правок:
[coraza]
spoe-agent coraza-agent
groups coraza-req
option var-prefix coraza
option set-on-error error
timeout hello 2s
timeout idle 2m
timeout processing 500ms
use-backend coraza-spoa
log global
spoe-message coraza-req
args app=var(txn.coraza.app) src-ip=src src-port=src_port dst-ip=dst dst-port=dst_port method=method path=path query=query version=req.ver headers=req.hdrs body=req.body exportRuleIDs=bool(false)
spoe-group coraza-req
messages coraza-req
Важный нюанс: имя движка в haproxy.cfg (filter spoe engine coraza ...) — это имя секции [coraza] в этом файле, а не имя spoe-agent coraza-agent. Легко перепутать при чтении доки.
В самом haproxy.cfg, во frontend:
http-request set-var(txn.coraza.app) str(shiro)
filter spoe engine coraza config /etc/haproxy/coraza.cfg
http-request send-spoe-group coraza coraza-req
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 }
# fail-closed: SPOA недоступен/ошибся -> 500 вместо тихого пропуска.
# Для fail-open — удалить эти две строки.
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 }
и backend, который проксирует данные агенту (SPOP работает поверх обычного TCP-соединения):
backend coraza-spoa
mode tcp
option spop-check
server coraza_spoa 127.0.0.1:9000 check
Отдельно стоит решение про fail-closed: если агент недоступен или упал с ошибкой, HAProxy вернёт 500, а не молча пропустит запрос мимо WAF. Это осознанный компромисс — доступность сайта против гарантии, что необработанный агентом трафик не проскочит фильтрацию.
Как это выглядит в бою
Из /var/log/coraza-spoa/shiro-app.log — обычный фон сканеров, которые прощупывают любой сайт в интернете:
Coraza: Warning. Restricted File Access Attempt [id "930130"] [msg "Restricted File Access Attempt"]
[data "Matched Data: .env found within REQUEST_FILENAME: /.env"] [uri "/.env"]
Coraza: Warning. Found User-Agent associated with security scanner [id "913100"]
[data "Matched Data: Fuzz Faster found within REQUEST_HEADERS:user-agent: Fuzz Faster U Fool v2.1.0-dev"]
Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 10) [id "949110"]
[uri "/wp-config.php"]
Каждое отдельное правило CRS просто добавляет очки в общий anomaly score запроса; финальное решение принимает правило 949110 (REQUEST-949-BLOCKING-EVALUATION) — если суммарный score выше порога, запрос помечается на блокировку, и уже тогда HAProxy получает var(txn.coraza.action) = deny и отдаёт 403. Это стандартная модель CRS — не бинарные "правило сработало → блок", а накопление веса по нескольким сигналам.
Как воспроизвести тестовый стенд у себя
Прежде чем катить такую связку в прод, её стоит собрать и прогнать отдельно — вслепую редактировать боевой HAProxy ради проверки синтаксиса SPOE не лучшая идея. Ниже — пошаговая инструкция стенда, которым я валидировал именно эту схему (HAProxy + coraza-spoa v0.7.2 + OWASP CRS) на живом трафике, прежде чем переносить её в прод. Все команды и результаты ниже реально выполнялись, а не собраны по памяти из документации.
1. Подготовка ОС
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl build-essential gcc socat jq
build-essential и gcc обязательны: сборка coraza-spoa v0.7.2 идёт с CGO_ENABLED=1 (это видно по выводу -version собранного бинаря), чистая кросс-компиляция с CGO_ENABLED=0 не проверялась.
2. HAProxy
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:vbernat/haproxy-3.2
sudo apt update
sudo apt install -y haproxy
haproxy -v
Стенд проверялся на HAProxy 3.4.0 из этой PPA; сайт работает на 3.2.23 из той же PPA — SPOE стабильна в обеих версиях, разницы в поведении не было.
3. Go и сборка coraza-spoa
go.mod репозитория требует Go 1.25.7 — стокового apt-пакета (обычно куда более старая версия) для этого недостаточно, ставим тарболл с golang.org:
export GOVER=1.25.7
curl -LO https://go.dev/dl/go${GOVER}.linux-amd64.tar.gz
sudo mv /usr/local/go /usr/local/go.bak
sudo tar -C /usr/local -xzf go${GOVER}.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee /etc/profile.d/golang.sh
source /etc/profile.d/golang.sh
go version
Сборка через штатный magefile проекта:
git clone --branch v0.7.2 https://github.com/corazawaf/coraza-spoa.git /opt/coraza-spoa
cd /opt/coraza-spoa
go run mage.go build
./build/coraza-spoa -version
Команда собирает build/coraza-spoa (~27 МБ); вывод -version печатает v0.7.2 и полный список зависимостей, среди них coraza-coreruleset/v4 v4.25.0 — OWASP CRS не тянется отдельным git-клоном, а зашивается в бинарь как обычная Go-зависимость на этапе компиляции. Захотите более свежий CRS — поднимите версию этого модуля в go.mod и пересоберите той же командой.
sudo install -m 0755 build/coraza-spoa /usr/bin/coraza-spoa
4. Конфиг coraza-spoa — начинаем с DetectionOnly
Та же схема config.yaml, что и в проде (см. выше), но с одним отличием на время обкатки:
SecRuleEngine DetectionOnly
В этом режиме атаки логируются с полным разбором сработавших правил CRS и итоговым anomaly score, но ничего не блокируется — самое время посмотреть, что происходит на легитимном трафике, прежде чем включать блокировку. Валидация конфига без запуска сервиса:
sudo -u coraza-spoa /usr/bin/coraza-spoa -config /etc/coraza-spoa/config.yaml -validate
5. systemd-unit, пользователь, логи
Пользователь и каталоги логов на реальном пакете создаются postinst-скриптом; при ручной установке — то же самое руками:
sudo addgroup --quiet --system coraza-spoa
sudo adduser --quiet --system --ingroup coraza-spoa --no-create-home --home /nonexistent --disabled-password coraza-spoa
sudo mkdir -p /var/log/coraza-spoa
sudo chown -R coraza-spoa:adm /var/log/coraza-spoa
sudo chmod 755 /var/log/coraza-spoa
sudo mkdir -p /etc/coraza-spoa
sudo cp coraza-spoa.yaml /etc/coraza-spoa/config.yaml
sudo chown -R coraza-spoa:coraza-spoa /etc/coraza-spoa
sudo cp /opt/coraza-spoa/contrib/coraza-spoa.service /etc/systemd/system/
sudo cp /opt/coraza-spoa/contrib/coraza-spoa.logrotate /etc/logrotate.d/coraza-spoa
Не забудьте про ExecReload из раздела про systemd-сервис выше — в штатном unit-файле его нет, а без него systemctl reload не работает, только полный restart.
sudo systemctl daemon-reload
sudo systemctl enable --now coraza-spoa
sudo systemctl status coraza-spoa
6–7. SPOE-конфиг и haproxy.cfg
Копируем coraza.cfg как есть (раздел выше), в haproxy.cfg прописываем frontend с filter spoe engine и backend, указывающий на реальный адрес защищаемого сайта. Если ваш бэкенд разруливает виртуальные хосты по заголовку Host — не забудьте добавить http-request set-header Host ваш-домен, иначе получите не тот сайт или редирект.
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl restart haproxy
sudo systemctl status haproxy coraza-spoa
8. Функциональные тесты — с реальными результатами
Легитимный запрос — обычный 200 и HTML главной страницы:
curl -si http://<адрес-стенда>/
Необработанные кавычки и угловые скобки в URL curl иногда отклоняет как "Malformed input to a URL function" ещё до отправки запроса — используйте percent-encoding:
# SQLi
curl -si "http://<адрес-стенда>/?id=1%27%20OR%20%271%27%3D%271"
# XSS
curl -si "http://<адрес-стенда>/?q=%3Cscript%3Ealert(1)%3C/script%3E"
В DetectionOnly оба запроса вернули 200, но в лог приложения реально записались сработки CRS:
Coraza: Warning. SQL Injection Attack Detected via libinjection
[file "@owasp_crs/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [id "942100"]
[data "Matched Data: s&sos found within ARGS:id: 1' OR '1'='1"] [severity "critical"]
Coraza: Warning. Inbound Anomaly Score Exceeded (Total Score: 5)
[file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [id "949110"]
9. Переключение в blocking и живой reload
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/coraza-spoa/config.yaml
sudo systemctl reload coraza-spoa
Перечитывание по SIGHUP проходит без разрыва уже установленных соединений и без простоя сервиса — в отличие от полного restart. Те же тестовые запросы после этого вернули:
HTTP/1.1 403 Forbidden
waf-block: request
а в access-логе HAProxy — строка с реальными значениями переменных:
... "GET /?q=%3Cscript%3Ealert(1)%3C/script%3E HTTP/1.1" txn:QVWBOMOAKYDMDCKC spoa-error:- waf-status:403 score:20 rules_hit:5
10. Проверка fail-open/fail-closed
sudo systemctl stop coraza-spoa
curl -si http://<адрес-стенда>/
sudo systemctl start coraza-spoa
С блокирующими правилами по var(txn.coraza.error) из конфига выше это честный fail-closed: при остановленном coraza-spoa HAProxy отвечает 500 на все запросы, включая легитимные. Нужен fail-open — уберите обе строки http-request/http-response deny по error из haproxy.cfg.
11. Про замер латентности
ab -n 1000 -c 20 http://<адрес-стенда>/
Если гоняете стенд через VPN/туннель до защищаемого сайта — цифры будут врать: сетевой джиттер такого канала (сотни миллисекунд между отдельными запросами) полностью маскирует добавленную WAF-латентность. Валидный замер p50/p99 нужно делать там же, где будет жить прод — с HAProxy, coraza-spoa и бэкендом на одной машине или в одном сегменте сети.
Итог
Получилась разделённая по процессам и правам схема: HAProxy занимается только терминацией TLS и маршрутизацией, WAF — отдельный демон с урезанным systemd-сэндбоксом и доступом только к localhost, а правила CRS обновляются как обычная Go-зависимость при пересборке coraza-spoa. Из минусов — SPOE добавляет сетевой прыжок на каждый запрос (localhost, но всё же), и его конфигурация неочевидна с первого взгляда: три разных файла (systemd-юнит, coraza.cfg, config.yaml) должны договориться об именах (coraza / coraza-agent / shiro), и легко перепутать, что где называется.
Приложение: полные конфиги
Выше — только фрагменты по делу, здесь всё целиком одним куском, для копипасты.
/etc/coraza-spoa/config.yaml
bind: 127.0.0.1:9000
log_level: info
log_file: /var/log/coraza-spoa/coraza-spoa.log
log_format: console
default_application: shiro
applications:
- name: shiro
directives: |
Include @coraza.conf-recommended
Include @crs-setup.conf.example
Include @owasp_crs/*.conf
SecRuleEngine On
response_check: false
transaction_ttl_ms: 60000
log_level: info
log_file: /var/log/coraza-spoa/shiro-app.log
log_format: console
/etc/haproxy/coraza.cfg (SPOE)
[coraza]
spoe-agent coraza-agent
groups coraza-req
option var-prefix coraza
option set-on-error error
timeout hello 2s
timeout idle 2m
timeout processing 500ms
use-backend coraza-spoa
log global
spoe-message coraza-req
args app=var(txn.coraza.app) src-ip=src src-port=src_port dst-ip=dst dst-port=dst_port method=method path=path query=query version=req.ver headers=req.hdrs body=req.body exportRuleIDs=bool(false)
spoe-group coraza-req
messages coraza-req
/etc/haproxy/haproxy.cfg — минимальный рабочий пример
global
log /dev/log local0
maxconn 4096
defaults
mode http
log global
option httplog
timeout connect 5s
timeout client 30s
timeout server 30s
frontend fe_shiro
bind *:80
log-format "%ci:%cp\ [%t]\ %ft\ %b/%s\ %Tt\ %ST\ %B\ %{+Q}r\ txn:%[var(txn.coraza.id)]\ spoa-error:%[var(txn.coraza.error)]\ waf-status:%[var(txn.coraza.status)]\ score:%[var(txn.coraza.anomaly_score)]\ rules_hit:%[var(txn.coraza.rules_hit)]"
http-request set-var(txn.coraza.app) str(shiro)
http-request set-header Host shiro.su
filter spoe engine coraza config /etc/haproxy/coraza.cfg
http-request send-spoe-group coraza coraza-req
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 }
# fail-closed: SPOA недоступен/ошибся -> 500 вместо тихого пропуска.
# Для fail-open — удали эти две строки.
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 }
default_backend be_shiro
backend be_shiro
server shiro_origin 127.0.0.1:8080 check
backend coraza-spoa
mode tcp
option spop-check
server coraza_spoa 127.0.0.1:9000 check
/etc/systemd/system/coraza-spoa.service — полный hardened unit
[Unit]
Description=Coraza WAF SPOA Daemon
Documentation=https://www.coraza.io
[Service]
ExecStart=/usr/bin/coraza-spoa -config=/etc/coraza-spoa/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
WorkingDirectory=/
Restart=always
Type=exec
User=coraza-spoa
Group=coraza-spoa
# Hardening
AmbientCapabilities=
MountFlags=private
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectProc=noaccess
ProtectClock=yes
ProtectHostname=yes
ProtectSystem=strict
RestrictSUIDSGID=true
RestrictRealtime=true
SecureBits=no-setuid-fixup-locked noroot-locked
TemporaryFileSystem=/etc
TemporaryFileSystem=/var
BindReadOnlyPaths=-/etc/ca-certificates
BindReadOnlyPaths=-/etc/crypto-policies
BindReadOnlyPaths=-/etc/fdns
BindReadOnlyPaths=-/etc/ld.so.cache
BindReadOnlyPaths=-/etc/ld.so.preload
BindReadOnlyPaths=-/etc/localtime
BindReadOnlyPaths=-/etc/nsswitch.conf
BindReadOnlyPaths=-/etc/passwd
BindReadOnlyPaths=-/etc/pki
BindReadOnlyPaths=-/etc/ssl
BindReadOnlyPaths=-/etc/coraza-spoa
BindPaths=-/var/log/coraza-spoa
InaccessiblePaths=-/opt
InaccessiblePaths=-/srv
#InaccessiblePaths=-/bin
InaccessiblePaths=-/bin/bash
inaccessiblepaths=-/bin/find
InaccessiblePaths=-/bin/less
InaccessiblePaths=-/bin/zcat
InaccessiblePaths=-/bin/rm
InaccessiblePaths=-/bin/readlink
InaccessiblePaths=-/bin/readpath
InaccessiblePaths=-/sbin
InaccessiblePaths=-/efi
InaccessiblePaths=-/run/media
InaccessiblePaths=-/run/mount
InaccessiblePaths=-/efi
InaccessiblePaths=-/boot
InaccessiblePaths=-/dev/kmsg
InaccessiblePaths=-/dev/port
InaccessiblePaths=-/lib/modules
InaccessiblePaths=-/lost+found
InaccessiblePaths=-/proc/bus
InaccessiblePaths=-/proc/config.gz
InaccessiblePaths=-/usr/bin/alsaloop
InaccessiblePaths=-/usr/bin/alsamixer
InaccessiblePaths=-/usr/bin/alsatplg
InaccessiblePaths=-/usr/bin/alsaucm
InaccessiblePaths=-/usr/bin/alsaunmute
InaccessiblePaths=-/usr/bin/attr
InaccessiblePaths=-/usr/bin/balooctl
InaccessiblePaths=-/usr/bin/bash
InaccessiblePaths=-/usr/bin/bootctl
InaccessiblePaths=-/usr/bin/busctl
InaccessiblePaths=-/usr/bin/chacl
InaccessiblePaths=-/usr/bin/chattr
InaccessiblePaths=-/usr/bin/cmp
InaccessiblePaths=-/usr/bin/coredumpctl
InaccessiblePaths=-/usr/bin/crontab
InaccessiblePaths=-/usr/bin/csh
InaccessiblePaths=-/usr/bin/dash
InaccessiblePaths=-/usr/bin/dd
InaccessiblePaths=-/usr/bin/df
InaccessiblePaths=-/usr/bin/diff
InaccessiblePaths=-/usr/bin/diff3
InaccessiblePaths=-/usr/bin/dmesg
InaccessiblePaths=-/usr/bin/dnf
InaccessiblePaths=-/usr/bin/dotty
InaccessiblePaths=-/usr/bin/dracut
InaccessiblePaths=-/usr/bin/evmctl
InaccessiblePaths=-/usr/bin/free
InaccessiblePaths=-/usr/bin/ftp
InaccessiblePaths=-/usr/bin/getfacl
InaccessiblePaths=-/usr/bin/getfattr
InaccessiblePaths=-/usr/bin/grotty
InaccessiblePaths=-/usr/bin/grub2-file
InaccessiblePaths=-/usr/bin/grub2-menulst2cfg
InaccessiblePaths=-/usr/bin/grub2-mkimage
InaccessiblePaths=-/usr/bin/grub2-mkrelpath
InaccessiblePaths=-/usr/bin/grub2-render-label
InaccessiblePaths=-/usr/bin/grub2-script-check
InaccessiblePaths=-/usr/bin/hostnamectl
InaccessiblePaths=-/usr/bin/htop
InaccessiblePaths=-/usr/bin/ipcmk
InaccessiblePaths=-/usr/bin/journalctl
InaccessiblePaths=-/usr/bin/keyctl
InaccessiblePaths=-/usr/bin/kill
InaccessiblePaths=-/usr/bin/killall
InaccessiblePaths=-/usr/bin/ksh
InaccessiblePaths=-/usr/bin/last
InaccessiblePaths=-/usr/bin/localectl
InaccessiblePaths=-/usr/bin/locate
InaccessiblePaths=-/usr/bin/loginctl
InaccessiblePaths=-/usr/bin/ls
InaccessiblePaths=-/usr/bin/lsattr
InaccessiblePaths=-/usr/bin/lsb_release
InaccessiblePaths=-/usr/bin/lsblk
InaccessiblePaths=-/usr/bin/lscpu
InaccessiblePaths=-/usr/bin/lsdiff
InaccessiblePaths=-/usr/bin/lsinitrd
InaccessiblePaths=-/usr/bin/lsipc
InaccessiblePaths=-/usr/bin/lslocks
InaccessiblePaths=-/usr/bin/lslogins
InaccessiblePaths=-/usr/bin/lsmem
InaccessiblePaths=-/usr/bin/lsns
InaccessiblePaths=-/usr/bin/lsof
InaccessiblePaths=-/usr/bin/lsscsi
InaccessiblePaths=-/usr/bin/lsusb
InaccessiblePaths=-/usr/bin/lua
InaccessiblePaths=-/usr/bin/lynis
InaccessiblePaths=-/usr/bin/mail
InaccessiblePaths=-/usr/bin/mkfifo
InaccessiblePaths=-/usr/bin/mkinitrd
InaccessiblePaths=-/usr/bin/mkisofs
InaccessiblePaths=-/usr/bin/mknod
InaccessiblePaths=-/usr/bin/mount
InaccessiblePaths=-/usr/bin/mountpoint
InaccessiblePaths=-/usr/bin/nc
InaccessiblePaths=-/usr/bin/netcap
InaccessiblePaths=-/usr/bin/netstat
InaccessiblePaths=-/usr/bin/netstat-nat
InaccessiblePaths=-/usr/bin/networkctl
InaccessiblePaths=-/usr/bin/nmap
InaccessiblePaths=-/usr/bin/nping
InaccessiblePaths=-/usr/bin/nsenter
InaccessiblePaths=-/usr/bin/pactl
InaccessiblePaths=-/usr/bin/panelctl
InaccessiblePaths=-/usr/bin/passwd
InaccessiblePaths=-/usr/bin/peekfd
InaccessiblePaths=-/usr/bin/pgrep
InaccessiblePaths=-/usr/bin/pidof
InaccessiblePaths=-/usr/bin/ping
InaccessiblePaths=-/usr/bin/pkill
InaccessiblePaths=-/usr/bin/pkttyagent
InaccessiblePaths=-/usr/bin/pmap
InaccessiblePaths=-/usr/bin/portablectl
InaccessiblePaths=-/usr/bin/prtstat
InaccessiblePaths=-/usr/bin/ps
InaccessiblePaths=-/usr/bin/pslog
InaccessiblePaths=-/usr/bin/pstree
InaccessiblePaths=-/usr/bin/pstree.x11
InaccessiblePaths=-/usr/bin/pulseaudio
InaccessiblePaths=-/usr/bin/pwdx
InaccessiblePaths=-/usr/bin/python
InaccessiblePaths=-/usr/bin/python2
InaccessiblePaths=-/usr/bin/python3
InaccessiblePaths=-/usr/bin/python3.9
InaccessiblePaths=-/usr/bin/resolvectl
InaccessiblePaths=-/usr/bin/rkhunter
InaccessiblePaths=-/usr/bin/rpm
InaccessiblePaths=-/usr/bin/rsync
InaccessiblePaths=-/usr/bin/ruby
InaccessiblePaths=-/usr/bin/run-parts
InaccessiblePaths=-/usr/bin/scp
InaccessiblePaths=-/usr/bin/screen
InaccessiblePaths=-/usr/bin/sdiff
InaccessiblePaths=-/usr/bin/setarch
InaccessiblePaths=-/usr/bin/setcifsacl
InaccessiblePaths=-/usr/bin/setfacl
InaccessiblePaths=-/usr/bin/setfattr
InaccessiblePaths=-/usr/bin/setpriv
InaccessiblePaths=-/usr/bin/setsid
InaccessiblePaths=-/usr/bin/setterm
InaccessiblePaths=-/usr/bin/setxkbmap
InaccessiblePaths=-/usr/bin/sftp
InaccessiblePaths=-/usr/bin/sh
InaccessiblePaths=-/usr/bin/skill
InaccessiblePaths=-/usr/bin/slabtop
InaccessiblePaths=-/usr/bin/snice
InaccessiblePaths=-/usr/bin/ssh
InaccessiblePaths=-/usr/bin/ssh-add
InaccessiblePaths=-/usr/bin/ssh-agent
InaccessiblePaths=-/usr/bin/ssh-copy-id
InaccessiblePaths=-/usr/bin/ssh-keyscan
InaccessiblePaths=-/usr/bin/strace
InaccessiblePaths=-/usr/bin/strace-log-merg
InaccessiblePaths=-/usr/bin/strings
InaccessiblePaths=-/usr/bin/stty
InaccessiblePaths=-/usr/bin/su
InaccessiblePaths=-/usr/bin/sudo
InaccessiblePaths=-/usr/bin/systemctl
InaccessiblePaths=-/usr/bin/systemd-tty-ask-password-agent
InaccessiblePaths=-/usr/bin/tcl
InaccessiblePaths=-/usr/bin/tcptraceroute
InaccessiblePaths=-/usr/bin/tcsh
InaccessiblePaths=-/usr/bin/telnet
InaccessiblePaths=-/usr/bin/timedatectl
InaccessiblePaths=-/usr/bin/tload
InaccessiblePaths=-/usr/bin/tmux
InaccessiblePaths=-/usr/bin/top
InaccessiblePaths=-/usr/bin/touch
InaccessiblePaths=-/usr/bin/tracepath
InaccessiblePaths=-/usr/bin/traceroute
InaccessiblePaths=-/usr/bin/traceroute6
InaccessiblePaths=-/usr/bin/tricklectl
InaccessiblePaths=-/usr/bin/tty
InaccessiblePaths=-/usr/bin/udevadm
InaccessiblePaths=-/usr/bin/udisksctl
InaccessiblePaths=-/usr/bin/umount
InaccessiblePaths=-/usr/bin/uname
InaccessiblePaths=-/usr/bin/unlink
InaccessiblePaths=-/usr/bin/updatedb
InaccessiblePaths=-/usr/bin/uptime
InaccessiblePaths=-/usr/bin/users
InaccessiblePaths=-/usr/bin/vi
InaccessiblePaths=-/usr/bin/vim
InaccessiblePaths=-/usr/bin/vim.nox
InaccessiblePaths=-/usr/bin/vim.tiny
InaccessiblePaths=-/usr/bin/vimtutor
InaccessiblePaths=-/usr/bin/vmware-checkvm
InaccessiblePaths=-/usr/bin/vmware-namespace-cmd
InaccessiblePaths=-/usr/bin/vmware-rpctool
InaccessiblePaths=-/usr/bin/vmware-toolbox-cmd
InaccessiblePaths=-/usr/bin/vmware-xferlogs
InaccessiblePaths=-/usr/bin/w
InaccessiblePaths=-/usr/bin/wall
InaccessiblePaths=-/usr/bin/watch
InaccessiblePaths=-/usr/bin/wdctl
InaccessiblePaths=-/usr/bin/wg
InaccessiblePaths=-/usr/bin/wget
InaccessiblePaths=-/usr/bin/who
InaccessiblePaths=-/usr/bin/whoami
InaccessiblePaths=-/usr/bin/zsh
InaccessiblePaths=-/usr/local
InaccessiblePaths=-/usr/sbin
InaccessiblePaths=-/proc/irq
InaccessiblePaths=-/proc/kallsyms
InaccessiblePaths=-/proc/kcore
InaccessiblePaths=-/proc/kmem
#*InaccessiblePaths=-/proc/kmsg
InaccessiblePaths=-/proc/mem
InaccessiblePaths=-/proc/sched_debug
InaccessiblePaths=-/proc/sys/efi/vars
InaccessiblePaths=-/proc/sys/fs/binfmt_misc
#*InaccessiblePaths=-/proc/sys/kernel/core_pattern
InaccessiblePaths=-/proc/sys/kernel/hotplug
#*InaccessiblePaths=-/proc/sys/kernel/modprobe
InaccessiblePaths=-/proc/sys/security
#*InaccessiblePaths=-/proc/sys/vm/panic_on_oom
InaccessiblePaths=-/proc/sysrq-trigger
InaccessiblePaths=-/proc/timer_list
InaccessiblePaths=-/proc/timer_stats
InaccessiblePaths=-/selinux
InaccessiblePaths=-/sys/firmware
InaccessiblePaths=-/sys/fs
InaccessiblePaths=-/sys/hypervisor
InaccessiblePaths=-/sys/kernel/debug
InaccessiblePaths=-/sys/kernel/uevent_helper
InaccessiblePaths=-/sys/kernel/vmcoreinfo
InaccessiblePaths=-/sys/module
InaccessiblePaths=-/sys/power
#*InaccessiblePaths=-/usr/lib/debug
InaccessiblePaths=-/usr/src/linux
LockPersonality=true
LogsDirectory=coraza-spoa
ConfigurationDirectory=coraza-spoa
# coraza-geoip
ReadOnlyPaths=/usr/share/GeoIP
#ReadOnlyPaths=/proc
MemoryDenyWriteExecute=yes
NoNewPrivileges=true
ProtectHome=true
PrivateDevices=true
PrivateUsers=true
PrivateTmp=true
RemoveIPC=true
RestrictAddressFamilies=AF_INET AF_INET6
#RestrictNamespaces=uts ipc pid user cgroup
SystemCallArchitectures=native
SystemCallFilter=@system-service -@setuid -@ipc -@mount
IPAddressDeny=any
IPAddressAllow=localhost
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target