#!/usr/bin/env bash # 3X-UI KIT (форк hacker07) — установка сервера одной командой из нашего канала. # # Что делает (по шагам): # 1) проверяет, что мы на чистом Debian/Ubuntu и под root, и что есть curl, ssh-keygen, openssl; # 2) берёт из нашего Gitea релиз своей же версии, проверяет ПОДПИСЬ SHA256SUMS и SHA256 установщика; # 3) запускает установщик автора (scripts/3x-ui.sh) — панель 3X-UI, протоколы, сайт-заглушка, # маскировка, сертификат, подписка, ufw; все обычные флаги установщика можно передавать как есть; # 4) досыпает то, чего у автора нет (поведение из StressOzz/vps-setup, но безопаснее): # ответ на ping закрывается правилом ufw, включаются обновления безопасности; # 5) при желании добавляет второй порт SSH (--ssh-port 2222) — прежний остаётся рабочим; # 6) в конце: kit fix (время и безопасные починки), kit check, kit local hardening. # # Запуск (примеры): # bash <(curl -fsSL https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v1.2.3/scripts/3x-ui-local.sh) --domain vpn.example.com # ... 3x-ui-local.sh --sni dl.google.com --harden # ... 3x-ui-local.sh --ssh-port 2222 --no-harden # # Флаги самого обёрточного скрипта (остальные уходят установщику автора): # --no-harden не отключать ping и не включать автообновления безопасности # --ssh-port N добавить порт SSH N (прежний порт продолжает работать) # --no-time не запускать kit fix (синхронизация времени и безопасные починки) # --no-check не запускать в конце kit check # --dry-run показать план и ничего не делать # -h | --help эта справка # # Наш файл: авторские релизы его не перезаписывают, а правки форка живут в scripts/kit-local.sh. set -Eeuo pipefail KIT_VERSION="1.2.5" # версия сборки форка; должна совпадать с VERSION релиза GITEA="${KIT_FORK_BASE:-https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS}" NS="3x-ui-kit-release" SIG_ID="releases@3x-ui-kit" # Публичный ключ форка. Меняется только вместе с ключом подписи (тогда обновляется и здесь, и в kit). SIGNER_PUB='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKM0Sfn/UQeqjvUvY9wVgg7QtG1RR2pEUkqrUD5Yy6EF 3X-UI KIT fork releases (hacker07)' G=$'\e[32m'; Y=$'\e[33m'; R=$'\e[31m'; B=$'\e[1m'; D=$'\e[2m'; N=$'\e[0m' [[ -t 1 ]] || { G=; Y=; R=; B=; D=; N=; } say() { printf '%s\n' "${G}==>${N} $*"; } warn() { printf '%s\n' "${Y}!${N} $*" >&2; } die() { printf '%s\n' "${R}✗${N} $*" >&2; exit 1; } usage() { cat <<'EOF' 3X-UI KIT (форк hacker07) — установка сервера одной командой из нашего канала. bash <(curl -fsSL <наш Gitea>/raw/tag/v<версия>/scripts/3x-ui-local.sh) [флаги…] Всё, что скрипт не знает сам, он передаёт установщику автора как есть: --domain vpn.example.com свой домен для маскировки (A-запись на этот сервер, свободный 80) --sni сайт.com маскировка под чужой сайт --panel-on domain | ip где открывать панель и подписку --host IP, --user имя, --port N, --protocols …, --restore файл Флаги этого скрипта: --no-harden не отключать ответ на ping и не включать автообновления безопасности --ssh-port N добавить порт SSH N (прежний порт продолжает работать) --no-time не запускать kit fix (время и безопасные починки) --no-check не запускать в конце kit check --verify-only только проверить релиз (подпись и SHA256) и выйти --dry-run показать план и ничего не делать -h | --help эта справка Что скрипт делает: проверяет сервер → скачивает релиз своей же версии из нашего Gitea, проверяет подпись SHA256SUMS и SHA256 установщика → запускает установщик автора → досыпает защиту (ping off правилом ufw, автообновления безопасности, при желании второй порт SSH) → kit fix и kit check. EOF } # ------------------------------------------------------------------ разбор флагов INSTALLER_ARGS=(); harden=yes; ssh_port=""; fix_time=yes; do_check=yes; dry=no; verify_only=no while (($#)); do case $1 in --no-harden) harden=no ;; --ssh-port) ssh_port=${2:-}; shift || die "--ssh-port: нужен номер порта" ;; --ssh-port=*) ssh_port=${1#*=} ;; --no-time) fix_time=no ;; --no-check) do_check=no ;; --verify-only) verify_only=yes ;; --dry-run) dry=yes ;; -h | --help) usage; exit 0 ;; *) INSTALLER_ARGS+=("$1") ;; esac shift done [[ -z $ssh_port || $ssh_port =~ ^[0-9]{1,5}$ ]] || die "--ssh-port: нужен номер порта, например 2222" pre() { printf ' %s\n' "$*"; } if [[ $dry == yes ]]; then say "план установки (--dry-run: ничего не делаю)" pre "версия сборки: $KIT_VERSION" pre "откуда: $GITEA (тег v$KIT_VERSION), подпись проверяется ключом форка" if ((${#INSTALLER_ARGS[@]})); then pre "установщик автора: bash scripts/3x-ui.sh ${INSTALLER_ARGS[*]}"; else pre "установщик автора: bash scripts/3x-ui.sh (без доп. флагов)"; fi pre "маскировка: своя заглушка/домен — как решит установщик автора" if [[ $harden == yes ]]; then pre "защита: ping off (правилом ufw) + автообновления безопасности"; else pre "защита: пропускаю (--no-harden)"; fi [[ -n $ssh_port ]] && pre "порт SSH: добавляю $ssh_port (прежний остаётся рабочим)" [[ $fix_time == yes ]] && pre "после: kit fix (время, безопасные починки)" [[ $do_check == yes ]] && pre "проверка: kit check + kit local hardening" echo say "запуск без --dry-run выполнит всё это на сервере" exit 0 fi # ------------------------------------------------------------------ релиз: скачать и проверить подпись # Подпись проверяем ДО запуска чужого кода, поэтому здесь нужны только curl/ssh-keygen/openssl. for c in curl ssh-keygen openssl; do command -v "$c" >/dev/null || die "нет $c — поставьте: apt-get update && apt-get install -y $c"; done say "беру релиз v$KIT_VERSION и проверяю подпись" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT raw="$GITEA/raw/tag/v$KIT_VERSION" for f in SHA256SUMS SHA256SUMS.sig scripts/3x-ui.sh; do mkdir -p "$tmp/$(dirname "$f")" curl -fsSL --retry 3 -m 60 -o "$tmp/$f" "$raw/$f" || die "не скачался $f из $raw (Gitea доступна с сервера?)" done printf '%s namespaces="%s" %s\n' "$SIG_ID" "$NS" "$SIGNER_PUB" >"$tmp/allowed_signers" ssh-keygen -Y verify -f "$tmp/allowed_signers" -I "$SIG_ID" -n "$NS" -s "$tmp/SHA256SUMS.sig" <"$tmp/SHA256SUMS" >/dev/null 2>&1 \ || die "подпись релиза v$KIT_VERSION не сошлась с ключом форка — это не наш релиз, ничего не запускаю" sum=$(awk '{f = $2; sub(/^\*/, "", f)} f == "scripts/3x-ui.sh" {print $1}' "$tmp/SHA256SUMS") [[ $(printf '%s' "$sum" | wc -c) -eq 64 ]] || die "в SHA256SUMS нет установщика — релиз собран неверно" got=$(sha256sum "$tmp/scripts/3x-ui.sh" | awk '{print $1}') [[ $got == "$sum" ]] || die "SHA256 установщика не совпал с подписанным — не запускаю" bash -n "$tmp/scripts/3x-ui.sh" || die "установщик не проходит проверку синтаксиса" printf ' ✓ подпись сошлась, SHA256 установщика совпал\n' if [[ $verify_only == yes ]]; then say "релиз v$KIT_VERSION проверен: подпись и SHA256 сошлись, установщик не запускался" exit 0 fi # ------------------------------------------------------------------ предполётные проверки say "проверяю сервер" [[ ${EUID:-0} -eq 0 ]] || die "нужен root: запустите от root (на чистом VPS обычно так и есть)" [[ -r /etc/os-release ]] || die "не вижу /etc/os-release — это не Debian/Ubuntu?" # shellcheck disable=SC1091 . /etc/os-release case ${ID:-} in debian | ubuntu | raspbian) ;; *) warn "система ${PRETTY_NAME:-?}: поддерживаются Debian и Ubuntu" ;; esac printf ' %s\n' "${PRETTY_NAME:-Linux}, ядро $(uname -r), памяти $(awk '/MemTotal/ {printf "%.1f ГБ", $2 / 1048576}' /proc/meminfo 2>/dev/null || echo '?')" df -P / | awk 'NR==2 {gsub("%","",$5); if ($5 >= 95) exit 1}' || warn "диск занят почти полностью — установка может не пройти" for p in 443 80; do ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]$p\$" && warn "порт $p уже кем-то занят"; done # ------------------------------------------------------------------ установка say "запускаю установщик автора (панель 3X-UI, протоколы, сайт-заглушка, подписка)" if ((${#INSTALLER_ARGS[@]})); then printf ' флаги: %s\n' "${INSTALLER_ARGS[*]}"; fi # stdin: если есть терминал — отдаём его установщику (там есть вопросы), иначе /dev/null, # иначе установщик начнёт читать наш собственный скрипт вместо ответов. if [[ -t 0 ]] || { : /dev/null; then bash "$tmp/scripts/3x-ui.sh" ${INSTALLER_ARGS[@]+"${INSTALLER_ARGS[@]}"} /dev/null || die "после установки нет команды kit — что-то пошло не так, смотрите вывод выше" # ------------------------------------------------------------------ наши дополнения if [[ $fix_time == yes ]]; then say "kit fix: синхронизация времени и безопасные починки (авторская команда, ничего необратимого)" kit fix || warn "kit fix завершился с замечаниями — посмотрите вывод" fi if [[ $harden == yes ]]; then say "защита сервера: закрываю ответ на ping и включаю обновления безопасности" kit local ping off || warn "не удалось закрыть ping — повторите: kit local ping off" kit local upgrades on || warn "не удалось включить автообновления — повторите: kit local upgrades on" fi if [[ -n $ssh_port ]]; then say "добавляю порт SSH $ssh_port (прежний порт продолжает работать)" kit local ssh "$ssh_port" || warn "порт не добавился — сделайте это отдельно: kit local ssh $ssh_port" cat < убедились — закройте прежние порты: kit local ssh --close-old $ssh_port EOF fi # ------------------------------------------------------------------ проверка if [[ $do_check == yes ]]; then say "проверяю, что получилось" kit check || warn "kit check нашёл замечания — читайте выше; безопасные починки: kit fix" kit local hardening fi cat <