Phase 1). Phase 2 — custom client domains через cloudflare-custom-hostnames.
Контекст
Multi-tenant pacient portal (см. multi-tenant-fhir-storage). Каждой Organization нужен свой адрес для доступа. Параллельно решается вопрос как клиент потом сможет подцепить собственный домен (patient.pzalab.com). Phase 1 — наш subdomain без участия клиента; Phase 2 — custom client-owned domains.
Рассматривали
- Slug-based
{slug}.portal.bloodgpt.tech(читаемое имя организации). Плюс — узнаваемо, легко произносить. Минус — slug можно угадать (zalab, demo, bgpt-eu и т.д.), что даёт атакующему target-список. Особенно плохо если URL раскрывает имя клиента, который сам это раскрывать не хочет. - Hash-based
{hash}.portal.bloodgpt.tech, гдеhash = sha256(orgId + HOSTING_SECRET).slice(0, 12). Hash непредсказуем без секрета, поэтому работает как proof-of-ownership: сам факт что клиент знает hash означает что мы ему его выдали. Wildcard DNS*.portal.bloodgpt.tech+ wildcard cert уже настроены — новые tenants работают безterraform apply. - Dash-delimited
{hash}-portal.bloodgpt.tech— рассмотрено как формат, но конфликтует с wildcard*.bloodgpt.tech(ловит api, www и прочие сервисы), отброшено.
Выбрали: hash-based на scope .portal.bloodgpt.tech
hash = sha256(orgId + HOSTING_SECRET).slice(0, 12)
// → "a1b2c3d4e5f6" → a1b2c3d4e5f6.portal.bloodgpt.tech
Wildcard инфраструктура:
*.portalDNS record вbloodgpt-stage-eu/project.hcl+bloodgpt-prod-eu/project.hcl*.portal.bloodgpt.techв cert hostnames вcommon-infra/cloudflare-ssl/terragrunt.hcl- Cookies шарятся через
Domain=.portal.bloodgpt.tech(wildcard cookie domain) — никакого token-redirect не нужно для Phase 1.
Почему
- Hash непредсказуем → нельзя enumerate чужие tenants по URL.
- Hash-домен сам по себе подтверждает ownership — отдельная TXT-верификация не нужна.
- Wildcard инфра уже на месте, новые клиенты не требуют infra-изменений.
.portal.bloodgpt.techscope не пересекается с другими bloodgpt-сервисами (api, www, app), поэтому wildcard cert и cookies работают без коллизий.
Ильдар: «слаг не работает сейчас. черех хеш лучше думаю да. может быть типа a1b2c3d4e5f6-portal.bloodgpt.tech?» (после обсуждения wildcard conflict выбрана dot-separated форма)
DDoS-защита
Замечание Евгения — hash-проверка должна происходить дёшево, иначе невалидные host’ы становятся вектором. Решение — 3 уровня защиты:
- CF edge WAF — формат hostname валидируется на edge (regex-check), невалидные отбрасываются до origin.
- Next.js middleware — in-memory
Set<string>допустимых хэшей, обновляется раз в минуту из БД. Невалидный host отбивается за микросекунды без обращения к Postgres. - GKE rate limit — традиционный ingress rate limit как последний слой.
Ильдар: «ЖЕня говорит что это будет опсаность ддоса» (ответ — 3-level defense, primary = CF edge + hash format validation)
Следствия
- Новый tenant видит свой URL мгновенно после создания Organization (без infra-pipeline).
- Phase 2 (custom client domains типа
patient.pzalab.com) — отдельный путь через Cloudflare Custom Hostnames + Transform Rule, см. cloudflare-custom-hostnames и custom-domains-saas. В Phase 2 потребуется token-redirect для cross-domain cookies (см. [Н5] в digest). - WorkOS auth: Phase 1 использует один
redirect_uri+state-parameter с return_to. Phase 2 потребует доп. шаг token-exchange. - Документация для клиента в Phase 2 — “добавьте 1 CNAME
patient.yourdomain.com → {hash}.portal.bloodgpt.tech”, остальное автоматически.
Связано
- custom-domains-saas — concept-страница (общий процесс)
- cloudflare-custom-hostnames — Phase 2 default pattern
- multi-tenant-fhir-storage — общий multi-tenant контекст
- workos — auth flow для tenant-domains
Источники
Источники: 1.
Сноски
-
Cloudflare Custom Hostnames docs, accessed 2026-05-17, https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/. ↩