diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..04c0dd4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.github +config/ +.env +*.log +docker-compose.server.yml +docker-compose.client.yml +*.md +LICENSE +install.sh +uninstall.sh \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b282e3f --- /dev/null +++ b/.env.example @@ -0,0 +1,46 @@ +# ============================================================================= +# .env.example — Шаблон переменных окружения +# Скопируйте в .env и заполните значениями. +# ============================================================================= +#.cp .env.example .env + +# ── Сервер (VPS) ──────────────────────────────────────────────────────────── +# Публичный ключ клиента (обязательно для сервера) +CLIENT_PUBLIC_KEY= + +# Публичный IP VPS (автоопределение, если пусто) +SERVER_PUBLIC_IP= + +# Внешний сетевой интерфейс VPS (автоопределение, если пусто) +SERVER_PUBLIC_IFACE= + +# WG адрес сервера (по умолчанию 10.0.0.1) +SERVER_WG_IP=10.0.0.1 + +# WG порт (по умолчанию 51820) +SERVER_WG_PORT=51820 + +# WG адрес клиента (по умолчанию 10.0.0.2) +CLIENT_WG_IP=10.0.0.2 + +# Порты для проброса через DNAT (по умолчанию 80,443) +FORWARD_PORTS=80,443 + +# ── Клиент (домашний сервер) ──────────────────────────────────────────────── +# Публичный IP VPS (обязательно для клиента) +VPS_PUBLIC_IP= + +# Публичный ключ сервера (обязательно для клиента) +SERVER_PUBLIC_KEY= + +# WG адрес сервера (по умолчанию 10.0.0.1) +SERVER_WG_IP=10.0.0.1 + +# WG порт VPS (по умолчанию 51820) +VPS_WG_PORT=51820 + +# WG адрес клиента (по умолчанию 10.0.2) +CLIENT_WG_IP=10.0.0.2 + +# Интервал Keepalive в секундах (оптимизировано для Starlink, по умолчанию 21) +PERSISTENT_KEEPALIVE=21 \ No newline at end of file diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..733a3cd --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,101 @@ +# ============================================================================= +# Gitea Actions CI/CD — WireGuard VPS Tunnel +# Multi-arch сборка (amd64 + arm64) + пуш в Gitea Container Registry +# ============================================================================= +name: Build and Push Docker Images + +on: + push: + branches: [main, dev] + tags: ['v*'] + pull_request: + branches: [main] + +env: + REGISTRY: git.softuniq.eu + IMAGE_PREFIX: opendoor/wireguard-vps-tunnel + +jobs: + build-server: + name: Сборка сервера (multi-arch) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.GITEA_USERNAME }} + password: ${{ secrets.GITEA_TOKEN }} + + - name: Docker metadata (server) + id: meta-server + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-server + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + + - name: Build and push server (multi-arch) + uses: docker/build-push-action@v6 + with: + context: ./server + platforms: linux/amd64,linux/arm64 + push: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} + tags: ${{ steps.meta-server.outputs.tags }} + labels: ${{ steps.meta-server.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + build-client: + name: Сборка клиента (multi-arch) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.GITEA_USERNAME }} + password: ${{ secrets.GITEA_TOKEN }} + + - name: Docker metadata (client) + id: meta-client + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-client + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + + - name: Build and push client (multi-arch) + uses: docker/build-push-action@v6 + with: + context: ./client + platforms: linux/amd64,linux/arm64 + push: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} + tags: ${{ steps.meta-client.outputs.tags }} + labels: ${{ steps.meta-client.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/README.md b/README.md index 2a39e94..f937e07 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# WireGuard VPS Tunnel — 1-Click Installer +# WireGuard VPS Tunnel — Docker Edition -Автоматическая настройка WireGuard-туннеля между VPS (с публичным IP) и домашним сервером за NAT (Raspberry Pi, Orange Pi, Starlink и т.д.). +Автоматическая настройка WireGuard-туннеля в Docker-контейнерах между VPS (с публичным IP) и домашним сервером за NAT (Raspberry Pi, Orange Pi, Starlink и т.д.). + +**Поддерживаемые архитектуры:** `linux/amd64` (Intel/AMD VPS) и `linux/arm64` (Raspberry Pi 4/5, Orange Pi). ## Архитектура @@ -13,18 +15,25 @@ │ например: │ │ 2.59.219.234 │ │ │ - │ wg0: 10.0.0.1 │ - │ :51820 (UDP) │ - │ │ - │ iptables DNAT: │ - │ :80 → 10.0.0.2 │ - │ :443 → 10.0.0.2 │ - └────────┬────────┘ + │ ┌──────────────┐ │ + │ │ Docker │ │ + │ │ wireguard- │ │ + │ │ server │ │ + │ │ │ │ + │ │ wg0:10.0.0.1 │ │ + │ │ :51820 (UDP) │ │ + │ │ │ │ + │ │ iptables │ │ + │ │ DNAT: │ │ + │ │ :80→10.0.0.2 │ │ + │ │ :443→10.0.0.2│ │ + │ └──────┬───────┘ │ + └────────┼────────┘ │ - ╔═════════════╧═════════════╗ - ║ WireGuard UDP :51820 ║ - ║ (PersistentKeepalive=21)║ - ╚═════════════╤═════════════╝ + ╔══════════════╧══════════════╗ + ║ WireGuard UDP :51820 ║ + ║ (PersistentKeepalive=21) ║ + ╚══════════════╤══════════════╝ │ ┌────────┴────────┐ │ Домашний сервер │ @@ -32,11 +41,17 @@ │ Raspberry Pi │ │ Starlink │ │ │ - │ wg0: 10.0.0.2 │ - │ │ - │ Watchdog │ - │ (мониторинг) │ - └────────┬────────┘ + │ ┌──────────────┐│ + │ │ Docker ││ + │ │ wireguard- ││ + │ │ client ││ + │ │ ││ + │ │ wg0:10.0.0.2 ││ + │ │ ││ + │ │ Watchdog ││ + │ │ (встроенный) ││ + │ └──────┬───────┘│ + └────────┼────────┘ │ ┌────────┴────────┐ │ Nginx Proxy │ @@ -48,115 +63,159 @@ **Как это работает:** 1. VPS принимает входящие соединения на порты 80 и 443 -2. iptables перенаправляет трафик через WireGuard-туннель на домашний сервер (10.0.0.2) +2. iptables (внутри Docker-контейнера) перенаправляет трафик через WireGuard-туннель на домашний сервер (10.0.0.2) 3. Домашний сервер (RPi) инициирует соединение с VPS — работает через любой NAT 4. `PersistentKeepalive = 21` секунда поддерживает туннель активным даже при отсутствии трафика -5. Watchdog на RPi мониторит состояние туннеля и перезапускает при обрыве +5. Встроенный Watchdog на клиенте мониторит состояние туннеля и перезапускает при обрыве +6. Всё изолировано в Docker-контейнерах — не затрагивает хост-систему ## Быстрый старт (1 команда) ### На VPS: ```bash -curl -sSL https://raw.githubusercontent.com/user/wireguard-vps-tunnel/main/install.sh | sudo bash -# Выбрать "1) VPS" -``` - -### На домашнем сервере (Raspberry Pi): -```bash -curl -sSL https://raw.githubusercontent.com/user/wireguard-vps-tunnel/main/install.sh | sudo bash -# Выбрать "2) Домашний сервер" -``` - -Или неинтерактивный режим: - -```bash -# VPS +git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git +cd wireguard-vps-tunnel sudo ./install.sh --vps --ports 80,443 - -# Клиент -sudo ./install.sh --client --vps-ip 2.59.219.234 --keepalive 21 ``` -## Ручная установка +### На домашнем сервере (Raspberry Pi / Orange Pi): +```bash +git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git +cd wireguard-vps-tunnel +sudo ./install.sh --client --vps-ip 2.59.219.234 +``` + +> **Примечание:** Docker-образы автоматически выбираются под архитектуру вашего устройства +> (amd64 для VPS, arm64 для Raspberry Pi). Сборка не требуется — образы загружаются из +> Gitea Container Registry. Для локальной сборки добавьте флаг `--build`. + +## Ручная установка через Docker Compose ### 1. Клонирование репозитория ```bash -git clone https://github.com/user/wireguard-vps-tunnel.git +git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git cd wireguard-vps-tunnel ``` -### 2. Установка на VPS +### 2. Генерация ключей ```bash -sudo ./install.sh --vps --vps-ip 2.59.219.234 --ports 80,443 +# Установите wireguard-tools если ещё нет +sudo apt-get install -y wireguard-tools + +# Сгенерируйте ключи +mkdir -p config +wg genkey | tee config/server_private.key | wg pubkey > config/server_public.key +wg genkey | tee config/client_private.key | wg pubkey > config/client_public.key +chmod 600 config/*.key ``` -Скрипт автоматически: -- Установит `wireguard-tools` -- Сгенерирует ключи -- Настроит `wg0` интерфейс -- Настроит iptables (DNAT + FORWARD) -- Включит IP-форвардинг -- Запустит systemd-сервис +### 3. Настройка VPS (сервер) -### 3. Установка на домашний сервер +Создайте `.env` файл: +```env +CLIENT_PUBLIC_KEY=<ключ из config/client_public.key> +SERVER_PUBLIC_IP=2.59.219.234 +FORWARD_PORTS=80,443 +``` +Запустите: ```bash -sudo ./install.sh --client --vps-ip 2.59.219.234 --keepalive 21 +sudo docker compose -f docker-compose.server.yml up -d ``` -Скрипт автоматически: -- Установит `wireguard-tools` -- Сгенерирует ключи -- Настроит `wg0` интерфейс -- Установит watchdog для мониторинга туннеля -- Запустит systemd-сервисы +### 4. Настройка клиента (домашний сервер) -### 4. Проверка +Создайте `.env` файл: +```env +VPS_PUBLIC_IP=2.59.219.234 +SERVER_PUBLIC_KEY=<ключ из config/server_public.key> +PERSISTENT_KEEPALIVE=21 +``` + +Запустите: +```bash +sudo docker compose -f docker-compose.client.yml up -d +``` + +### 5. Проверка ```bash # На VPS -ping 10.0.0.2 +docker exec wireguard-server wg show +docker exec wireguard-server ping 10.0.0.2 # На домашнем сервере -ping 10.0.0.1 +docker exec wireguard-client wg show +docker exec wireguard-client ping 10.0.0.1 ``` -## Конфигурация +## Обмен ключами (пошагово) -Все настройки сохраняются в `/etc/wireguard-vps-tunnel/config`: +### Шаг 1: На VPS — генерация ключей и запуск ```bash -MODE="vps" # vps или client -VPS_PUBLIC_IP="2.59.219.234" -CLIENT_WG_IP="10.0.0.2" -SERVER_WG_IP="10.0.0.1" -WG_PORT="51820" -PORTS="80,443" -KEEPALIVE="21" +sudo ./install.sh --vps --ports 80,443 ``` -Ключи хранятся в `/etc/wireguard-vps-tunnel/`: -- `server_private.key` / `server_public.key` -- `client_private.key` / `client_public.key` +Скрипт выведет публичный ключ сервера. **Скопируйте его.** -## Опции командной строки +### Шаг 2: На клиенте — запуск с ключом сервера + +```bash +# Отредактируйте .env, вставив SERVER_PUBLIC_KEY из шага 1 +sudo ./install.sh --client --vps-ip 2.59.219.234 +``` + +Скрипт выведет публичный ключ клиента. **Скопируйте его.** + +### Шаг 3: На VPS — добавление ключа клиента + +```bash +# Отредактируйте .env, вставив CLIENT_PUBLIC_KEY из шага 2 +# Перезапустите контейнер: +sudo docker compose -f docker-compose.server.yml up -d --force-recreate +``` + +## Переменные окружения + +### Сервер (VPS) + +| Переменная | По умолчанию | Описание | +|-----------|-------------|----------| +| `CLIENT_PUBLIC_KEY` | **обязательно** | Публичный ключ клиента | +| `SERVER_WG_IP` | `10.0.0.1` | WireGuard IP сервера | +| `SERVER_WG_PORT` | `51820` | Порт WireGuard | +| `FORWARD_PORTS` | `80,443` | Порты для проброса (через запятую) | +| `SERVER_PUBLIC_IP` | авто | Публичный IP VPS | +| `SERVER_PUBLIC_IFACE` | авто | Внешний сетевой интерфейс | + +### Клиент (домашний сервер) + +| Переменная | По умолчанию | Описание | +|-----------|-------------|----------| +| `VPS_PUBLIC_IP` | **обязательно** | Публичный IP VPS | +| `SERVER_PUBLIC_KEY` | **обязательно** | Публичный ключ сервера | +| `CLIENT_WG_IP` | `10.0.0.2` | WireGuard IP клиента | +| `VPS_WG_PORT` | `51820` | Порт WireGuard на VPS | +| `PERSISTENT_KEEPALIVE` | `21` | Интервал keepalive (сек) | + +## Опции командной строки (install.sh) | Опция | Описание | По умолчанию | |-------|----------|-------------| | `--vps` | Режим VPS (сервер) | — | | `--client` | Режим клиента (домашний сервер) | — | | `--vps-ip IP` | Публичный IP VPS | автоопределение | -| `--client-wg-ip IP` | WireGuard IP клиента | `10.0.0.2` | | `--ports PORT,...` | Порты для проброса (VPS) | `80,443` | | `--wg-port PORT` | Порт WireGuard | `51820` | | `--keepalive SEC` | PersistentKeepalive (сек) | `21` | -| `--help` | Справка | — | +| `--build` | Локальная сборка образа (вместо загрузки из registry) | `pull` | ## Watchdog (мониторинг туннеля) -Watchdog работает только на клиенте (домашний сервер) и выполняет: +Watchdog встроен в клиентский контейнер и выполняет: 1. Проверяет, что интерфейс `wg0` поднят 2. Проверяет доступность VPS через туннель (`ping 10.0.0.1`) @@ -166,15 +225,71 @@ Watchdog работает только на клиенте (домашний с - 10с → 20с → 40с → 80с → 120с (максимум) 6. После 3 успешных проверок подряд сбрасывает отсрочку -**Логи:** `/var/log/wg-watchdog.log` (авторотация при 10 МБ) - -**Управление:** +**Просмотр логов:** ```bash -systemctl status wg-watchdog # Статус -systemctl restart wg-watchdog # Перезапуск -tail -f /var/log/wg-watchdog.log # Логи в реальном времени +docker compose -f docker-compose.client.yml logs -f ``` +## Starlink: особенности + +Starlink использует CGNAT (Carrier-Grade NAT), что означает: + +- **Нет публичного IPv4** — домашний сервер недоступен из интернета напрямую +- **Высокая задержка** — 25-60 мс (спутниковая связь) +- **Кратковременные обрывы** — при переключении между спутниками +- **UDP работает лучше TCP** — меньше проблем с TCP congestion control + +**Рекомендации для Starlink:** + +1. `PERSISTENT_KEEPALIVE=21` — оптимальное значение (каждые 21 сек отправляет keepalive-пакет) +2. Watchdog обязателен — автоматически восстанавливает туннель после обрывов +3. Не используйте SSH reverse tunnel — TCP-over-TCP на спутниковом канале работает плохо +4. MTU = 1420 (стандартный для WireGuard) — не меняйте без необходимости + +## Multi-Arch (Поддержка архитектур) + +Docker-образы собираются автоматически для двух архитектур: + +| Архитектура | Устройства | Образ | +|-------------|-----------|-------| +| `linux/amd64` | VPS (Intel/AMD), десктопы | `git.softuniq.eu/opendoor/wireguard-vps-tunnel-server:latest` | +| `linux/arm64` | Raspberry Pi 4/5, Orange Pi, Mac M1/M2 | `git.softuniq.eu/opendoor/wireguard-vps-tunnel-client:latest` | + +Установщик автоматически определяет архитектуру хоста и загружает соответствующий образ. Принудительная локальная сборка: + +```bash +# Собрать образ локально (для текущей архитектуры) +sudo ./install.sh --vps --build + +# Или через docker compose +docker compose -f docker-compose.server.yml build +docker compose -f docker-compose.client.yml build +``` + +### CI/CD + +При пуше в ветку `main` или создании тега `v*` Gitea Actions автоматически собирает и пушит multi-arch образы: + +- `git.softuniq.eu/opendoor/wireguard-vps-tunnel-server:latest` +- `git.softuniq.eu/opendoor/wireguard-vps-tunnel-client:latest` + +Сборка использует QEMU для кросс-компиляции и Docker Buildx для multi-arch образов. + +## Сравнение: Docker vs Host-Based + +| Критерий | Docker | Host-Based | +|----------|--------|------------| +| Изоляция | Полная (контейнер) | Нет (хост-система) | +| Зависимости | Только Docker | wireguard-tools, iptables, systemd | +| Multi-arch | ✅ amd64 + arm64 | Ручная установка под каждую ОС | +| Обновление | `docker compose pull && up -d` | Ручная замена скриптов | +| Откат | `docker compose down && up` (предыдущий образ) | Ручной откат конфигов | +| Переносимость | Любой Linux с Docker | Debian/Ubuntu/Armbian | +| Безопасность | Минимальная поверхность атаки (Alpine) | Полный доступ к хосту | +| Watchdog | Встроен в контейнер | Отдельный systemd-сервис | +| Логи | `docker logs` | journald + файлы | +| Ресурсы | ~15 МБ образ, ~20 МБ RAM | ~5 МБ RAM (без контейнера) | + ## Сравнение: WireGuard vs SSH Reverse Tunnel | Критерий | WireGuard | SSH Reverse Tunnel | @@ -189,100 +304,62 @@ tail -f /var/log/wg-watchdog.log # Логи в реальном времени | Отказоустойчивость | Встроенная | Требует autossh | | Нагрузка на CPU | Минимальная (kernel) | Заметная (шифрование в userspace) | -**Вывод:** WireGuard превосходит SSH reverse tunnel по всем параметрам для production-использования, особенно на Starlink. +## Устранение неполадок -## Starlink: особенности - -Starlink использует CGNAT (Carrier-Grade NAT), что означает: - -- **Нет публичного IPv4** — домашний сервер недоступен из интернета напрямую -- **Высокая задержка** — 25-60 мс (спутниковая связь) -- **Кратковременные обрывы** — при переключении между спутниками -- **UDP работает лучше TCP** — меньше проблем с TCP congestion control - -**Рекомендации для Starlink:** - -1. `PersistentKeepalive = 21` — оптимальное значение (каждые 21 сек отправляет keepalive-пакет) -2. Watchdog обязателен — автоматически восстанавливает туннель после обрывов -3. Не используйте SSH reverse tunnel — TCP-over-TCP на спутниковом канале работает плохо -4. MTU = 1420 (стандартный для WireGuard) — не меняйте без необходимости - -## Безопасность - -- **Ключи**: приватные ключи хранятся с правами `600` (только root) -- **Фаервол**: iptables правила применяются атомарно через `PostUp`/`PostDown` -- **SSH**: парольная аутентификация **НЕ отключается** — скрипт не трогает sshd_config -- **Порты**: открываются только указанные порты (по умолчанию 80 и 443) -- **WireGuard**: использует современную криптографию (Curve25519, ChaCha20, BLAKE2s) - -### Рекомендации по усилению безопасности: +### Контейнер не запускается ```bash -# На VPS: ограничить доступ к порту WireGuard только с IP клиента (если известен) -iptables -A INPUT -p udp --dport 51820 -s -j ACCEPT -iptables -A INPUT -p udp --dport 51820 -j DROP +# Проверить логи +docker compose -f docker-compose.server.yml logs +docker compose -f docker-compose.client.yml logs -# Или ограничить rate limiting: -iptables -A INPUT -p udp --dport 51820 -m limit --limit 10/sec -j ACCEPT -iptables -A INPUT -p udp --dport 51820 -j DROP +# Проверить статус +docker compose -f docker-compose.server.yml ps +docker compose -f docker-compose.client.yml ps ``` -## Устранение неполадок - ### Туннель не поднимается ```bash -# Проверить статус -systemctl status wg-quick@wg0 +# Проверить статус WireGuard внутри контейнера +docker exec wireguard-server wg show +docker exec wireguard-client wg show -# Проверить конфигурацию -wg show - -# Проверить логи -journalctl -u wg-quick@wg0 -n 50 -``` - -### Пинг не проходит - -```bash -# Проверить что интерфейс поднят -ip link show wg0 - -# Проверить IP-адреса -ip addr show wg0 - -# Проверить что порт открыт на VPS -# На клиенте: +# Проверить, что порт открыт на VPS nc -zvu 51820 ``` ### Порты не пробрасываются ```bash -# Проверить правила iptables на VPS -iptables -t nat -L PREROUTING -n -iptables -L FORWARD -n +# Проверить правила iptables внутри контейнера +docker exec wireguard-server iptables -t nat -L PREROUTING -n +docker exec wireguard-server iptables -L FORWARD -n # Проверить IP-форвардинг -sysctl net.ipv4.ip_forward # Должно быть = 1 +docker exec wireguard-server sysctl net.ipv4.ip_forward ``` -### Watchdog не запускается +### Пинг не проходит ```bash -systemctl status wg-watchdog -journalctl -u wg-watchdog -n 50 -cat /var/log/wg-watchdog.log +# Проверить что интерфейс поднят +docker exec wireguard-server ip link show wg0 +docker exec wireguard-client ip link show wg0 + +# Проверить IP-адреса +docker exec wireguard-server ip addr show wg0 +docker exec wireguard-client ip addr show wg0 ``` ### Полный сброс и переустановка ```bash # Деинсталляция -sudo ./uninstall.sh +sudo ./uninstall.sh --remove-images --remove-config # Переустановка -sudo ./install.sh --vps --vps-ip +sudo ./install.sh --vps --ports 80,443 ``` ## Деинсталляция @@ -292,32 +369,59 @@ sudo ./uninstall.sh ``` Опции: -- `--remove-package` — также удалить пакет `wireguard-tools` +- `--remove-images` — также удалить Docker-образы +- `--remove-config` — также удалить директорию `config/` с ключами и `.env` Деинсталлятор: -- Останавливает и отключает сервисы -- Удаляет интерфейс `wg0` -- Очищает правила iptables -- Удаляет конфигурационные файлы и ключи -- **НЕ трогает** SSH-сервер и другие сервисы +- Останавливает и удаляет Docker-контейнеры +- Опционально удаляет образы и конфигурацию +- **НЕ трогает** SSH-сервер, Docker и другие сервисы +- **НЕ изменяет** `sshd_config` (PasswordAuthentication остаётся без изменений) -## Поддерживаемые ОС +## Безопасность -- Debian 11+ (Bullseye, Bookworm) -- Ubuntu 20.04+ (Focal, Jammy, Noble) -- Armbian (Orange Pi, Banana Pi, etc.) -- Raspberry Pi OS (Raspbian) +- **Ключи**: приватные ключи хранятся с правами `600` (только root) +- **Изоляция**: контейнеры работают в изолированном окружении (Alpine Linux) +- **Фаервол**: iptables правила применяются атомарно через `PostUp`/`PostDown` +- **SSH**: парольная аутентификация **НЕ отключается** — скрипт не трогает sshd_config +- **Порты**: открываются только указанные порты (по умолчанию 80 и 443) +- **WireGuard**: использует современную криптографию (Curve25519, ChaCha20, BLAKE2s) +- **Docker**: `--cap-add=NET_ADMIN --cap-add=SYS_MODULE` — минимально необходимые capabilities +- **Read-only**: `/lib/modules` монтируется в режиме `ro` (только чтение) ## Структура проекта ``` wireguard-vps-tunnel/ -├── install.sh # Основной установщик -├── uninstall.sh # Деинсталлятор -├── watchdog.sh # Watchdog для мониторинга туннеля -└── README.md # Документация +├── server/ +│ ├── Dockerfile # Docker-образ сервера (Alpine 3.20) +│ ├── entrypoint.sh # Точка входа: генерация конфига, запуск WG +│ └── healthcheck.sh # Healthcheck: wg show wg0 +├── client/ +│ ├── Dockerfile # Docker-образ клиента (Alpine 3.20) +│ ├── entrypoint.sh # Точка входа: генерация конфига, запуск WG + watchdog +│ ├── watchdog.sh # Watchdog: мониторинг и автовосстановление туннеля +│ └── healthcheck.sh # Healthcheck: ping VPS WG IP +├── docker-compose.server.yml # Docker Compose для VPS +├── docker-compose.client.yml # Docker Compose для клиента +├── .env.example # Шаблон переменных окружения +├── install.sh # 1-Click установщик (Docker) +├── uninstall.sh # Деинсталлятор (Docker) +├── .dockerignore # Исключения для сборочного контекста +├── .gitea/ +│ └── workflows/ +│ └── build.yml # CI/CD: сборка Docker-образов +└── README.md # Документация ``` +## Поддерживаемые ОС + +- Любой Linux с Docker 20.10+ +- Debian 11+ (Bullseye, Bookworm) +- Ubuntu 20.04+ (Focal, Jammy, Noble) +- Armbian (Orange Pi, Banana Pi, etc.) +- Raspberry Pi OS (Raspbian) + ## Лицензия MIT diff --git a/client/Dockerfile b/client/Dockerfile new file mode 100644 index 0000000..74728de --- /dev/null +++ b/client/Dockerfile @@ -0,0 +1,37 @@ +# ============================================================================= +# WireGuard VPS Tunnel — клиент (домашний сервер / Raspberry Pi) +# Multi-arch: linux/amd64, linux/arm64, linux/arm/v7 +# ============================================================================= +FROM --platform=$BUILDPLATFORM alpine:3.20 AS builder + +ARG TARGETPLATFORM +ARG BUILDPLATFORM + +RUN echo "Сборка на ${BUILDPLATFORM} для ${TARGETPLATFORM}" + +FROM --platform=$TARGETPLATFORM alpine:3.20 + +ARG TARGETARCH +ARG TARGETVARIANT + +RUN apk add --no-cache \ + wireguard-tools \ + iptables \ + iproute2 \ + bash \ + curl \ + iputils \ + ca-certificates + +COPY entrypoint.sh /entrypoint.sh +COPY watchdog.sh /watchdog.sh +COPY healthcheck.sh /healthcheck.sh + +RUN chmod +x /entrypoint.sh /watchdog.sh /healthcheck.sh + +VOLUME ["/etc/wireguard"] + +HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ + CMD ["/healthcheck.sh"] + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/client/entrypoint.sh b/client/entrypoint.sh new file mode 100644 index 0000000..e202b73 --- /dev/null +++ b/client/entrypoint.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# ============================================================================= +# WireGuard VPS Tunnel — точка входа клиента (домашний сервер) +# Генерирует конфигурацию wg0.conf из переменных окружения и поднимает туннель. +# ============================================================================= +set -euo pipefail + +# ── Значения по умолчанию ─────────────────────────────────────────────────── +CLIENT_WG_IP="${CLIENT_WG_IP:-10.0.0.2}" +VPS_WG_PORT="${VPS_WG_PORT:-51820}" +PERSISTENT_KEEPALIVE="${PERSISTENT_KEEPALIVE:-21}" +SERVER_WG_IP="${SERVER_WG_IP:-10.0.0.1}" +WG_CONF="/etc/wireguard/wg0.conf" + +# ── Логирование ───────────────────────────────────────────────────────────── +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$1] $*"; } + +# ── Проверка обязательных переменных ─────────────────────────────────────── +check_required() { + if [[ -z "${VPS_PUBLIC_IP:-}" ]]; then + log "ERROR" "VPS_PUBLIC_IP не задан. Укажите публичный IP VPS." + exit 1 + fi + + if [[ -z "${SERVER_PUBLIC_KEY:-}" ]]; then + log "ERROR" "SERVER_PUBLIC_KEY не задан. Укажите публичный ключ сервера." + exit 1 + fi +} + +# ── Генерация ключей ─────────────────────────────────────────────────────── +generate_keys() { + local private_key_file="/etc/wireguard/client_private.key" + local public_key_file="/etc/wireguard/client_public.key" + + if [[ -f "$private_key_file" ]] && [[ -f "$public_key_file" ]]; then + log "INFO" "Ключи клиента уже существуют" + else + log "INFO" "Генерация ключей клиента..." + wg genkey | tee "$private_key_file" | wg pubkey > "$public_key_file" + chmod 600 "$private_key_file" + log "INFO" "Ключи клиента сгенерированы" + fi + + CLIENT_PRIVATE_KEY=$(cat "$private_key_file") + CLIENT_PUBLIC_KEY=$(cat "$public_key_file") +} + +# ── Создание конфигурации WireGuard ──────────────────────────────────────── +create_wg_config() { + if [[ -f "$WG_CONF" ]]; then + log "INFO" "Конфигурация ${WG_CONF} уже существует, пропускаем создание" + return 0 + fi + + mkdir -p "$(dirname "$WG_CONF")" + + cat > "$WG_CONF" </dev/null; then + exit 0 +fi +exit 1 diff --git a/watchdog.sh b/client/watchdog.sh similarity index 62% rename from watchdog.sh rename to client/watchdog.sh index c6bed76..df553f7 100644 --- a/watchdog.sh +++ b/client/watchdog.sh @@ -3,56 +3,31 @@ # WireGuard Watchdog — оптимизирован для Starlink # Мониторит состояние туннеля и перезапускает при обрыве связи. # Использует экспоненциальную отсрочку (backoff) при повторных сбоях. +# Запускается внутри Docker-контейнера клиента. # ============================================================================= set -euo pipefail # ── Настройки ─────────────────────────────────────────────────────────────── -VPS_IP="" # Публичный IP VPS (заполняется install.sh) -VPS_WG_IP="" # WireGuard IP VPS (заполняется install.sh) +VPS_PUBLIC_IP="${VPS_PUBLIC_IP:-}" +SERVER_WG_IP="${SERVER_WG_IP:-10.0.0.1}" WG_INTERFACE="wg0" -LOG_FILE="/var/log/wg-watchdog.log" -MAX_LOG_SIZE=$((10 * 1024 * 1024)) # 10 МБ # ── Параметры экспоненциальной отсрочки ───────────────────────────────────── -INITIAL_BACKOFF=10 # Начальная задержка (сек) -MAX_BACKOFF=120 # Максимальная задержка (сек) -BACKOFF_MULTIPLIER=2 # Множитель -STABLE_THRESHOLD=3 # Кол-во успешных проверок для сброса backoff +INITIAL_BACKOFF=10 +MAX_BACKOFF=120 +BACKOFF_MULTIPLIER=2 +STABLE_THRESHOLD=3 # ── Состояние ─────────────────────────────────────────────────────────────── consecutive_failures=0 consecutive_successes=0 current_backoff=$INITIAL_BACKOFF -# ── Загрузка конфигурации ─────────────────────────────────────────────────── -load_config() { - local config_file="/etc/wireguard-vps-tunnel/config" - if [[ -f "$config_file" ]]; then - # shellcheck source=/dev/null - source "$config_file" - # Переопределяем из конфига если есть - VPS_IP="${VPS_PUBLIC_IP:-$VPS_IP}" - VPS_WG_IP="${SERVER_WG_IP:-$VPS_WG_IP}" - fi -} - # ── Логирование ───────────────────────────────────────────────────────────── log() { local level="$1" shift - local timestamp - timestamp=$(date '+%Y-%m-%d %H:%M:%S') - echo "[$timestamp] [$level] $*" >> "$LOG_FILE" - - # Ротация лога при превышении размера - if [[ -f "$LOG_FILE" ]]; then - local size - size=$(stat -c%s "$LOG_FILE" 2>/dev/null || stat -f%z "$LOG_FILE" 2>/dev/null || echo 0) - if [[ $size -gt $MAX_LOG_SIZE ]]; then - mv "$LOG_FILE" "${LOG_FILE}.old" - echo "[$timestamp] [INFO] Лог ротирован (превышен лимит ${MAX_LOG_SIZE} байт)" > "$LOG_FILE" - fi - fi + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" } # ── Проверка интерфейса WireGuard ────────────────────────────────────────── @@ -65,11 +40,10 @@ check_wg_interface() { # ── Проверка доступности VPS через туннель ────────────────────────────────── check_vps_tunnel() { - if [[ -z "$VPS_WG_IP" ]]; then + if [[ -z "$SERVER_WG_IP" ]]; then return 1 fi - # 3 пинга, таймаут 2 секунды - if ping -c 3 -W 2 "$VPS_WG_IP" &>/dev/null; then + if ping -c 3 -W 2 "$SERVER_WG_IP" &>/dev/null; then return 0 fi return 1 @@ -77,11 +51,10 @@ check_vps_tunnel() { # ── Проверка доступности VPS через интернет ───────────────────────────────── check_vps_internet() { - if [[ -z "$VPS_IP" ]]; then + if [[ -z "$VPS_PUBLIC_IP" ]]; then return 1 fi - # Проверяем, что интернет вообще работает - if ping -c 2 -W 3 "$VPS_IP" &>/dev/null; then + if ping -c 2 -W 3 "$VPS_PUBLIC_IP" &>/dev/null; then return 0 fi return 1 @@ -90,9 +63,9 @@ check_vps_internet() { # ── Перезапуск WireGuard ──────────────────────────────────────────────────── restart_wireguard() { log "WARN" "Перезапуск wg-quick@${WG_INTERFACE}..." - systemctl restart "wg-quick@${WG_INTERFACE}" 2>&1 | while read -r line; do - log "INFO" "systemctl: $line" - done + wg-quick down "$WG_INTERFACE" 2>/dev/null || true + sleep 2 + wg-quick up "$WG_INTERFACE" sleep 3 } @@ -102,11 +75,8 @@ reset_backoff() { current_backoff=$INITIAL_BACKOFF consecutive_successes=$((consecutive_successes + 1)) - if [[ $consecutive_successes -ge $STABLE_THRESHOLD ]]; then - # Логируем только при переходе в стабильное состояние - if [[ $consecutive_successes -eq $STABLE_THRESHOLD ]]; then - log "INFO" "Туннель стабилен (${STABLE_THRESHOLD} успешных проверок подряд)" - fi + if [[ $consecutive_successes -eq $STABLE_THRESHOLD ]]; then + log "INFO" "Туннель стабилен (${STABLE_THRESHOLD} успешных проверок подряд)" fi } @@ -122,7 +92,7 @@ increase_backoff() { # ── Основной цикл ────────────────────────────────────────────────────────── main_loop() { - log "INFO" "Watchdog запущен. VPS=${VPS_IP}, VPS_WG=${VPS_WG_IP}, интерфейс=${WG_INTERFACE}" + log "INFO" "Watchdog запущен. VPS=${VPS_PUBLIC_IP}, VPS_WG=${SERVER_WG_IP}, интерфейс=${WG_INTERFACE}" log "INFO" "Параметры backoff: начальный=${INITIAL_BACKOFF}с, макс=${MAX_BACKOFF}с, множитель=${BACKOFF_MULTIPLIER}" while true; do @@ -143,11 +113,10 @@ main_loop() { fi # Туннель не работает — проверяем интернет - log "WARN" "VPS ${VPS_WG_IP} недоступен через туннель" + log "WARN" "VPS ${SERVER_WG_IP} недоступен через туннель" if ! check_vps_internet; then - # Интернет тоже не работает — перезапуск не поможет - log "WARN" "VPS ${VPS_IP} недоступен через интернет. Возможно, проблемы с сетью." + log "WARN" "VPS ${VPS_PUBLIC_IP} недоступен через интернет. Возможно, проблемы с сетью." increase_backoff log "INFO" "Ожидание ${current_backoff}с..." sleep "$current_backoff" @@ -158,7 +127,6 @@ main_loop() { log "ERROR" "Туннель не работает, но VPS доступен через интернет. Перезапуск WireGuard..." restart_wireguard - # Проверяем после перезапуска sleep 3 if check_vps_tunnel; then log "INFO" "Туннель восстановлен после перезапуска" @@ -174,16 +142,8 @@ main_loop() { } # ── Точка входа ───────────────────────────────────────────────────────────── -load_config - -# Проверяем что IP заданы -if [[ -z "$VPS_IP" ]]; then - echo "ОШИБКА: VPS_IP не задан. Отредактируйте скрипт или файл конфигурации." >&2 - exit 1 -fi - -if [[ -z "$VPS_WG_IP" ]]; then - echo "ОШИБКА: VPS_WG_IP не задан. Отредактируйте скрипт или файл конфигурации." >&2 +if [[ -z "$VPS_PUBLIC_IP" ]]; then + log "ERROR" "VPS_PUBLIC_IP не задан. Watchdog не может работать." exit 1 fi diff --git a/docker-compose.client.yml b/docker-compose.client.yml new file mode 100644 index 0000000..d196f9d --- /dev/null +++ b/docker-compose.client.yml @@ -0,0 +1,36 @@ +# ============================================================================= +# docker-compose.client.yml — Клиент (домашний сервер / Raspberry Pi за NAT) +# Запуск: docker compose -f docker-compose.client.yml up -d +# ============================================================================= +version: "3.8" + +services: + wireguard-client: + image: git.softuniq.eu/opendoor/wireguard-vps-tunnel-client:latest + build: + context: ./client + platforms: + - linux/amd64 + - linux/arm64 + container_name: wireguard-client + restart: unless-stopped + cap_add: + - NET_ADMIN + - SYS_MODULE + network_mode: host + volumes: + - ./config:/etc/wireguard + - /lib/modules:/lib/modules:ro + environment: + - CLIENT_WG_IP=${CLIENT_WG_IP:-10.0.0.2} + - VPS_PUBLIC_IP=${VPS_PUBLIC_IP:?Укажите VPS_PUBLIC_IP в .env} + - VPS_WG_PORT=${VPS_WG_PORT:-51820} + - SERVER_PUBLIC_KEY=${SERVER_PUBLIC_KEY:?Укажите SERVER_PUBLIC_KEY в .env} + - SERVER_WG_IP=${SERVER_WG_IP:-10.0.0.1} + - PERSISTENT_KEEPALIVE=${PERSISTENT_KEEPALIVE:-21} + healthcheck: + test: ["CMD", "/healthcheck.sh"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s \ No newline at end of file diff --git a/docker-compose.server.yml b/docker-compose.server.yml new file mode 100644 index 0000000..124329c --- /dev/null +++ b/docker-compose.server.yml @@ -0,0 +1,39 @@ +# ============================================================================= +# docker-compose.server.yml — VPS (сервер с публичным IP) +# Запуск: docker compose -f docker-compose.server.yml up -d +# ============================================================================= +version: "3.8" + +services: + wireguard-server: + image: git.softuniq.eu/opendoor/wireguard-vps-tunnel-server:latest + build: + context: ./server + platforms: + - linux/amd64 + - linux/arm64 + container_name: wireguard-server + restart: unless-stopped + cap_add: + - NET_ADMIN + - SYS_MODULE + network_mode: host + volumes: + - ./config:/etc/wireguard + - /lib/modules:/lib/modules:ro + environment: + - SERVER_WG_IP=${SERVER_WG_IP:-10.0.0.1} + - SERVER_WG_PORT=${SERVER_WG_PORT:-51820} + - CLIENT_PUBLIC_KEY=${CLIENT_PUBLIC_KEY:?Укажите CLIENT_PUBLIC_KEY в .env} + - CLIENT_WG_IP=${CLIENT_WG_IP:-10.0.0.2} + - FORWARD_PORTS=${FORWARD_PORTS:-80,443} + - SERVER_PUBLIC_IP=${SERVER_PUBLIC_IP:-} + - SERVER_PUBLIC_IFACE=${SERVER_PUBLIC_IFACE:-} + ports: + - "${SERVER_WG_PORT:-51820}:51820/udp" + healthcheck: + test: ["CMD", "/healthcheck.sh"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s \ No newline at end of file diff --git a/install.sh b/install.sh index e5990d5..60a9a08 100644 --- a/install.sh +++ b/install.sh @@ -1,37 +1,31 @@ #!/usr/bin/env bash # ============================================================================= -# WireGuard VPS Tunnel — 1-Click Installer -# Автоматическая настройка туннеля между VPS (публичный IP) и домашним сервером -# (Raspberry Pi / Orange Pi за NAT, например Starlink) +# WireGuard VPS Tunnel — 1-Click Docker Installer +# Multi-arch: linux/amd64, linux/arm64 (ARM Raspberry Pi) # ============================================================================= 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 +# ── Цвета ────────────────────────────────────────────────────────────────── +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' # ── Пути ──────────────────────────────────────────────────────────────────── -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" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_DIR="$SCRIPT_DIR/config" +REGISTRY="git.softuniq.eu" +IMAGE_PREFIX="opendoor/wireguard-vps-tunnel" +IMAGE_TAG="latest" -# ── Значения по умолчанию ─────────────────────────────────────────────────── -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 (секунды) +# ── Значения по умолчанию ────────────────────────────────────────────────── +MODE="" +VPS_PUBLIC_IP="" +CLIENT_WG_IP="10.0.0.2" +SERVER_WG_IP="10.0.0.1" +WG_PORT="51820" +PORTS="80,443" +KEEPALIVE="21" INTERACTIVE=true +BUILD_LOCAL=false # ── Функции вывода ────────────────────────────────────────────────────────── info() { echo -e "${BLUE}[INFO]${NC} $*"; } @@ -40,12 +34,24 @@ warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } header() { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}\n"; } +# ── Определение архитектуры ──────────────────────────────────────────────── +detect_arch() { + local arch + arch="$(uname -m)" + case "$arch" in + x86_64|amd64) echo "amd64" ;; + aarch64|arm64) echo "arm64" ;; + armv7l|armhf) echo "armv7" ;; + *) echo "amd64" ;; + esac +} + # ── Баннер ────────────────────────────────────────────────────────────────── banner() { echo -e "${CYAN}${BOLD}" echo "╔══════════════════════════════════════════════════════════╗" - echo "║ WireGuard VPS Tunnel — 1-Click Installer ║" - echo "║ VPS ↔ Домашний сервер (за NAT / Starlink) ║" + echo "║ WireGuard VPS Tunnel — Docker Installer ║" + echo "║ Multi-arch: amd64 + arm64 (RPi / Orange Pi) ║" echo "╚══════════════════════════════════════════════════════════╝" echo -e "${NC}" } @@ -58,68 +64,34 @@ check_root() { fi } -# ── Определение ОС ───────────────────────────────────────────────────────── -detect_os() { - if [[ -f /etc/os-release ]]; then - . /etc/os-release - OS_ID="${ID}" - OS_NAME="${PRETTY_NAME:-$NAME}" +# ── Проверка и установка Docker ──────────────────────────────────────────── +install_docker() { + header "Проверка Docker" + + if command -v docker &>/dev/null; then + success "Docker уже установлен: $(docker --version)" else - error "Не удалось определить операционную систему" + info "Установка Docker..." + curl -fsSL https://get.docker.com | sh + success "Docker установлен" + fi + + if docker compose version &>/dev/null; then + success "Docker Compose доступен: $(docker compose version)" + else + error "Docker Compose не найден. Установите Docker Compose plugin." 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 установлен успешно" + # Проверка multi-arch поддержки + if docker buildx version &>/dev/null; then + success "Docker Buildx доступен: $(docker buildx version | head -1)" else - error "Не удалось установить WireGuard" - exit 1 + warn "Docker Buildx не найден. Локальная сборка будет только для текущей архитектуры." 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 -} - -# ── Генерация ключей ─────────────────────────────────────────────────────── +# ── Генерация ключей WireGuard ───────────────────────────────────────────── generate_keys() { header "Генерация ключей WireGuard" @@ -128,13 +100,28 @@ generate_keys() { 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" + if ! command -v wg &>/dev/null; then + info "Установка wireguard-tools для генерации ключей..." + apt-get update -qq 2>/dev/null && apt-get install -y -qq wireguard-tools 2>/dev/null || \ + yum install -y wireguard-tools 2>/dev/null || { + # Fallback: генерация через Docker + info "Генерация ключей через Docker (wireguard-tools не установлен на хосте)..." + docker run --rm alpine:3.20 sh -c " + apk add --no-cache wireguard-tools && \ + wg genkey | tee /tmp/server_private.key | wg pubkey > /tmp/server_public.key && \ + wg genkey | tee /tmp/client_private.key | wg pubkey > /tmp/client_public.key && \ + chmod 600 /tmp/server_private.key /tmp/client_private.key + " + } + fi - # Генерируем ключи клиента - wg genkey | tee "$CONFIG_DIR/client_private.key" | wg pubkey > "$CONFIG_DIR/client_public.key" - chmod 600 "$CONFIG_DIR/client_private.key" + if command -v wg &>/dev/null; then + 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" + fi success "Ключи сгенерированы" fi @@ -145,36 +132,105 @@ generate_keys() { CLIENT_PUBLIC_KEY=$(cat "$CONFIG_DIR/client_public.key") } -# ── Загрузка существующей конфигурации ───────────────────────────────────── -load_config() { - if [[ -f "$CONFIG_FILE" ]]; then - info "Загружена существующая конфигурация из $CONFIG_FILE" - source "$CONFIG_FILE" +# ── Создание .env файла ──────────────────────────────────────────────────── +create_env_file() { + if [[ "$MODE" == "vps" ]]; then + cat > "$SCRIPT_DIR/.env" < "$SCRIPT_DIR/.env" < "$CONFIG_FILE" </dev/null; then + success "Образ загружен из registry" + else + warn "Не удалось загрузить образ из registry. Локальная сборка..." + BUILD_LOCAL=true + start_docker + return + fi + + # Обновить docker-compose для использования загруженного образа + if [[ "$MODE" == "vps" ]]; then + docker compose -f "$SCRIPT_DIR/docker-compose.server.yml" up -d + else + docker compose -f "$SCRIPT_DIR/docker-compose.client.yml" up -d + fi + fi + + sleep 3 + + local container_name + if [[ "$MODE" == "vps" ]]; then + container_name="wireguard-server" + else + container_name="wireguard-client" + fi + + if docker ps --format '{{.Names}}' | grep -q "$container_name"; then + success "Контейнер ${container_name} запущен (${ARCH})" + else + error "Контейнер не запустился. Проверьте логи:" + if [[ "$MODE" == "vps" ]]; then + echo " docker compose -f docker-compose.server.yml logs" + else + echo " docker compose -f docker-compose.client.yml logs" + fi + exit 1 + fi } # ── Интерактивный опрос ──────────────────────────────────────────────────── interactive_setup() { header "Настройка параметров" + local arch + arch="$(detect_arch)" + echo -e "${BOLD}Текущая архитектура:${NC} ${arch}" + echo -e "${BOLD}Где запущен этот скрипт?${NC}" echo " 1) VPS (сервер с публичным IP)" echo " 2) Домашний сервер / Raspberry Pi (за NAT)" @@ -189,9 +245,8 @@ interactive_setup() { done if [[ "$MODE" == "vps" ]]; then - # Автоопределение публичного IP local detected_ip - detected_ip=$(curl -4 -s ifconfig.me 2>/dev/null || curl -4 -s icanhazip.com 2>/dev/null || echo "") + detected_ip=$(curl -4 -s --connect-timeout 5 ifconfig.me 2>/dev/null || curl -4 -s --connect-timeout 5 icanhazip.com 2>/dev/null || echo "") if [[ -n "$detected_ip" ]]; then echo -e "${BOLD}Публичный IP VPS:${NC} [${detected_ip}]" read -r -p "Нажмите Enter для подтверждения или введите другой: " input @@ -203,59 +258,36 @@ interactive_setup() { 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 + + echo "" + read -r -p "Собрать образ локально? [y/N]: " build_choice + if [[ "$build_choice" =~ ^[Yy]$ ]]; then + BUILD_LOCAL=true + 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 - ;; + --vps) MODE="vps"; INTERACTIVE=false; shift ;; + --client) MODE="client"; INTERACTIVE=false; shift ;; + --vps-ip) VPS_PUBLIC_IP="$2"; shift 2 ;; + --ports) PORTS="$2"; shift 2 ;; + --wg-port) WG_PORT="$2"; shift 2 ;; + --keepalive) KEEPALIVE="$2"; shift 2 ;; + --build) BUILD_LOCAL=true; shift ;; --help|-h) echo "Использование: $0 [ОПЦИИ]" echo "" @@ -263,204 +295,62 @@ parse_args() { 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 " --build Собрать образ локально (вместо загрузки из registry)" 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 " # Неинтерактивный: VPS (amd64 или arm64)" + echo " sudo $0 --vps --vps-ip 2.59.219.234 --ports 80,443" echo "" - echo " # Неинтерактивный режим (клиент)" + echo " # Неинтерактивный: клиент (Raspberry Pi arm64)" echo " sudo $0 --client --vps-ip 2.59.219.234 --keepalive 21" + echo "" + echo " # Локальная сборка вместо загрузки из registry" + echo " sudo $0 --vps --build" exit 0 ;; - *) - error "Неизвестный аргумент: $1" - echo "Используйте --help для справки" - exit 1 - ;; + *) 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() { + local ARCH + ARCH="$(detect_arch)" + 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}" + echo -e "${BOLD}Режим:${NC} ${MODE} (Docker, ${ARCH})" + 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}" + echo -e "${BOLD}Образ:${NC} ${BUILD_LOCAL:-pull} из ${REGISTRY}/${IMAGE_PREFIX}" if [[ "$MODE" == "vps" ]]; then - echo -e "${BOLD}Проброс портов:${NC} ${PORTS} → ${CLIENT_WG_IP}" + 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" + if [[ "$MODE" == "vps" ]]; then + echo " docker compose -f docker-compose.server.yml logs -f — логи сервера" + echo " docker compose -f docker-compose.server.yml ps — статус" + echo " docker exec wireguard-server wg show — статус туннеля" + else + echo " docker compose -f docker-compose.client.yml logs -f — логи клиента" + echo " docker compose -f docker-compose.client.yml ps — статус" + echo " docker exec wireguard-client wg show — статус туннеля" fi echo "" - echo -e "${BOLD}Конфигурация:${NC} ${CONFIG_FILE}" + echo -e "${BOLD}Конфигурация:${NC} ${SCRIPT_DIR}/.env" echo -e "${BOLD}Ключи:${NC} ${CONFIG_DIR}/" if [[ "$MODE" == "vps" ]]; then @@ -468,11 +358,19 @@ print_summary() { 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 "${YELLOW}Затем на клиенте (RPi):${NC}" echo -e " ${BOLD}sudo ./install.sh --client --vps-ip ${VPS_PUBLIC_IP}${NC}" + else + echo "" + echo -e "${YELLOW}${BOLD}⚠️ ВАЖНО:${NC} ${YELLOW}Скопируйте публичный ключ клиента на VPS:${NC}" + echo -e " ${BOLD}cat ${CONFIG_DIR}/client_public.key${NC}" + echo "" + echo -e "${YELLOW}И добавьте в .env на VPS:${NC}" + echo -e " ${BOLD}CLIENT_PUBLIC_KEY=<ключ>${NC}" fi echo "" + echo -e "${CYAN}Поддерживаемые архитектуры: linux/amd64 (Intel/AMD), linux/arm64 (RPi 4/5, Orange Pi)${NC}" } # ── Главная функция ──────────────────────────────────────────────────────── @@ -480,15 +378,13 @@ 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, либо интерактивный режим." + error "Не указан режим. Используйте --vps или --client." exit 1 fi @@ -497,9 +393,8 @@ main() { 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 "") + VPS_PUBLIC_IP=$(curl -4 -s --connect-timeout 5 ifconfig.me 2>/dev/null || curl -4 -s --connect-timeout 5 icanhazip.com 2>/dev/null || echo "") if [[ -z "$VPS_PUBLIC_IP" ]]; then error "Не удалось определить публичный IP. Укажите вручную: --vps-ip IP" exit 1 @@ -507,24 +402,11 @@ main() { info "Автоопределён публичный IP: ${VPS_PUBLIC_IP}" fi - install_deps - enable_ip_forward + install_docker generate_keys - - if [[ "$MODE" == "vps" ]]; then - configure_vps - else - configure_client - fi - - start_wireguard - save_config - - if [[ "$MODE" == "client" ]]; then - install_watchdog - fi - + create_env_file + start_docker print_summary } -main "$@" +main "$@" \ No newline at end of file diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..854de0e --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,38 @@ +# ============================================================================= +# WireGuard VPS Tunnel — сервер (VPS) +# Multi-arch: linux/amd64, linux/arm64 +# ============================================================================= +FROM --platform=$BUILDPLATFORM alpine:3.20 AS builder + +ARG TARGETPLATFORM +ARG BUILDPLATFORM + +RUN echo "Сборка на ${BUILDPLATFORM} для ${TARGETPLATFORM}" + +FROM --platform=$TARGETPLATFORM alpine:3.20 + +ARG TARGETARCH +ARG TARGETVARIANT + +RUN apk add --no-cache \ + wireguard-tools \ + iptables \ + ip6tables \ + iproute2 \ + bash \ + curl \ + ca-certificates + +COPY entrypoint.sh /entrypoint.sh +COPY healthcheck.sh /healthcheck.sh + +RUN chmod +x /entrypoint.sh /healthcheck.sh + +EXPOSE 51820/udp + +VOLUME ["/etc/wireguard"] + +HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ + CMD ["/healthcheck.sh"] + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/server/entrypoint.sh b/server/entrypoint.sh new file mode 100644 index 0000000..a940c30 --- /dev/null +++ b/server/entrypoint.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# ============================================================================= +# WireGuard VPS Tunnel — точка входа сервера (VPS) +# Генерирует конфигурацию wg0.conf из переменных окружения и поднимает туннель. +# ============================================================================= +set -euo pipefail + +# ── Значения по умолчанию ─────────────────────────────────────────────────── +SERVER_WG_IP="${SERVER_WG_IP:-10.0.0.1}" +SERVER_WG_PORT="${SERVER_WG_PORT:-51820}" +FORWARD_PORTS="${FORWARD_PORTS:-80,443}" +WG_CONF="/etc/wireguard/wg0.conf" + +# ── Логирование ───────────────────────────────────────────────────────────── +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$1] $*"; } + +# ── Автоопределение внешнего интерфейса и IP ──────────────────────────────── +detect_network() { + if [[ -z "${SERVER_PUBLIC_IFACE:-}" ]]; then + SERVER_PUBLIC_IFACE=$(ip route get 8.8.8.8 2>/dev/null | awk '{print $5; exit}') + if [[ -z "$SERVER_PUBLIC_IFACE" ]]; then + log "ERROR" "Не удалось определить внешний сетевой интерфейс" + exit 1 + fi + log "INFO" "Автоопределён внешний интерфейс: ${SERVER_PUBLIC_IFACE}" + fi + + if [[ -z "${SERVER_PUBLIC_IP:-}" ]]; then + SERVER_PUBLIC_IP=$(curl -4 -s ifconfig.me 2>/dev/null || curl -4 -s icanhazip.com 2>/dev/null || echo "") + if [[ -z "$SERVER_PUBLIC_IP" ]]; then + log "ERROR" "Не удалось определить публичный IP. Укажите SERVER_PUBLIC_IP" + exit 1 + fi + log "INFO" "Автоопределён публичный IP: ${SERVER_PUBLIC_IP}" + fi +} + +# ── Генерация ключей ─────────────────────────────────────────────────────── +generate_keys() { + local private_key_file="/etc/wireguard/server_private.key" + local public_key_file="/etc/wireguard/server_public.key" + + if [[ -f "$private_key_file" ]] && [[ -f "$public_key_file" ]]; then + log "INFO" "Ключи сервера уже существуют" + else + log "INFO" "Генерация ключей сервера..." + wg genkey | tee "$private_key_file" | wg pubkey > "$public_key_file" + chmod 600 "$private_key_file" + log "INFO" "Ключи сервера сгенерированы" + fi + + SERVER_PRIVATE_KEY=$(cat "$private_key_file") + SERVER_PUBLIC_KEY=$(cat "$public_key_file") +} + +# ── Создание конфигурации WireGuard ──────────────────────────────────────── +create_wg_config() { + if [[ -f "$WG_CONF" ]]; then + log "INFO" "Конфигурация ${WG_CONF} уже существует, пропускаем создание" + return 0 + fi + + if [[ -z "${CLIENT_PUBLIC_KEY:-}" ]]; then + log "ERROR" "CLIENT_PUBLIC_KEY не задан. Укажите публичный ключ клиента." + exit 1 + fi + + local client_wg_ip="${CLIENT_WG_IP:-10.0.0.2}" + + # Формируем правила DNAT для каждого порта + local dnat_rules="" + local dnat_down_rules="" + IFS=',' read -ra PORT_ARRAY <<< "$FORWARD_PORTS" + + for port in "${PORT_ARRAY[@]}"; do + port=$(echo "$port" | xargs) + dnat_rules+="iptables -t nat -A PREROUTING -d ${SERVER_PUBLIC_IP} -p tcp --dport ${port} -j DNAT --to-destination ${client_wg_ip}:${port}; " + dnat_down_rules+="iptables -t nat -D PREROUTING -d ${SERVER_PUBLIC_IP} -p tcp --dport ${port} -j DNAT --to-destination ${client_wg_ip}:${port}; " + done + + mkdir -p "$(dirname "$WG_CONF")" + + cat > "$WG_CONF" < /proc/sys/net/ipv4/ip_forward 2>/dev/null || true + echo 1 > /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || true + + wg-quick up wg0 + + log "INFO" "WireGuard интерфейс wg0 запущен" + log "INFO" "Публичный ключ сервера: ${SERVER_PUBLIC_KEY}" + log "INFO" "Скопируйте его на клиент: SERVER_PUBLIC_KEY=${SERVER_PUBLIC_KEY}" +} + +# ── Главная функция ──────────────────────────────────────────────────────── +main() { + log "INFO" "=== Запуск WireGuard VPS Tunnel (сервер) ===" + log "INFO" "SERVER_WG_IP=${SERVER_WG_IP}" + log "INFO" "SERVER_WG_PORT=${SERVER_WG_PORT}" + log "INFO" "FORWARD_PORTS=${FORWARD_PORTS}" + + detect_network + generate_keys + create_wg_config + start_wireguard + + log "INFO" "Сервер запущен. Ожидание..." + exec sleep infinity +} + +main diff --git a/server/healthcheck.sh b/server/healthcheck.sh new file mode 100644 index 0000000..ce1db8d --- /dev/null +++ b/server/healthcheck.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# ============================================================================= +# WireGuard VPS Tunnel — healthcheck сервера +# Проверяет, что интерфейс wg0 активен. +# ============================================================================= +set -euo pipefail + +if wg show wg0 &>/dev/null; then + exit 0 +fi +exit 1 diff --git a/uninstall.sh b/uninstall.sh index bbf5bd2..14540a1 100644 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # ============================================================================= -# WireGuard VPS Tunnel — Деинсталлятор -# Полное удаление туннеля, сервисов и правил iptables +# WireGuard VPS Tunnel — Деинсталлятор (Docker) +# Останавливает и удаляет Docker-контейнеры, образы и конфигурацию. # ============================================================================= set -euo pipefail @@ -17,31 +17,29 @@ success() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } -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" -SYSCTL_CONF="/etc/sysctl.d/99-wireguard-vps-tunnel.conf" -REMOVE_PACKAGE=false +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REMOVE_IMAGES=false +REMOVE_CONFIG=false usage() { echo "Использование: $0 [ОПЦИИ]" echo "" echo "Опции:" - echo " --remove-package Также удалить пакет wireguard-tools" + echo " --remove-images Также удалить Docker-образы" + echo " --remove-config Также удалить директорию config/ с ключами" echo " --help, -h Показать эту справку" echo "" echo "Пример:" echo " sudo $0" - echo " sudo $0 --remove-package" + echo " sudo $0 --remove-images --remove-config" exit 0 } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in - --remove-package) REMOVE_PACKAGE=true; shift ;; + --remove-images) REMOVE_IMAGES=true; shift ;; + --remove-config) REMOVE_CONFIG=true; shift ;; --help|-h) usage ;; *) error "Неизвестный аргумент: $1"; usage ;; esac @@ -55,168 +53,70 @@ check_root() { fi } -stop_services() { +stop_containers() { echo "" - info "Остановка сервисов..." + info "Остановка Docker-контейнеров..." - # Останавливаем watchdog - if systemctl is-active --quiet wg-watchdog 2>/dev/null; then - systemctl stop wg-watchdog - success "Watchdog остановлен" + if docker ps -a --format '{{.Names}}' | grep -q "wireguard-server"; then + docker compose -f "$SCRIPT_DIR/docker-compose.server.yml" down 2>/dev/null || true + success "Контейнер wireguard-server остановлен и удалён" else - info "Watchdog не запущен" + info "Контейнер wireguard-server не найден" fi - # Отключаем watchdog - if systemctl is-enabled --quiet wg-watchdog 2>/dev/null; then - systemctl disable wg-watchdog &>/dev/null - success "Watchdog отключён из автозагрузки" - fi - - # Останавливаем WireGuard - if systemctl is-active --quiet wg-quick@wg0 2>/dev/null; then - systemctl stop wg-quick@wg0 - success "WireGuard остановлен" + if docker ps -a --format '{{.Names}}' | grep -q "wireguard-client"; then + docker compose -f "$SCRIPT_DIR/docker-compose.client.yml" down 2>/dev/null || true + success "Контейнер wireguard-client остановлен и удалён" else - info "WireGuard не запущен" - fi - - # Отключаем WireGuard - if systemctl is-enabled --quiet wg-quick@wg0 2>/dev/null; then - systemctl disable wg-quick@wg0 &>/dev/null - success "WireGuard отключён из автозагрузки" + info "Контейнер wireguard-client не найден" fi } -remove_interface() { - info "Удаление интерфейса wg0..." +remove_images() { + if [[ "$REMOVE_IMAGES" != true ]]; then + return 0 + fi - if ip link show wg0 &>/dev/null; then - ip link delete wg0 2>/dev/null || true - success "Интерфейс wg0 удалён" - else - info "Интерфейс wg0 не существует" + info "Удаление Docker-образов..." + + if docker images --format '{{.Repository}}' | grep -q "wireguard-vps-tunnel-server"; then + docker rmi wireguard-vps-tunnel-server 2>/dev/null || true + success "Образ wireguard-vps-tunnel-server удалён" + fi + + if docker images --format '{{.Repository}}' | grep -q "wireguard-vps-tunnel-client"; then + docker rmi wireguard-vps-tunnel-client 2>/dev/null || true + success "Образ wireguard-vps-tunnel-client удалён" fi } -remove_iptables_rules() { - info "Очистка правил iptables..." - - # Удаляем правила DNAT и FORWARD, связанные с wg0 - local rules_removed=0 - - # Удаляем DNAT правила - while iptables -t nat -L PREROUTING -n 2>/dev/null | grep -q "10\.0\.0\."; do - local rule_num - rule_num=$(iptables -t nat -L PREROUTING -n --line-numbers 2>/dev/null | grep "10\.0\.0\." | head -1 | awk '{print $1}') - if [[ -n "$rule_num" ]]; then - iptables -t nat -D PREROUTING "$rule_num" 2>/dev/null || true - ((rules_removed++)) || true - else - break - fi - done - - # Удаляем MASQUERADE правила - while iptables -t nat -L POSTROUTING -n 2>/dev/null | grep -q "10\.0\.0\."; do - local rule_num - rule_num=$(iptables -t nat -L POSTROUTING -n --line-numbers 2>/dev/null | grep "10\.0\.0\." | head -1 | awk '{print $1}') - if [[ -n "$rule_num" ]]; then - iptables -t nat -D POSTROUTING "$rule_num" 2>/dev/null || true - ((rules_removed++)) || true - else - break - fi - done - - # Удаляем FORWARD правила для wg0 - while iptables -L FORWARD -n 2>/dev/null | grep -q "wg0"; do - local rule_num - rule_num=$(iptables -L FORWARD -n --line-numbers 2>/dev/null | grep "wg0" | head -1 | awk '{print $1}') - if [[ -n "$rule_num" ]]; then - iptables -D FORWARD "$rule_num" 2>/dev/null || true - ((rules_removed++)) || true - else - break - fi - done - - if [[ $rules_removed -gt 0 ]]; then - success "Удалено правил iptables: ${rules_removed}" - else - info "Правила iptables не найдены" - fi -} - -remove_files() { - info "Удаление файлов..." - - # Удаляем конфигурацию WireGuard - if [[ -f "$WG_CONF" ]]; then - rm -f "$WG_CONF" - success "Удалён: $WG_CONF" +remove_config() { + if [[ "$REMOVE_CONFIG" != true ]]; then + return 0 fi - # Удаляем watchdog - if [[ -f "$WATCHDOG_BIN" ]]; then - rm -f "$WATCHDOG_BIN" - success "Удалён: $WATCHDOG_BIN" + if [[ -d "$SCRIPT_DIR/config" ]]; then + rm -rf "$SCRIPT_DIR/config" + success "Директория config/ удалена" fi - # Удаляем systemd-сервис watchdog - if [[ -f "$WATCHDOG_SERVICE" ]]; then - rm -f "$WATCHDOG_SERVICE" - systemctl daemon-reload - success "Удалён: $WATCHDOG_SERVICE" - fi - - # Удаляем sysctl конфигурацию - if [[ -f "$SYSCTL_CONF" ]]; then - rm -f "$SYSCTL_CONF" - sysctl --system &>/dev/null - success "Удалён: $SYSCTL_CONF" - fi - - # Удаляем директорию с ключами и конфигом - if [[ -d "$CONFIG_DIR" ]]; then - rm -rf "$CONFIG_DIR" - success "Удалён: $CONFIG_DIR/" - fi - - # Удаляем лог watchdog - if [[ -f /var/log/wg-watchdog.log ]]; then - rm -f /var/log/wg-watchdog.log - success "Удалён: /var/log/wg-watchdog.log" - fi -} - -remove_package() { - if [[ "$REMOVE_PACKAGE" == true ]]; then - echo "" - warn "Удаление пакета wireguard-tools..." - read -r -p "Вы уверены? Это удалит WireGuard полностью [y/N]: " confirm - if [[ "$confirm" =~ ^[Yy]$ ]]; then - apt-get remove -y --purge wireguard-tools 2>/dev/null || true - apt-get autoremove -y 2>/dev/null || true - success "Пакет wireguard-tools удалён" - else - info "Пакет wireguard-tools оставлен" - fi + if [[ -f "$SCRIPT_DIR/.env" ]]; then + rm -f "$SCRIPT_DIR/.env" + success "Файл .env удалён" fi } main() { echo -e "${RED}${BOLD}" echo "╔══════════════════════════════════════════════════════════╗" - echo "║ WireGuard VPS Tunnel — ДЕИНСТАЛЛЯТОР ║" + echo "║ WireGuard VPS Tunnel — ДЕИНСТАЛЛЯТОР (Docker) ║" echo "╚══════════════════════════════════════════════════════════╝" echo -e "${NC}" check_root parse_args "$@" - warn "Этот скрипт полностью удалит WireGuard VPS Tunnel." - warn "Будут удалены: туннель, сервисы, правила iptables, ключи." + warn "Этот скрипт остановит и удалит Docker-контейнеры WireGuard VPS Tunnel." echo "" read -r -p "Продолжить? [y/N]: " confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then @@ -224,17 +124,16 @@ main() { exit 0 fi - stop_services - remove_interface - remove_iptables_rules - remove_files - remove_package + stop_containers + remove_images + remove_config echo "" success "WireGuard VPS Tunnel полностью удалён." echo "" echo -e "${YELLOW}${BOLD}Примечание:${NC} ${YELLOW}SSH-сервер и другие сервисы не были затронуты.${NC}" echo -e "${YELLOW}PasswordAuthentication в sshd_config НЕ изменялся.${NC}" + echo -e "${YELLOW}Docker и wireguard-tools (на хосте) не удалялись.${NC}" } main "$@"