#!/usr/bin/env bash # ============================================================================= # WireGuard VPS Tunnel — 1-Click Installer # Автоматическая настройка туннеля между VPS (публичный IP) и домашним сервером # (Raspberry Pi / Orange Pi за NAT, например Starlink) # ============================================================================= set -euo pipefail # ── Цвета для вывода ──────────────────────────────────────────────────────── 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' # No Color # ── Пути ──────────────────────────────────────────────────────────────────── CONFIG_DIR="/etc/wireguard-vps-tunnel" CONFIG_FILE="$CONFIG_DIR/config" WG_CONF="/etc/wireguard/wg0.conf" WATCHDOG_BIN="/usr/local/bin/wg-watchdog.sh" WATCHDOG_SERVICE="/etc/systemd/system/wg-watchdog.service" WG_SERVICE="wg-quick@wg0" # ── Значения по умолчанию ─────────────────────────────────────────────────── MODE="" # vps или client VPS_PUBLIC_IP="" # Публичный IP VPS CLIENT_WG_IP="10.0.0.2" # WireGuard IP клиента SERVER_WG_IP="10.0.0.1" # WireGuard IP сервера WG_PORT="51820" # Порт WireGuard PORTS="80,443" # Порты для проброса (только VPS) KEEPALIVE="21" # PersistentKeepalive (секунды) INTERACTIVE=true # ── Функции вывода ────────────────────────────────────────────────────────── info() { echo -e "${BLUE}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } header() { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}\n"; } # ── Баннер ────────────────────────────────────────────────────────────────── banner() { echo -e "${CYAN}${BOLD}" echo "╔══════════════════════════════════════════════════════════╗" echo "║ WireGuard VPS Tunnel — 1-Click Installer ║" echo "║ VPS ↔ Домашний сервер (за NAT / Starlink) ║" echo "╚══════════════════════════════════════════════════════════╝" echo -e "${NC}" } # ── Проверка прав ────────────────────────────────────────────────────────── check_root() { if [[ $EUID -ne 0 ]]; then error "Скрипт должен выполняться от root. Используйте: sudo $0" exit 1 fi } # ── Определение ОС ───────────────────────────────────────────────────────── detect_os() { if [[ -f /etc/os-release ]]; then . /etc/os-release OS_ID="${ID}" OS_NAME="${PRETTY_NAME:-$NAME}" else error "Не удалось определить операционную систему" exit 1 fi case "$OS_ID" in debian|ubuntu|armbian|raspbian) info "Обнаружена ОС: ${OS_NAME}" ;; *) warn "ОС '$OS_ID' не тестировалась. Продолжаем, но возможны проблемы." ;; esac } # ── Установка зависимостей ───────────────────────────────────────────────── install_deps() { header "Установка зависимостей" if command -v wg &>/dev/null; then success "WireGuard уже установлен: $(wg --version 2>&1 | head -1)" return 0 fi info "Установка wireguard-tools..." apt-get update -qq apt-get install -y -qq wireguard-tools iptables if command -v wg &>/dev/null; then success "WireGuard установлен успешно" else error "Не удалось установить WireGuard" exit 1 fi } # ── Включение IP-форвардинга ─────────────────────────────────────────────── enable_ip_forward() { header "Включение IP-форвардинга" local sysctl_conf="/etc/sysctl.d/99-wireguard-vps-tunnel.conf" if [[ -f "$sysctl_conf" ]]; then info "IP-форвардинг уже настроен" else cat > "$sysctl_conf" <<'SYSCTL' # Включение IP-форвардинга для WireGuard VPS Tunnel net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1 SYSCTL sysctl --system &>/dev/null success "IP-форвардинг включён" fi } # ── Генерация ключей ─────────────────────────────────────────────────────── generate_keys() { header "Генерация ключей WireGuard" mkdir -p "$CONFIG_DIR" if [[ -f "$CONFIG_DIR/server_private.key" ]] && [[ -f "$CONFIG_DIR/client_private.key" ]]; then info "Ключи уже существуют, пропускаем генерацию" else # Генерируем ключи сервера wg genkey | tee "$CONFIG_DIR/server_private.key" | wg pubkey > "$CONFIG_DIR/server_public.key" chmod 600 "$CONFIG_DIR/server_private.key" # Генерируем ключи клиента wg genkey | tee "$CONFIG_DIR/client_private.key" | wg pubkey > "$CONFIG_DIR/client_public.key" chmod 600 "$CONFIG_DIR/client_private.key" success "Ключи сгенерированы" fi SERVER_PRIVATE_KEY=$(cat "$CONFIG_DIR/server_private.key") SERVER_PUBLIC_KEY=$(cat "$CONFIG_DIR/server_public.key") CLIENT_PRIVATE_KEY=$(cat "$CONFIG_DIR/client_private.key") CLIENT_PUBLIC_KEY=$(cat "$CONFIG_DIR/client_public.key") } # ── Загрузка существующей конфигурации ───────────────────────────────────── load_config() { if [[ -f "$CONFIG_FILE" ]]; then info "Загружена существующая конфигурация из $CONFIG_FILE" source "$CONFIG_FILE" fi } # ── Сохранение конфигурации ──────────────────────────────────────────────── save_config() { mkdir -p "$CONFIG_DIR" cat > "$CONFIG_FILE" </dev/null || curl -4 -s icanhazip.com 2>/dev/null || echo "") if [[ -n "$detected_ip" ]]; then echo -e "${BOLD}Публичный IP VPS:${NC} [${detected_ip}]" read -r -p "Нажмите Enter для подтверждения или введите другой: " input VPS_PUBLIC_IP="${input:-$detected_ip}" else read -r -p "Введите публичный IP VPS: " VPS_PUBLIC_IP fi read -r -p "Порты для проброса (через запятую) [80,443]: " input PORTS="${input:-80,443}" read -r -p "WireGuard IP клиента [10.0.0.2]: " input CLIENT_WG_IP="${input:-10.0.0.2}" read -r -p "WireGuard порт [51820]: " input WG_PORT="${input:-51820}" else read -r -p "Публичный IP VPS: " VPS_PUBLIC_IP read -r -p "WireGuard IP этого сервера [10.0.0.2]: " input CLIENT_WG_IP="${input:-10.0.0.2}" read -r -p "WireGuard порт VPS [51820]: " input WG_PORT="${input:-51820}" read -r -p "PersistentKeepalive (сек) [21]: " input KEEPALIVE="${input:-21}" fi } # ── Парсинг аргументов командной строки ──────────────────────────────────── parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --vps) MODE="vps" INTERACTIVE=false shift ;; --client) MODE="client" INTERACTIVE=false shift ;; --vps-ip) VPS_PUBLIC_IP="$2" shift 2 ;; --client-wg-ip) CLIENT_WG_IP="$2" shift 2 ;; --ports) PORTS="$2" shift 2 ;; --wg-port) WG_PORT="$2" shift 2 ;; --keepalive) KEEPALIVE="$2" shift 2 ;; --help|-h) echo "Использование: $0 [ОПЦИИ]" echo "" echo "Опции:" echo " --vps Режим VPS (сервер с публичным IP)" echo " --client Режим клиента (домашний сервер за NAT)" echo " --vps-ip IP Публичный IP-адрес VPS" echo " --client-wg-ip IP WireGuard IP клиента (по умолчанию: 10.0.0.2)" echo " --ports PORT,... Порты для проброса (по умолчанию: 80,443)" echo " --wg-port PORT Порт WireGuard (по умолчанию: 51820)" echo " --keepalive SEC PersistentKeepalive в секундах (по умолчанию: 21)" echo " --help, -h Показать эту справку" echo "" echo "Примеры:" echo " # Интерактивный режим" echo " sudo $0" echo "" echo " # Неинтерактивный режим (VPS)" echo " sudo $0 --vps --vps-ip 2.59.219.234 --ports 80,443,8080" echo "" echo " # Неинтерактивный режим (клиент)" echo " sudo $0 --client --vps-ip 2.59.219.234 --keepalive 21" exit 0 ;; *) error "Неизвестный аргумент: $1" echo "Используйте --help для справки" exit 1 ;; esac done } # ── Создание конфигурации WireGuard для VPS ──────────────────────────────── configure_vps() { header "Настройка WireGuard (VPS — сервер)" # Формируем правила iptables для каждого порта local dnat_rules="" local dnat_down_rules="" IFS=',' read -ra PORT_ARRAY <<< "$PORTS" for port in "${PORT_ARRAY[@]}"; do port=$(echo "$port" | xargs) # trim whitespace dnat_rules+="iptables -t nat -A PREROUTING -d ${VPS_PUBLIC_IP} -p tcp --dport ${port} -j DNAT --to-destination ${CLIENT_WG_IP}:${port}; " dnat_down_rules+="iptables -t nat -D PREROUTING -d ${VPS_PUBLIC_IP} -p tcp --dport ${port} -j DNAT --to-destination ${CLIENT_WG_IP}:${port}; " done # Определяем внешний интерфейс local ext_if ext_if=$(ip route get 8.8.8.8 | awk '{print $5; exit}') cat > "$WG_CONF" < "$WG_CONF" </dev/null; then info "WireGuard уже запущен, перезапускаем..." systemctl stop "$WG_SERVICE" fi systemctl enable "$WG_SERVICE" &>/dev/null systemctl start "$WG_SERVICE" sleep 2 if systemctl is-active --quiet "$WG_SERVICE"; then success "WireGuard запущен и добавлен в автозагрузку" else error "Не удалось запустить WireGuard. Проверьте: systemctl status $WG_SERVICE" exit 1 fi # Показываем статус интерфейса echo "" wg show wg0 2>/dev/null || warn "Интерфейс wg0 не найден" } # ── Установка watchdog (только для клиента) ──────────────────────────────── install_watchdog() { header "Установка Watchdog (мониторинг туннеля)" # Копируем watchdog из директории скрипта local script_dir script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [[ -f "$script_dir/watchdog.sh" ]]; then cp "$script_dir/watchdog.sh" "$WATCHDOG_BIN" else error "Файл watchdog.sh не найден в $script_dir" error "Поместите watchdog.sh рядом с install.sh и запустите снова" return 1 fi chmod +x "$WATCHDOG_BIN" # Подставляем IP VPS в watchdog sed -i "s/VPS_IP=\"\"/VPS_IP=\"${VPS_PUBLIC_IP}\"/" "$WATCHDOG_BIN" 2>/dev/null || true sed -i "s/VPS_WG_IP=\"\"/VPS_WG_IP=\"${SERVER_WG_IP}\"/" "$WATCHDOG_BIN" 2>/dev/null || true # Создаём systemd-сервис для watchdog cat > "$WATCHDOG_SERVICE" </dev/null systemctl start wg-watchdog if systemctl is-active --quiet wg-watchdog; then success "Watchdog установлен и запущен" else warn "Watchdog установлен, но не запустился. Проверьте: systemctl status wg-watchdog" fi } # ── Вывод итоговой информации ────────────────────────────────────────────── print_summary() { header "Установка завершена" echo -e "${BOLD}Режим:${NC} ${MODE}" echo -e "${BOLD}Публичный IP VPS:${NC} ${VPS_PUBLIC_IP}" echo -e "${BOLD}WireGuard IP:${NC} ${CLIENT_WG_IP} (клиент) ↔ ${SERVER_WG_IP} (сервер)" echo -e "${BOLD}Порт WG:${NC} ${WG_PORT}" if [[ "$MODE" == "vps" ]]; then echo -e "${BOLD}Проброс портов:${NC} ${PORTS} → ${CLIENT_WG_IP}" fi echo "" echo -e "${BOLD}Полезные команды:${NC}" echo " wg show — статус туннеля" echo " systemctl status wg-quick@wg0 — статус сервиса" echo " ping ${SERVER_WG_IP} — проверка туннеля" if [[ "$MODE" == "client" ]]; then echo " systemctl status wg-watchdog — статус watchdog" echo " tail -f /var/log/wg-watchdog.log — логи watchdog" fi echo "" echo -e "${BOLD}Конфигурация:${NC} ${CONFIG_FILE}" echo -e "${BOLD}Ключи:${NC} ${CONFIG_DIR}/" if [[ "$MODE" == "vps" ]]; then echo "" echo -e "${YELLOW}${BOLD}⚠️ ВАЖНО:${NC} ${YELLOW}Скопируйте публичный ключ сервера на клиент:${NC}" echo -e " ${BOLD}cat ${CONFIG_DIR}/server_public.key${NC}" echo "" echo -e "${YELLOW}Затем запустите install.sh на клиенте:${NC}" echo -e " ${BOLD}sudo ./install.sh --client --vps-ip ${VPS_PUBLIC_IP}${NC}" fi echo "" } # ── Главная функция ──────────────────────────────────────────────────────── main() { banner check_root parse_args "$@" detect_os load_config if [[ "$INTERACTIVE" == true ]] && [[ -z "$MODE" ]]; then interactive_setup fi if [[ -z "$MODE" ]]; then error "Не указан режим. Используйте --vps или --client, либо интерактивный режим." exit 1 fi if [[ "$MODE" == "client" ]] && [[ -z "$VPS_PUBLIC_IP" ]]; then error "Не указан публичный IP VPS. Используйте --vps-ip IP" exit 1 fi # Автоопределение публичного IP для VPS если не указан if [[ "$MODE" == "vps" ]] && [[ -z "$VPS_PUBLIC_IP" ]]; then VPS_PUBLIC_IP=$(curl -4 -s ifconfig.me 2>/dev/null || curl -4 -s icanhazip.com 2>/dev/null || echo "") if [[ -z "$VPS_PUBLIC_IP" ]]; then error "Не удалось определить публичный IP. Укажите вручную: --vps-ip IP" exit 1 fi info "Автоопределён публичный IP: ${VPS_PUBLIC_IP}" fi install_deps enable_ip_forward generate_keys if [[ "$MODE" == "vps" ]]; then configure_vps else configure_client fi start_wireguard save_config if [[ "$MODE" == "client" ]]; then install_watchdog fi print_summary } main "$@"