diff --git a/README.md b/README.md index b272ae2..8c159f4 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ systemd ──> pzmanager ──> start-server.sh ──> java (GameServer) ## Быстрый старт -Собрать бинарник (нужен Go 1.24+): +Собрать бинарник (нужен Go 1.24+; собирать можно на любой ОС, включая Windows): ```bash make build-linux @@ -59,8 +59,30 @@ make build-linux sudo ./deploy/install.sh ``` -Установщик заведёт пользователя `pzserver`, поставит зависимости (SteamCMD, -JRE, SDL2), положит бинарник в `/usr/local/bin` и включит systemd-юнит. +Установщик заведёт системного пользователя `pzserver`, поставит зависимости +(SteamCMD, JRE, SDL2), положит бинарник в `/usr/local/bin`, создаст структуру +в `/opt/pzmanager` и включит systemd-юнит: + +``` +/opt/pzmanager/ +├── config.yaml настройки менеджера +├── data/ учётки панели и скачанный SteamCMD +├── server/ серверные файлы Project Zomboid +├── Zomboid/ миры, конфиги и логи игры +└── backups/ архивы +``` + +Куда ставить и на каком адресе слушать, задаётся переменными окружения: + +```bash +# панель доступна в локальной сети по адресу машины +sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh + +# другой каталог и пользователь +sudo PZ_HOME=/srv/pz PZ_USER=zomboid ./deploy/install.sh +``` + +По умолчанию панель слушает `127.0.0.1:8080` — только с самой машины. Дальше: @@ -70,29 +92,30 @@ JRE, SDL2), положит бинарник в `/usr/local/bin` и включи journalctl -u pzmanager -n 30 --no-pager ``` -2. Откройте панель. По умолчанию она слушает только `127.0.0.1:8080`, так что - зайти на неё можно через SSH-туннель: +2. Откройте панель в браузере и создайте администратора, введя этот код. + Если панель слушает localhost, пробросьте порт: ```bash ssh -L 8080:127.0.0.1:8080 пользователь@сервер ``` - Затем откройте и создайте администратора, введя код - из журнала. - 3. Во вкладке «Обзор» нажмите **Установить / обновить через SteamCMD** и дождитесь окончания — прогресс виден во вкладке «Консоль». -4. Запустите сервер. При первом запуске PZ создаст `~/Zomboid/Server/*.ini` — +4. Запустите сервер. При первом запуске PZ создаст `Zomboid/Server/*.ini` — после этого станут доступны вкладки «Конфиг сервера», «Песочница» и «Моды». -Не забудьте открыть игровые порты: +Порты, которые нужно открыть: ```bash -sudo ufw allow 16261/udp -sudo ufw allow 16262/udp +sudo ufw allow 8080/tcp # веб-панель, если она слушает не localhost +sudo ufw allow 16261/udp # игровой порт +sudo ufw allow 16262/udp # второй игровой порт ``` +Обновление ставится поверх: соберите новый бинарник и повторите +`sudo ./deploy/install.sh` — конфиг, миры и учётки останутся на месте. + ## Моды Project Zomboid хранит моды в двух строках конфига: `WorkshopItems` — что @@ -160,6 +183,11 @@ Mod ID, который нужен серверу, лежит внутри сам **SSH-туннель (по умолчанию)** — ничего настраивать не нужно, панель слушает только localhost. +**Доверенная локальная сеть** — `PZ_LISTEN=:8080` при установке или +`listen` в конфиге. Учтите, что HTTP не шифруется: пароль от панели идёт по +сети открытым текстом, так что для доступа через интернет этот вариант не +годится. + **HTTPS через reverse-proxy** — если панель нужна с телефона. Настройте nginx с сертификатом (например, от Let's Encrypt): @@ -200,14 +228,15 @@ pzmanager users # список пользователей ## Конфигурация -Файл `~/.config/pzmanager/config.yaml` создаётся при первом запуске. Всё, что в -нём есть, правится и через вкладку «Настройки». +Файл `/opt/pzmanager/config.yaml` создаёт установщик (при запуске без него +менеджер возьмёт `~/.config/pzmanager/config.yaml`). Всё, что в нём есть, +правится и через вкладку «Настройки». ```yaml -listen: 127.0.0.1:8080 # адрес веб-панели -data_dir: /home/pzserver/.local/share/pzmanager -server_dir: /home/pzserver/pzserver # куда SteamCMD ставит сервер -zomboid_dir: /home/pzserver/Zomboid # миры, конфиги и логи игры +listen: 127.0.0.1:8080 # адрес веб-панели; 0.0.0.0 или IP — доступ по сети +data_dir: /opt/pzmanager/data +server_dir: /opt/pzmanager/server # куда SteamCMD ставит сервер +zomboid_dir: /opt/pzmanager/Zomboid # миры, конфиги и логи игры steamcmd_path: "" # пусто — найти в PATH или скачать profiles: # серверы: у каждого свой мир и настройки - id: vanilla # vanilla -> vanilla.ini, Saves/Multiplayer/vanilla @@ -224,7 +253,7 @@ autorestart: true # поднимать сервер после п stop_timeout: 1m30s # сколько ждать после quit до SIGTERM log_buffer_lines: 5000 # строк консоли в памяти backup: - dir: /home/pzserver/pzbackups + dir: /opt/pzmanager/backups schedule: 6h # 0 — отключить автобэкапы keep: 20 # архивов на профиль stop_server: false # останавливать сервер на время бэкапа diff --git a/cmd/pzmanager/main.go b/cmd/pzmanager/main.go index 4a4eded..c8e9d85 100644 --- a/cmd/pzmanager/main.go +++ b/cmd/pzmanager/main.go @@ -8,6 +8,7 @@ import ( "errors" "flag" "fmt" + "net" "net/http" "os" "os/signal" @@ -147,6 +148,15 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error { IdleTimeout: 120 * time.Second, } + // Адрес занимаем до автозапуска игрового сервера: если его не поднять + // (IP ещё не назначен, порт занят), лучше упасть сразу, чем оставить + // работающий сервер PZ без панели управления. + listener, err := net.Listen("tcp", cfg.Listen) + if err != nil { + return fmt.Errorf("не удалось занять адрес %s: %w", cfg.Listen, err) + } + fmt.Printf("pzmanager слушает http://%s\n", cfg.Listen) + if cfg.AutoStart { if err := supervisor.Start(); err != nil { supervisor.AppendLog("manager", "Автозапуск не удался: "+err.Error()) @@ -155,8 +165,7 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error { errCh := make(chan error, 1) go func() { - fmt.Printf("pzmanager слушает http://%s\n", cfg.Listen) - if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + if err := httpServer.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { errCh <- err } }() diff --git a/deploy/install.sh b/deploy/install.sh index fa2440b..9849efc 100644 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -1,13 +1,21 @@ #!/usr/bin/env bash -# Установка pzmanager на Ubuntu. Запускать от root: +# Установка pzmanager на чистый Ubuntu/Debian. Запускать от root: +# # sudo ./deploy/install.sh # -# Скрипт: заводит системного пользователя, кладёт бинарник в /usr/local/bin, -# ставит systemd-юнит и зависимости, нужные серверу Project Zomboid. +# Переменные окружения: +# PZ_HOME куда ставить всё хозяйство (по умолчанию /opt/pzmanager) +# PZ_USER системный пользователь (по умолчанию pzserver) +# PZ_LISTEN адрес веб-панели (по умолчанию 127.0.0.1:8080) +# BINARY путь к собранному бинарнику (по умолчанию ./pzmanager) +# +# Пример — панель доступна в локальной сети по адресу машины: +# sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh set -euo pipefail PZ_USER="${PZ_USER:-pzserver}" -PZ_HOME="${PZ_HOME:-/home/${PZ_USER}}" +PZ_HOME="${PZ_HOME:-/opt/pzmanager}" +PZ_LISTEN="${PZ_LISTEN:-127.0.0.1:8080}" BINARY="${BINARY:-./pzmanager}" UNIT_SRC="$(dirname "$0")/pzmanager.service" @@ -23,27 +31,69 @@ fi echo "==> Устанавливаю системные зависимости" apt-get update -qq -# lib32gcc-s1 нужен SteamCMD, остальное — самому серверу PZ. +# lib32gcc-s1 нужен SteamCMD, остальное — самому серверу PZ. Свою JRE сервер +# приносит с собой, системная нужна как запасной вариант. apt-get install -y --no-install-recommends \ ca-certificates curl lib32gcc-s1 libsdl2-2.0-0 openjdk-17-jre-headless if ! id "$PZ_USER" &>/dev/null; then echo "==> Создаю пользователя $PZ_USER" - adduser --system --group --home "$PZ_HOME" --shell /bin/bash "$PZ_USER" + adduser --system --group --home "$PZ_HOME" --shell /usr/sbin/nologin "$PZ_USER" fi echo "==> Копирую бинарник в /usr/local/bin/pzmanager" install -m 0755 "$BINARY" /usr/local/bin/pzmanager -echo "==> Готовлю директории" +echo "==> Готовлю директории в $PZ_HOME" install -d -o "$PZ_USER" -g "$PZ_USER" -m 0755 \ - "$PZ_HOME/.config/pzmanager" \ - "$PZ_HOME/.local/share/pzmanager" \ - "$PZ_HOME/pzserver" \ - "$PZ_HOME/pzbackups" + "$PZ_HOME" \ + "$PZ_HOME/data" \ + "$PZ_HOME/server" \ + "$PZ_HOME/Zomboid" \ + "$PZ_HOME/backups" + +# Конфиг создаётся только при первой установке: обновление не должно +# затирать настройки, которые уже поправили руками или через панель. +CONFIG="$PZ_HOME/config.yaml" +if [[ -f "$CONFIG" ]]; then + echo "==> Конфиг $CONFIG уже есть — оставляю как есть" +else + echo "==> Создаю конфиг $CONFIG (панель будет слушать $PZ_LISTEN)" + cat > "$CONFIG" < Ставлю systemd-юнит" -sed -e "s|/home/pzserver|${PZ_HOME}|g" \ +sed -e "s|/opt/pzmanager|${PZ_HOME}|g" \ -e "s|User=pzserver|User=${PZ_USER}|" \ -e "s|Group=pzserver|Group=${PZ_USER}|" \ "$UNIT_SRC" > /etc/systemd/system/pzmanager.service @@ -51,15 +101,28 @@ sed -e "s|/home/pzserver|${PZ_HOME}|g" \ systemctl daemon-reload systemctl enable --now pzmanager +# Дадим сервису секунду подняться, чтобы код настройки успел попасть в журнал. +sleep 2 + echo -echo "Готово. Панель запущена." -echo "Код первичной настройки смотрите в журнале:" +echo "Готово. Панель запущена и слушает ${PZ_LISTEN}." echo -echo " journalctl -u pzmanager -n 30 --no-pager" +if [[ "$PZ_LISTEN" == 127.0.0.1:* || "$PZ_LISTEN" == localhost:* ]]; then + echo "Панель доступна только с самой машины. Пробросьте порт:" + echo + echo " ssh -L 8080:${PZ_LISTEN} <пользователь>@<сервер>" + echo +else + PORT="${PZ_LISTEN##*:}" + echo "Откройте в браузере: http://${PZ_LISTEN}" + echo + echo "Если включён firewall, разрешите порт панели:" + echo + echo " sudo ufw allow ${PORT}/tcp" + echo +fi +echo "Код первичной настройки — в журнале:" echo -echo "По умолчанию панель слушает 127.0.0.1:8080." -echo "Откройте её через SSH-туннель:" +echo " journalctl -u pzmanager -n 30 --no-pager | grep -A2 'код первичной'" echo -echo " ssh -L 8080:127.0.0.1:8080 <пользователь>@<сервер>" -echo -echo "Не забудьте открыть игровые порты: udp/16261 и udp/16262." +echo "Игровые порты сервера: udp/16261 и udp/16262." diff --git a/deploy/pzmanager.service b/deploy/pzmanager.service index fc66ff3..b4e1c58 100644 --- a/deploy/pzmanager.service +++ b/deploy/pzmanager.service @@ -7,8 +7,8 @@ Wants=network-online.target Type=simple User=pzserver Group=pzserver -WorkingDirectory=/home/pzserver -ExecStart=/usr/local/bin/pzmanager serve --config /home/pzserver/.config/pzmanager/config.yaml +WorkingDirectory=/opt/pzmanager +ExecStart=/usr/local/bin/pzmanager serve --config /opt/pzmanager/config.yaml Restart=on-failure RestartSec=5 @@ -18,12 +18,12 @@ KillSignal=SIGTERM KillMode=mixed TimeoutStopSec=300 -# Панель пишет только в свои директории. +# Всё своё панель держит в /opt/pzmanager, домашние каталоги ей не нужны. NoNewPrivileges=true PrivateTmp=true ProtectSystem=full -ProtectHome=read-only -ReadWritePaths=/home/pzserver +ProtectHome=true +ReadWritePaths=/opt/pzmanager StandardOutput=journal StandardError=journal