РазделДорожная карта
ДокументацияУстройство

Дорожная карта, матрица тестирования, CI

Этап 0 — каркас (выполнен 2026-09-05, проверен на Ubuntu 24.04)

  • Репозиторий, сборка Go (make build), nfpm.yaml, units, sysusers/tmpfiles, install.sh, mp setup.
  • OS Profile для Debian/Ubuntu и EL9/EL10 (+ generic для dev-машин); агент с ApplyConfigSet, EnsureGroup, EnsureUnixUser, EnsureDirs, Service, Pkg; peer-cred на обоих сокетах.
  • SQLite-схема и миграции; auth (argon2id, сессии, Bearer-токены, CSRF-проверка); job-runner с локами; SSE.
  • Установка nginx с nginx.org (mp stack install nginx), шаблоны nginx/Apache/php-fpm с golden-тестами, CLI и TUI.
  • ACME (lego, HTTP-01 по webroot): mp ssl issue/list/renew/rm, таблица certificates, автопродление, hot-swap сертификата панели (mp web tls). Проверено выпуском боевого сертификата Let's Encrypt на тестовом хосте.
  • Сборка SvelteKit-приложения (нужен Node; пока заглушка web/build/index.html).
  • Репозиторий deb/rpm и подпись пакетов.
  • Установка PHP (Sury/Remi) и Percona/MySQL 8.4 с auth_socket — переносится в начало этапа 1.
  • VM-матрица: тестовая площадка на Proxmox с девятью машинами по всей матрице ОС, make testbed-matrix (2026-09-09, см. docs/08-testbed.md). В CI пока не подключена — нужен self-hosted раннер с доступом к хосту.

Этап 1 — MVP (выполнен 2026-09-05, проверен на Ubuntu 24.04)

  • PHP 5.6–8.5 через Sury/Remi (mp php), несколько версий параллельно.
  • Сайты в режимах A (nginx → php-fpm), B (nginx → Apache → php-fpm) и proxy; per-site php_value; ACL; страница-заглушка; авто-сертификат.
  • Apache 2.4 как компонент (Debian/Ubuntu). EL — не проверено.
  • БД: Percona/MySQL 8.4 с auth_socket, базы и пользователи, legacy-аутентификация для PHP < 7.4. phpMyAdmin — не сделан.
  • Cron, firewall (nftables) + fail2ban, бэкапы restic, метрики, логи, mp doctor, DNS-01, TUI-паритет, Web UI (SvelteKit), ru.
  • Полная тест-матрица в CI на 9 ОС (нужен self-hosted раннер с VM).

Этап 2 — v1.0 (частично, 2026-09-05)

  • Файловый менеджер через helper (mp files, API /files), SFTP-chroot, unix-пароли.
  • Webhooks (HMAC), TOTP 2FA, API-токены, история конфигов (confhistory), переопределение шаблонов, mp doctor.
  • Собственные сборки PHP, терминал в браузере, квоты, self-update, WHMCS-модуль, WebAuthn, SELinux confined-домен.

Автоматическое тестирование (2026-09-06)

  • Фейковый агент internal/agent/agenttest: unix-сокет, типизированные ответы, запись всех вызовов — покрывает job-и панели (сайты, пользователи, пресеты) без root и systemd.
  • Тесты пайплайна сайта: рендер nginx/пула, пресеты CMS и их PHP-значения, приоритет php_ini над пресетом, allow-list, отказы валидации, suspend, удаление, свои nginx-директивы с откатом, каскадное удаление пользователя. Покрытие internal/api 9.5% → 21.2%, общее 23.3%.
  • Таймауты готовности вынесены в Server.SetReadinessWaits: тесты не ждут nginx и сокет php-fpm (набор идёт ~2 с).
  • make check (fmt + vet + lint + test), make test-race, make cover, make web-check, make help.
  • golangci-lint v2.13.2 с .golangci.yml; намеренно игнорируемые ошибки помечены //nolint:errcheck с причиной, реальные находки (S1017, S1009, ineffassign, unconvert) исправлены.
  • scripts/check-templates.sh: golden-конфиги проверяются настоящими nginx -t и apachectl -t (в CI ставится nginx-core и apache2).
  • e2e/ (тег e2e): сценарий против живой панели — аккаунт, сайт с пресетом, PHP отвечает, запрет PHP в uploads работает, база, удаление; чистит за собой. Токен или логин/пароль через переменные окружения.
  • Токены по локальному сокету: POST /tokens принимает user, root без аккаунта получает токен единственного администратора (при нескольких — ошибка со списком), администратор видит и отзывает токены любого аккаунта; make e2e HOST=… берёт токен по ssh и отзывает его после прогона.
  • CI: параллельные джобы go / lint / templates / web, кэш модулей и pnpm, -race с покрытием в summary, сборка amd64 + arm64, e2e по workflow_dispatch.

