# module-03-vm2. Nginx ВМ2 Processing > Статус: целевая спецификация nginx на ВМ2. > Канонический контракт (TLS/ACME, request id, internal 404, logs, reload) — [`arch-08-nginx.md`](../../architectory/arch-08-nginx.md). > Контур ВМ1 — [`module-03-nginx-vm1.md`](../../VM1_app/documentation/module-03-nginx-vm1.md). Guest API, SPA, auth и SMS callback на этой машине не публикуются. ## 1. Назначение и границы Nginx ВМ2 — самостоятельный ingress Processing: - public `80/443` на отдельном DNS host: ACME/redirect и два exact CRM webhook; - private `8443` с сертификатом internal CA: только server-to-server Message Safety и approved ops. Публичный трафик ВМ2 не проходит через ВМ1. Отказ nginx ВМ1 не прерывает приём CRM webhook. SPA, `/api/`, `/auth/`, WS и Direct SMS callback здесь отсутствуют. ## 2. Routing matrix ### Public `80/443` | Внешний путь | Upstream | Режим | |---|---|---| | exact `/bitrix/sync/webhook/contact` | `bitrix-sync:8080` | public HTTPS Contact event; source IP CIDR/method/body/rate limits, query-token auth в upstream | | exact `/bitrix/sync/webhook/alert` | `bitrix-sync:8080` | public HTTPS smart-process event; те же ограничения | До full sync cutover оба exact webhook route закрыты либо возвращают retryable `503`; успешный `2xx ignored` запрещён. Query не участвует в exact location matching: URL штатного робота `/bitrix/sync/webhook/?token=...&ID=...` попадает в соответствующий exact route. `/internal/`, `/_internal/`, Redis/OTLP/admin/status/config files на public host → `404`. Fallback на ВМ1 или SPA запрещён. Прочие paths/methods → `404/405`. ### Private `8443` | Path | Local upstream | Caller | |---|---|---| | `/internal/safety/v2/*` | `message-safety-api:8080` | api-backend ВМ1 | | exact `/internal/safety/status` | `message-safety-api:8080/health/ready` | api-backend ВМ1, ops; capability snapshot, не correctness gate | | `/internal/sync/v1/*` | `bitrix-sync:8080` | ops/allow-listed service | Public и private server blocks не имеют общего fallback. Safety status alias доступен только на private listener; public `/health/ready` и `/internal/safety/status` не публикуются. ## 3. Webhook allow-list и preflight До proxy nginx проверяет непосредственный source IP по version-controlled `BITRIX_WEBHOOK_ALLOWED_CIDRS`; пустой/невалидный список при enabled receiver блокирует deployment. Адрес из недоверенного `X-Forwarded-For` не используется. `BITRIX_SYNC_ENABLED`, public route, readiness и allow-list согласуются одним preflight: disabled требует `deny all;`, enabled — reviewed non-empty CIDR и ready receiver. Обратные комбинации блокируют deployment. Запрос вне allow-list получает generic `403` без proxy. В безопасном журнале с ограниченным retention сохраняются только timestamp, source IP, route class и outcome; query/body не сохраняются. Telemetry pipeline экспортирует `webhook_rejected_total{receiver,reason="source_ip"}` без IP label. Allow-list не расширяется автоматически: всплеск Contact, восстановленных инкрементальной reconciliation, инициирует проверку rejected-IP журнала, подтверждение принадлежности адреса Битрикс24 и reviewed reload конфигурации. Bitrix webhook retries имеют отдельный достаточный burst, проходят source IP allow-list и проверяют query receiver token в сервисе. ## 4. Private `8443` fail-closed До утверждённого Safety cutover active caller allow-list содержит только `deny all;`; после cutover он совпадает с SG/host-firewall источниками ВМ1. Расхождение любого из трёх слоёв (nginx allow-list, SG, host firewall) блокирует rollout. Private listener доверяет forwarded headers только от allow-listed private caller. ## 5. Listeners и TLS - `:80` — только ACME challenge и `308 https://$host$request_uri`; - исключение: `/bitrix/sync/webhook/contact|alert` на HTTP возвращают generic `404/426` **без redirect и отражения query token**; - `:443` — только ACME-совместимый TLS и exact CRM webhook; - private `:8443` — internal CA, SAN совпадает с private DNS именем ВМ2; - public и private сертификаты разные; public key недоступен приложению. ## 6. Timeouts и body limits ВМ2 CRM webhook: connect/send/read `3s / 30s / 60s` (arch-08). Private Safety не короче caller budget. JSON body webhook ограничен; file bytes через этот nginx не идут. Cache на webhook выключен. ## 7. Edge rate limits ВМ2 Отдельные зоны для двух exact webhook (мягкий burst под retries Битрикс24) и `limit_conn`. Зоны guest API/auth/WS/SPA/SMS на ВМ2 не создаются. Ответ превышения — `429`, `Retry-After`, request id. Generic `403` для wrong source IP — до proxy, не `429`. ## 8. Security headers Public webhook: не кэшировать, не ослаблять CSP ради SPA (SPA нет). `Server` / `X-Powered-By` скрыты (arch-08). CORS wildcard с credentials запрещён; webhook не требует browser CORS. ## 9. Health и synthetic ВМ2 Внутренний `/nginx-health/live` — arch-08 §8. Synthetic отдельно: HTTP redirect/ACME policy, internal 404 на public host, GET webhook `405/404`, valid отдельный cert, POST webhook allowed/denied source IP без помещения query token в shell history или логи. Private `8443` проверяется с VM1/ops host, не из интернета. ## 10. Layout и Compose ВМ2 Каркас arch-08 §10 плюс: - отдельный template/server block private `8443`; - snippet/allow-list `BITRIX_WEBHOOK_ALLOWED_CIDRS`; - без `websocket.conf` и без `frontend-static`. Public ports: `${NGINX_HTTP_PORT}:80`, `${NGINX_HTTPS_PORT}:443`. Private `8443` публикуется только в сеть/SG, разрешённую ВМ1 и ops — не как internet listener. Детали портов/SG — arch-03 / arch-06 / [`arch-10-deployment.md`](../../architectory/arch-10-deployment.md). ## 11. Failure behavior ВМ2 Дополнительно к arch-08 §12: - rollback sync: закрыть public webhook routes либо вернуть retryable `503`; возврат к фиктивному `202 ignored` запрещён; - ВМ1 при этом не изменяется; - Safety slow на private `8443`: не маскировать ошибку `2xx`; caller ВМ1 держит poll budget. ## 12. Валидация и тесты ВМ2 Автоматические тесты: - CRM webhook exact routes принимают query без изменения location matching; - allowed source IP проксируется, wrong IP получает `403` до upstream; - HTTP webhook URL с query token не перенаправляется на HTTPS и не отражает query в `Location`/error; - source-IP rejects попадают в безопасный bounded-retention журнал и low-cardinality telemetry без query/body/IP label; - disabled/`deny all` и enabled/ready/CIDR preflight: обратные комбинации блокируют deployment; - public `/internal/safety/*` и `/internal/sync/*` → `404`; - private exact `/internal/safety/status` возвращает capability readiness Safety, а соседние неразрешённые health paths → `404`; - private `8443` до cutover `deny all`, после cutover только источники ВМ1; - GET webhook `405/404`; прочие paths `404`; - TLS scan public host и отдельная проверка internal CA на `8443`; - ACME/redirect rehearsal без остановки приёма (кроме контролируемого окна); - logs/traces не содержат query token, form body, Authorization. Acceptance не использует public host ВМ1 и не требует running nginx ВМ1. ## 13. Definition of Done ВМ2 Дополнительно к arch-08 §13: - независимые public `80/443` и private `8443`; - routing matrix §2 покрыта; - CRM webhook достигает ВМ2 напрямую и продолжает приниматься при остановленном nginx ВМ1; - CRM webhook ограничен version-controlled source IP CIDR allow-list; query token и form body отсутствуют в access/error logs и traces; - preflight `BITRIX_SYNC_ENABLED` согласован с route/allow-list/readiness; - private Safety не опубликован на public host. ## 14. TBD ВМ2 - N1: доверенные WAF/LB CIDR перед public host ВМ2, если появятся; до этого allow-list — непосредственный source IP. - N3: нужен ли публичный health на processing host. - N6: финальный burst webhook зон. ## 15. Ссылки - Контракт: [`arch-08-nginx.md`](../../architectory/arch-08-nginx.md). - ВМ1: [`module-03-nginx-vm1.md`](../../VM1_app/documentation/module-03-nginx-vm1.md). - Указатель: [`module-03-nginx.md`](module-03-nginx.md). - Safety / sync / observability: [`module-05-message-safety.md`](module-05-message-safety.md), [`module-07-bitrix-sync.md`](module-07-bitrix-sync.md), [`module-09-observability-vm2.md`](module-09-observability-vm2.md). - Деплой: [`module-10-deployment-vm2.md`](module-10-deployment-vm2.md).