К содержимому
OneKYC
Админ-панель

Хранилище (BYOB)

Обновлено:

Статус: Доступно с 2026-04-22.

Раздел Хранилище позволяет тенанту подключить собственный объектный сторадж (S3-бакет, S3-совместимый бакет, GCS-бакет или Azure Blob-контейнер) и хранить документы аппликантов / KYB-материалы / селфи в нём, а не в общем хранилище OneKYC. Эта возможность называется BYOB — Bring Your Own Bucket.

По умолчанию все тенанты используют общее хранилище OneKYC (режим shared). Подключение собственного бакета — опциональная функция, доступная без миграции данных и без даунтайма.

Когда использовать BYOB

Подключение собственного бакета имеет смысл в следующих случаях:

  • Соответствие требованиям регуляторов / data residency. Документы пользователей должны храниться в конкретной юрисдикции или в инфраструктуре, контролируемой тенантом.
  • Корпоративные политики безопасности. Доступ к данным должен ограничиваться силами команды безопасности тенанта (own KMS keys, own IAM, own audit trail в CloudTrail / Cloud Audit Logs / Storage Analytics).
  • Контроль расходов. Тенант оплачивает хранение напрямую своему облачному провайдеру.
  • Повышенный контроль за жизненным циклом. Самостоятельное управление lifecycle-политиками (archival → Glacier / Coldline / Cool tier, retention).

Если ни один из пунктов не критичен, рекомендуется оставить общее хранилище — оно требует нулевой настройки и обслуживается командой OneKYC.

Поддерживаемые провайдеры

ПровайдерЗначение providerАутентификацияОсобенности
AWS S3aws_s3STS AssumeRole + ExternalIDCross-account роль; OneKYC не хранит долговременные ключи AWS
S3-совместимыйs3_compatStatic AccessKey + SecretKeyCloudflare R2, Backblaze B2, MinIO, Wasabi, IBM Cloud Object Storage
Google Cloud StoragegcsService Account JSONbase64-encoded ключ Service Account
Azure Blob Storageazure_blobAccount Key, ConnectionString или SAS TokenЛюбой из трёх способов

Для AWS S3 используется паттерн AssumeRole + ExternalID — рекомендованный AWS способ предоставления стороннему сервису ограниченного доступа к ресурсам аккаунта без долговременных credentials. ExternalID защищает от атаки «confused deputy».

Создание конфигурации

Через админ-панель

  1. Перейдите в Настройки → Хранилище.
  2. Нажмите «Подключить собственный бакет».
  3. Выберите тип провайдера и заполните параметры (см. ниже).
  4. Сохраните черновик — конфигурация создаётся в статусе pending и пока не используется.
  5. Запустите валидацию (см. раздел «Проверка конфигурации»).
  6. После успешной валидации — активируйте конфигурацию.

Через API

Пример для AWS S3

POST /v1/admin/storage/configs HTTP/1.1
Authorization: Bearer <admin_jwt>
Content-Type: application/json

{
  "provider": "aws_s3",
  "bucket": "my-tenant-onekyc",
  "region": "eu-central-1",
  "aws_role_arn": "arn:aws:iam::123456789012:role/OneKYCStorage",
  "sse_mode": "sse_kms",
  "sse_kms_key_id": "arn:aws:kms:eu-central-1:123456789012:key/abcd-..."
}

В ответе вернётся объект конфигурации со статусом pending и сгенерированный aws_external_id. Этот ExternalID необходимо добавить в условие trust policy IAM-роли на стороне тенанта (см. ниже).

Пример для S3-совместимого хранилища (Cloudflare R2)

POST /v1/admin/storage/configs HTTP/1.1

{
  "provider": "s3_compat",
  "bucket": "tenant-onekyc-r2",
  "region": "auto",
  "endpoint": "https://<account-id>.r2.cloudflarestorage.com",
  "use_path_style": true,
  "access_key_id": "<R2 access key id>",
  "secret_key": "<R2 secret>"
}

Пример для Google Cloud Storage

POST /v1/admin/storage/configs HTTP/1.1

{
  "provider": "gcs",
  "bucket": "tenant-onekyc-gcs",
  "service_account_json": "<base64-encoded SA JSON key file>"
}

Пример для Azure Blob

POST /v1/admin/storage/configs HTTP/1.1

