#!/bin/bash # ============================================================================= # mita + WARP Setup Script # Ubuntu / Debian VPS # ============================================================================= set -euo pipefail # Директория, из которой запущен скрипт (нужна для поиска install-panel.sh рядом) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # ========================= ЦВЕТА ДЛЯ ВЫВОДА ========================= RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $*"; } ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } section() { echo -e "\n${CYAN}══════ $* ══════${NC}"; } # ============================================================================= # ██████████████████████████████████████████████████████████████████████████ # !! НАСТРОЙКИ — РЕДАКТИРОВАТЬ ЗДЕСЬ !! # ██████████████████████████████████████████████████████████████████████████ # ============================================================================= # ---- Порты mita (можно указать диапазон или одиночные порты) ---- MITA_PORT_RANGE="2100-2110" # диапазон портов mita MITA_PROTOCOL="TCP" # TCP или UDP # ---- Пользователи mita ---- # Формат: "имя:пароль" # Добавляйте сколько угодно строк # ВНИМАНИЕ: перед использованием ОБЯЗАТЕЛЬНО замените пароли ниже на свои (минимум 8 символов)! MITA_USERS=( "alice:qwerty1234" "bob:qwerty1234" ) # ---- WARP ---- WARP_ENABLED=true # true — включить WARP; false — только mita без WARP WARP_PORT=40000 # локальный порт SOCKS5 от WARP (не менять без причины) # ---- Логирование mita ---- MITA_LOG_LEVEL="INFO" # INFO или DEBUG # ============================================================================= # !! КОНЕЦ НАСТРОЕК !! # ============================================================================= MITA_CONFIG_DIR="/etc/mita" MITA_CONFIG_FILE="$MITA_CONFIG_DIR/server_config.json" LISTS_DIR="/etc/mita/lists" CUSTOM_DOMAINS_FILE="$LISTS_DIR/custom_domains.txt" CUSTOM_IPS_FILE="$LISTS_DIR/custom_ips.txt" AUTO_DOMAINS_FILE="$LISTS_DIR/auto_domains.txt" AUTO_IPS_FILE="$LISTS_DIR/auto_ips.txt" UPDATE_SCRIPT="/usr/local/bin/mita-update-lists" CRON_FILE="/etc/cron.d/mita-update" # ============================================================================= # ПРОВЕРКИ # ============================================================================= section "Проверка окружения" [[ $EUID -ne 0 ]] && error "Запустите скрипт от root: sudo bash install.sh" OS=$(. /etc/os-release && echo "$ID") [[ "$OS" != "ubuntu" && "$OS" != "debian" ]] && error "Поддерживается только Ubuntu/Debian" ok "ОС: $OS" # Проверка пользователей for entry in "${MITA_USERS[@]}"; do name="${entry%%:*}"; pass="${entry##*:}" [[ "$pass" == *"ЗАМЕНИТЕ"* ]] && error "Замените пароль для пользователя '$name' в начале скрипта!" [[ ${#pass} -lt 8 ]] && error "Пароль пользователя '$name' слишком короткий (минимум 8 символов)" done ok "Пользователи настроены корректно (${#MITA_USERS[@]} шт.)" # ============================================================================= # ВЫБОР ПОРТОВ MITA # ============================================================================= section "Порты mita" echo -e " Текущее значение: ${YELLOW}${MITA_PORT_RANGE}${NC} (${MITA_PROTOCOL})" echo -e " Допустимые форматы:" echo -e " диапазон: ${CYAN}2100-2110${NC} — mita случайно выбирает порт из диапазона при каждом соединении" echo -e " одиночный: ${CYAN}2100${NC} — фиксированный порт" echo -e " Рекомендуется диапазон из 5-20 портов." echo "" read -r -p " Введите порт или диапазон [${MITA_PORT_RANGE}]: " _PORT_INPUT if [[ -n "$_PORT_INPUT" ]]; then if [[ "$_PORT_INPUT" =~ ^([0-9]+)-([0-9]+)$ ]]; then _P_START="${BASH_REMATCH[1]}" _P_END="${BASH_REMATCH[2]}" if [[ $_P_START -lt 1024 || $_P_END -gt 65535 ]]; then error "Порты должны быть в диапазоне 1024–65535. Получено: $_P_START-$_P_END" fi if [[ $_P_START -ge $_P_END ]]; then error "Начало диапазона ($_P_START) должно быть меньше конца ($_P_END)" fi MITA_PORT_RANGE="$_PORT_INPUT" ok "Диапазон портов: $MITA_PORT_RANGE" elif [[ "$_PORT_INPUT" =~ ^[0-9]+$ ]]; then if [[ $_PORT_INPUT -lt 1024 || $_PORT_INPUT -gt 65535 ]]; then error "Порт $_PORT_INPUT вне допустимого диапазона (1024–65535)" fi MITA_PORT_RANGE="$_PORT_INPUT" ok "Порт: $MITA_PORT_RANGE" else error "Неверный формат: '$_PORT_INPUT'. Ожидается число (2100) или диапазон (2100-2110)" fi else ok "Оставлен по умолчанию: $MITA_PORT_RANGE" fi # ============================================================================= # ЗАВИСИМОСТИ # ============================================================================= section "Установка зависимостей" apt-get update -qq || { warn "apt-get update завершился с ошибкой, продолжаем..."; true; } DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \ curl wget jq cron \ ca-certificates gnupg lsb-release \ iptables ufw \ fail2ban openssl \ net-tools \ python3 python3-yaml \ || error "Ошибка установки зависимостей. Запустите вручную: apt-get install -y curl wget jq cron iptables ufw fail2ban python3 python3-yaml" # ipset и iptables-persistent — опциональные, не прерываем при ошибке DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ipset iptables-persistent 2>/dev/null || true ok "Базовые пакеты установлены (включая fail2ban, ufw, ipset, iptables-persistent, python3-yaml)" # ============================================================================= # УСТАНОВКА MITA # ============================================================================= section "Установка mita" MITA_VERSION=$(curl -s https://api.github.com/repos/enfein/mieru/releases/latest \ | jq -r '.tag_name' | tr -d 'v') [[ -z "$MITA_VERSION" ]] && error "Не удалось получить версию mita с GitHub" info "Последняя версия mita: $MITA_VERSION" ARCH=$(uname -m) case "$ARCH" in x86_64) DEB_ARCH="amd64" ;; aarch64) DEB_ARCH="arm64" ;; armv7l) DEB_ARCH="armhf" ;; *) error "Неподдерживаемая архитектура: $ARCH" ;; esac DEB_FILE="mita_${MITA_VERSION}_${DEB_ARCH}.deb" DL_URL="https://github.com/enfein/mieru/releases/download/v${MITA_VERSION}/${DEB_FILE}" info "Загрузка: $DL_URL" wget -q --show-progress "$DL_URL" -O /tmp/"$DEB_FILE" || error "Ошибка загрузки mita" dpkg -i /tmp/"$DEB_FILE" || apt-get install -f -y -qq rm -f /tmp/"$DEB_FILE" ok "mita $MITA_VERSION установлен" # ============================================================================= # УСТАНОВКА DOCKER + WARP # ============================================================================= if $WARP_ENABLED; then section "Установка Docker и Cloudflare WARP" if ! command -v docker &>/dev/null; then info "Установка Docker..." curl -fsSL https://get.docker.com | sh -s -- -q systemctl enable docker --now ok "Docker установлен" else ok "Docker уже установлен" fi info "Запуск Cloudflare WARP в Docker..." docker rm -f cloudflare-warp 2>/dev/null || true docker run -d \ --name cloudflare-warp \ --restart unless-stopped \ -p 127.0.0.1:${WARP_PORT}:40000 \ seiry/cloudflare-warp-proxy # Подождать пока WARP поднимется info "Ожидание запуска WARP (до 30 сек)..." for i in $(seq 1 30); do if curl -s --max-time 3 --proxy "socks5h://127.0.0.1:${WARP_PORT}" \ "https://www.cloudflare.com/cdn-cgi/trace" 2>/dev/null | grep -q "warp=on"; then ok "WARP активен и работает" break fi sleep 1 [[ $i -eq 30 ]] && warn "WARP не ответил за 30 сек — продолжаем, проверьте позже: docker logs cloudflare-warp" done fi # ============================================================================= # СОЗДАНИЕ ДИРЕКТОРИЙ И ФАЙЛОВ СПИСКОВ # ============================================================================= section "Создание файлов конфигурации" mkdir -p "$MITA_CONFIG_DIR" "$LISTS_DIR" # ---- custom_domains.txt ---- cat > "$CUSTOM_DOMAINS_FILE" << 'EOF' # ================================================================ # ПОЛЬЗОВАТЕЛЬСКИЙ СПИСОК ДОМЕНОВ → трафик пойдёт через WARP # ================================================================ # Добавляйте домены по одному на строку. # Строки начинающиеся с # — комментарии, игнорируются. # Субдомены включаются автоматически (openai.com → *.openai.com). # # Примеры: # openai.com # chatgpt.com # anthropic.com # claude.ai # ================================================================ EOF ok "Создан: $CUSTOM_DOMAINS_FILE" # ---- custom_ips.txt ---- cat > "$CUSTOM_IPS_FILE" << 'EOF' # ================================================================ # ПОЛЬЗОВАТЕЛЬСКИЙ СПИСОК IP/CIDR → трафик пойдёт через WARP # ================================================================ # Формат: одна подсеть CIDR или IP на строку. # Примеры: # 104.16.0.0/12 # 172.64.0.0/13 # ================================================================ EOF ok "Создан: $CUSTOM_IPS_FILE" # ---- auto_domains.txt (источники для автообновления) ---- cat > "$AUTO_DOMAINS_FILE" << 'EOF' # ================================================================ # ИСТОЧНИКИ АВТООБНОВЛЯЕМЫХ ДОМЕНОВ (ссылки или geosite-категории) # ================================================================ # Поддерживаемые форматы строк: # # 1) URL на текстовый файл (один домен на строку): # https://raw.githubusercontent.com/v2fly/domain-list-community/release/openai.txt # # 2) geosite-категория из репозитория v2fly: # geosite:openai # geosite:google # geosite:netflix # geosite:category-ru ← российские сайты # (полный список: github.com/v2fly/domain-list-community/tree/master/data) # # Строки с # — комментарии. # ================================================================ # --- OpenAI / AI сервисы --- geosite:openai # --- Примеры (раскомментируйте нужное) --- # geosite:google # geosite:netflix # https://raw.githubusercontent.com/nicholaswilde/blocklists/main/domain-lists/streaming.txt EOF ok "Создан: $AUTO_DOMAINS_FILE" # ---- auto_ips.txt ---- cat > "$AUTO_IPS_FILE" << 'EOF' # ================================================================ # ИСТОЧНИКИ АВТООБНОВЛЯЕМЫХ IP-ДИАПАЗОНОВ # ================================================================ # Формат строк: # # 1) URL на файл с CIDR: # https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/master/ipv4/us.cidr # # 2) geoip-страна (ISO-код): # geoip:us # geoip:gb # geoip:de # # ================================================================ # Пример: американские IP через WARP # geoip:us EOF ok "Создан: $AUTO_IPS_FILE" # ============================================================================= # СКРИПТ ОБНОВЛЕНИЯ СПИСКОВ # ============================================================================= section "Создание скрипта обновления списков" cat > "$UPDATE_SCRIPT" << UPDATEEOF #!/bin/bash # Автообновление списков доменов/IP и перегенерация конфига mita # Запускается cron ежедневно set -euo pipefail LISTS_DIR="$LISTS_DIR" MITA_CONFIG_FILE="$MITA_CONFIG_FILE" WARP_ENABLED=$WARP_ENABLED WARP_PORT=$WARP_PORT MITA_PORT_RANGE="$MITA_PORT_RANGE" MITA_PROTOCOL="$MITA_PROTOCOL" MITA_LOG_LEVEL="$MITA_LOG_LEVEL" GEOSITE_YML_URL="https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat_plain.yml" GEOSITE_CACHE="/var/cache/mita-geosite.yml" GEOIP_BASE="https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/master/ipv4" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] \$*" | tee -a /var/log/mita-update.log; } # ---------- Сбор доменов ---------- log "Сбор доменов..." TMP_DOMAINS=\$(mktemp) # Пользовательские домены grep -v '^\s*#' "\$LISTS_DIR/custom_domains.txt" | grep -v '^\s*$' >> "\$TMP_DOMAINS" || true # Проверяем, нужен ли geosite YAML (есть хотя бы одна строка geosite: в auto_domains.txt) NEED_GEOSITE=false grep -q '^geosite:' "\$LISTS_DIR/auto_domains.txt" 2>/dev/null && NEED_GEOSITE=true if \$NEED_GEOSITE; then mkdir -p "\$(dirname "\$GEOSITE_CACHE")" CACHE_AGE_DAYS=999 if [[ -f "\$GEOSITE_CACHE" ]]; then CACHE_AGE_SEC=\$(( \$(date +%s) - \$(stat -c %Y "\$GEOSITE_CACHE" 2>/dev/null || echo 0) )) CACHE_AGE_DAYS=\$(( CACHE_AGE_SEC / 86400 )) fi if [[ \$CACHE_AGE_DAYS -ge 7 ]]; then log "Загрузка базы geosite (dlc.dat_plain.yml, ~10-15 МБ, кэш обновляется раз в 7 дней)..." if curl -sfL --max-time 60 "\$GEOSITE_YML_URL" -o "\$GEOSITE_CACHE.tmp" 2>/dev/null; then mv "\$GEOSITE_CACHE.tmp" "\$GEOSITE_CACHE" log "База geosite обновлена" else log "WARN: не удалось загрузить базу geosite — используется старый кэш (если есть)" rm -f "\$GEOSITE_CACHE.tmp" fi else log "База geosite в кэше актуальна (обновлена \$CACHE_AGE_DAYS дн. назад)" fi fi # Автоматические источники while IFS= read -r line; do line="\$(echo "\$line" | xargs)" [[ -z "\$line" || "\$line" == \#* ]] && continue if [[ "\$line" == geosite:* ]]; then category="\${line#geosite:}" log " geosite:\$category" if [[ -f "\$GEOSITE_CACHE" ]]; then python3 -c " import yaml, sys try: with open('\$GEOSITE_CACHE') as f: data = yaml.safe_load(f) found = False for entry in data.get('lists', []): if entry.get('name','').lower() == '\$category'.lower(): found = True for rule in entry.get('rules', []): d = rule for prefix in ('domain:', 'full:'): if d.startswith(prefix): d = d[len(prefix):] print(d) break else: # regexp: и include: пропускаем — не доменные правила pass if not found: sys.stderr.write('NOTFOUND\\n') except Exception as e: sys.stderr.write(f'ERROR:{e}\\n') " >> "\$TMP_DOMAINS" 2>/tmp/geosite_err.log if grep -q "NOTFOUND" /tmp/geosite_err.log 2>/dev/null; then log " WARN: категория geosite:\$category не найдена в базе" elif grep -q "ERROR" /tmp/geosite_err.log 2>/dev/null; then log " WARN: ошибка парсинга geosite:\$category — $(cat /tmp/geosite_err.log)" fi rm -f /tmp/geosite_err.log else log " WARN: база geosite недоступна, пропускаю geosite:\$category" fi elif [[ "\$line" == http* ]]; then log " URL: \$line" curl -sf "\$line" 2>/dev/null \ | grep -v '^\s*#' | grep -v '^\s*$' \ >> "\$TMP_DOMAINS" || log " WARN: не удалось загрузить \$line" fi done < "\$LISTS_DIR/auto_domains.txt" # Дедупликация доменов FINAL_DOMAINS=\$(sort -u "\$TMP_DOMAINS" | grep -v '^\s*$') rm -f "\$TMP_DOMAINS" DOMAIN_COUNT=\$(echo "\$FINAL_DOMAINS" | grep -c . || echo 0) log "Доменов собрано: \$DOMAIN_COUNT" # ---------- Сбор IP-диапазонов ---------- log "Сбор IP-диапазонов..." TMP_IPS=\$(mktemp) grep -v '^\s*#' "\$LISTS_DIR/custom_ips.txt" | grep -v '^\s*$' >> "\$TMP_IPS" || true while IFS= read -r line; do line="\$(echo "\$line" | xargs)" [[ -z "\$line" || "\$line" == \#* ]] && continue if [[ "\$line" == geoip:* ]]; then country="\${line#geoip:}" log " geoip:\$country" curl -sf "\$GEOIP_BASE/\${country}.cidr" 2>/dev/null \ >> "\$TMP_IPS" || log " WARN: не удалось загрузить geoip:\$country" elif [[ "\$line" == http* ]]; then log " URL: \$line" curl -sf "\$line" 2>/dev/null | grep -v '^\s*#' | grep -v '^\s*$' \ >> "\$TMP_IPS" || log " WARN: не удалось загрузить \$line" fi done < "\$LISTS_DIR/auto_ips.txt" FINAL_IPS=\$(sort -u "\$TMP_IPS" | grep -v '^\s*$') rm -f "\$TMP_IPS" IP_COUNT=\$(echo "\$FINAL_IPS" | grep -c . || echo 0) log "IP-диапазонов собрано: \$IP_COUNT" # ---------- Сборка JSON egress-правил ---------- build_egress() { local domains="\$1" local ips="\$2" if ! \$WARP_ENABLED; then echo '{"rules": [{"ipRanges": ["*"], "domainNames": ["*"], "action": "DIRECT"}]}' return fi local domain_arr="[]" local ip_arr="[]" [[ -n "\$domains" ]] && domain_arr=\$(echo "\$domains" | jq -R . | jq -s .) [[ -n "\$ips" ]] && ip_arr=\$(echo "\$ips" | jq -R . | jq -s .) # Строим правила: сначала WARP-правило (если есть домены/IP), потом DIRECT для всего остального local rules="[]" if [[ \$DOMAIN_COUNT -gt 0 || \$IP_COUNT -gt 0 ]]; then warp_rule=\$(jq -n \ --argjson d "\$domain_arr" \ --argjson i "\$ip_arr" \ '{action: "PROXY", proxyNames: ["warp"], domainNames: \$d, ipRanges: \$i}') rules=\$(echo "[\$warp_rule]") fi # Финальное правило — всё остальное напрямую direct_rule='{"ipRanges": ["*"], "domainNames": ["*"], "action": "DIRECT"}' rules=\$(echo "\$rules" | jq ". + [\$direct_rule]" --argjson direct "\$direct_rule") jq -n \ --argjson rules "\$rules" \ '{ proxies: [{ name: "warp", protocol: "SOCKS5_PROXY_PROTOCOL", host: "127.0.0.1", port: \$ENV.WARP_PORT|tonumber }], rules: \$rules }' <<< "" 2>/dev/null || jq -n \ --argjson rules "\$rules" \ --argjson port "\$WARP_PORT" \ '{ proxies: [{name: "warp", protocol: "SOCKS5_PROXY_PROTOCOL", host: "127.0.0.1", port: \$port}], rules: \$rules }' } EGRESS_JSON=\$(build_egress "\$FINAL_DOMAINS" "\$FINAL_IPS") # ---------- Чтение текущих пользователей ---------- CURRENT_USERS=\$(jq '.users' "\$MITA_CONFIG_FILE" 2>/dev/null || echo '[]') # ---------- Генерация итогового конфига ---------- log "Генерация конфига mita..." # Разбор диапазона портов if [[ "\$MITA_PORT_RANGE" == *"-"* ]]; then PORT_BINDING=\$(jq -n --arg r "\$MITA_PORT_RANGE" --arg p "\$MITA_PROTOCOL" \ '[{portRange: \$r, protocol: \$p}]') else PORT_BINDING=\$(jq -n --arg port "\$MITA_PORT_RANGE" --arg p "\$MITA_PROTOCOL" \ '[{port: (\$port|tonumber), protocol: \$p}]') fi jq -n \ --argjson portBindings "\$PORT_BINDING" \ --argjson users "\$CURRENT_USERS" \ --arg logLevel "\$MITA_LOG_LEVEL" \ --argjson egress "\$EGRESS_JSON" \ '{ portBindings: \$portBindings, users: \$users, loggingLevel: \$logLevel, egress: \$egress }' > "\${MITA_CONFIG_FILE}.tmp" mv "\${MITA_CONFIG_FILE}.tmp" "\$MITA_CONFIG_FILE" log "Конфиг сохранён: \$MITA_CONFIG_FILE" # ---------- Применение конфига ---------- log "Применение конфига mita..." # Запустить mita если не запущен (нужен для apply config) MITA_WAS_STOPPED=false if ! systemctl is-active --quiet mita 2>/dev/null; then log "mita не запущен — запускаем для применения конфига..." systemctl reset-failed mita 2>/dev/null || true /usr/bin/mita run & MITA_BG_PID=\$! sleep 3 MITA_WAS_STOPPED=true fi mita apply config "\$MITA_CONFIG_FILE" && log "Конфиг применён успешно" \ || { log "ОШИБКА применения конфига"; exit 1; } if \$MITA_WAS_STOPPED; then kill \$MITA_BG_PID 2>/dev/null || true sleep 1 systemctl reset-failed mita 2>/dev/null || true systemctl start mita && log "mita запущен" || log "WARN: не удалось запустить mita" else systemctl restart mita log "mita перезапущен" fi log "Обновление завершено. Доменов: \$DOMAIN_COUNT, IP: \$IP_COUNT" UPDATEEOF chmod +x "$UPDATE_SCRIPT" ok "Скрипт обновления создан: $UPDATE_SCRIPT" # ============================================================================= # ПЕРВОНАЧАЛЬНАЯ ГЕНЕРАЦИЯ КОНФИГА С ПОЛЬЗОВАТЕЛЯМИ # ============================================================================= section "Генерация начального конфига mita" # Собрать JSON пользователей USERS_JSON="[" for i in "${!MITA_USERS[@]}"; do entry="${MITA_USERS[$i]}" name="${entry%%:*}" pass="${entry##*:}" sep=$([[ $i -lt $((${#MITA_USERS[@]}-1)) ]] && echo "," || echo "") USERS_JSON+=$(jq -n --arg n "$name" --arg p "$pass" \ '{name: $n, password: $p}') USERS_JSON+="$sep" done USERS_JSON+="]" # Разбор диапазона портов для начального конфига if [[ "$MITA_PORT_RANGE" == *"-"* ]]; then PORT_BINDING=$(jq -n --arg r "$MITA_PORT_RANGE" --arg p "$MITA_PROTOCOL" \ '[{portRange: $r, protocol: $p}]') else PORT_BINDING=$(jq -n --arg port "$MITA_PORT_RANGE" --arg p "$MITA_PROTOCOL" \ '[{port: ($port|tonumber), protocol: $p}]') fi # Базовый конфиг без egress (применим после первого запуска update-lists) jq -n \ --argjson portBindings "$PORT_BINDING" \ --argjson users "$USERS_JSON" \ --arg logLevel "$MITA_LOG_LEVEL" \ '{ portBindings: $portBindings, users: $users, loggingLevel: $logLevel, egress: {} }' > "$MITA_CONFIG_FILE" ok "Начальный конфиг записан: $MITA_CONFIG_FILE" # Применить конфиг mita apply config "$MITA_CONFIG_FILE" ok "Конфиг mita применён" # ============================================================================= # ЗАПУСК MITA # ============================================================================= section "Запуск mita" systemctl enable mita 2>/dev/null || true systemctl restart mita sleep 2 if systemctl is-active --quiet mita; then ok "mita запущен и работает" else warn "mita не запустился — проверьте: journalctl -u mita -n 50" fi # ============================================================================= # НАСТРОЙКА CRON # ============================================================================= section "Настройка автообновления (cron)" cat > "$CRON_FILE" << EOF # Обновление списков доменов/IP для mita — каждый день в 04:00 0 4 * * * root $UPDATE_SCRIPT >> /var/log/mita-update.log 2>&1 EOF ok "Cron задача создана: $CRON_FILE" # ============================================================================= # ПЕРВЫЙ ЗАПУСК ОБНОВЛЕНИЯ СПИСКОВ # ============================================================================= section "Первое обновление списков" info "Запуск $UPDATE_SCRIPT..." bash "$UPDATE_SCRIPT" && ok "Списки обновлены, конфиг применён" \ || warn "Ошибка при обновлении списков, проверьте /var/log/mita-update.log" # ============================================================================= # ОТКРЫТИЕ ПОРТОВ В FIREWALL # ============================================================================= section "Настройка firewall" if command -v ufw &>/dev/null && ufw status | grep -q "Status: active"; then # UFW требует двоеточие для диапазонов: 3000:3010/tcp, а не 3000-3010/tcp if [[ "$MITA_PORT_RANGE" == *"-"* ]]; then _UFW_START="${MITA_PORT_RANGE%-*}"; _UFW_END="${MITA_PORT_RANGE#*-}" ufw allow "${_UFW_START}:${_UFW_END}/${MITA_PROTOCOL,,}" comment "mita proxy" 2>/dev/null || true else ufw allow "${MITA_PORT_RANGE}/${MITA_PROTOCOL,,}" comment "mita proxy" 2>/dev/null || true fi ok "UFW: порты $MITA_PORT_RANGE/$MITA_PROTOCOL открыты" echo "" warn "ВАЖНО: UFW управляет только локальным фаерволом ОС." warn "Если ваш VPS у облачного провайдера (Hetzner, AWS, GCP, DigitalOcean, Vultr и др.)," warn "откройте порты ${MITA_PORT_RANGE} (${MITA_PROTOCOL}) ТАКЖЕ в консоли провайдера" warn "(Security Groups / Firewall / Network Rules) — иначе подключения не будет." echo "" elif command -v iptables &>/dev/null; then if [[ "$MITA_PORT_RANGE" == *"-"* ]]; then START_PORT="${MITA_PORT_RANGE%-*}"; END_PORT="${MITA_PORT_RANGE#*-}" iptables -I INPUT -p "${MITA_PROTOCOL,,}" --dport "$START_PORT:$END_PORT" -j ACCEPT 2>/dev/null || true else iptables -I INPUT -p "${MITA_PROTOCOL,,}" --dport "$MITA_PORT_RANGE" -j ACCEPT 2>/dev/null || true fi ok "iptables: порты открыты" fi # ============================================================================= # ИТОГОВЫЙ ОТЧЁТ # ============================================================================= section "Установка завершена!" SERVER_IP=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') echo "" echo -e "${GREEN}╔══════════════════════════════════════════════════════╗" echo -e "║ УСТАНОВКА ЗАВЕРШЕНА УСПЕШНО ║" echo -e "╚══════════════════════════════════════════════════════╝${NC}" echo "" echo -e "${CYAN}Параметры сервера для клиентов:${NC}" echo -e " IP сервера: ${YELLOW}$SERVER_IP${NC}" echo -e " Порты mita: ${YELLOW}$MITA_PORT_RANGE ($MITA_PROTOCOL)${NC}" echo "" echo -e "${CYAN}Пользователи:${NC}" for entry in "${MITA_USERS[@]}"; do name="${entry%%:*}"; pass="${entry##*:}" echo -e " ${YELLOW}$name${NC} / $pass" done echo "" echo -e "${CYAN}Файлы конфигурации:${NC}" echo -e " Конфиг mita: ${YELLOW}$MITA_CONFIG_FILE${NC}" echo -e " Свои домены → WARP: ${YELLOW}$CUSTOM_DOMAINS_FILE${NC}" echo -e " Свои IP → WARP: ${YELLOW}$CUSTOM_IPS_FILE${NC}" echo -e " Авто-источники (dom): ${YELLOW}$AUTO_DOMAINS_FILE${NC}" echo -e " Авто-источники (IP): ${YELLOW}$AUTO_IPS_FILE${NC}" echo -e " Лог обновлений: ${YELLOW}/var/log/mita-update.log${NC}" echo "" echo -e "${CYAN}Полезные команды:${NC}" echo -e " ${YELLOW}mita status${NC} — статус сервера" echo -e " ${YELLOW}mita get users${NC} — список пользователей и их трафик" echo -e " ${YELLOW}mita describe config${NC} — текущий конфиг" echo -e " ${YELLOW}$UPDATE_SCRIPT${NC} — обновить списки вручную" echo -e " ${YELLOW}journalctl -u mita -f${NC} — логи mita в реальном времени" if $WARP_ENABLED; then echo -e " ${YELLOW}docker logs cloudflare-warp${NC} — логи WARP" fi echo "" echo -e "${CYAN}Следующий шаг — добавьте нужные домены в:${NC}" echo -e " ${YELLOW}$CUSTOM_DOMAINS_FILE${NC} — ваши домены" echo -e " ${YELLOW}$AUTO_DOMAINS_FILE${NC} — geosite/URL источники" echo -e "Затем запустите: ${YELLOW}$UPDATE_SCRIPT${NC}" echo "" # ============================================================================= # УСТАНОВКА ВЕБ-ПАНЕЛИ (опционально) # ============================================================================= section "Веб-панель управления" # Ищем install-panel.sh в нескольких возможных местах: # 1) Прямо рядом с install.sh (актуальная структура архива) # 2) В подпапке mita-panel-pkg/ (старая структура архива) PANEL_INSTALLER="" for candidate in \ "$SCRIPT_DIR/install-panel.sh" \ "$SCRIPT_DIR/mita-panel-pkg/install-panel.sh" do if [[ -f "$candidate" ]]; then PANEL_INSTALLER="$candidate" break fi done if [[ -z "$PANEL_INSTALLER" ]]; then warn "Файл install-panel.sh не найден рядом со скриптом." warn "Искал в: $SCRIPT_DIR/ и $SCRIPT_DIR/mita-panel-pkg/" warn "Убедитесь что install-panel.sh, app.py и templates/ распакованы рядом с install.sh." else echo -e "Установить веб-панель управления mita? (логин/пароль и секретный путь генерируются автоматически)" echo -e " ${GREEN}y${NC} — да, установить сейчас" echo -e " ${YELLOW}n${NC} — пропустить (можно установить позже: bash $PANEL_INSTALLER)" echo "" read -r -p "Установить панель? [y/N]: " INSTALL_PANEL echo "" if [[ "${INSTALL_PANEL,,}" == "y" || "${INSTALL_PANEL,,}" == "yes" ]]; then info "Запуск установки веб-панели..." echo "" bash "$PANEL_INSTALLER" else info "Пропускаем установку панели." echo -e " Запустить позже: ${YELLOW}bash $PANEL_INSTALLER${NC}" fi fi # ═══════════════════════════════════════════════════════════════════════ # УСТАНОВКА TELEGRAM-БОТА (опционально) # ═══════════════════════════════════════════════════════════════════════ section "Telegram-бот" BOT_INSTALLER="" for candidate in \ "$SCRIPT_DIR/install-bot.sh" \ "$SCRIPT_DIR/mita-bot-pkg/install-bot.sh" do if [[ -f "$candidate" ]]; then BOT_INSTALLER="$candidate" break fi done if [[ -z "$BOT_INSTALLER" ]]; then warn "install-bot.sh не найден рядом со скриптом." warn "Поместите bot.py и install-bot.sh рядом с install.sh для установки бота." else echo -e "Установить Telegram-бота для управления сервером?" echo -e " Бот позволит: создавать/удалять пользователей, смотреть трафик," echo -e " включать WARP, мониторить CPU/RAM/диск — всё через Telegram." echo "" echo -e " ${GREEN}y${NC} — да, установить сейчас (потребуется токен и Telegram ID)" echo -e " ${YELLOW}n${NC} — пропустить (установить позже: bash $BOT_INSTALLER или через веб-панель)" echo "" read -r -p "Установить бота? [y/N]: " INSTALL_BOT echo "" if [[ "${INSTALL_BOT,,}" == "y" || "${INSTALL_BOT,,}" == "yes" ]]; then echo "" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN} Инструкция по созданию бота${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo "" echo -e " ${BOLD}1.${NC} Откройте Telegram и найдите ${GREEN}@BotFather${NC}" echo -e " Отправьте команду ${YELLOW}/newbot${NC}" echo -e " Следуйте инструкциям: придумайте имя и username бота" echo -e " В ответ вы получите ${BOLD}токен${NC} — длинную строку вида:" echo -e " ${YELLOW}1234567890:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw${NC}" echo "" echo -e " ${BOLD}2.${NC} Откройте своего бота в Telegram и отправьте любое сообщение" echo -e " (например «привет»). ID определится автоматически." echo "" echo -e " ${BOLD}3.${NC} Когда токен готов — нажмите Enter для продолжения" while true; do echo "" read -r -p " Впишите токен от @BotFather: " BOT_TOKEN if [[ -z "$BOT_TOKEN" ]]; then warn "Токен не может быть пустым" continue fi break done echo "" info "Определение вашего Telegram ID..." echo -e " Отправьте ${YELLOW}любое сообщение${NC} своему боту в Telegram прямо сейчас." echo -e " Ожидание сообщения (макс. 60 сек)..." BOT_ADMIN="" for i in $(seq 1 30); do BOT_ADMIN=$(curl -s --max-time 5 "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates" 2>/dev/null | \ python3 -c " import sys,json try: d=json.load(sys.stdin) ids=[r['message']['from']['id'] for r in d.get('result',[]) if 'message' in r and 'from' in r['message']] print(ids[-1] if ids else '') except: pass " 2>/dev/null) [[ -n "$BOT_ADMIN" ]] && break sleep 2 done if [[ -z "$BOT_ADMIN" || ! "$BOT_ADMIN" =~ ^[0-9]+$ ]]; then warn "Не удалось определить ID автоматически." while true; do echo "" read -r -p " Впишите ваш Telegram ID вручную (число): " BOT_ADMIN if [[ -n "$BOT_ADMIN" && "$BOT_ADMIN" =~ ^[0-9]+$ ]]; then break fi warn "Telegram ID должен быть числом" done else ok "Telegram ID определён: ${GREEN}${BOT_ADMIN}${NC}" fi echo "" info "Запуск установки Telegram-бота..." BOT_TOKEN_NONINTERACTIVE="$BOT_TOKEN" BOT_ADMIN_NONINTERACTIVE="$BOT_ADMIN" bash "$BOT_INSTALLER" else info "Пропускаем установку бота." echo -e " Установить позже: ${YELLOW}bash $BOT_INSTALLER${NC}" echo -e " Или через веб-панель: раздел «Telegram бот»" fi fi # ── Установка mita-ctl как системной команды ────────────────────── section "Установка утилиты управления mita-ctl" CTL_SRC="$SCRIPT_DIR/mita-ctl.sh" if [[ -f "$CTL_SRC" ]]; then cp "$CTL_SRC" /usr/local/bin/mita-ctl chmod +x /usr/local/bin/mita-ctl ok "mita-ctl установлен!" echo -e " Управление сервером: ${YELLOW}sudo mita-ctl${NC}" else warn "mita-ctl.sh не найден рядом с install.sh" warn "Установите вручную: cp mita-ctl.sh /usr/local/bin/mita-ctl && chmod +x /usr/local/bin/mita-ctl" fi