Безопасность
Главное
Доступ к nkt равносилен root на хосте; доступ к хабу — root на всех его хостах. Интерфейс не открывается в интернет без отдельного слоя аутентификации (VPN, SSO, allow-list адресов). Что открывать и что нет — Порты и доступ.
Вход и сессии
- Пароли — argon2id (64 МиБ, t=3, p=2), необратимо; минимальная длина — 10 знаков. «Текущий» пароль восстановить неоткуда — только выпустить новый (
nkt passwd). - Сессии — случайные отзываемые токены в SQLite; cookie
HttpOnly,SameSite=Lax,Secureпо умолчанию. Смена пароля завершает все сессии этой учётной записи. - После пяти неудачных попыток вход с адреса блокируется с нарастающей задержкой.
- Роли:
viewer— только чтение,admin— изменения. Глобальный рубильникNKT_ALLOW_MUTATIONS=falseдействует и на TUI. Свою учётную запись понизить, отключить или удалить нельзя.
Запросы с чужих страниц
Запросы, меняющие состояние, принимаются только со своего origin: Sec-Fetch-Site должен быть same-origin (или none), а при его отсутствии Origin должен совпадать с Host. Дополнительные origin — NKT_CORS_ORIGINS. Исключения — вебхуки выкладок (/api/hub/hooks/, доступ по подписи) и пробросы портов (/k8s/pf/, свой токен в пути).
Пробросы портов Kubernetes
«Открыть в браузере» у пода или сервиса отдаёт приложение с отдельного порта (8446, NKT_FORWARD_ADDR) — другой origin, поэтому JavaScript приложения не видит cookie и API nkt. Ссылка одноразовая, с токеном; проброс закрывается кнопкой. Cookie nkt в приложение не передаются, а cookie приложения переписываются на путь проброса. Если отдельный порт выключен (off), приложение отдаётся по пути интерфейса в песочнице (Content-Security-Policy: sandbox).
Что можно изменить
- Редактирование конфигов ограничено списком каталогов (корни nginx, haproxy, Caddy, systemd, netplan, sshd, sysctl, cron, перечисленные compose-файлы,
/etc/libvirt/qemu). Пути с..отклоняются. - Перед записью конфиг проверяется самим сервисом (
nginx -t,haproxy -c,caddy validate,sshd -t…); не прошёл — файл возвращается как был. Оптимистичная блокировка по SHA-256: файл, изменившийся на диске после открытия, молча не перезапишется. - Действия над сервисами и firewall принимают значения только из фиксированных списков — имя или действие нельзя подставить в команду. Значения из запросов не попадают ни в командные строки, ни в пути без проверки строгим шаблоном.
- Удаление правила ufw сверяется с точным текстом, который видел оператор: номера смещаются после каждого изменения, а удалённое «не то» правило может отрезать SSH.
- Каждое изменяющее действие пишется в журнал действий с пользователем, результатом и выводом команды.
Песочница systemd
Юниты из deploy/ сужают привилегии: ProtectSystem=strict с явным списком каталогов для записи, урезанный CapabilityBoundingSet, SystemCallFilter=@system-service, NoNewPrivileges.
Намеренное исключение — веб-терминал, обновления пакетов ОС, установки из интерфейса и самообновление по резервному каналу. Они запускаются через systemd-run в отдельном неограниченном юните, а не дочерним процессом nkt: иначе root-оболочка или сброс привилегий apt-get упёрлись бы в ограничения, защищающие сам демон. Ограничения демона защищают от компрометации его самого, а не от действий аутентифицированного администратора, явно запросившего root. Работает, только когда nkt запущен юнитом и есть systemd-run.
systemd-run нужен живой D-Bus. Где его нет (минимальные образы, Debian 11 без dbus), запасной путь — CAP_SYS_ADMIN в юните и nsenter --mount в пространство имён PID 1; RestrictNamespaces сужен ровно до mnt. Страница «Терминал» сообщает, если недоступен ни один путь, и предлагает установить dbus.
Команды для таких сессий собирает только сервер; имена из запроса (контейнер, пакет, под) проверяются и сверяются с инвентарём. Если nkt запущен не юнитом и запускает программу напрямую, её имя берётся из закрытого списка (оболочки, apt-get, snap, docker, podman, lxc, virsh, kubectl, k3s, tmux, btop и т. п.); остальное не запускается.
Веб-терминал по умолчанию выключен (NKT_TERMINAL_ENABLED); у хоста под хабом — отдельная галочка в его форме.
Хаб
- Секреты хостов (SSH-пароли и ключи, учётные записи администратора хостов, токены резервного канала, доступы конвейеров, ключ API модели) хранятся зашифрованными AES-256-GCM мастер-ключом (
NKT_HUB_MASTER_KEYилиhub.keyв каталоге данных). Показать их из интерфейса нельзя — только заменить. - Ключ SSH хоста запоминается при первом подключении и требуется при каждом следующем; подменённый ключ — ошибка, а не вход.
- Резервный канал — TLS с закреплённым отпечатком сертификата хоста и отдельный токен на хост; пять неверных попыток — блокировка адреса.
- Управляющие операции идут через SSH-туннель к API самого хоста, который применяет свои правила (
NKT_ALLOW_MUTATIONS, роль); хаб их не обходит. - Экспорт несёт мастер-ключ, поэтому шифруется паролем в браузере (PBKDF2 + AES-256-GCM); без пароля — только через отдельное подтверждение.
nkt hub deleteзатирает ключ и базу. - Секреты Kubernetes: в списках — только имена ключей; значения — кнопкой «показать» администратору, каждый показ пишется в журнал действий.
- Секреты не передаются в параметрах заданий и командных строках — только через окружение или stdin дочернего процесса.
Выкладки и вебхуки
- Вебхук принимается только с верной подписью: HMAC-SHA256 (GitHub, Gitea, Forgejo), секретный токен (GitLab) или подпись nkt с отметкой времени не старше 5 минут. Повтор доставки отвергается. Неизвестный адрес и неверная подпись дают одинаковый
401. - Вебхук ничего не выбирает сам: что и куда выкладывать, задано на хабе; из запроса берутся только ветка, коммит и тег, и каждый проверяется шаблоном.
- Доступы к репозиторию и registry хранятся зашифрованными и передаются git через окружение, не через адрес и не через аргументы.
nkt-edge
- На VPS нет ни базы, ни секретов конвейеров, ни доступа к хостам — захваченный edge может лишь слать запросы, которые хаб без подписи отвергнет.
- Туннель инициирует хаб; TLS 1.3, хаб доверяет ровно сертификату своего edge (единственный корень проверки), затем токен.
- Через туннель хаб обслуживает только роли этого edge: «вебхуки» —
POST /hooks/{id}; «API» — подписанные запросы токенов с галочкой «через nkt-edge» (Bearer, cookie, вход по паролю и веб-сокеты не проходят: секрет токена на VPS не попадает, подпись одноразовая); «колбэки» —POST /callbacks/slack/…с подписью Slack; «проверки снаружи» — только потоки, которые открывает хаб. Интерфейс через edge недоступен никогда. - Служба на VPS — системный пользователь
nkt-edge, единственная capability — привязка к 443; сертификат выпускает certbot.
Подробно — nkt-edge.
API-токены
- Токен открывает не весь API, а список вызовов автоматизации; управление хабом (учётки, токены, экспорт, обновление, edge, боты), терминал, консоль, файлы, бэкапы, конфиги, Kubernetes и веб-сокеты закрыты ему при любой роли.
- Роль «чтение» читает, «администратор» ещё и действует; пределы по хостам и группам сужают и списки, и действия (выкладка — только конвейера, все хосты которого в пределах; сухой прогон — только сохранённого конвейера).
- Секрет хранится зашифрованным мастер-ключом (подпись HMAC проверяется по нему) и показывается один раз; подпись живёт 5 минут, nonce одноразовый, тело подписано.
- Список адресов токена сверяется с настоящим адресом соединения; заголовкам прокси хаб верит только с loopback (свой обратный прокси, туннель edge). Пять неверных токенов с адреса — пауза.
- Создание, правка, новый секрет, отзыв и каждый вызов — в журнале действий от имени
token:<имя>.
Исходящие вебхуки и боты
- Исходящие вебхуки подписываются HMAC секретом адресата (как входящие вебхуки nkt); переадресации не выполняются, тело ответа не читается дальше 300 байт.
- Бот Telegram ходит к Telegram сам (долгий опрос) — вход в хаб не нужен; Slack присылает команды сам — каждый запрос проверяется подписью Slack и временем (не старше 5 минут).
- Боты отвечают только чатам и каналам из списка; действия — только с ролью «администратор», от разрешённых людей и после кнопки подтверждения (живёт 10 минут, только у того, кто вызвал).
- Токены ботов и секреты подписи хранятся зашифрованными мастер-ключом и в журналах ошибок не показываются.
Разбор моделью (ИИ)
Выключен по умолчанию. Из запросов к модели всегда вырезаются пароли, токены, ключи, хэши паролей и учётные данные в URL; по умолчанию имена, адреса и домены заменяются псевдонимами. «Показать запрос» показывает ровно то, что ушло. Модель ничего не выполняет — команды только показываются.
Проверки в CI
.github/workflows/security.yml на каждый push, pull request и раз в неделю: govulncheck и gitleaks (блокируют), CodeQL для Go и TypeScript, gosec и Trivy (зависимости, манифесты deploy/, Dockerfile) — в Security → Code scanning. Dependabot заводит сгруппированные еженедельные PR с обновлениями зависимостей.