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

14 KiB
Raw Permalink Blame History

module-03-vm1. Nginx ВМ1 HAN Chat

Статус: целевая спецификация nginx на ВМ1.
Канонический контракт (TLS/ACME, request id, internal 404, logs, reload) — arch-08-nginx.md.
Обязательный host/container hardening baseline — arch-06-service-hosting-security.md.
Контур ВМ2 — 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):

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. Ссылки