Архитектура связки Cardinal Panel и Cardinal Wings: от браузерного терминала до cgroups v2
Глубокий технический разбор многоуровневой архитектуры: разделение веб-контроллера (Nuxt 4 + Drizzle) и нодового демона на Go, проксирование WebSocket, потоковые логи через SSE и детерминированная адресация srv-<uuid>.
- ▸Панель не имеет прямого доступа к сокету хоста: взаимодействие с узлами происходит строго по защищённому REST API Wings.
- ▸Использование детерминированного префикса `srv-<uuid>` устраняет необходимость синхронизировать случайные хэши контейнеров при переустановках.
- ▸Потоковый вывод ANSI-логов в браузер организован через Server-Sent Events (SSE), а интерактивный TTY-терминал — через защищённый WebSocket-мост.
- ▸Встроенный SFTP-сервер в демоне Wings chroot-изолирует сессию в каталог тома конкретного сервера.
1. Топология и разделение ответственности
При проектировании панели управления серверами (PaaS / Game Server Hosting) критически важно не связывать веб-интерфейс напрямую с системными сокетами контейнерного рантайма на хосте. В Cardinal эта задача решена через строгое трёхуровневое разделение: пользовательский браузер взаимодействует только с Nuxt 4 веб-панелью, панель опрашивает агент Cardinal Wings на целевом сервере, а Wings локально управляет процессами через Cardinal.
[ Browser (Client) ]
│ HTTPS (:443) / WSS (WebSocket Terminal)
▼
[ Cardinal Panel (Nuxt 4 + Drizzle ORM) ] :3000
│ Bearer Auth · Multi-Node Routing
▼
[ Cardinal Wings Daemon (Go) ] :8080 (REST + SSE) · :2022 (SFTP)
│ Loopback IPC / HTTP
▼
[ Cardinal Runtime (serve) ] ~5 MB Go Static Binary
│ clone(2) · setns(2) · pivot_root · cgroups v2
▼
[ Isolated Linux Containers (Namespaces + OverlayFS) ]2. Детерминированная адресация: паттерн `srv-<uuid>`
Традиционные панели часто сохраняют в базе данных сгенерированный рантаймом хэш контейнера (например, a1b2c3d4...). При переустановке сервера (reinstall), обновлении базового образа или аварийном падении хэш меняется, что приводит к рассинхронизации состояния. В связке Panel + Wings контейнер всегда адресуется стабильным именем вида `srv-<server-uuid>`. Демон Wings содержит внутренний резолвер (`resolve.go`), сопоставляющий UUID сервера с живым процессом Cardinal. Панели не требуется мигрировать базу данных при пересоздании контейнера.
// ResolveContainer converts panel server reference to Cardinal container ID
func (r *Resolver) Resolve(serverRef string) (*ContainerMeta, error) {
if !strings.HasPrefix(serverRef, "srv-") {
serverRef = "srv-" + serverRef
}
container, err := r.cardinalClient.Inspect(serverRef)
if err != nil {
return nil, fmt.Errorf("server %s not found on this node: %w", serverRef, err)
}
return container, nil
}3. Потоковый вывод логов и интерактивный терминал
Передача консольного вывода делится на два сценария: чтение архивных и свежих логов, а также интерактивный ввод команд (exec TTY). Для логов Wings предоставляет HTTP эндпоинт с Server-Sent Events (SSE): браузер подписывается на поток, экономя системные ресурсы по сравнению с постоянным polling-опросом. Для интерактивной консоли панель поднимает WebSocket-соединение с Wings, проксируя сырой TTY-поток напрямую в библиотеку xterm.js с поддержкой цветных ANSI-последовательностей.
4. Изолированный SFTP-сервер на порту :2022
Управление конфигурационными файлами серверов (например, server.properties для Minecraft или postgresql.conf) требует безопасного файлового доступа. Вместо запуска системного OpenSSH-сервера хоста, демон Cardinal Wings содержит встроенную реализацию SFTP-сервера на Go. При авторизации пользователя Wings валидирует права доступа к конкретному серверу и выполняет строгий chroot в директорию тома контейнера, предотвращая выход за пределы папки сервера.
# Подключение через стандартный SFTP-клиент
sftp -P 2022 admin.srv-7f8a9b2c@node1.example.com
# Внутренняя структура директории контейнера в Cardinal:
/var/lib/cardinal/containers/srv-7f8a9b2c/
├── config.json # OCI спецификация процесса и cgroups v2
├── rootfs/ # Смонтированный OverlayFS слой
└── data/ # Персистентный том сервера (SFTP chroot root)5. Модель безопасности: Scoped Bearer Tokens и Loopback
Демон Wings по умолчанию слушает локальный интерфейс (loopback) либо защищается внешним mTLS/Nginx прокси. Доступ к API защищён ролевой моделью токенов (Read-Only, Operator, Node Admin). Это гарантирует, что даже в случае компрометации отдельного сервера злоумышленник не получит доступ к управляющему рантайму хоста.
