docs(security): периметр 80/443→HAProxy, 25→KSMG; UFW на .117
Переписаны docs/security под текущий этап: SSH/stats из SSH_ALLOWED, 80/443 для всех. Скрипт UFW дополнен правилом 8404.
This commit is contained in:
+24
-38
@@ -1,54 +1,40 @@
|
|||||||
# Безопасность периметра HAProxy (kalinamall.ru)
|
# Безопасность kalinamall.ru
|
||||||
|
|
||||||
План защиты хоста **haproxy.kalinamall.ru** и **бэкендов** за ним. HAProxy слушает **25**, **80**, **443**; с **интернета** (MikroTik DNAT) доступны только **80** и **443**. Фильтрация по SNI/Host и allowlist уже в `haproxy.cfg`, но **UFW на хосте inactive** — ниже поэтапное включение **defense in depth**.
|
Защита **haproxy.kalinamall.ru** (`192.168.160.117`) и бэкендов за ним.
|
||||||
|
|
||||||
## Модель угроз (кратко)
|
## Периметр
|
||||||
|
|
||||||
| Уровень | Что закрывает |
|
- **80/443** с интернета → HAProxy `.117` → бэкенды (ACL в `haproxy.cfg`).
|
||||||
|---------|----------------|
|
- **25** с интернета → **KSMG** `.57` напрямую (мимо HAProxy).
|
||||||
| **MikroTik / провайдер** | DDoS, geo; DNAT на HAProxy только **80/443** |
|
|
||||||
| **UFW на HAProxy** | SSH с доверенных IP; **80/443** (и **25** только если нужен с LAN) |
|
|
||||||
| **HAProxy ACL** | Неизвестный SNI, allowlist по сервисам |
|
|
||||||
| **UFW на бэкендах** | Сервисы доступны **только с IP HAProxy** (`192.168.160.117`) |
|
|
||||||
| **Приложение** | Auth OWA, RD Gateway, Gitea, SAC, 1С |
|
|
||||||
|
|
||||||
Обход HAProxy (VPN в `192.168.160.0/24`, прямой RDP) — отдельная политика; см. [backend-hosts.md](backend-hosts.md).
|
## Сейчас: UFW на HAProxy
|
||||||
|
|
||||||
## Документы
|
| Документ | Назначение |
|
||||||
|
|----------|------------|
|
||||||
|
| [architecture.md](architecture.md) | Схема, зоны, матрица |
|
||||||
|
| [haproxy-host.md](haproxy-host.md) | **UFW и правила** — основной документ для внедрения |
|
||||||
|
| [deployment-plan.md](deployment-plan.md) | Пошаговый план (фаза 2 = UFW, стоп) |
|
||||||
|
| [checklist.md](checklist.md) | Проверки после UFW |
|
||||||
|
|
||||||
| Файл | Содержание |
|
**Позже** (не в текущем scope): [backend-hosts.md](backend-hosts.md), UFW на KSMG и бэкендах.
|
||||||
|------|------------|
|
|
||||||
| [architecture.md](architecture.md) | Схема потоков, зоны, принципы |
|
|
||||||
| [haproxy-host.md](haproxy-host.md) | Hardening Ubuntu, UFW, SSH, fail2ban, HAProxy |
|
|
||||||
| [backend-hosts.md](backend-hosts.md) | Правила для каждого бэкенда |
|
|
||||||
| [deployment-plan.md](deployment-plan.md) | **Пошаговый план** внедрения (фазы 0–5) |
|
|
||||||
| [checklist.md](checklist.md) | Проверки после внедрения |
|
|
||||||
|
|
||||||
## Скрипты
|
## Скрипты
|
||||||
|
|
||||||
| Скрипт | Назначение |
|
|
||||||
|--------|------------|
|
|
||||||
| [scripts/security/haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh) | UFW на HAProxy (dry-run по умолчанию) |
|
|
||||||
| [scripts/security/haproxy-ufw.env.example](../../scripts/security/haproxy-ufw.env.example) | Пример env для HAProxy |
|
|
||||||
| [scripts/security/backend-ufw-from-haproxy.sh](../../scripts/security/backend-ufw-from-haproxy.sh) | UFW на бэкенде: порт только с HAProxy |
|
|
||||||
| [scripts/security/backend-ufw.env.example](../../scripts/security/backend-ufw.env.example) | Пример env для бэкенда |
|
|
||||||
|
|
||||||
## Быстрый старт (после чтения deployment-plan)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# на HAProxy — сначала dry-run
|
sudo mkdir -p /etc/haproxy/security
|
||||||
sudo cp ~/reverse-proxy/scripts/security/haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env
|
sudo cp ~/reverse-proxy/scripts/security/haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env
|
||||||
sudo nano /etc/haproxy/security/haproxy-ufw.env # SSH_ALLOWED, при необходимости порты
|
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh # dry-run
|
||||||
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh
|
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh --apply # вторая SSH-сессия!
|
||||||
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh --apply
|
|
||||||
```
|
```
|
||||||
|
|
||||||
На каждом бэкенде — [backend-hosts.md](backend-hosts.md) и `backend-ufw-from-haproxy.sh`.
|
| Скрипт | Назначение |
|
||||||
|
|--------|------------|
|
||||||
|
| [haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh) | UFW на HAProxy |
|
||||||
|
| [haproxy-ufw.env.example](../../scripts/security/haproxy-ufw.env.example) | `SSH_ALLOWED`, порты |
|
||||||
|
| [backend-ufw-from-haproxy.sh](../../scripts/security/backend-ufw-from-haproxy.sh) | Бэкенды — позже |
|
||||||
|
|
||||||
## Связь с конфигом HAProxy
|
## Allowlist HAProxy
|
||||||
|
|
||||||
Allowlist-файлы в `/etc/haproxy/` (**не** заменяют UFW):
|
Файлы в `/etc/haproxy/`: `git-sac-allowed`, `rds-allowed`, `1c-allowed`, `zabbix-allowed`, `syno-allowed`.
|
||||||
|
|
||||||
- `git-sac-allowed`, `rds-allowed`, `1c-allowed`, `zabbix-allowed`, `syno-allowed`
|
Без allowlist на 443: Exchange (`ext`, `autodiscover`), `sac-api`.
|
||||||
|
|
||||||
Сервисы **без** allowlist на 443: **Exchange** (`ext`, `autodiscover`), **sac-api**. **SMTP 25** с интернета на HAProxy **не проброшен** (см. [architecture.md](architecture.md)); `fe_smtp` — для LAN/внутреннего использования.
|
|
||||||
|
|||||||
@@ -1,37 +1,35 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 500" role="img" aria-label="Поток трафика: Интернет → MikroTik → HAProxy → ACL → бэкенды">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 340" role="img" aria-label="Периметр: 80/443 на HAProxy, 25 на KSMG">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
|
||||||
<polygon points="0,0 10,3.5 0,7" fill="#555"/>
|
<polygon points="0,0 8,3 0,6" fill="#555"/>
|
||||||
</marker>
|
</marker>
|
||||||
</defs>
|
</defs>
|
||||||
<style>
|
<style>
|
||||||
.box { fill: #f6f8fa; stroke: #24292f; stroke-width: 1.5; }
|
.box { fill: #f6f8fa; stroke: #24292f; stroke-width: 1.5; }
|
||||||
.text { fill: #24292f; font: 14px system-ui, -apple-system, sans-serif; text-anchor: middle; }
|
.text { fill: #24292f; font: 13px system-ui, sans-serif; text-anchor: middle; }
|
||||||
.edge { stroke: #57606a; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }
|
.edge { stroke: #57606a; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }
|
||||||
.edge-label { fill: #57606a; font: 12px system-ui, -apple-system, sans-serif; text-anchor: middle; }
|
.label { fill: #57606a; font: 11px system-ui, sans-serif; text-anchor: middle; }
|
||||||
|
.title { fill: #24292f; font: bold 12px system-ui, sans-serif; text-anchor: middle; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<rect class="box" x="95" y="10" width="110" height="40" rx="6"/>
|
<text class="title" x="210" y="18">Интернет</text>
|
||||||
<text class="text" x="150" y="35">Интернет</text>
|
|
||||||
|
|
||||||
<line class="edge" x1="150" y1="50" x2="150" y2="78"/>
|
<line class="edge" x1="130" y1="28" x2="130" y2="52"/>
|
||||||
|
<text class="label" x="130" y="46">80/443</text>
|
||||||
|
<rect class="box" x="55" y="52" width="150" height="32" rx="5"/>
|
||||||
|
<text class="text" x="130" y="73">MikroTik DNAT</text>
|
||||||
|
<line class="edge" x1="130" y1="84" x2="130" y2="108"/>
|
||||||
|
<rect class="box" x="40" y="108" width="180" height="32" rx="5"/>
|
||||||
|
<text class="text" x="130" y="129">HAProxy .117</text>
|
||||||
|
<line class="edge" x1="130" y1="140" x2="130" y2="164"/>
|
||||||
|
<rect class="box" x="25" y="164" width="210" height="32" rx="5"/>
|
||||||
|
<text class="text" x="130" y="185">ACL → бэкенды .160.0/24</text>
|
||||||
|
|
||||||
<rect class="box" x="45" y="78" width="210" height="40" rx="6"/>
|
<line class="edge" x1="290" y1="28" x2="290" y2="52"/>
|
||||||
<text class="text" x="150" y="103">MikroTik / периметр</text>
|
<text class="label" x="290" y="46">25</text>
|
||||||
|
<rect class="box" x="215" y="52" width="150" height="32" rx="5"/>
|
||||||
<line class="edge" x1="150" y1="118" x2="150" y2="146"/>
|
<text class="text" x="290" y="73">MikroTik DNAT</text>
|
||||||
<text class="edge-label" x="150" y="136">DNAT публичный IP</text>
|
<line class="edge" x1="290" y1="84" x2="290" y2="108"/>
|
||||||
|
<rect class="box" x="230" y="108" width="120" height="32" rx="5"/>
|
||||||
<rect class="box" x="20" y="146" width="260" height="40" rx="6"/>
|
<text class="text" x="290" y="129">KSMG .57</text>
|
||||||
<text class="text" x="150" y="171">HAProxy 192.168.160.117 (80 443)</text>
|
|
||||||
|
|
||||||
<line class="edge" x1="150" y1="186" x2="150" y2="214"/>
|
|
||||||
|
|
||||||
<rect class="box" x="35" y="214" width="230" height="40" rx="6"/>
|
|
||||||
<text class="text" x="150" y="239">HAProxy ACL: SNI / Host allowlist</text>
|
|
||||||
|
|
||||||
<line class="edge" x1="150" y1="254" x2="150" y2="282"/>
|
|
||||||
|
|
||||||
<rect class="box" x="15" y="282" width="270" height="40" rx="6"/>
|
|
||||||
<text class="text" x="150" y="307">Бэкенды 192.168.160.0/24</text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -1,67 +1,64 @@
|
|||||||
# Архитектура защиты
|
# Архитектура защиты
|
||||||
|
|
||||||
## Поток трафика
|
## Периметр (MikroTik)
|
||||||
|
|
||||||
|
| С интернета | DNAT на | Порт |
|
||||||
|
|-------------|---------|------|
|
||||||
|
| HTTP/HTTPS | **HAProxy** `192.168.160.117` | **80**, **443** |
|
||||||
|
| SMTP | **KSMG** `192.168.160.57` | **25** |
|
||||||
|
|
||||||
|
На HAProxy с интернета **только 80 и 443**. Порт **25** на HAProxy **не проброшен**.
|
||||||
|
|
||||||
|
## Потоки
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Схема в SVG — без Mermaid, чтобы Gitea/Firefox не ломали рендер из‑за `<br>` в XML.
|
|
||||||
|
|
||||||
Та же схема текстом:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Интернет → MikroTik (DNAT 80/443) → HAProxy .117 → ACL (SNI/Host) → бэкенды .160.0/24
|
Интернет ──80/443──► MikroTik ──► HAProxy .117 ── ACL (SNI/Host) ──► бэкенды .160.0/24
|
||||||
|
Интернет ──25──────► MikroTik ──► KSMG .57
|
||||||
```
|
```
|
||||||
|
|
||||||
**Периметр MikroTik:** с интернета на HAProxy проброшены только **80** и **443**. Порт **25** (и прочие) на HAProxy снаружи **недоступны** — DNAT не настроен.
|
### Через HAProxy (80/443)
|
||||||
|
|
||||||
## Два контура
|
- **443**: reject неизвестного SNI; allowlist — git, sac-web, rds, erp, syno; без allowlist — Exchange, sac-api.
|
||||||
|
- **80**: только `Host: zabbix.kalinamall.ru` + `zabbix-allowed`.
|
||||||
|
|
||||||
### 1. Публичный вход (через HAProxy)
|
Прокси и health-check идут **с IP `.117`**. Бэкенды должны разрешать этот источник (UFW на бэкендах — **позже**, см. [backend-hosts.md](backend-hosts.md)).
|
||||||
|
|
||||||
Клиент → публичный IP → **MikroTik (DNAT 80/443)** → **HAProxy** → (проверки) → один бэкенд.
|
### Мимо HAProxy
|
||||||
|
|
||||||
На HAProxy с интернета доступны только:
|
- **Почта**: inbound SMTP → **напрямую KSMG** (не через `fe_smtp` на `.117`).
|
||||||
|
- **LAN/VPN**: прямой доступ к бэкендам, RDP, SSH — если нет host firewall.
|
||||||
|
|
||||||
- **443/TCP**: отклонение неизвестного SNI; allowlist для git, sac-web, rds, erp, syno.
|
## Текущий этап: UFW на HAProxy
|
||||||
- **80/HTTP**: только `Host: zabbix.kalinamall.ru` + `zabbix-allowed`.
|
|
||||||
|
|
||||||
**25/SMTP** в `haproxy.cfg` слушает на хосте, но **с интернета не проброшен** (MikroTik DNAT только 80/443). Inbound-почта на KSMG идёт **не через** HAProxy:25 с периметра.
|
Сейчас внедряем только [haproxy-host.md](haproxy-host.md) — UFW на `.117`:
|
||||||
|
|
||||||
### 2. Внутренняя сеть (LAN / VPN)
|
| Порт | Кому |
|
||||||
|
|------|------|
|
||||||
|
| **22** | `SSH_ALLOWED`: `5.100.81.121`, `192.168.128.0/17` |
|
||||||
|
| **8404** (stats) | тот же список (если frontend включён) |
|
||||||
|
| **80**, **443** | все |
|
||||||
|
| остальное | deny (в т.ч. **25** на HAProxy) |
|
||||||
|
|
||||||
Трафик **мимо HAProxy** возможен, если:
|
## Матрица фильтрации
|
||||||
|
|
||||||
- есть VPN в `192.168.160.0/24`;
|
| Угроза | MikroTik | UFW HAProxy | HAProxy cfg | UFW бэкенд* | Приложение |
|
||||||
- бэкенд слушает на LAN без host firewall;
|
|--------|----------|-------------|-------------|-------------|------------|
|
||||||
- RDP/RDP Gateway по внутренним адресам.
|
| Лишние порты с интернета | ✓ | ✓ | — | — | — |
|
||||||
|
|
||||||
Защита: **UFW на бэкендах** + политика «сервисный порт только с `.117`», админ-доступ — отдельные правила.
|
|
||||||
|
|
||||||
## IP HAProxy (источник для бэкендов)
|
|
||||||
|
|
||||||
| Имя | IP |
|
|
||||||
|-----|-----|
|
|
||||||
| haproxy.kalinamall.ru | **192.168.160.117** |
|
|
||||||
|
|
||||||
Health-check HAProxy и прокси идут **с этого IP**. Правила на бэкендах должны разрешать его явно.
|
|
||||||
|
|
||||||
## Матрица: кто фильтрует что
|
|
||||||
|
|
||||||
| Угроза | MikroTik | UFW HAProxy | HAProxy cfg | UFW бэкенд | Приложение |
|
|
||||||
|--------|----------|-------------|-------------|------------|------------|
|
|
||||||
| Сканирование портов | ✓ | ✓ | — | ✓ | — |
|
|
||||||
| Неверный SNI / Host | — | — | ✓ | — | — |
|
| Неверный SNI / Host | — | — | ✓ | — | — |
|
||||||
| Доступ не из allowlist | — | — | ✓ | — | ✓ |
|
| Не из allowlist | — | — | ✓ | — | ✓ |
|
||||||
| Прямой удар в бэкенд | ✓ | — | — | ✓ | — |
|
| Прямой удар в бэкенд | частично | — | — | ✓ | — |
|
||||||
| Brute SSH на HAProxy | ✓ | ✓ | — | — | fail2ban |
|
| Brute SSH | — | ✓ | — | — | fail2ban† |
|
||||||
| DDoS L4 | ✓ | частично | conn limits* | — | — |
|
| SMTP-спам на KSMG | — | — | — | ✓‡ | KSMG |
|
||||||
|
|
||||||
\* Опционально: `maxconn`, rate-limit на frontend (Enterprise / custom); на 2.8 — sysctl, MikroTik, внешний scrubbing.
|
\* Бэкенды — следующий этап.
|
||||||
|
† Рекомендуется, не в scope текущего UFW.
|
||||||
|
‡ KSMG — периметровый хост для **25**, отдельный план.
|
||||||
|
|
||||||
## Принципы
|
## Принципы
|
||||||
|
|
||||||
1. **Default deny** на вход (UFW `incoming deny`, бэкенды — только HAProxy + admin).
|
1. **Default deny** на вход (UFW `incoming deny`).
|
||||||
2. **Минимум открытых портов** с интернета на HAProxy: **80** и **443** (DNAT на MikroTik).
|
2. Периметр на MikroTik — источник истины для DNAT; UFW на HAProxy — defense in depth + SSH.
|
||||||
3. **Не дублировать** смысл правил без нужды: allowlist в HAProxy для HTTP/SNI; UFW — транспорт и админ.
|
3. Allowlist в `haproxy.cfg` для 443/80; UFW не дублирует SNI/Host.
|
||||||
4. **Сначала dry-run**, потом `--apply`; для SSH — **console/IPMI** или сессия, которую UFW не оборвёт.
|
4. UFW: сначала dry-run, потом `--apply`; держать вторую SSH-сессию.
|
||||||
5. **Логи**: HAProxy + UFW + fail2ban; алерты на аномалии (Zabbix уже в инфраструктуре).
|
|
||||||
|
|||||||
@@ -1,90 +1,55 @@
|
|||||||
# Firewall на бэкендах
|
# Firewall на бэкендах
|
||||||
|
|
||||||
Цель: сервисный порт доступен **с HAProxy** (`192.168.160.117`) и при необходимости с **admin**-подсетей; **не** с произвольного LAN/Internet напрямую.
|
**Статус: не внедряем сейчас.** Текущий этап — UFW только на HAProxy ([haproxy-host.md](haproxy-host.md)).
|
||||||
|
|
||||||
|
Цель (когда дойдём): сервисный порт только с **HAProxy** `192.168.160.117` + admin при необходимости.
|
||||||
|
|
||||||
Скрипт: [backend-ufw-from-haproxy.sh](../../scripts/security/backend-ufw-from-haproxy.sh).
|
Скрипт: [backend-ufw-from-haproxy.sh](../../scripts/security/backend-ufw-from-haproxy.sh).
|
||||||
|
|
||||||
## Таблица бэкендов
|
## Бэкенды за HAProxy
|
||||||
|
|
||||||
| Сервис | IP | Порт | Протокол | HAProxy backend | Примечание |
|
| Сервис | IP | Порт | HAProxy backend |
|
||||||
|--------|-----|------|----------|-----------------|------------|
|
|--------|-----|------|-----------------|
|
||||||
| Exchange | 192.168.160.42 | 443 | TCP | be_exchange | OWA, ActiveSync |
|
| Exchange | 192.168.160.42 | 443 | be_exchange |
|
||||||
| RDS Gateway | 192.168.160.40 | **4430** | TCP | be_rds | не 443 |
|
| RDS Gateway | 192.168.160.40 | 4430 | be_rds |
|
||||||
| 1С ERP | 192.168.160.150 | 443 | TCP | be_erp | |
|
| 1С ERP | 192.168.160.150 | 443 | be_erp |
|
||||||
| Git (Gitea) | 192.168.160.129 | 443 | TCP | be_git | SSH Gitea 2222 — отдельно |
|
| Git (Gitea) | 192.168.160.129 | 443 | be_git |
|
||||||
| SAC | 192.168.160.145 | 443 | TCP | be_sac | |
|
| SAC | 192.168.160.145 | 443 | be_sac |
|
||||||
| Zabbix | 192.168.160.53 | **80** | TCP | be_zabbix | HTTP |
|
| Zabbix | 192.168.160.53 | 80 | be_zabbix |
|
||||||
| Synology heap | 192.168.160.185 | **5001** | TCP | be_syno_heap | DSM HTTPS |
|
| Synology heap | 192.168.160.185 | 5001 | be_syno_heap |
|
||||||
| Synology exchange | 192.168.160.35 | **5001** | TCP | be_syno_exchange | |
|
| Synology exchange | 192.168.160.35 | 5001 | be_syno_exchange |
|
||||||
| KSMG | 192.168.160.57 | 25 | TCP | be_ksmg_smtp | см. ниже |
|
|
||||||
|
|
||||||
## Шаблон правил (логика)
|
`fe_smtp` в HAProxy — опционально для LAN; inbound **25** с интернета на **KSMG**, не через `.117`.
|
||||||
|
|
||||||
```
|
## KSMG (192.168.160.57) — отдельно от HAProxy
|
||||||
allow from 192.168.160.117 to <PORT>
|
|
||||||
allow from ADMIN_ALLOWED to 22/tcp # SSH, если на этом хосте
|
|
||||||
allow from LAN to ... # только если нужен прямой доступ (минимизировать)
|
|
||||||
deny incoming (default)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пример env для Git (192.168.160.129)
|
| Путь | DNAT |
|
||||||
|
|------|------|
|
||||||
|
| Интернет → SMTP | MikroTik **25** → `.57` |
|
||||||
|
| HAProxy → KSMG | только если используется `fe_smtp` из LAN |
|
||||||
|
|
||||||
|
При UFW на KSMG: разрешить **25** с интернета (или по списку MTA), **не** полагаться на HAProxy.
|
||||||
|
|
||||||
|
## Шаблон (Linux)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
HAPROXY_IP=192.168.160.117
|
HAPROXY_IP=192.168.160.117
|
||||||
BACKEND_PORT=443
|
BACKEND_PORT=443
|
||||||
ADMIN_ALLOWED="192.168.160.0/24"
|
ADMIN_ALLOWED="192.168.128.0/17"
|
||||||
UFW_EXTRA_SERVICES="2222/tcp" # если Gitea SSH с admin LAN
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Особые случаи
|
|
||||||
|
|
||||||
### KSMG (192.168.160.57:25)
|
|
||||||
|
|
||||||
С **интернета** на HAProxy проброшены только **80/443** — inbound SMTP **не** приходит на **HAProxy:25**. Почта с периметра идёт **напрямую на KSMG** (или иным маршрутом), минуя HAProxy.
|
|
||||||
|
|
||||||
`fe_smtp` в `haproxy.cfg` — для **LAN** (прокси на KSMG с `.117`). На KSMG в `EXTRA_ALLOWED` укажите **192.168.160.117**, если этот путь используется. IP внешних MTA, шлющих **напрямую** на `.57`, тоже нужно явно разрешить — иначе после UFW почта встанет.
|
|
||||||
|
|
||||||
### RDS (192.168.160.40)
|
|
||||||
|
|
||||||
Порт **4430**, не 443. VPN-клиенты с прямым RDP на `.3` / `.40` обходят HAProxy — ограничивать **3389/4430** на session hosts отдельной GPO/файрволом.
|
|
||||||
|
|
||||||
### Synology
|
|
||||||
|
|
||||||
DSM может требовать доп. порты (5000, 5001, relay). Для доступа **только через HAProxy** достаточно **5001** с `.117`. Прямой доступ к NAS с LAN — отключить или ограничить в DSM **Панель → Безопасность → Брандмауэр**.
|
|
||||||
|
|
||||||
### Exchange
|
|
||||||
|
|
||||||
Внутренние клиенты Outlook могут ходить **напрямую** на `.42` по LAN — UFW не сломать без списка подсетей Exchange. Варианты:
|
|
||||||
|
|
||||||
- разрешить `192.168.160.0/24` на 443 (шире);
|
|
||||||
- или только `.117` + подсеть Exchange DAG/clients (уже).
|
|
||||||
|
|
||||||
### Windows vs Linux
|
|
||||||
|
|
||||||
Скрипт UFW — для **Linux**. Для Windows (Exchange, RDS, 1С) — **Windows Firewall with Advanced Security**: inbound allow **Remote IP** `192.168.160.117`, порт сервиса.
|
|
||||||
|
|
||||||
## Развёртывание на Linux-бэкенде
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /etc/haproxy/security
|
sudo bash backend-ufw-from-haproxy.sh # dry-run
|
||||||
sudo cp backend-ufw.env.example /etc/haproxy/security/backend-ufw.env
|
|
||||||
sudo nano /etc/haproxy/security/backend-ufw.env
|
|
||||||
sudo bash backend-ufw-from-haproxy.sh
|
|
||||||
sudo bash backend-ufw-from-haproxy.sh --apply
|
sudo bash backend-ufw-from-haproxy.sh --apply
|
||||||
```
|
```
|
||||||
|
|
||||||
## Проверка
|
## Особые случаи (кратко)
|
||||||
|
|
||||||
С **HAProxy** (или симуляция):
|
- **Exchange / Windows** — Windows Firewall, не UFW; внутренние Outlook-клиенты могут ходить на `.42` по LAN.
|
||||||
|
- **RDS** — порт **4430**; прямой RDP мимо HAProxy — отдельная политика.
|
||||||
|
- **Synology** — DSM firewall; с HAProxy достаточно **5001** с `.117`.
|
||||||
|
- **Gitea SSH 2222** — отдельное правило в `UFW_EXTRA_SERVICES`.
|
||||||
|
|
||||||
```bash
|
## Проверка (когда внедрим)
|
||||||
curl -kI --resolve git.kalinamall.ru:443:ПУБЛИЧНЫЙ_IP https://git.kalinamall.ru/
|
|
||||||
```
|
|
||||||
|
|
||||||
С **другого** LAN-хоста (не .117):
|
С HAProxy — сервис OK. С другого LAN IP на `https://192.168.160.129/` — refused/timeout.
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -kI --connect-timeout 3 https://192.168.160.129/ # ожидается timeout/refused
|
|
||||||
```
|
|
||||||
|
|
||||||
В логе HAProxy — backend **UP**.
|
|
||||||
|
|||||||
+24
-31
@@ -1,44 +1,37 @@
|
|||||||
# Чеклист после внедрения защиты
|
# Чеклист
|
||||||
|
|
||||||
## HAProxy (192.168.160.117)
|
## UFW на HAProxy (текущий этап)
|
||||||
|
|
||||||
- [ ] `ufw status verbose` — active; есть 22 из admin, **80/443** open (25 — только если явно включён для LAN)
|
- [ ] `ufw status verbose` — active
|
||||||
- [ ] SSH с admin-IP работает; с random IP — **нет** (или только ключ + fail2ban)
|
- [ ] **22/tcp** — только `5.100.81.121`, `192.168.128.0/17`
|
||||||
- [ ] `ss -tlnp` — нет лишних listen (8404, 3000, …)
|
- [ ] **8404/tcp** — только тот же список (если stats включён)
|
||||||
|
- [ ] **80/tcp**, **443/tcp** — `Anywhere`
|
||||||
|
- [ ] **25/tcp** — нет правила allow (default deny)
|
||||||
|
- [ ] SSH с admin-IP — OK; с чужого IP — refused
|
||||||
|
- [ ] Сервисы через HAProxy (git, zabbix, rds, …) — OK
|
||||||
- [ ] `haproxy -c -f /etc/haproxy/haproxy.cfg` — OK
|
- [ ] `haproxy -c -f /etc/haproxy/haproxy.cfg` — OK
|
||||||
- [ ] С allowlist IP: git, rds, zabbix, erp, syno — **OK**
|
- [ ] `/etc/haproxy/security/haproxy-ufw.env` — backup
|
||||||
- [ ] С чужого IP: git/rds/erp — **reject** (нет TCP reset или обрыв на ACL)
|
|
||||||
- [ ] `fail2ban-client status sshd` — active
|
|
||||||
- [ ] Логи: `grep -E 'SC|NOSRV|reject' /var/log/haproxy.log | tail`
|
|
||||||
|
|
||||||
## MikroTik / периметр
|
## MikroTik
|
||||||
|
|
||||||
- [ ] С интернета на публичный IP: открыты только **80, 443**
|
- [ ] DNAT **80/443** → `192.168.160.117`
|
||||||
- [ ] Нет DNAT 3389, 5001, 4430 на внутренние хосты
|
- [ ] DNAT **25** → `192.168.160.57` (не на HAProxy)
|
||||||
- [ ] HAProxy health-check не блокируется
|
- [ ] Нет DNAT 3389, 5001, 4430 на внутренние IP
|
||||||
|
|
||||||
## Бэкенды (каждый)
|
## После UFW (опционально, не блокер)
|
||||||
|
|
||||||
- [ ] UFW/firewall active
|
- [ ] fail2ban sshd
|
||||||
- [ ] С `.117` на сервисный порт — OK
|
- [ ] `unattended-upgrades`
|
||||||
- [ ] С другого LAN IP на сервисный порт — **closed** (если так задумано)
|
|
||||||
- [ ] HAProxy backend **UP** в stats/log
|
|
||||||
- [ ] Прикладной smoke-test (OWA, RD Web, Gitea login, …)
|
|
||||||
|
|
||||||
## KSMG (.57)
|
---
|
||||||
|
|
||||||
- [ ] Inbound-почта с интернета **не** идёт через HAProxy:25 (DNAT на MikroTik только 80/443)
|
## Позже — бэкенды
|
||||||
- [ ] На KSMG в `EXTRA_ALLOWED` — **192.168.160.117** (если используется `fe_smtp` из LAN) и прямые MTA
|
|
||||||
|
|
||||||
## Документация и runbook
|
- [ ] UFW: сервисный порт только с `.117`
|
||||||
|
- [ ] KSMG: UFW под прямой **25** с интернета
|
||||||
|
|
||||||
- [ ] `/etc/haproxy/security/*.env` — backup в password store
|
## Регулярно
|
||||||
- [ ] Откат UFW: `ufw disable` + процедура задокументирована
|
|
||||||
- [ ] Ответственный и дата внедрения фикс
|
|
||||||
|
|
||||||
## Регулярно (ежемесячно)
|
|
||||||
|
|
||||||
- [ ] `apt upgrade` на HAProxy
|
- [ ] `apt upgrade` на HAProxy
|
||||||
- [ ] Проверка актуальности allowlist / `rds-allowed` cron
|
- [ ] Актуальность allowlist / `rds-allowed`
|
||||||
- [ ] Review fail2ban / auth.log
|
- [ ] `ufw status`, auth.log
|
||||||
- [ ] Тест restore конфига из git
|
|
||||||
|
|||||||
@@ -1,100 +1,61 @@
|
|||||||
# План развёртывания защиты
|
# План внедрения
|
||||||
|
|
||||||
Оценка: **2–4 рабочих дня** с окнами обслуживания на бэкендах (по одному хосту).
|
**Текущая остановка:** UFW на HAProxy (фаза 2). Бэкенды и KSMG — позже.
|
||||||
|
|
||||||
## Фаза 0 — Подготовка (без изменений на проде)
|
## Фаза 0 — Подготовка
|
||||||
|
|
||||||
- [ ] Зафиксировать **публичный IP** HAProxy и правила DNAT на MikroTik (**только 80/443**).
|
- [ ] Зафиксировать DNAT на MikroTik:
|
||||||
- [ ] Убедиться, что **SSH** на `192.168.160.117` доступен с admin-IP (не только с одного ноутбука).
|
- `80`, `443` → `192.168.160.117`
|
||||||
- [ ] Снимок/backup: `haproxy.cfg`, allowlist, `/etc/ssh/sshd_config`.
|
- `25` → `192.168.160.57` (KSMG)
|
||||||
- [ ] Клонировать репозиторий на HAProxy: `git pull kalinamall main`.
|
- [ ] SSH на `.117` работает с `5.100.81.121` или `192.168.128.0/17`
|
||||||
- [ ] Прочитать [checklist.md](checklist.md).
|
- [ ] Backup: `haproxy.cfg`, allowlist, `sshd_config`
|
||||||
|
- [ ] `git pull` на HAProxy
|
||||||
**Критерий готовности:** есть консоль или второй admin-IP для отката UFW.
|
- [ ] Консоль/IPMI на случай lockout SSH
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Фаза 1 — HAProxy: базовый hardening (без UFW enable)
|
## Фаза 1 — Подготовка хоста (без UFW)
|
||||||
|
|
||||||
На **haproxy.kalinamall.ru**:
|
- [ ] `apt update && apt upgrade`
|
||||||
|
- [ ] SSH: ключи, без root/password ([haproxy-host.md](haproxy-host.md))
|
||||||
|
- [ ] `haproxy -c -f /etc/haproxy/haproxy.cfg` — OK
|
||||||
|
- [ ] `fe_stats` (:8404) — закомментирован или будет под UFW
|
||||||
|
- [ ] Решить судьбу `fe_smtp` (:25): нужен из LAN или отключить
|
||||||
|
|
||||||
- [ ] `apt update && apt upgrade` (или unattended-upgrades).
|
**Проверка:** `ss -tlnp` — ожидаемо 22, 80, 443; возможно 25.
|
||||||
- [ ] SSH: `PasswordAuthentication no`, `PermitRootLogin no`, только ключи (`/etc/ssh/sshd_config.d/`).
|
|
||||||
- [ ] Установить **fail2ban** (`sshd` jail), см. [haproxy-host.md](haproxy-host.md).
|
|
||||||
- [ ] Убедиться, что **stats :8404** не включены в prod (закомментированы в `haproxy.cfg`).
|
|
||||||
- [ ] Права: `/etc/haproxy/*-allowed` — `0644 root:root`; скрипты в `/usr/local/bin` — `0755`.
|
|
||||||
- [ ] `sysctl`: syncookies, rp_filter (см. [haproxy-host.md](haproxy-host.md)).
|
|
||||||
|
|
||||||
**Проверка:** `ss -tlnp` — только 22, 25, 80, 443 (+ systemd-resolved если локально).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Фаза 2 — UFW на HAProxy
|
## Фаза 2 — UFW на HAProxy ← **СТОП ЗДЕСЬ**
|
||||||
|
|
||||||
- [ ] `cp scripts/security/haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env`
|
- [ ] `cp scripts/security/haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env`
|
||||||
- [ ] Заполнить **`SSH_ALLOWED`** (офис, VPN, admin; минимум ваш текущий IP + LAN).
|
- [ ] Проверить `SSH_ALLOWED="5.100.81.121 192.168.128.0/17"`
|
||||||
- [ ] Dry-run: `bash scripts/security/haproxy-ufw-apply.sh`
|
- [ ] Dry-run: `bash scripts/security/haproxy-ufw-apply.sh`
|
||||||
- [ ] **Не закрывая SSH-сессию**, в second tab: `bash scripts/security/haproxy-ufw-apply.sh --apply`
|
- [ ] **Вторая SSH-сессия:** `bash scripts/security/haproxy-ufw-apply.sh --apply`
|
||||||
- [ ] Проверить с admin-IP: `ssh haproxy.kalinamall.ru`, `curl` на 80/443 с allowlist.
|
- [ ] [checklist.md](checklist.md) — HAProxy
|
||||||
- [ ] Убедиться, что **25** с интернета на HAProxy **не** доступен (ожидаемо: DNAT не настроен).
|
|
||||||
|
|
||||||
**Откат:** `ufw disable` (временно) или `ufw delete` по номерам правил; env-файл сохранить.
|
**Откат:** `ufw disable`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Фаза 3 — UFW на бэкендах (по одному хосту)
|
## Фаза 3 — UFW на бэкендах (позже)
|
||||||
|
|
||||||
Порядок рекомендуемый (от менее критичных к более):
|
По одному хосту, см. [backend-hosts.md](backend-hosts.md): Zabbix → Git → SAC → Synology → ERP → RDS → Exchange.
|
||||||
|
|
||||||
1. Zabbix `.53:80`
|
|
||||||
2. Git `.129:443`
|
|
||||||
3. SAC `.145:443`
|
|
||||||
4. Synology `.185:5001`, `.35:5001`
|
|
||||||
5. ERP `.150:443`
|
|
||||||
6. RDS `.40:4430`
|
|
||||||
7. Exchange `.42:443`
|
|
||||||
8. KSMG `.57:25` — **особый случай** (почта может идти не только с HAProxy); см. [backend-hosts.md](backend-hosts.md)
|
|
||||||
|
|
||||||
На каждом хосте:
|
|
||||||
|
|
||||||
- [ ] `cp backend-ufw.env.example` → `/etc/haproxy/security/backend-ufw.env` (или `/etc/security/`)
|
|
||||||
- [ ] Задать `BACKEND_PORT`, при необходимости `ADMIN_ALLOWED`, `EXTRA_ALLOWED`
|
|
||||||
- [ ] Dry-run → `--apply`
|
|
||||||
- [ ] Проверить сервис через HAProxy снаружи и health-check в логе HAProxy
|
|
||||||
|
|
||||||
**Критерий:** с другого LAN-хоста (не `.117`) `curl https://<backend-ip>:<port>` **не** отвечает; с HAProxy — работает.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Фаза 4 — MikroTik и мониторинг
|
## Фаза 4 — KSMG (позже)
|
||||||
|
|
||||||
- [ ] На MikroTik: с интернета на HAProxy только **80, 443** (не пробрасывать 25, 3389, 5001, 4430 на внутренние IP).
|
Периметровый **25** на `.57` — отдельный UFW и политика relay. HAProxy к inbound-почте не привязан.
|
||||||
- [ ] Raw/firewall: опционально drop obvious scanners (согласовать с `rds-allowed` / RU geo).
|
|
||||||
- [ ] Zabbix: триггеры на `ufw status`, `fail2ban`, рост `fe_https_sni` reject, backend DOWN.
|
|
||||||
- [ ] Ротация логов `/var/log/haproxy.log`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Фаза 5 — Усиление (по приоритету)
|
## Фаза 5 — Усиление (позже)
|
||||||
|
|
||||||
| Задача | Зачем |
|
Allowlist Exchange/sac-api, TLS verify на бэкендах, мониторинг UFW/fail2ban в Zabbix.
|
||||||
|--------|--------|
|
|
||||||
| Allowlist или rate-limit для **Exchange** | Сейчас 443 без IP-фильтра |
|
|
||||||
| Ограничить **sac-api** (если бизнес позволяет) | Сейчас без allowlist |
|
|
||||||
| TLS verify на бэкендах HAProxy | Вместо `verify none` — внутренняя CA |
|
|
||||||
| Отдельный VLAN для DMZ / HAProxy | Сегментация |
|
|
||||||
| WAF / reverse proxy HTTP для Zabbix → HTTPS | Порт 80 только HTTP |
|
|
||||||
| 2FA на Git, OWA, RD Gateway | Учётные записи |
|
|
||||||
|
|
||||||
---
|
| Фаза | Риск |
|
||||||
|
|------|------|
|
||||||
## Окна и коммуникация
|
| 1 | низкий |
|
||||||
|
| 2 | **SSH lockout** — вторая сессия + console |
|
||||||
| Фаза | Риск | Окно |
|
| 3+ | обрыв сервиса при ошибке порта |
|
||||||
|------|------|------|
|
|
||||||
| 1 | Низкий | любое |
|
|
||||||
| 2 | **SSH lockout** | рабочее время + console |
|
|
||||||
| 3 | Обрыв сервиса при ошибке порта | вечер / по сервису |
|
|
||||||
| 4 | Ошибка NAT | согласование с сетью |
|
|
||||||
|
|
||||||
После каждой фазы — отметки в [checklist.md](checklist.md).
|
|
||||||
|
|||||||
+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.
|
Хост: **haproxy.kalinamall.ru**, **192.168.160.117**, Ubuntu 24.04, HAProxy 2.8.x.
|
||||||
|
|
||||||
## Слушающие порты (prod)
|
## Порты на хосте
|
||||||
|
|
||||||
| Порт | Frontend | Назначение |
|
| Порт | Служба | С интернета (MikroTik) | UFW (текущий план) |
|
||||||
|------|----------|------------|
|
|------|--------|------------------------|---------------------|
|
||||||
| 22 | sshd | Администрирование (не в haproxy.cfg) |
|
| 22 | sshd | нет DNAT | только `SSH_ALLOWED` |
|
||||||
| 25 | fe_smtp | SMTP → KSMG (**LAN**; с интернета не проброшен) |
|
| 25 | fe_smtp → KSMG | **нет** (25 → `.57`) | **закрыт** |
|
||||||
| 80 | fe_http_zabbix | HTTP Zabbix |
|
| 80 | fe_http_zabbix | да | все |
|
||||||
| 443 | fe_https_sni | TLS passthrough по SNI |
|
| 443 | fe_https_sni | да | все |
|
||||||
|
| 8404 | stats (закомментирован) | нет | только `SSH_ALLOWED` |
|
||||||
|
|
||||||
Не включать **8404/stats** на `0.0.0.0` без VPN и пароля.
|
## UFW — правила
|
||||||
|
|
||||||
## Периметр (MikroTik)
|
|
||||||
|
|
||||||
С **интернета** на публичный IP HAProxy проброшены только **TCP 80** и **443**. Остальные порты HAProxy (в т.ч. **25**) снаружи **недоступны** — DNAT не настроен.
|
|
||||||
|
|
||||||
## UFW
|
|
||||||
|
|
||||||
Скрипт: [haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh).
|
Скрипт: [haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh).
|
||||||
|
|
||||||
Политика:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
default incoming: deny
|
default incoming: deny
|
||||||
default outgoing: allow
|
default outgoing: allow
|
||||||
allow 22/tcp from SSH_ALLOWED
|
|
||||||
allow 80, 443/tcp from anywhere # сервисы периметра (MikroTik DNAT)
|
22/tcp from 5.100.81.121, 192.168.128.0/17
|
||||||
# allow 25/tcp from LAN_CIDR # только если нужен SMTP через HAProxy изнутри сети
|
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;
|
### Env
|
||||||
- LAN `192.168.160.0/24` (если SSH только изнутри);
|
|
||||||
- **не** `0.0.0.0/0` без крайней необходимости.
|
|
||||||
|
|
||||||
Перед `--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
|
PermitRootLogin no
|
||||||
PasswordAuthentication no
|
PasswordAuthentication no
|
||||||
KbdInteractiveAuthentication no
|
|
||||||
PubkeyAuthentication yes
|
PubkeyAuthentication yes
|
||||||
MaxAuthTries 3
|
MaxAuthTries 3
|
||||||
AllowUsers papatramp
|
AllowUsers papatramp
|
||||||
```
|
```
|
||||||
|
|
||||||
После правок: `sshd -t && systemctl reload sshd`.
|
`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.
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# UFW для HAProxy: default deny, SSH из SSH_ALLOWED, inbound 80/443 (25 — опционально для LAN).
|
# UFW для HAProxy: SSH и stats из SSH_ALLOWED, 80/443 для всех.
|
||||||
# Dry-run по умолчанию; --apply для включения.
|
# Dry-run по умолчанию; --apply для включения.
|
||||||
#
|
#
|
||||||
# cp haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env
|
# cp haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env
|
||||||
@@ -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}
|
||||||
|
STATS_TCP=${STATS_TCP:-8404}
|
||||||
UFW_IPV6=${UFW_IPV6:-no}
|
UFW_IPV6=${UFW_IPV6:-no}
|
||||||
|
|
||||||
[[ -n $SSH_ALLOWED ]] || die "SSH_ALLOWED is empty — lockout risk"
|
[[ -n $SSH_ALLOWED ]] || die "SSH_ALLOWED is empty — lockout risk"
|
||||||
@@ -63,6 +64,9 @@ run ufw default allow outgoing
|
|||||||
|
|
||||||
for cidr in $SSH_ALLOWED; do
|
for cidr in $SSH_ALLOWED; do
|
||||||
run ufw allow from "$cidr" to any port 22 proto tcp comment 'admin ssh'
|
run ufw allow from "$cidr" to any port 22 proto tcp comment 'admin ssh'
|
||||||
|
for port in $STATS_TCP; do
|
||||||
|
run ufw allow from "$cidr" to any port "$port" proto tcp comment 'haproxy stats'
|
||||||
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
for port in $INBOUND_TCP; do
|
for port in $INBOUND_TCP; do
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
# UFW для haproxy.kalinamall.ru — пример.
|
# UFW для haproxy.kalinamall.ru
|
||||||
# Копия: /etc/haproxy/security/haproxy-ufw.env (не коммитить в GitHub с prod-IP при необходимости)
|
# Копия: /etc/haproxy/security/haproxy-ufw.env
|
||||||
|
|
||||||
# IP/CIDR для SSH (через пробел). Обязательно перед --apply.
|
# SSH и stats — только эти IP/CIDR (через пробел)
|
||||||
SSH_ALLOWED="192.168.160.0/24 5.100.81.121"
|
SSH_ALLOWED="5.100.81.121 192.168.128.0/17"
|
||||||
|
|
||||||
# Публичные сервисы HAProxy с периметра (пробел). MikroTik DNAT: только 80 и 443.
|
# С периметра (MikroTik DNAT) — для всех
|
||||||
INBOUND_TCP="80 443"
|
INBOUND_TCP="80 443"
|
||||||
|
|
||||||
# Добавьте 25, если нужен SMTP через HAProxy только из LAN (не с интернета):
|
# HAProxy stats (если fe_stats включён в haproxy.cfg)
|
||||||
# INBOUND_TCP="80 443 25"
|
STATS_TCP="8404"
|
||||||
|
|
||||||
# IPv6: yes | no
|
|
||||||
UFW_IPV6=no
|
UFW_IPV6=no
|
||||||
|
|||||||
Reference in New Issue
Block a user