Сборка 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 - документация на русском: ИЗМЕНЕНИЯ, БЕЗОПАСНОСТЬ, ИНСТРУКЦИЯ
This commit is contained in:
Executable
+252
@@ -0,0 +1,252 @@
|
||||
#!/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'
|
||||
Reference in New Issue
Block a user