Fix DB auth: local-only Postgres, install password defaults

This commit is contained in:
vpn-panel
2026-05-21 19:07:07 +03:00
parent 74d5d34679
commit 98f78d712e
5 changed files with 106 additions and 33 deletions
+6 -4
View File
@@ -1,13 +1,15 @@
# Скопируйте в .env или запустите: go run ./cmd/install # Скопируйте в .env ПЕРЕД первым запуском postgres, или: ./scripts/setup.sh
# PostgreSQL доступен только внутри Docker-сети (порт 5432 снаружи закрыт).
APP_PORT=8080 APP_PORT=8080
APP_DOMAIN=localhost 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_USER=vpnpanel
POSTGRES_PASSWORD=changeme POSTGRES_PASSWORD=changeme
POSTGRES_DB=vpnpanel POSTGRES_DB=vpnpanel
POSTGRES_HOST=postgres POSTGRES_HOST=postgres
POSTGRES_PORT=5432 POSTGRES_PORT=5432
DATABASE_URL=postgres://vpnpanel:changeme@postgres:5432/vpnpanel?sslmode=disable
SECRET_KEY=change-me-to-random-32-bytes-base64
INSTALLED=false
+3 -2
View File
@@ -66,8 +66,8 @@ docker compose ps
| Параметр | Значение | | Параметр | Значение |
|----------|------------| |----------|------------|
| Хост | `postgres` (внутри Docker) / `127.0.0.1` (с хоста) | | Хост | `postgres` (только Docker-сеть) |
| Порт | `5432` | | Порт | `5432` (с интернета **закрыт**, наружу не пробрасывается) |
| БД | `vpnpanel` | | БД | `vpnpanel` |
| Пользователь | `vpnpanel` | | Пользователь | `vpnpanel` |
| Пароль | `changeme` (смените в установщике) | | Пароль | `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` | | `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` и отредактируйте | | `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` | | `DATABASE_URL не задан` | Запустите установщик в Docker или создайте `.env` из `.env.example` |
| Нет подключения к БД | `docker compose ps`, проверьте `healthy` у postgres | | Нет подключения к БД | `docker compose ps`, проверьте `healthy` у postgres |
| Порт занят | Смените `APP_PORT` в `.env` и в `docker-compose.yml` | | Порт занят | Смените `APP_PORT` в `.env` и в `docker-compose.yml` |
+49 -10
View File
@@ -11,6 +11,7 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/joho/godotenv"
"golang.org/x/term" "golang.org/x/term"
"vpn-panel/internal/auth" "vpn-panel/internal/auth"
"vpn-panel/internal/database" "vpn-panel/internal/database"
@@ -18,6 +19,8 @@ import (
) )
func main() { func main() {
_ = godotenv.Load()
fmt.Println() fmt.Println()
fmt.Println(" ╔══════════════════════════════════════╗") fmt.Println(" ╔══════════════════════════════════════╗")
fmt.Println(" ║ VPN Panel — Установщик ║") fmt.Println(" ║ VPN Panel — Установщик ║")
@@ -41,12 +44,18 @@ func main() {
domain := prompt(reader, "Домен панели (например panel.example.com)", "localhost") domain := prompt(reader, "Домен панели (например panel.example.com)", "localhost")
appPort := prompt(reader, "Порт приложения", "8080") appPort := prompt(reader, "Порт приложения", "8080")
fmt.Println("\n--- PostgreSQL 17 ---") fmt.Println("\n--- PostgreSQL 17 (только локально, Docker-сеть) ---")
dbHost := prompt(reader, "Хост БД", "postgres") fmt.Println("Хост БД в Docker: postgres (не localhost)")
dbPort := prompt(reader, "Порт БД", "5432") if os.Getenv("POSTGRES_PASSWORD") != "" {
dbUser := prompt(reader, "Пользователь БД", "vpnpanel") fmt.Printf("Пароль из окружения/.env: задан (пользователь %s)\n", envOr("POSTGRES_USER", "vpnpanel"))
dbPass := promptSecret("Пароль БД") }
dbName := prompt(reader, "Имя базы данных", "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--- Администратор (единственный) ---") fmt.Println("\n--- Администратор (единственный) ---")
adminEmail := prompt(reader, "Email администратора", "admin@localhost") adminEmail := prompt(reader, "Email администратора", "admin@localhost")
@@ -78,7 +87,7 @@ func main() {
pool, err := database.Connect(ctx, databaseURL) pool, err := database.Connect(ctx, databaseURL)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "не удалось подключиться: %v\n", err) fmt.Fprintf(os.Stderr, "не удалось подключиться: %v\n", err)
fmt.Println("Запустите PostgreSQL (docker compose up -d postgres) и повторите установку.") printDBHelp(dbPassDefault)
os.Exit(1) os.Exit(1)
} }
defer pool.Close() defer pool.Close()
@@ -114,7 +123,7 @@ DATABASE_URL=%s
SECRET_KEY=%s SECRET_KEY=%s
INSTALLED=true INSTALLED=true
# PostgreSQL (для docker-compose) # PostgreSQL (docker-compose, только внутренняя сеть)
POSTGRES_USER=%s POSTGRES_USER=%s
POSTGRES_PASSWORD=%s POSTGRES_PASSWORD=%s
POSTGRES_DB=%s POSTGRES_DB=%s
@@ -135,13 +144,33 @@ POSTGRES_PORT=%s
fmt.Println(" ✓ Файл .env создан") fmt.Println(" ✓ Файл .env создан")
fmt.Println() fmt.Println()
fmt.Println(" Дальше:") fmt.Println(" Дальше:")
fmt.Println(" docker compose up -d") fmt.Println(" docker compose up -d --build panel")
fmt.Println(" или: go run ./cmd/panel")
fmt.Println() fmt.Println()
fmt.Printf(" Панель: http://%s:%s\n", domain, appPort) fmt.Printf(" Панель: http://%s:%s\n", domain, appPort)
fmt.Println() 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 { func prompt(reader *bufio.Reader, label, defaultVal string) string {
if defaultVal != "" { if defaultVal != "" {
fmt.Printf("%s [%s]: ", label, defaultVal) fmt.Printf("%s [%s]: ", label, defaultVal)
@@ -166,6 +195,16 @@ func promptSecret(label string) string {
return string(b) 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) { func generateSecret() (string, error) {
b := make([]byte, 32) b := make([]byte, 32)
if _, err := rand.Read(b); err != nil { if _, err := rand.Read(b); err != nil {
+9 -2
View File
@@ -9,8 +9,9 @@ services:
POSTGRES_DB: ${POSTGRES_DB:-vpnpanel} POSTGRES_DB: ${POSTGRES_DB:-vpnpanel}
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data
ports: # БД только внутри Docker-сети, с интернета недоступна
- "5432:5432" expose:
- "5432"
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-vpnpanel} -d ${POSTGRES_DB:-vpnpanel}"] test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-vpnpanel} -d ${POSTGRES_DB:-vpnpanel}"]
interval: 5s interval: 5s
@@ -26,6 +27,12 @@ services:
volumes: volumes:
- .:/work - .:/work
entrypoint: ["/app/install"] 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: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
+39 -15
View File
@@ -1,30 +1,54 @@
#!/bin/bash #!/bin/bash
# Установка без Go — только Docker # Установка без Go — только Docker. БД только внутри Docker-сети.
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
echo "=== VPN Panel — установка (Docker) ===" echo "=== VPN Panel — установка (Docker) ==="
# Если .env нет — создаём с паролем ДО первого запуска postgres (том получит правильный пароль)
if [ ! -f .env ]; then if [ ! -f .env ]; then
echo "Файл .env не найден — запускаем установщик..." PG_PASS="${POSTGRES_PASSWORD:-$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)}"
docker compose up -d postgres SECRET="${SECRET_KEY:-$(openssl rand -base64 32)}"
echo "Ожидание PostgreSQL..." cat > .env <<EOF
for i in $(seq 1 30); do # Создано scripts/setup.sh — отредактируйте при необходимости
if docker compose exec -T postgres pg_isready -U "${POSTGRES_USER:-vpnpanel}" -d "${POSTGRES_DB:-vpnpanel}" >/dev/null 2>&1; then APP_PORT=8080
break APP_DOMAIN=localhost
fi POSTGRES_USER=vpnpanel
sleep 2 POSTGRES_PASSWORD=${PG_PASS}
done POSTGRES_DB=vpnpanel
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
DATABASE_URL=postgres://vpnpanel:${PG_PASS}@postgres:5432/vpnpanel?sslmode=disable
SECRET_KEY=${SECRET}
INSTALLED=false
EOF
chmod 600 .env
echo "Создан .env с новым паролем PostgreSQL (сохраните файл)."
fi
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
if grep -q '^INSTALLED=true' .env 2>/dev/null; then
echo ".env уже помечен INSTALLED=true — пропускаем установщик."
else
docker compose build install docker compose build install
docker compose --profile tools run --rm install docker compose --profile tools run --rm install
else
echo "Найден .env — пропускаем установщик."
docker compose up -d postgres
fi fi
echo "Запуск панели..." echo "Запуск панели..."
docker compose up -d --build panel 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 ""
echo "Готово. Панель: http://${APP_DOMAIN:-localhost}:${APP_PORT:-8080}" echo "Готово. Панель: http://${APP_DOMAIN}:${APP_PORT}"
echo "Проверка: curl -s http://127.0.0.1:${APP_PORT:-8080}/health" echo "Проверка: curl -s http://127.0.0.1:${APP_PORT}/health"
echo "PostgreSQL: только внутри Docker (порт 5432 снаружи закрыт)."