{
  "provider": "azure_blob",
  "bucket": "onekyc-data",
  "endpoint": "<storage account name>",
  "azure_account_key": "<account key>"
}

Поля secret_key, service_account_json, azure_account_key, azure_connection_string, azure_sas_token являются write-only — они шифруются и никогда не возвращаются ни одним из эндпоинтов.

Требуемые IAM-разрешения (AWS)

Trust policy IAM-роли (на стороне тенанта):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::<onekyc account id>:role/onekyc-byob-resolver" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "<aws_external_id из ответа Create>" }
    }
  }]
}

Permission policy IAM-роли:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "s3:PutObject",
      "s3:GetObject",
      "s3:DeleteObject",
      "s3:GetObjectAcl",
      "s3:PutObjectAcl"
    ],
    "Resource": "arn:aws:s3:::<bucket>/*"
  }, {
    "Effect": "Allow",
    "Action": [
      "s3:ListBucket",
      "s3:GetBucketLocation"
    ],
    "Resource": "arn:aws:s3:::<bucket>"
  }]
}

Если включён режим SSE-KMS (sse_mode = "sse_kms"), также потребуется политика на сам KMS-ключ:

{
  "Effect": "Allow",
  "Action": [
    "kms:Encrypt",
    "kms:Decrypt",
    "kms:GenerateDataKey"
  ],
  "Resource": "<sse_kms_key_id>"
}

Проверка конфигурации (Validation Probe)

Перед активацией конфигурация проходит автоматический набор из 14 проверок (10 базовых + 4 production-readiness):

POST /v1/admin/storage/configs/{id}/validate
#ПроверкаЧто проверяет
1head_bucketБакет существует и предоставлены права s3:ListBucket
2put_probeЗагрузка пробного объекта (__probe__/health-*.txt)
3get_probeЧтение того же объекта
4delete_probeУдаление пробного объекта
5presign_getГенерация подписанного GET URL (для отдачи фронтенду)
6presign_postГенерация подписанного POST URL (для прямой загрузки с устройства аппликанта)
7list_objectsЛистинг объектов по префиксу
8category_dispatchКорректное разделение по категориям (documents / selfies / ...)
9health_quickБазовый health-check провайдера
10probe_cleanupУдаление всех созданных пробных объектов
11public_accessАнонимное чтение должно быть запрещено (Block Public Access)
12clock_skewРасхождение часов сервера и клиента не превышает 60 сек
13multipart_uploadКорректная работа multipart-загрузки больших файлов
14copy_objectПоддержка серверного копирования (требуется для миграции shared → tenant)

Дополнительный 15-й probe (sse_enforcement) запускается при sse_mode = "sse_kms" и подсказывает bucket policy для запрета загрузок без шифрования.

Ответ содержит per-probe детализацию:

{
  "data": {
    "report": {
      "results": [
        { "name": "head_bucket", "passed": true, "duration_ns": 145000000 },
        { "name": "put_probe", "passed": false,
          "error": "AccessDenied",
          "action_hint": "Grant s3:PutObject on bucket prefix" }
      ],
      "all_passed": false
    },
    "all_passed": false
  }
}

Для каждой проваленной проверки возвращается action_hint — конкретная подсказка («добавьте s3:PutObject», «снимите Public Access»). Validation возвращает 200 OK и при провале части проб (с all_passed=false) — это позволяет UI показать чек-лист, не парся error envelope.

Активация

После успешной валидации конфигурация переводится в активное состояние:

POST /v1/admin/storage/configs/{id}/activate
X-Confirm-Tenant: <tenant UUID>

Заголовок X-Confirm-Tenant обязателен — он защищает от случайных активаций. Значение должно совпадать с UUID текущего тенанта.

После активации:

  • Все новые загрузки (документы, селфи и т. д.) идут сразу в подключённый бакет тенанта.
  • Исторические данные в общем хранилище копируются в фоне.

Действие фиксируется в журнале аудита (tenant.updated + action=storage_config_activated).

Миграция исторических данных

Активация сразу переключает новые загрузки в подключённый бакет. Файлы, загруженные ранее, остаются доступными, пока OneKYC копирует их в бакет тенанта.

Параметры

  • Rate-limit: 10 МБ/с на тенанта по умолчанию.
  • Проверка целостности: скопированные объекты проверяются после загрузки.
  • Grace period: 30 дней между завершением миграции и удалением копий из общего хранилища.

