From 98f78d712ea20c39f6e9d583bd31b6bf45c1344f Mon Sep 17 00:00:00 2001 From: vpn-panel Date: Thu, 21 May 2026 19:07:07 +0300 Subject: [PATCH] Fix DB auth: local-only Postgres, install password defaults --- .env.example | 10 +++++--- DEPLOY.md | 5 ++-- cmd/install/main.go | 59 +++++++++++++++++++++++++++++++++++++-------- docker-compose.yml | 11 +++++++-- scripts/setup.sh | 54 +++++++++++++++++++++++++++++------------ 5 files changed, 106 insertions(+), 33 deletions(-) diff --git a/.env.example b/.env.example index f1213fe..d3d2afa 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,15 @@ -# Скопируйте в .env или запустите: go run ./cmd/install +# Скопируйте в .env ПЕРЕД первым запуском postgres, или: ./scripts/setup.sh +# PostgreSQL доступен только внутри Docker-сети (порт 5432 снаружи закрыт). APP_PORT=8080 APP_DOMAIN=localhost -DATABASE_URL=postgres://vpnpanel:changeme@postgres:5432/vpnpanel?sslmode=disable -SECRET_KEY=change-me-to-random-32-bytes-base64 -INSTALLED=false POSTGRES_USER=vpnpanel POSTGRES_PASSWORD=changeme POSTGRES_DB=vpnpanel POSTGRES_HOST=postgres POSTGRES_PORT=5432 + +DATABASE_URL=postgres://vpnpanel:changeme@postgres:5432/vpnpanel?sslmode=disable +SECRET_KEY=change-me-to-random-32-bytes-base64 +INSTALLED=false diff --git a/DEPLOY.md b/DEPLOY.md index 439bf2e..4298baf 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -66,8 +66,8 @@ docker compose ps | Параметр | Значение | |----------|------------| -| Хост | `postgres` (внутри Docker) / `127.0.0.1` (с хоста) | -| Порт | `5432` | +| Хост | `postgres` (только Docker-сеть) | +| Порт | `5432` (с интернета **закрыт**, наружу не пробрасывается) | | БД | `vpnpanel` | | Пользователь | `vpnpanel` | | Пароль | `changeme` (смените в установщике) | @@ -219,6 +219,7 @@ cat backup.sql | docker exec -i vpn-panel-db psql -U vpnpanel vpnpanel |----------|---------| | `Command 'go' not found` | Не ставьте Go — используйте `./scripts/setup.sh` или `docker compose --profile tools run --rm install` | | `env file .env not found` | Обновите репозиторий (`git pull`) или создайте `.env`: `cp .env.example .env` и отредактируйте | +| `password authentication failed` | Пароль в установщике ≠ пароль при первом запуске тома. Enter = `changeme`, или сброс: `docker volume rm vpn-panel_pgdata` | | `DATABASE_URL не задан` | Запустите установщик в Docker или создайте `.env` из `.env.example` | | Нет подключения к БД | `docker compose ps`, проверьте `healthy` у postgres | | Порт занят | Смените `APP_PORT` в `.env` и в `docker-compose.yml` | diff --git a/cmd/install/main.go b/cmd/install/main.go index 9c89930..7e69ee2 100644 --- a/cmd/install/main.go +++ b/cmd/install/main.go @@ -11,6 +11,7 @@ import ( "strings" "syscall" + "github.com/joho/godotenv" "golang.org/x/term" "vpn-panel/internal/auth" "vpn-panel/internal/database" @@ -18,6 +19,8 @@ import ( ) func main() { + _ = godotenv.Load() + fmt.Println() fmt.Println(" ╔══════════════════════════════════════╗") fmt.Println(" ║ VPN Panel — Установщик ║") @@ -41,12 +44,18 @@ func main() { domain := prompt(reader, "Домен панели (например panel.example.com)", "localhost") appPort := prompt(reader, "Порт приложения", "8080") - fmt.Println("\n--- PostgreSQL 17 ---") - dbHost := prompt(reader, "Хост БД", "postgres") - dbPort := prompt(reader, "Порт БД", "5432") - dbUser := prompt(reader, "Пользователь БД", "vpnpanel") - dbPass := promptSecret("Пароль БД") - dbName := prompt(reader, "Имя базы данных", "vpnpanel") + fmt.Println("\n--- PostgreSQL 17 (только локально, Docker-сеть) ---") + fmt.Println("Хост БД в Docker: postgres (не localhost)") + if os.Getenv("POSTGRES_PASSWORD") != "" { + fmt.Printf("Пароль из окружения/.env: задан (пользователь %s)\n", envOr("POSTGRES_USER", "vpnpanel")) + } + + dbHost := prompt(reader, "Хост БД", envOr("POSTGRES_HOST", "postgres")) + dbPort := prompt(reader, "Порт БД", envOr("POSTGRES_PORT", "5432")) + dbUser := prompt(reader, "Пользователь БД", envOr("POSTGRES_USER", "vpnpanel")) + dbPassDefault := envOr("POSTGRES_PASSWORD", "changeme") + dbPass := promptSecretDefault("Пароль БД", dbPassDefault) + dbName := prompt(reader, "Имя базы данных", envOr("POSTGRES_DB", "vpnpanel")) fmt.Println("\n--- Администратор (единственный) ---") adminEmail := prompt(reader, "Email администратора", "admin@localhost") @@ -78,7 +87,7 @@ func main() { pool, err := database.Connect(ctx, databaseURL) if err != nil { fmt.Fprintf(os.Stderr, "не удалось подключиться: %v\n", err) - fmt.Println("Запустите PostgreSQL (docker compose up -d postgres) и повторите установку.") + printDBHelp(dbPassDefault) os.Exit(1) } defer pool.Close() @@ -114,7 +123,7 @@ DATABASE_URL=%s SECRET_KEY=%s INSTALLED=true -# PostgreSQL (для docker-compose) +# PostgreSQL (docker-compose, только внутренняя сеть) POSTGRES_USER=%s POSTGRES_PASSWORD=%s POSTGRES_DB=%s @@ -135,13 +144,33 @@ POSTGRES_PORT=%s fmt.Println(" ✓ Файл .env создан") fmt.Println() fmt.Println(" Дальше:") - fmt.Println(" docker compose up -d") - fmt.Println(" или: go run ./cmd/panel") + fmt.Println(" docker compose up -d --build panel") fmt.Println() fmt.Printf(" Панель: http://%s:%s\n", domain, appPort) fmt.Println() } +func printDBHelp(defaultPass string) { + fmt.Println() + fmt.Println(" Подсказки:") + fmt.Println(" 1. PostgreSQL уже запущен? Пароль задан при ПЕРВОМ старте тома pgdata.") + fmt.Printf(" По умолчанию был: %s — нажмите Enter в поле «Пароль БД».\n", defaultPass) + fmt.Println(" 2. Сменили пароль в установщике, а том старый — сбросьте БД:") + fmt.Println(" docker compose down") + fmt.Println(" docker volume rm vpn-panel_pgdata") + fmt.Println(" docker compose up -d postgres") + fmt.Println(" docker compose --profile tools run --rm install") + fmt.Println(" 3. Убедитесь: docker compose up -d postgres && хост БД = postgres") + fmt.Println() +} + +func envOr(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} + func prompt(reader *bufio.Reader, label, defaultVal string) string { if defaultVal != "" { fmt.Printf("%s [%s]: ", label, defaultVal) @@ -166,6 +195,16 @@ func promptSecret(label string) string { return string(b) } +func promptSecretDefault(label, defaultVal string) string { + fmt.Printf("%s [%s] (Enter = по умолчанию): ", label, defaultVal) + b, err := term.ReadPassword(int(syscall.Stdin)) + fmt.Println() + if err != nil || len(b) == 0 { + return defaultVal + } + return string(b) +} + func generateSecret() (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { diff --git a/docker-compose.yml b/docker-compose.yml index f4e9b1d..68f7af7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,8 +9,9 @@ services: POSTGRES_DB: ${POSTGRES_DB:-vpnpanel} volumes: - pgdata:/var/lib/postgresql/data - ports: - - "5432:5432" + # БД только внутри Docker-сети, с интернета недоступна + expose: + - "5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-vpnpanel} -d ${POSTGRES_DB:-vpnpanel}"] interval: 5s @@ -26,6 +27,12 @@ services: volumes: - .:/work entrypoint: ["/app/install"] + environment: + POSTGRES_HOST: postgres + POSTGRES_PORT: "5432" + POSTGRES_USER: ${POSTGRES_USER:-vpnpanel} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme} + POSTGRES_DB: ${POSTGRES_DB:-vpnpanel} depends_on: postgres: condition: service_healthy diff --git a/scripts/setup.sh b/scripts/setup.sh index 43570e1..6b0a6ac 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,30 +1,54 @@ #!/bin/bash -# Установка без Go — только Docker +# Установка без Go — только Docker. БД только внутри Docker-сети. set -e cd "$(dirname "$0")/.." echo "=== VPN Panel — установка (Docker) ===" +# Если .env нет — создаём с паролем ДО первого запуска postgres (том получит правильный пароль) if [ ! -f .env ]; then - echo "Файл .env не найден — запускаем установщик..." - docker compose up -d postgres - echo "Ожидание PostgreSQL..." - for i in $(seq 1 30); do - if docker compose exec -T postgres pg_isready -U "${POSTGRES_USER:-vpnpanel}" -d "${POSTGRES_DB:-vpnpanel}" >/dev/null 2>&1; then - break - fi - sleep 2 - done + PG_PASS="${POSTGRES_PASSWORD:-$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)}" + SECRET="${SECRET_KEY:-$(openssl rand -base64 32)}" + cat > .env </dev/null 2>&1; then + break + fi + sleep 2 +done + +if grep -q '^INSTALLED=true' .env 2>/dev/null; then + echo ".env уже помечен INSTALLED=true — пропускаем установщик." +else docker compose build install docker compose --profile tools run --rm install -else - echo "Найден .env — пропускаем установщик." - docker compose up -d postgres fi echo "Запуск панели..." docker compose up -d --build panel +APP_PORT=$(grep -E '^APP_PORT=' .env | cut -d= -f2- | tr -d '\r' || echo 8080) +APP_DOMAIN=$(grep -E '^APP_DOMAIN=' .env | cut -d= -f2- | tr -d '\r' || echo localhost) + echo "" -echo "Готово. Панель: http://${APP_DOMAIN:-localhost}:${APP_PORT:-8080}" -echo "Проверка: curl -s http://127.0.0.1:${APP_PORT:-8080}/health" +echo "Готово. Панель: http://${APP_DOMAIN}:${APP_PORT}" +echo "Проверка: curl -s http://127.0.0.1:${APP_PORT}/health" +echo "PostgreSQL: только внутри Docker (порт 5432 снаружи закрыт)."