feat: multi-arch Docker образы (amd64+arm64), CI/CD, registry
Some checks failed
Build and Push Docker Images / Сборка сервера (multi-arch) (push) Has been cancelled
Build and Push Docker Images / Сборка клиента (multi-arch) (push) Has been cancelled

- Dockerfile с multi-stage сборкой для linux/amd64 и linux/arm64
- Gitea Actions CI/CD для автоматической сборки и пуша в registry
- docker-compose с поддержкой multi-arch образов
- install.sh с автоопределением архитектуры и выбором: pull vs build
- uninstall.sh обновлён для Docker-деинсталляции
- README.md обновлён: multi-arch, CI/CD, Gitea registry
This commit is contained in:
Deploy Bot
2026-07-29 20:38:47 +01:00
parent a4e80f712a
commit 5d618a04b5
15 changed files with 1131 additions and 710 deletions

11
.dockerignore Normal file
View File

@@ -0,0 +1,11 @@
.git
.github
config/
.env
*.log
docker-compose.server.yml
docker-compose.client.yml
*.md
LICENSE
install.sh
uninstall.sh

46
.env.example Normal file
View File

@@ -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

101
.gitea/workflows/build.yml Normal file
View File

@@ -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

424
README.md
View File

@@ -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 │ │ 2.59.219.234 │
│ │ │ │
wg0: 10.0.0.1 ┌──────────────┐
:51820 (UDP) │ Docker │
│ wireguard-
iptables DNAT: │ server │
:80 → 10.0.0.2 │ │
:443 → 10.0.0.2 │ wg0:10.0.0.1 │
└────────┬────────┘ │ │ :51820 (UDP) │ │
│ │ │ │
│ │ iptables │ │
│ │ DNAT: │ │
│ │ :80→10.0.0.2 │ │
│ │ :443→10.0.0.2│ │
│ └──────┬───────┘ │
└────────┼────────┘
╔══════════════════════════╗ ╔══════════════╧══════════════╗
WireGuard UDP :51820 ║ ║ WireGuard UDP :51820
(PersistentKeepalive=21)║ ║ (PersistentKeepalive=21)
╚══════════════════════════╝ ╚══════════════╤══════════════╝
┌────────┴────────┐ ┌────────┴────────┐
│ Домашний сервер │ │ Домашний сервер │
@@ -32,11 +41,17 @@
│ Raspberry Pi │ │ Raspberry Pi │
│ Starlink │ │ Starlink │
│ │ │ │
wg0: 10.0.0.2 ┌──────────────┐
│ Docker
Watchdog │ wireguard-
(мониторинг) │ client │
└────────┬────────┘ │ │ ││
│ │ wg0:10.0.0.2 ││
│ │ ││
│ │ Watchdog ││
│ │ (встроенный) ││
│ └──────┬───────┘│
└────────┼────────┘
┌────────┴────────┐ ┌────────┴────────┐
│ Nginx Proxy │ │ Nginx Proxy │
@@ -48,115 +63,159 @@
**Как это работает:** **Как это работает:**
1. VPS принимает входящие соединения на порты 80 и 443 1. VPS принимает входящие соединения на порты 80 и 443
2. iptables перенаправляет трафик через WireGuard-туннель на домашний сервер (10.0.0.2) 2. iptables (внутри Docker-контейнера) перенаправляет трафик через WireGuard-туннель на домашний сервер (10.0.0.2)
3. Домашний сервер (RPi) инициирует соединение с VPS — работает через любой NAT 3. Домашний сервер (RPi) инициирует соединение с VPS — работает через любой NAT
4. `PersistentKeepalive = 21` секунда поддерживает туннель активным даже при отсутствии трафика 4. `PersistentKeepalive = 21` секунда поддерживает туннель активным даже при отсутствии трафика
5. Watchdog на RPi мониторит состояние туннеля и перезапускает при обрыве 5. Встроенный Watchdog на клиенте мониторит состояние туннеля и перезапускает при обрыве
6. Всё изолировано в Docker-контейнерах — не затрагивает хост-систему
## Быстрый старт (1 команда) ## Быстрый старт (1 команда)
### На VPS: ### На VPS:
```bash ```bash
curl -sSL https://raw.githubusercontent.com/user/wireguard-vps-tunnel/main/install.sh | sudo bash git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git
# Выбрать "1) VPS" cd wireguard-vps-tunnel
```
### На домашнем сервере (Raspberry Pi):
```bash
curl -sSL https://raw.githubusercontent.com/user/wireguard-vps-tunnel/main/install.sh | sudo bash
# Выбрать "2) Домашний сервер"
```
Или неинтерактивный режим:
```bash
# VPS
sudo ./install.sh --vps --ports 80,443 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. Клонирование репозитория ### 1. Клонирование репозитория
```bash ```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 cd wireguard-vps-tunnel
``` ```
### 2. Установка на VPS ### 2. Генерация ключей
```bash ```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
``` ```
Скрипт автоматически: ### 3. Настройка VPS (сервер)
- Установит `wireguard-tools`
- Сгенерирует ключи
- Настроит `wg0` интерфейс
- Настроит iptables (DNAT + FORWARD)
- Включит IP-форвардинг
- Запустит systemd-сервис
### 3. Установка на домашний сервер Создайте `.env` файл:
```env
CLIENT_PUBLIC_KEY=<ключ из config/client_public.key>
SERVER_PUBLIC_IP=2.59.219.234
FORWARD_PORTS=80,443
```
Запустите:
```bash ```bash
sudo ./install.sh --client --vps-ip 2.59.219.234 --keepalive 21 sudo docker compose -f docker-compose.server.yml up -d
``` ```
Скрипт автоматически: ### 4. Настройка клиента (домашний сервер)
- Установит `wireguard-tools`
- Сгенерирует ключи
- Настроит `wg0` интерфейс
- Установит watchdog для мониторинга туннеля
- Запустит systemd-сервисы
### 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 ```bash
# На VPS # На 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 ```bash
MODE="vps" # vps или client sudo ./install.sh --vps --ports 80,443
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"
``` ```
Ключи хранятся в `/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 (сервер) | — | | `--vps` | Режим VPS (сервер) | — |
| `--client` | Режим клиента (домашний сервер) | — | | `--client` | Режим клиента (домашний сервер) | — |
| `--vps-ip IP` | Публичный IP VPS | автоопределение | | `--vps-ip IP` | Публичный IP VPS | автоопределение |
| `--client-wg-ip IP` | WireGuard IP клиента | `10.0.0.2` |
| `--ports PORT,...` | Порты для проброса (VPS) | `80,443` | | `--ports PORT,...` | Порты для проброса (VPS) | `80,443` |
| `--wg-port PORT` | Порт WireGuard | `51820` | | `--wg-port PORT` | Порт WireGuard | `51820` |
| `--keepalive SEC` | PersistentKeepalive (сек) | `21` | | `--keepalive SEC` | PersistentKeepalive (сек) | `21` |
| `--help` | Справка | — | | `--build` | Локальная сборка образа (вместо загрузки из registry) | `pull` |
## Watchdog (мониторинг туннеля) ## Watchdog (мониторинг туннеля)
Watchdog работает только на клиенте (домашний сервер) и выполняет: Watchdog встроен в клиентский контейнер и выполняет:
1. Проверяет, что интерфейс `wg0` поднят 1. Проверяет, что интерфейс `wg0` поднят
2. Проверяет доступность VPS через туннель (`ping 10.0.0.1`) 2. Проверяет доступность VPS через туннель (`ping 10.0.0.1`)
@@ -166,15 +225,71 @@ Watchdog работает только на клиенте (домашний с
- 10с → 20с → 40с → 80с → 120с (максимум) - 10с → 20с → 40с → 80с → 120с (максимум)
6. После 3 успешных проверок подряд сбрасывает отсрочку 6. После 3 успешных проверок подряд сбрасывает отсрочку
**Логи:** `/var/log/wg-watchdog.log` (авторотация при 10 МБ) **Просмотр логов:**
**Управление:**
```bash ```bash
systemctl status wg-watchdog # Статус docker compose -f docker-compose.client.yml logs -f
systemctl restart wg-watchdog # Перезапуск
tail -f /var/log/wg-watchdog.log # Логи в реальном времени
``` ```
## 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 vs SSH Reverse Tunnel
| Критерий | WireGuard | SSH Reverse Tunnel | | Критерий | WireGuard | SSH Reverse Tunnel |
@@ -189,100 +304,62 @@ tail -f /var/log/wg-watchdog.log # Логи в реальном времени
| Отказоустойчивость | Встроенная | Требует autossh | | Отказоустойчивость | Встроенная | Требует autossh |
| Нагрузка на CPU | Минимальная (kernel) | Заметная (шифрование в userspace) | | Нагрузка на 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 ```bash
# На VPS: ограничить доступ к порту WireGuard только с IP клиента (если известен) # Проверить логи
iptables -A INPUT -p udp --dport 51820 -s <IP_КЛИЕНТА> -j ACCEPT docker compose -f docker-compose.server.yml logs
iptables -A INPUT -p udp --dport 51820 -j DROP docker compose -f docker-compose.client.yml logs
# Или ограничить rate limiting: # Проверить статус
iptables -A INPUT -p udp --dport 51820 -m limit --limit 10/sec -j ACCEPT docker compose -f docker-compose.server.yml ps
iptables -A INPUT -p udp --dport 51820 -j DROP docker compose -f docker-compose.client.yml ps
``` ```
## Устранение неполадок
### Туннель не поднимается ### Туннель не поднимается
```bash ```bash
# Проверить статус # Проверить статус WireGuard внутри контейнера
systemctl status wg-quick@wg0 docker exec wireguard-server wg show
docker exec wireguard-client wg show
# Проверить конфигурацию # Проверить, что порт открыт на VPS
wg show
# Проверить логи
journalctl -u wg-quick@wg0 -n 50
```
### Пинг не проходит
```bash
# Проверить что интерфейс поднят
ip link show wg0
# Проверить IP-адреса
ip addr show wg0
# Проверить что порт открыт на VPS
# На клиенте:
nc -zvu <VPS_IP> 51820 nc -zvu <VPS_IP> 51820
``` ```
### Порты не пробрасываются ### Порты не пробрасываются
```bash ```bash
# Проверить правила iptables на VPS # Проверить правила iptables внутри контейнера
iptables -t nat -L PREROUTING -n docker exec wireguard-server iptables -t nat -L PREROUTING -n
iptables -L FORWARD -n docker exec wireguard-server iptables -L FORWARD -n
# Проверить IP-форвардинг # Проверить IP-форвардинг
sysctl net.ipv4.ip_forward # Должно быть = 1 docker exec wireguard-server sysctl net.ipv4.ip_forward
``` ```
### Watchdog не запускается ### Пинг не проходит
```bash ```bash
systemctl status wg-watchdog # Проверить что интерфейс поднят
journalctl -u wg-watchdog -n 50 docker exec wireguard-server ip link show wg0
cat /var/log/wg-watchdog.log 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 ```bash
# Деинсталляция # Деинсталляция
sudo ./uninstall.sh sudo ./uninstall.sh --remove-images --remove-config
# Переустановка # Переустановка
sudo ./install.sh --vps --vps-ip <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`
Деинсталлятор: Деинсталлятор:
- Останавливает и отключает сервисы - Останавливает и удаляет Docker-контейнеры
- Удаляет интерфейс `wg0` - Опционально удаляет образы и конфигурацию
- Очищает правила iptables - **НЕ трогает** SSH-сервер, Docker и другие сервисы
- Удаляет конфигурационные файлы и ключи - **НЕ изменяет** `sshd_config` (PasswordAuthentication остаётся без изменений)
- **НЕ трогает** SSH-сервер и другие сервисы
## Поддерживаемые ОС ## Безопасность
- Debian 11+ (Bullseye, Bookworm) - **Ключи**: приватные ключи хранятся с правами `600` (только root)
- Ubuntu 20.04+ (Focal, Jammy, Noble) - **Изоляция**: контейнеры работают в изолированном окружении (Alpine Linux)
- Armbian (Orange Pi, Banana Pi, etc.) - **Фаервол**: iptables правила применяются атомарно через `PostUp`/`PostDown`
- Raspberry Pi OS (Raspbian) - **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/ wireguard-vps-tunnel/
├── install.sh # Основной установщик ├── server/
├── uninstall.sh # Деинсталлятор │ ├── Dockerfile # Docker-образ сервера (Alpine 3.20)
├── watchdog.sh # Watchdog для мониторинга туннеля │ ├── entrypoint.sh # Точка входа: генерация конфига, запуск WG
└── README.md # Документация │ └── 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 MIT

