eID — консоль администратора (панель оператора)¶
Руководство для операторов и разработчиков: консоль администратора платформы eID — это
связка операторской панели на Next.js (admin/, порт 3001) и Go-адмиn API (/v3/admin/*).
Через неё оператор управляет гражданами, устройствами, сертификатами, сессиями, KYC/DAN,
организациями (Legal Person), доверяющими сторонами (RP), журналами аудита и статусом системы.
Отдельное приложение. Консоль администратора — полностью отдельное приложение Next.js относительно
web/(демо RP для граждан, порт 3000). Дерево кода:admin/src/.
1. Чем управляет¶
| Раздел | Возможности |
|---|---|
Граждане (/users) |
Поиск (etsi / РД / имя / documentNumber), детали + устройства + сертификаты + аудит |
Устройства (/devices) |
Список/поиск, детали, деактивация утерянных устройств |
Сертификаты (/certificates) |
Список/поиск, статус, отзыв |
PKI (/pki, /org-ca) |
Состояние CA, пути OCSP/CRL, статус QSCD для e-Seal |
Организации (/organizations) |
Регистрация Legal Person, представители, выпуск сертификата e-Seal |
RP (/rps) |
Регистрация/редактирование/деактивация/реактивация RP, ротация секрета, управление подсистемами |
Согласования (/approvals) |
Правило четырёх глаз: чувствительные операции подтверждает второй администратор |
Аудит (/audit) |
Журнал всех операций + экспорт в CSV (eIDAS/ISO 27001) |
Администраторы (/admins) |
Создание/смена роли/деактивация администраторов (только SUPER_ADMIN) |
Система (/system) |
Версия, uptime, НЕсекретный статус конфигурации/HSM/KYC/PKI |
2. Архитектура — BFF (браузер никогда не видит секрет)¶
Консоль администратора построена по паттерну Backend-for-Frontend (BFF). Браузер НИКОГДА
не видит токен админской сессии — токен хранится только в httpOnly-cookie Next-сервера.
Браузер ──fetch──▶ Next BFF (порт 3001) ──Bearer──▶ Go admin API (/v3/admin/*)
(cookie: /api/login, /api/mfa, middleware adminAuth
admin_session, /api/proxy/[...path] + requireCap (RBAC)
httpOnly)
- Клиент → BFF. Когда страницы вызывают
api("users?q=…"), запрос идёт на маршрут/api/proxy/*(admin/src/lib/client.ts). - BFF → Go. Прокси-маршрут читает токен сессии из cookie и передаёт его как
Authorization: Bearer <token>на${ADMIN_BACKEND_URL}/v3/admin/<path>(admin/src/app/api/proxy/[...path]/route.ts). Токен никогда не попадает в браузер, поэтому его нельзя похитить через XSS. - Защита. Белый список против path-traversal (
[A-Za-z0-9._-], запрещает./..), сверка хоста Origin/Referer сhostна изменяющих запросах (эшелонированная защита от CSRF в дополнение кsameSite=strict) и маскирование upstream-ошибок 5xx общим сообщением. - Guard аутентификации. Layout
(app)вызываетgetMe()на сервере и перенаправляет на/login, если валидной сессии нет (admin/src/app/(app)/layout.tsx). - CSP/заголовки безопасности.
admin/src/middleware.ts— nonce +strict-dynamic,connect-src 'self',frame-ancestors 'none', HSTS и другие.
Граница доверия. На стороне Go маршруты
/v3/admin/*защищены middlewareadminAuth(server/internal/httpapi/server.go). Порядок: (1)Authorization: Bearer <токен сессии>→ полноценный RBAC; (2)X-Admin-Key→ аварийный (break-glass) SUPER_ADMIN (сравнение за константное время); (3) только в профилеdev, еслиSMARTID_ADMIN_API_KEYпуст — открытый SUPER_ADMIN. На staging/prod fail-open недопустим НИКОГДА: обязателен Bearer либо break-glass-ключ.
3. Вход + MFA¶
Сценарий: email/пароль → TOTP (MFA) → токен сессии. При первом входе пользователь регистрирует аутентификатор по QR-коду.
| Шаг | Маршрут BFF | Эндпойнт Go | Описание |
|---|---|---|---|
| 1. Вход | POST /api/login |
POST /v3/admin/auth/login |
Проверка email/пароля; при успехе — токен со scope mfa |
| 2. MFA | POST /api/mfa |
POST /v3/admin/auth/mfa |
Код TOTP → токен со scope session |
| 3. Выход | POST /api/logout |
POST /v3/admin/auth/logout |
Удаление cookie (токен stateless) |
- Пароль. Хешируется и хранится через
internal/admin/password.go; сообщения об ошибках обобщены («неверный email или пароль») для защиты от timing-атак и перебора. - TOTP (MFA). RFC 6238, HMAC-SHA1, шаг 30 секунд, 6 цифр — реализовано своими силами без
внешней библиотеки
(
server/internal/admin/totp.go). При первом входе сервер возвращает URIotpauth://и секрет; страница логина превращает его в QR-код для добавления в Google/Microsoft Authenticator. У кода есть окно ±1 шаг (расхождение часов), а использованный счётчик сохраняется, чтобы блокировать replay (RFC 6238 §5.2). - Токен. Stateless-токен, подписанный HMAC-SHA256 (без JWT-библиотеки),
<b64url(payload)>.<b64url(HMAC)>(server/internal/admin/token.go). Scopemfa— ~5 минут, scopesession— ~1 час. - Cookie.
admin_mfa(5 минут, ожидание MFA) иadmin_session(1 час) — обеhttpOnly,sameSite=strict, в продакшене такжеsecure. - Rate limit. Вход (по IP+email) и MFA (по IP) ограничиваются; при превышении — 429 +
Retry-After. - Смена пароля.
POST /v3/admin/auth/password— вошедший администратор меняет собственный пароль (с подтверждением текущего). Отдельная capability не требуется.
4. RBAC — роль и capability¶
Каждый маршрут объявляет требуемую capability; middleware requireCap проверяет, есть ли
эта capability у роли вошедшего администратора (принцип наименьших привилегий). Матрица:
server/internal/admin/roles.go.
Capabilities: rp:read, rp:write, org:read, org:write, user:read, device:read,
device:revoke, cert:read, cert:revoke, session:read, audit:read, admin:manage,
config:read, config:write.
Роль → capability:
| Роль | Предоставленные capabilities |
|---|---|
SUPER_ADMIN |
все capabilities (включая управление администраторами) |
RP_OPERATOR |
rp:read rp:write org:read org:write user:read session:read audit:read |
SUPPORT |
rp:read org:read user:read device:read device:revoke cert:read session:read audit:read |
AUDITOR |
rp:read org:read user:read device:read cert:read session:read audit:read config:read |
SECURITY_OFFICER |
org:read user:read device:read device:revoke cert:read cert:revoke session:read audit:read config:read |
Если capability недостаточно, Go-сторона возвращает 403 («недостаточно прав для этой операции»). Навигация во фронтенде показывает все страницы, но неавторизованная операция блокируется ответом 403 на бэкенде.
5. Основные возможности и эндпойнты /v3/admin/*¶
Все находятся за adminAuth. Требуемая capability указана рядом.
Гражданин / устройство / сертификат / сессия (чтение)¶
| Метод + путь | Cap | Описание |
|---|---|---|
GET /users?q=&limit=&offset= |
user:read |
Поиск граждан (etsi/РД/имя); если ничего не найдено — поиск владельца по documentNumber |
GET /users/{etsi} |
user:read |
Гражданин + устройства + сертификаты + аудит |
GET /devices?q=&active= |
device:read |
Поиск устройств |
GET /devices/{documentNumber} |
device:read |
Детали устройства |
GET /certificates?q=&status= |
cert:read |
Поиск сертификатов |
GET /users/{etsi}/certificates |
cert:read |
История сертификатов гражданина |
GET /sessions/{sessionId} |
session:read |
Безопасный просмотр сессии |
Секретный материал НИКОГДА не возвращается. Функции представления (
userView/deviceView/certView/sessionView,handlers_admin_read.go) вырезают handle HSM,Enc(x_client), модуль Пайе, секрет/токен сессии и объёмный base64 сертификата, показывая только безопасные поля.
RP (доверяющая сторона)¶
| Метод + путь | Cap | Описание |
|---|---|---|
POST /relying-parties |
rp:write |
Регистрация RP — API-секрет возвращается ТОЛЬКО здесь и ОДИН раз |
GET /relying-parties, GET /relying-parties/{id} |
rp:read |
Список / детали |
PATCH /relying-parties/{id} |
rp:write |
Редактирование |
POST /relying-parties/{id}/deactivate | /reactivate |
rp:write |
Деактивация / реактивация |
POST /relying-parties/{id}/rotate-secret |
rp:write |
Новый секрет (ТОЛЬКО здесь, ОДИН раз) |
Подсистема RP. Подсистема автоматически регистрируется самим RP (find-or-create); администратор может только просматривать / переименовывать / деактивировать / объединять:
| Метод + путь | Cap | Описание |
|---|---|---|
GET /relying-parties/{id}/subsystems |
rp:read |
Список подсистем внутри RP |
PATCH /relying-parties/{id}/subsystems/{sid} |
rp:write |
Изменение отображаемого имени подсистемы |
POST /relying-parties/{id}/subsystems/{sid}/deactivate | /activate |
rp:write |
Деактивация / активация |
POST /relying-parties/{id}/subsystems/{sid}/merge |
rp:write |
Объединение с другой подсистемой |
Полное руководство по интеграции RP: RP_INTEGRATION.md. Модель и протокол подсистем: RP_SUBSYSTEMS.md.
Отзыв сертификатов + PKI¶
| Метод + путь | Cap | Описание |
|---|---|---|
POST /certificates/{serial}/revoke |
cert:revoke |
Отзыв по серийному номеру (OCSP/CRL + статус + деактивация устройства); {reason} 0–10 |
POST /devices/{documentNumber}/deactivate |
device:revoke |
Деактивация утерянного устройства (по умолчанию reason=1 keyCompromise) |
GET /pki/status |
config:read |
Subject/серийный номер/срок действия CA, число отзывов, URL OCSP/CRL, Org CA, QSCD для e-Seal |
KYC / DAN¶
Сценарий KYC в основном проходит через мобильное приложение и callback DAN (/v3/kyc/*,
вне консоли администратора —
server.go mountKyc).
GET /v3/kyc/methods показывает активные методы KYC (dan / gsign / passport / citizenCard).
Блок kyc в системном GET /v3/admin/system отражает НЕсекретный статус провайдера KYC.
Организация (Legal Person)¶
| Метод + путь | Cap | Описание |
|---|---|---|
GET /organizations?q= |
org:read |
Список/поиск |
GET /organizations/{etsi} |
org:read |
Детали + представители |
POST /organizations |
org:write |
Регистрация (PENDING либо сразу ACTIVE) |
PATCH /organizations/{etsi} |
org:write |
Название / статус (FSM) |
POST /organizations/{etsi}/representatives |
org:write |
Добавить представителя |
DELETE /organizations/{etsi}/representatives/{id} |
org:write |
Деактивировать представительство |
POST /organizations/{etsi}/seal-certificate |
org:write |
Выпустить сертификат e-Seal (NTRMN) |
Подключение организации: ORG_ONBOARDING.md.
Аудит + статистика¶
| Метод + путь | Cap | Описание |
|---|---|---|
GET /audit?type=&subject=&limit=&offset= |
audit:read |
Журнал аудита (с пагинацией) |
GET /audit/export?type=&subject= |
audit:read |
Экспорт в CSV (отчёт eIDAS/ISO 27001) |
GET /stats |
audit:read |
Метрики панели + тренды |
Система / статус HSM (только чтение)¶
| Метод + путь | Cap | Описание |
|---|---|---|
GET /system |
config:read |
Версия, uptime, НЕсекретный статус конфигурации security/kyc/pki/hsm/push |
Секреты не раскрываются.
adminSystemInfo(handlers_admin_system.go) НИКОГДА не раскрывает ключи, пароли и API-ключи — он лишь указывает, «настроено ли» (например,swMasterKeyConfigured: true).
Управление администраторами (только SUPER_ADMIN)¶
| Метод + путь | Cap | Описание |
|---|---|---|
GET /admins |
admin:manage |
Список администраторов (представление без секретов) |
POST /admins |
admin:manage |
Новый администратор (email + пароль от 8 символов + роль) |
PATCH /admins/{id} |
admin:manage |
Смена роли |
POST /admins/{id}/deactivate |
admin:manage |
Деактивация (вместо удаления — сохраняет аудит) |
Процесс согласования по правилу четырёх глаз¶
При SMARTID_ADMIN_REQUIRE_4EYES=true чувствительная операция (отзыв сертификата, деактивация
устройства, деактивация RP) не выполняется сразу — она становится запросом в статусе PENDING
(202), который подтверждает другой администратор.
| Метод + путь | Cap | Описание |
|---|---|---|
GET /approvals?status= |
audit:read |
Ожидающие запросы |
POST /approvals/{id}/approve |
(capability самой операции, динамически) | Подтвердить |
POST /approvals/{id}/reject |
(capability самой операции, динамически) | Отклонить |
Подтверждающий обязан (а) обладать capability операции (например, отзыв сертификата → cert:revoke)
И (б) отличаться от инициатора — попытка подтвердить собственный запрос блокируется ошибкой
ErrSelfApproval (403)
(handlers_admin_approval.go).
6. Запуск¶
Фронтенд (консоль администратора):
cd admin
npm run dev # порт 3001 (next dev -p 3001)
npm run build
npm run lint
npm run test # vitest
Одна ключевая переменная окружения: ADMIN_BACKEND_URL (адрес Go-бэкенда,
по умолчанию http://localhost:8080).
Бэкенд (Go admin API) — запускается из server/ (go run ./cmd/smartid).
Переменные окружения для админки/RBAC (server/internal/config/config.go, все с префиксом SMARTID_*):
| Env | Описание |
|---|---|
SMARTID_ADMIN_TOKEN_SECRET |
HMAC-секрет токена сессии (≥16 символов). Если пуст, админская аутентификация отключена |
SMARTID_ADMIN_SEED_EMAIL |
Email первого SUPER_ADMIN (seed при старте, идемпотентный) |
SMARTID_ADMIN_SEED_PASSWORD |
Пароль первого администратора (только для seed — затем обязательно сменить) |
SMARTID_ADMIN_SEED_ROLE |
Роль для seed (по умолчанию SUPER_ADMIN) |
SMARTID_ADMIN_API_KEY |
Аварийный X-Admin-Key (SUPER_ADMIN вне RBAC; в проде опционально) |
SMARTID_ADMIN_REQUIRE_4EYES |
Требовать подтверждение вторым администратором для чувствительных операций (по умолчанию false) |
Конфигурация для dev. В профиле
dev, еслиSMARTID_ADMIN_API_KEYпуст,/v3/admin/*становится открытым SUPER_ADMIN (Bearer не требуется) — только для локальной разработки. На staging/prod такого НЕ происходит никогда; необходимо настроитьSMARTID_ADMIN_TOKEN_SECRET+ seed либо break-glass-ключ.
7. Ссылки¶
- Интеграция RP (RP_INTEGRATION.md) — подключение доверяющей стороны
- Подключение организации (ORG_ONBOARDING.md) — Legal Person / e-Seal
- Подключение PKI / CA (PKI_CA_ONBOARDING.md) — цепочка CA, OCSP/CRL
Исходный код:
- Go admin API:
server/internal/httpapi/handlers_admin_*.go, маршруты:server/internal/httpapi/server.go - RBAC / аутентификация:
server/internal/admin/(roles.go,service.go,totp.go,token.go,password.go) - Конфигурация:
server/internal/config/config.go - Фронтенд:
admin/src/