# BYOC — Bring Your Own Credentials **Дата**: 2026-04-09 **Статус**: частично реализовано (backend), UI/API — TODO --- ## Суть Возможность создать тенанта с **произвольными** Access Key и Secret Key вместо авто-генерируемых. Нужно для: - демо-стенда с фиксированными credentials (README всегда актуален) - интеграционных тестов с предсказуемыми значениями - миграции с другого SQS-совместимого сервиса (сохранение существующих ключей) --- ## Что уже сделано ### `app/tenant/tenant_store.go` — `CreateFixed` ```go func (s *TenantStore) CreateFixed( name string, maxQueues int, tenantID string, accessKey string, secretKey string, ) (*Tenant, error) ``` Создаёт тенанта с заранее известными credentials. Проверяет уникальность и `tenantID`, и `accessKey` — конфликт возвращает ошибку. ### `app/cmd/seed.go` — демо-тенант Использует `CreateFixed` при `SHARED_SQS_SEED_DEMO=true`: ``` tenantID = "t-demo-shared-sqs-ngcloud" accessKey = "SSAK-demo-shared-sqs" secretKey = "demo-secret-key-shared-sqs-ngcloud-2026" ``` --- ## Что нужно сделать (TODO) ### Admin API — `POST /admin/tenants` Добавить в `createTenantRequest` два опциональных поля: ```go // app/admin/admin.go type createTenantRequest struct { Name string `json:"name"` MaxQueues int `json:"max_queues"` AccessKey string `json:"access_key,omitempty"` // TODO: BYOC SecretKey string `json:"secret_key,omitempty"` // TODO: BYOC } ``` Логика в `createTenant` handler: ```go var t *tenant.Tenant var err error if req.AccessKey != "" || req.SecretKey != "" { // BYOC: оба поля обязательны if req.AccessKey == "" || req.SecretKey == "" { jsonErr(w, http.StatusBadRequest, "both access_key and secret_key required when specifying custom credentials") return } // Минимальная длина — защита от случайно слабых ключей if len(req.AccessKey) < 8 || len(req.SecretKey) < 16 { jsonErr(w, http.StatusBadRequest, "access_key min 8 chars, secret_key min 16 chars") return } t, err = h.store.CreateFixed(req.Name, req.MaxQueues, generateTenantID(), req.AccessKey, req.SecretKey) } else { t, err = h.store.Create(req.Name, req.MaxQueues) } ``` > `generateTenantID()` — уже есть в tenant_store.go, нужно экспортировать или вынести. ### UI — Web форма создания тенанта - Добавить в модальное окно "Создать тенанта" два опциональных поля: Access Key, Secret Key - Показывать только если нажата кнопка "задать свои credentials" - Валидация на клиенте: оба поля заполнены, мин. длина --- ## Безопасность - BYOC-credentials **не дают доступа к admin API** — admin защищён отдельным Bearer токеном - Тенант видит **только свои очереди** — изоляция по AccessKey в auth middleware - Слабые ключи отклоняются на уровне API (минимальная длина) - Credentials передаются только по HTTPS --- ## Демо-credentials (открыты намеренно) | | | |---|---| | **Access Key** | `SSAK-demo-shared-sqs` | | **Secret Key** | `demo-secret-key-shared-sqs-ngcloud-2026` | | **Tenant ID** | `t-demo-shared-sqs-ngcloud` | | **Лимит очередей** | 10 | Эти credentials жёстко прописаны в `app/cmd/seed.go`. Тенант создаётся только если `SHARED_SQS_SEED_DEMO=true` (env var в deployment.yaml).