Состояния задачи миграции

СтатусОписание
pendingЗадача создана, ожидает запуска
runningИдёт копирование
pausedПриостановлено (только через супер-админа)
completedВсе объекты успешно скопированы
failedДостигнут лимит retry; см. last_error
cancelledТенант инициировал Disable во время миграции

Запрос статуса миграции

GET /v1/admin/storage/migration/current

Ответ содержит счётчики total_objects / copied_objects / failed_objects, прогресс в байтах, started_at, completed_at, последнюю ошибку.

Мониторинг здоровья

GET /v1/admin/storage/health

Возвращает последний снимок здоровья хранилища:

ПолеЗначение
statushealthy / degraded / unhealthy
last_check_atВремя последней проверки
last_success_atВремя последней успешной проверки
consecutive_failuresКоличество подряд неуспешных проверок
last_error / last_error_codeПоследняя ошибка
alert_sent_atКогда отправлено уведомление о деградации

Health-check выполняется автоматически в фоне (легковесный probe). При нескольких подряд неуспешных проверках администратор тенанта получает email-уведомление.

Ротация credentials

Если ключи скомпрометированы или подходит срок плановой ротации:

POST /v1/admin/storage/configs/{id}/rotate
Content-Type: application/json

{
  "access_key_id": "<новый AccessKey>",
  "secret_key": "<новый SecretKey>"
}

Для AWS S3 обычно ротируются aws_role_arn и пересоздаётся ExternalID; для GCS — новый service_account_json; для Azure — новый Account Key / SAS Token.

После ротации статус сбрасывается на pending — необходимо снова запустить Validate, и только после успешной валидации конфигурация снова становится активной (Activate).

Отключение

Если тенант хочет вернуться к общему хранилищу:

POST /v1/admin/storage/disable
X-Confirm-Tenant: <tenant UUID>
Content-Type: application/json

{
  "reason": "Описание причины (минимум 10 символов)"
}

Эффект:

  • Конфигурация переводится в статус disabled.
  • Все новые загрузки снова идут в общее хранилище OneKYC.
  • Незавершённая миграция отменяется (статус cancelled).
  • Файлы, уже записанные в бакет тенанта, НЕ удаляются — тенант сохраняет полный контроль над своим бакетом и может удалить их самостоятельно.
  • Эти файлы остаются доступными, пока конфигурация не будет удалена.

Поле reason обязательно (минимум 10 символов) — оно фиксируется в журнале аудита для последующего разбора инцидентов.

Чтобы снова подключить собственный бакет, потребуется создать новую конфигурацию с нуля.

Удаление конфигурации

DELETE /v1/admin/storage/configs/{id}

Удалить можно только конфигурацию в статусе pending, validated или failed. Активную конфигурацию необходимо предварительно перевести в disabled через эндпоинт /disable.

Шифрование credentials

Все секреты (secret_key, service_account_json, ключи Azure) хранятся в БД в зашифрованном виде:

  • Алгоритм — AES-256-GCM (envelope encryption).
  • DEK (Data Encryption Key) выводится из master-ключа OneKYC через HKDF-SHA256 с UUID тенанта в качестве salt и version-tagged info-строки.
  • Master-ключ хранится в переменной окружения STORAGE_MASTER_KEY (не пересекается с ENCRYPTION_KEY).
  • При смене формата шифрования старые ciphertexts продолжают расшифровываться через credentials_version, новые шифруются текущей версией.

Секреты невозможно прочитать из API — даже у супер-админа OneKYC нет интерфейса для их извлечения. В случае утраты — единственный путь восстановления — ротация (/rotate) с новыми credentials.

Часто задаваемые вопросы

Сколько стоит BYOB?

Со стороны OneKYC — бесплатно (опция доступна на любом тарифе). Расходы на хранение и трафик оплачивает тенант напрямую своему облачному провайдеру (AWS, GCP, Cloudflare и т. д.).

Увеличится ли latency загрузок?

Незначительно. OneKYC использует presigned URL — браузер аппликанта загружает файл напрямую в бакет тенанта, минуя API OneKYC. Latency определяется выбранным регионом бакета (рекомендуется выбрать регион, ближайший к большинству пользователей).

