docs(security): план UFW, hardening HAProxy и бэкендов, скрипты deploy.

This commit is contained in:
PTah
2026-06-13 21:27:49 +10:00
parent f26d52b3b4
commit 787671f9ca
13 changed files with 679 additions and 0 deletions
+2
View File
@@ -7,6 +7,8 @@ zabbix-allowed text eol=lf
syno-allowed text eol=lf
rds-allowed-static text eol=lf
scripts/*.sh text eol=lf
scripts/security/*.sh text eol=lf
scripts/security/*.example text eol=lf
scripts/*.cron text eol=lf
scripts/haproxy_ru_ipdeny_lib.sh text eol=lf
.gitignore text eol=lf
+1
View File
@@ -38,3 +38,4 @@ DNS: **A** для `ext`, `autodiscover`, `rds`, `erp`, `git`, `sac`, **`sac-api`
- [Работа с Git: clone, pull, push, ключи и PAT](docs/git.md)
- [Политика: Gitea — основной, GitHub — резерв без секретов](docs/repo-policy.md)
- [Let's Encrypt / ACME через HAProxy (порт 80)](docs/letsencrypt-acme.md)
- [Безопасность: UFW, бэкенды, план внедрения](docs/security/README.md)
+2
View File
@@ -65,6 +65,8 @@ sudo haproxy -c -f /etc/haproxy/haproxy.cfg
На хосте с HAProxy с интернета обычно открывают только то, что слушает конфиг: **25**, **80** (Zabbix HTTP), **443** (и при использовании — **8404** для stats). Остальной трафик — по политике безопасности.
**Полный план:** [docs/security/README.md](security/README.md) — UFW, бэкенды, чеклист, скрипты `scripts/security/`.
Порт **80** используется для **`http://zabbix.kalinamall.ru`** (allowlist `zabbix-allowed`). Для ACME на других именах см. [letsencrypt-acme.md](letsencrypt-acme.md) — потребуется расширить `fe_http_zabbix` маршрутизацией по Host/пути.
## SAC и Seaca (sac-api)
+54
View File
@@ -0,0 +1,54 @@
# Безопасность периметра HAProxy (kalinamall.ru)
План защиты хоста **haproxy.kalinamall.ru** и **бэкендов** за ним. HAProxy слушает **25**, **80**, **443**; фильтрация по SNI/Host и allowlist уже в `haproxy.cfg`, но **UFW на хосте inactive** — ниже поэтапное включение **defense in depth**.
## Модель угроз (кратко)
| Уровень | Что закрывает |
|---------|----------------|
| **MikroTik / провайдер** | DDoS, geo, лишние порты до DMZ |
| **UFW на HAProxy** | Вход только 25/80/443 + SSH с доверенных IP |
| **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).
## Документы
| Файл | Содержание |
|------|------------|
| [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
# на HAProxy — сначала dry-run
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
sudo bash ~/reverse-proxy/scripts/security/haproxy-ufw-apply.sh --apply
```
На каждом бэкенде — [backend-hosts.md](backend-hosts.md) и `backend-ufw-from-haproxy.sh`.
## Связь с конфигом HAProxy
Allowlist-файлы в `/etc/haproxy/` (**не** заменяют UFW):
- `git-sac-allowed`, `rds-allowed`, `1c-allowed`, `zabbix-allowed`, `syno-allowed`
Сервисы **без** allowlist на 443: **Exchange** (`ext`, `autodiscover`), **sac-api**, **SMTP 25** — усилить на уровне приложения и мониторинга; при необходимости добавить allowlist или rate-limit (см. [haproxy-host.md](haproxy-host.md)).
+68
View File
@@ -0,0 +1,68 @@
# Архитектура защиты
## Поток трафика
```mermaid
flowchart TB
Internet[Интернет]
MT[MikroTik / периметр]
HAP[HAProxy 192.168.160.117<br/>25 80 443]
ACL[HAProxy ACL<br/>SNI Host allowlist]
BE[Бэкенды 192.168.160.0/24]
Internet --> MT
MT -->|DNAT публичный IP| HAP
HAP --> ACL
ACL --> BE
```
## Два контура
### 1. Публичный вход (через HAProxy)
Клиент → публичный IP → **HAProxy** → (проверки) → один бэкенд.
На HAProxy уже есть:
- **443/TCP**: отклонение неизвестного SNI; allowlist для git, sac-web, rds, erp, syno.
- **80/HTTP**: только `Host: zabbix.kalinamall.ru` + `zabbix-allowed`.
- **25/SMTP**: passthrough на KSMG без IP-фильтра на frontend.
### 2. Внутренняя сеть (LAN / VPN)
Трафик **мимо HAProxy** возможен, если:
- есть VPN в `192.168.160.0/24`;
- бэкенд слушает на 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 | — | — | ✓ | — | — |
| Доступ не из allowlist | — | — | ✓ | — | ✓ |
| Прямой удар в бэкенд | ✓ | — | — | ✓ | — |
| Brute SSH на HAProxy | ✓ | ✓ | — | — | fail2ban |
| DDoS L4 | ✓ | частично | conn limits* | — | — |
\* Опционально: `maxconn`, rate-limit на frontend (Enterprise / custom); на 2.8 — sysctl, MikroTik, внешний scrubbing.
## Принципы
1. **Default deny** на вход (UFW `incoming deny`, бэкенды — только HAProxy + admin).
2. **Минимум открытых портов** с интернета: 25, 80, 443 на одном хосте.
3. **Не дублировать** смысл правил без нужды: allowlist в HAProxy для HTTP/SNI; UFW — транспорт и админ.
4. **Сначала dry-run**, потом `--apply`; для SSH — **console/IPMI** или сессия, которую UFW не оборвёт.
5. **Логи**: HAProxy + UFW + fail2ban; алерты на аномалии (Zabbix уже в инфраструктуре).
+88
View File
@@ -0,0 +1,88 @@
# Firewall на бэкендах
Цель: сервисный порт доступен **с HAProxy** (`192.168.160.117`) и при необходимости с **admin**-подсетей; **не** с произвольного LAN/Internet напрямую.
Скрипт: [backend-ufw-from-haproxy.sh](../../scripts/security/backend-ufw-from-haproxy.sh).
## Таблица бэкендов
| Сервис | IP | Порт | Протокол | HAProxy backend | Примечание |
|--------|-----|------|----------|-----------------|------------|
| Exchange | 192.168.160.42 | 443 | TCP | be_exchange | OWA, ActiveSync |
| RDS Gateway | 192.168.160.40 | **4430** | TCP | be_rds | не 443 |
| 1С ERP | 192.168.160.150 | 443 | TCP | be_erp | |
| Git (Gitea) | 192.168.160.129 | 443 | TCP | be_git | SSH Gitea 2222 — отдельно |
| SAC | 192.168.160.145 | 443 | TCP | be_sac | |
| Zabbix | 192.168.160.53 | **80** | TCP | be_zabbix | HTTP |
| Synology heap | 192.168.160.185 | **5001** | TCP | be_syno_heap | DSM HTTPS |
| Synology exchange | 192.168.160.35 | **5001** | TCP | be_syno_exchange | |
| KSMG | 192.168.160.57 | 25 | TCP | be_ksmg_smtp | см. ниже |
## Шаблон правил (логика)
```
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)
```bash
HAPROXY_IP=192.168.160.117
BACKEND_PORT=443
ADMIN_ALLOWED="192.168.160.0/24"
UFW_EXTRA_SERVICES="2222/tcp" # если Gitea SSH с admin LAN
```
## Особые случаи
### KSMG (192.168.160.57:25)
Почта с интернета приходит на **HAProxy:25** → KSMG. На KSMG в `EXTRA_ALLOWED` укажите **192.168.160.117**. Если другие MTA шлют **напрямую** на `.57` (минуя HAProxy), их IP тоже нужно явно разрешить — иначе после 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
sudo mkdir -p /etc/haproxy/security
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
```
## Проверка
С **HAProxy** (или симуляция):
```bash
curl -kI --resolve git.kalinamall.ru:443:ПУБЛИЧНЫЙ_IP https://git.kalinamall.ru/
```
С **другого** LAN-хоста (не .117):
```bash
curl -kI --connect-timeout 3 https://192.168.160.129/ # ожидается timeout/refused
```
В логе HAProxy — backend **UP**.
+44
View File
@@ -0,0 +1,44 @@
# Чеклист после внедрения защиты
## HAProxy (192.168.160.117)
- [ ] `ufw status verbose` — active; есть 22 из admin, 25/80/443 open
- [ ] SSH с admin-IP работает; с random IP — **нет** (или только ключ + fail2ban)
- [ ] `ss -tlnp` — нет лишних listen (8404, 3000, …)
- [ ] `haproxy -c -f /etc/haproxy/haproxy.cfg` — OK
- [ ] С allowlist IP: git, rds, zabbix, erp, syno — **OK**
- [ ] С чужого IP: git/rds/erp — **reject** (нет TCP reset или обрыв на ACL)
- [ ] `fail2ban-client status sshd` — active
- [ ] Логи: `grep -E 'SC|NOSRV|reject' /var/log/haproxy.log | tail`
## MikroTik / периметр
- [ ] С интернета на публичный IP: открыты только **25, 80, 443**
- [ ] Нет DNAT 3389, 5001, 4430 на внутренние хосты
- [ ] HAProxy health-check не блокируется
## Бэкенды (каждый)
- [ ] UFW/firewall active
- [ ] С `.117` на сервисный порт — OK
- [ ] С другого LAN IP на сервисный порт — **closed** (если так задумано)
- [ ] HAProxy backend **UP** в stats/log
- [ ] Прикладной smoke-test (OWA, RD Web, Gitea login, …)
## KSMG (.57)
- [ ] Почта inbound через HAProxy:25 — OK
- [ ] Прямые отправители (если есть) — в `EXTRA_ALLOWED`
## Документация и runbook
- [ ] `/etc/haproxy/security/*.env` — backup в password store
- [ ] Откат UFW: `ufw disable` + процедура задокументирована
- [ ] Ответственный и дата внедрения фикс
## Регулярно (ежемесячно)
- [ ] `apt upgrade` на HAProxy
- [ ] Проверка актуальности allowlist / `rds-allowed` cron
- [ ] Review fail2ban / auth.log
- [ ] Тест restore конфига из git
+100
View File
@@ -0,0 +1,100 @@
# План развёртывания защиты
Оценка: **24 рабочих дня** с окнами обслуживания на бэкендах (по одному хосту).
## Фаза 0 — Подготовка (без изменений на проде)
- [ ] Зафиксировать **публичный IP** HAProxy и правила DNAT на MikroTik.
- [ ] Убедиться, что **SSH** на `192.168.160.117` доступен с admin-IP (не только с одного ноутбука).
- [ ] Снимок/backup: `haproxy.cfg`, allowlist, `/etc/ssh/sshd_config`.
- [ ] Клонировать репозиторий на HAProxy: `git pull kalinamall main`.
- [ ] Прочитать [checklist.md](checklist.md).
**Критерий готовности:** есть консоль или второй admin-IP для отката UFW.
---
## Фаза 1 — HAProxy: базовый hardening (без UFW enable)
На **haproxy.kalinamall.ru**:
- [ ] `apt update && apt upgrade` (или unattended-upgrades).
- [ ] 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
- [ ] `cp scripts/security/haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env`
- [ ] Заполнить **`SSH_ALLOWED`** (офис, VPN, admin; минимум ваш текущий IP + LAN).
- [ ] Dry-run: `bash scripts/security/haproxy-ufw-apply.sh`
- [ ] **Не закрывая SSH-сессию**, в second tab: `bash scripts/security/haproxy-ufw-apply.sh --apply`
- [ ] Проверить с admin-IP: `ssh haproxy.kalinamall.ru`, `curl` на 80/443 с allowlist.
- [ ] Проверить SMTP 25 с внешнего MTA (если используется).
**Откат:** `ufw disable` (временно) или `ufw delete` по номерам правил; env-файл сохранить.
---
## Фаза 3 — UFW на бэкендах (по одному хосту)
Порядок рекомендуемый (от менее критичных к более):
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 и мониторинг
- [ ] На MikroTik: с интернета на HAProxy только **25, 80, 443** (не пробрасывать 3389, 5001, 4430 на внутренние IP).
- [ ] Raw/firewall: опционально drop obvious scanners (согласовать с `rds-allowed` / RU geo).
- [ ] Zabbix: триггеры на `ufw status`, `fail2ban`, рост `fe_https_sni` reject, backend DOWN.
- [ ] Ротация логов `/var/log/haproxy.log`.
---
## Фаза 5 — Усиление (по приоритету)
| Задача | Зачем |
|--------|--------|
| 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 | Обрыв сервиса при ошибке порта | вечер / по сервису |
| 4 | Ошибка NAT | согласование с сетью |
После каждой фазы — отметки в [checklist.md](checklist.md).
+138
View File
@@ -0,0 +1,138 @@
# Hardening хоста HAProxy
Хост: **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 |
| 80 | fe_http_zabbix | HTTP Zabbix |
| 443 | fe_https_sni | TLS passthrough по SNI |
Не включать **8404/stats** на `0.0.0.0` без VPN и пароля.
## UFW
Скрипт: [haproxy-ufw-apply.sh](../../scripts/security/haproxy-ufw-apply.sh).
Политика:
```
default incoming: deny
default outgoing: allow
allow 22/tcp from SSH_ALLOWED
allow 25, 80, 443/tcp from anywhere # сервисы периметра
```
**SSH_ALLOWED** — несколько CIDR/ IP через пробел в env. Минимум:
- подсеть админов / VPN;
- LAN `192.168.160.0/24` (если SSH только изнутри);
- **не** `0.0.0.0/0` без крайней необходимости.
Перед `--apply` обязательно dry-run и **вторая SSH-сессия**.
## SSH
Пример drop-in `/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.
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# UFW на бэкенде: сервисный порт только с HAProxy (+ admin/extra).
#
# cp backend-ufw.env.example /etc/haproxy/security/backend-ufw.env
# ./backend-ufw-from-haproxy.sh --apply
set -Eeuo pipefail
ENV_FILE="${BACKEND_UFW_ENV:-/etc/haproxy/security/backend-ufw.env}"
APPLY=0
die() { echo "error: $*" >&2; exit 1; }
log() { echo "[*] $*"; }
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=1; shift ;;
--env-file) ENV_FILE=$2; shift 2 ;;
-h | --help)
echo "Usage: $0 [--apply] [--env-file PATH]"
exit 0
;;
*) die "unknown option: $1" ;;
esac
done
[[ -f $ENV_FILE ]] || die "missing env: $ENV_FILE"
# shellcheck source=/dev/null
source "$ENV_FILE"
HAPROXY_IP=${HAPROXY_IP:-192.168.160.117}
BACKEND_PORT=${BACKEND_PORT:-}
ADMIN_ALLOWED=${ADMIN_ALLOWED:-}
EXTRA_ALLOWED=${EXTRA_ALLOWED:-}
UFW_EXTRA_SERVICES=${UFW_EXTRA_SERVICES:-}
UFW_IPV6=${UFW_IPV6:-no}
[[ -n $BACKEND_PORT ]] || die "BACKEND_PORT required"
command -v ufw >/dev/null 2>&1 || die "ufw not installed"
run() {
log "$*"
[[ $APPLY -eq 1 ]] && "$@"
}
[[ $APPLY -eq 0 ]] && echo "=== DRY-RUN (pass --apply) ==="
if [[ $APPLY -eq 1 ]]; then
sed -i "s/^IPV6=.*/IPV6=${UFW_IPV6}/" /etc/default/ufw 2>/dev/null || true
fi
run ufw --force reset
run ufw default deny incoming
run ufw default allow outgoing
run ufw allow from "$HAPROXY_IP" to any port "$BACKEND_PORT" proto tcp comment 'haproxy'
for cidr in $EXTRA_ALLOWED; do
[[ -z $cidr ]] && continue
run ufw allow from "$cidr" to any port "$BACKEND_PORT" proto tcp comment 'extra'
done
for cidr in $ADMIN_ALLOWED; do
[[ -z $cidr ]] && continue
run ufw allow from "$cidr" to any port 22 proto tcp comment 'admin ssh'
for svc in $UFW_EXTRA_SERVICES; do
[[ -z $svc ]] && continue
run ufw allow from "$cidr" to any port "$svc" comment 'admin extra'
done
done
if [[ $APPLY -eq 1 ]]; then
ufw --force enable
ufw status verbose
log "done port=$BACKEND_PORT haproxy=$HAPROXY_IP"
else
echo "=== end dry-run ==="
fi
+14
View File
@@ -0,0 +1,14 @@
# UFW на бэкенде — пример для Git 192.168.160.129
HAPROXY_IP=192.168.160.117
BACKEND_PORT=443
# Admin SSH / мониторинг (пробел), опционально
ADMIN_ALLOWED="192.168.160.0/24"
# Доп. порты с ADMIN_ALLOWED, напр. 2222/tcp для Gitea SSH
UFW_EXTRA_SERVICES=""
# Доп. источники на BACKEND_PORT (напр. MTA для KSMG)
EXTRA_ALLOWED=""
UFW_IPV6=no
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# UFW для HAProxy: default deny, SSH из SSH_ALLOWED, inbound 25/80/443.
# Dry-run по умолчанию; --apply для включения.
#
# cp haproxy-ufw.env.example /etc/haproxy/security/haproxy-ufw.env
# ./haproxy-ufw-apply.sh
# ./haproxy-ufw-apply.sh --apply
set -Eeuo pipefail
ENV_FILE="${HAPROXY_UFW_ENV:-/etc/haproxy/security/haproxy-ufw.env}"
APPLY=0
die() { echo "error: $*" >&2; exit 1; }
log() { echo "[*] $*"; }
usage() {
cat <<'EOF'
Usage: haproxy-ufw-apply.sh [--apply] [--env-file PATH]
--apply выполнить ufw enable/reset (иначе только показ команд)
--env-file путь к env (default: /etc/haproxy/security/haproxy-ufw.env)
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=1; shift ;;
--env-file) ENV_FILE=$2; shift 2 ;;
-h | --help) usage; exit 0 ;;
*) die "unknown option: $1" ;;
esac
done
[[ -f $ENV_FILE ]] || die "missing env: $ENV_FILE (copy from haproxy-ufw.env.example)"
# shellcheck source=/dev/null
source "$ENV_FILE"
SSH_ALLOWED=${SSH_ALLOWED:-}
INBOUND_TCP=${INBOUND_TCP:-25 80 443}
UFW_IPV6=${UFW_IPV6:-no}
[[ -n $SSH_ALLOWED ]] || die "SSH_ALLOWED is empty — lockout risk"
command -v ufw >/dev/null 2>&1 || die "ufw not installed (apt install ufw)"
run() {
log "$*"
[[ $APPLY -eq 1 ]] && "$@"
}
if [[ $APPLY -eq 0 ]]; then
echo "=== DRY-RUN (pass --apply to execute) ==="
fi
if [[ $APPLY -eq 1 ]]; then
sed -i "s/^IPV6=.*/IPV6=${UFW_IPV6}/" /etc/default/ufw 2>/dev/null || true
fi
run ufw --force reset
run ufw default deny incoming
run ufw default allow outgoing
for cidr in $SSH_ALLOWED; do
run ufw allow from "$cidr" to any port 22 proto tcp comment 'admin ssh'
done
for port in $INBOUND_TCP; do
run ufw allow "$port/tcp" comment "haproxy service $port"
done
if [[ $APPLY -eq 1 ]]; then
ufw --force enable
ufw status verbose
log "done"
else
echo "=== end dry-run ==="
fi
+11
View File
@@ -0,0 +1,11 @@
# UFW для haproxy.kalinamall.ru — пример.
# Копия: /etc/haproxy/security/haproxy-ufw.env (не коммитить в GitHub с prod-IP при необходимости)
# IP/CIDR для SSH (через пробел). Обязательно перед --apply.
SSH_ALLOWED="192.168.160.0/24 5.100.81.121"
# Публичные сервисы HAProxy (пробел)
INBOUND_TCP="25 80 443"
# IPv6: yes | no
UFW_IPV6=no