Skip to content

🔐 Токены безопасности: основной и временный ​

📌 Описание ​

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) и возвращает открытым текстом. У платформы, которая ходит в ноду напрямую, другого способа узнать новое значение нет.

Ротация двухфазная:

  1. Демон отзывает все временные токены, ставит новый основной и оставляет старый действующим на окно совместимости (по умолчанию 300 секунд, максимум 3600, 0 — отключить).
  2. Вызывающая сторона сохраняет новый токен и подтверждает через POST /v1/token/rotate/commit, новым токеном. Подтверждение старым (grace) токеном отклоняется — иначе платформа, ничего не сохранившая, отрапортовала бы об успехе.
  3. Если подтверждение не пришло, окно просто истекает.

⚠️ Предыдущий токен хранится только в памяти, поэтому перезапуск демона внутри окна обрывает окно досрочно. Окно — подушка на случай медленной записи на другой стороне, а не гарантия. Если новый токен потерян: на ноде с SSH платформа перечитает api.token по SSH, а оператор может выполнить sudo asc api token show на машине. У ноды, доступной только по прямому TLS и без SSH, автоматического восстановления нет.

Ручки ​

RESTgRPCГардОписание
POST /v1/token/accessTokenService.IssueAccessTokenосновнойВыпуск временного токена; {"ttl_secs"?, "label"?} → {"token", "expires_at", "ttl_secs"}
DELETE /v1/token/accessTokenService.RevokeAccessTokensосновнойГасит все живые временные токены → {"revoked": n}
POST /v1/token/rotateTokenService.RotatePrimaryTokenосновнойНовый основной; {"grace_secs"?} → {"token", "rotated_at", "grace_until"}
POST /v1/token/rotate/commitTokenService.CommitPrimaryTokenRotationосновной, не grace-токенПодтверждение ротации, закрытие окна
GET /v1/tokenTokenService.GetTokenStatusлюбойСтатус, никогда не секрет
POST /v1/system/rebootSystemService.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 — сторона платформы: выпуск, кэширование, ротация и отзыв из настроек ноды.

Распространяется по лицензии MIT.