diff --git a/README.md b/README.md index 3807057..5525ed4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ | `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | | `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | | `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | +| `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 | Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. @@ -57,7 +58,7 @@ swapon --show sysctl vm.swappiness ``` -## 2. Базовые пакеты, часовой пояс и брандмауэр +## 2. Базовые пакеты, SSH-порт и брандмауэр Файл: `ochenstarik-server-2.sh`. @@ -188,6 +189,33 @@ curl -4 https://api.ipify.org sudo ./ochenstarik-server-vpn-5.sh --disable ``` +## 6. Панель 3x-ui и локальный proxy Cloudflare WARP + +Файл: `ochenstarik-server-panel-warp-6.sh`. + +Скрипт предлагает выбрать три разных порта: + +- порт веб-панели 3x-ui, по умолчанию `2053`; +- порт подписок 3x-ui, по умолчанию `2096`; +- локальный SOCKS5/HTTP proxy-порт WARP, по умолчанию `40000`. + +3x-ui устанавливается официальным интерактивным установщиком. После установки выбранные порты записываются в стандартную SQLite-базу с предварительной резервной копией. Скрипт включает сервис подписок, запускает `x-ui` и проверяет оба TCP-слушателя. + +Cloudflare WARP устанавливается из официального APT-репозитория, переводится в Local proxy mode через MASQUE и проверяется запросом к Cloudflare: ответ должен содержать `warp=on`. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-panel-warp-6.sh +chmod 700 ochenstarik-server-panel-warp-6.sh +bash -n ochenstarik-server-panel-warp-6.sh +sudo ./ochenstarik-server-panel-warp-6.sh +``` + +В UFW открываются и проверяются `80/tcp`, `443/tcp`, выбранный порт панели и выбранный порт подписок. WARP слушает только локально на `127.0.0.1:40000` либо другом выбранном порту. Этот порт намеренно не публикуется через UFW, поскольку локальный proxy не требует аутентификации. + +Поддерживаются официально доступные пакеты WARP для Ubuntu `jammy`, `noble`, `resolute` и Debian `bookworm`, `trixie` на `amd64` и `arm64`. + +Официальные источники: [установка 3x-ui](https://github.com/MHSanaei/3x-ui/wiki/Installation), [пакеты Cloudflare WARP](https://pkg.cloudflareclient.com/) и [режимы WARP](https://developers.cloudflare.com/warp-client/warp-modes/). + ## Полная установка Если нужны все модули, запускайте их по очереди: @@ -198,9 +226,10 @@ sudo ./ochenstarik-server-2.sh sudo ./ochenstarik-server-user-3.sh sudo ./ochenstarik-server-tg-4.sh sudo ./ochenstarik-server-vpn-5.sh +sudo ./ochenstarik-server-panel-warp-6.sh ``` -VPN и Telegram-уведомления являются необязательными этапами. +VPN, 3x-ui, WARP и Telegram-уведомления являются необязательными этапами. ## Безопасность diff --git a/ochenstarik-server-panel-warp-6.sh b/ochenstarik-server-panel-warp-6.sh new file mode 100644 index 0000000..91cdfb1 --- /dev/null +++ b/ochenstarik-server-panel-warp-6.sh @@ -0,0 +1,289 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +readonly XUI_INSTALL_URL="https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh" +readonly XUI_BINARY="/usr/local/x-ui/x-ui" +readonly XUI_DB="/etc/x-ui/x-ui.db" +readonly CLOUDFLARE_KEY_URL="https://pkg.cloudflareclient.com/pubkey.gpg" +readonly CLOUDFLARE_KEYRING="/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg" +readonly CLOUDFLARE_REPO="/etc/apt/sources.list.d/cloudflare-client.list" +readonly DEFAULT_PANEL_PORT="2053" +readonly DEFAULT_SUBSCRIPTION_PORT="2096" +readonly DEFAULT_WARP_PORT="40000" +readonly SSH_PORT_CONFIG="/etc/ochenstarik-server/ssh-port.conf" + +TMP_DIR="" + +log() { printf '[+] %s\n' "$*"; } +warn() { printf '[!] %s\n' "$*" >&2; } +die() { printf '[x] %s\n' "$*" >&2; exit 1; } + +cleanup() { + if [[ -n "$TMP_DIR" && "$TMP_DIR" == /tmp/* && -d "$TMP_DIR" ]]; then + rm -rf -- "$TMP_DIR" + fi +} +trap cleanup EXIT + +require_command() { + command -v "$1" >/dev/null 2>&1 || die "Не найдена команда: $1" +} + +is_valid_port() { + local port="$1" + [[ "$port" =~ ^[0-9]{1,5}$ ]] || return 1 + (( 10#$port >= 1 && 10#$port <= 65535 )) +} + +port_is_forbidden() { + local candidate="$1" forbidden + shift + for forbidden in "$@"; do + [[ "$candidate" != "$forbidden" ]] || return 0 + done + return 1 +} + +choose_port() { + local variable_name="$1" label="$2" default_port="$3" + shift 3 + local selected_port + local -a forbidden_ports=("$@") + + while :; do + read -rp "${label} [${default_port}]: " selected_port + selected_port="${selected_port:-$default_port}" + if ! is_valid_port "$selected_port"; then + warn "Порт должен быть числом от 1 до 65535" + continue + fi + selected_port="$((10#$selected_port))" + if port_is_forbidden "$selected_port" "${forbidden_ports[@]}"; then + warn "Порт ${selected_port} уже зарезервирован другой службой" + continue + fi + printf -v "$variable_name" '%s' "$selected_port" + return 0 + done +} + +read_saved_ssh_port() { + local saved_port + SAVED_SSH_PORT="" + [[ -e "$SSH_PORT_CONFIG" ]] || return 0 + [[ -f "$SSH_PORT_CONFIG" && ! -L "$SSH_PORT_CONFIG" ]] \ + || die "$SSH_PORT_CONFIG должен быть обычным файлом" + saved_port="$(sed -n 's/^SSH_PORT=//p' "$SSH_PORT_CONFIG" | head -n1 | tr -d '\r')" + is_valid_port "$saved_port" || die "Некорректный SSH-порт в $SSH_PORT_CONFIG" + SAVED_SSH_PORT="$((10#$saved_port))" +} + +wait_for_tcp_listener() { + local port="$1" description="$2" attempt + for attempt in {1..20}; do + if ss -H -ltn "sport = :${port}" | grep -q .; then + log "${description} слушает TCP-порт ${port}" + return 0 + fi + sleep 1 + done + die "${description} не открыл TCP-порт ${port}" +} + +allow_ufw_port() { + local port="$1" description="$2" + log "UFW: открываю ${port}/tcp (${description})" + ufw allow "${port}/tcp" +} + +verify_ufw_port() { + local port="$1" + LANG=C ufw status | grep -Eq "(^|[[:space:]])${port}/tcp[[:space:]]+ALLOW" \ + || die "В UFW не найдено разрешающее правило для ${port}/tcp" +} + +set_xui_setting() { + local key="$1" value="$2" + sqlite3 "$XUI_DB" </dev/null 2>&1 || true + if ! "$XUI_BINARY" setting -port "$PANEL_PORT"; then + cp -a -- "$backup_file" "$XUI_DB" + systemctl start x-ui.service >/dev/null 2>&1 || true + die "Не удалось изменить порт панели 3x-ui" + fi + + if ! set_xui_setting subPort "$SUBSCRIPTION_PORT" \ + || ! set_xui_setting subEnable true; then + cp -a -- "$backup_file" "$XUI_DB" + systemctl start x-ui.service >/dev/null 2>&1 || true + die "Не удалось изменить порт подписок 3x-ui; база восстановлена" + fi + + systemctl daemon-reload + systemctl enable --now x-ui.service + systemctl restart x-ui.service + systemctl is-active --quiet x-ui.service || die "Служба x-ui не запущена" + + panel_value="$(sqlite3 "$XUI_DB" "SELECT value FROM settings WHERE key='webPort' LIMIT 1;")" + subscription_value="$(sqlite3 "$XUI_DB" "SELECT value FROM settings WHERE key='subPort' LIMIT 1;")" + [[ "$panel_value" == "$PANEL_PORT" ]] \ + || die "В базе 3x-ui указан неожиданный порт панели: ${panel_value:-пусто}" + [[ "$subscription_value" == "$SUBSCRIPTION_PORT" ]] \ + || die "В базе 3x-ui указан неожиданный порт подписок: ${subscription_value:-пусто}" + XUI_WEB_PATH="$(sqlite3 "$XUI_DB" "SELECT value FROM settings WHERE key='webBasePath' LIMIT 1;")" + XUI_SUB_PATH="$(sqlite3 "$XUI_DB" "SELECT value FROM settings WHERE key='subPath' LIMIT 1;")" + [[ "$XUI_WEB_PATH" == /* ]] || XUI_WEB_PATH="/${XUI_WEB_PATH}" + [[ "$XUI_SUB_PATH" == /* ]] || XUI_SUB_PATH="/${XUI_SUB_PATH}" +} + +install_cloudflare_warp() { + local os_id version_codename architecture key_file keyring_file + + os_id="$(sed -n 's/^ID=//p' /etc/os-release | head -n1 | tr -d '\"')" + version_codename="$(sed -n 's/^VERSION_CODENAME=//p' /etc/os-release | head -n1 | tr -d '\"')" + architecture="$(dpkg --print-architecture)" + [[ "$architecture" == amd64 || "$architecture" == arm64 ]] \ + || die "Cloudflare WARP поддерживает здесь только amd64 и arm64; получено: $architecture" + + case "${os_id}:${version_codename}" in + ubuntu:jammy|ubuntu:noble|ubuntu:resolute|debian:bookworm|debian:trixie) + ;; + *) + die "Неподдерживаемая Cloudflare WARP система: ${os_id} ${version_codename}" + ;; + esac + + key_file="${TMP_DIR}/cloudflare-warp.gpg" + keyring_file="${TMP_DIR}/cloudflare-warp-archive-keyring.gpg" + curl --fail --silent --show-error --location \ + --proto '=https' --tlsv1.2 "$CLOUDFLARE_KEY_URL" -o "$key_file" + gpg --batch --yes --dearmor --output "$keyring_file" "$key_file" + install -o root -g root -m 644 "$keyring_file" "$CLOUDFLARE_KEYRING" + + printf 'deb [signed-by=%s] https://pkg.cloudflareclient.com/ %s main\n' \ + "$CLOUDFLARE_KEYRING" "$version_codename" > "$CLOUDFLARE_REPO" + chmod 644 "$CLOUDFLARE_REPO" + + apt-get update + apt-get install -y cloudflare-warp + systemctl enable --now warp-svc.service + require_command warp-cli + + if ! warp-cli --accept-tos registration show >/dev/null 2>&1; then + warp-cli --accept-tos registration new + fi + warp-cli --accept-tos disconnect >/dev/null 2>&1 || true + warp-cli --accept-tos tunnel protocol set MASQUE + warp-cli --accept-tos mode proxy + warp-cli --accept-tos proxy port "$WARP_PORT" + warp-cli --accept-tos connect +} + +verify_warp_proxy() { + local trace attempt + + wait_for_tcp_listener "$WARP_PORT" "Cloudflare WARP local proxy" + trace="" + for attempt in {1..20}; do + trace="$(curl --fail --silent --show-error --max-time 15 \ + --proxy "socks5h://127.0.0.1:${WARP_PORT}" \ + https://www.cloudflare.com/cdn-cgi/trace 2>/dev/null || true)" + grep -Fqx 'warp=on' <<< "$trace" && break + sleep 1 + done + grep -Fqx 'warp=on' <<< "$trace" \ + || die "Проверка Cloudflare через локальный proxy не вернула warp=on" + log "Cloudflare WARP работает через 127.0.0.1:${WARP_PORT}" +} + +[[ "$EUID" -eq 0 ]] || die "Запустите этот скрипт от имени root" +for command_name in apt-get dpkg grep install sed systemctl tr; do + require_command "$command_name" +done + +read_saved_ssh_port +forbidden_common=("22" "80" "443") +[[ -z "$SAVED_SSH_PORT" ]] || forbidden_common+=("$SAVED_SSH_PORT") + +PANEL_PORT="" +SUBSCRIPTION_PORT="" +WARP_PORT="" +XUI_WEB_PATH="/" +XUI_SUB_PATH="/sub/" +choose_port PANEL_PORT "Порт панели 3x-ui" "$DEFAULT_PANEL_PORT" "${forbidden_common[@]}" +choose_port SUBSCRIPTION_PORT "Порт подписок 3x-ui" "$DEFAULT_SUBSCRIPTION_PORT" \ + "${forbidden_common[@]}" "$PANEL_PORT" +choose_port WARP_PORT "Локальный proxy-порт WARP" "$DEFAULT_WARP_PORT" \ + "${forbidden_common[@]}" "$PANEL_PORT" "$SUBSCRIPTION_PORT" + +printf '\nБудут использованы порты:\n' +printf ' 3x-ui panel: %s/tcp\n' "$PANEL_PORT" +printf ' 3x-ui subscriptions: %s/tcp\n' "$SUBSCRIPTION_PORT" +printf ' WARP local proxy: 127.0.0.1:%s/tcp\n\n' "$WARP_PORT" + +export DEBIAN_FRONTEND=noninteractive +log "Установка системных зависимостей" +apt-get update +apt-get install -y ca-certificates curl gnupg iproute2 sqlite3 ufw +for command_name in curl gpg sqlite3 ss ufw; do + require_command "$command_name" +done + +TMP_DIR="$(mktemp -d)" +chmod 700 "$TMP_DIR" + +log "Загрузка официального установщика 3x-ui" +XUI_INSTALLER="${TMP_DIR}/3x-ui-install.sh" +curl --fail --silent --show-error --location \ + --proto '=https' --tlsv1.2 "$XUI_INSTALL_URL" -o "$XUI_INSTALLER" +chmod 700 "$XUI_INSTALLER" +bash -n "$XUI_INSTALLER" +bash "$XUI_INSTALLER" + +log "Настройка портов 3x-ui" +configure_xui_ports + +log "Установка и настройка Cloudflare WARP" +install_cloudflare_warp + +log "Настройка UFW" +allow_ufw_port 80 "HTTP" +allow_ufw_port 443 "HTTPS" +allow_ufw_port "$PANEL_PORT" "панель 3x-ui" +allow_ufw_port "$SUBSCRIPTION_PORT" "подписки 3x-ui" +ufw --force enable + +for port in 80 443 "$PANEL_PORT" "$SUBSCRIPTION_PORT"; do + verify_ufw_port "$port" +done + +wait_for_tcp_listener "$PANEL_PORT" "Панель 3x-ui" +wait_for_tcp_listener "$SUBSCRIPTION_PORT" "Сервис подписок 3x-ui" +verify_warp_proxy + +printf '\nУстановка завершена.\n' +printf 'Панель 3x-ui: http(s)://:%s%s\n' "$PANEL_PORT" "$XUI_WEB_PATH" +printf 'Подписки 3x-ui: http(s)://:%s%s\n' "$SUBSCRIPTION_PORT" "$XUI_SUB_PATH" +printf 'Локальный WARP proxy: socks5://127.0.0.1:%s\n' "$WARP_PORT" +printf '\nПорт WARP не открыт публично в UFW: локальный proxy не требует аутентификации.\n' +printf 'Настройте TLS, сложный путь панели, новые учётные данные и двухфакторную аутентификацию в 3x-ui.\n'