diff --git a/README.md b/README.md index d339066..9e3aeb8 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ The control plane and data plane are separated: See [architecture](docs/architecture.md), [security model](docs/security-model.md), [roadmap](docs/roadmap.md), [Linux bootstrap contract](docs/installer-contract.md), and the [Provisioning and Xray specification](docs/provisioning-vpn-requirements.md). +Work order is governed by [product horizons](docs/product-horizons.md): the roadmap records what is done, the horizons record what may be started. Planned subsystems are specified separately in [approval policies](docs/approval-policies.md) and the [KAgent integration](docs/integration-kagent.md). Both describe target behaviour and are not descriptions of the current alpha; nothing in them is implemented. + Operational procedures are documented in [Linux bootstrap](docs/linux-bootstrap.md), [Control backup and recovery](docs/control-backup.md), and the [three-server acceptance test](docs/three-server-acceptance.md). ## Repository layout diff --git a/docs/approval-policies.md b/docs/approval-policies.md new file mode 100644 index 0000000..b211f16 --- /dev/null +++ b/docs/approval-policies.md @@ -0,0 +1,71 @@ +# Политики подтверждения + +Статус: спецификация. Реализовано частично — сегодня подтверждение бинарное: действие либо требует `ConfirmationRequired`, либо нет. + +## Задача + +Разные операции несут разный риск, но сейчас различаются только наличием подтверждения. Смена таймзоны и ротация Control CA проходят через одну и ту же процедуру. Нужен явный, версионируемый набор режимов. + +## Режимы + +| Режим | Что требуется | +|---|---| +| `none` | Ничего. Допустим только для операций без побочных эффектов на хосте | +| `operator` | Подтверждение действующим Operator-сертификатом с `audit reason` | +| `operator_reauth` | То же плюс повторное доказательство владения identity непосредственно перед подтверждением | +| `owner` | Подтверждение identity с ролью владельца установки, отличной от обычного Operator | +| `two_person` | Два разных Operator; второй не может быть инициатором. Оба подтверждения в аудите | +| `time_window` | Подтверждение действительно ограниченное время; по истечении задание возвращается в `AwaitingConfirmation` | +| `maintenance_window` | Выполнение разрешено только внутри объявленного окна обслуживания | +| `emergency_only` | Доступно только при активном инциденте, с обязательным пост-фактум разбором в аудите | +| `read_only_automation` | Automation-идентичности разрешено чтение результата, но не инициирование | + +## Формат + +```yaml +service.restart: + approval: none +system.packages: + approval: operator +firewall.rule.add: + approval: operator_reauth +ssh.migrate: + approval: operator_reauth +user.delete: + approval: owner +ca.rotate: + approval: two_person +node.delete: + approval: two_person +``` + +Политика версионируется вместе с каталогом действий, хранится на Control и не может быть ослаблена запросом клиента. Отсутствие действия в политике означает `owner`, а не `none`. + +## Связь с существующими механизмами + +Режимы не заменяют то, что уже работает, а надстраиваются над ним: + +- `ConfirmationRequired` и `ConfirmedAt` в `ProvisioningJob` остаются местом фиксации факта подтверждения; +- `time_window` естественно ложится на существующий TTL задания; +- execution grant, привязанный к `node_id`, `job_id` и SHA-256 подтверждённого plan, выдаётся только после того, как режим удовлетворён; +- `audit reason` обязателен во всех режимах, кроме `none`; +- idempotency key не позволяет повторному запросу обойти подтверждение. + +## Требования к реализации + +- решение о достаточности подтверждения принимает Control, а не клиент; +- при `two_person` инициатор и подтверждающий сравниваются по identity, а не по отображаемому имени; +- при `operator_reauth` повторное доказательство привязывается к конкретному `job_id`; повторное использование для другого задания отклоняется; +- понижение режима для конкретного задания невозможно; изменение политики действует только на задания, созданные после изменения, и само является аудируемым событием; +- недостижимость условий режима не приводит к выполнению: задание остаётся в `AwaitingConfirmation` до истечения TTL, затем отменяется. + +## Что нельзя ослаблять + +Для операций, способных лишить доступа к серверу или к управлению, режим не может быть ниже `operator_reauth`: + +- закрытие действующего SSH-порта; +- изменение правил firewall; +- удаление или блокировка последнего административного пользователя; +- включение системного VPN и kill switch; +- удаление Node; +- ротация Control CA — не ниже `two_person`. diff --git a/docs/integration-kagent.md b/docs/integration-kagent.md new file mode 100644 index 0000000..00225a4 --- /dev/null +++ b/docs/integration-kagent.md @@ -0,0 +1,203 @@ +# Интеграция с KAgent + +Статус: спецификация. Ничего из описанного не реализовано. Работы разрешены после закрытия Горизонта 1 — см. [горизонты продукта](product-horizons.md). + +## Принцип + +KAgent — внешняя AI-система. Она **просит**, Server Monitor Manager **решает и исполняет**. Никакая часть интеграции не даёт KAgent исполнения на хосте: запрос от KAgent порождает обычный typed provisioning job с immutable plan, режимом подтверждения из [политик](approval-policies.md), execution grant, factual verification, audit и rollback. Отдельного быстрого пути для AI не существует. + +SMM обязан полностью работать без KAgent. Недоступность KAgent не влияет на мониторинг, Links, provisioning, alerts, backups и аудит. + +## Инвариант недоверенного исполнителя + +Это главное требование раздела. KAgent Worker — процесс, исполняющий произвольный код по построению: сборка репозитория запускает его build-скрипты. Worker считается недоверенным субъектом, и изолируется он **от control plane**, а не только от внешней сети. + +Из инварианта следуют обязательные условия установки Worker на Node: + +- отдельный системный пользователь без sudo, отдельные каталоги, отдельный cgroup; +- нет доступа к сокету provisioning-helper `/run/ochenstarik-server-monitor-manager/provisioning.sock`; членство в группе `ochenstarik-smm-agent` запрещено; +- нет доступа к `agent.pfx`, `control-ca.crt`, `agent.env`, `nodes.tsv`, каталогу `mesh` и каталогу rollback; +- нет сетевого доступа к Control API и к порту Control; +- исполнение в контейнере: read-only rootfs, seccomp, AppArmor, сброшенные capabilities, ограничение pids; +- системные лимиты (`CPUQuota`, `MemoryMax`, `TasksMax`, `IOWeight`) дополняют изоляцию, но не заменяют её; +- порт Worker не публикуется наружу. + +Компрометация Worker не должна давать ни управления Node, ни доступа к другим Node, ни возможности создать Link. + +## Модель возможностей + +Три класса, а не два. + +**Чтение.** Не меняет состояние. + +```text +infrastructure.nodes.read +infrastructure.metrics.read +infrastructure.alerts.read +infrastructure.links.read +infrastructure.jobs.read +infrastructure.drift.read +infrastructure.services.read (после Горизонта 1) +infrastructure.containers.read (после Горизонта 1) +infrastructure.logs.read (после Горизонта 2) +infrastructure.backups.read (после Горизонта 1) +``` + +**Запрос.** Создаёт предложение, которое проходит обычный конвейер подтверждения. Само по себе ничего не меняет. + +```text +infrastructure.diagnostics.request +infrastructure.link.plan +infrastructure.link.request +infrastructure.provisioning.plan +infrastructure.provisioning.request +infrastructure.backup.request +infrastructure.service.restart.request +infrastructure.remediation.request +``` + +**Невыдаваемые.** Не «выключены по умолчанию», а отсутствуют как код в пути KAgent-идентичности. Их нельзя включить настройкой. + +```text +root.execute +ca.rotate +secrets.read +audit.disable +firewall.apply (доступен только через .request с режимом operator_reauth) +users.modify (доступен только через .request) +node.delete +``` + +Прецедент в проекте: Automation-сертификат физически не может изменять Links — не по умолчанию, а вообще. Здесь применяется та же строгость. + +## Идентичность + +Отдельная роль `KAgentIntegration`, связанная с installation ID, организацией, списком разрешённых Nodes, набором возможностей, сроком действия, отпечатком сертификата и состоянием отзыва. Выдаётся по одноразовому token, как остальные identity. Не наследует прав Operator и не может выдавать себе новые возможности. + +## Локальное обнаружение + +```text +/run/server-monitor-manager/integration.json +/run/server-monitor-manager/integration.sock +``` + +Права `root:smm-integrations`, `0640`, каталог `0750`. + +Членства в группе **недостаточно**. Сокет обязан повторить меры, уже реализованные в `ProvisioningHelperServer`: + +- проверка `SO_PEERCRED` со сверкой uid обращающегося процесса; +- обработка соединения вне цикла accept, ограничение параллелизма; +- таймаут на соединение целиком; +- чтение буфером с ограничением размера запроса; +- ограничение частоты запросов и отдельный счётчик неавторизованных попыток. + +## Протокол + +```text +KAgent-SMM Integration Protocol v1 +``` + +Handshake: + +```json +{ + "client": "kagent", + "client_version": "0.8.0", + "protocol_versions": ["1.0", "1.1"], + "requested_capabilities": ["nodes.read", "metrics.read", "jobs.plan"] +} +``` + +Ответ содержит выбранную версию протокола и **фактически выданные** возможности, которые могут быть уже запрошенных: + +```json +{ + "server": "server-monitor-manager", + "server_version": "0.2.0", + "selected_protocol": "1.1", + "granted_capabilities": ["nodes.read", "metrics.read"] +} +``` + +Версия сервера в ответе — реальная версия сборки. Неизвестные поля запроса отклоняются, неизвестные версии протокола не согласуются. + +## Поверхность API + +Проектируется против того, что существует. Эндпоинты для сущностей, которых в системе нет (containers, services, logs, backups), добавляются вместе с самими сущностями, а не заранее. + +Первый этап: + +```text +GET /api/v1/integrations/capabilities +GET /api/v1/integrations/version +GET /api/v1/kagent/nodes +GET /api/v1/kagent/nodes/{id} +GET /api/v1/kagent/nodes/{id}/metrics +GET /api/v1/kagent/nodes/{id}/health +GET /api/v1/kagent/events +``` + +Второй этап, после появления соответствующих модулей: + +```text +POST /api/v1/kagent/links/plan +POST /api/v1/kagent/links/request +GET /api/v1/kagent/links/{id} +POST /api/v1/kagent/links/{id}/disable-request +POST /api/v1/kagent/jobs/plan +POST /api/v1/kagent/jobs/request +GET /api/v1/kagent/jobs/{id} +POST /api/v1/kagent/jobs/{id}/cancel-request +POST /api/v1/kagent/diagnostics +GET /api/v1/kagent/diagnostics/{id} +``` + +Любая мутация требует idempotency key и audit reason. `*.request` возвращает идентификатор задания, а не результат: результат наступает после подтверждения человеком. + +## Временные Links для задач + +Обязательные поля: source, destination, протокол, порт, TTL, идентификатор задачи, причина, версия политики, владелец, режим подтверждения. + +Ограничения: + +- destination не может быть Hub; +- destination не может быть другим Worker; +- TTL обязателен и ограничен сверху; Link без TTL для задачи не создаётся; +- снятие выполняется по завершении задачи, отказу, таймауту, уходу Worker в offline, истечению аренды или аварийной остановке; +- снятие проверяется фактически, как и любая другая Link-операция. + +## События + +SMM передаёт versioned infrastructure events и не становится внутренней очередью KAgent: + +```text +node.online +node.offline +metric.threshold +link.active +link.disabled +provisioning.started +provisioning.completed +provisioning.failed +certificate.expiring +backup.failed +worker.resource_exceeded +worker.quarantined +``` + +## Аварийные средства + +Operator может в любой момент, без участия KAgent: отозвать сертификат интеграции, остановить все Worker, отключить задачные Links, заблокировать новые запросы, перевести интеграцию в режим только чтения, прекратить аренды, поместить Worker в карантин, отключить его сеть. Журналы для разбора сохраняются. + +## Поведение при отказе + +Недоступность KAgent не влияет на работу SMM. Задание с неопределённым результатом получает `NeedsReconciliation`; после восстановления связи проверяется фактическое состояние, а не предполагается успех. + +## Вне области + +- root shell в любой форме; +- выдача приватных ключей и содержимого секретов; +- изменение или отключение аудита; +- произвольные правила firewall; +- прямое управление пользователями без конвейера подтверждения; +- превращение SMM в очередь задач или планировщик KAgent. diff --git a/docs/product-horizons.md b/docs/product-horizons.md new file mode 100644 index 0000000..25adfe5 --- /dev/null +++ b/docs/product-horizons.md @@ -0,0 +1,67 @@ +# Горизонты продукта и гейты + +Этот документ управляет очерёдностью работ. [Roadmap](roadmap.md) отвечает на вопрос «что сделано», этот документ — на вопрос «что разрешено начинать». + +## Правило + +Горизонт закрывается целиком. Переход к следующему запрещён, пока не выполнены все критерии выхода текущего. Критерий, сформулированный через фактическое поведение системы, закрывается только измерением на реальной топологии; зелёный CI его не заменяет. + +Причина правила: безопасность всех последующих функций держится не на их собственном коде, а на том, что typed provisioning, approval, audit, execution grant, factual verification и emergency recovery работают в действительности. Сегодня мутирующий provisioning выполняет одно действие — смену таймзоны. Пока это так, любая функция, построенная поверх этой машинерии, проверяет её сразу на самом опасном для себя сценарии. + +## Горизонт 0 — закрыть начатое + +Ничто новое не начинается. + +- фоновая реконсиляция Link-политик от фактического состояния (`B-3`/`B-3R`) доведена до merge с зелёным Linux CI; +- **физический acceptance** `tests/acceptance/three-server-mesh.sh` выполнен с `SMM_ACCEPT_RESTORE=1 SMM_ACCEPT_REBOOT=1` на реальной тройке Hub + source Node + два destination Node; +- подписанный release manifest: хэши всех артефактов, версии Control/Agent/helper/Desktop, минимальные совместимые версии; публичный ключ проверки вшит в bootstrap и Desktop; `update-*` отвергает несовместимые версии; см. [контракт bootstrap](installer-contract.md); +- роль Monitor реализована в bootstrap: системный пользователь, root-owned forced command, установка публичного ключа из Desktop, идемпотентная переустановка и удаление; +- срок жизни и ротация клиентских сертификатов: автопродление Agent до истечения, документированная процедура ротации Control CA; +- GitHub Actions запиннены по commit SHA, включён dependabot. + +**Критерий выхода.** Чистый поддерживаемый сервер устанавливается из подписанного релиза одной командой, регистрируется одноразовым кодом, попадает под мониторинг без ручной правки `authorized_keys`, и после перезагрузки Hub и Node фактическое состояние Links совпадает с желаемым — проверено связностью, а не состоянием в API. + +## Горизонт 1 — продукт, пригодный для одного владельца + +Разрешён после закрытия Горизонта 0. + +- provisioning-модули поверх готового каркаса, по одному: `system.locale`, `system.packages`, `system.swap`, `user.create`, `user.disable`, `ssh.key.add`, `ssh.key.remove`; +- редактор managed firewall rules и двухфазная миграция SSH-порта — см. [ТЗ Provisioning и Xray VPN](provisioning-vpn-requirements.md), раздел 10; +- alert engine: пороги, warning/critical, duration, cooldown, дедупликация, подтверждение, история инцидентов, тихие часы; +- Telegram — **только чтение**: `/status`, `/servers`, `/alerts`, `/links`, `/jobs`. Никаких мутаций, приватных ключей, управления CA и произвольных команд; +- Docker и systemd — сначала наблюдение: контейнеры, образы, healthcheck, счётчики рестартов, failed units, journald. Действия только после наблюдения и только по allowlist; +- Backup Manager: local и S3, расписания, шифрование, retention, контрольные суммы, проверка восстановлением. + +**Критерий выхода.** Каждый мутирующий модуль имеет versioned schema, immutable plan, подтверждение, factual verification, rollback и audit, и прошёл физическую приёмку на VM-матрице. Закрытие действующего SSH-порта невозможно до успешной проверки нового подключения. + +## Горизонт 2 — платформа + +- Web UI и аутентификация. До начала работ принять и записать решение по разделу «Публичный интерфейс» в [модели безопасности](security-model.md): отдельный listener, отношение веб-сессий к mTLS-идентичностям, перечень операций, недоступных веб-сессии в принципе; +- Secret Vault: envelope encryption, версии, ротация, маскированный показ, scoped delivery, TTL, отзыв, redaction. Является предусловием для Telegram-токена, ключей S3 и VPN-подписок; +- централизованные логи. До начала работ принять решение о хранилище: Control SQLite для этого не предназначен; +- security posture: оценка, объяснения, план исправления, безопасное применение, история, принятые риски; +- multi-Hub, фаза 1: резервные копии БД и CA, шифрованное хранилище, проверка восстановления, ручное переключение. Фазы репликации и кворума не входят; +- **один** VPN-модуль (Xray VLESS Reality) до физической приёмки включительно. Второй модуль не начинается, пока первый не принят. + +## Горизонт 3 — интеграция с KAgent + +Разрешён после закрытия Горизонта 1. Спецификация: [интеграция с KAgent](integration-kagent.md). + +Порядок внутри горизонта: discovery и identity → чтение Nodes, метрик и событий → планы и запросы → жизненный цикл Worker. Обратный порядок недопустим. + +## Что принято из внешнего документа видения + +| Раздел | Решение | +|---|---| +| Approval policies | Принято, см. [approval-policies.md](approval-policies.md) | +| Capability model, разделение `.read` / `.request` | Принято с ужесточением, см. [integration-kagent.md](integration-kagent.md) | +| Emergency controls для интеграции | Принято | +| Поведение при недоступном KAgent | Принято | +| Provisioning catalog, desired/factual, drift | Принято как расширение существующей модели | +| Формат alert rule | Принят | +| Web UI и полный стек аутентификации | Отложено в Горизонт 2 с обязательным решением по модели угроз | +| Централизованные логи | Отложено, требует решения по хранилищу | +| Шесть VPN-модулей | Сокращено до одного до физической приёмки | +| Multi-Hub, репликация и кворум | Отложено за пределы горизонтов | +| Установка KAgent Worker | Горизонт 3, с инвариантом недоверенного исполнителя | +| Расширенный мониторинг: SMART, RAID, GPU, UPS | Отложено, каждый пункт требует привилегированного сборщика | diff --git a/docs/roadmap.md b/docs/roadmap.md index ecf6d6d..4c960ea 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -4,6 +4,8 @@ Текущее автоматическое покрытие поддерживаемых Linux-платформ описано в [Linux platform matrix](linux-platform-matrix.md). +Очерёдность работ определяется [горизонтами продукта](product-horizons.md). Этот документ отвечает на вопрос «что сделано», горизонты — на вопрос «что разрешено начинать». Этапы 14–18 ниже относятся к Горизонтам 1–3 и не начинаются до закрытия Горизонта 0. + ## Этап 0 — граница и базовая архитектура - [x] переименовать проект в Server Monitor Manager; @@ -181,3 +183,56 @@ - [ ] macOS и Linux Desktop после стабилизации Core/API; - [ ] Android/iOS companion clients; - [ ] push-уведомления без административных secrets у push provider. + +## Этап 14 — роль Monitor и доверенная поставка (Горизонт 0) + +- [ ] `install-monitor` в bootstrap: системный пользователь, root-owned forced command, установка публичного ключа из Desktop, идемпотентная переустановка и удаление; +- [ ] контрактный тест формата снимка метрик, общий для forced command и Desktop-парсера; +- [ ] подписанный release manifest с хэшами всех артефактов и версиями Control/Agent/helper/Desktop; +- [ ] публичный ключ проверки вшит в bootstrap и Desktop, проверка подписи до разбора manifest; +- [ ] отказ `update-control`/`update-agent` при несовместимых версиях; +- [ ] негативные тесты в CI: подменённый архив, подменённый хэш, manifest без подписи; +- [ ] пиннинг GitHub Actions по commit SHA, dependabot, SBOM; +- [ ] автопродление сертификата Agent и документированная ротация Control CA; +- [ ] доверенная подпись Windows MSIX. + +## Этап 15 — политики подтверждения (Горизонт 1) + +Спецификация: [политики подтверждения](approval-policies.md). + +- [ ] режимы `none`, `operator`, `operator_reauth`, `owner`, `two_person`, `time_window`, `maintenance_window`, `emergency_only`, `read_only_automation`; +- [ ] версионируемая политика на Control, недоступная для ослабления клиентом; +- [ ] повторное доказательство владения identity, привязанное к конкретному `job_id`; +- [ ] сравнение инициатора и подтверждающего по identity при `two_person`; +- [ ] изменение политики как аудируемое событие, действующее только на новые задания. + +## Этап 16 — alerts, уведомления и наблюдение за сервисами (Горизонт 1) + +- [ ] alert rules: порог, duration, severity, cooldown, дедупликация; +- [ ] режим обслуживания, подтверждение, эскалация, история инцидентов, автозакрытие, тихие часы; +- [ ] Telegram только на чтение: `/status`, `/servers`, `/alerts`, `/links`, `/jobs`; +- [ ] наблюдение Docker: контейнеры, образы, healthcheck, счётчики рестартов, лимиты; +- [ ] наблюдение systemd: units, failed units, счётчики рестартов, journald; +- [ ] действия над Docker и systemd только по allowlist, с подтверждением и аудитом. + +## Этап 17 — Backup Manager (Горизонт 1) + +- [ ] источники: каталоги, конфигурации, Docker volumes, Control DB и CA; +- [ ] хранилища local и S3; +- [ ] расписания, шифрование, retention, контрольные суммы; +- [ ] проверка архива и тестовое восстановление; +- [ ] оповещения об ошибках резервного копирования и об устаревшей копии. + +## Этап 18 — интеграция с KAgent (Горизонт 3) + +Спецификация: [интеграция с KAgent](integration-kagent.md). Начинается только после закрытия Горизонта 1. + +- [ ] локальное обнаружение через Unix-сокет с проверкой `SO_PEERCRED`, таймаутами и лимитами; +- [ ] идентичность `KAgentIntegration` с одноразовым token, сроком, областью Nodes и отзывом; +- [ ] модель возможностей: чтение, запрос, невыдаваемые; +- [ ] согласование версии протокола и фактически выданных возможностей; +- [ ] чтение Nodes, метрик, здоровья и событий; +- [ ] планы и запросы через обычный конвейер typed provisioning; +- [ ] жизненный цикл Worker с инвариантом недоверенного исполнителя; +- [ ] временные задачные Links с обязательным TTL и запретом destination = Hub или другой Worker; +- [ ] аварийные средства: отзыв, остановка, карантин, режим только чтения. diff --git a/docs/security-model.md b/docs/security-model.md index 7cb3319..c4630da 100644 --- a/docs/security-model.md +++ b/docs/security-model.md @@ -81,6 +81,43 @@ Provisioning расширяет существующую модель ролей Подробный контракт приведён в [ТЗ Provisioning и Xray VPN](provisioning-vpn-requirements.md). +## Недоверенные исполнители на Node + +Статус: инвариант для будущих модулей, реализация не начата. + +Любой компонент, исполняющий на Node внешний код — прежде всего KAgent Worker, — считается недоверенным субъектом и изолируется от control plane, а не только от внешней сети. Сборка репозитория запускает его build-скрипты, поэтому «ограниченный набор возможностей» не означает ограниченного исполнения. + +Обязательные условия: + +- отдельный системный пользователь без sudo, отдельные каталоги и cgroup; +- нет доступа к сокету provisioning-helper и запрет членства в группе Agent; +- нет доступа к `agent.pfx`, `control-ca.crt`, `agent.env`, состоянию mesh и каталогу rollback; +- нет сетевого доступа к Control API; +- исполнение в контейнере с read-only rootfs, seccomp, AppArmor и сброшенными capabilities; +- системные лимиты дополняют изоляцию, но не заменяют её; +- задачный Link не может иметь destination Hub или другой недоверенный исполнитель. + +Компрометация такого исполнителя не должна давать управления Node, доступа к другим Node или возможности создать Link. Подробности: [интеграция с KAgent](integration-kagent.md). + +## Публичный интерфейс + +Статус: решение не принято, работы не начаты. + +Текущая модель держится на трёх утверждениях: у Node нет публичного входящего API, Control доступен только по mTLS, порт Control не открывается без ограничений firewall. Веб-интерфейс с аутентификацией по паролю вводит браузерный surface на Hub и вторую систему идентичности рядом с сертификатами устройств. + +До начала работ над веб-интерфейсом требуется зафиксировать здесь: + +- отдельный listener и порт, отношение к цепочке сертификатов Control; +- как веб-сессии соотносятся с mTLS-идентичностями в модели ролей; +- перечень операций, недоступных веб-сессии в принципе и требующих mTLS-идентичности — как минимум изменение firewall, ротация CA, удаление Node и экспорт секретов; +- привязка сессии к устройству, защита от CSRF, ограничение частоты и повторная аутентификация для операций повышенного риска. + +## Невыдаваемые возможности + +Часть возможностей не является настройкой и не может быть выдана никакой внешней идентичности: исполнение произвольных команд от root, ротация Control CA, чтение содержимого секретов, отключение аудита, удаление Node. Требование означает отсутствие соответствующего кода в пути внешней идентичности, а не значение по умолчанию. + +Прецедент: сертификат `Automation` физически не может изменять Links. Та же строгость распространяется на все последующие внешние интеграции. + ## Не входит в первый MVP - выполнение произвольных root-команд;