2026-06-19 14:30:41 +03:00
|
|
|
|
#!/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'
|
2026-06-27 00:05:09 +03:00
|
|
|
|
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
2026-06-19 14:30:41 +03:00
|
|
|
|
|
|
|
|
|
|
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 ----
|
|
|
|
|
|
# Формат: "имя:пароль"
|
|
|
|
|
|
# Добавляйте сколько угодно строк
|
2026-06-19 22:46:43 +03:00
|
|
|
|
# ВНИМАНИЕ: перед использованием ОБЯЗАТЕЛЬНО замените пароли ниже на свои (минимум 8 символов)!
|
2026-06-19 14:30:41 +03:00
|
|
|
|
MITA_USERS=(
|
2026-06-19 22:46:43 +03:00
|
|
|
|
"alice:qwerty1234"
|
|
|
|
|
|
"bob:qwerty1234"
|
2026-06-19 14:30:41 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ---- 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[@]} шт.)"
|
|
|
|
|
|
|
2026-06-19 22:46:43 +03:00
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# ВЫБОР ПОРТОВ 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
|
|
|
|
|
|
|
2026-06-19 14:30:41 +03:00
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# ЗАВИСИМОСТИ
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
section "Установка зависимостей"
|
|
|
|
|
|
|
2026-06-19 23:43:12 +03:00
|
|
|
|
apt-get update -qq || { warn "apt-get update завершился с ошибкой, продолжаем..."; true; }
|
2026-06-19 22:46:43 +03:00
|
|
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
2026-06-19 14:30:41 +03:00
|
|
|
|
curl wget jq cron \
|
|
|
|
|
|
ca-certificates gnupg lsb-release \
|
2026-06-19 23:43:12 +03:00
|
|
|
|
iptables ufw \
|
2026-06-19 14:30:41 +03:00
|
|
|
|
fail2ban openssl \
|
|
|
|
|
|
net-tools \
|
2026-06-19 15:23:17 +03:00
|
|
|
|
python3 python3-yaml \
|
2026-06-19 23:43:12 +03:00
|
|
|
|
|| 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
|
2026-06-19 22:46:43 +03:00
|
|
|
|
ok "Базовые пакеты установлены (включая fail2ban, ufw, ipset, iptables-persistent, python3-yaml)"
|
2026-06-19 14:30:41 +03:00
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# УСТАНОВКА 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"
|
2026-06-19 15:23:17 +03:00
|
|
|
|
GEOSITE_YML_URL="https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat_plain.yml"
|
|
|
|
|
|
GEOSITE_CACHE="/var/cache/mita-geosite.yml"
|
2026-06-19 14:30:41 +03:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-06-19 15:23:17 +03:00
|
|
|
|
# Проверяем, нужен ли 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
|
|
|
|
|
|
|
2026-06-19 14:30:41 +03:00
|
|
|
|
# Автоматические источники
|
|
|
|
|
|
while IFS= read -r line; do
|
|
|
|
|
|
line="\$(echo "\$line" | xargs)"
|
|
|
|
|
|
[[ -z "\$line" || "\$line" == \#* ]] && continue
|
|
|
|
|
|
|
|
|
|
|
|
if [[ "\$line" == geosite:* ]]; then
|
|
|
|
|
|
category="\${line#geosite:}"
|
|
|
|
|
|
log " geosite:\$category"
|
2026-06-19 15:23:17 +03:00
|
|
|
|
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
|
2026-06-19 14:30:41 +03:00
|
|
|
|
|
|
|
|
|
|
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
|
2026-06-26 23:37:05 +03:00
|
|
|
|
# UFW требует двоеточие для диапазонов: 3000:3010/tcp, а не 3000-3010/tcp
|
2026-06-19 14:30:41 +03:00
|
|
|
|
if [[ "$MITA_PORT_RANGE" == *"-"* ]]; then
|
2026-06-26 23:37:05 +03:00
|
|
|
|
_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
|
2026-06-19 14:30:41 +03:00
|
|
|
|
else
|
|
|
|
|
|
ufw allow "${MITA_PORT_RANGE}/${MITA_PROTOCOL,,}" comment "mita proxy" 2>/dev/null || true
|
|
|
|
|
|
fi
|
|
|
|
|
|
ok "UFW: порты $MITA_PORT_RANGE/$MITA_PROTOCOL открыты"
|
2026-06-26 23:37:05 +03:00
|
|
|
|
echo ""
|
|
|
|
|
|
warn "ВАЖНО: UFW управляет только локальным фаерволом ОС."
|
|
|
|
|
|
warn "Если ваш VPS у облачного провайдера (Hetzner, AWS, GCP, DigitalOcean, Vultr и др.),"
|
|
|
|
|
|
warn "откройте порты ${MITA_PORT_RANGE} (${MITA_PROTOCOL}) ТАКЖЕ в консоли провайдера"
|
|
|
|
|
|
warn "(Security Groups / Firewall / Network Rules) — иначе подключения не будет."
|
|
|
|
|
|
echo ""
|
2026-06-19 14:30:41 +03:00
|
|
|
|
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 "Веб-панель управления"
|
|
|
|
|
|
|
2026-06-19 15:23:17 +03:00
|
|
|
|
# Ищем 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
|
2026-06-19 14:30:41 +03:00
|
|
|
|
|
2026-06-19 15:23:17 +03:00
|
|
|
|
if [[ -z "$PANEL_INSTALLER" ]]; then
|
2026-06-19 14:30:41 +03:00
|
|
|
|
warn "Файл install-panel.sh не найден рядом со скриптом."
|
2026-06-19 15:23:17 +03:00
|
|
|
|
warn "Искал в: $SCRIPT_DIR/ и $SCRIPT_DIR/mita-panel-pkg/"
|
|
|
|
|
|
warn "Убедитесь что install-panel.sh, app.py и templates/ распакованы рядом с install.sh."
|
2026-06-19 14:30:41 +03:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-06-26 23:37:05 +03:00
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
# УСТАНОВКА 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 ""
|
2026-06-27 00:05:09 +03:00
|
|
|
|
echo -e " ${BOLD}2.${NC} Откройте своего бота в Telegram и отправьте любое сообщение"
|
|
|
|
|
|
echo -e " (например «привет»). ID определится автоматически."
|
2026-06-26 23:37:05 +03:00
|
|
|
|
echo ""
|
2026-06-27 00:05:09 +03:00
|
|
|
|
echo -e " ${BOLD}3.${NC} Когда токен готов — нажмите Enter для продолжения"
|
2026-06-26 23:37:05 +03:00
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
read -r -p " Впишите токен от @BotFather: " BOT_TOKEN
|
|
|
|
|
|
if [[ -z "$BOT_TOKEN" ]]; then
|
|
|
|
|
|
warn "Токен не может быть пустым"
|
|
|
|
|
|
continue
|
|
|
|
|
|
fi
|
|
|
|
|
|
break
|
|
|
|
|
|
done
|
|
|
|
|
|
|
2026-06-27 00:05:09 +03:00
|
|
|
|
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
|
2026-06-26 23:37:05 +03:00
|
|
|
|
done
|
|
|
|
|
|
|
2026-06-27 00:05:09 +03:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-06-26 23:37:05 +03:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-06-19 14:30:41 +03:00
|
|
|
|
# ── Установка 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
|