Управление расширениями PHP (2026-09-06)

  • GET|POST /php/versions/{v}/extensions, mp php ext list|enable|disable, переключатели в карточке ветки на странице PHP. Состояние читается из conf.d ветки (phpquery отвечает из реестра Debian и продолжает показывать модуль после phpdismod), список доступных — из mods-available. После переключения php-fpm ветки перезапускается.
  • Только на ветку целиком: php-fpm — один мастер на версию, пулы наследуют его расширения, отключить расширение для отдельного сайта нельзя. В интерфейсе это сказано прямо.
  • Расширения, без которых типовой сайт не работает, помечены и требуют подтверждения; запретов нет — администратор и так root.
  • Агенту добавлена операция dir/list с белым списком каталогов (/etc/php/*/mods-available, */conf.d, /etc/opt/remi/php*/php.d): без неё панель видит только загруженные модули, но не выключенные.

PHP 8.5 (2026-09-06)

  • Установка 8.5 падала: панель просила php8.5-opcache, которого в Sury нет — начиная с 8.5 opcache собран в ядро (php8.5 -m показывает Zend OPcache, opcache.enable => On). Отдельный пакет существует только с 7.0 по 8.4; правило и тест на обе границы — в internal/osprofile/php.go.

Адаптация Web UI под телефоны и планшеты (2026-09-06)

  • Каркас: боковое меню шириной 240px ниже lg превращается в выезжающую панель с затемнением и шапкой с кнопкой; выше — прежняя колонка. Переход по пункту закрывает меню, цели нажатия увеличены.
  • Таблицы: приём один на все девять страниц, потому что все используют класс .tbl. Ниже 40rem строка становится карточкой, заголовки колонок уходят в подписи слева (data-label на ячейках), таблица перестаёт быть таблицей (display: block) — иначе она считает ширину по содержимому и длинные значения уезжают за край. Ячейки без подписи — кнопки действий — держатся справа.
  • Мелочи: тост во всю ширину на телефоне, перенос длинного docroot, панель файлового менеджера в две строки, отступы страницы меньше на узком экране.
  • Проверено на 375×812 и 768×1024 по всем страницам: горизонтального выхода за экран нет нигде, включая редактор Monaco и карточку сайта с вкладками.

Файловый менеджер в Web UI (2026-09-06)

  • FileManager.svelte: обзор домашнего каталога аккаунта, хлебные крошки, переход к каталогу сайта, создание папки и файла, загрузка перетаскиванием и выбором, скачивание, переименование, права, распаковка архивов, множественное выделение и удаление — всё поверх существующего /files-API, то есть от имени владельца через helper.
  • Редактор текстовых файлов: нумерация строк, Ctrl+S, Tab, защита от ухода с несохранёнными правками. Двоичные файлы (по расширению или нулевому байту) и файлы больше 1 МБ открываются только на скачивание. Без подсветки синтаксиса — она означала бы новую npm-зависимость.
  • Страница /files (с выбором аккаунта для администратора) и вкладка «Файлы» в карточке сайта, открывающаяся в его docroot.
  • Редактор — Monaco, то же ядро, что в VS Code (web/static/monaco, MIT): подсветка php/html/css/js/ts/json/xml/ini/shell/sql/yaml/markdown/python/dockerfile, поиск и замена, мультикурсор, свёртка, палитра команд, тема следует за темой панели, высота — по содержимому. Сборка урезана с 24 МБ до 4,7: без языковых служб (IntelliSense-воркеры json/css/html/ts), переводов интерфейса и лишних режимов; бинарник вырос с 26 до 31 МБ. Грузится лениво, при сбое загрузки остаётся простое поле. Вендорится в репозиторий, а не тянется из npm или CDN: панель должна ставиться на сервер без интернета, а node-инструментов в репозитории нет. В CSP добавлен font-src data: — шрифт иконок вшит в CSS Monaco.
  • API: операция touch (fsop touch с O_EXCL) — пустое тело PUT huma не принимает, а создание файла не должно затирать существующий; force опустошает файл. Тесты internal/cli/fsop_test.go на touch и на то, что «..» упирается в домашний каталог.

