Files
redl-gamepanel/install-node.sh
T
redl 6ee2744e3a Сборка REDL: панель на PHP 8, капча-тумблер, закрытые уязвимости, установщики в один клик
- панель работает на PHP 8.4 (оригинал под PHP 7.0): elFinder utf8_encode, apache_get_modules, warnings
- капча выключена и управляется из админки (флаг captcha_enable + блок настроек)
- закрыты две SQL-инъекции без авторизации (createAuthLog: пароль из POST и CF-Connecting-IP)
- убрано хранение паролей в открытом виде в authlog, XSS в поле ref
- установщики install-panel.sh и install-node.sh (оригинальный затирал sources.list репозиториями Debian 9)
- современный Dockerfile (оригинальный на debian:stretch больше не собирается)
- планировщик и автозапуск работают без systemd
- брендинг REDL.IO, год 2026, ссылки на redl.io
- документация на русском: ИЗМЕНЕНИЯ, БЕЗОПАСНОСТЬ, ИНСТРУКЦИЯ
2026-07-30 02:00:01 +00:00

253 lines
16 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
#
# Установка игровой ноды (локации) в один клик.
# Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом.
#
# Нода — это машина, на которой реально крутятся игровые сервера.
# Панель подключается к ней по SSH и создаёт docker-контейнер на каждый сервер.
#
# Что делает скрипт:
# 1. Проверяет, что docker здесь вообще может работать (в контейнерных VPS — не может)
# 2. Ставит docker, собирает образ debian:stretch (имя ждёт панель)
# 3. Создаёт раскладку /home/cp/gameservers/files и группу gameservers
# 4. Ставит MariaDB для баз игровых серверов и SteamCMD
# 5. Настраивает SSH и ProFTPD так, как ожидает панель
# 6. Печатает данные для подключения локации в админке
#
# Запуск: sudo bash install-node.sh
#
set -euo pipefail
C_OK=$'\033[1;32m'; C_ERR=$'\033[1;31m'; C_INF=$'\033[1;36m'; C_W=$'\033[1;33m'; C_0=$'\033[0m'
step() { printf '%s»%s %s\n' "$C_INF" "$C_0" "$1"; }
ok() { printf ' %s✓%s %s\n' "$C_OK" "$C_0" "$1"; }
warn() { printf ' %s!%s %s\n' "$C_W" "$C_0" "$1"; }
die() { printf '%s✗ %s%s\n' "$C_ERR" "$1" "$C_0" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "Запустите скрипт от root: sudo bash $0"
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
printf '\n%s=== Игровая нода · установка ===%s\n' "$C_INF" "$C_0"
printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n'
# ── 0. проверка пригодности машины ───────────────────────────────────────────
step "Проверяю, может ли эта машина запускать docker…"
CAPFAIL=0
if ! unshare -Ur true 2>/dev/null; then
CAPFAIL=1
warn "Ядро запрещает вложенные namespace (unshare не работает)"
fi
if [[ ! -e /dev/fuse && $CAPFAIL -eq 1 ]]; then
warn "Нет /dev/fuse"
fi
if [[ $CAPFAIL -eq 1 ]]; then
printf '\n%sЭТА МАШИНА НЕ ПОДХОДИТ ПОД ИГРОВУЮ НОДУ.%s\n' "$C_ERR" "$C_0"
printf 'Панель разворачивает каждый игровой сервер отдельным docker-контейнером,\n'
printf 'а здесь docker не запустится: это контейнерный VPS (LXC/OpenVZ или\n'
printf 'аналог) без CAP_SYS_ADMIN. Rootless podman тоже не поможет — ядро\n'
printf 'запрещает вложенные user namespace.\n\n'
printf 'Нужен сервер с полным доступом к ядру: выделенный сервер (dedicated)\n'
printf 'или KVM/VMware-виртуализация. Панель при этом может жить где угодно,\n'
printf 'включая контейнерный VPS — ей docker не нужен.\n\n'
read -rp "Всё равно продолжить установку? (yes/no): " GO
[[ "$GO" == "yes" ]] || exit 1
else
ok "Namespace доступны, docker будет работать"
fi
read -rp "IP этой ноды (как её увидит панель): " NODEIP
[[ -n "$NODEIP" ]] || die "IP не может быть пустым"
read -rp "Разрешить панели вход по SSH под root с паролем? (yes/no): " SSHROOT
. /etc/os-release 2>/dev/null || true
CODENAME="${VERSION_CODENAME:-bookworm}"
DISTRO_ID="${ID:-debian}"
step "Система: ${PRETTY_NAME:-неизвестна}"
export DEBIAN_FRONTEND=noninteractive
# ── 1. базовые пакеты ────────────────────────────────────────────────────────
step "Ставлю базовые пакеты…"
apt-get update -qq
apt-get install -y -qq curl wget ca-certificates gnupg unzip tar sudo pwgen htop \
openssh-server mariadb-server proftpd-basic lib32stdc++6 >/dev/null 2>&1 || \
apt-get install -y -qq curl wget ca-certificates gnupg unzip tar sudo pwgen htop \
openssh-server mariadb-server proftpd lib32stdc++6 >/dev/null
ok "Пакеты установлены"
# ── 2. docker ────────────────────────────────────────────────────────────────
step "Ставлю Docker…"
if command -v docker >/dev/null 2>&1; then
ok "Docker уже установлен: $(docker --version 2>/dev/null | head -1)"
else
install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/$DISTRO_ID/gpg" -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$DISTRO_ID $CODENAME stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io >/dev/null
ok "Docker установлен из официального репозитория"
fi
service docker start >/dev/null 2>&1 || /etc/init.d/docker start >/dev/null 2>&1 || true
sleep 3
docker info >/dev/null 2>&1 || die "Docker-демон не поднялся. Без него нода не заработает"
ok "Docker-демон отвечает"
# ── 3. образ игровых серверов ────────────────────────────────────────────────
step "Собираю образ для игровых серверов (тег debian:stretch — его ждёт панель)…"
if docker image inspect debian:stretch >/dev/null 2>&1; then
ok "Образ debian:stretch уже собран"
else
DF="$SRC/docker/Dockerfile"
[[ -f "$DF" ]] || die "Не найден $DF — распакуйте репозиторий целиком"
docker build -t debian:stretch -f "$DF" "$SRC/docker" >/tmp/redl-docker-build.log 2>&1 \
|| { tail -25 /tmp/redl-docker-build.log; die "Сборка образа не удалась, полный лог: /tmp/redl-docker-build.log"; }
ok "Образ собран (Debian 12 + 32-битные библиотеки + Java + Node.js + screen)"
fi
# ── 4. раскладка каталогов ───────────────────────────────────────────────────
step "Создаю раскладку каталогов…"
groupadd -f gameservers
mkdir -p /home/cp/backups /home/cp/gameservers/files
chown -R root:root /home
chmod 755 /home /home/cp /home/cp/gameservers /home/cp/gameservers/files
chmod 700 /home/cp/backups
ok "/home/cp/gameservers/files — сюда кладутся сборки игр, /home/gs<id> — сами сервера"
# ── 5. база для игровых серверов ─────────────────────────────────────────────
step "Настраиваю MariaDB для баз игровых серверов…"
mkdir -p /run/mysqld && chown mysql:mysql /run/mysqld
if ! mysqladmin ping >/dev/null 2>&1; then
service mariadb start >/dev/null 2>&1 || /etc/init.d/mariadb start >/dev/null 2>&1 || true
for _ in $(seq 1 45); do mysqladmin ping >/dev/null 2>&1 && break; sleep 2; done
fi
mysqladmin ping >/dev/null 2>&1 || die "MariaDB не поднялась"
SQLCNF=/etc/mysql/mariadb.conf.d/50-server.cnf
if [[ -f "$SQLCNF" ]]; then
sed -i 's/^bind-address.*/bind-address = 0.0.0.0/' "$SQLCNF"
grep -q '^max_connections' "$SQLCNF" || sed -i '/^\[mysqld\]/a max_connections = 1000' "$SQLCNF"
fi
DBADMPASS="$(pwgen -cns -1 20)"
mysql -e "CREATE USER IF NOT EXISTS 'gsadmin'@'%' IDENTIFIED BY '$DBADMPASS';
ALTER USER 'gsadmin'@'%' IDENTIFIED BY '$DBADMPASS';
GRANT ALL PRIVILEGES ON *.* TO 'gsadmin'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;"
service mariadb restart >/dev/null 2>&1 || /etc/init.d/mariadb restart >/dev/null 2>&1 || true
sleep 3
umask 077; printf 'mysql_user=gsadmin\nmysql_pass=%s\n' "$DBADMPASS" > /root/.redl-node-credentials
warn "MariaDB слушает 0.0.0.0:3306 — так требует панель. ОБЯЗАТЕЛЬНО ограничьте порт файрволом (см. конец вывода)"
ok "Пользователь gsadmin создан, пароль в /root/.redl-node-credentials"
# ── 6. SSH ───────────────────────────────────────────────────────────────────
step "Настраиваю SSH…"
SSHD=/etc/ssh/sshd_config
cp -n "$SSHD" "$SSHD.bak-redl" 2>/dev/null || true
grep -q '^DenyGroups gameservers' "$SSHD" || echo 'DenyGroups gameservers' >> "$SSHD"
if [[ "$SSHROOT" == "yes" ]]; then
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/' "$SSHD"
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/' "$SSHD"
ROOTPASS="$(pwgen -cns -1 24)"
echo "root:$ROOTPASS" | chpasswd
printf 'ssh_user=root\nssh_pass=%s\n' "$ROOTPASS" >> /root/.redl-node-credentials
ok "Вход root по паролю разрешён, пароль сгенерирован (в /root/.redl-node-credentials)"
warn "Панель хранит этот пароль в своей базе в открытом виде — таково её устройство"
else
ROOTPASS=""
warn "Вход root по паролю не менялся. Панель умеет подключаться ТОЛЬКО по логину и паролю,"
warn "и её команды требуют прав root — иначе создание серверов не заработает"
fi
if ! sshd -t 2>/dev/null; then
cp "$SSHD.bak-redl" "$SSHD"
die "Конфиг sshd не прошёл проверку — откатил, ничего не сломано"
fi
service ssh restart >/dev/null 2>&1 || service sshd restart >/dev/null 2>&1 || /etc/init.d/ssh restart >/dev/null 2>&1 || true
ok "Группе gameservers вход по SSH запрещён (игровые пользователи не зайдут в шелл)"
# ── 7. FTP ───────────────────────────────────────────────────────────────────
step "Настраиваю ProFTPD (доступ клиента к файлам сервера)…"
PFTP=/etc/proftpd/proftpd.conf
if [[ -f "$PFTP" ]]; then
grep -q '^DefaultRoot ~' "$PFTP" || echo 'DefaultRoot ~' >> "$PFTP"
grep -q '^RequireValidShell off' "$PFTP" || echo 'RequireValidShell off' >> "$PFTP"
service proftpd restart >/dev/null 2>&1 || /etc/init.d/proftpd restart >/dev/null 2>&1 || true
ok "Клиент видит только свой каталог (DefaultRoot ~)"
else
warn "Конфиг ProFTPD не найден — FTP не настроен, файловый менеджер панели работать не будет"
fi
# ── 8. SteamCMD ──────────────────────────────────────────────────────────────
step "Ставлю SteamCMD (нужен для CS 1.6 / CS:S и других игр Steam)…"
if [[ -x /root/steamcmd/steamcmd.sh ]]; then
ok "SteamCMD уже установлен"
else
mkdir -p /root/steamcmd && cd /root/steamcmd
if curl -fsSL -o steamcmd_linux.tar.gz https://media.steampowered.com/client/steamcmd_linux.tar.gz; then
tar xzf steamcmd_linux.tar.gz && rm -f steamcmd_linux.tar.gz
ok "SteamCMD в /root/steamcmd"
else
warn "Не удалось скачать SteamCMD — поставьте позже вручную"
fi
cd "$SRC"
fi
# ── 9. сторож docker ─────────────────────────────────────────────────────────
step "Ставлю сторож docker…"
cat > /usr/local/bin/gamenode-guard <<'GUARD'
#!/bin/sh
# Поднимает docker и MariaDB, если упали или машина перезагрузилась.
docker info >/dev/null 2>&1 || service docker start >/dev/null 2>&1
mysqladmin ping >/dev/null 2>&1 || service mariadb start >/dev/null 2>&1
# запускаем контейнеры серверов, помеченных как работающие
for c in $(docker ps -a --filter "name=^gs" --filter "status=exited" --format '{{.Names}}' 2>/dev/null); do
docker start "$c" >/dev/null 2>&1
done
GUARD
chmod +x /usr/local/bin/gamenode-guard
cat > /etc/cron.d/gamenode-guard <<'G'
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/2 * * * * root /usr/local/bin/gamenode-guard >/dev/null 2>&1
@reboot root sleep 30; /usr/local/bin/gamenode-guard >/dev/null 2>&1
G
chmod 644 /etc/cron.d/gamenode-guard
service cron start >/dev/null 2>&1 || /etc/init.d/cron start >/dev/null 2>&1 || true
ok "Проверка раз в 2 минуты + подъём после перезагрузки"
# ── 10. проверка ─────────────────────────────────────────────────────────────
step "Финальная проверка…"
docker image inspect debian:stretch >/dev/null 2>&1 && ok "образ debian:stretch на месте" || warn "образа debian:stretch НЕТ"
[[ -d /home/cp/gameservers/files ]] && ok "каталоги на месте"
getent group gameservers >/dev/null && ok "группа gameservers создана"
mysqladmin ping >/dev/null 2>&1 && ok "MariaDB отвечает"
# проверяем, что контейнер реально создаётся и стартует
if docker run --rm debian:stretch /bin/echo test >/dev/null 2>&1; then
ok "Тестовый контейнер запускается — нода готова принимать игровые сервера"
else
warn "Тестовый контейнер НЕ запустился — проверьте docker вручную"
fi
CPU="$(nproc)"; RAM="$(free -m | awk '/^Mem:/{print $2}')"; HDD="$(df -BG --output=size / | tail -1 | tr -dc '0-9')"
printf '\n%s=== Нода установлена ===%s\n\n' "$C_OK" "$C_0"
printf '%sПодключение локации в панели (Админка → Локации → Добавить):%s\n' "$C_INF" "$C_0"
printf ' IP : %s\n' "$NODEIP"
printf ' Пользователь SSH: root\n'
if [[ -n "$ROOTPASS" ]]; then
printf ' Пароль SSH : %s\n' "$ROOTPASS"
else
printf ' Пароль SSH : (вы отказались менять — укажите действующий пароль root)\n'
fi
printf ' Ресурсы : %s ядер, %s МБ ОЗУ, %s ГБ диска\n\n' "$CPU" "$RAM" "$HDD"
printf ' Данные MySQL для баз игровых серверов — в /root/.redl-node-credentials\n\n'
printf '%sОБЯЗАТЕЛЬНО закройте порты файрволом (замените IP_ПАНЕЛИ на адрес панели):%s\n' "$C_W" "$C_0"
printf ' ufw allow from IP_ПАНЕЛИ to any port 22 proto tcp\n'
printf ' ufw allow from IP_ПАНЕЛИ to any port 3306 proto tcp\n'
printf ' ufw deny 3306\n'
printf ' # порты игровых серверов (например 7777, 22005, 25565) оставьте открытыми\n\n'
printf '%sСборки игр%s кладите в /home/cp/gameservers/files/<код_игры>/\n' "$C_INF" "$C_0"
printf ' Коды игр берите в Админка → Игры. Оригинальные ссылки на сборки в\n'
printf ' установщике вели на сторонние сайты по HTTP — мы их не используем,\n'
printf ' подробности в БЕЗОПАСНОСТЬ.md\n\n'
printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n'