Реализована интеграция с СМС провайдером

This commit is contained in:
mi
2026-07-23 11:49:15 +03:00
parent cc0163eb94
commit b1ed714d5b
89 changed files with 5934 additions and 202 deletions
+57 -24
View File
@@ -39,6 +39,9 @@ Placeholders:
<RELEASE> immutable tag/git SHA
<ACME_EMAIL> адрес ops, не placeholder в реальном запуске
<BITRIX_PORTAL> разрешённый портал
<IDGTL_SENDER_NAME> согласованное в Direct имя отправителя
<IDGTL_STATIC_EGRESS_IP> фактический статический egress IP `sms-worker`
<IDGTL_TEST_PHONE> контролируемый номер для provider smoke
```
## 3. Stage 0 — решения до provisioning
@@ -72,7 +75,7 @@ Placeholders:
- [ ] RPO/RTO приняты хотя бы временно: ориентир RPO PG ≤15 минут/PITR, RTO ≤4 часа.
- [ ] Решено: images pull из registry или build на VM.
- [ ] Remote telemetry backend выбран либо явно принят ограниченный debug-only режим.
- [ ] Риск mock OTP и Safety stub письменно принят.
- [ ] Риск mock OTP до SMS cutover и Safety stub письменно принят; real SMS не включается без gates module-11.
**Ожидаемый результат:** есть release checklist с конкретными values; не создано ни одной публичной БД/Redis.
@@ -90,7 +93,7 @@ Security groups:
| internet | VM | TCP 80 | allow для redirect/ACME |
| internet | VM | TCP 443 | allow |
| VM private IP/SG | managed PG | `<PG_PORT>` | allow |
| VM | internet | 443 | allow egress: registry, Bitrix, S3, OTLP, ACME |
| VM | internet | 443 | allow egress: registry, Bitrix, S3, OTLP, ACME, i-Digital Direct |
| internet | managed PG | any | deny |
| internet | VM | 6379, 4317, 4318, 8000, 8080, 9000 | deny |
@@ -197,7 +200,7 @@ CA managed PostgreSQL скачать из панели или документа
Прототип `init-managed-postgres.py` выдаёт runtime roles `CREATE` на schema и печатает DSN. Это допустимо только для bootstrap/dev, но **слишком широко для production runtime**. Перед production адаптировать:
1. создать пять schemas: `han_app`, `bitrix_local`, `bitrix_sync`, `keycloak`, `message_safety`;
1. создать шесть schemas: `han_app`, `bitrix_local`, `bitrix_sync`, `keycloak`, `message_safety`, `sms`;
2. создать runtime roles;
3. создать migration roles либо controlled admin job;
4. schema owner = migration role;
@@ -239,14 +242,15 @@ psql "host=<PG_PRIVATE_HOST> port=<PG_PORT> dbname=<PG_DATABASE> user=<RUNTIME_U
2. `bitrix-local-app` Alembic владеет `bitrix_local`;
3. `message-safety` stub не создаёт PG tables до production implementation;
4. `bitrix-sync` stub — optional empty baseline;
5. Keycloak мигрирует standard tables сам; custom provider имеет собственные versioned migrations.
5. Keycloak мигрирует standard tables сам; custom provider имеет собственные versioned migrations;
6. `sms-service` владеет versioned migrations/seed schema `sms`; runtime `sms_user` не имеет доступа к `han_app`/`keycloak`.
Только expand/migrate/contract. Destructive migration — отдельный backup, approval и release. Downgrade data migrations не обещается; rollback приложения требует backward-compatible schema.
### Gate 3
- [ ] Backups/PITR/TLS/deletion protection включены.
- [ ] Пять schemas/roles созданы.
- [ ] Шесть schemas/roles созданы, включая `sms`/`sms_user`.
- [ ] Runtime roles не имеют DDL/чужого доступа.
- [ ] Migration credentials отделены от runtime.
- [ ] Empty/previous-version migration test успешен.
@@ -366,6 +370,7 @@ openssl rand -hex 32
- Redis ACL credentials/URLs DB0/1/2;
- public web/API/auth URLs;
- Keycloak realm/audience/hostname/bootstrap/provider technical secrets;
- SMS DB URL, парные Keycloak↔SMS tokens, Direct `TOKEN_1`, callback URL и отдельные callback credentials;
- paired service tokens из arch-02;
- Bitrix client/application/webhook/encryption secrets;
- S3 endpoint/buckets/API and read-only Safety credentials;
@@ -378,6 +383,7 @@ openssl rand -hex 32
```text
BITRIX_LOCAL_APP_INTERNAL_TOKEN == BITRIX_INTERNAL_API_TOKEN
BITRIX_API_FORWARD_TOKEN == BITRIX_API_INBOX_TOKEN
KEYCLOAK_SMS_SERVICE_TOKEN == SMS_SERVICE_TOKEN
```
Service token и webhook token — разные secrets.
@@ -398,6 +404,7 @@ Service token и webhook token — разные secrets.
- Safety timeout согласован с nginx;
- secrets minimum length;
- mock OTP risk flag explicitly accepted.
- placeholders `change-me`/`<...>` запрещены; real mode требует sender/template/API key/callback credentials и recorded static egress IP;
```bash
cd <BACKEND_ROOT>
@@ -466,12 +473,13 @@ cd <BACKEND_ROOT>
docker compose config --services
```
Ожидаются: `nginx`, `api-backend`, `message-safety`, `keycloak`, `bitrix-sync`, `bitrix-local-app`, `redis`, `otel-collector` и one-shot jobs/profile components.
В целевом real-SMS release ожидаются: `nginx`, `api-backend`, `message-safety`, `keycloak`, `sms-service`, `sms-worker` (либо документированный worker process), `bitrix-sync`, `bitrix-local-app`, `redis`, `otel-collector` и one-shot jobs/profile components.
Networks:
- `public`: nginx и минимально Keycloak/frontend path;
- `backend`: internal services/Redis;
- `egress`: только утверждённые outbound workers; Keycloak в неё не входит, `sms-worker` входит;
- `observability`: services + Collector.
Volumes:
@@ -661,6 +669,25 @@ docker compose run --rm api-backend python -m app.cli.validate_settings
- [ ] Mandatory settings valid; secrets отсутствуют в `app_settings`.
- [ ] Backward compatibility с текущими images подтверждена.
### 13.4. Controlled rollout real SMS
До переключения Keycloak:
1. применить App DB seed `otp.phone.code_length`, `otp.phone.ttl_seconds`, `otp.phone.sms_order_timeout_ms`;
2. создать schema/role `sms`, применить migrations и idempotent seed `sms_setting`/active approved `auth_otp`;
3. в test environment развернуть `sms-service`/worker с локальным mock Direct и выполнить contract/E2E;
4. получить production Direct `TOKEN_1`, согласованные sender и template, отдельные callback credentials;
5. определить egress IP фактическим запросом из `sms-worker`, подтвердить его статичность/NAT, записать в inventory и передать Direct для allowlist;
6. развернуть production `sms-service`/worker и callback route, оставив `KEYCLOAK_OTP_MOCK_ENABLED=true`;
7. применить Keycloak expand migration/SPI, мигрировать старые challenges по module-11;
8. выполнить provider smoke отдельной ops-командой на `<IDGTL_TEST_PHONE>`; проверить journal, callback, redaction и отсутствие duplicate;
9. только после подписанных evidence переключить `KEYCLOAK_OTP_MOCK_ENABLED=false`;
10. проверить durable order до Direct response, resend/superseded, expiry snapshot, limits и verify при provider reject/timeout.
Production cutover запрещён при любом placeholder, несогласованном sender/template, отсутствующем API key/callback credentials, неподтверждённом callback IP или нестатическом egress IP. Direct API key — готовый `TOKEN_1` для Basic, повторно Base64 не кодируется.
Rollback SMS: немедленно вернуть Keycloak в mock mode; не удалять schema/journal и не откатывать migrations без доказанной backward compatibility. Остановить новые real orders, дать worker завершить либо зафиксировать in-flight/`uncertain`; предпочтителен forward-fix.
## 14. Stage 11 — Keycloak bootstrap
### 14.1. Первый старт
@@ -713,22 +740,25 @@ Custom OTP tables мигрируются versioned mechanism до включен
Архитектурный порядок:
1. Redis;
2. Keycloak;
3. OTEL Collector;
4. Message Safety;
5. API backend;
6. Bitrix local app;
7. Bitrix sync;
8. nginx.
2. OTEL Collector;
3. API backend/settings;
4. SMS service/worker после migrations (при SMS release; Keycloak пока mock);
5. Keycloak;
6. Message Safety;
7. Bitrix local app;
8. Bitrix sync;
9. nginx.
Команды:
```bash
cd <BACKEND_ROOT>
docker compose up -d redis
docker compose up -d keycloak otel-collector
docker compose up -d message-safety
docker compose up -d otel-collector
docker compose up -d api-backend
docker compose up -d sms-service sms-worker
docker compose up -d keycloak
docker compose up -d message-safety
docker compose up -d bitrix-local-app bitrix-sync
docker compose up -d nginx
docker compose ps
@@ -827,6 +857,7 @@ Expected: 308; public 200 strict DTO; discovery 200; internal 404; valid cert.
- silent refresh работает без OTP;
- logout очищает tokens;
- wrong/replayed OTP не выдаёт tokens.
- real mode: durable order возвращает `sms_message_id` до Direct response; callback обновляет только SMS journal; resend делает старый challenge `superseded`.
### 17.3. Message Safety правила stub
@@ -955,13 +986,14 @@ DB backup включает realm/users/signing keys/provider data. Secret-free r
### Application-only
1. объявить incident/maintenance;
2. сохранить diagnostics и current state;
3. остановить новые claims/send при возможности;
4. переключить image tags на previous digests;
5. не выполнять Alembic downgrade;
6. `docker compose up -d`;
7. health/smoke/idempotency;
8. проверить outbox/inbox/recovery.
2. при SMS incident вернуть `KEYCLOAK_OTP_MOCK_ENABLED=true`, прекратить новые real orders и сохранить journal/in-flight state;
3. сохранить diagnostics и current state;
4. остановить новые claims/send при возможности;
5. переключить image tags на previous digests;
6. не выполнять Alembic downgrade;
7. `docker compose up -d`;
8. health/smoke/idempotency;
9. проверить outbox/inbox/SMS pending/uncertain/recovery.
### После backward-incompatible migration
@@ -1162,7 +1194,7 @@ certbot delete active cert
- D-A2: обязательный минимум — `otel-collector`; доступность remote backend не предполагается до закрытия D-TBD11, local Grafana stack не обязателен.
- D-A3: managed provider даёт private network, TLS, backups/PITR.
- D-A4: Bitrix portal/connector/line остаются разрешёнными значениями architecture.
- D-A5: mock OTP временно разрешён как documented risk.
- D-A5: mock OTP временно разрешён до controlled SMS cutover как documented risk.
### TBD до production
@@ -1186,8 +1218,9 @@ certbot delete active cert
4. Прототипные TLS scripts используют отдельный service Compose/standalone downtime, тогда как целевая архитектура требует root Compose и two-phase webroot.
5. Prototype публиковал `/bitrix-internal/*` и использовал `/internal/v1/*`; целевой контур это запрещает и использует `/internal/openlines/v1/*`.
6. `arch-04` не содержит ряд proposed env из module-0409; production `.env.example` должен быть синхронизирован до реализации.
7. Точные RPO/RTO, retention, SLO, Keycloak version/TTL и Bitrix retry semantics не утверждены; начальные значения runbook не закрывают product/security decision.
7. Точные RPO/RTO, SLO, Keycloak version и Bitrix retry semantics не утверждены; OTP TTL задаётся `app_settings`, SMS journal по module-11 хранится бессрочно.
8. `init-managed-postgres.py` по умолчанию не задаёт TLS parameters при bootstrap connection и печатает credential-bearing DSN; его production-hardening обязателен.
9. Текущие Compose/env/config artifacts могут ещё не содержать `sms-service`; документация не разрешает real mode до реализации и прохождения rollout gates.
## 29. Ссылки на прототип