Что произойдёт при недоступности AWS / KMS?

Загрузки на тот период вернутся с ошибкой 5xx; OneKYC не «откатывается» автоматически на shared (это нарушило бы compliance-инвариант). При длительной недоступности администратор тенанта может выполнить Disable — и записи снова пойдут в общее хранилище.

Health-check фиксирует деградацию и отправляет email-уведомление при нескольких подряд неуспешных проверках.

Можно ли иметь две активные конфигурации одновременно?

Нет. На тенанта одновременно может быть активна только одна конфигурация. Чтобы переключиться на другой бакет, текущую активную конфигурацию нужно сначала Disable, затем создать новую.

Диагностика

ПроверкаТипичная причина провалаДействие
head_bucketБакет не существует / нет s3:ListBucketПроверьте имя бакета, добавьте права
put_probeНет s3:PutObjectПроверьте permission policy
get_probe / delete_probeНет s3:GetObject / s3:DeleteObjectДобавьте права
presign_getEndpoint неверно сконфигурированПроверьте endpoint для S3-compat
public_accessБакет отвечает на анонимные запросыВключите Block Public Access (AWS) / publicAccessPrevention=enforced (GCS) / allowBlobPublicAccess=false (Azure)
clock_skewРасхождение часов превышает 60 секНастройте NTP на стороне сервера-источника
multipart_uploadПровайдер не поддерживает multipart или нет прав на сегментыПроверьте поддержку multipart у провайдера
copy_objectНет s3:CopyObject или копирование между бакетами заблокированоДобавьте права s3:GetObject + s3:PutObject на исходный и целевой бакет

Все ошибки также фиксируются в журнале аудита (storage_config_validated / storage_config_activated / storage_config_disabled).

Устранение неполадок

Прогресс показывает 0 / 0 N с ошибками

Если на свежей активации всё ещё 0 / 0 — значит в общем хранилище нет файлов для миграции, новые загрузки появятся по мере работы. Это ожидаемое состояние для нового тенанта.

Статус Завершено, но строка N с ошибками

Если часть файлов не удалось скопировать, задача переходит в failed. OneKYC автоматически повторяет попытку, а на панели появляется кнопка Повторить миграцию для немедленного ручного перезапуска.

Если задача остаётся failed после нескольких попыток — посмотрите текст last_error на панели. Самые частые причины:

  • signature does not match — в credentials есть пробелы / переводы строк, либо region не совпадает с сегментом URL endpoint. Сделайте rotate, вставьте ключи БЕЗ хвостовых переводов строки.
  • permission denied — bucket policy запрещает s3:PutObject или у IAM-роли нет inline-полиса. Проверьте политику через мастер.
  • bucket not found — бакет был удалён или переименован вне OneKYC.

Расшифровка кодов здоровья

Health-панель показывает короткий код ошибки и локализованную подсказку. Полная таблица:

КодЗначениеДействие
CONFIG_NOT_FOUNDСтрока конфига отсутствует в БДЗапустите Activate заново
CREDENTIALS_INVALIDДрайвер отклонил credentialsRotate со свежими ключами
EXTERNAL_ID_MISMATCHAWS AssumeRole external ID не совпадаетВставьте External ID в trust policy IAM-роли
TENANT_STORAGE_UNAVAILABLEПровайдер временно недоступенПодождите следующей проверки; если повторяется — обратитесь к провайдеру
PERMISSION_DENIEDBucket policy блокирует операциюСкорректируйте bucket / IAM policy
BUCKET_NOT_FOUNDБакет удалён вне OneKYCСоздайте бакет заново ИЛИ Disable + новый конфиг
SIGNATURE_MISMATCHРасхождение региона или пробелы в ключахСделайте rotate, исправьте поле региона
HEALTH_CHECK_FAILEDОбщий случайСмотрите last_error для деталей

Path-style адресация

S3-совместимые провайдеры используют разные способы адресации. Одни бакеты работают через path-style URL (host/bucket/key), другие через virtual-hosted URL (bucket.host/key).

OneKYC сохраняет выбранное значение и использует его как есть при создании storage provider. Следующий шаг Validate проверяет бакет именно в этом режиме; если провайдер отклоняет способ адресации, вернитесь назад, переключите Use path-style и запустите validation снова.

См. также: Журнал аудита, Настройки тенанта.

На этой странице