NexorPanel

Как устроена NexorPanel

NexorPanel — самостоятельная панель управления хостингом: свой интерфейс, свой API, свои учётные записи, своё хранилище и свой агент на машине. Ничего из этого не одалживается у другой программы.

Панель и агент

браузер ──HTTPS──► nexor-api (Go) ──── panel.db (SQLite): учётные записи,
  React/TS SPA          │                сайты, журнал
                        │
                        └──HMAC──► nexor-agent на машине
                                      │
                                      ├─ каталоги сайтов, системные пользователи
                                      ├─ конфигурация веб-сервера + перезагрузка
                                      └─ метрики, службы, файлы, логи

Панель хранит, каким должен быть сервер. Она не ходит по машине, не знает её путей и не запускает на ней программ. Её файл — это ответ на вопрос «как должно быть».

Агент приводит машину к этому состоянию. Он знает свою машину, свои каталоги и свои службы — и ничего не знает о панели, кроме того, что та прислала: имя домена, имя ящика, признак «включено». Путь в запросе не приходит никогда; каталоги агент берёт из своей конфигурации, которую пишет администратор машины, а не панель.

Отсюда главное свойство: тот, кто получил доступ к панели, не получает машину. Он получает право просить агента о том, на что агента настроили, — и ни о чём сверх того. Единственное названное исключение — раздел «Терминал», который выполняет на машине набранную команду; он выключен по умолчанию и включается человеком на машине строкой NEXOR_AGENT_CONSOLE=1 в настройках агента.

Компоненты

ПрограммаЧто делает
nexor-apiПанель: HTTP API, сессии, учётные записи, своё хранилище и собранный интерфейс. Один статический бинарник на Go под systemd, за nginx.
nexor-api doctorПроверяет живую установку с тем же окружением, что и служба: файл данных, настройку агента, ключ сессий, источник запросов, доверенные прокси. Ничего не меняет и пишет, что именно поправить.
nexor-agentПомощник на каждой управляемой машине: каталоги сайтов, системные пользователи, веб-сервер, пулы PHP, сертификаты, почта, DNS, базы, копии, метрики, службы. Принимает только запросы панели, подписанные HMAC.
nexor-hookНеобязательный приёмник вебхука: push в git превращается в обновление сервера.
Веб-интерфейсОдностраничное приложение на React + TypeScript. Это обычный клиент API, и другого пути в панель у него нет — поэтому всё, что умеет интерфейс, умеет и API.

Исходный код

КаталогЧто внутри
apps/apibackend на Go: API панели, хранилище, сессии, агент, doctor
apps/webfrontend на React + TypeScript: оболочка, дашборд, списки, вход; сквозные тесты на Playwright и axe
deploysystemd-юниты, примеры конфигурации, nginx, приёмник вебхука, инструкция по установке
packaging.deb-пакеты панели и агента: control, postinst / prerm / postrm
docsархитектура, API, дорожная карта, макеты и скриншоты

Слои backend:

cmd/nexor-api       точка входа, конфигурация, выбор режима, graceful shutdown
cmd/nexor-agent     агент на управляемой машине
cmd/nexor-hook      приёмник вебхука
internal/config     конфигурация из окружения
internal/native     собственное хранилище: файл, учётные записи, сайты
internal/agent      агент и клиент панели к нему
internal/store      что панели нужно от источника данных: интерфейс и реализации
internal/session    сессии, cookie HttpOnly / SameSite=Strict
internal/httpapi    маршруты, middleware, JSON-ответы и коды ошибок

internal/store — это граница: всё, что умеет панель, описано там интерфейсом. Собственное хранилище, внешний движок и фикстуры встают на одно и то же место, и ни один экран не знает, что за ним.

На сервере

ПутьЧто
/usr/local/bin/nexor-apiбинарник панели, работает от системного пользователя nexor
/usr/local/bin/nexor-agentбинарник агента, работает от root
/usr/local/share/nexorpanel/webсобранный интерфейс (NEXOR_WEB_ROOT)
/etc/nexorpanel/nexor-api.envнастройки панели, chmod 600; пакет создаёт его со сгенерированным NEXOR_SESSION_KEY
/etc/nexorpanel/nexor-agent.envнастройки агента: адрес, общий секрет, службы, каталоги
/var/lib/nexorpanel/panel.dbвсе собственные данные панели (NEXOR_DATA)
nexor-api.service, nexor-agent.servicesystemd-юниты

По умолчанию панель слушает 127.0.0.1:8080, агент — 127.0.0.1:8462; перед панелью стоит nginx или другой терминатор TLS. Одна панель может управлять несколькими машинами: на каждой свой агент, а панели они перечисляются в NEXOR_AGENTS.

Режимы запуска

Режим не задаётся отдельной настройкой, а выводится из остальных.

РежимКогдаЧто делает
Собственныйничего не задано — умолчаниесвоё хранилище, свой агент
Внешний движокзадан NEXOR_ENGINE_URLработает через внешний исполнительный движок по его API; оставлен для установок, где он уже стоит
ФикстурыNEXOR_MOCK=1демонстрационные данные; ничем не управляет, годится посмотреть экраны

Порядок действий

Сначала запись, потом машина. Если запись не удалась, на диске не остаётся ничего, о чём панель не знает. Худший случай — запись без каталога: она видна в списке, её можно применить заново или удалить.

При удалении порядок обратный: сначала снять с машины, потом забыть. Удаление сайта снимает конфигурацию, но не трогает его файлы — конфигурация делается заново за минуту, файлы — неоткуда.