Обновление панели из релизов (2026-09-06)

  • internal/updater: релизы GitHub (или GitHub Enterprise через api), выбор по версии, а не по дате публикации; канал stable/beta; имена артефактов фиксированы (monopanel_<v>_<arch>.deb, monopanel-<v>.<arch>.rpm, monopanel-linux-<arch>, SHA256SUMS, SHA256SUMS.sig).
  • Подпись релиза ed25519: scripts/release keygen|sign|verify, приватный ключ — секрет репозитория, публичный — update.public_key в config.yaml (root-only, менять из панели нельзя). При заданном ключе неподписанный релиз не устанавливается; агент проверяет подпись повторно, а не доверяет хешу от API-процесса.
  • Установка вне панели: агент POST /v1/panel/install запускает transient-юнит monopanel-update.service (StartTransientUnit), который переживает перезапуск API и агента; mp update-run ставит пакет, перезапускает юниты, ждёт /health с новой версией и откатывает прежний бинарник, если она не отвечает. Итог пишется в <data>/updates/state.json и попадает в audit после перезапуска.
  • API GET|PUT /system/update, POST /system/update/check|apply (только администратор), задача panel.update, ежедневная проверка по расписанию и auto_apply; токен репозитория шифруется секрет-боксом.
  • mp update, mp update check|apply|settings|trust; карточка «Обновление панели» в настройках Web UI ждёт перезапуск и перезагружает страницу; проверка update в mp doctor.
  • Установка последней версии сама считается проверкой (2026-09-24): задача panel.update запоминает найденный релиз, и после mp update apply статус больше не называет последним тот, что видела прошлая проверка.
  • Пакеты: make packages (deb+rpm × amd64+arm64 + бинарники + SHA256SUMS), make release VERSION=…, workflow release.yml по тегу v*; preremove больше не выключает панель при обновлении, postinstall перезапускает юниты только на апгрейде.

