Реализация на отдельных двух машинах с протестированным взаимодействием по проверке сообщений
This commit is contained in:
@@ -91,7 +91,6 @@ networks:
|
||||
|
||||
volumes:
|
||||
redis-data:
|
||||
nginx-certs:
|
||||
```
|
||||
|
||||
Root Compose ВМ2 включает собственный nginx с public/private server blocks, Message Safety API/worker, `clamd`/`freshclam`, `bitrix-sync`, Redis Safety и локальный OTEL Collector. Секреты, сети и volumes двух projects не общие.
|
||||
@@ -103,6 +102,7 @@ Root Compose ВМ2 включает собственный nginx с public/priva
|
||||
- Публикация портов наружу (`ports:`) разрешена **только** для `nginx` (80/443). Все остальные сервисы используют `expose:` для внутренних портов и общаются через Docker-сети.
|
||||
- `bitrix-local-app` не публикует `8080` на хост (даже на `127.0.0.1`) — он доступен `api-backend` и `nginx` через сеть `backend`/`public`. Ранее применявшийся `127.0.0.1:8080:8080` считаем устаревшим; проверки через curl на `127.0.0.1:8080` заменяются на `docker compose exec bitrix-local-app` или прокси через `nginx`.
|
||||
- Каждый сервисный compose-файл должен запускаться и в составе корневого контура, и автономно (`docker compose -f bitrix-local-app/docker-compose.yml up`) для локальной разработки сервиса — при условии, что переменные окружения заданы. Для автономного запуска сервис может объявлять заглушки сетей/volumes, но в составе корневого контура они переопределяются общими.
|
||||
- При сборке образов нужно добавлять защиту на CRLF → LF
|
||||
|
||||
### Обязательный container hardening
|
||||
|
||||
@@ -176,7 +176,7 @@ docker compose exec api-backend ruff format .
|
||||
|
||||
Требования:
|
||||
|
||||
- публикует наружу только `80` и `443` (см. политику HTTP ниже);
|
||||
- публикует в internet только `80` и `443` (см. политику HTTP ниже); private `8443` ВМ2 публикуется только в VPC/SG для ВМ1 и ops;
|
||||
- принимает внешний HTTPS-трафик;
|
||||
- выполняет TLS termination на reverse proxy; внутренний HTTP между контейнерами — только в закрытой Docker-сети `backend`;
|
||||
- non-root nginx получает writable tmpfs только для `/etc/nginx/conf.d`,
|
||||
@@ -196,13 +196,7 @@ docker compose exec api-backend ruff format .
|
||||
- при recreate/смене IP upstream действует та же post-ready reload policy либо
|
||||
используется явно протестированный dynamic resolver; stale IP/DNS в
|
||||
загруженной nginx config недопустим;
|
||||
- **политика HTTP/HTTPS по доменам** (каноническое правило — [`arch-01-system-architecture.md`](arch-01-system-architecture.md), «Принципы безопасности»):
|
||||
- **веб-домен** (frontend, SPA, статика): `listen 80` допускается **только** для безусловного редиректа `301`/`308` на HTTPS; обработка бизнес-логики по HTTP запрещена;
|
||||
- **API-домен** (если выделен отдельный host, напр. `api.example.ru`): **не** слушает порт `80`; только `listen 443 ssl`; HTTP-запросы к API-домену недоступны;
|
||||
- **единый домен MVP** (напр. `tohin.ru` с путями `/api/*`, `/auth/*`, web): считается веб-доменом; порт `80` — только redirect на HTTPS для всего server block; после редиректа весь пользовательский трафик — HTTPS;
|
||||
- **auth** на том же host, что API (`/auth/*`): следует политике host (redirect-only на :80 или HTTPS-only для выделенного API-host);
|
||||
- **Bitrix local-app callbacks ВМ1** (`/bitrix/handler|install|placement`): только HTTPS; порт `80` — только redirect;
|
||||
- **CRM sync webhook ВМ2** (exact `/bitrix/sync/webhook/contact|alert`): только HTTPS на отдельном processing host; HTTP не отражает query token в redirect;
|
||||
- применяет каноническую HTTP/HTTPS policy из [`arch-08-nginx.md`](arch-08-nginx.md): web/единый MVP host использует только `308` на HTTPS, выделенный API host не слушает `:80`; единственное route-specific исключение — HTTP exact CRM webhook ВМ2 возвращает `404/426` без redirect и без отражения query token;
|
||||
- маршрутизирует `/api/*` в `api-backend` (включая WebSocket upgrade для `/api/v1/realtime`);
|
||||
- маршрутизирует `/auth/*` в `keycloak` или проксирует отдельный auth-домен;
|
||||
- маршрутизирует публичные `/bitrix/*` endpoint в `bitrix-local-app`;
|
||||
@@ -415,16 +409,19 @@ Identity provider. **Обязателен** в compose-контуре с пер
|
||||
|
||||
## Volumes
|
||||
|
||||
Минимальные volumes:
|
||||
Минимальные persistent volumes:
|
||||
|
||||
- ВМ1: Redis DB0/DB1 data, public TLS/ACME, local OTEL queue;
|
||||
- ВМ1: Redis DB0/DB1 data, local OTEL queue;
|
||||
- ВМ2: Redis Safety data (rebuildable), ClamAV signatures и runtime,
|
||||
internal TLS secrets, local OTEL queue.
|
||||
|
||||
Public TLS и ACME на ВМ2 — не named volumes: Compose монтирует read-only host
|
||||
staging `/var/lib/han-chat/public-tls` и ACME webroot
|
||||
`/var/lib/han-chat/acme`. Internal TLS certificate/key передаются отдельными
|
||||
Compose secrets и не объединяются с public TLS.
|
||||
Public TLS и ACME на обеих VM — не named volumes. Root-only ACME state остаётся
|
||||
на host в `/etc/letsencrypt`; root hook атомарно копирует только нужные
|
||||
`fullchain.pem`/`privkey.pem` в `/var/lib/han-chat/public-tls`. Compose
|
||||
монтирует этот staging read-only в nginx, а host webroot
|
||||
`/var/lib/han-chat/acme` — в nginx и ACME client с минимально необходимыми
|
||||
правами. Internal TLS certificate/key передаются отдельными Compose secrets и
|
||||
не объединяются с public TLS.
|
||||
|
||||
Данные PostgreSQL **не** хранятся в Docker volumes — только managed PostgreSQL вне compose.
|
||||
|
||||
@@ -454,9 +451,9 @@ Local OTEL queue на каждой VM использует отдельный pe
|
||||
|
||||
| Host | Порт 80 | Порт 443 | Примечание |
|
||||
|---|---|---|---|
|
||||
| Веб-домен (frontend) | только `301`/`308` → HTTPS | HTTPS, бизнес-логика | MVP: `tohin.ru`; staging/dev может использовать отдельный host |
|
||||
| Веб-домен (frontend) | только `308` → HTTPS | HTTPS, бизнес-логика | MVP: `tohin.ru`; staging/dev может использовать отдельный host |
|
||||
| API-домен (если выделен) | **не слушает** | только HTTPS | Post-MVP: `api.example.ru` |
|
||||
| Bitrix Local App ВМ1 (`/bitrix/handler|install|placement`) | только redirect на web host | HTTPS | install/handler/placement |
|
||||
| Bitrix Local App ВМ1 (`/bitrix/handler|install|placement`) | только `308` на HTTPS | HTTPS | install/handler/placement |
|
||||
| CRM webhook ВМ2 (exact `/bitrix/sync/webhook/contact|alert`) | generic `404/426`, без redirect query token | HTTPS | отдельный processing host |
|
||||
|
||||
Правила:
|
||||
@@ -468,26 +465,12 @@ Local OTEL queue на каждой VM использует отдельный pe
|
||||
|
||||
### TLS и заголовки
|
||||
|
||||
- cookies в web-клиенте: `Secure`, `HttpOnly`, корректный `SameSite`;
|
||||
- OIDC redirect URI в Keycloak — HTTPS;
|
||||
- `KEYCLOAK_PUBLIC_URL`, issuer и frontend auth discovery URL совпадают по схеме, host и path;
|
||||
- backend формирует внешние ссылки с учётом `X-Forwarded-Proto=https`;
|
||||
- HSTS включается в production-like среде **после** проверки доменов и сертификатов;
|
||||
- TLS 1.0/1.1 запрещены; минимум TLS 1.2, предпочтительно TLS 1.3;
|
||||
- слабые шифры запрещены на уровне `nginx`;
|
||||
- `nginx` скрывает `Server`, `X-Powered-By` и аналогичные технологические заголовки;
|
||||
- security headers: `Strict-Transport-Security`, `X-Content-Type-Options`, `Referrer-Policy`, `Content-Security-Policy` для web-приложения;
|
||||
- инструкция по установке всегда открывается новой вкладкой, поэтому CSP SPA задаёт `frame-src 'none'`; allow-list iframe для инструкций отсутствует;
|
||||
- секретный ключ сертификата не коммитится в репозиторий;
|
||||
- использовать сертификаты доверенного CA; автоматизировать выпуск и продление (Let's Encrypt + reload `nginx`);
|
||||
- non-root nginx не монтирует root-only дерево Let's Encrypt целиком:
|
||||
root deploy hook атомарно копирует только `fullchain.pem` и `privkey.pem` в
|
||||
host staging `root:<dedicated-tls-group>` (`0750`, файлы `0640`), а Compose
|
||||
монтирует staging read-only;
|
||||
- reload после renewal выполняется только после `openssl` certificate/key
|
||||
match и полного `nginx -t`; internal TLS PEM также проверяется на raw PEM,
|
||||
отсутствие literal `\n`/double-base64 и совпадение ключа;
|
||||
- закрыть прямой доступ к внутренним портам контейнеров извне.
|
||||
TLS versions, trusted CA, HSTS/security headers, certificate validation и safe
|
||||
reload принадлежат [`arch-08-nginx.md`](arch-08-nginx.md); cookie/OIDC и
|
||||
application security — [`arch-01-system-architecture.md`](arch-01-system-architecture.md).
|
||||
Compose применяет их через host binds из раздела «Volumes», не монтирует
|
||||
root-only `/etc/letsencrypt` в nginx, не объявляет named volume public
|
||||
certificates и не публикует внутренние порты.
|
||||
|
||||
## Nginx routing для Bitrix24 Local App
|
||||
|
||||
@@ -495,7 +478,7 @@ Local OTEL queue на каждой VM использует отдельный pe
|
||||
|
||||
Рекомендуемая схема:
|
||||
|
||||
- **веб-домен** (MVP: `tohin.ru`): `/api/*` (REST + WS realtime), `/auth/*`, web frontend; `:80` → redirect HTTPS; `:443` — TLS + маршрутизация;
|
||||
- **веб-домен** (MVP: `tohin.ru`): `/api/*` (REST + WS realtime), `/auth/*`, web frontend; `:80` → `308` HTTPS; `:443` — TLS + маршрутизация;
|
||||
- **выделенный API-домен** (post-MVP, опционально): отдельный `server { listen 443 ssl; ... }` **без** `listen 80`; только `/api/*`;
|
||||
- для `location` WebSocket (`/api/v1/realtime`): `proxy_http_version 1.1`, `Upgrade`/`Connection` headers, увеличенный `proxy_read_timeout`;
|
||||
- только `/bitrix/handler`, `/bitrix/install`, `/bitrix/placement` на ВМ1 → `bitrix-local-app`; CRM `/bitrix/sync/*` на этом host не маршрутизируется;
|
||||
@@ -576,7 +559,7 @@ WAF не заменяет обязательные лимиты, валидац
|
||||
|
||||
Минимальные проверки:
|
||||
|
||||
- `nginx`: на веб-домене — `301` с `:80` на HTTPS; на API-домене (если выделен) — `:80` не слушает; `:443` — HTTP 200/301 и успешная TLS handshake;
|
||||
- `nginx`: на веб-домене — `308` с `:80` на HTTPS; на API-домене (если выделен) — `:80` не слушает; `:443` — успешная TLS handshake и ожидаемый route response;
|
||||
- `api-backend`: `/health/live` проверяет процесс; `/health/ready` проверяет PostgreSQL `han_app`, Redis DB0/DB1, JWKS/discovery Keycloak и S3 permissions. Недоступность remote Message Safety отражается как degraded dependency и блокирует только send path, но не readiness read API;
|
||||
- `message-safety`: `/health/ready` возвращает process/core status и capability map `text|links|files|worker`; ClamAV/S3 не выключают text, DNS не выключает text без ссылок, Redis hot cache не является core gate;
|
||||
- `bitrix-sync`: `/health/live` проверяет процесс; `/health/ready` проверяет validated config/secrets, PostgreSQL/grants, worker/limiter state и CRM webhook config; invalid credential/config даёт not-ready, краткая CRM outage — degraded по stale policy; при `BITRIX_SYNC_ENABLED=false` ready возвращает not-ready `sync_disabled`;
|
||||
@@ -635,7 +618,7 @@ endpoint обязан fail-closed при недоступной требуемо
|
||||
1. ВМ1, ВМ2, SigNoz и managed PostgreSQL находятся в одной private network/VPC; ВМ1 и ВМ2 имеют независимые public DNS/TLS ingress.
|
||||
2. Managed PostgreSQL не имеет public IP; SG разрешает каждой VM только нужные DB roles/schemas.
|
||||
3. На каждой VM отдельный root-owned systemd unit выполняет её root Compose; `deploy` не входит в `docker`.
|
||||
4. Public nginx ВМ2 публикует `80/443`; `80` используется только для ACME/redirect, `443` — только exact CRM webhook. Private `8443` разрешён только от SG ВМ1 и ops для Message Safety/internal access.
|
||||
4. Public nginx ВМ2 публикует `80/443`; `80` обслуживает ACME, а HTTP exact CRM webhook возвращает `404/426` без redirect; `443` публикует только exact CRM webhook. Private `8443` разрешён только от SG ВМ1 и ops для Message Safety/internal access.
|
||||
5. Deploy/cutover ВМ2 не требует изменения public routes ВМ1. Для `bitrix-sync` rollback закрывает webhook routes на nginx ВМ2 либо возвращает retryable `503`, останавливает claims и сохраняет durable tasks/mapping; возврат к фиктивному `202 ignored` запрещён.
|
||||
|
||||
Host firewall обеих VM учитывает post-DNAT semantics `DOCKER-USER`: policy
|
||||
|
||||
Reference in New Issue
Block a user