# 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 (подставить `` ВМ1): ```text docker compose config docker compose exec nginx nginx -t curl -I http:/// curl -vk https:///api/v1/public/app-config openssl s_client -connect :443 -servername curl -i https:///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).