feat(security): fail2ban для HAProxy JSON reject и Zabbix 403.
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
## После UFW (опционально, не блокер)
|
## После UFW (опционально, не блокер)
|
||||||
|
|
||||||
- [ ] fail2ban sshd
|
- [ ] fail2ban sshd
|
||||||
|
- [ ] fail2ban haproxy-reject + haproxy-zabbix-403 (`scripts/security/fail2ban-install.sh`)
|
||||||
- [ ] `unattended-upgrades`
|
- [ ] `unattended-upgrades`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ default outgoing: allow
|
|||||||
|
|
||||||
`192.168.128.0/17` покрывает офисную/VPN-подсеть (в т.ч. `192.168.160.0/24`).
|
`192.168.128.0/17` покрывает офисную/VPN-подсеть (в т.ч. `192.168.160.0/24`).
|
||||||
|
|
||||||
|
Ответный трафик с бэкендов (`.42`, `.129` → высокие порты на `.117`):
|
||||||
|
|
||||||
|
```
|
||||||
|
ufw allow from 192.168.160.0/24 comment 'LAN backends return'
|
||||||
|
```
|
||||||
|
|
||||||
|
(в [haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh) — `LAN_BACKENDS_CIDR`).
|
||||||
|
|
||||||
### Env
|
### Env
|
||||||
|
|
||||||
`/etc/haproxy/security/haproxy-ufw.env` (из [haproxy-ufw.env.example](../../scripts/security/haproxy-ufw.env.example)):
|
`/etc/haproxy/security/haproxy-ufw.env` (из [haproxy-ufw.env.example](../../scripts/security/haproxy-ufw.env.example)):
|
||||||
@@ -73,11 +81,31 @@ Inbound-почта с интернета идёт на **KSMG `.57`**, не на
|
|||||||
|------|--------|
|
|------|--------|
|
||||||
| SSH: ключи, `PermitRootLogin no`, `PasswordAuthentication no` | до/вместе с UFW |
|
| SSH: ключи, `PermitRootLogin no`, `PasswordAuthentication no` | до/вместе с UFW |
|
||||||
| fail2ban на sshd | подстраховка SSH |
|
| fail2ban на sshd | подстраховка SSH |
|
||||||
|
| fail2ban на HAProxy JSON | бан сканеров / reject SNI / Zabbix 403 |
|
||||||
| `unattended-upgrades` | патчи |
|
| `unattended-upgrades` | патчи |
|
||||||
| Не открывать лишние listen | `ss -tlnp` |
|
| Не открывать лишние listen | `ss -tlnp` |
|
||||||
|
|
||||||
Опционально позже: sysctl syncookies, rate-limit на frontend, allowlist для Exchange/sac-api.
|
Опционально позже: sysctl syncookies, rate-limit на frontend, allowlist для Exchange/sac-api.
|
||||||
|
|
||||||
|
## fail2ban (HAProxy JSON)
|
||||||
|
|
||||||
|
Скрипт: [fail2ban-install.sh](../../scripts/security/fail2ban-install.sh), конфиги в `scripts/security/fail2ban/`.
|
||||||
|
|
||||||
|
| Jail | Что ловит |
|
||||||
|
|------|-----------|
|
||||||
|
| `haproxy-reject` | JSON: `"be":"fe_https_sni","srv":"<NOSRV>"` — сканеры, чужой SNI, allowlist reject |
|
||||||
|
| `haproxy-zabbix-403` | httplog: `fe_http_zabbix` + `403` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install -y fail2ban
|
||||||
|
sudo bash ~/reverse-proxy/scripts/security/fail2ban-install.sh
|
||||||
|
sudo fail2ban-client status haproxy-reject
|
||||||
|
```
|
||||||
|
|
||||||
|
Порог: **15** reject за **10 мин** → бан **24 ч** через UFW. `ignoreip`: LAN, VPN, admin.
|
||||||
|
|
||||||
|
После `systemctl restart fail2ban` подождите 1–2 с перед `fail2ban-client status` (сокет).
|
||||||
|
|
||||||
### SSH (пример)
|
### SSH (пример)
|
||||||
|
|
||||||
`/etc/ssh/sshd_config.d/99-hardening.conf`:
|
`/etc/ssh/sshd_config.d/99-hardening.conf`:
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Установка fail2ban filter/jail для HAProxy JSON-лога.
|
||||||
|
# sudo bash fail2ban-install.sh
|
||||||
|
# sudo bash fail2ban-install.sh --test # только fail2ban-regex
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
F2B_DIR="$SCRIPT_DIR/fail2ban"
|
||||||
|
TEST_ONLY=0
|
||||||
|
|
||||||
|
die() { echo "error: $*" >&2; exit 1; }
|
||||||
|
log() { echo "[*] $*"; }
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--test) TEST_ONLY=1; shift ;;
|
||||||
|
-h | --help)
|
||||||
|
echo "Usage: fail2ban-install.sh [--test]"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*) die "unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -d $F2B_DIR ]] || die "missing $F2B_DIR"
|
||||||
|
|
||||||
|
command -v fail2ban-client >/dev/null 2>&1 || die "install fail2ban first (apt install fail2ban)"
|
||||||
|
|
||||||
|
install -m 0644 "$F2B_DIR/haproxy-json-reject.conf" /etc/fail2ban/filter.d/haproxy-json-reject.conf
|
||||||
|
install -m 0644 "$F2B_DIR/haproxy-zabbix-403.conf" /etc/fail2ban/filter.d/haproxy-zabbix-403.conf
|
||||||
|
install -m 0644 "$F2B_DIR/haproxy.local" /etc/fail2ban/jail.d/haproxy.local
|
||||||
|
|
||||||
|
log "fail2ban-regex haproxy-json-reject"
|
||||||
|
fail2ban-regex /var/log/haproxy.log /etc/fail2ban/filter.d/haproxy-json-reject.conf | tail -8
|
||||||
|
|
||||||
|
if [[ $TEST_ONLY -eq 1 ]]; then
|
||||||
|
log "test only — jail not restarted"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
fail2ban-client -t
|
||||||
|
systemctl enable fail2ban
|
||||||
|
systemctl restart fail2ban
|
||||||
|
sleep 1
|
||||||
|
fail2ban-client status
|
||||||
|
fail2ban-client status haproxy-reject
|
||||||
|
log "done"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Сканеры и reject на fe_https_sni (неизвестный SNI, allowlist, мусор)
|
||||||
|
failregex = ^.*haproxy\[(?:\d+|master)\]: \{"time":"[^"]*","client":"<HOST>".*"fe":"fe_https_sni","be":"fe_https_sni","srv":"<NOSRV>"
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = ^.*haproxy\[(?:\d+|master)\]: <HOST>:\d+ \[[^\]]+\] fe_http_zabbix .* 403
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
banaction = ufw
|
||||||
|
banaction_allports = ufw
|
||||||
|
allowipv6 = no
|
||||||
|
|
||||||
|
[haproxy-reject]
|
||||||
|
enabled = true
|
||||||
|
filter = haproxy-json-reject
|
||||||
|
logpath = /var/log/haproxy.log
|
||||||
|
maxretry = 15
|
||||||
|
findtime = 600
|
||||||
|
bantime = 86400
|
||||||
|
ignoreip = 127.0.0.1/8 192.168.160.0/24 192.168.128.0/17 5.100.81.121
|
||||||
|
|
||||||
|
[haproxy-zabbix-403]
|
||||||
|
enabled = true
|
||||||
|
filter = haproxy-zabbix-403
|
||||||
|
logpath = /var/log/haproxy.log
|
||||||
|
maxretry = 10
|
||||||
|
findtime = 600
|
||||||
|
bantime = 86400
|
||||||
|
ignoreip = 127.0.0.1/8 192.168.160.0/24 192.168.128.0/17 5.100.81.121
|
||||||
@@ -38,6 +38,7 @@ source "$ENV_FILE"
|
|||||||
|
|
||||||
SSH_ALLOWED=${SSH_ALLOWED:-}
|
SSH_ALLOWED=${SSH_ALLOWED:-}
|
||||||
INBOUND_TCP=${INBOUND_TCP:-80 443}
|
INBOUND_TCP=${INBOUND_TCP:-80 443}
|
||||||
|
LAN_BACKENDS_CIDR=${LAN_BACKENDS_CIDR:-192.168.160.0/24}
|
||||||
STATS_TCP=${STATS_TCP:-8404}
|
STATS_TCP=${STATS_TCP:-8404}
|
||||||
UFW_IPV6=${UFW_IPV6:-no}
|
UFW_IPV6=${UFW_IPV6:-no}
|
||||||
|
|
||||||
@@ -73,6 +74,10 @@ for port in $INBOUND_TCP; do
|
|||||||
run ufw allow "$port/tcp" comment "haproxy service $port"
|
run ufw allow "$port/tcp" comment "haproxy service $port"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ -n ${LAN_BACKENDS_CIDR:-} ]]; then
|
||||||
|
run ufw allow from "$LAN_BACKENDS_CIDR" comment 'LAN backends return'
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $APPLY -eq 1 ]]; then
|
if [[ $APPLY -eq 1 ]]; then
|
||||||
ufw --force enable
|
ufw --force enable
|
||||||
ufw status verbose
|
ufw status verbose
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ SSH_ALLOWED="5.100.81.121 192.168.128.0/17"
|
|||||||
|
|
||||||
# С периметра (MikroTik DNAT) — для всех
|
# С периметра (MikroTik DNAT) — для всех
|
||||||
INBOUND_TCP="80 443"
|
INBOUND_TCP="80 443"
|
||||||
|
LAN_BACKENDS_CIDR="192.168.160.0/24"
|
||||||
|
|
||||||
# HAProxy stats (если fe_stats включён в haproxy.cfg)
|
# HAProxy stats (если fe_stats включён в haproxy.cfg)
|
||||||
STATS_TCP="8404"
|
STATS_TCP="8404"
|
||||||
|
|||||||
Reference in New Issue
Block a user