# VPN Panel Админ-панель VPN на Go + PostgreSQL 17. Готово к деплою в **Dokploy** через Docker Compose. ## Стек - Go 1.23 (веб-админка) - PostgreSQL 17 - Docker Compose - Reverse proxy + SSL: **Caddy** / **Nginx** / **Traefik** ## Быстрый старт (Dokploy / Docker) 1. В Dokploy создайте **Docker Compose** приложение из этого репозитория. 2. Задайте Environment Variables (`.env` в git не лежит): ```env APP_SECRET=long-random-secret ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=strong-password POSTGRES_PASSWORD=strong-db-password ``` 3. Deploy. Домен в Dokploy → сервис **`app`**, порт **`8080`** (не postgres). 4. Если **Bad Gateway**: смотрите логи `app` — чаще всего падение из‑за БД или домен смотрит не на тот порт. ```bash docker compose up -d --build docker compose logs -f app ``` Панель: `http://localhost:8080` · health: `/health` ## Авторизация админа (из `.env`) | Переменная | Значение по умолчанию | |---|---| | `ADMIN_EMAIL` | `admin@panel.local` | | `ADMIN_PASSWORD` | `Admin123!ChangeMe` | | `ADMIN_NAME` | `Administrator` | При первом запуске админ создаётся автоматически из этих переменных. --- ## Режим ноды (как Remnawave, но проще) Панель **не** гоняет Xray сама — VPN поднимается на отдельных серверах-нодах (как [Remnawave Node](https://docs.rw/install/remnawave-node)). ### Чем удобнее | Remnawave Node | VPN Panel | |---|---| | Docker вручную + вставить compose | **Авто по SSH**: host/user/пароль (или ключ) → панель ставит Docker + агент | | Или вручную | **Ручной режим** — тот же compose, если SSH недоступен | ### Как добавить ноду 1. Админка → **Ноды** → **Добавить ноду** 2. Укажите имя, IP/хост, SSH (`root` + пароль или ключ), порт API ноды (по умолчанию `2222`) 3. Режим: - **Авто по SSH** — мастер подключается, ставит Docker (если нет), заливает бинарник `vpn-node`, поднимает `docker compose` - **Вручную** — скопируйте `docker-compose.yml` и бинарник `/app/bin/vpn-node` на сервер 4. На firewall ноды откройте `NODE_PORT` **только** для IP панели 5. Нажмите **Проверить online** Агент слушает `NODE_PORT`, авторизация: `Authorization: Bearer `. Эндпоинты агента: `GET /health`, `GET|POST /api/v1/config`, `GET /api/v1/status`. SSH-секреты в БД шифруются AES-GCM ключом от `APP_SECRET`. > Панель в Docker должна иметь сетевой доступ до SSH (22) и NODE_PORT ноды. На VPS/Dokploy это обычно ок. --- ## Reverse proxy + SSL (Let's Encrypt) Примеры лежат в `deploy/`. Сеть Docker: `vpn-panel-net`. ### Что нужно до выпуска сертификата 1. **DNS**: A (и при необходимости AAAA) запись `DOMAIN` → публичный IP сервера. 2. **Порты**: `80` и `443` открыты на firewall / security group (TCP; для Caddy HTTP/3 ещё `443/udp`). 3. **Домен в `.env`**: ```env DOMAIN=panel.example.com ACME_EMAIL=admin@example.com ``` 4. **Не светить панель наружу на `:8080`** — только через proxy: ```bash docker compose -f docker-compose.yml -f deploy/compose.behind-proxy.yml up -d ``` Либо в Dokploy / compose закомментируйте `ports` у `app`, оставьте доступ по внутренней сети `app:8080`. 5. Прокси обязан передавать заголовки `Host`, `X-Forwarded-Proto`, `X-Forwarded-For` (уже есть в примерах). > Let's Encrypt не выпустит сертификат на `localhost` / приватный IP. Нужен реальный домен с публичным DNS. --- ### Вариант A — Caddy (самый простой, SSL автоматом) Caddy сам получает и продлевает сертификат. 1. В `deploy/caddy/Caddyfile` домен берётся из `{$DOMAIN:...}` (или задайте в `.env`). 2. Запуск вместе с панелью: ```bash # из корня репозитория docker compose \ -f docker-compose.yml \ -f deploy/compose.behind-proxy.yml \ -f deploy/caddy/docker-compose.caddy.yml \ up -d --build ``` 3. Откройте `https://panel.example.com`. Файлы: `deploy/caddy/Caddyfile`, `deploy/caddy/docker-compose.caddy.yml`. --- ### Вариант B — Nginx + Certbot 1. Подставьте свой домен в `deploy/nginx/nginx.conf` (`server_name` и пути к сертификатам). 2. Поднимите стек (сначала HTTP + ACME webroot): ```bash docker compose \ -f docker-compose.yml \ -f deploy/compose.behind-proxy.yml \ -f deploy/nginx/docker-compose.nginx.yml \ up -d --build ``` 3. Выпустите сертификат: ```bash docker compose -f deploy/nginx/docker-compose.nginx.yml run --rm certbot certonly \ --webroot -w /var/www/certbot \ -d panel.example.com \ --email admin@example.com \ --agree-tos --no-eff-email ``` 4. В `deploy/nginx/nginx.conf`: - раскомментируйте блок `server { listen 443 ... }`; - включите `return 301 https://$host$request_uri;` в HTTP-сервере; - закомментируйте временный `location /` на порту 80 (оставьте только ACME). 5. Перезагрузите nginx: ```bash docker compose -f docker-compose.yml -f deploy/nginx/docker-compose.nginx.yml exec nginx nginx -s reload ``` Продление: контейнер `certbot` крутит `renew` раз в 12 часов; после renew — `nginx -s reload`. Файлы: `deploy/nginx/`. --- ### Вариант C — Traefik (свой или Dokploy) #### C1. Свой Traefik из репозитория ```bash docker compose \ -f docker-compose.yml \ -f deploy/compose.behind-proxy.yml \ -f deploy/traefik/docker-compose.traefik.yml \ up -d --build ``` В `.env`: `DOMAIN`, `ACME_EMAIL`. Сертификат выпускается HTTP-challenge на entrypoint `web`. #### C2. Dokploy (встроенный Traefik) Dokploy уже поднимает Traefik — **не** добавляйте `deploy/traefik/docker-compose.traefik.yml`. 1. В Dokploy укажите домен для сервиса `app`. 2. Либо добавьте labels к `app` (см. `deploy/traefik/labels.example.yml`): ```yaml labels: - traefik.enable=true - traefik.http.routers.vpn-panel.rule=Host(`panel.example.com`) - traefik.http.routers.vpn-panel.entrypoints=websecure - traefik.http.routers.vpn-panel.tls=true - traefik.http.routers.vpn-panel.tls.certresolver=letsencrypt - traefik.http.services.vpn-panel.loadbalancer.server.port=8080 ``` Имя `certresolver` в Dokploy часто `letsencrypt` — проверьте в настройках (или `TRAEFIK_CERT_RESOLVER` в `.env`). 3. Не публикуйте порт `8080` наружу в UI Dokploy, если трафик идёт только через Traefik. --- ### Чеклист после включения HTTPS | Проверка | Ожидание | |---|---| | `https://DOMAIN/` | Главная панель | | `https://DOMAIN/health` | `ok` | | HTTP → HTTPS | Редирект 301 (Caddy/Traefik по умолчанию; Nginx — после шага 4) | | Сертификат | Валидный LE, не self-signed | | Порт `8080` снаружи | Закрыт | ### Типичные ошибки | Симптом | Причина | |---|---| | ACME timeout / pending | DNS ещё не указывает на сервер, или закрыт порт 80 | | 502 Bad Gateway | Proxy не в сети `vpn-panel-net` / сервис `app` не запущен | | Редирект-петля | За приложением HTTPS, а `X-Forwarded-Proto` не передаётся | | Certbot: no such file | Домен в путях `nginx.conf` не совпадает с `-d` | --- ## Протоколы (seed) WireGuard, OpenVPN, VLESS, VMess, Trojan, Shadowsocks, Hysteria2 — можно включать/выключать в админке. ## Локальная разработка без Docker ```bash # нужен локальный Postgres 17 и .env с POSTGRES_HOST=localhost go mod tidy go run ./cmd/server ``` ## Эндпоинты - `/` — главная - `/login` — вход - `/admin` — дашборд - `/admin/nodes` — ноды - `/admin/protocols` — протоколы - `/health` — healthcheck