🔐 Токены безопасности: основной и временный
📌 Описание
TCP-API демона защищают два вида bearer-токенов.
Основной токен (primary) демон генерирует при первом запуске и держит в api.token рядом с config.toml (только root, 0600). Он бессрочный, именно его платформа сохраняет при подключении ноды, и начиная с DMN-065 он ведёт себя как refresh-токен: его дело — выпускать другие токены и ротироваться, а не подписывать повседневный трафик.
Временный токен (access) живёт 10 минут (по умолчанию), хранится только в памяти демона и выпускается по предъявлении основного. Прав у него столько же, сколько у основного, кроме управления токенами: он не может прочитать или сменить основной, не может выпускать новые временные и не может их отзывать. Больше отличий нет.
Unix-сокета это не касается: там личность даёт ядро (SO_PEERCRED), а не токен. См. 📡 API.
🎯 Сценарии
- Платформа хранит основной токен зашифрованным и ходит в ноду с 10-минутным временным, обновляя его через основной по истечении срока или после перезапуска демона.
- Оператор запрашивает временный токен в платформе, видит его один раз в браузере и работает с ноутбука:
curl -H "Authorization: Bearer <токен>" https://node.example.com:8420/v1/apps. Через десять минут токен перестаёт работать сам. - Токен утёк. Оператор нажимает «отозвать все временные токены» — все выданные временные токены умирают немедленно, ничего другого на ноде не меняется.
- Нода скомпрометирована или закончилась работа подрядчика. Оператор запускает ротацию основного токена из danger zone; временные токены отзываются в рамках той же операции, платформа сохраняет новый основной токен.
- Локально:
sudo asc api token show,sudo asc api token rotate,sudo asc api status.
🏗️ Техническое решение
Хранилище токенов
Одно TokenStore в памяти, за ApiState:
- Основной — текущий токен плюс, во время ротации, предыдущий с дедлайном (окно совместимости ниже).
- Временные — карта по SHA-256-дайджесту токена, а не по самому токену: она живёт минутами, в отличие от 30-секундных консольных токенов, и дамп памяти или случайная
Debug-печать не должны выдавать рабочие учётные данные.
Временные токены многоразовые до истечения (в отличие от одноразовых консольных), чистятся лениво при каждом выпуске и каждой проверке и ограничены 64 живыми записями — сверх лимита вытесняется самая старая, так что платформа с утечкой рефрешей не раздует карту.
Разбор предъявленного токена
auth() определяет вид токена в таком порядке: текущий основной → основной в окне совместимости → живой временный → отказ. Отказ отвечает как и раньше: REST — 401, gRPC — UNAUTHENTICATED.
Что запрещено временному токену
Три ручки требуют основного токена (require_primary): ротация, подтверждение ротации, выпуск и отзыв временных токенов. Всё остальное ему доступно. На unix-сокете тот же гард пропускает root-пира — благодаря этому работает sudo asc api token rotate, а обычному пользователю на сокете отказано.
⚠️ Временный токен — это полный контроль над машиной минус управление токенами. Он может установить и запустить приложение, то есть склонировать и выполнить произвольный git-репозиторий от root, и открыть консоль внутри контейнера. Выдавать его — то же самое, что выдать root-шелл с десятиминутным предохранителем.
Отзыв
DELETE /v1/token/access гасит все живые временные токены и отвечает их количеством. Та же процедура выполняется первым шагом ротации, так что «отозвать всё» существует ровно в одном месте. Сам отзыв требует основного токена: иначе утёкший временный токен мог бы выбить платформу с ноды. Обратная сторона — обладатель временного токена не может отозвать сам себя; отзыв идёт через платформу, где он аудируется.
Ротация основного токена
POST /v1/token/rotate выпускает новый основной токен, пишет его атомарно (временный файл → 0600 → fsync → rename → fsync каталога, чтобы прерванная запись никогда не оставила пустой api.token) и возвращает открытым текстом. У платформы, которая ходит в ноду напрямую, другого способа узнать новое значение нет.
Ротация двухфазная:
- Демон отзывает все временные токены, ставит новый основной и оставляет старый действующим на окно совместимости (по умолчанию 300 секунд, максимум 3600,
0— отключить). - Вызывающая сторона сохраняет новый токен и подтверждает через
POST /v1/token/rotate/commit, новым токеном. Подтверждение старым (grace) токеном отклоняется — иначе платформа, ничего не сохранившая, отрапортовала бы об успехе. - Если подтверждение не пришло, окно просто истекает.
⚠️ Предыдущий токен хранится только в памяти, поэтому перезапуск демона внутри окна обрывает окно досрочно. Окно — подушка на случай медленной записи на другой стороне, а не гарантия. Если новый токен потерян: на ноде с SSH платформа перечитает
api.tokenпо SSH, а оператор может выполнитьsudo asc api token showна машине. У ноды, доступной только по прямому TLS и без SSH, автоматического восстановления нет.
Ручки
| REST | gRPC | Гард | Описание |
|---|---|---|---|
POST /v1/token/access | TokenService.IssueAccessToken | основной | Выпуск временного токена; {"ttl_secs"?, "label"?} → {"token", "expires_at", "ttl_secs"} |
DELETE /v1/token/access | TokenService.RevokeAccessTokens | основной | Гасит все живые временные токены → {"revoked": n} |
POST /v1/token/rotate | TokenService.RotatePrimaryToken | основной | Новый основной; {"grace_secs"?} → {"token", "rotated_at", "grace_until"} |
POST /v1/token/rotate/commit | TokenService.CommitPrimaryTokenRotation | основной, не grace-токен | Подтверждение ротации, закрытие окна |
GET /v1/token | TokenService.GetTokenStatus | любой | Статус, никогда не секрет |
POST /v1/system/reboot | SystemService.RebootSystem | основной | Принимает полную перезагрузку ОС: демон сначала отвечает, затем запрашивает reboot, не принимая произвольную команду |
GET /v1/token отдаёт вид предъявленного токена, срок его жизни (если есть), число живых временных токенов, состояние ротации и primary_digest — первые 16 hex-символов SHA-256 основного токена. Этого достаточно, чтобы платформа ответила «тот ли у меня основной токен», ничего не пересылая.
CLI
asc api status— слушатель, режим TLS, домен, отпечаток, число живых временных токенов, состояние ротации.asc api token show— напечатать основной токен (root, только через unix-сокет).asc api token rotate [--grace <секунды>]asc api token issue [--ttl <секунды>] [--label <текст>]asc api token revoke— отозвать все временные токены.
🔗 Связанные задачи
- DMN-065 — модель токенов, ручки и запретный список.
- DMN-066 — ротация: атомарная запись, окно совместимости, двухфазное подтверждение,
asc api token. - DMN-061 — прямой доступ к API демона по TLS.
- NODE-008 / NODE-009 — сторона платформы: выпуск, кэширование, ротация и отзыв из настроек ноды.