Files
2026-08-26 11:05:32 +03:00

216 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `/mobile/oidc/callback` | static nginx | HTTPS-мост Android Custom Tabs → `han-chat://auth/callback` |
| 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).