HAProxy 0.7.2: sac-api для Seaca, check-ssl на всех HTTPS-бэкендах.

Веб-SAC остаётся за allowlist; sac-api.kalinamall.ru открыт для мобильных клиентов. Документация по connection coalescing Git+SAC.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-13 12:01:06 +10:00
parent ed77ec1e56
commit f802a5ac46
4 changed files with 142 additions and 37 deletions
+93
View File
@@ -0,0 +1,93 @@
# SAC: веб, Seaca (sac-api) и HAProxy
## Два имени — один сервер
| Имя | Клиенты | IP-allowlist HAProxy | Бэкенд |
|-----|---------|----------------------|--------|
| **`sac.kalinamall.ru`** | Веб-интерфейс SAC (браузер) | **Да** (`git-sac-allowed`) | 192.168.160.145:443 |
| **`sac-api.kalinamall.ru`** | Seaca (Android), mobile API | **Нет** (доступ с любого IP) | тот же 192.168.160.145:443 |
Защита `sac-api`: код регистрации `sacmob_…`, JWT, отзыв устройств в веб-SAC.
### DNS
Добавьте **A**-запись:
```text
sac-api.kalinamall.ru → тот же публичный IP, что у sac / ext / git (хост HAProxy)
```
### nginx на 192.168.160.145
В `server_name` должны быть **оба** имени (wildcard `*.kalinamall.ru` в сертификате это покрывает):
```nginx
server_name sac.kalinamall.ru sac-api.kalinamall.ru;
```
Для чужих имён (например `ext.kalinamall.ru`) — отдельный default или `return 444`, иначе SAC ответит на любой Host.
### Seaca
В приложении указывают **`https://sac-api.kalinamall.ru`**, не `sac.kalinamall.ru`.
---
## Git → SAC в одном браузере: «Page Not Found — Gitea»
### Симптом
1. Открыли **`git.kalinamall.ru`** — Gitea работает.
2. В **той же вкладке/браузере** перешли на **`sac.kalinamall.ru`**.
3. В адресной строке SAC, на экране — **Gitea 404** («Page Not Found - Gitea»).
В логах HAProxy при этом может быть **`be_git`**, хотя URL — sac.
### Причина: HTTP/2 connection coalescing
Git и SAC снаружи идут на **один IP:443** HAProxy. В режиме **TCP passthrough** backend выбирается **один раз** при ClientHello (SNI) на **TCP-соединение**.
Современные браузеры (Chrome, Firefox) при **HTTP/2** могут **объединять** несколько hostname на одном IP в **одно TLS-соединение**, если сертификаты «совместимы» (часто так с `*.kalinamall.ru`).
Тогда:
1. Первое соединение: SNI `git.kalinamall.ru` → HAProxy → **Gitea**.
2. Запрос к `sac.kalinamall.ru` идёт **по тому же TCP** (без нового SNI для HAProxy).
3. Gitea получает `Host: sac.kalinamall.ru` → отдаёт **404 Gitea**.
Это **не ошибка маршрутизации ACL** и **не баг SAC** — ограничение схемы «несколько HTTPS-имён на одном IP + TCP passthrough».
### Обходные пути (без смены архитектуры)
| Способ | Комментарий |
|--------|-------------|
| **Новая вкладка / приватное окно** для SAC после Git | Новое TCP-соединение, свой SNI |
| **Закрыть вкладку с Git** перед SAC | Сброс keep-alive / H2 coalescing |
| **Разные публичные IP** в DNS: `git.*` → IP1, `sac.*` → IP2 | Надёжное решение для passthrough |
| **`http2 off;`** в nginx Gitea и SAC | Часто уменьшает coalescing (на серверах .129 и .145) |
| **`Connection: close`** в ответах Git | Жёстче рвёт keep-alive (на .129) |
### Долгосрочно
- Отдельный WAN IP для Git и для SAC, **или**
- TLS-терминация на HAProxy с маршрутизацией по Host (больше изменений).
---
## Деплой изменений HAProxy
```bash
sudo cp ~/reverse-proxy/haproxy.cfg /etc/haproxy/haproxy.cfg
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
```
Проверка:
```bash
# веб SAC — только с allowlist IP
curl -sI --resolve sac.kalinamall.ru:443:ПУБЛИЧНЫЙ_IP https://sac.kalinamall.ru/dashboard
# mobile API — с любого IP
curl -s --resolve sac-api.kalinamall.ru:443:ПУБЛИЧНЫЙ_IP https://sac-api.kalinamall.ru/health
```