Добавлено при миграции сайтов с FASTPANEL (2026-09-05)

  • IP-allow-list на сайт (sites.allow_from, mp site add|set --allow), ACME-проверка остаётся доступной.
  • Импорт готовых сертификатов (POST /certificates/import, mp ssl import); Let's Encrypt-сертификаты продолжают продлеваться через ACME.
  • Доверенные прокси для real_ip (mp stack real-ip --cloudflare, --from), файл http.d/10-real-ip.conf.
  • App-сервисы пользователей (apps, mp app): systemd-юнит от имени пользователя, ProtectSystem=full, PrivateTmp.
  • HSTS в server-блоке и в static-локации при принудительном HTTPS.
  • Web UI: allow-list в настройках сайта, импорт сертификатов, app-сервисы и cron в карточке пользователя, real-ip в настройках.
  • Удаление пользователя (DELETE /users/{login}?purge=, job user.delete, mp user rm): сайты → сертификаты → базы → app-сервисы → crontab → unix-аккаунт (агент user/remove: убивает процессы uid, userdel [-r] только для home под www_root, uid ≥ 1000).
  • Редактирование nginx-директив сайта (sites/<domain>.d/custom.conf, GET|PUT /sites/{domain}/nginx, mp site nginx): nginx -t, откат и текст ошибки в ответе; сгенерированный server-блок показывается read-only.
  • PHP-параметры сайта: GET /sites/{domain}/php (эффективные значения + допустимые ключи), редактор переопределений во вкладке PHP; список ключей расширен (opcache.jit, session.cookie_*, max_file_uploads …).
  • Пресеты CMS (sites.preset, templates/nginx/presets/*.conf.tmpl как {{ define "preset-…" }} в общем наборе шаблонов, presetIni в ops_presets.go): WordPress, Joomla, 1С-Битрикс, OpenCart; выбор в форме создания и в настройках сайта, GET /sites/presets; проверены на nginx реальными запросами (ЧПУ, запреты, /api, urlrewrite, route).
  • Дизайн Web UI 0.4: токены светлой/тёмной темы (prefers-color-scheme + data-theme из localStorage, инициализация до первой отрисовки с хешем в CSP), анимации (переходы страниц, stagger строк, прогресс задач, skeleton), иконки, модальные подтверждения.

Почтовый сервер (2026-09-07)

  • mp mail install: postfix + dovecot + opendkim одной задачей — пакеты, пользователь vmail, конфигурация, сертификат, порты в firewall. Проверено на Ubuntu 24.04: submission с настоящим сертификатом, SASL через dovecot, LMTP-доставка, IMAP/POP3/ManageSieve, подпись DKIM, отклонение подмены отправителя.
  • Домены, ящики и алиасы (включая catch-all) в базе панели; postfix читает hash:-карты, dovecot — passwd-файл с {BLF-CRYPT} и userdb_quota_rule. Выключенный ящик исчезает из обеих карт: почта не принимается, войти нельзя, письма на диске остаются.
  • DKIM: ключ RSA 2048 генерируется панелью, приватный хранится зашифрованным в базе и файлом у opendkim; mp mail domain dns показывает MX/SPF/DKIM/DMARC/PTR и сверяет их с публичными резолверами (1.1.1.1, 8.8.8.8).
  • Сертификат почты — из того же хранилища, что у сайтов; при выпуске и продлении задача cert.issue сама перезапускает postfix и dovecot. Пока сертификата нет, поднимается самоподписанный, и панель об этом предупреждает.
  • Занятый 25-й порт (на dev-хосте его держит приёмник mail-tester) не ломает установку: панель узнаёт чужой демон по баннеру, выключает приём снаружи и говорит об этом. После применения конфигурации порты проверяются на самом деле — юнит postfix рапортует «active» и когда мастер не поднялся.
  • Вебпочта: mp mail webmail <домен> ставит Roundcube 1.7.4 (архив с проверкой sha256) обычным сайтом панели — своя база, свой пул php-fpm, свой сертификат, installer удаляется.
  • Web UI /mail: статус, порты, домены с DNS-подсказками, ящики, алиасы, установка вебпочты; CLI mp mail; тесты на рендер конфигурации, карты, catch-all и уступку 25-го порта.
  • Вебпочта на порту: --port 2096 публикует Roundcube на имени и сертификате почтового сервера (свой серверный блок nginx в http.d, порт в firewall, слушает и на петле) — без отдельной записи в DNS и второго сертификата.
  • Домен-приёмник (--lenient): строгие проверки HELO и отправителя перенесены в общий список с получателем, а карта check_recipient_access обрывает его на OK. Так на dev-хосте postfix занял 25-й порт, а mail-tester забирает почту из служебного ящика по IMAP и берёт IP отправителя из Received — проверено настоящим письмом с внешнего адреса и несуществующим доменом отправителя (отчёт тестера: «IP отправителя 83.97.77.254 (по заголовкам)»).
  • Страница почты грузилась 4,3 с: восемь портов опрашивались подряд и дважды, а на портах с обёрткой TLS проба ждала баннер, которого не бывает. Опрос разом, один раз на запрос → 0,02 с; Web UI забирает четыре списка параллельно и показывает скелет.
  • dovecot 2.4 (Debian 13, Ubuntu 26.04) — другой синтаксис конфигурации; установка отказывается, пока шаблон не написан и не проверен.
  • EL 9/10: пакеты есть (opendkim из EPEL), конфигурация не проверялась.
  • Антиспам глубже postfix-проверок (rspamd), смена пароля ящика из Roundcube, автонастройка почтовых клиентов (autoconfig/autodiscover).

Перенос между панелями (2026-09-09)

  • Прямой перенос MonoPanel → MonoPanel: mp migrate grant на источнике, mp migrate plan (разбор с конфликтами, ничего не меняет) и mp migrate run на приёмнике. Схема — 07-migration.md.
  • Источник только читает: состояние, поток файлов (tar) и поток дампа. Токен с областью migrate:user:<логин> пускает только на GET под /migrate и только для своего аккаунта; прочие области остаются пометками, чтобы не сломать выпущенные раньше токены.
  • Пароли переезжают хешами: argon2id панели, yescrypt из shadow (chpasswd -e), SHOW CREATE USER ... AS '<хеш>' для MySQL, {BLF-CRYPT} для ящиков. Пользователи переезда не замечают, панель нигде не видит открытый текст.
  • Потоки идут насквозь через две новые операции агента (/v1/stream/out, /v1/stream/in) и рекурсивный chown: домашний каталог нигде не складывается на диск целиком.
  • Тест на две панели сразу: фикстура-источник и фикстура-приёмник, между ними настоящий HTTP — проверяются область токена, разбор, перенос аккаунта, сайта и cron, хеш пароля и оба потока tar. На живом сервере проверена отдающая половина: поток файлов, состояние с секретами и сертификатом.
  • Досинхронизация (resync) и завершение (finish) с заказом сертификатов после переключения DNS.
  • Адаптеры для чужих панелей (2026-09-12): --from bitrixvm и --from fastpanel читают старый сервер по ssh и отдают тот же бандл; проверены на площадке против bitrix-env 9 и FASTPANEL 1.11 (scripts/testbed/sources.sh).
  • Области site: и server:, пакет-файл, адаптер plain.

Два языка Web UI (2026-09-13)

  • Весь Web UI переведён на английский; русский и английский лежат в словаре web/src/lib/i18n/msgs/*.ts (фрагмент на страницу), t()/tn() берут текущий язык из стейта — переключение перерисовывает интерфейс без перезагрузки.
  • Язык по умолчанию — из языка браузера: русский для языков стран СНГ (ru, uk, be, kk, ky, uz, tg, tk, hy, az, ka, ro-MD…), английский для всех остальных; тот же список в app.html, чтобы <html lang> был верным до старта приложения.
  • Переключатель «Авто / Русский / English» в настройках и на экране входа; выбор — в localStorage.lang; даты и единицы (when(), bytes()) следуют языку.
  • Сообщения API, журналы задач и doctor — на английском (2026-09-25); заглушка нового сайта и страница приостановленного показываются на языке браузера посетителя, пресеты и каталог CMS интерфейс переводит сам.
  • CLI и TUI пока только на русском; русские сообщения API для русского интерфейса — со словарём на стороне Go и языком у задачи.

Firewall: порядок правил и защита от самоблокировки (2026-09-14)

  • Цепочка собирается по полкам: allow с источником → deny → всегда открытые порты и allow без источника. Раньше deny шёл первым, и «deny 8443 + allow 8443 с VPN» закрывал панель для всех, включая VPN.
  • Guard: deny без источника на SSH или порт панели принимается только при наличии allow с источником; удаление последнего такого allow, а также deny или ban, накрывающие адрес запроса, отклоняются. Статус показывает закрытые порты и кому они открыты (restricted, строка в mp firewall status, теги на странице).

Статика: сжатие и кэш (2026-09-15)

  • Редактор открывался долго: http.FileServer отдавал вшитую статику без gzip, ETag и Cache-Control, и каждая перезагрузка страницы заново тянула 4,4 МБ Monaco шестью последовательными запросами. Теперь файл сжимается gzip один раз в память (Monaco 4,4 → 1,2 МБ), хешированные чанки (_app/immutable/*, name-<hash>.js) кэшируются на год как immutable, остальное — no-cache со слабым ETag и 304. FileManager подгружает Monaco в idle сразу при открытии страницы файлов, а не по клику.

Документация API без чужого origin (2026-09-21)

  • Страница /api/v1/docs грузила Stoplight Elements с unpkg.com, а для путей /api/ панель не ставит CSP: сторонний скрипт работал на origin панели рядом с сессией администратора. Теперь Elements 9.0.15 вшит в бинарник (internal/api/elements, Apache-2.0), страница и её файлы отдаются самой панелью с CSP default-src 'none'; script-src 'self' без inline-скриптов и eval; тест пинит sha384 файлов к релизу npm и отсутствие чужих адресов на странице.
  • Документация, спецификация OpenAPI (/openapi*) и JSON-схемы (/schemas/*) закрыты за авторизацией: сессия или API-токен любого аккаунта, токен переезда — 403, аноним — 401 (браузеру по ссылке на /docs — редирект на вход). Без входа остаются только /health и /auth/login.
  • /health отдаёт версию панели только вошедшему (сессия, токен, локальный сокет); анониму — status и time. Экран входа версию больше не показывает.

Глобальные PHP-параметры (2026-09-23)

  • У значений php.ini был источник «панель», который менялся только по одному сайту. Теперь слои: панель → глобально → пресет → сайт. Глобальный слой — GET|PUT /php/settings (админ), mp php ini [set k=v|unset k], карточка «PHP-параметры для всех сайтов» на странице PHP; сохранение пересобирает пулы всех PHP-сайтов (site.apply на каждый). Вкладка PHP сайта показывает источник «глобально» и порядок слоёв.

Ротация логов сайтов (2026-09-24)

  • Логи в /var/www/<логин>/data/logs не ротировал никто: панель не писала конфигурацию logrotate, а конфиги дистрибутива этот путь не покрывают. Теперь /etc/logrotate.d/monopanel-sites — блок на аккаунт с su <логин> (каталог клиентский, root в нём ничего не делает): раз в неделю или по 100 МБ, восемь копий, сжатие со второй, create 0660 <логин> <логин>. Все логи сайта — аккаунта с правами 0660: панель создаёт их до того, как их откроют nginx, Apache и php-fpm, а созданные ими раньше передаёт аккаунту — новым флагом mode операции агента chown, через открытый файл (O_NOFOLLOW, fchown, fchmod). Иначе logrotate 3.18 на EL9 не справлялся: ротируя, он открывает лог, а сжимая — на запись. Файл переписывается при создании и удалении аккаунта (строка удаляемого уходит до userdel, чтобы logrotate не встретил несуществующего пользователя) и при старте панели, перед записью проверяется logrotate --debug.
  • Рабочие процессы nginx не могли переоткрыть логи сайтов после USR1 — ни после этой ротации, ни после ночной ротации самого nginx: в data/logs не было прохода для группы monopanel-web, и в журнал nginx каждую ночь падало emerg … Permission denied, а запись продолжалась в старый файл. data/logs получила ACL g:monopanel-web:x при создании сайта и mp site fix, существующие аккаунты — при старте панели. Apache переоткрывает логи graceful-рестартом по USR1 через pid-файл: systemctl из logrotate на EL упирается в SELinux.
  • slow-лог php-fpm на EL был пустым: трассировку php-fpm снимает через ptrace рабочего процесса, а штатная политика молча запрещает httpd_t возможность sys_ptrace. Панель ставит свой модуль политики monopanel (CIL, semodule -i) с allow httpd_t self:capability sys_ptrace и self:process ptrace — вместе с подготовкой хоста под хостинг, а на уже подготовленных хостах при старте, без повторного relabel.
  • Проверено на Debian 13 (с Apache), Ubuntu 22.04 (logrotate 3.19), Rocky 9 (3.18) и AlmaLinux 10 в enforcing: после ротации nginx, Apache, PHP и php-fpm пишут в новые файлы, вторая ротация сжимает первую от имени аккаунта, в slow-логе на EL есть трассировки, logrotate.service проходит, отказов SELinux нет.

Valkey на аккаунт (2026-09-24)

  • mp stack install valkey ставит Valkey из дистрибутива (Debian 13, Ubuntu 24.04/26.04, EL9/EL10 AppStream, Debian 12 — из backports, если они подключены), а где его нет — Ubuntu 22.04, Debian 12 без backports — Redis (6.0 и 7.0) с тем же протоколом; общий экземпляр пакета (127.0.0.1:6379 без пароля для всех) останавливается и выключается. Сервер не удалить, пока на нём есть экземпляры.
  • У аккаунта два отдельных экземпляра: cache (allkeys-lru, --save "") и sessions (volatile-lru, снимок раз в минуту). Каждый — monopanel-valkey-<login>-<purpose>.service от имени пользователя, Type=notify (перезапуск возвращается, когда снимок загружен и сокет принимает), MemoryMax вдвое больше maxmemory с запасом, конфигурация целиком в аргументах (к /etc/monopanel у аккаунта доступа нет). Слушает только unix-сокет с правами 600 в каталоге 0700 (RuntimeDirectory=), снимки в StateDirectory=; mp valkey add|list|restart|rm, /users/{login}/valkey[/{purpose}], кнопка valkey на странице «Пользователи». Удаление аккаунта уносит его экземпляры, перенос между панелями переносит их настройки без данных.
  • PHP-сессии сайта в Valkey: mp site set <домен> --sessions valkey|files, поле «PHP-сессии» в настройках сайта; пул получает session.save_handler = redis и путь unix://…/valkey.sock через php_admin_value. Нужны экземпляр sessions и расширение redis у ветки PHP сайта; пока сайт держит там сессии, экземпляр и расширение не удалить (409), сайт в режиме proxy сессий не держит.
  • SELinux (EL): сокеты — redis_var_run_t, каталоги снимков — redis_var_lib_t, сам /var/lib/monopanel-valkey остаётся var_lib_t: init_t не может добавлять записи в каталог redis_var_lib_t, и StateDirectory= падал с EACCES без единого AVC. Юнит без ProtectKernelTunables=, ProtectKernelModules= и RestrictAddressFamilies=: для не-root User= они включают NoNewPrivileges, а политика EL9 под ним не пускает переход init_t → redis_t, и сервер оставался в init_t без права писать снимки. Живая проверка на Debian 13, Ubuntu 22.04, AlmaLinux 10 и Rocky 9 (enforcing): другой аккаунт и веб-сервер получают «Permission denied», PHP-сессия через php-fpm переживает перезапуск, AVC нет.

Этап 3 — v1.x

  • Режим proxy для Node/Python/Docker-приложений.
  • Почта: postfix + dovecot + opendkim + Roundcube (Debian/Ubuntu).
  • Перенос аккаунтов между панелями (MonoPanel → MonoPanel).
  • Изолированные пулы с cgroup-лимитами, FTP, DNS (PowerDNS), WAF, reseller-роль, aarch64-сборка (кросс-компиляция готова: make build-arm64), multi-server.

Матрица ОС

ОСРежимыPHP (выборочно)СУБД
Debian 12, 13A, B5.6, 7.4, 8.2, 8.5Percona 8.4, MySQL 8.4
Ubuntu 22.04, 24.04, 26.04A, B7.4, 8.3, 8.5Percona 8.4
AlmaLinux, Rocky Linux, Oracle Linux 9, 10A, B7.4, 8.4, 8.5 (+ 5.6 на EL9)Percona 8.4, MySQL 8.4

Раннер: площадка на Proxmox — одиннадцать VM из cloud-образов с cloud-init и откатом к чистому снимку; запускается с рабочего места, не в CI. Контейнеров недостаточно для SELinux, nftables, квот и systemd-слайсов.

Сценарий make testbed-matrix: установка пакета → mp setup → nginx, PHP, СУБД → e2e (пользователь → сайт с пресетом → база → nginx и PHP отвечают → удаление) → mp doctor без ошибок. Перед релизом make testbed-full добавляет переносы между панелями, CMS на каждой машине и переезд с BitrixVM и FASTPANEL. Планируются: HTTPS через Pebble, бэкап → восстановление → suspend → purge.

Планируется периодический job (еженедельно): доступность пакетов в вендорских репозиториях по матрице и появление новых версий (PHP 8.6/9.0, nginx stable, Percona 8.4.x, релизы ОС).

Риски и как закрыты

РискМитигация
Sury/Remi исчезнут или сломают совместимостьЭтап 2: собственные сборки и репозиторий; OS Profile позволяет держать оба источника одновременно
MySQL 9.x удалит mysql_native_passwordОстаёмся на 8.4 LTS до 2032; для legacy PHP — только 8.4; переход на 9.x — ручная процедура
Новый релиз ОС без пакетов вендоровПоддержка объявляется только после прохождения матрицы; еженедельная проверка
Ручные правки конфигов администраторомInclude-каталоги, confhistory, mp doctor показывает дрейф, mp site apply перегенерирует сайт
Компрометация Web-слояapi без привилегий; агент с allow-list; helper с setuid; секреты зашифрованы
Рост числа сайтов (сотни)pm=ondemand, один master на версию, SQLite с WAL держит десятки тысяч сущностей; метрики — ролапы
Исходник страницы на GitHub