37
client/Dockerfile Normal file
View File

@@ -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"]

108
client/entrypoint.sh Normal file
View File

@@ -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" <<WGCONF
# WireGuard VPS Tunnel — конфигурация клиента (домашний сервер)
# Создано: $(date '+%Y-%m-%d %H:%M:%S')
[Interface]
PrivateKey = ${CLIENT_PRIVATE_KEY}
Address = ${CLIENT_WG_IP}/24
[Peer]
# VPS (сервер)
PublicKey = ${SERVER_PUBLIC_KEY}
Endpoint = ${VPS_PUBLIC_IP}:${VPS_WG_PORT}
AllowedIPs = ${SERVER_WG_IP}/24
PersistentKeepalive = ${PERSISTENT_KEEPALIVE}
WGCONF
chmod 600 "$WG_CONF"
log "INFO" "Конфигурация WireGuard создана: ${WG_CONF}"
}
# ── Запуск WireGuard ───────────────────────────────────────────────────────
start_wireguard() {
log "INFO" "Запуск WireGuard интерфейса wg0..."
wg-quick up wg0
log "INFO" "WireGuard интерфейс wg0 запущен"
log "INFO" "Публичный ключ клиента: ${CLIENT_PUBLIC_KEY}"
log "INFO" "Скопируйте его на сервер: CLIENT_PUBLIC_KEY=${CLIENT_PUBLIC_KEY}"
}
# ── Главная функция ────────────────────────────────────────────────────────
main() {
log "INFO" "=== Запуск WireGuard VPS Tunnel (клиент) ==="
log "INFO" "CLIENT_WG_IP=${CLIENT_WG_IP}"
log "INFO" "VPS_PUBLIC_IP=${VPS_PUBLIC_IP}"
log "INFO" "VPS_WG_PORT=${VPS_WG_PORT}"
log "INFO" "PERSISTENT_KEEPALIVE=${PERSISTENT_KEEPALIVE}"
check_required
generate_keys
create_wg_config
start_wireguard
# Запуск watchdog в фоне
log "INFO" "Запуск watchdog..."
/watchdog.sh &
log "INFO" "Клиент запущен. Ожидание..."
exec sleep infinity
}
main

