Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dc0238c66 | ||
|
|
9e59e4c5a1 | ||
|
|
1c4a40f0c1 | ||
|
|
dcebf21e03 |
16
.env.example
16
.env.example
@@ -43,4 +43,18 @@ VPS_WG_PORT=51820
|
|||||||
CLIENT_WG_IP=10.0.0.2
|
CLIENT_WG_IP=10.0.0.2
|
||||||
|
|
||||||
# Интервал Keepalive в секундах (оптимизировано для Starlink, по умолчанию 21)
|
# Интервал Keepalive в секундах (оптимизировано для Starlink, по умолчанию 21)
|
||||||
PERSISTENT_KEEPALIVE=21
|
PERSISTENT_KEEPALIVE=21
|
||||||
|
|
||||||
|
# ── Важные замечания по развёртыванию ──────────────────────────────────────
|
||||||
|
# 1. На VPS: включите IP-форвардинг на хосте (контейнер не может /proc/sys):
|
||||||
|
# echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf && sysctl -p
|
||||||
|
# 2. Ключи генерируются автоматически при первом запуске.
|
||||||
|
# Сервер: /etc/wireguard/server_private.key, server_public.key
|
||||||
|
# Клиент: /etc/wireguard/client_private.key, client_public.key
|
||||||
|
# 3. После первого запуска сервера скопируйте SERVER_PUBLIC_KEY в .env клиента.
|
||||||
|
# 4. После первого запуска клиента скопируйте CLIENT_PUBLIC_KEY в .env сервера.
|
||||||
|
# 5. Перезапустите контейнеры после обновления ключей:
|
||||||
|
# docker compose -f docker-compose.server.yml restart (на VPS)
|
||||||
|
# docker compose -f docker-compose.client.yml restart (на RPi)
|
||||||
|
# 6. Контейнер использует network_mode: host — порты пробрасываются
|
||||||
|
# через iptables DNAT, а не через Docker port mapping.
|
||||||
135
README.md
135
README.md
@@ -69,7 +69,68 @@
|
|||||||
5. Встроенный Watchdog на клиенте мониторит состояние туннеля и перезапускает при обрыве
|
5. Встроенный Watchdog на клиенте мониторит состояние туннеля и перезапускает при обрыве
|
||||||
6. Всё изолировано в Docker-контейнерах — не затрагивает хост-систему
|
6. Всё изолировано в Docker-контейнерах — не затрагивает хост-систему
|
||||||
|
|
||||||
## Быстрый старт (1 команда)
|
## 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:
|
### На VPS:
|
||||||
```bash
|
```bash
|
||||||
@@ -212,6 +273,8 @@ sudo docker compose -f docker-compose.server.yml up -d --force-recreate
|
|||||||
| `--wg-port PORT` | Порт WireGuard | `51820` |
|
| `--wg-port PORT` | Порт WireGuard | `51820` |
|
||||||
| `--keepalive SEC` | PersistentKeepalive (сек) | `21` |
|
| `--keepalive SEC` | PersistentKeepalive (сек) | `21` |
|
||||||
| `--build` | Локальная сборка образа (вместо загрузки из registry) | `pull` |
|
| `--build` | Локальная сборка образа (вместо загрузки из registry) | `pull` |
|
||||||
|
| `--server-key KEY` | Публичный ключ сервера (для неинтерактивной настройки клиента) | — |
|
||||||
|
| `--client-key KEY` | Публичный ключ клиента (для неинтерактивной настройки сервера) | — |
|
||||||
|
|
||||||
## Watchdog (мониторинг туннеля)
|
## Watchdog (мониторинг туннеля)
|
||||||
|
|
||||||
@@ -414,6 +477,76 @@ wireguard-vps-tunnel/
|
|||||||
└── README.md # Документация
|
└── 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+
|
- Любой Linux с Docker 20.10+
|
||||||
|
|||||||
@@ -48,9 +48,10 @@ generate_keys() {
|
|||||||
|
|
||||||
# ── Создание конфигурации WireGuard ────────────────────────────────────────
|
# ── Создание конфигурации WireGuard ────────────────────────────────────────
|
||||||
create_wg_config() {
|
create_wg_config() {
|
||||||
|
# Всегда пересоздаём конфиг — ключи или эндпоинт могли измениться
|
||||||
if [[ -f "$WG_CONF" ]]; then
|
if [[ -f "$WG_CONF" ]]; then
|
||||||
log "INFO" "Конфигурация ${WG_CONF} уже существует, пропускаем создание"
|
log "INFO" "Обновление конфигурации ${WG_CONF}..."
|
||||||
return 0
|
rm -f "$WG_CONF"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$(dirname "$WG_CONF")"
|
mkdir -p "$(dirname "$WG_CONF")"
|
||||||
@@ -78,6 +79,14 @@ WGCONF
|
|||||||
# ── Запуск WireGuard ───────────────────────────────────────────────────────
|
# ── Запуск WireGuard ───────────────────────────────────────────────────────
|
||||||
start_wireguard() {
|
start_wireguard() {
|
||||||
log "INFO" "Запуск WireGuard интерфейса wg0..."
|
log "INFO" "Запуск WireGuard интерфейса wg0..."
|
||||||
|
|
||||||
|
# Удаляем старый интерфейс если остался от предыдущего запуска
|
||||||
|
if ip link show wg0 &>/dev/null; then
|
||||||
|
log "INFO" "Удаление старого интерфейса wg0..."
|
||||||
|
ip link del wg0 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
wg-quick up wg0
|
wg-quick up wg0
|
||||||
log "INFO" "WireGuard интерфейс wg0 запущен"
|
log "INFO" "WireGuard интерфейс wg0 запущен"
|
||||||
log "INFO" "Публичный ключ клиента: ${CLIENT_PUBLIC_KEY}"
|
log "INFO" "Публичный ключ клиента: ${CLIENT_PUBLIC_KEY}"
|
||||||
|
|||||||
524
deploy.sh
Executable file
524
deploy.sh
Executable file
@@ -0,0 +1,524 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# WireGuard VPS Tunnel — 1-Command Deployer
|
||||||
|
# Deploys both server (VPS) and client (home server) from any machine with
|
||||||
|
# SSH access to both. Generates all WireGuard keys locally, pre-populates
|
||||||
|
# configs, and starts containers — no manual key exchange needed.
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Colors ──────────────────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||||
|
|
||||||
|
# ── Paths ───────────────────────────────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DEPLOY_TAR="/tmp/wg-vps-tunnel-deploy-$$.tar.gz"
|
||||||
|
|
||||||
|
# ── Defaults ────────────────────────────────────────────────────────────────
|
||||||
|
VPS_HOST=""
|
||||||
|
CLIENT_HOST=""
|
||||||
|
VPS_PASS=""
|
||||||
|
CLIENT_PASS=""
|
||||||
|
PORTS="80,443"
|
||||||
|
WG_PORT="51820"
|
||||||
|
KEEPALIVE="21"
|
||||||
|
FORCE=false
|
||||||
|
VPS_PUBLIC_IP=""
|
||||||
|
|
||||||
|
# ── Output ──────────────────────────────────────────────────────────────────
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
||||||
|
header() { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}\n"; }
|
||||||
|
|
||||||
|
# ── Banner ──────────────────────────────────────────────────────────────────
|
||||||
|
banner() {
|
||||||
|
echo -e "${CYAN}${BOLD}"
|
||||||
|
echo "╔══════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ WireGuard VPS Tunnel — 1-Command Deployer ║"
|
||||||
|
echo "║ Deploy server + client from any machine ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════════╝"
|
||||||
|
echo -e "${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Usage ───────────────────────────────────────────────────────────────────
|
||||||
|
usage() {
|
||||||
|
echo -e "${BOLD}Usage:${NC} $0 --vps HOST --client HOST [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Required:${NC}"
|
||||||
|
echo " --vps HOST VPS address (root@2.59.219.234)"
|
||||||
|
echo " --client HOST Client address (root@192.168.2.44)"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Optional:${NC}"
|
||||||
|
echo " --vps-pass PASS VPS SSH password"
|
||||||
|
echo " --client-pass PASS Client SSH password"
|
||||||
|
echo " --ports PORT,... Ports to forward (default: 80,443)"
|
||||||
|
echo " --wg-port PORT WireGuard port (default: 51820)"
|
||||||
|
echo " --keepalive SEC PersistentKeepalive seconds (default: 21)"
|
||||||
|
echo " --force Stop existing containers and redeploy"
|
||||||
|
echo " --help, -h Show this help"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Examples:${NC}"
|
||||||
|
echo " # Key-based SSH auth"
|
||||||
|
echo " $0 --vps root@2.59.219.234 --client root@192.168.2.44 --ports 80,443"
|
||||||
|
echo ""
|
||||||
|
echo " # Password-based SSH auth"
|
||||||
|
echo " $0 --vps root@2.59.219.234 --vps-pass wumN8inGSTiFk3Jcy5 \\"
|
||||||
|
echo " --client root@192.168.2.44 --client-pass retrowest --ports 80,443"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Parse arguments ─────────────────────────────────────────────────────────
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--vps) VPS_HOST="$2"; shift 2 ;;
|
||||||
|
--client) CLIENT_HOST="$2"; shift 2 ;;
|
||||||
|
--vps-pass) VPS_PASS="$2"; shift 2 ;;
|
||||||
|
--client-pass) CLIENT_PASS="$2"; shift 2 ;;
|
||||||
|
--ports) PORTS="$2"; shift 2 ;;
|
||||||
|
--wg-port) WG_PORT="$2"; shift 2 ;;
|
||||||
|
--keepalive) KEEPALIVE="$2"; shift 2 ;;
|
||||||
|
--force) FORCE=true; shift ;;
|
||||||
|
--help|-h) usage ;;
|
||||||
|
*)
|
||||||
|
error "Unknown argument: $1"
|
||||||
|
echo "Use --help for usage information."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Validate arguments ──────────────────────────────────────────────────────
|
||||||
|
validate_args() {
|
||||||
|
if [[ -z "$VPS_HOST" ]]; then
|
||||||
|
error "--vps is required (e.g. root@2.59.219.234)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "$CLIENT_HOST" ]]; then
|
||||||
|
error "--client is required (e.g. root@192.168.2.44)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── SSH helpers ─────────────────────────────────────────────────────────────
|
||||||
|
ssh_remote() {
|
||||||
|
local host="$1" pass="$2" cmd="$3"
|
||||||
|
if [[ -n "$pass" ]]; then
|
||||||
|
sshpass -p "$pass" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o BatchMode=no "$host" "$cmd"
|
||||||
|
else
|
||||||
|
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$host" "$cmd"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
scp_remote() {
|
||||||
|
local host="$1" pass="$2" src="$3" dst="$4"
|
||||||
|
if [[ -n "$pass" ]]; then
|
||||||
|
sshpass -p "$pass" scp -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$src" "${host}:${dst}"
|
||||||
|
else
|
||||||
|
scp -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$src" "${host}:${dst}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Check dependencies ──────────────────────────────────────────────────────
|
||||||
|
check_deps() {
|
||||||
|
header "Checking dependencies"
|
||||||
|
|
||||||
|
local missing=()
|
||||||
|
|
||||||
|
if ! command -v ssh &>/dev/null; then
|
||||||
|
missing+=("ssh")
|
||||||
|
fi
|
||||||
|
if ! command -v scp &>/dev/null; then
|
||||||
|
missing+=("scp")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$VPS_PASS" ]] || [[ -n "$CLIENT_PASS" ]]; then
|
||||||
|
if ! command -v sshpass &>/dev/null; then
|
||||||
|
warn "sshpass not found. Attempting to install..."
|
||||||
|
if command -v apt-get &>/dev/null; then
|
||||||
|
apt-get update -qq && apt-get install -y -qq sshpass 2>/dev/null || missing+=("sshpass")
|
||||||
|
elif command -v yum &>/dev/null; then
|
||||||
|
yum install -y sshpass 2>/dev/null || missing+=("sshpass")
|
||||||
|
else
|
||||||
|
missing+=("sshpass")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||||
|
error "Missing dependencies: ${missing[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "All dependencies available"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Generate WireGuard keys ─────────────────────────────────────────────────
|
||||||
|
generate_all_keys() {
|
||||||
|
header "Generating WireGuard keys"
|
||||||
|
|
||||||
|
if command -v wg &>/dev/null; then
|
||||||
|
info "Using host wireguard-tools"
|
||||||
|
SERVER_PRIVATE_KEY=$(wg genkey)
|
||||||
|
SERVER_PUBLIC_KEY=$(echo "$SERVER_PRIVATE_KEY" | wg pubkey)
|
||||||
|
CLIENT_PRIVATE_KEY=$(wg genkey)
|
||||||
|
CLIENT_PUBLIC_KEY=$(echo "$CLIENT_PRIVATE_KEY" | wg pubkey)
|
||||||
|
else
|
||||||
|
info "wireguard-tools not found on host, using Docker fallback..."
|
||||||
|
local keys
|
||||||
|
keys=$(docker run --rm alpine:3.20 sh -c "
|
||||||
|
apk add --no-cache wireguard-tools >/dev/null 2>&1
|
||||||
|
spk=\$(wg genkey); echo \"SERVER_PRIV=\$spk\"; echo \"SERVER_PUB=\$(echo \$spk | wg pubkey)\"
|
||||||
|
cpk=\$(wg genkey); echo \"CLIENT_PRIV=\$cpk\"; echo \"CLIENT_PUB=\$(echo \$cpk | wg pubkey)\"
|
||||||
|
" 2>/dev/null) || {
|
||||||
|
error "Docker key generation failed. Install wireguard-tools: apt-get install wireguard-tools"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
SERVER_PRIVATE_KEY=$(echo "$keys" | grep '^SERVER_PRIV=' | cut -d= -f2-)
|
||||||
|
SERVER_PUBLIC_KEY=$(echo "$keys" | grep '^SERVER_PUB=' | cut -d= -f2-)
|
||||||
|
CLIENT_PRIVATE_KEY=$(echo "$keys" | grep '^CLIENT_PRIV=' | cut -d= -f2-)
|
||||||
|
CLIENT_PUBLIC_KEY=$(echo "$keys" | grep '^CLIENT_PUB=' | cut -d= -f2-)
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Server key: ${SERVER_PUBLIC_KEY:0:12}..."
|
||||||
|
success "Client key: ${CLIENT_PUBLIC_KEY:0:12}..."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Detect VPS info ────────────────────────────────────────────────────────
|
||||||
|
detect_vps_info() {
|
||||||
|
header "Detecting VPS network info"
|
||||||
|
|
||||||
|
if [[ -z "$VPS_PUBLIC_IP" ]]; then
|
||||||
|
info "Detecting VPS public IP..."
|
||||||
|
VPS_PUBLIC_IP=$(ssh_remote "$VPS_HOST" "$VPS_PASS" \
|
||||||
|
"curl -4 -s --connect-timeout 5 ifconfig.me 2>/dev/null || curl -4 -s --connect-timeout 5 icanhazip.com 2>/dev/null || echo ''")
|
||||||
|
if [[ -z "$VPS_PUBLIC_IP" ]]; then
|
||||||
|
error "Could not detect VPS public IP. Provide it via VPS_PUBLIC_IP env or check connectivity."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
success "VPS public IP: ${VPS_PUBLIC_IP}"
|
||||||
|
|
||||||
|
info "Detecting VPS public interface..."
|
||||||
|
VPS_IFACE=$(ssh_remote "$VPS_HOST" "$VPS_PASS" \
|
||||||
|
"ip route get 8.8.8.8 2>/dev/null | awk '{print \$5; exit}'")
|
||||||
|
if [[ -z "$VPS_IFACE" ]]; then
|
||||||
|
warn "Could not detect VPS interface. Container will auto-detect."
|
||||||
|
VPS_IFACE=""
|
||||||
|
else
|
||||||
|
success "VPS public interface: ${VPS_IFACE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Create deployment archive ────────────────────────────────────────────────
|
||||||
|
create_archive() {
|
||||||
|
header "Creating deployment archive"
|
||||||
|
tar czf "$DEPLOY_TAR" \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='deploy.sh' \
|
||||||
|
--exclude='config' \
|
||||||
|
--exclude='.env' \
|
||||||
|
-C "$SCRIPT_DIR" .
|
||||||
|
success "Archive created: ${DEPLOY_TAR} ($(du -h "$DEPLOY_TAR" | cut -f1))"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Deploy to remote host ───────────────────────────────────────────────────
|
||||||
|
deploy_to_host() {
|
||||||
|
local host="$1" pass="$2" role="$3"
|
||||||
|
local target="/opt/wireguard-vps-tunnel"
|
||||||
|
|
||||||
|
header "Deploying to ${role}: ${host}"
|
||||||
|
|
||||||
|
info "Copying archive..."
|
||||||
|
scp_remote "$host" "$pass" "$DEPLOY_TAR" "/tmp/wg-deploy.tar.gz"
|
||||||
|
|
||||||
|
info "Extracting to ${target}..."
|
||||||
|
ssh_remote "$host" "$pass" "mkdir -p ${target} && rm -rf ${target}/* 2>/dev/null; tar xzf /tmp/wg-deploy.tar.gz -C ${target} && rm -f /tmp/wg-deploy.tar.gz"
|
||||||
|
|
||||||
|
info "Creating config directory with keys..."
|
||||||
|
ssh_remote "$host" "$pass" "
|
||||||
|
mkdir -p ${target}/config
|
||||||
|
echo '${SERVER_PRIVATE_KEY}' > ${target}/config/server_private.key
|
||||||
|
echo '${SERVER_PUBLIC_KEY}' > ${target}/config/server_public.key
|
||||||
|
echo '${CLIENT_PRIVATE_KEY}' > ${target}/config/client_private.key
|
||||||
|
echo '${CLIENT_PUBLIC_KEY}' > ${target}/config/client_public.key
|
||||||
|
chmod 600 ${target}/config/*.key
|
||||||
|
"
|
||||||
|
|
||||||
|
if [[ "$role" == "vps" ]]; then
|
||||||
|
info "Creating server .env..."
|
||||||
|
ssh_remote "$host" "$pass" "
|
||||||
|
cat > ${target}/.env << 'DOTENV'
|
||||||
|
# WireGuard VPS Tunnel — Server (VPS)
|
||||||
|
CLIENT_PUBLIC_KEY=${CLIENT_PUBLIC_KEY}
|
||||||
|
SERVER_PUBLIC_IP=${VPS_PUBLIC_IP}
|
||||||
|
SERVER_PUBLIC_IFACE=${VPS_IFACE}
|
||||||
|
FORWARD_PORTS=${PORTS}
|
||||||
|
SERVER_WG_PORT=${WG_PORT}
|
||||||
|
DOTENV
|
||||||
|
chmod 600 ${target}/.env
|
||||||
|
"
|
||||||
|
else
|
||||||
|
info "Creating client .env..."
|
||||||
|
ssh_remote "$host" "$pass" "
|
||||||
|
cat > ${target}/.env << 'DOTENV'
|
||||||
|
# WireGuard VPS Tunnel — Client (home server)
|
||||||
|
VPS_PUBLIC_IP=${VPS_PUBLIC_IP}
|
||||||
|
SERVER_PUBLIC_KEY=${SERVER_PUBLIC_KEY}
|
||||||
|
PERSISTENT_KEEPALIVE=${KEEPALIVE}
|
||||||
|
VPS_WG_PORT=${WG_PORT}
|
||||||
|
DOTENV
|
||||||
|
chmod 600 ${target}/.env
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Deployed to ${role}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Stop host-level WireGuard ───────────────────────────────────────────────
|
||||||
|
stop_host_wg() {
|
||||||
|
local host="$1" pass="$2"
|
||||||
|
|
||||||
|
ssh_remote "$host" "$pass" "
|
||||||
|
if systemctl is-active --quiet wg-quick@wg0 2>/dev/null; then
|
||||||
|
echo '[WARN] Stopping host-level wg-quick@wg0...'
|
||||||
|
systemctl stop wg-quick@wg0 2>/dev/null || true
|
||||||
|
systemctl disable wg-quick@wg0 2>/dev/null || true
|
||||||
|
ip link del wg0 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if systemctl is-active --quiet wg-watchdog 2>/dev/null; then
|
||||||
|
systemctl stop wg-watchdog 2>/dev/null || true
|
||||||
|
systemctl disable wg-watchdog 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Enable IP forwarding on VPS ─────────────────────────────────────────────
|
||||||
|
enable_ip_forwarding() {
|
||||||
|
header "Enabling IP forwarding on VPS"
|
||||||
|
|
||||||
|
ssh_remote "$VPS_HOST" "$VPS_PASS" "
|
||||||
|
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
|
||||||
|
|
||||||
|
if ! grep -q 'net.ipv4.ip_forward' /etc/sysctl.conf 2>/dev/null; then
|
||||||
|
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
|
||||||
|
else
|
||||||
|
sed -i 's/.*net.ipv4.ip_forward.*/net.ipv4.ip_forward = 1/' /etc/sysctl.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -q 'net.ipv6.conf.all.forwarding' /etc/sysctl.conf 2>/dev/null; then
|
||||||
|
echo 'net.ipv6.conf.all.forwarding = 1' >> /etc/sysctl.conf
|
||||||
|
else
|
||||||
|
sed -i 's/.*net.ipv6.conf.all.forwarding.*/net.ipv6.conf.all.forwarding = 1/' /etc/sysctl.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
sysctl -p 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
success "IP forwarding enabled on VPS"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Start container ─────────────────────────────────────────────────────────
|
||||||
|
start_container() {
|
||||||
|
local host="$1" pass="$2" role="$3"
|
||||||
|
local target="/opt/wireguard-vps-tunnel"
|
||||||
|
local compose_file
|
||||||
|
|
||||||
|
if [[ "$role" == "vps" ]]; then
|
||||||
|
compose_file="docker-compose.server.yml"
|
||||||
|
else
|
||||||
|
compose_file="docker-compose.client.yml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
header "Starting ${role} container: ${host}"
|
||||||
|
|
||||||
|
# Remove stale wg0 interface (leftover from previous container with host network)
|
||||||
|
ssh_remote "$host" "$pass" "ip link del wg0 2>/dev/null || true"
|
||||||
|
|
||||||
|
ssh_remote "$host" "$pass" "
|
||||||
|
cd ${target}
|
||||||
|
docker compose -f ${compose_file} up -d --remove-orphans 2>&1
|
||||||
|
"
|
||||||
|
|
||||||
|
success "${role} container started"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Force stop existing containers ──────────────────────────────────────────
|
||||||
|
force_stop() {
|
||||||
|
header "Force-stopping existing containers"
|
||||||
|
|
||||||
|
info "Stopping VPS container and cleaning up..."
|
||||||
|
ssh_remote "$VPS_HOST" "$VPS_PASS" "
|
||||||
|
cd /opt/wireguard-vps-tunnel 2>/dev/null || exit 0
|
||||||
|
docker compose -f docker-compose.server.yml down --remove-orphans 2>/dev/null || true
|
||||||
|
ip link del wg0 2>/dev/null || true
|
||||||
|
" || true
|
||||||
|
|
||||||
|
info "Stopping client container and cleaning up..."
|
||||||
|
ssh_remote "$CLIENT_HOST" "$CLIENT_PASS" "
|
||||||
|
cd /opt/wireguard-vps-tunnel 2>/dev/null || exit 0
|
||||||
|
docker compose -f docker-compose.client.yml down --remove-orphans 2>/dev/null || true
|
||||||
|
ip link del wg0 2>/dev/null || true
|
||||||
|
" || true
|
||||||
|
|
||||||
|
success "Existing containers stopped and interfaces cleaned"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Wait for healthy containers ─────────────────────────────────────────────
|
||||||
|
wait_for_healthy() {
|
||||||
|
header "Waiting for containers to be healthy (max 60s)"
|
||||||
|
|
||||||
|
local vps_healthy=false
|
||||||
|
local client_healthy=false
|
||||||
|
|
||||||
|
for i in $(seq 1 12); do
|
||||||
|
if [[ "$vps_healthy" != true ]]; then
|
||||||
|
local vps_status
|
||||||
|
vps_status=$(ssh_remote "$VPS_HOST" "$VPS_PASS" \
|
||||||
|
"docker inspect wireguard-server --format='{{.State.Health.Status}}' 2>/dev/null || echo 'missing'")
|
||||||
|
if [[ "$vps_status" == "healthy" ]]; then
|
||||||
|
success "VPS container healthy"
|
||||||
|
vps_healthy=true
|
||||||
|
elif [[ "$vps_status" == "missing" ]]; then
|
||||||
|
warn "VPS container not found yet..."
|
||||||
|
else
|
||||||
|
info "VPS container status: ${vps_status} (${i}/12)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$client_healthy" != true ]]; then
|
||||||
|
local client_status
|
||||||
|
client_status=$(ssh_remote "$CLIENT_HOST" "$CLIENT_PASS" \
|
||||||
|
"docker inspect wireguard-client --format='{{.State.Health.Status}}' 2>/dev/null || echo 'missing'")
|
||||||
|
if [[ "$client_status" == "healthy" ]]; then
|
||||||
|
success "Client container healthy"
|
||||||
|
client_healthy=true
|
||||||
|
elif [[ "$client_status" == "missing" ]]; then
|
||||||
|
warn "Client container not found yet..."
|
||||||
|
else
|
||||||
|
info "Client container status: ${client_status} (${i}/12)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$vps_healthy" == true ]] && [[ "$client_healthy" == true ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$vps_healthy" != true ]]; then
|
||||||
|
warn "VPS container may not be fully healthy. Check logs: docker compose -f docker-compose.server.yml logs"
|
||||||
|
fi
|
||||||
|
if [[ "$client_healthy" != true ]]; then
|
||||||
|
warn "Client container may not be fully healthy. Check logs: docker compose -f docker-compose.client.yml logs"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Verify tunnel ───────────────────────────────────────────────────────────
|
||||||
|
verify_tunnel() {
|
||||||
|
header "Verifying tunnel"
|
||||||
|
|
||||||
|
info "Ping from VPS (10.0.0.1) to client (10.0.0.2)..."
|
||||||
|
if ssh_remote "$VPS_HOST" "$VPS_PASS" "ping -c 3 -W 2 10.0.0.2" 2>/dev/null; then
|
||||||
|
success "VPS → client ping OK"
|
||||||
|
else
|
||||||
|
warn "VPS → client ping failed. Tunnel may need more time to establish."
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Ping from client (10.0.0.2) to VPS (10.0.0.1)..."
|
||||||
|
if ssh_remote "$CLIENT_HOST" "$CLIENT_PASS" "ping -c 3 -W 2 10.0.0.1" 2>/dev/null; then
|
||||||
|
success "Client → VPS ping OK"
|
||||||
|
else
|
||||||
|
warn "Client → VPS ping failed. Tunnel may need more time to establish."
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Checking DNAT on VPS (curl localhost)..."
|
||||||
|
local http_code
|
||||||
|
http_code=$(ssh_remote "$VPS_HOST" "$VPS_PASS" \
|
||||||
|
"curl -s -o /dev/null -w '%{http_code}' --connect-timeout 3 http://localhost 2>/dev/null || echo '000'")
|
||||||
|
if [[ "$http_code" != "000" ]]; then
|
||||||
|
success "DNAT responding (HTTP ${http_code})"
|
||||||
|
else
|
||||||
|
warn "DNAT check: no response (expected if no web server on client yet)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Print summary ───────────────────────────────────────────────────────────
|
||||||
|
print_summary() {
|
||||||
|
header "Deployment Complete"
|
||||||
|
|
||||||
|
echo -e "${BOLD}${GREEN}WireGuard VPS Tunnel deployed successfully!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}VPS (Server):${NC} ${VPS_HOST}"
|
||||||
|
echo -e "${BOLD}Client:${NC} ${CLIENT_HOST}"
|
||||||
|
echo -e "${BOLD}VPS Public IP:${NC} ${VPS_PUBLIC_IP}"
|
||||||
|
echo -e "${BOLD}WireGuard Network:${NC} 10.0.0.0/24"
|
||||||
|
echo -e "${BOLD} Server WG IP:${NC} 10.0.0.1"
|
||||||
|
echo -e "${BOLD} Client WG IP:${NC} 10.0.0.2"
|
||||||
|
echo -e "${BOLD}WG Port:${NC} ${WG_PORT}/udp"
|
||||||
|
echo -e "${BOLD}Forwarded Ports:${NC} ${PORTS} → 10.0.0.2"
|
||||||
|
echo -e "${BOLD}Keepalive:${NC} ${KEEPALIVE}s"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Keys:${NC}"
|
||||||
|
echo -e " Server Public: ${SERVER_PUBLIC_KEY}"
|
||||||
|
echo -e " Client Public: ${CLIENT_PUBLIC_KEY}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Useful commands:${NC}"
|
||||||
|
echo " # Check tunnel status on VPS"
|
||||||
|
echo " ssh ${VPS_HOST} docker exec wireguard-server wg show"
|
||||||
|
echo ""
|
||||||
|
echo " # Check tunnel status on client"
|
||||||
|
echo " ssh ${CLIENT_HOST} docker exec wireguard-client wg show"
|
||||||
|
echo ""
|
||||||
|
echo " # View logs"
|
||||||
|
echo " ssh ${VPS_HOST} 'docker compose -f /opt/wireguard-vps-tunnel/docker-compose.server.yml logs -f'"
|
||||||
|
echo " ssh ${CLIENT_HOST} 'docker compose -f /opt/wireguard-vps-tunnel/docker-compose.client.yml logs -f'"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Config locations:${NC}"
|
||||||
|
echo " VPS: /opt/wireguard-vps-tunnel/"
|
||||||
|
echo " Client: /opt/wireguard-vps-tunnel/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Cleanup ─────────────────────────────────────────────────────────────────
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$DEPLOY_TAR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# ── Main ────────────────────────────────────────────────────────────────────
|
||||||
|
main() {
|
||||||
|
banner
|
||||||
|
parse_args "$@"
|
||||||
|
validate_args
|
||||||
|
check_deps
|
||||||
|
|
||||||
|
if [[ "$FORCE" == true ]]; then
|
||||||
|
force_stop
|
||||||
|
fi
|
||||||
|
|
||||||
|
generate_all_keys
|
||||||
|
detect_vps_info
|
||||||
|
create_archive
|
||||||
|
|
||||||
|
deploy_to_host "$VPS_HOST" "$VPS_PASS" "vps"
|
||||||
|
deploy_to_host "$CLIENT_HOST" "$CLIENT_PASS" "client"
|
||||||
|
|
||||||
|
stop_host_wg "$VPS_HOST" "$VPS_PASS"
|
||||||
|
stop_host_wg "$CLIENT_HOST" "$CLIENT_PASS"
|
||||||
|
enable_ip_forwarding
|
||||||
|
|
||||||
|
start_container "$VPS_HOST" "$VPS_PASS" "vps"
|
||||||
|
start_container "$CLIENT_HOST" "$CLIENT_PASS" "client"
|
||||||
|
|
||||||
|
wait_for_healthy
|
||||||
|
verify_tunnel
|
||||||
|
print_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
# docker-compose.client.yml — Клиент (домашний сервер / Raspberry Pi за NAT)
|
# docker-compose.client.yml — Клиент (домашний сервер / Raspberry Pi за NAT)
|
||||||
# Запуск: docker compose -f docker-compose.client.yml up -d
|
# Запуск: docker compose -f docker-compose.client.yml up -d
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
version: "3.8"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
wireguard-client:
|
wireguard-client:
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
# docker-compose.server.yml — VPS (сервер с публичным IP)
|
# docker-compose.server.yml — VPS (сервер с публичным IP)
|
||||||
# Запуск: docker compose -f docker-compose.server.yml up -d
|
# Запуск: docker compose -f docker-compose.server.yml up -d
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
version: "3.8"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
wireguard-server:
|
wireguard-server:
|
||||||
|
|||||||
110
install.sh
110
install.sh
@@ -26,6 +26,8 @@ PORTS="80,443"
|
|||||||
KEEPALIVE="21"
|
KEEPALIVE="21"
|
||||||
INTERACTIVE=true
|
INTERACTIVE=true
|
||||||
BUILD_LOCAL=false
|
BUILD_LOCAL=false
|
||||||
|
SERVER_KEY=""
|
||||||
|
CLIENT_KEY=""
|
||||||
|
|
||||||
# ── Функции вывода ──────────────────────────────────────────────────────────
|
# ── Функции вывода ──────────────────────────────────────────────────────────
|
||||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
@@ -91,12 +93,78 @@ install_docker() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Включение IP-форвардинга (обязательно для VPS) ──────────────────────────
|
||||||
|
enable_ip_forwarding() {
|
||||||
|
header "Настройка 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
|
||||||
|
|
||||||
|
# Включить постоянно
|
||||||
|
if ! grep -q "net.ipv4.ip_forward" /etc/sysctl.conf 2>/dev/null; then
|
||||||
|
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
|
||||||
|
else
|
||||||
|
sed -i 's/.*net.ipv4.ip_forward.*/net.ipv4.ip_forward = 1/' /etc/sysctl.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -q "net.ipv6.conf.all.forwarding" /etc/sysctl.conf 2>/dev/null; then
|
||||||
|
echo "net.ipv6.conf.all.forwarding = 1" >> /etc/sysctl.conf
|
||||||
|
else
|
||||||
|
sed -i 's/.*net.ipv6.conf.all.forwarding.*/net.ipv6.conf.all.forwarding = 1/' /etc/sysctl.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
sysctl -p 2>/dev/null || true
|
||||||
|
|
||||||
|
success "IP-форвардинг включён"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Остановка host-level WireGuard ────────────────────────────────────────
|
||||||
|
stop_host_wireguard() {
|
||||||
|
if systemctl is-active --quiet wg-quick@wg0 2>/dev/null; then
|
||||||
|
warn "Обнаружен host-level WireGuard (wg-quick@wg0). Останавливаем..."
|
||||||
|
systemctl stop wg-quick@wg0 2>/dev/null || true
|
||||||
|
systemctl disable wg-quick@wg0 2>/dev/null || true
|
||||||
|
ip link del wg0 2>/dev/null || true
|
||||||
|
success "Host-level WireGuard остановлен и отключён"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Остановить watchdog если есть
|
||||||
|
if systemctl is-active --quiet wg-watchdog 2>/dev/null; then
|
||||||
|
systemctl stop wg-watchdog 2>/dev/null || true
|
||||||
|
systemctl disable wg-watchdog 2>/dev/null || true
|
||||||
|
success "wg-watchdog остановлен"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# ── Генерация ключей WireGuard ─────────────────────────────────────────────
|
# ── Генерация ключей WireGuard ─────────────────────────────────────────────
|
||||||
generate_keys() {
|
generate_keys() {
|
||||||
header "Генерация ключей WireGuard"
|
header "Генерация ключей WireGuard"
|
||||||
|
|
||||||
mkdir -p "$CONFIG_DIR"
|
mkdir -p "$CONFIG_DIR"
|
||||||
|
|
||||||
|
# Если ключи переданы через аргументы — используем их
|
||||||
|
if [[ -n "$SERVER_KEY" ]] && [[ -n "$CLIENT_KEY" ]]; then
|
||||||
|
info "Использование ключей из аргументов командной строки"
|
||||||
|
SERVER_PUBLIC_KEY="$SERVER_KEY"
|
||||||
|
CLIENT_PUBLIC_KEY="$CLIENT_KEY"
|
||||||
|
# Приватные ключи не нужны для .env, только публичные
|
||||||
|
SERVER_PRIVATE_KEY=""
|
||||||
|
CLIENT_PRIVATE_KEY=""
|
||||||
|
success "Ключи загружены из аргументов"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$SERVER_KEY" ]]; then
|
||||||
|
info "Использование SERVER_PUBLIC_KEY из аргумента --server-key"
|
||||||
|
SERVER_PUBLIC_KEY="$SERVER_KEY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$CLIENT_KEY" ]]; then
|
||||||
|
info "Использование CLIENT_PUBLIC_KEY из аргумента --client-key"
|
||||||
|
CLIENT_PUBLIC_KEY="$CLIENT_KEY"
|
||||||
|
fi
|
||||||
|
|
||||||
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
|
||||||
@@ -126,10 +194,19 @@ generate_keys() {
|
|||||||
success "Ключи сгенерированы"
|
success "Ключи сгенерированы"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SERVER_PRIVATE_KEY=$(cat "$CONFIG_DIR/server_private.key")
|
# Загружаем ключи из файлов, если не были переданы через аргументы
|
||||||
SERVER_PUBLIC_KEY=$(cat "$CONFIG_DIR/server_public.key")
|
if [[ -z "${SERVER_PRIVATE_KEY:-}" ]] && [[ -f "$CONFIG_DIR/server_private.key" ]]; then
|
||||||
CLIENT_PRIVATE_KEY=$(cat "$CONFIG_DIR/client_private.key")
|
SERVER_PRIVATE_KEY=$(cat "$CONFIG_DIR/server_private.key")
|
||||||
CLIENT_PUBLIC_KEY=$(cat "$CONFIG_DIR/client_public.key")
|
fi
|
||||||
|
if [[ -z "${SERVER_PUBLIC_KEY:-}" ]] && [[ -f "$CONFIG_DIR/server_public.key" ]]; then
|
||||||
|
SERVER_PUBLIC_KEY=$(cat "$CONFIG_DIR/server_public.key")
|
||||||
|
fi
|
||||||
|
if [[ -z "${CLIENT_PRIVATE_KEY:-}" ]] && [[ -f "$CONFIG_DIR/client_private.key" ]]; then
|
||||||
|
CLIENT_PRIVATE_KEY=$(cat "$CONFIG_DIR/client_private.key")
|
||||||
|
fi
|
||||||
|
if [[ -z "${CLIENT_PUBLIC_KEY:-}" ]] && [[ -f "$CONFIG_DIR/client_public.key" ]]; then
|
||||||
|
CLIENT_PUBLIC_KEY=$(cat "$CONFIG_DIR/client_public.key")
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Создание .env файла ────────────────────────────────────────────────────
|
# ── Создание .env файла ────────────────────────────────────────────────────
|
||||||
@@ -288,6 +365,8 @@ parse_args() {
|
|||||||
--wg-port) WG_PORT="$2"; shift 2 ;;
|
--wg-port) WG_PORT="$2"; shift 2 ;;
|
||||||
--keepalive) KEEPALIVE="$2"; shift 2 ;;
|
--keepalive) KEEPALIVE="$2"; shift 2 ;;
|
||||||
--build) BUILD_LOCAL=true; shift ;;
|
--build) BUILD_LOCAL=true; shift ;;
|
||||||
|
--server-key) SERVER_KEY="$2"; shift 2 ;;
|
||||||
|
--client-key) CLIENT_KEY="$2"; shift 2 ;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
echo "Использование: $0 [ОПЦИИ]"
|
echo "Использование: $0 [ОПЦИИ]"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -299,6 +378,8 @@ parse_args() {
|
|||||||
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 " --build Собрать образ локально (вместо загрузки из registry)"
|
||||||
|
echo " --server-key KEY Публичный ключ сервера (для неинтерактивной настройки клиента)"
|
||||||
|
echo " --client-key KEY Публичный ключ клиента (для неинтерактивной настройки сервера)"
|
||||||
echo " --help, -h Показать эту справку"
|
echo " --help, -h Показать эту справку"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Примеры:"
|
echo "Примеры:"
|
||||||
@@ -335,6 +416,8 @@ print_summary() {
|
|||||||
|
|
||||||
if [[ "$MODE" == "vps" ]]; then
|
if [[ "$MODE" == "vps" ]]; then
|
||||||
echo -e "${BOLD}Проброс портов:${NC} ${PORTS} → ${CLIENT_WG_IP}"
|
echo -e "${BOLD}Проброс портов:${NC} ${PORTS} → ${CLIENT_WG_IP}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}${BOLD}✅ IP-форвардинг включён (net.ipv4.ip_forward=1)${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -355,11 +438,14 @@ print_summary() {
|
|||||||
|
|
||||||
if [[ "$MODE" == "vps" ]]; then
|
if [[ "$MODE" == "vps" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
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 " 1. Запишите публичный ключ сервера:"
|
||||||
echo ""
|
echo -e " ${BOLD}cat ${CONFIG_DIR}/server_public.key${NC}"
|
||||||
echo -e "${YELLOW}Затем на клиенте (RPi):${NC}"
|
echo -e " 2. Передайте его на клиент (в .env как SERVER_PUBLIC_KEY)"
|
||||||
echo -e " ${BOLD}sudo ./install.sh --client --vps-ip ${VPS_PUBLIC_IP}${NC}"
|
echo -e " 3. После запуска клиента, получите CLIENT_PUBLIC_KEY:"
|
||||||
|
echo -e " ${BOLD}cat ${CONFIG_DIR}/client_public.key${NC} (на клиенте)"
|
||||||
|
echo -e " 4. Впишите CLIENT_PUBLIC_KEY в .env на VPS и перезапустите:"
|
||||||
|
echo -e " ${BOLD}docker compose -f docker-compose.server.yml restart${NC}"
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}${BOLD}⚠️ ВАЖНО:${NC} ${YELLOW}Скопируйте публичный ключ клиента на VPS:${NC}"
|
echo -e "${YELLOW}${BOLD}⚠️ ВАЖНО:${NC} ${YELLOW}Скопируйте публичный ключ клиента на VPS:${NC}"
|
||||||
@@ -367,6 +453,8 @@ print_summary() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}И добавьте в .env на VPS:${NC}"
|
echo -e "${YELLOW}И добавьте в .env на VPS:${NC}"
|
||||||
echo -e " ${BOLD}CLIENT_PUBLIC_KEY=<ключ>${NC}"
|
echo -e " ${BOLD}CLIENT_PUBLIC_KEY=<ключ>${NC}"
|
||||||
|
echo -e "${YELLOW}Затем перезапустите сервер:${NC}"
|
||||||
|
echo -e " ${BOLD}docker compose -f docker-compose.server.yml restart${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -403,8 +491,12 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
install_docker
|
install_docker
|
||||||
|
stop_host_wireguard
|
||||||
generate_keys
|
generate_keys
|
||||||
create_env_file
|
create_env_file
|
||||||
|
if [[ "$MODE" == "vps" ]]; then
|
||||||
|
enable_ip_forwarding
|
||||||
|
fi
|
||||||
start_docker
|
start_docker
|
||||||
print_summary
|
print_summary
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ generate_keys() {
|
|||||||
|
|
||||||
# ── Создание конфигурации WireGuard ────────────────────────────────────────
|
# ── Создание конфигурации WireGuard ────────────────────────────────────────
|
||||||
create_wg_config() {
|
create_wg_config() {
|
||||||
|
# Всегда пересоздаём конфиг — ключи или порты могли измениться
|
||||||
if [[ -f "$WG_CONF" ]]; then
|
if [[ -f "$WG_CONF" ]]; then
|
||||||
log "INFO" "Конфигурация ${WG_CONF} уже существует, пропускаем создание"
|
log "INFO" "Обновление конфигурации ${WG_CONF}..."
|
||||||
return 0
|
rm -f "$WG_CONF"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "${CLIENT_PUBLIC_KEY:-}" ]]; then
|
if [[ -z "${CLIENT_PUBLIC_KEY:-}" ]]; then
|
||||||
@@ -106,10 +107,17 @@ WGCONF
|
|||||||
start_wireguard() {
|
start_wireguard() {
|
||||||
log "INFO" "Запуск WireGuard интерфейса wg0..."
|
log "INFO" "Запуск WireGuard интерфейса wg0..."
|
||||||
|
|
||||||
# Включаем IP-форвардинг
|
# Включаем IP-форвардинг (может быть read-only в Docker, хост уже настроен)
|
||||||
echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null || true
|
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
|
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || true
|
||||||
|
|
||||||
|
# Удаляем старый интерфейс если остался от предыдущего запуска
|
||||||
|
if ip link show wg0 &>/dev/null; then
|
||||||
|
log "INFO" "Удаление старого интерфейса wg0..."
|
||||||
|
ip link del wg0 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
wg-quick up wg0
|
wg-quick up wg0
|
||||||
|
|
||||||
log "INFO" "WireGuard интерфейс wg0 запущен"
|
log "INFO" "WireGuard интерфейс wg0 запущен"
|
||||||
|
|||||||
Reference in New Issue
Block a user