Разделы переписываются на собственный код по одному. Пока раздел не готов, он отвечает «ещё не подключён», а не пустым списком: пустой список читается как «ничего нет».

Учётные записи и роли

  • Роли: администратор (всё, включая серверы, тарифы, межсетевой экран и журналы), реселлер (свои клиенты и их объекты) и клиент (только свои сайты, почта, DNS, базы).
  • Клиент видит только свои объекты, и это обеспечивает запрос, а не фильтр поверх ответа. Чужой объект и несуществующий одинаково отвечают 404.
  • Пароли хранятся bcrypt-хешем. Заблокированная запись сообщает «заблокирована» только тому, кто знает пароль.
  • Сессии — запечатанные cookie (AES-256-GCM, HttpOnly, Secure, SameSite=Strict), перепроверяются на каждом запросе: блокировка, смена пароля или снятая роль заканчивают сессию сразу.
  • Второй фактор — коды TOTP и резервные коды.
  • Программы входят ключами API, а не паролем: ключ на одну задачу, при необходимости только на чтение, со сроком, отзывается отдельно. См. ключи API.
  • Первый администратор заводится при первом запуске. Пароль берётся из NEXOR_ADMIN_PASSWORD или придумывается и один раз пишется в журнал.

Хранилище

Один файл SQLite рядом с панелью: учётные записи, сайты, журнал панели, привязки второго фактора, ключи API и отозванные сессии. Отдельного сервера баз данных нет — панель, которой для запуска нужна ещё одна программа, уже не самостоятельная. Резервная копия — это копия файла; на пустой машине, где есть только бинарник, панель поднимается целиком.

Схема применяется на каждом запуске, каждый шаг идемпотентен, поэтому версиям схемы нечему расходиться. Копии панели можно отправлять и в S3-совместимое хранилище (NEXOR_S3_*), предварительно шифруя ключом NEXOR_BACKUP_KEY.

Настройки

Всё настраивается переменными окружения в файлах .env, перечисленных выше. Главные из них:

ПеременнаяЗначение
NEXOR_ADDRгде слушает панель, например 127.0.0.1:8080
NEXOR_PUBLIC_ORIGINисточник, с которого открывается панель; изменяющие запросы с любого другого отклоняются
NEXOR_SESSION_KEY32 байта в base64 для cookie сессий; без него каждый перезапуск разлогинивает всех
NEXOR_SESSION_TTLсрок сессии, например 8h
NEXOR_DATAфайл данных
NEXOR_WEB_ROOTсобранный интерфейс; пусто, если его отдаёт nginx
NEXOR_ENTRANCEтайный вход: строка в адресе, под которой живёт вся панель
NEXOR_TRUSTED_PROXIESпрокси, чьему X-Forwarded-For можно верить
NEXOR_AGENTSмашины и их агенты
NEXOR_S3_*, NEXOR_BACKUP_KEYвнешнее хранилище для копий панели и ключ их шифрования
NEXOR_NOTIFY_URL, NEXOR_NOTIFY_TOKENкуда отправлять уведомления за пределы панели
NEXOR_IMAP, NEXOR_SMTPпочтовые серверы для почты в браузере
NEXOR_AGENT_LISTEN, NEXOR_AGENT_SECRETсторона агента: адрес и общий секрет HMAC
NEXOR_AGENT_SERVICESсторона агента: какие службы systemd панель может запускать, останавливать и перезапускать
NEXOR_AGENT_CONSOLEсторона агента: 1 включает раздел «Терминал»

После изменений выполните nexor-api doctor.

Правила безопасности

  • Изменяющие запросы — только POST, PUT и DELETE, с проверкой Origin.
  • Административные разделы закрыты отдельным слоем adminOnly, независимо от проверок принадлежности в хранилище.
  • Агент проверяет на каждом запросе подпись HMAC с одноразовым номером; секрет лежит в файле, который читает только root, и никуда не отправляется.
  • Агент запускает только программы из своего списка и с аргументами, которые составил сам. Оболочки нет нигде: точка с запятой в имени — это точка с запятой, а не вторая команда.
  • Попытки входа ограничены: 5 в минуту на связку адрес + логин и отдельный лимит на адрес.
  • Путь к файлу не выходит за пределы папки сайта, в том числе через символические ссылки. Удалённые файлы 30 дней лежат в корзине сайта .nexor-trash.
  • Тайный вход (NEXOR_ENTRANCE) прячет форму входа от сканеров: всё остальное получает одинаковую пустую 404.
  • Заголовки защиты ставит сама служба — политика содержимого без вставных скриптов, запрет рамки, nosniff, Referrer-Policy.
  • Пароли баз данных панель не хранит; phpMyAdmin открывается получасовой учётной записью, заведённой под один вход.

Стек

СлойВыбор
BackendGo, один статический бинарник под systemd; HTTP на стандартной библиотеке, без веб-фреймворка
ХранилищеSQLite одним файлом (modernc.org/sqlite, без cgo)
Паролиbcrypt
FrontendReact 19 + TypeScript + Vite; свои компоненты на дизайн-токенах, свои SVG-графики
Языкирусский, английский, украинский, белорусский, немецкий, польский — API отвечает на языке из Accept-Language
Проверкиgofmt, go vet, go test -race; проверка типов и полноты словарей; тесты установщика; сквозные тесты с проверкой WCAG 2.1 AA в обеих схемах

Справочник API Скачать