13
client/healthcheck.sh Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# =============================================================================
# WireGuard VPS Tunnel — healthcheck клиента
# Проверяет доступность VPS через WireGuard-туннель (ping 10.0.0.1).
# =============================================================================
set -euo pipefail
SERVER_WG_IP="${SERVER_WG_IP:-10.0.0.1}"
if ping -c 1 -W 2 "$SERVER_WG_IP" &>/dev/null; then
exit 0
fi
exit 1

View File

@@ -3,56 +3,31 @@
# WireGuard Watchdog — оптимизирован для Starlink # WireGuard Watchdog — оптимизирован для Starlink
# Мониторит состояние туннеля и перезапускает при обрыве связи. # Мониторит состояние туннеля и перезапускает при обрыве связи.
# Использует экспоненциальную отсрочку (backoff) при повторных сбоях. # Использует экспоненциальную отсрочку (backoff) при повторных сбоях.
# Запускается внутри Docker-контейнера клиента.
# ============================================================================= # =============================================================================
set -euo pipefail set -euo pipefail
# ── Настройки ─────────────────────────────────────────────────────────────── # ── Настройки ───────────────────────────────────────────────────────────────
VPS_IP="" # Публичный IP VPS (заполняется install.sh) VPS_PUBLIC_IP="${VPS_PUBLIC_IP:-}"
VPS_WG_IP="" # WireGuard IP VPS (заполняется install.sh) SERVER_WG_IP="${SERVER_WG_IP:-10.0.0.1}"
WG_INTERFACE="wg0" WG_INTERFACE="wg0"
LOG_FILE="/var/log/wg-watchdog.log"
MAX_LOG_SIZE=$((10 * 1024 * 1024)) # 10 МБ
# ── Параметры экспоненциальной отсрочки ───────────────────────────────────── # ── Параметры экспоненциальной отсрочки ─────────────────────────────────────
INITIAL_BACKOFF=10 # Начальная задержка (сек) INITIAL_BACKOFF=10
MAX_BACKOFF=120 # Максимальная задержка (сек) MAX_BACKOFF=120
BACKOFF_MULTIPLIER=2 # Множитель BACKOFF_MULTIPLIER=2
STABLE_THRESHOLD=3 # Кол-во успешных проверок для сброса backoff STABLE_THRESHOLD=3
# ── Состояние ─────────────────────────────────────────────────────────────── # ── Состояние ───────────────────────────────────────────────────────────────
consecutive_failures=0 consecutive_failures=0
consecutive_successes=0 consecutive_successes=0
current_backoff=$INITIAL_BACKOFF 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() { log() {
local level="$1" local level="$1"
shift shift
local timestamp echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*"
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
} }
# ── Проверка интерфейса WireGuard ────────────────────────────────────────── # ── Проверка интерфейса WireGuard ──────────────────────────────────────────
@@ -65,11 +40,10 @@ check_wg_interface() {
# ── Проверка доступности VPS через туннель ────────────────────────────────── # ── Проверка доступности VPS через туннель ──────────────────────────────────
check_vps_tunnel() { check_vps_tunnel() {
if [[ -z "$VPS_WG_IP" ]]; then if [[ -z "$SERVER_WG_IP" ]]; then
return 1 return 1
fi fi
# 3 пинга, таймаут 2 секунды if ping -c 3 -W 2 "$SERVER_WG_IP" &>/dev/null; then
if ping -c 3 -W 2 "$VPS_WG_IP" &>/dev/null; then
return 0 return 0
fi fi
return 1 return 1
@@ -77,11 +51,10 @@ check_vps_tunnel() {
# ── Проверка доступности VPS через интернет ───────────────────────────────── # ── Проверка доступности VPS через интернет ─────────────────────────────────
check_vps_internet() { check_vps_internet() {
if [[ -z "$VPS_IP" ]]; then if [[ -z "$VPS_PUBLIC_IP" ]]; then
return 1 return 1
fi fi
# Проверяем, что интернет вообще работает if ping -c 2 -W 3 "$VPS_PUBLIC_IP" &>/dev/null; then
if ping -c 2 -W 3 "$VPS_IP" &>/dev/null; then
return 0 return 0
fi fi
return 1 return 1
@@ -90,9 +63,9 @@ check_vps_internet() {
# ── Перезапуск WireGuard ──────────────────────────────────────────────────── # ── Перезапуск WireGuard ────────────────────────────────────────────────────
restart_wireguard() { restart_wireguard() {
log "WARN" "Перезапуск wg-quick@${WG_INTERFACE}..." log "WARN" "Перезапуск wg-quick@${WG_INTERFACE}..."
systemctl restart "wg-quick@${WG_INTERFACE}" 2>&1 | while read -r line; do wg-quick down "$WG_INTERFACE" 2>/dev/null || true
log "INFO" "systemctl: $line" sleep 2
done wg-quick up "$WG_INTERFACE"
sleep 3 sleep 3
} }
@@ -102,11 +75,8 @@ reset_backoff() {
current_backoff=$INITIAL_BACKOFF current_backoff=$INITIAL_BACKOFF
consecutive_successes=$((consecutive_successes + 1)) consecutive_successes=$((consecutive_successes + 1))
if [[ $consecutive_successes -ge $STABLE_THRESHOLD ]]; then if [[ $consecutive_successes -eq $STABLE_THRESHOLD ]]; then
# Логируем только при переходе в стабильное состояние log "INFO" "Туннель стабилен (${STABLE_THRESHOLD} успешных проверок подряд)"
if [[ $consecutive_successes -eq $STABLE_THRESHOLD ]]; then
log "INFO" "Туннель стабилен (${STABLE_THRESHOLD} успешных проверок подряд)"
fi
fi fi
} }
@@ -122,7 +92,7 @@ increase_backoff() {
# ── Основной цикл ────────────────────────────────────────────────────────── # ── Основной цикл ──────────────────────────────────────────────────────────
main_loop() { 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}" log "INFO" "Параметры backoff: начальный=${INITIAL_BACKOFF}с, макс=${MAX_BACKOFF}с, множитель=${BACKOFF_MULTIPLIER}"
while true; do while true; do
@@ -143,11 +113,10 @@ main_loop() {
fi fi
# Туннель не работает — проверяем интернет # Туннель не работает — проверяем интернет
log "WARN" "VPS ${VPS_WG_IP} недоступен через туннель" log "WARN" "VPS ${SERVER_WG_IP} недоступен через туннель"
if ! check_vps_internet; then if ! check_vps_internet; then
# Интернет тоже не работает — перезапуск не поможет log "WARN" "VPS ${VPS_PUBLIC_IP} недоступен через интернет. Возможно, проблемы с сетью."
log "WARN" "VPS ${VPS_IP} недоступен через интернет. Возможно, проблемы с сетью."
increase_backoff increase_backoff
log "INFO" "Ожидание ${current_backoff}с..." log "INFO" "Ожидание ${current_backoff}с..."
sleep "$current_backoff" sleep "$current_backoff"
@@ -158,7 +127,6 @@ main_loop() {
log "ERROR" "Туннель не работает, но VPS доступен через интернет. Перезапуск WireGuard..." log "ERROR" "Туннель не работает, но VPS доступен через интернет. Перезапуск WireGuard..."
restart_wireguard restart_wireguard
# Проверяем после перезапуска
sleep 3 sleep 3
if check_vps_tunnel; then if check_vps_tunnel; then
log "INFO" "Туннель восстановлен после перезапуска" log "INFO" "Туннель восстановлен после перезапуска"
@@ -174,16 +142,8 @@ main_loop() {
} }
# ── Точка входа ───────────────────────────────────────────────────────────── # ── Точка входа ─────────────────────────────────────────────────────────────
load_config if [[ -z "$VPS_PUBLIC_IP" ]]; then
log "ERROR" "VPS_PUBLIC_IP не задан. Watchdog не может работать."
# Проверяем что IP заданы
if [[ -z "$VPS_IP" ]]; then
echo "ОШИБКА: VPS_IP не задан. Отредактируйте скрипт или файл конфигурации." >&2
exit 1
fi
if [[ -z "$VPS_WG_IP" ]]; then
echo "ОШИБКА: VPS_WG_IP не задан. Отредактируйте скрипт или файл конфигурации." >&2
exit 1 exit 1
fi fi

36
docker-compose.client.yml Normal file
View File

@@ -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

39
docker-compose.server.yml Normal file
View File

@@ -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

View File

@@ -1,37 +1,31 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ============================================================================= # =============================================================================
# WireGuard VPS Tunnel — 1-Click Installer # WireGuard VPS Tunnel — 1-Click Docker Installer
# Автоматическая настройка туннеля между VPS (публичный IP) и домашним сервером # Multi-arch: linux/amd64, linux/arm64 (ARM Raspberry Pi)
# (Raspberry Pi / Orange Pi за NAT, например Starlink)
# ============================================================================= # =============================================================================
set -euo pipefail set -euo pipefail
# ── Цвета для вывода ──────────────────────────────────────────────────────── # ── Цвета ──────────────────────────────────────────────────────────────────
RED='\033[0;31m' RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
GREEN='\033[0;32m' BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
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" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="$CONFIG_DIR/config" CONFIG_DIR="$SCRIPT_DIR/config"
WG_CONF="/etc/wireguard/wg0.conf" REGISTRY="git.softuniq.eu"
WATCHDOG_BIN="/usr/local/bin/wg-watchdog.sh" IMAGE_PREFIX="opendoor/wireguard-vps-tunnel"
WATCHDOG_SERVICE="/etc/systemd/system/wg-watchdog.service" IMAGE_TAG="latest"
WG_SERVICE="wg-quick@wg0"
# ── Значения по умолчанию ────────────────────────────────────────────────── # ── Значения по умолчанию ──────────────────────────────────────────────────
MODE="" # vps или client MODE=""
VPS_PUBLIC_IP="" # Публичный IP VPS VPS_PUBLIC_IP=""
CLIENT_WG_IP="10.0.0.2" # WireGuard IP клиента CLIENT_WG_IP="10.0.0.2"
SERVER_WG_IP="10.0.0.1" # WireGuard IP сервера SERVER_WG_IP="10.0.0.1"
WG_PORT="51820" # Порт WireGuard WG_PORT="51820"
PORTS="80,443" # Порты для проброса (только VPS) PORTS="80,443"
KEEPALIVE="21" # PersistentKeepalive (секунды) KEEPALIVE="21"
INTERACTIVE=true INTERACTIVE=true
BUILD_LOCAL=false
# ── Функции вывода ────────────────────────────────────────────────────────── # ── Функции вывода ──────────────────────────────────────────────────────────
info() { echo -e "${BLUE}[INFO]${NC} $*"; } info() { echo -e "${BLUE}[INFO]${NC} $*"; }
@@ -40,12 +34,24 @@ warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; }
header() { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}\n"; } 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() { banner() {
echo -e "${CYAN}${BOLD}" echo -e "${CYAN}${BOLD}"
echo "╔══════════════════════════════════════════════════════════╗" echo "╔══════════════════════════════════════════════════════════╗"
echo "║ WireGuard VPS Tunnel — 1-Click Installer ║" echo "║ WireGuard VPS Tunnel — Docker Installer ║"
echo "║ VPS ↔ Домашний сервер (за NAT / Starlink) ║" echo "║ Multi-arch: amd64 + arm64 (RPi / Orange Pi) ║"
echo "╚══════════════════════════════════════════════════════════╝" echo "╚══════════════════════════════════════════════════════════╝"
echo -e "${NC}" echo -e "${NC}"
} }
@@ -58,68 +64,34 @@ check_root() {
fi fi
} }
# ── Определение ОС ───────────────────────────────────────────────────────── # ── Проверка и установка Docker ────────────────────────────────────────────
detect_os() { install_docker() {
if [[ -f /etc/os-release ]]; then header "Проверка Docker"
. /etc/os-release
OS_ID="${ID}" if command -v docker &>/dev/null; then
OS_NAME="${PRETTY_NAME:-$NAME}" success "Docker уже установлен: $(docker --version)"
else 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 exit 1
fi fi
case "$OS_ID" in # Проверка multi-arch поддержки
debian|ubuntu|armbian|raspbian) if docker buildx version &>/dev/null; then
info "Обнаружена ОС: ${OS_NAME}" success "Docker Buildx доступен: $(docker buildx version | head -1)"
;;
*)
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 else
error "Не удалось установить WireGuard" warn "Docker Buildx не найден. Локальная сборка будет только для текущей архитектуры."
exit 1
fi fi
} }
# ── Включение IP-форвардинга ─────────────────────────────────────────────── # ── Генерация ключей WireGuard ─────────────────────────────────────────────
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() { generate_keys() {
header "Генерация ключей WireGuard" header "Генерация ключей WireGuard"
@@ -128,13 +100,28 @@ generate_keys() {
if [[ -f "$CONFIG_DIR/server_private.key" ]] && [[ -f "$CONFIG_DIR/client_private.key" ]]; then if [[ -f "$CONFIG_DIR/server_private.key" ]] && [[ -f "$CONFIG_DIR/client_private.key" ]]; then
info "Ключи уже существуют, пропускаем генерацию" info "Ключи уже существуют, пропускаем генерацию"
else else
# Генерируем ключи сервера if ! command -v wg &>/dev/null; then
wg genkey | tee "$CONFIG_DIR/server_private.key" | wg pubkey > "$CONFIG_DIR/server_public.key" info "Установка wireguard-tools для генерации ключей..."
chmod 600 "$CONFIG_DIR/server_private.key" 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
# Генерируем ключи клиента if command -v wg &>/dev/null; then
wg genkey | tee "$CONFIG_DIR/client_private.key" | wg pubkey > "$CONFIG_DIR/client_public.key" wg genkey | tee "$CONFIG_DIR/server_private.key" | wg pubkey > "$CONFIG_DIR/server_public.key"
chmod 600 "$CONFIG_DIR/client_private.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 "Ключи сгенерированы" success "Ключи сгенерированы"
fi fi
@@ -145,36 +132,105 @@ generate_keys() {
CLIENT_PUBLIC_KEY=$(cat "$CONFIG_DIR/client_public.key") CLIENT_PUBLIC_KEY=$(cat "$CONFIG_DIR/client_public.key")
} }
# ── Загрузка существующей конфигурации ───────────────────────────────────── # ── Создание .env файла ────────────────────────────────────────────────────
load_config() { create_env_file() {
if [[ -f "$CONFIG_FILE" ]]; then if [[ "$MODE" == "vps" ]]; then
info "Загружена существующая конфигурация из $CONFIG_FILE" cat > "$SCRIPT_DIR/.env" <<EOF
source "$CONFIG_FILE" # WireGuard VPS Tunnel — конфигурация сервера (VPS)
CLIENT_PUBLIC_KEY=${CLIENT_PUBLIC_KEY}
SERVER_PUBLIC_IP=${VPS_PUBLIC_IP:-}
SERVER_PUBLIC_IFACE=
FORWARD_PORTS=${PORTS}
EOF
else
cat > "$SCRIPT_DIR/.env" <<EOF
# WireGuard VPS Tunnel — конфигурация клиента
VPS_PUBLIC_IP=${VPS_PUBLIC_IP}
SERVER_PUBLIC_KEY=${SERVER_PUBLIC_KEY}
PERSISTENT_KEEPALIVE=${KEEPALIVE}
EOF
fi fi
chmod 600 "$SCRIPT_DIR/.env"
success "Файл .env создан"
} }
# ── Сохранение конфигурации ──────────────────────────────────────────────── # ── Запуск Docker Compose ──────────────────────────────────────────────────
save_config() { start_docker() {
mkdir -p "$CONFIG_DIR" header "Запуск Docker-контейнера"
cat > "$CONFIG_FILE" <<EOF
# WireGuard VPS Tunnel — конфигурация local ARCH
# Создано: $(date '+%Y-%m-%d %H:%M:%S') ARCH="$(detect_arch)"
MODE="$MODE" info "Архитектура: ${ARCH}"
VPS_PUBLIC_IP="$VPS_PUBLIC_IP"
CLIENT_WG_IP="$CLIENT_WG_IP" if [[ "$BUILD_LOCAL" == true ]]; then
SERVER_WG_IP="$SERVER_WG_IP" # Локальная сборка для текущей архитектуры
WG_PORT="$WG_PORT" if [[ "$MODE" == "vps" ]]; then
PORTS="$PORTS" info "Локальная сборка серверного контейнера (${ARCH})..."
KEEPALIVE="$KEEPALIVE" docker compose -f "$SCRIPT_DIR/docker-compose.server.yml" up -d --build
EOF else
chmod 600 "$CONFIG_FILE" info "Локальная сборка клиентского контейнера (${ARCH})..."
success "Конфигурация сохранена в $CONFIG_FILE" docker compose -f "$SCRIPT_DIR/docker-compose.client.yml" up -d --build
fi
else
# Использование готового образа из registry
local image_name
if [[ "$MODE" == "vps" ]]; then
image_name="${REGISTRY}/${IMAGE_PREFIX}-server:${IMAGE_TAG}"
else
image_name="${REGISTRY}/${IMAGE_PREFIX}-client:${IMAGE_TAG}"
fi
info "Загрузка образа: ${image_name} (${ARCH})..."
# Подтянуть образ для текущей архитектуры
if docker pull "${image_name}" 2>/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() { interactive_setup() {
header "Настройка параметров" header "Настройка параметров"
local arch
arch="$(detect_arch)"
echo -e "${BOLD}Текущая архитектура:${NC} ${arch}"
echo -e "${BOLD}Где запущен этот скрипт?${NC}" echo -e "${BOLD}Где запущен этот скрипт?${NC}"
echo " 1) VPS (сервер с публичным IP)" echo " 1) VPS (сервер с публичным IP)"
echo " 2) Домашний сервер / Raspberry Pi (за NAT)" echo " 2) Домашний сервер / Raspberry Pi (за NAT)"
@@ -189,9 +245,8 @@ interactive_setup() {
done done
if [[ "$MODE" == "vps" ]]; then if [[ "$MODE" == "vps" ]]; then
# Автоопределение публичного IP
local detected_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 if [[ -n "$detected_ip" ]]; then
echo -e "${BOLD}Публичный IP VPS:${NC} [${detected_ip}]" echo -e "${BOLD}Публичный IP VPS:${NC} [${detected_ip}]"
read -r -p "Нажмите Enter для подтверждения или введите другой: " input read -r -p "Нажмите Enter для подтверждения или введите другой: " input
@@ -203,59 +258,36 @@ interactive_setup() {
read -r -p "Порты для проброса (через запятую) [80,443]: " input read -r -p "Порты для проброса (через запятую) [80,443]: " input
PORTS="${input:-80,443}" 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 read -r -p "WireGuard порт [51820]: " input
WG_PORT="${input:-51820}" WG_PORT="${input:-51820}"
else else
read -r -p "Публичный IP VPS: " VPS_PUBLIC_IP 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 read -r -p "WireGuard порт VPS [51820]: " input
WG_PORT="${input:-51820}" WG_PORT="${input:-51820}"
read -r -p "PersistentKeepalive (сек) [21]: " input read -r -p "PersistentKeepalive (сек) [21]: " input
KEEPALIVE="${input:-21}" KEEPALIVE="${input:-21}"
fi fi
echo ""
read -r -p "Собрать образ локально? [y/N]: " build_choice
if [[ "$build_choice" =~ ^[Yy]$ ]]; then
BUILD_LOCAL=true
fi
} }
# ── Парсинг аргументов командной строки ──────────────────────────────────── # ── Парсинг аргументов ─────────────────────────────────────────────────────
parse_args() { parse_args() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--vps) --vps) MODE="vps"; INTERACTIVE=false; shift ;;
MODE="vps" --client) MODE="client"; INTERACTIVE=false; shift ;;
INTERACTIVE=false --vps-ip) VPS_PUBLIC_IP="$2"; shift 2 ;;
shift --ports) PORTS="$2"; shift 2 ;;
;; --wg-port) WG_PORT="$2"; shift 2 ;;
--client) --keepalive) KEEPALIVE="$2"; shift 2 ;;
MODE="client" --build) BUILD_LOCAL=true; shift ;;
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) --help|-h)
echo "Использование: $0 [ОПЦИИ]" echo "Использование: $0 [ОПЦИИ]"
echo "" echo ""
@@ -263,204 +295,62 @@ parse_args() {
echo " --vps Режим VPS (сервер с публичным IP)" echo " --vps Режим VPS (сервер с публичным IP)"
echo " --client Режим клиента (домашний сервер за NAT)" echo " --client Режим клиента (домашний сервер за NAT)"
echo " --vps-ip IP Публичный IP-адрес VPS" echo " --vps-ip IP Публичный IP-адрес VPS"
echo " --client-wg-ip IP WireGuard IP клиента (по умолчанию: 10.0.0.2)"
echo " --ports PORT,... Порты для проброса (по умолчанию: 80,443)" echo " --ports PORT,... Порты для проброса (по умолчанию: 80,443)"
echo " --wg-port PORT Порт WireGuard (по умолчанию: 51820)" echo " --wg-port PORT Порт WireGuard (по умолчанию: 51820)"
echo " --keepalive SEC PersistentKeepalive в секундах (по умолчанию: 21)" echo " --keepalive SEC PersistentKeepalive в секундах (по умолчанию: 21)"
echo " --build Собрать образ локально (вместо загрузки из registry)"
echo " --help, -h Показать эту справку" echo " --help, -h Показать эту справку"
echo "" echo ""
echo "Примеры:" echo "Примеры:"
echo " # Интерактивный режим" echo " # Интерактивный режим"
echo " sudo $0" echo " sudo $0"
echo "" echo ""
echo " # Неинтерактивный режим (VPS)" echo " # Неинтерактивный: VPS (amd64 или arm64)"
echo " sudo $0 --vps --vps-ip 2.59.219.234 --ports 80,443,8080" echo " sudo $0 --vps --vps-ip 2.59.219.234 --ports 80,443"
echo "" echo ""
echo " # Неинтерактивный режим (клиент)" echo " # Неинтерактивный: клиент (Raspberry Pi arm64)"
echo " sudo $0 --client --vps-ip 2.59.219.234 --keepalive 21" echo " sudo $0 --client --vps-ip 2.59.219.234 --keepalive 21"
echo ""
echo " # Локальная сборка вместо загрузки из registry"
echo " sudo $0 --vps --build"
exit 0 exit 0
;; ;;
*) *) error "Неизвестный аргумент: $1"; echo "Используйте --help"; exit 1 ;;
error "Неизвестный аргумент: $1"
echo "Используйте --help для справки"
exit 1
;;
esac esac
done 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" <<WGCONF
# WireGuard VPS Tunnel — конфигурация сервера (VPS)
# Создано: $(date '+%Y-%m-%d %H:%M:%S')
[Interface]
PrivateKey = ${SERVER_PRIVATE_KEY}
Address = ${SERVER_WG_IP}/24
ListenPort = ${WG_PORT}
# Правила проброса портов и форвардинга
PostUp = ${dnat_rules} iptables -t nat -A POSTROUTING -d ${CLIENT_WG_IP} -j MASQUERADE; iptables -I FORWARD 1 -i ${ext_if} -o wg0 -p tcp -m multiport --dports ${PORTS} -j ACCEPT; iptables -I FORWARD 2 -i wg0 -o ${ext_if} -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = ${dnat_down_rules} iptables -t nat -D POSTROUTING -d ${CLIENT_WG_IP} -j MASQUERADE; iptables -D FORWARD -i ${ext_if} -o wg0 -p tcp -m multiport --dports ${PORTS} -j ACCEPT; iptables -D FORWARD -i wg0 -o ${ext_if} -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
[Peer]
# Клиент (домашний сервер / Raspberry Pi)
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = ${CLIENT_WG_IP}/32
WGCONF
chmod 600 "$WG_CONF"
success "Конфигурация WireGuard создана: $WG_CONF"
}
# ── Создание конфигурации WireGuard для клиента ────────────────────────────
configure_client() {
header "Настройка WireGuard (клиент — домашний сервер)"
cat > "$WG_CONF" <<WGCONF
# WireGuard VPS Tunnel — конфигурация клиента (домашний сервер)
# Создано: $(date '+%Y-%m-%d %H:%M:%S')
[Interface]
PrivateKey = ${CLIENT_PRIVATE_KEY}
Address = ${CLIENT_WG_IP}/24
[Peer]
# VPS (сервер)
PublicKey = ${SERVER_PUBLIC_KEY}
Endpoint = ${VPS_PUBLIC_IP}:${WG_PORT}
AllowedIPs = ${SERVER_WG_IP}/24
PersistentKeepalive = ${KEEPALIVE}
WGCONF
chmod 600 "$WG_CONF"
success "Конфигурация WireGuard создана: $WG_CONF"
}
# ── Запуск WireGuard ───────────────────────────────────────────────────────
start_wireguard() {
header "Запуск WireGuard"
# Останавливаем если уже запущен (идемпотентность)
if systemctl is-active --quiet "$WG_SERVICE" 2>/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" <<SYSTEMD
[Unit]
Description=WireGuard Watchdog — оптимизирован для Starlink
After=network-online.target wg-quick@wg0.service
Wants=network-online.target
[Service]
Type=simple
ExecStart=${WATCHDOG_BIN}
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=wg-watchdog
[Install]
WantedBy=multi-user.target
SYSTEMD
systemctl daemon-reload
systemctl enable wg-watchdog &>/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() { print_summary() {
local ARCH
ARCH="$(detect_arch)"
header "Установка завершена" header "Установка завершена"
echo -e "${BOLD}Режим:${NC} ${MODE}" echo -e "${BOLD}Режим:${NC} ${MODE} (Docker, ${ARCH})"
echo -e "${BOLD}Публичный IP VPS:${NC} ${VPS_PUBLIC_IP}" echo -e "${BOLD}Публичный IP VPS:${NC} ${VPS_PUBLIC_IP}"
echo -e "${BOLD}WireGuard IP:${NC} ${CLIENT_WG_IP} (клиент) ↔ ${SERVER_WG_IP} (сервер)" echo -e "${BOLD}WireGuard IP:${NC} ${CLIENT_WG_IP} (клиент) ↔ ${SERVER_WG_IP} (сервер)"
echo -e "${BOLD}Порт WG:${NC} ${WG_PORT}" echo -e "${BOLD}WG порт:${NC} ${WG_PORT}"
echo -e "${BOLD}Образ:${NC} ${BUILD_LOCAL:-pull} из ${REGISTRY}/${IMAGE_PREFIX}"
if [[ "$MODE" == "vps" ]]; then if [[ "$MODE" == "vps" ]]; then
echo -e "${BOLD}Проброс портов:${NC} ${PORTS}${CLIENT_WG_IP}" echo -e "${BOLD}Проброс портов:${NC} ${PORTS}${CLIENT_WG_IP}"
fi fi
echo "" echo ""
echo -e "${BOLD}Полезные команды:${NC}" echo -e "${BOLD}Полезные команды:${NC}"
echo " wg show — статус туннеля" if [[ "$MODE" == "vps" ]]; then
echo " systemctl status wg-quick@wg0 — статус сервиса" echo " docker compose -f docker-compose.server.yml logs -f — логи сервера"
echo " ping ${SERVER_WG_IP} — проверка туннеля" echo " docker compose -f docker-compose.server.yml ps — статус"
echo " docker exec wireguard-server wg show — статус туннеля"
if [[ "$MODE" == "client" ]]; then else
echo " systemctl status wg-watchdog — статус watchdog" echo " docker compose -f docker-compose.client.yml logs -f — логи клиента"
echo " tail -f /var/log/wg-watchdog.log — логи watchdog" echo " docker compose -f docker-compose.client.yml ps — статус"
echo " docker exec wireguard-client wg show — статус туннеля"
fi fi
echo "" echo ""
echo -e "${BOLD}Конфигурация:${NC} ${CONFIG_FILE}" echo -e "${BOLD}Конфигурация:${NC} ${SCRIPT_DIR}/.env"
echo -e "${BOLD}Ключи:${NC} ${CONFIG_DIR}/" echo -e "${BOLD}Ключи:${NC} ${CONFIG_DIR}/"
if [[ "$MODE" == "vps" ]]; then if [[ "$MODE" == "vps" ]]; then
@@ -468,11 +358,19 @@ print_summary() {
echo -e "${YELLOW}${BOLD}⚠️ ВАЖНО:${NC} ${YELLOW}Скопируйте публичный ключ сервера на клиент:${NC}" echo -e "${YELLOW}${BOLD}⚠️ ВАЖНО:${NC} ${YELLOW}Скопируйте публичный ключ сервера на клиент:${NC}"
echo -e " ${BOLD}cat ${CONFIG_DIR}/server_public.key${NC}" echo -e " ${BOLD}cat ${CONFIG_DIR}/server_public.key${NC}"
echo "" 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}" 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 fi
echo "" echo ""
echo -e "${CYAN}Поддерживаемые архитектуры: linux/amd64 (Intel/AMD), linux/arm64 (RPi 4/5, Orange Pi)${NC}"
} }
# ── Главная функция ──────────────────────────────────────────────────────── # ── Главная функция ────────────────────────────────────────────────────────
@@ -480,15 +378,13 @@ main() {
banner banner
check_root check_root
parse_args "$@" parse_args "$@"
detect_os
load_config
if [[ "$INTERACTIVE" == true ]] && [[ -z "$MODE" ]]; then if [[ "$INTERACTIVE" == true ]] && [[ -z "$MODE" ]]; then
interactive_setup interactive_setup
fi fi
if [[ -z "$MODE" ]]; then if [[ -z "$MODE" ]]; then
error "Не указан режим. Используйте --vps или --client, либо интерактивный режим." error "Не указан режим. Используйте --vps или --client."
exit 1 exit 1
fi fi
@@ -497,9 +393,8 @@ main() {
exit 1 exit 1
fi fi
# Автоопределение публичного IP для VPS если не указан
if [[ "$MODE" == "vps" ]] && [[ -z "$VPS_PUBLIC_IP" ]]; then 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 if [[ -z "$VPS_PUBLIC_IP" ]]; then
error "Не удалось определить публичный IP. Укажите вручную: --vps-ip IP" error "Не удалось определить публичный IP. Укажите вручную: --vps-ip IP"
exit 1 exit 1
@@ -507,24 +402,11 @@ main() {
info "Автоопределён публичный IP: ${VPS_PUBLIC_IP}" info "Автоопределён публичный IP: ${VPS_PUBLIC_IP}"
fi fi
install_deps install_docker
enable_ip_forward
generate_keys generate_keys
create_env_file
if [[ "$MODE" == "vps" ]]; then start_docker
configure_vps
else
configure_client
fi
start_wireguard
save_config
if [[ "$MODE" == "client" ]]; then
install_watchdog
fi
print_summary print_summary
} }
main "$@" main "$@"

38
server/Dockerfile Normal file
View File

@@ -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"]

136
server/entrypoint.sh Normal file
View File

@@ -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" <<WGCONF
# WireGuard VPS Tunnel — конфигурация сервера (VPS)
# Создано: $(date '+%Y-%m-%d %H:%M:%S')
[Interface]
PrivateKey = ${SERVER_PRIVATE_KEY}
Address = ${SERVER_WG_IP}/24
ListenPort = ${SERVER_WG_PORT}
PostUp = ${dnat_rules} iptables -t nat -A POSTROUTING -d ${client_wg_ip} -j MASQUERADE; iptables -I FORWARD 1 -i ${SERVER_PUBLIC_IFACE} -o wg0 -p tcp -m multiport --dports ${FORWARD_PORTS} -j ACCEPT; iptables -I FORWARD 2 -i wg0 -o ${SERVER_PUBLIC_IFACE} -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = ${dnat_down_rules} iptables -t nat -D POSTROUTING -d ${client_wg_ip} -j MASQUERADE; iptables -D FORWARD -i ${SERVER_PUBLIC_IFACE} -o wg0 -p tcp -m multiport --dports ${FORWARD_PORTS} -j ACCEPT; iptables -D FORWARD -i wg0 -o ${SERVER_PUBLIC_IFACE} -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
[Peer]
# Клиент (домашний сервер / Raspberry Pi)
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = ${client_wg_ip}/32
WGCONF
chmod 600 "$WG_CONF"
log "INFO" "Конфигурация WireGuard создана: ${WG_CONF}"
}
# ── Запуск WireGuard ───────────────────────────────────────────────────────
start_wireguard() {
log "INFO" "Запуск WireGuard интерфейса wg0..."
# Включаем IP-форвардинг
echo 1 > /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

11
server/healthcheck.sh Normal file
View File

@@ -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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ============================================================================= # =============================================================================
# WireGuard VPS Tunnel — Деинсталлятор # WireGuard VPS Tunnel — Деинсталлятор (Docker)
# Полное удаление туннеля, сервисов и правил iptables # Останавливает и удаляет Docker-контейнеры, образы и конфигурацию.
# ============================================================================= # =============================================================================
set -euo pipefail set -euo pipefail
@@ -17,31 +17,29 @@ success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; }
CONFIG_DIR="/etc/wireguard-vps-tunnel" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="$CONFIG_DIR/config" REMOVE_IMAGES=false
WG_CONF="/etc/wireguard/wg0.conf" REMOVE_CONFIG=false
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
usage() { usage() {
echo "Использование: $0 [ОПЦИИ]" echo "Использование: $0 [ОПЦИИ]"
echo "" echo ""
echo "Опции:" echo "Опции:"
echo " --remove-package Также удалить пакет wireguard-tools" echo " --remove-images Также удалить Docker-образы"
echo " --remove-config Также удалить директорию config/ с ключами"
echo " --help, -h Показать эту справку" echo " --help, -h Показать эту справку"
echo "" echo ""
echo "Пример:" echo "Пример:"
echo " sudo $0" echo " sudo $0"
echo " sudo $0 --remove-package" echo " sudo $0 --remove-images --remove-config"
exit 0 exit 0
} }
parse_args() { parse_args() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--remove-package) REMOVE_PACKAGE=true; shift ;; --remove-images) REMOVE_IMAGES=true; shift ;;
--remove-config) REMOVE_CONFIG=true; shift ;;
--help|-h) usage ;; --help|-h) usage ;;
*) error "Неизвестный аргумент: $1"; usage ;; *) error "Неизвестный аргумент: $1"; usage ;;
esac esac
@@ -55,168 +53,70 @@ check_root() {
fi fi
} }
stop_services() { stop_containers() {
echo "" echo ""
info "Остановка сервисов..." info "Остановка Docker-контейнеров..."
# Останавливаем watchdog if docker ps -a --format '{{.Names}}' | grep -q "wireguard-server"; then
if systemctl is-active --quiet wg-watchdog 2>/dev/null; then docker compose -f "$SCRIPT_DIR/docker-compose.server.yml" down 2>/dev/null || true
systemctl stop wg-watchdog success "Контейнер wireguard-server остановлен и удалён"
success "Watchdog остановлен"
else else
info "Watchdog не запущен" info "Контейнер wireguard-server не найден"
fi fi
# Отключаем watchdog if docker ps -a --format '{{.Names}}' | grep -q "wireguard-client"; then
if systemctl is-enabled --quiet wg-watchdog 2>/dev/null; then docker compose -f "$SCRIPT_DIR/docker-compose.client.yml" down 2>/dev/null || true
systemctl disable wg-watchdog &>/dev/null success "Контейнер wireguard-client остановлен и удалён"
success "Watchdog отключён из автозагрузки"
fi
# Останавливаем WireGuard
if systemctl is-active --quiet wg-quick@wg0 2>/dev/null; then
systemctl stop wg-quick@wg0
success "WireGuard остановлен"
else else
info "WireGuard не запущен" info "Контейнер wireguard-client не найден"
fi
# Отключаем WireGuard
if systemctl is-enabled --quiet wg-quick@wg0 2>/dev/null; then
systemctl disable wg-quick@wg0 &>/dev/null
success "WireGuard отключён из автозагрузки"
fi fi
} }
remove_interface() { remove_images() {
info "Удаление интерфейса wg0..." if [[ "$REMOVE_IMAGES" != true ]]; then
return 0
fi
if ip link show wg0 &>/dev/null; then info "Удаление Docker-образов..."
ip link delete wg0 2>/dev/null || true
success "Интерфейс wg0 удалён" if docker images --format '{{.Repository}}' | grep -q "wireguard-vps-tunnel-server"; then
else docker rmi wireguard-vps-tunnel-server 2>/dev/null || true
info "Интерфейс wg0 не существует" 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 fi
} }
remove_iptables_rules() { remove_config() {
info "Очистка правил iptables..." if [[ "$REMOVE_CONFIG" != true ]]; then
return 0
# Удаляем правила 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"
fi fi
# Удаляем watchdog if [[ -d "$SCRIPT_DIR/config" ]]; then
if [[ -f "$WATCHDOG_BIN" ]]; then rm -rf "$SCRIPT_DIR/config"
rm -f "$WATCHDOG_BIN" success "Директория config/ удалена"
success "Удалён: $WATCHDOG_BIN"
fi fi
# Удаляем systemd-сервис watchdog if [[ -f "$SCRIPT_DIR/.env" ]]; then
if [[ -f "$WATCHDOG_SERVICE" ]]; then rm -f "$SCRIPT_DIR/.env"
rm -f "$WATCHDOG_SERVICE" success "Файл .env удалён"
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
fi fi
} }
main() { main() {
echo -e "${RED}${BOLD}" echo -e "${RED}${BOLD}"
echo "╔══════════════════════════════════════════════════════════╗" echo "╔══════════════════════════════════════════════════════════╗"
echo "║ WireGuard VPS Tunnel — ДЕИНСТАЛЛЯТОР ║" echo "║ WireGuard VPS Tunnel — ДЕИНСТАЛЛЯТОР (Docker) ║"
echo "╚══════════════════════════════════════════════════════════╝" echo "╚══════════════════════════════════════════════════════════╝"
echo -e "${NC}" echo -e "${NC}"
check_root check_root
parse_args "$@" parse_args "$@"
warn "Этот скрипт полностью удалит WireGuard VPS Tunnel." warn "Этот скрипт остановит и удалит Docker-контейнеры WireGuard VPS Tunnel."
warn "Будут удалены: туннель, сервисы, правила iptables, ключи."
echo "" echo ""
read -r -p "Продолжить? [y/N]: " confirm read -r -p "Продолжить? [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
@@ -224,17 +124,16 @@ main() {
exit 0 exit 0
fi fi
stop_services stop_containers
remove_interface remove_images
remove_iptables_rules remove_config
remove_files
remove_package
echo "" echo ""
success "WireGuard VPS Tunnel полностью удалён." success "WireGuard VPS Tunnel полностью удалён."
echo "" echo ""
echo -e "${YELLOW}${BOLD}Примечание:${NC} ${YELLOW}SSH-сервер и другие сервисы не были затронуты.${NC}" echo -e "${YELLOW}${BOLD}Примечание:${NC} ${YELLOW}SSH-сервер и другие сервисы не были затронуты.${NC}"
echo -e "${YELLOW}PasswordAuthentication в sshd_config НЕ изменялся.${NC}" echo -e "${YELLOW}PasswordAuthentication в sshd_config НЕ изменялся.${NC}"
echo -e "${YELLOW}Docker и wireguard-tools (на хосте) не удалялись.${NC}"
} }
main "$@" main "$@"