Проект разделен на два репозитория

This commit is contained in:
mi
2026-08-14 15:42:45 +03:00
parent e06a77ee1d
commit bbef7a30c9
521 changed files with 2597 additions and 2302 deletions
@@ -0,0 +1,214 @@
# module-03-vm1. Nginx ВМ1 HAN Chat
> Статус: целевая спецификация nginx на ВМ1.
> Канонический контракт (TLS/ACME, request id, internal 404, logs, reload) — [`arch-08-nginx.md`](../../architectory/arch-08-nginx.md).
> Обязательный host/container hardening baseline — [`arch-06-service-hosting-security.md`](../../architectory/arch-06-service-hosting-security.md).
> Контур ВМ2 — [`module-03-nginx-vm2.md`](../../VM2_services/documentation/module-03-nginx-vm2.md). Публичный трафик ВМ2 не проходит через этот nginx.
## 1. Назначение и границы
Nginx ВМ1 — публичная точка входа приложения: frontend, API, auth, Open Lines, SMS callback. Message Safety и CRM webhook на этой машине не публикуются.
Канонический вызов Safety: `api-backend` напрямую → private `8443` nginx ВМ2. Public `/internal/` на ВМ1 всегда `404`; upstream `processing_gateway` и proxy route Safety в nginx ВМ1 запрещены.
## 2. Routing matrix
Порядок location критичен. Prefix `/` — последним.
| Внешний путь | Upstream | Режим |
|---|---|---|
| `/api/` | `api-backend:8000` | REST; `/api/v1/realtime` WS |
| `/auth/` | `keycloak:8080` | OIDC/OTP, prefix/hostname согласован с issuer |
| exact `/callbacks/idgtl/sms` | `sms-service:8080` | public HTTPS POST Direct; IP allowlist + Basic auth в upstream |
| `/bitrix/handler`, `/bitrix/install`, `/bitrix/placement` | `bitrix-local-app:8080` | public HTTPS |
| exact `/health/live`, `/health/ready` | `bitrix-local-app:8080` | по умолчанию не публикуются; только при явно выбранной ops/monitoring policy |
| `/` | static SPA либо Expo dev upstream | `try_files` fallback |
Notification paths внутри `/api/` имеют отдельные edge-зоны. `/internal/`, `/_internal/`, Redis/OTLP/admin/status/config files → `404` (arch-08). `/bitrix/sync/webhook/*` на ВМ1 не маршрутизируется.
## 3. Upstreams
Именованные upstream: `api_backend`, `keycloak`, `sms_service`, `bitrix_local`, опционально `frontend_dev`.
Upstream failures: `502/504` с безопасным body и `X-Request-ID`. Custom JSON error допустим для `/api`, но не имитирует backend domain code. Fallback в SPA запрещён.
## 4. Listeners и TLS
Public host ВМ1: `:80` только ACME + `308 https://$host$request_uri`; `:443 ssl http2` по arch-08. Собственный сертификат, не разделяется с ВМ2.
## 5. WebSocket
Только exact `location = /api/v1/realtime`:
- `proxy_http_version 1.1`;
- `Upgrade $http_upgrade`, `Connection` через `map`;
- buffering и response cache выключены;
- read timeout больше ping interval (ориентир 75 с), send timeout bounded;
- rate limit handshake и `limit_conn` на IP;
- query `access_token` вырезается/редактируется из логов;
- subprotocol передаётся;
- при shutdown nginx позволяет grace reconnect, frontend восстанавливается polling.
## 6. Timeouts ВМ1
Ориентиры arch-08 §6. Обязательно:
- message POST read timeout ≥ `MESSAGE_SAFETY_TASK_POLL_MAX_SEC + 30s`; при default safety max 300 сек — не меньше 330 сек;
- значение из env template до startup.
`client_max_body_size` global 8m; JSON API locations строже, где возможно. Байты вложения через nginx не идут.
## 7. Edge rate limits ВМ1
Зоны `limit_req_zone` по binary remote address:
- `auth`: `NGINX_RATE_LIMIT_AUTH`, малый burst, без большого nodelay;
- `public`: config/content, 60/min/IP;
- `api`: общий API;
- `polling`: GET messages fallback;
- `downloads`: issuance URL;
- `notifications_read`: list/counter/detail;
- `notifications_action`: read/hide/CTA/button;
- `notification_upload`: универсальные upload drafts;
- `notifications_public`: guest notifications и каталог видов;
- `bitrix_callbacks`: мягкий burst для повторов local app;
- `idgtl_callbacks`: отдельный bounded burst, учитывающий повтор каждые 5 минут в течение суток;
- `ws_connect`: handshake;
- `connections`: `limit_conn`.
Ответ превышения — `429`, `Retry-After`, request id. OPTIONS не должен расходовать auth budget чрезмерно. Resource/FD limits учитывают WS.
## 8. Static SPA и dev mode
Production:
- root `${FRONTEND_STATIC_PATH}`;
- volume `frontend-static``/usr/share/nginx/html:ro`;
- существующие hashed assets — `Cache-Control: public, max-age=31536000, immutable`;
- `index.html`, manifest/service worker — `no-cache` либо короткая revalidation;
- `try_files $uri $uri/ /index.html`;
- dotfiles, source maps (если не предназначены), config/env artifacts запрещены;
- API/Bitrix/auth/internal locations объявлены до SPA и никогда в неё не fallback.
Dev: при `FRONTEND_DEV_PROXY_ENABLED=true` `/` проксируется на allow-listed `EXPO_DEV_SERVER_URL`, с WS/HMR. Этот режим запрещён при `APP_ENV=production-like|production`; startup template validator fail-fast.
## 9. Public caching
`GET /api/v1/public/app-config` и `/content` кэшируются только для GET/HEAD, с key `scheme+host+uri+accept-encoding` (и locale query, если контракт его использует). Backend `Cache-Control`/ETag учитываются. Базовый TTL — `security.public_cache.max_age_seconds`/3600.
- `Set-Cookie` не кэшируется;
- Authorization request bypass cache;
- stale-if-error допускается ограниченно и маркируется `Warning`;
- mutation, auth, Bitrix, profile, dialogs, downloads и errors не кэшируются;
- cache status пишется в log, но наружу технологический header опционален.
## 10. CORS, CSP и security headers
CORS — exact allow-list из согласованного deploy config; application CORS остаётся последней инстанцией. Wildcard с credentials запрещён. Allowed headers: `Authorization`, `Content-Type`, `X-Request-ID`, `X-Ux-Session-Id`, `Idempotency-Key`, `traceparent`; методы соответствуют OpenAPI. Preflight получает bounded max-age.
Для SPA:
- CSP default-src `'self'`;
- connect-src `'self'` `https:` к разрешённому S3 endpoint и `wss:` текущего host;
- img-src `'self' data: blob:` и разрешённые signed HTTPS resources;
- object-src `'none'`, base-uri `'self'`, frame-ancestors `'none'`;
- frame-src `'none'`: инструкция `install_app` всегда открывается в новой вкладке, iframe/модалка не поддерживается;
- script-src без `unsafe-eval` production; nonce/hash при необходимости;
- style-src policy согласовать с Expo build, постепенно исключить unsafe-inline.
Для Keycloak login endpoints под `/auth/` применяется отдельный CSP, не SPA-policy. При `KEYCLOAK_YANDEX_CAPTCHA_ENABLED=true` он точечно разрешает `https://smartcaptcha.cloud.yandex.ru` и необходимые static resources `https://yastatic.net` только в соответствующих directives; wildcard и ослабление CSP остальных `/auth/*` запрещены. При выключенной CAPTCHA эти origins отсутствуют. Env validator обязан согласовать CAPTCHA flag, CSP allow-list и ограниченный egress Keycloak.
Также: `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy`, frame protection через CSP, корректный COOP/CORP без поломки Keycloak redirect/S3.
Bitrix placement может требовать embedding: для exact `/bitrix/placement` CSP `frame-ancestors` задаётся отдельным allow-list Bitrix24, а не ослабляет SPA.
## 11. Callback i-Digital Direct
- Только exact `location = /callbacks/idgtl/sms`; разрешён только `POST`, остальные методы отклоняются.
- Source IP allowlist — `185.203.96.7`, но значение обязательно повторно сверяется с актуальной документацией Direct перед production. При WAF/LB используется только нормализованный trusted client IP.
- TLS обязателен; cache выключен; body size ограничен под массив callback items.
- Basic `Authorization` передаётся `sms-service`, но никогда не записывается в access/error logs. URL с credentials также редактируется.
- Nginx не проверяет provider payload и не преобразует статусы; это делает `sms-service`. Ошибку upstream/DB нельзя маскировать `2xx`, иначе Direct не повторит callback.
- `/internal/sms/*` и порт sms-service наружу не публиковать.
## 12. Health и synthetic ВМ1
Внутренний `/nginx-health/live` — arch-08 §8. Внешняя synthetic проверка отдельно проверяет TLS, redirect, public API, auth discovery и SMS callback route. Upstream `/health/ready` local app не публикуется без решения ops.
## 13. Layout и Compose ВМ1
Каркас arch-08 §10 плюс snippets `websocket.conf`. Volume `frontend-static` только на ВМ1.
`nginx` публикует `${NGINX_HTTP_PORT}:80`, `${NGINX_HTTPS_PORT}:443`. Private `8443` на ВМ1 нет.
## 14. Failure behavior ВМ1
Дополнительно к arch-08 §12:
- API/Keycloak upstream down: bounded 502/504, без SPA fallback.
- Safety slow: nginx ждёт message budget ≥ max+30, затем 504; backend checkpoint продолжает recovery.
- Redis down не влияет на запуск nginx; app решает degraded policy.
Остановленный nginx ВМ1 не должен ломать приём CRM webhook на ВМ2.
## 15. Валидация и тесты ВМ1
Команды acceptance (подставить `<PUBLIC_HOST>` ВМ1):
```text
docker compose config
docker compose exec nginx nginx -t
curl -I http://<PUBLIC_HOST>/
curl -vk https://<PUBLIC_HOST>/api/v1/public/app-config
openssl s_client -connect <PUBLIC_HOST>:443 -servername <PUBLIC_HOST>
curl -i https://<PUBLIC_HOST>/internal/safety/v2/messages/check
```
Ожидания: HTTP → 308; public config 200; internal Safety снаружи `404`; valid cert.
Автоматические тесты:
- Test::Nginx/containers для route precedence, methods, 404 internal;
- TLS scan: только 1.2/1.3, chain/hostname/expiry;
- redirect и ACME challenge;
- request-id valid/invalid/generation/propagation;
- forwarded spoof rejection;
- WS handshake, ping idle и reconnect;
- message request длительнее safety max не обрывается до budget;
- body/header limits;
- rate zones/429/Retry-After, включая notification read/action/upload/public;
- public cache HIT/MISS/bypass/no private cache;
- CSP/CORS preflight и Bitrix placement exception;
- CSP содержит `frame-src 'none'`; инструкция проверяется как новая вкладка без embedded content;
- `/internal/notifications/*` снаружи всегда `404`;
- allowed Direct callback проходит; wrong IP/method и любой `/internal/sms/*` отклоняются; Authorization отсутствует в логах;
- CRM webhook paths на ВМ1 не проксируются в `bitrix-sync`;
- upstream down/timeout, failed reload, renewal rehearsal;
- logs не содержат secrets/query tokens.
## 16. Definition of Done ВМ1
Дополнительно к arch-08 §13:
- routing matrix §2 и route precedence покрыты;
- WS работает на `/api/v1/realtime`;
- message timeout равен safety max + минимум 30 секунд;
- limits, public cache, CSP/CORS/security headers проверены;
- static production и dev proxy guard работают;
- SMS callback allow-list/method/redaction проверены;
- internal Safety/sync снаружи `404`.
## 17. TBD ВМ1
- N1: доверенные WAF CIDR перед ВМ1.
- N3: нужен ли публичный health.
- N4: точный CSP Expo build.
- N5: Bitrix frame ancestor domains.
- N6: финальные burst/connection limits зон ВМ1.
## 18. Ссылки
- Контракт: [`arch-08-nginx.md`](../../architectory/arch-08-nginx.md).
- ВМ2: [`module-03-nginx-vm2.md`](../../VM2_services/documentation/module-03-nginx-vm2.md).
- Указатель: [`module-03-nginx.md`](module-03-nginx.md).
- API / local app / SMS / Keycloak: [`module-01-api-backend.md`](module-01-api-backend.md), [`module-06-bitrix-local-app.md`](module-06-bitrix-local-app.md), [`module-11-idgtl-sms.md`](module-11-idgtl-sms.md), [`module-08-keycloak.md`](module-08-keycloak.md).