58 lines
11 KiB
Markdown
58 lines
11 KiB
Markdown
# SSH Manager — постоянные и разовые права
|
||
|
||
Проверено и развёрнуто: 2026-10-19 22:19 UTC+6 (Asia/Bishkek).
|
||
|
||
SSH Manager **0.4.0**, implementation commit `d2806ac150363712b8baa9c40dbd37f2946b8c13`. Рабочее дерево чистое при сборке, `vcs.modified=false`. Активный binary SHA256 `536d389a4261e186fce8dfabe4a3adfe605d190d84ef188cdf105c3e8ee079bd`, inode работающего процесса совпадает с установленным файлом. HTTP MCP `127.0.0.1:17796/mcp` отдаёт **13 инструментов**.
|
||
|
||
Balda остаётся на чистом upstream `138cc4a71d6889de30fe90e368f27f129fd8d792`, binary SHA256 `31c448af84428f83491d5fb8317549466f814f51c453de11fc8f6c9bd0836672`. Каталог/кэш MCP обновлён штатным перезапуском: `2026-10-19T16:15:26.785581969Z`. Config/env/mounts, OAuth login, SSH inventory и ключи сохранены; health/login 200, Mattermost websocket подключён. Все 49 runtime sessions и metadata, 28 questions, 2 users/bindings и 24 archive sessions из остановленного snapshot сохранены. Workspace Manager не менялся.
|
||
|
||
## Что исправлено
|
||
|
||
В [разобранном треде](https://mm.qentra.top/qentra/pl/skeyf33mx3f6tcnmx11qukgs5r) штатный ключ уже работал, но bubblewrap закрывал сеть хоста, Docker socket и sudo. Модель попыталась использовать sandbox-доступ Mac как административный и запустить вложенный `ssh kraken`; он завершился DNS-ошибкой. Прав не поменялось. У менеджера тогда отсутствовало API изменения профиля. Сбой произошёл до обновления Balda на upstream.
|
||
|
||
Теперь изменения выполняются через SSH Manager существующим операторским bootstrap-доступом, без передачи его ключей агенту и без хардкода хоста. Менеджер не получил инструмент произвольного выполнения команд: он выдаёт разрешение и native SSH-команду, которую запускает клиент своим установленным ключом.
|
||
|
||
## Права
|
||
|
||
- `network`: сеть хоста вместо отдельного Linux network namespace; на Mac — IP networking через Seatbelt.
|
||
- `read_paths` / `write_paths`: конкретные канонические пути. Manager control state, личные SSH credentials, системные корни и каталоги с socket-файлами не выдаются обычным файловым grant. Linux использует проверенные открытые FD для bind mounts.
|
||
- `docker`: `none`, `read` или `manage`. Read разрешает просмотр/логи/инспекцию, но не изменения. Docker CLI работает с фиксированным локальным daemon endpoint и закрытым control HOME/config; socket не появляется в sandbox.
|
||
- `admin`: прямой `sudo` с уже существующими noninteractive правами оператора. Sudoers не изменяется. Native timeout запускается с теми же привилегиями, чтобы остановить обычную root-команду.
|
||
|
||
`docker=manage` и произвольный sudo могут обойти файловые/сетевые ограничения; форма подтверждения сообщает об этом. Это отдельные явно разрешённые исключения. Завершение вызова не откатывает его побочные эффекты и не обещает отозвать намеренно оставленную привилегированную работу.
|
||
|
||
## Постоянное изменение
|
||
|
||
1. `ssh_permissions` возвращает профиль и revision.
|
||
2. `ssh_permissions_update`: полный новый профиль плюс `expected_revision`.
|
||
3. Native owner form показывает before/after; затем выполняются реальная приёмка изоляции, CAS только managed key line и SSH-проверка из настроенного клиента. Ключ и маршрут сохраняются.
|
||
4. Ошибка активации/проверки восстанавливает и проверяет прежний профиль либо явно сообщает, что rollback не подтверждён.
|
||
|
||
Новые вызовы используют новый профиль. Старые запущенные процессы не завершаются; неиспользованные разовые допуски инвалидируются. Действующие production `mac` и `kraken` пока возвращают `managed=false`: их прежние принятые ограничения при deployment не заменялись. Первый переход на управляемый профиль — отдельное явное `ssh_permissions_update` с owner confirmation, выполняемое самой Balda через MCP; ручная настройка JSON/хоста не требуется.
|
||
|
||
## Разовый вызов
|
||
|
||
`ssh_call_request` принимает хост, точный argv и только временные overrides профиля. Например: command `["docker","ps","-a"]`, permissions `{"docker":"read"}`.
|
||
|
||
Одно owner confirmation связывает хост, argv, эффективные права и сроки. Затем Balda запускает возвращённый `ssh_command` своим установленным ключом. Сервер атомарно потребляет id **до запуска**, включая ошибочные вызовы; повтор/гонка второго подключения не дают второго запуска. Argv исполняется буквально, stdin закрыт. Start TTL 1–900 секунд (default 300), runtime 1–600 секунд (default 60). Часы — общесистемные на целевом хосте, включая macOS system Python.
|
||
|
||
`ssh_call_status` читает серверную квитанцию; issued не означает выполнение, consumed без result не означает, что процесс ещё работает. Успех — finished и exit_code 0 после реального SSH. `ssh_call_cancel` подтверждённо отменяет неиспользованный допуск, но не останавливает запущенную команду. Изменение профиля и revoke не воскрешают старые разрешения при повторной регистрации.
|
||
|
||
Постоянный профиль при разовом вызове не меняется. Штатная политика Balda для запуска локальной SSH-команды остаётся отдельным уровнем; SSH grant не выключает её. Старые диалоги с кэшированной MCP schema обновляются обычной новой сессией / `/reset`; история автоматически не сбрасывалась.
|
||
|
||
## Проверено
|
||
|
||
- Go race и vet, diff --check — pass.
|
||
- Native macOS + MCP + временный sshd: **22 проверки**, включая отказы, stale revision, rollback после сбоя SSH-проверки, буквальный argv, закрытый stdin, race двух соединений, consumption после ошибки, TTL/timeout/cancel, постоянные file/network права и отзыв/re-authorize.
|
||
- Linux strict bubblewrap в Colima: **10 проверок**, в том числе Docker read/manage отдельно, отказ управления при read, sudo от **обычного пользователя** с получением host root и остановка root-команды по тайм-ауту. Kernel/AppArmor/proc protection не ослаблялись. Временный Docker volume удалён.
|
||
- Прежний native lifecycle произвольных хостов/ProxyJump/pin/restart/revoke/remove: **14 проверок**.
|
||
- Реальная модель в работающей Balda: session `01a154e9-5b6a-7a32-ae5b-d5efcdc03ca6`. Постоянный update → one-call → проверка серверной квитанции → восстановление профиля. Ровно 3 native owner forms для этих 3 изменений, без generic MCP approval. Harness выполнил pinned native SSH из реального контейнера UID 1000 установленным **временным fixture-ключом**; успех подтверждён, повтор запрещён, исходная сеть восстановлена.
|
||
- После deployment свежий Codex ACP вызвал новый `ssh_permissions` на production Mac/Kraken: session `01a154f0-1bb2-7932-aa2d-cf2907fc4ed0`, **0 подтверждений**. Это read-only проверка установленного API, без миграции профилей.
|
||
- После перезапуска Balda прежние production-ключи к Mac и Kraken ещё раз проверены native SSH из настоящего UID 1000 Balda; оба вернули случайный marker.
|
||
|
||
Тесты используют случайные aliases/порты, временные ключи и отдельные private пути. Тестовые сообщения в Mattermost не отправлялись. Никакие bootstrap keys/passwords не передавались модели или в agent volume.
|
||
|
||
Evidence: `/Users/admin/.local/share/balda-workspace/candidate/ssh-permissions-20261019`. Backup менеджера: `/Users/admin/.local/share/balda-workspace/backups/ssh-permissions-20261019T161039Z`. Согласованный state backup Balda: `/Users/admin/.local/share/balda-workspace/backups/ssh-permissions-balda-20261019T161515Z`. Backup изменённых заметок: `/Users/admin/.local/share/balda-workspace/backups/obsidian-ssh-permissions-20261019T221941+0600`.
|
||
|
||
Исходники и contract: `/Users/admin/Developer/ssh-manager/README.md`, `docs/permissions-2026-10-19.md`. Текущий setup: [[personal/projects/balda/setup]]. План: [[personal/plans/balda-remote-agent]].
|