docs(security): периметр 80/443→HAProxy, 25→KSMG; UFW на .117
Переписаны docs/security под текущий этап: SSH/stats из SSH_ALLOWED, 80/443 для всех. Скрипт UFW дополнен правилом 8404.
This commit is contained in:
+67
-117
@@ -1,143 +1,93 @@
|
||||
# Hardening хоста HAProxy
|
||||
# HAProxy: UFW и hardening
|
||||
|
||||
Хост: **haproxy.kalinamall.ru**, **192.168.160.117**, Ubuntu 24.04, HAProxy 2.8.x.
|
||||
|
||||
## Слушающие порты (prod)
|
||||
## Порты на хосте
|
||||
|
||||
| Порт | Frontend | Назначение |
|
||||
|------|----------|------------|
|
||||
| 22 | sshd | Администрирование (не в haproxy.cfg) |
|
||||
| 25 | fe_smtp | SMTP → KSMG (**LAN**; с интернета не проброшен) |
|
||||
| 80 | fe_http_zabbix | HTTP Zabbix |
|
||||
| 443 | fe_https_sni | TLS passthrough по SNI |
|
||||
| Порт | Служба | С интернета (MikroTik) | UFW (текущий план) |
|
||||
|------|--------|------------------------|---------------------|
|
||||
| 22 | sshd | нет DNAT | только `SSH_ALLOWED` |
|
||||
| 25 | fe_smtp → KSMG | **нет** (25 → `.57`) | **закрыт** |
|
||||
| 80 | fe_http_zabbix | да | все |
|
||||
| 443 | fe_https_sni | да | все |
|
||||
| 8404 | stats (закомментирован) | нет | только `SSH_ALLOWED` |
|
||||
|
||||
Не включать **8404/stats** на `0.0.0.0` без VPN и пароля.
|
||||
|
||||
## Периметр (MikroTik)
|
||||
|
||||
С **интернета** на публичный IP HAProxy проброшены только **TCP 80** и **443**. Остальные порты HAProxy (в т.ч. **25**) снаружи **недоступны** — DNAT не настроен.
|
||||
|
||||
## UFW
|
||||
## UFW — правила
|
||||
|
||||
Скрипт: [haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh).
|
||||
|
||||
Политика:
|
||||
|
||||
```
|
||||
default incoming: deny
|
||||
default outgoing: allow
|
||||
allow 22/tcp from SSH_ALLOWED
|
||||
allow 80, 443/tcp from anywhere # сервисы периметра (MikroTik DNAT)
|
||||
# allow 25/tcp from LAN_CIDR # только если нужен SMTP через HAProxy изнутри сети
|
||||
|
||||
22/tcp from 5.100.81.121, 192.168.128.0/17
|
||||
8404/tcp from 5.100.81.121, 192.168.128.0/17 # если включите fe_stats
|
||||
80/tcp from anywhere
|
||||
443/tcp from anywhere
|
||||
```
|
||||
|
||||
**SSH_ALLOWED** — несколько CIDR/ IP через пробел в env. Минимум:
|
||||
`192.168.128.0/17` покрывает офисную/VPN-подсеть (в т.ч. `192.168.160.0/24`).
|
||||
|
||||
- подсеть админов / VPN;
|
||||
- LAN `192.168.160.0/24` (если SSH только изнутри);
|
||||
- **не** `0.0.0.0/0` без крайней необходимости.
|
||||
### Env
|
||||
|
||||
Перед `--apply` обязательно dry-run и **вторая SSH-сессия**.
|
||||
`/etc/haproxy/security/haproxy-ufw.env` (из [haproxy-ufw.env.example](../../scripts/security/haproxy-ufw.env.example)):
|
||||
|
||||
## SSH
|
||||
```bash
|
||||
SSH_ALLOWED="5.100.81.121 192.168.128.0/17"
|
||||
INBOUND_TCP="80 443"
|
||||
STATS_TCP="8404"
|
||||
UFW_IPV6=no
|
||||
```
|
||||
|
||||
Пример drop-in `/etc/ssh/sshd_config.d/99-hardening.conf`:
|
||||
### Внедрение
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/haproxy/security
|
||||
sudo cp ~/reverse-proxy/scripts/security/haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env
|
||||
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh
|
||||
# вторая SSH-сессия:
|
||||
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh --apply
|
||||
sudo ufw status numbered
|
||||
```
|
||||
|
||||
**Откат:** `sudo ufw disable` (временно).
|
||||
|
||||
### Проверка
|
||||
|
||||
- SSH с `5.100.81.121` или из `192.168.128.0/17` — OK.
|
||||
- SSH с другого IP — timeout/refused.
|
||||
- `curl -I http://zabbix.kalinamall.ru` / HTTPS на allowlist-сервис — OK.
|
||||
- `ss -tlnp | grep :25` — слушает HAProxy, но UFW + MikroTik не пускают с периметра.
|
||||
|
||||
## fe_smtp (:25)
|
||||
|
||||
Inbound-почта с интернета идёт на **KSMG `.57`**, не на HAProxy. `fe_smtp` в `haproxy.cfg` можно оставить для LAN или **отключить**, если не используется — UFW его не открывает.
|
||||
|
||||
## stats (:8404)
|
||||
|
||||
В `haproxy.cfg` закомментирован. Если включите — UFW уже ограничит доступ списком `SSH_ALLOWED`; обязательно `stats auth` и не `0.0.0.0` без нужды.
|
||||
|
||||
## Рекомендуется дополнительно (не блокирует UFW)
|
||||
|
||||
| Мера | Зачем |
|
||||
|------|--------|
|
||||
| SSH: ключи, `PermitRootLogin no`, `PasswordAuthentication no` | до/вместе с UFW |
|
||||
| fail2ban на sshd | подстраховка SSH |
|
||||
| `unattended-upgrades` | патчи |
|
||||
| Не открывать лишние listen | `ss -tlnp` |
|
||||
|
||||
Опционально позже: sysctl syncookies, rate-limit на frontend, allowlist для Exchange/sac-api.
|
||||
|
||||
### SSH (пример)
|
||||
|
||||
`/etc/ssh/sshd_config.d/99-hardening.conf`:
|
||||
|
||||
```
|
||||
PermitRootLogin no
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
PubkeyAuthentication yes
|
||||
MaxAuthTries 3
|
||||
AllowUsers papatramp
|
||||
```
|
||||
|
||||
После правок: `sshd -t && systemctl reload sshd`.
|
||||
|
||||
## fail2ban
|
||||
|
||||
```bash
|
||||
sudo apt install -y fail2ban
|
||||
```
|
||||
|
||||
`/etc/fail2ban/jail.d/sshd.local`:
|
||||
|
||||
```ini
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = ssh
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
maxretry = 5
|
||||
findtime = 600
|
||||
bantime = 3600
|
||||
```
|
||||
|
||||
`systemctl enable --now fail2ban`, проверка: `fail2ban-client status sshd`.
|
||||
|
||||
## HAProxy
|
||||
|
||||
### Уже в конфиге
|
||||
|
||||
- Reject неизвестного SNI.
|
||||
- Allowlist по сервисам.
|
||||
- Длинные timeout для RDP/Exchange — осознанно; не занижать без теста.
|
||||
|
||||
### Рекомендуется добавить (отдельный PR / окно)
|
||||
|
||||
1. **Лимит соединений** (global уже `maxconn 50000`; при атаке снизить или ограничить на frontend):
|
||||
|
||||
```
|
||||
# frontend fe_https_sni
|
||||
# maxconn 10000
|
||||
```
|
||||
|
||||
2. **Exchange / sac-api без allowlist** — рассмотреть:
|
||||
- отдельный allowlist;
|
||||
- или `tcp-request content reject` при превышении conn rate (HAProxy 2.8+: stick-table / sc1 — сложнее в TCP+SNI).
|
||||
|
||||
3. **Логирование отказов** — grep `SC` / `<NOSRV>` / короткие сессии в SIEM.
|
||||
|
||||
4. **`ssl verify required`** на бэкендах после выпуска внутренней CA.
|
||||
|
||||
### systemd
|
||||
|
||||
```bash
|
||||
systemctl enable haproxy
|
||||
# после reload: systemctl status haproxy
|
||||
```
|
||||
|
||||
## sysctl (опционально)
|
||||
|
||||
`/etc/sysctl.d/99-haproxy-security.conf`:
|
||||
|
||||
```
|
||||
net.ipv4.tcp_syncookies = 1
|
||||
net.ipv4.conf.all.rp_filter = 1
|
||||
net.ipv4.conf.default.rp_filter = 1
|
||||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||
```
|
||||
|
||||
`sysctl --system`
|
||||
|
||||
## Обновления
|
||||
|
||||
```bash
|
||||
sudo apt install -y unattended-upgrades
|
||||
sudo dpkg-reconfigure -plow unattended-upgrades
|
||||
```
|
||||
|
||||
Исключить автоперезапуск HAProxy в неудобное окно — настроить `Unattended-Upgrade::Automatic-Reboot` по политике.
|
||||
|
||||
## Права и секреты
|
||||
|
||||
- Allowlist и `haproxy.cfg` — root, без world-readable секретов (в репо секретов нет).
|
||||
- `/etc/haproxy/security/` — `0750 root:root`.
|
||||
- Не коммитить `haproxy-ufw.env` с реальными IP в публичный GitHub (в kalinamall — по политике repo).
|
||||
|
||||
## Мониторинг
|
||||
|
||||
- HAProxy backend **DOWN** / **L4CON**.
|
||||
- Рост отклонённых TCP (`SC`, reject по ACL).
|
||||
- `ufw status numbered`, диск под логами.
|
||||
- fail2ban bans.
|
||||
`sshd -t && systemctl reload sshd`
|
||||
|
||||
Reference in New Issue
Block a user