Files
wireguard-vps-tunnel/README.md
Deploy Bot 9e59e4c5a1
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
feat: 1-command deploy.sh with automated key exchange
- deploy.sh: generates all 4 WG keys locally, SCPs project to both
  machines, starts containers, verifies tunnel — zero manual steps
- deploy.sh: supports --vps-pass and --client-pass for password auth
- deploy.sh: auto-detects VPS public IP and network interface
- deploy.sh: stops host-level WG, enables IP forwarding, health checks
- deploy.sh: idempotent, --force flag for clean redeploy
- install.sh: added --server-key and --client-key args for non-interactive
- README.md: added 1-Command Deployment section at the top
2026-07-30 01:31:37 +01:00

561 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WireGuard VPS Tunnel — Docker Edition
Автоматическая настройка WireGuard-туннеля в Docker-контейнерах между VPS (с публичным IP) и домашним сервером за NAT (Raspberry Pi, Orange Pi, Starlink и т.д.).
**Поддерживаемые архитектуры:** `linux/amd64` (Intel/AMD VPS) и `linux/arm64` (Raspberry Pi 4/5, Orange Pi).
## Архитектура
```
Интернет
┌────────┴────────┐
│ VPS (сервер) │
│ Публичный IP │
│ например: │
│ 2.59.219.234 │
│ │
│ ┌──────────────┐ │
│ │ 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) ║
╚══════════════╤══════════════╝
┌────────┴────────┐
│ Домашний сервер │
│ (за NAT/CGNAT) │
│ Raspberry Pi │
│ Starlink │
│ │
│ ┌──────────────┐│
│ │ Docker ││
│ │ wireguard- ││
│ │ client ││
│ │ ││
│ │ wg0:10.0.0.2 ││
│ │ ││
│ │ Watchdog ││
│ │ (встроенный) ││
│ └──────┬───────┘│
└────────┼────────┘
┌────────┴────────┐
│ Nginx Proxy │
│ Manager :80,443│
│ → backend │
└─────────────────┘
```
**Как это работает:**
1. VPS принимает входящие соединения на порты 80 и 443
2. iptables (внутри Docker-контейнера) перенаправляет трафик через WireGuard-туннель на домашний сервер (10.0.0.2)
3. Домашний сервер (RPi) инициирует соединение с VPS — работает через любой NAT
4. `PersistentKeepalive = 21` секунда поддерживает туннель активным даже при отсутствии трафика
5. Встроенный Watchdog на клиенте мониторит состояние туннеля и перезапускает при обрыве
6. Всё изолировано в Docker-контейнерах — не затрагивает хост-систему
## 1-Command Deployment (deploy.sh)
Разверните туннель одной командой с любой машины, имеющей SSH-доступ к VPS и домашнему серверу. **Ключи генерируются локально, обмен происходит автоматически — никакого ручного копирования.**
```bash
# Клонируйте репозиторий на любую машину (не обязательно VPS или RPi)
git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git
cd wireguard-vps-tunnel
# Запустите одной командой
./deploy.sh --vps root@2.59.219.234 --client root@192.168.2.44 --ports 80,443
```
### С парольной аутентификацией SSH
```bash
./deploy.sh \
--vps root@2.59.219.234 --vps-pass wumN8inGSTiFk3Jcy5 \
--client root@192.168.2.44 --client-pass retrowest \
--ports 80,443
```
### Что делает deploy.sh
1. Генерирует все 4 ключа WireGuard локально (на машине, где запущен скрипт)
2. Определяет публичный IP и сетевой интерфейс VPS
3. Создаёт `.env` файлы для сервера и клиента с предзаполненными ключами
4. Копирует проект на обе машины в `/opt/wireguard-vps-tunnel/`
5. Останавливает host-level WireGuard (если запущен)
6. Включает IP-форвардинг на VPS
7. Запускает Docker-контейнеры на обеих машинах
8. Ждёт healthcheck и проверяет туннель (ping + HTTP)
### Опции deploy.sh
| Опция | По умолчанию | Описание |
|-------|-------------|----------|
| `--vps HOST` | **обязательно** | Адрес VPS (root@IP) |
| `--client HOST` | **обязательно** | Адрес клиента (root@IP) |
| `--vps-pass PASS` | — | Пароль SSH для VPS |
| `--client-pass PASS` | — | Пароль SSH для клиента |
| `--ports PORT,...` | `80,443` | Порты для проброса |
| `--wg-port PORT` | `51820` | Порт WireGuard |
| `--keepalive SEC` | `21` | PersistentKeepalive (сек) |
| `--force` | — | Остановить существующие контейнеры и переразвернуть |
### Требования для deploy.sh
- **На локальной машине:** `ssh`, `scp`, `wg` (или Docker для генерации ключей)
- **На VPS и клиенте:** Docker 20.10+, Docker Compose plugin
- **SSH-доступ:** ключевая или парольная аутентификация
- **Права root:** на обеих удалённых машинах
### Идемпотентность
`deploy.sh` безопасно запускать повторно:
- Существующие контейнеры перезапускаются с новыми ключами
- Старые ключи заменяются новыми
- IP-форвардинг настраивается повторно (без дублирования в sysctl.conf)
- Флаг `--force` принудительно останавливает контейнеры перед развёртыванием
## Быстрый старт (install.sh — пошагово)
### На VPS:
```bash
git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git
cd wireguard-vps-tunnel
sudo ./install.sh --vps --ports 80,443
```
### На домашнем сервере (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 http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git
cd wireguard-vps-tunnel
```
### 2. Генерация ключей
```bash
# Установите 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 (сервер)
Создайте `.env` файл:
```env
CLIENT_PUBLIC_KEY=<ключ из config/client_public.key>
SERVER_PUBLIC_IP=2.59.219.234
FORWARD_PORTS=80,443
```
Запустите:
```bash
sudo docker compose -f docker-compose.server.yml up -d
```
### 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
docker exec wireguard-server wg show
docker exec wireguard-server ping 10.0.0.2
# На домашнем сервере
docker exec wireguard-client wg show
docker exec wireguard-client ping 10.0.0.1
```
## Обмен ключами (пошагово)
### Шаг 1: На VPS — генерация ключей и запуск
```bash
sudo ./install.sh --vps --ports 80,443
```
Скрипт выведет публичный ключ сервера. **Скопируйте его.**
### Шаг 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 | автоопределение |
| `--ports PORT,...` | Порты для проброса (VPS) | `80,443` |
| `--wg-port PORT` | Порт WireGuard | `51820` |
| `--keepalive SEC` | PersistentKeepalive (сек) | `21` |
| `--build` | Локальная сборка образа (вместо загрузки из registry) | `pull` |
| `--server-key KEY` | Публичный ключ сервера (для неинтерактивной настройки клиента) | — |
| `--client-key KEY` | Публичный ключ клиента (для неинтерактивной настройки сервера) | — |
## Watchdog (мониторинг туннеля)
Watchdog встроен в клиентский контейнер и выполняет:
1. Проверяет, что интерфейс `wg0` поднят
2. Проверяет доступность VPS через туннель (`ping 10.0.0.1`)
3. Проверяет доступность VPS через интернет (`ping VPS_IP`)
4. Если туннель не работает, но VPS доступен — перезапускает WireGuard
5. Использует экспоненциальную отсрочку при повторных сбоях:
- 10с → 20с → 40с → 80с → 120с (максимум)
6. После 3 успешных проверок подряд сбрасывает отсрочку
**Просмотр логов:**
```bash
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 |
|----------|-----------|-------------------|
| Протокол | UDP (kernel) | TCP (userspace) |
| Производительность | ~1 Gbps | ~100 Mbps |
| Задержка | Минимальная | TCP-over-TCP проблема |
| Starlink | ✅ Оптимально (UDP) | ❌ Плохо (TCP через TCP) |
| Переподключение | Мгновенное | Заметная задержка |
| NAT traversal | PersistentKeepalive | Autossh + мониторинг |
| Проброс портов | iptables DNAT | `ssh -R` |
| Отказоустойчивость | Встроенная | Требует autossh |
| Нагрузка на CPU | Минимальная (kernel) | Заметная (шифрование в userspace) |
## Устранение неполадок
### Контейнер не запускается
```bash
# Проверить логи
docker compose -f docker-compose.server.yml logs
docker compose -f docker-compose.client.yml logs
# Проверить статус
docker compose -f docker-compose.server.yml ps
docker compose -f docker-compose.client.yml ps
```
### Туннель не поднимается
```bash
# Проверить статус WireGuard внутри контейнера
docker exec wireguard-server wg show
docker exec wireguard-client wg show
# Проверить, что порт открыт на VPS
nc -zvu <VPS_IP> 51820
```
### Порты не пробрасываются
```bash
# Проверить правила iptables внутри контейнера
docker exec wireguard-server iptables -t nat -L PREROUTING -n
docker exec wireguard-server iptables -L FORWARD -n
# Проверить IP-форвардинг
docker exec wireguard-server sysctl net.ipv4.ip_forward
```
### Пинг не проходит
```bash
# Проверить что интерфейс поднят
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 --remove-images --remove-config
# Переустановка
sudo ./install.sh --vps --ports 80,443
```
## Деинсталляция
```bash
sudo ./uninstall.sh
```
Опции:
- `--remove-images` — также удалить Docker-образы
- `--remove-config` — также удалить директорию `config/` с ключами и `.env`
Деинсталлятор:
- Останавливает и удаляет Docker-контейнеры
- Опционально удаляет образы и конфигурацию
- **НЕ трогает** SSH-сервер, Docker и другие сервисы
- **НЕ изменяет** `sshd_config` (PasswordAuthentication остаётся без изменений)
## Безопасность
- **Ключи**: приватные ключи хранятся с правами `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/
├── 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 # Документация
```
## Нюансы развёртывания (выявлены на практике)
### 1. IP-форвардинг на хосте (обязательно)
Контейнер не может записать в `/proc/sys/net/ipv4/ip_forward` (read-only в Docker). Включите на хосте:
```bash
# Временно
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
# Постоянно
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' >> /etc/sysctl.conf
sysctl -p
```
Без этого DNAT-трафик не будет перенаправляться.
### 2. Обмен ключами: двухэтапный запуск
Ключи генерируются **внутри контейнера** при первом запуске. Поэтому:
1. Запустите сервер → он сгенерирует ключи и выведет `SERVER_PUBLIC_KEY=...`
2. Впишите `SERVER_PUBLIC_KEY` в `.env` на клиенте
3. Запустите клиент → он выведет `CLIENT_PUBLIC_KEY=...`
4. Впишите `CLIENT_PUBLIC_KEY` в `.env` на сервере
5. Перезапустите сервер: `docker compose -f docker-compose.server.yml restart`
Конфигурация `wg0.conf` пересоздаётся при каждом перезапуске, поэтому обновление ключей не требует ручного редактирования.
### 3. Удаление старого интерфейса wg0
При перезапуске контейнера интерфейс `wg0` может остаться от предыдущего запуска. Entrypoint автоматически удаляет его перед запуском `wg-quick up`. Если на хосте остался host-level WireGuard — остановите его:
```bash
systemctl stop wg-quick@wg0
systemctl disable wg-quick@wg0
ip link del wg0 2>/dev/null
rm -f /etc/wireguard/wg0.conf
```
### 4. Docker Compose `version` устарел
Поле `version: "3.8"` в docker-compose вызывает предупреждение:
`the attribute 'version' is obsolete`. Оно удалено из файлов проекта.
Если вы видите это предупреждение — просто удалите строку `version:` из compose-файла.
### 5. Multi-arch сборка требует Buildx
Стандартный `docker build` не поддерживает `platforms: linux/amd64,linux/arm64`.
Для multi-arch сборки через CI/CD используется Docker Buildx с QEMU.
Для локальной сборки на текущей архитектуре достаточно убрать `platforms:` из compose-файла.
### 6. network_mode: host и порты
Контейнеры используют `network_mode: host`. Секция `ports:` в compose-файле игнорируется (Docker выведет предупреждение). Все порты доступны напрямую через iptables DNAT.
### 7. Конфликт с Docker iptables
Правила DNAT вставляются через `iptables -I FORWARD 1`**перед** цепочкой DOCKER-USER.
Это гарантирует, что WireGuard-трафик не блокируется Docker. Не меняйте приоритет
без необходимости.
### 8. Парольная аутентификация SSH
Скрипт **НЕ отключает** `PasswordAuthentication` в sshd. Если вы хотите отключить,
сделайте это вручную и обязательно добавьте SSH-ключ в `authorized_keys` **до**
отключения пароля, иначе потеряете доступ.
## Поддерживаемые ОС
- Любой 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