magos/config.go

226 lines
8.1 KiB
Go
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"database/sql"
"fmt"
"log"
"net"
"os"
"strconv"
"github.com/joho/godotenv"
"github.com/lib/pq"
)
// Константы для graceful shutdown
const (
// Время ожидания перед удалением контейнера при масштабировании вниз (в секундах)
// Должно быть больше чем keepalive_timeout в nginx (обычно 60-75 секунд)
ContainerDrainTimeout = 90
// Время ожидания перед удалением контейнера при миграции между хостами (в секундах)
// Увеличенное время для безопасного переключения трафика на новый хост
MigrationDrainTimeout = 3600 // 1 час
)
// Константы диапазонов портов для Docker
const (
HTTPPortStart = 18000 // Начало диапазона HTTP портов
HTTPPortEnd = 20999 // Конец диапазона HTTP портов (3000 портов)
HTTPSPortStart = 21000 // Начало диапазона HTTPS портов
HTTPSPortEnd = 23999 // Конец диапазона HTTPS портов (3000 портов)
)
// Пути к файлам конфигурации
const (
EnvFilePath = "/etc/magos/magos.env"
)
// Константы конфигурации по умолчанию (используются как fallback если env не загружен)
const (
DefaultDBName = "waf_info"
DefaultDBPort = 5432
DefaultPrimaryDBHost = "10.100.10.8"
DefaultSecondaryDBHost = "10.100.13.5"
DefaultDockerImage = "ptaf-core-nginx-agent:release-4.1.6.409465"
DefaultDockerImageURL = ""
DefaultWorkerProcesses = 4
DefaultWorkerConnections = 64000
)
// Конфигурация приложения
type Config struct {
DBUser string
DBPassword string
DBName string
DBPort int
PrimaryDBHost string
SecondaryDBHost string
APIToken string
AntibotNetworks []string
WAFNetworks []string
WorkerProcesses int
WorkerConnections int
DockerImage string
DockerImageURL string // URL для скачивания tar-архива образа
}
// loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env
func loadEnvFile() error {
if err := godotenv.Load(EnvFilePath); err != nil {
return fmt.Errorf("ошибка загрузки %s: %w", EnvFilePath, err)
}
log.Printf("✓ Загружен файл конфигурации: %s", EnvFilePath)
return nil
}
// getEnv возвращает значение переменной окружения или дефолтное значение
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
// getEnvInt возвращает int значение переменной окружения или дефолтное значение
func getEnvInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intVal, err := strconv.Atoi(value); err == nil {
return intVal
}
log.Printf("⚠ Некорректное значение %s=%s, используется дефолт %d", key, value, defaultValue)
}
return defaultValue
}
// getDefaultConfig возвращает конфигурацию из env-файла или дефолтные значения
func getDefaultConfig() Config {
// Пытаемся загрузить env файл (не критично если не получится)
if err := loadEnvFile(); err != nil {
log.Printf("⚠ %v", err)
log.Printf(" Используются дефолтные значения для DB_USER, DB_PASSWORD, API_TOKEN")
}
return Config{
DBUser: getEnv("DB_USER", ""),
DBPassword: getEnv("DB_PASSWORD", ""),
DBName: getEnv("DB_NAME", DefaultDBName),
DBPort: getEnvInt("DB_PORT", DefaultDBPort),
PrimaryDBHost: getEnv("PRIMARY_DB_HOST", DefaultPrimaryDBHost),
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
APIToken: getEnv("API_TOKEN", ""),
AntibotNetworks: []string{
"185.66.85.0/24",
"185.66.86.0/24",
"185.35.5.0/24",
"185.35.6.0/24",
"212.67.26.0/24",
},
WAFNetworks: []string{
"109.238.89.0/24",
"89.20.63.0/24",
},
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
DockerImage: getEnv("DOCKER_IMAGE", DefaultDockerImage),
DockerImageURL: getEnv("DOCKER_IMAGE_URL", DefaultDockerImageURL),
}
}
// initConfig инициализирует конфигурацию, загружая IP списки из БД
func initConfig(db *sql.DB) (Config, error) {
// Читаем IP адреса и сети из таблицы ips
antibotNetworks, wafNetworks, err := loadIPsFromDB(db)
if err != nil {
log.Printf("⚠ Не удалось загрузить IP настройки из БД: %v", err)
log.Printf(" Используются значения по умолчанию")
return getDefaultConfig(), nil
}
// Загружаем env файл для получения credentials
if err := loadEnvFile(); err != nil {
log.Printf("⚠ %v", err)
log.Printf(" Используются дефолтные значения для DB_USER, DB_PASSWORD, API_TOKEN")
}
// Формируем Config с настройками из env + IP из БД
config := Config{
DBUser: getEnv("DB_USER", ""),
DBPassword: getEnv("DB_PASSWORD", ""),
DBName: getEnv("DB_NAME", DefaultDBName),
DBPort: getEnvInt("DB_PORT", DefaultDBPort),
PrimaryDBHost: getEnv("PRIMARY_DB_HOST", DefaultPrimaryDBHost),
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
APIToken: getEnv("API_TOKEN", ""),
DockerImage: getEnv("DOCKER_IMAGE", DefaultDockerImage),
DockerImageURL: getEnv("DOCKER_IMAGE_URL", DefaultDockerImageURL),
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
AntibotNetworks: antibotNetworks,
WAFNetworks: wafNetworks,
}
// Валидация критичных параметров
if config.DBUser == "" {
log.Printf("⚠ DB_USER не установлен в %s", EnvFilePath)
}
if config.DBPassword == "" {
log.Printf("⚠ DB_PASSWORD не установлен в %s", EnvFilePath)
}
if config.APIToken == "" {
log.Printf("⚠ API_TOKEN не установлен в %s", EnvFilePath)
}
log.Printf("✓ IP конфигурация загружена из таблицы ips")
log.Printf(" - Antibot Networks: %d сетей", len(config.AntibotNetworks))
log.Printf(" - WAF Networks: %d сетей", len(config.WAFNetworks))
return config, nil
}
// loadIPsFromDB загружает списки IP адресов и сетей из таблицы ips
func loadIPsFromDB(db *sql.DB) (antibotNetworks []string, wafNetworks []string, err error) {
query := `
SELECT antibot_networks, waf_networks
FROM ips
WHERE id = 1
`
// PostgreSQL массивы читаем через pq.Array
var antibotNetworksArray, wafNetworksArray []string
err = db.QueryRow(query).Scan(
pq.Array(&antibotNetworksArray),
pq.Array(&wafNetworksArray),
)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil, fmt.Errorf("записи с id=1 не найдено в таблице ips")
}
return nil, nil, fmt.Errorf("ошибка чтения из таблицы ips: %w", err)
}
return antibotNetworksArray, wafNetworksArray, nil
}
// isIPInWAFNetworks проверяет, входит ли IP адрес в одну из WAF подсетей
func isIPInWAFNetworks(ipStr string, wafNetworks []string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
for _, cidr := range wafNetworks {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
continue
}
if ipNet.Contains(ip) {
return true
}
}
return false
}