Compare commits

..

4 Commits

Author SHA1 Message Date
PTah 088eec369f fix(haproxy): ACL 404 для HAProxy 2.8 и deploy-скрипт
Исправлен синтаксис !(host_ext path_autodiscover) и порядок http-request/use_backend. Скрипт deploy сохраняет stats auth с сервера.
2026-06-20 17:06:09 +10:00
PTah ade8e4cafa fix(haproxy): Autodiscover на :80 и точный fail2ban для Zabbix
Outlook при настройке ext.kalinamall.ru слал GET /autodiscover/autodiscover.xml на порт 80; старый fe_http_zabbix отвечал 403 всем не-zabbix Host, fail2ban банил клиента на сутки. fe_http проксирует Autodiscover на Exchange :80, 403 только для zabbix вне allowlist, ignoreregex для /autodiscover/.
2026-06-20 16:58:32 +10:00
PTah 900749457f chore(docs): sync repository URLs for kalinamall mirror
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:40:58 +10:00
PTah f8617ec1e5 fix(scripts): Push-Mirror ignores untracked files
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:40:49 +10:00
11 changed files with 148 additions and 32 deletions
+28 -4
View File
@@ -1,4 +1,4 @@
# reverse-proxy # reverse-proxy
Периметровая конфигурация **HAProxy** для сети **kalinamall.ru**: Exchange, RDS (веб/шлюз по HTTPS), 1С, Git, SAC, почтовый шлюз KSMG. Прямой RDP (3389) через HAProxy не используется — клиенты идут через RDS на **443** (`rds.kalinamall.ru`). Периметровая конфигурация **HAProxy** для сети **kalinamall.ru**: Exchange, RDS (веб/шлюз по HTTPS), 1С, Git, SAC, почтовый шлюз KSMG. Прямой RDP (3389) через HAProxy не используется — клиенты идут через RDS на **443** (`rds.kalinamall.ru`).
@@ -6,14 +6,14 @@ TLS: HAProxy на **443** в режиме **TCP + SNI**, **не терминир
Доступ снаружи по allowlist: **Git** и **веб-SAC**`git-sac-allowed`; **RDS**`rds-allowed` (RU ipdeny + static); **1С**`1c-allowed`; **Zabbix** (HTTP) — `zabbix-allowed`; **Synology** (`heap`, `exchange`) — `syno-allowed`. **Seaca****`sac-api.kalinamall.ru`** без IP-фильтра. Доступ снаружи по allowlist: **Git** и **веб-SAC**`git-sac-allowed`; **RDS**`rds-allowed` (RU ipdeny + static); **1С**`1c-allowed`; **Zabbix** (HTTP) — `zabbix-allowed`; **Synology** (`heap`, `exchange`) — `syno-allowed`. **Seaca****`sac-api.kalinamall.ru`** без IP-фильтра.
Репозиторий: [https://github.com/PTah/reverse-proxy](https://github.com/PTah/reverse-proxy) Репозиторий: [https://git.kalinamall.ru/PapaTramp/reverse-proxy](https://git.kalinamall.ru/PapaTramp/reverse-proxy)
## Соответствие имён и адресов ## Соответствие имён и адресов
| Служба | Внешнее имя (клиенты) | Внутренний хост / IP | Порт | Allowlist | | Служба | Внешнее имя (клиенты) | Внутренний хост / IP | Порт | Allowlist |
|--------|------------------------|----------------------|------|-----------| |--------|------------------------|----------------------|------|-----------|
| Microsoft Exchange (OWA и т.д.) | `ext.kalinamall.ru` | fifth.kalinamall.ru — **192.168.160.42** | 443 | нет | | Microsoft Exchange (OWA и т.д.) | `ext.kalinamall.ru` | fifth.kalinamall.ru — **192.168.160.42** | 443 | нет |
| Autodiscover | `autodiscover.kalinamall.ru` | тот же Exchange — **192.168.160.42** | 443 | нет | | Autodiscover | `autodiscover.kalinamall.ru` | тот же Exchange — **192.168.160.42** | 443; **80** (HTTP Autodiscover → Exchange) | нет |
| RDS | `rds.kalinamall.ru` | k6a-dc3 — **192.168.160.40**, служба **4430** | 443 → 4430 | **`rds-allowed`** | | RDS | `rds.kalinamall.ru` | k6a-dc3 — **192.168.160.40**, служба **4430** | 443 → 4430 | **`rds-allowed`** |
| 1С | `erp.kalinamall.ru` | hp-serv — **192.168.160.150** | 443 | **`1c-allowed`** | | 1С | `erp.kalinamall.ru` | hp-serv — **192.168.160.150** | 443 | **`1c-allowed`** |
| Git (HTTPS) | `git.kalinamall.ru` | **192.168.160.129** | 443 | **`git-sac-allowed`** | | Git (HTTPS) | `git.kalinamall.ru` | **192.168.160.129** | 443 | **`git-sac-allowed`** |
@@ -26,7 +26,31 @@ TLS: HAProxy на **443** в режиме **TCP + SNI**, **не терминир
DNS: **A** для `ext`, `autodiscover`, `rds`, `erp`, `git`, `sac`, **`sac-api`**, **`zabbix`**, **`heap`**, **`exchange`** → публичный IP HAProxy. Inbound SMTP (**25**) с интернета на HAProxy **не** пробрасывается; `ksmg` / MX — отдельная схема (см. [security/architecture.md](docs/security/architecture.md)). DNS: **A** для `ext`, `autodiscover`, `rds`, `erp`, `git`, `sac`, **`sac-api`**, **`zabbix`**, **`heap`**, **`exchange`** → публичный IP HAProxy. Inbound SMTP (**25**) с интернета на HAProxy **не** пробрасывается; `ksmg` / MX — отдельная схема (см. [security/architecture.md](docs/security/architecture.md)).
Снаружи Zabbix — **`http://zabbix.kalinamall.ru`**. Synology DSM — **`https://heap.kalinamall.ru`**, **`https://exchange.kalinamall.ru`** (TLS на NAS, wildcard `*.kalinamall.ru`). Имя **`exchange.kalinamall.ru`** — NAS, не OWA (**`ext.kalinamall.ru`**). Снаружи Zabbix — **`http://zabbix.kalinamall.ru`**. Outlook при настройке почты на **`ext.kalinamall.ru`** шлёт **HTTP Autodiscover** на `:80` (`/autodiscover/autodiscover.xml`) — HAProxy проксирует на Exchange **192.168.160.42:80**, без allowlist (как HTTPS OWA). Synology DSM — **`https://heap.kalinamall.ru`**, **`https://exchange.kalinamall.ru`** (TLS на NAS, wildcard `*.kalinamall.ru`). Имя **`exchange.kalinamall.ru`** — NAS, не OWA (**`ext.kalinamall.ru`**).
### Порт 80: Zabbix и Outlook (Autodiscover)
До **0.7.8** весь `:80` обрабатывался как «только Zabbix»: любой другой `Host` (в т.ч. Autodiscover при настройке Outlook) получал **403**, fail2ban jail **`haproxy-zabbix-403`** банил IP на сутки через UFW — пользователь мог не знать про Zabbix.
С **0.7.8** frontend **`fe_http`**:
| Host / путь | Куда | Allowlist |
|-------------|------|-----------|
| `autodiscover.kalinamall.ru` | Exchange `.42:80` | нет |
| `ext.kalinamall.ru` + `/autodiscover/*` | Exchange `.42:80` | нет |
| `zabbix.kalinamall.ru` | Zabbix `.53:80` | **`zabbix-allowed`** |
| прочее | **404** (не 403) | — |
fail2ban **`haproxy-zabbix-403`**: только **403** на Zabbix с IP вне allowlist; в filter добавлен **`ignoreregex`** для `/autodiscover/`.
После деплоя на **192.168.160.117**:
```bash
sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
sudo cp ~/reverse-proxy/scripts/security/fail2ban/haproxy-zabbix-403.conf /etc/fail2ban/filter.d/
sudo fail2ban-client reload
sudo fail2ban-client set haproxy-zabbix-403 unbanip 5.143.32.177 # если ещё забанен
```
Если после **Git** в том же браузере **SAC** показывает Gitea — см. [SAC, Seaca и coalescing](docs/sac-access.md). Если после **Git** в том же браузере **SAC** показывает Gitea — см. [SAC, Seaca и coalescing](docs/sac-access.md).
+8 -1
View File
@@ -67,7 +67,7 @@ sudo haproxy -c -f /etc/haproxy/haproxy.cfg
**Полный план:** [docs/security/README.md](security/README.md) — UFW, бэкенды, чеклист, скрипты `scripts/security/`. **Полный план:** [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/пути. Порт **80**: **`fe_http`** — Exchange Autodiscover (`autodiscover`, `ext` + `/autodiscover/*`) и **`http://zabbix.kalinamall.ru`** (allowlist `zabbix-allowed`). Для ACME на других именах см. [letsencrypt-acme.md](letsencrypt-acme.md).
## SAC и Seaca (sac-api) ## SAC и Seaca (sac-api)
@@ -90,6 +90,13 @@ sudo haproxy -c -f /etc/haproxy/haproxy.cfg
curl -sI --resolve zabbix.kalinamall.ru:80:ПУБЛИЧНЫЙ_IP http://zabbix.kalinamall.ru/ curl -sI --resolve zabbix.kalinamall.ru:80:ПУБЛИЧНЫЙ_IP http://zabbix.kalinamall.ru/
``` ```
Проверка Autodiscover (без allowlist, как Outlook):
```bash
curl -sI --resolve autodiscover.kalinamall.ru:80:ПУБЛИЧНЫЙ_IP http://autodiscover.kalinamall.ru/autodiscover/autodiscover.xml
# ожидается не 403 (обычно 301/302 на HTTPS или 401 от Exchange)
```
## Synology DSM (heap, exchange) ## Synology DSM (heap, exchange)
| Имя | NAS | Порт DSM | | Имя | NAS | Порт DSM |
+5 -5
View File
@@ -1,4 +1,4 @@
# Политика репозиториев: git.kalinamall.ru и GitHub # Политика репозиториев: git.kalinamall.ru и GitHub
Для перечисленных проектов действует единая схема: **основное хранилище — корпоративный Gitea**, **GitHub — резервная копия без секретов**. Для перечисленных проектов действует единая схема: **основное хранилище — корпоративный Gitea**, **GitHub — резервная копия без секретов**.
@@ -6,11 +6,11 @@
| Проект | Основной (Gitea) | Резерв (GitHub) | | Проект | Основной (Gitea) | Резерв (GitHub) |
|--------|------------------|-----------------| |--------|------------------|-----------------|
| reverse-proxy | [PapaTramp/reverse-proxy](https://git.kalinamall.ru/PapaTramp/reverse-proxy) | [PTah/reverse-proxy](https://github.com/PTah/reverse-proxy) | | reverse-proxy | [PapaTramp/reverse-proxy](https://git.kalinamall.ru/PapaTramp/reverse-proxy) | [PTah/reverse-proxy](https://git.kalinamall.ru/PapaTramp/reverse-proxy) |
| security-alert-center | [PapaTramp/security-alert-center](https://git.kalinamall.ru/PapaTramp/security-alert-center) | [PTah/security-alert-center](https://github.com/PTah/security-alert-center) | | security-alert-center | [PapaTramp/security-alert-center](https://git.kalinamall.ru/PapaTramp/security-alert-center) | [PTah/security-alert-center](https://git.kalinamall.ru/PapaTramp/security-alert-center) |
| seaca | [PapaTramp/seaca](https://git.kalinamall.ru/PapaTramp/seaca) | резерв не ведётся (только Gitea) | | seaca | [PapaTramp/seaca](https://git.kalinamall.ru/PapaTramp/seaca) | резерв не ведётся (только Gitea) |
| rdp-login-monitor | [PapaTramp/rdp-login-monitor](https://git.kalinamall.ru/PapaTramp/rdp-login-monitor) | [PTah/RDP-login-monitor](https://github.com/PTah/RDP-login-monitor) | | rdp-login-monitor | [PapaTramp/rdp-login-monitor](https://git.kalinamall.ru/PapaTramp/rdp-login-monitor) | [PTah/RDP-login-monitor](https://git.kalinamall.ru/PapaTramp/RDP-login-monitor) |
| ssh-monitor | [PapaTramp/ssh-monitor](https://git.kalinamall.ru/PapaTramp/ssh-monitor) | [PTah/ssh-monitor](https://github.com/PTah/ssh-monitor) | | ssh-monitor | [PapaTramp/ssh-monitor](https://git.kalinamall.ru/PapaTramp/ssh-monitor) | [PTah/ssh-monitor](https://git.kalinamall.ru/PapaTramp/ssh-monitor) |
**seaca** — мобильное приложение; публичного зеркала на GitHub нет. При необходимости можно создать отдельный репозиторий **без** `google-services.json`, keystore, API-ключей и prod-конфигов. **seaca** — мобильное приложение; публичного зеркала на GitHub нет. При необходимости можно создать отдельный репозиторий **без** `google-services.json`, keystore, API-ключей и prod-конфигов.
+1 -1
View File
@@ -21,7 +21,7 @@
### Через HAProxy (80/443) ### Через HAProxy (80/443)
- **443**: reject неизвестного SNI; allowlist — git, sac-web, rds, erp, syno; без allowlist — Exchange, sac-api. - **443**: reject неизвестного SNI; allowlist — git, sac-web, rds, erp, syno; без allowlist — Exchange, sac-api.
- **80**: только `Host: zabbix.kalinamall.ru` + `zabbix-allowed`. - **80**: `autodiscover.kalinamall.ru` и `ext.kalinamall.ru/autodiscover/*` → Exchange (без allowlist); `zabbix.kalinamall.ru` + `zabbix-allowed`; прочие Host → 404.
Прокси и health-check идут **с IP `.117`**. Бэкенды должны разрешать этот источник (UFW на бэкендах — **позже**, см. [backend-hosts.md](backend-hosts.md)). Прокси и health-check идут **с IP `.117`**. Бэкенды должны разрешать этот источник (UFW на бэкендах — **позже**, см. [backend-hosts.md](backend-hosts.md)).
+1
View File
@@ -16,6 +16,7 @@
| Git (Gitea) | 192.168.160.129 | 443 | be_git | | Git (Gitea) | 192.168.160.129 | 443 | be_git |
| SAC | 192.168.160.145 | 443 | be_sac | | SAC | 192.168.160.145 | 443 | be_sac |
| Zabbix | 192.168.160.53 | 80 | be_zabbix | | Zabbix | 192.168.160.53 | 80 | be_zabbix |
| Exchange (HTTP Autodiscover) | 192.168.160.42 | 80 | be_exchange_http |
| Synology heap | 192.168.160.185 | 5001 | be_syno_heap | | Synology heap | 192.168.160.185 | 5001 | be_syno_heap |
| Synology exchange | 192.168.160.35 | 5001 | be_syno_exchange | | Synology exchange | 192.168.160.35 | 5001 | be_syno_exchange |
+2 -2
View File
@@ -8,7 +8,7 @@
|------|--------|------------------------|---------------------| |------|--------|------------------------|---------------------|
| 22 | sshd | нет DNAT | только `SSH_ALLOWED` | | 22 | sshd | нет DNAT | только `SSH_ALLOWED` |
| 25 | fe_smtp → KSMG | **нет** (25 → `.57`) | **закрыт** | | 25 | fe_smtp → KSMG | **нет** (25 → `.57`) | **закрыт** |
| 80 | fe_http_zabbix | да | все | | 80 | fe_http (Autodiscover + Zabbix) | да | все |
| 443 | fe_https_sni | да | все | | 443 | fe_https_sni | да | все |
| 8404 | stats (закомментирован) | нет | только `SSH_ALLOWED` | | 8404 | stats (закомментирован) | нет | только `SSH_ALLOWED` |
@@ -94,7 +94,7 @@ Inbound-почта с интернета идёт на **KSMG `.57`**, не на
| Jail | Что ловит | | Jail | Что ловит |
|------|-----------| |------|-----------|
| `haproxy-reject` | JSON: `"be":"fe_https_sni","srv":"<NOSRV>"` — сканеры, чужой SNI, allowlist reject | | `haproxy-reject` | JSON: `"be":"fe_https_sni","srv":"<NOSRV>"` — сканеры, чужой SNI, allowlist reject |
| `haproxy-zabbix-403` | httplog: `fe_http_zabbix` + `403` | | `haproxy-zabbix-403` | httplog: `fe_http` + **403** только при доступе к Zabbix с IP вне `zabbix-allowed` (не Autodiscover) |
```bash ```bash
sudo apt install -y fail2ban sudo apt install -y fail2ban
+24 -8
View File
@@ -1,7 +1,7 @@
# HAProxy — периметровый reverse-proxy для kalinamall.ru # HAProxy — периметровый reverse-proxy для kalinamall.ru
# Config version: 0.7.7 # Config version: 0.7.8
# Режим: TCP + SNI на 443 (TLS end-to-end, сертификаты на бэкендах) # Режим: TCP + SNI на 443 (TLS end-to-end, сертификаты на бэкендах)
# HTTP на 80: zabbix.kalinamall.ru → 192.168.160.53 (allowlist zabbix-allowed) # HTTP на 80: Exchange Autodiscover (ext/autodiscover) + zabbix.kalinamall.ru (allowlist)
# #
# Проверка синтаксиса: haproxy -c -f haproxy.cfg # Проверка синтаксиса: haproxy -c -f haproxy.cfg
# Установка: скопируйте в /etc/haproxy/haproxy.cfg # Установка: скопируйте в /etc/haproxy/haproxy.cfg
@@ -115,23 +115,39 @@ backend be_syno_exchange
server syno_exch 192.168.160.35:5001 check check-ssl check-sni exchange.kalinamall.ru verify none server syno_exch 192.168.160.35:5001 check check-ssl check-sni exchange.kalinamall.ru verify none
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# HTTP (80): Zabbix — только HTTP на бэкенде, снаружи http://zabbix.kalinamall.ru # HTTP (80): Exchange Autodiscover (Outlook) + Zabbix UI
# Allowlist: /etc/haproxy/zabbix-allowed # Autodiscover: autodiscover.kalinamall.ru, ext.kalinamall.ru/autodiscover/* → Exchange :80
# Zabbix: только Host zabbix.kalinamall.ru + allowlist /etc/haproxy/zabbix-allowed
# Прочие Host на :80 → 404 (не 403), чтобы fail2ban не путал с атакой на Zabbix
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
frontend fe_http_zabbix frontend fe_http
bind *:80 bind *:80
mode http mode http
option httplog option httplog
option forwardfor except 127.0.0.0/8 option forwardfor except 127.0.0.0/8
acl host_zabbix hdr(host) -i zabbix.kalinamall.ru acl host_zabbix hdr(host) -i zabbix.kalinamall.ru
acl host_autodiscover hdr(host) -i autodiscover.kalinamall.ru
acl host_ext hdr(host) -i ext.kalinamall.ru
acl path_autodiscover path_beg /autodiscover
acl src_zabbix_allowed src -f /etc/haproxy/zabbix-allowed acl src_zabbix_allowed src -f /etc/haproxy/zabbix-allowed
http-request deny deny_status 403 unless host_zabbix # Zabbix: 403 только при неверном IP (fail2ban haproxy-zabbix-403)
http-request deny deny_status 403 if !src_zabbix_allowed http-request deny deny_status 403 if host_zabbix !src_zabbix_allowed
default_backend be_zabbix http-request deny deny_status 404 if host_ext !path_autodiscover
http-request deny deny_status 404 if !host_zabbix !host_autodiscover !host_ext
use_backend be_exchange_http if host_autodiscover
use_backend be_exchange_http if host_ext path_autodiscover
use_backend be_zabbix if host_zabbix
backend be_exchange_http
mode http
option forwardfor
# fifth.kalinamall.ru (192.168.160.42) — IIS Autodiscover / редирект на HTTPS OWA
server exch 192.168.160.42:80 check
backend be_zabbix backend be_zabbix
mode http mode http
+3 -3
View File
@@ -21,8 +21,8 @@ if ($LASTEXITCODE -ne 0) {
throw "remote not configured: $remote" throw "remote not configured: $remote"
} }
if ((git status --porcelain)) { if ((git status --porcelain -uno)) {
throw 'working tree not clean; commit or stash first' throw 'working tree has uncommitted tracked changes; commit or stash first'
} }
$before = (git rev-parse HEAD).Trim() $before = (git rev-parse HEAD).Trim()
@@ -36,6 +36,6 @@ if (-not (git status --porcelain)) {
git add -A git add -A
git commit -m "chore(docs): sync repository URLs for ${Target} mirror" git commit -m "chore(docs): sync repository URLs for ${Target} mirror"
git push $remote main git push --force-with-lease $remote main
git reset --hard $before git reset --hard $before
Write-Output "pushed $remote with ${Target} URLs; local main reset to $before" Write-Output "pushed $remote with ${Target} URLs; local main reset to $before"
+6 -6
View File
@@ -28,15 +28,15 @@ switch ($Target) {
$BaseHost = $Base -replace '^https://', '' $BaseHost = $Base -replace '^https://', ''
$patterns = @( $patterns = @(
@{ From = 'https://github.com/PTah/([^)/''"\s]+)/blob/main/'; To = "$Base/`$1$BlobSuffix/" } @{ From = 'https://git.kalinamall.ru/PapaTramp/([^)/''"\s]+)/blob/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.kalinamall.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.kalinamall.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" } @{ From = 'https://git.kalinamall.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PTah/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://github.com/PTah/'; To = "$Base/" }
@{ From = 'https://git.kalinamall.ru/PapaTramp/'; To = "$Base/" } @{ From = 'https://git.kalinamall.ru/PapaTramp/'; To = "$Base/" }
@{ From = 'https://git.papatramp.ru/PTah/'; To = "$Base/" } @{ From = 'https://git.kalinamall.ru/PapaTramp/'; To = "$Base/" }
@{ From = 'github.com/PTah/'; To = "$BaseHost/" } @{ From = 'https://git.kalinamall.ru/PapaTramp/'; To = "$Base/" }
@{ From = 'git.kalinamall.ru/PapaTramp/'; To = "$BaseHost/" }
@{ From = 'git.kalinamall.ru/PapaTramp/'; To = "$BaseHost/" }
@{ From = 'git.kalinamall.ru/PapaTramp/'; To = "$BaseHost/" } @{ From = 'git.kalinamall.ru/PapaTramp/'; To = "$BaseHost/" }
@{ From = 'git.papatramp.ru/PTah/'; To = "$BaseHost/" }
) )
$extensions = @('*.md', '*.json', '*.service', '*.example', '*.sh', '*.ps1', '*.yml', '*.yaml') $extensions = @('*.md', '*.json', '*.service', '*.example', '*.sh', '*.ps1', '*.yml', '*.yaml')
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Deploy fe_http + fail2ban fix on HAProxy host. Preserves stats auth from live config.
set -euo pipefail
NEW_CFG="${1:-/tmp/haproxy.cfg.new}"
F2B_FILTER="${2:-/tmp/haproxy-zabbix-403.conf}"
LIVE_CFG="/etc/haproxy/haproxy.cfg"
TS="$(date +%Y%m%d-%H%M%S)"
if [[ ! -f "$NEW_CFG" ]]; then
echo "Missing $NEW_CFG" >&2
exit 1
fi
STATS_AUTH="$(grep -E '^[[:space:]]+stats auth ' "$LIVE_CFG" | head -1 | sed 's/^[[:space:]]*//')"
if [[ -z "$STATS_AUTH" ]]; then
echo "Could not read stats auth from $LIVE_CFG" >&2
exit 1
fi
MERGED="/tmp/haproxy.cfg.merged-${TS}"
cp "$NEW_CFG" "$MERGED"
# Replace placeholder; stats line has no spaces in password
sed -i "s|stats auth admin:CHANGEME|${STATS_AUTH}|" "$MERGED"
echo "Backup: ${LIVE_CFG}.bak-${TS}"
cp "$LIVE_CFG" "${LIVE_CFG}.bak-${TS}"
echo "=== merged checks ==="
grep 'Config version' "$MERGED"
grep -E 'frontend fe_http|fe_http_zabbix|be_exchange_http' "$MERGED"
grep -E '^[[:space:]]+stats auth ' "$MERGED"
haproxy -c -f "$MERGED"
cp "$MERGED" "$LIVE_CFG"
systemctl reload haproxy
echo "HAProxy reloaded OK"
if [[ -f "$F2B_FILTER" ]]; then
cp "$F2B_FILTER" /etc/fail2ban/filter.d/haproxy-zabbix-403.conf
systemctl reload fail2ban || fail2ban-client reload
echo "fail2ban filter updated"
fi
for jail in haproxy-zabbix-403 haproxy-reject sshd; do
if fail2ban-client status "$jail" >/dev/null 2>&1; then
banned="$(fail2ban-client status "$jail" | sed -n 's/.*Banned IP list:[[:space:]]*//p')"
if [[ -n "${banned// }" ]]; then
for ip in $banned; do
fail2ban-client set "$jail" unbanip "$ip"
echo "unbanned $ip from $jail"
done
fi
fi
done
if fail2ban-client unban --all >/dev/null 2>&1; then
fail2ban-client unban --all
echo "fail2ban-client unban --all done"
fi
echo "=== final ==="
grep 'Config version' "$LIVE_CFG"
fail2ban-client status haproxy-zabbix-403 | grep -E 'Currently banned|Banned IP'
fail2ban-client status haproxy-reject | grep -E 'Currently banned|Banned IP'
@@ -1,5 +1,8 @@
[Definition] [Definition]
failregex = ^.*haproxy\[(?:\d+|master)\]: <HOST>:\d+ \[[^\]]+\] fe_http_zabbix .* 403 # 403 на fe_http только при доступе к Zabbix с IP вне zabbix-allowed.
# Autodiscover (Outlook) и прочие Host на :80 больше не получают 403 — см. haproxy.cfg fe_http.
failregex = ^.*haproxy\[(?:\d+|master)\]: <HOST>:\d+ \[[^\]]+\] fe_http .* 403
ignoreregex = # Подстраховка: не банить HTTP Autodiscover, если в логе всё ещё 403
ignoreregex = /autodiscover/