Deploy Bot 1c4a40f0c1
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: add deployment hardening from production experience
- Add IP forwarding setup (sysctl) to install.sh — container can't
  write /proc/sys, must be done on host
- Add host-level WireGuard detection and auto-stop in install.sh
- Update key exchange instructions: 2-step process (server first,
  then client, then restart server with client key)
- Add deployment nuances section to README (ip_forward, wg0 cleanup,
  compose version warning, key exchange, network_mode, iptables order)
- Add production deployment notes to .env.example
- Remove obsolete compose version warnings noted during deployment
2026-07-30 01:05:13 +01:00

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 команда)

На VPS:

git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git
cd wireguard-vps-tunnel
sudo ./install.sh --vps --ports 80,443

На домашнем сервере (Raspberry Pi / Orange Pi):

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. Клонирование репозитория

git clone http://192.168.2.28:3000/OpenDoor/wireguard-vps-tunnel.git
cd wireguard-vps-tunnel

2. Генерация ключей

# Установите 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 файл:

CLIENT_PUBLIC_KEY=<ключ из config/client_public.key>
SERVER_PUBLIC_IP=2.59.219.234
FORWARD_PORTS=80,443

Запустите:

sudo docker compose -f docker-compose.server.yml up -d

4. Настройка клиента (домашний сервер)

Создайте .env файл:

VPS_PUBLIC_IP=2.59.219.234
SERVER_PUBLIC_KEY=<ключ из config/server_public.key>
PERSISTENT_KEEPALIVE=21

Запустите:

sudo docker compose -f docker-compose.client.yml up -d

5. Проверка

# На 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 — генерация ключей и запуск

sudo ./install.sh --vps --ports 80,443

Скрипт выведет публичный ключ сервера. Скопируйте его.

Шаг 2: На клиенте — запуск с ключом сервера

# Отредактируйте .env, вставив SERVER_PUBLIC_KEY из шага 1
sudo ./install.sh --client --vps-ip 2.59.219.234

Скрипт выведет публичный ключ клиента. Скопируйте его.

Шаг 3: На VPS — добавление ключа клиента

# Отредактируйте .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

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 успешных проверок подряд сбрасывает отсрочку

Просмотр логов:

docker compose -f docker-compose.client.yml logs -f

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

Установщик автоматически определяет архитектуру хоста и загружает соответствующий образ. Принудительная локальная сборка:

# Собрать образ локально (для текущей архитектуры)
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)

Устранение неполадок

Контейнер не запускается

# Проверить логи
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

Туннель не поднимается

# Проверить статус WireGuard внутри контейнера
docker exec wireguard-server wg show
docker exec wireguard-client wg show

# Проверить, что порт открыт на VPS
nc -zvu <VPS_IP> 51820

Порты не пробрасываются

# Проверить правила 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

Пинг не проходит

# Проверить что интерфейс поднят
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

Полный сброс и переустановка

# Деинсталляция
sudo ./uninstall.sh --remove-images --remove-config

# Переустановка
sudo ./install.sh --vps --ports 80,443

Деинсталляция

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). Включите на хосте:

# Временно
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 — остановите его:

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

Description
WireGuard VPN туннель между VPS и домашним сервером (Starlink-оптимизированный). Установка в 1 клик.
Readme 115 KiB
2026-07-29 19:39:08 +00:00
Languages
Shell 97.4%
Dockerfile 2.6%