13 KiB
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 /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-evalproduction; 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. Ссылки
- Контракт:
arch-08-nginx.md. - ВМ2:
module-03-nginx-vm2.md. - Указатель:
module-03-nginx.md. - API / local app / SMS / Keycloak:
module-01-api-backend.md,module-06-bitrix-local-app.md,module-11-idgtl-sms.md,module-08-keycloak.md.