Add SW
This commit is contained in:
parent
50f53b042e
commit
49010457db
5 changed files with 253 additions and 59 deletions
18
config.go
18
config.go
|
|
@ -46,6 +46,7 @@ const (
|
||||||
DefaultPrimaryDBHost = "10.100.10.8"
|
DefaultPrimaryDBHost = "10.100.10.8"
|
||||||
DefaultSecondaryDBHost = "10.100.13.5"
|
DefaultSecondaryDBHost = "10.100.13.5"
|
||||||
DefaultDockerImage = "ptaf-core-nginx-agent:release-4.1.6.409465"
|
DefaultDockerImage = "ptaf-core-nginx-agent:release-4.1.6.409465"
|
||||||
|
DefaultDockerImageURL = "" // пустой дефолт — не скачивать если не задан в env
|
||||||
DefaultWorkerProcesses = 4
|
DefaultWorkerProcesses = 4
|
||||||
DefaultWorkerConnections = 64000
|
DefaultWorkerConnections = 64000
|
||||||
)
|
)
|
||||||
|
|
@ -64,6 +65,7 @@ type Config struct {
|
||||||
WorkerProcesses int
|
WorkerProcesses int
|
||||||
WorkerConnections int
|
WorkerConnections int
|
||||||
DockerImage string
|
DockerImage string
|
||||||
|
DockerImageURL string // URL для скачивания tar-архива образа если отсутствует локально
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env
|
// loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env
|
||||||
|
|
@ -111,19 +113,20 @@ func getDefaultConfig() Config {
|
||||||
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
|
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
|
||||||
APIToken: getEnv("API_TOKEN", ""),
|
APIToken: getEnv("API_TOKEN", ""),
|
||||||
AntibotNetworks: []string{
|
AntibotNetworks: []string{
|
||||||
"185.66.85.0/24",
|
"185.66.23.0/24",
|
||||||
"185.66.86.0/24",
|
"185.66.25.0/24",
|
||||||
"185.35.5.0/24",
|
"185.35.26.0/24",
|
||||||
"185.35.6.0/24",
|
"185.35.26.0/24",
|
||||||
"212.67.26.0/24",
|
"212.67.25.0/24",
|
||||||
},
|
},
|
||||||
WAFNetworks: []string{
|
WAFNetworks: []string{
|
||||||
"109.238.89.0/24",
|
"109.238.9.0/24",
|
||||||
"89.20.63.0/24",
|
"89.20.3.0/24",
|
||||||
},
|
},
|
||||||
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
|
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
|
||||||
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
|
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
|
||||||
DockerImage: getEnv("DOCKER_IMAGE", DefaultDockerImage),
|
DockerImage: getEnv("DOCKER_IMAGE", DefaultDockerImage),
|
||||||
|
DockerImageURL: getEnv("DOCKER_IMAGE_URL", DefaultDockerImageURL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +156,7 @@ func initConfig(db *sql.DB) (Config, error) {
|
||||||
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
|
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
|
||||||
APIToken: getEnv("API_TOKEN", ""),
|
APIToken: getEnv("API_TOKEN", ""),
|
||||||
DockerImage: getEnv("DOCKER_IMAGE", DefaultDockerImage),
|
DockerImage: getEnv("DOCKER_IMAGE", DefaultDockerImage),
|
||||||
|
DockerImageURL: getEnv("DOCKER_IMAGE_URL", DefaultDockerImageURL),
|
||||||
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
|
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
|
||||||
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
|
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
|
||||||
AntibotNetworks: antibotNetworks,
|
AntibotNetworks: antibotNetworks,
|
||||||
|
|
|
||||||
4
db.go
4
db.go
|
|
@ -153,7 +153,8 @@ func getAppsSettingsByClientTitle(db *sql.DB, clientTitle string) ([]AppsSetting
|
||||||
ssl_enabled,
|
ssl_enabled,
|
||||||
custom_angie_ssl,
|
custom_angie_ssl,
|
||||||
max_fails,
|
max_fails,
|
||||||
fail_timeout
|
fail_timeout,
|
||||||
|
custom_sw_nginx_ssl
|
||||||
FROM apps_settings
|
FROM apps_settings
|
||||||
WHERE client_title = $1
|
WHERE client_title = $1
|
||||||
ORDER BY l7resourceid ASC
|
ORDER BY l7resourceid ASC
|
||||||
|
|
@ -186,6 +187,7 @@ func getAppsSettingsByClientTitle(db *sql.DB, clientTitle string) ([]AppsSetting
|
||||||
&a.CustomAngieSSL,
|
&a.CustomAngieSSL,
|
||||||
&a.MaxFails,
|
&a.MaxFails,
|
||||||
&a.FailTimeout,
|
&a.FailTimeout,
|
||||||
|
&a.CustomSWNginxSSL,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ошибка сканирования строки: %w", err)
|
return nil, fmt.Errorf("ошибка сканирования строки: %w", err)
|
||||||
|
|
|
||||||
229
main.go
229
main.go
|
|
@ -3,9 +3,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -13,7 +16,7 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// ════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════
|
||||||
// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Lock-файл для предотвращения параллельного запуска
|
// Lock-файл для предотвращения параллельного запуска
|
||||||
// ════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════
|
||||||
lockFile := "/var/lock/ptaf-main.lock"
|
lockFile := "/var/lock/ptaf-main.lock"
|
||||||
|
|
||||||
|
|
@ -25,11 +28,9 @@ func main() {
|
||||||
log.Fatal("❌ Не удалось создать lock файл: ", err)
|
log.Fatal("❌ Не удалось создать lock файл: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Записываем PID в lock файл
|
|
||||||
pid := os.Getpid()
|
pid := os.Getpid()
|
||||||
f.WriteString(fmt.Sprintf("%d\n", pid))
|
f.WriteString(fmt.Sprintf("%d\n", pid))
|
||||||
|
|
||||||
// Гарантированно удаляем lock при выходе
|
|
||||||
defer func() {
|
defer func() {
|
||||||
f.Close()
|
f.Close()
|
||||||
os.Remove(lockFile)
|
os.Remove(lockFile)
|
||||||
|
|
@ -67,10 +68,19 @@ func main() {
|
||||||
log.Printf("Instances для хоста (%d): %s", len(instanceNames), strings.Join(instanceNames, ", "))
|
log.Printf("Instances для хоста (%d): %s", len(instanceNames), strings.Join(instanceNames, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверка наличия Docker образа только если на хосте есть PTAF-клиенты
|
||||||
|
if hasPTAFClients(db, instanceNames) {
|
||||||
|
if err := ensureDockerImageExists(config); err != nil {
|
||||||
|
log.Fatalf("❌ Ошибка подготовки Docker образа: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("ℹ PTAF-клиентов на хосте нет, проверка Docker образа пропускается")
|
||||||
|
}
|
||||||
|
|
||||||
// Глобальная обработка drain-контейнеров и миграций
|
// Глобальная обработка drain-контейнеров и миграций
|
||||||
processAllDrainingContainersGlobally(db, hostname)
|
processAllDrainingContainersGlobally(db, hostname)
|
||||||
|
|
||||||
// Аллокатор портов (заменяет глобальную переменную)
|
// Аллокатор портов
|
||||||
portAllocator := NewPortAllocator()
|
portAllocator := NewPortAllocator()
|
||||||
|
|
||||||
totalClients := 0
|
totalClients := 0
|
||||||
|
|
@ -92,6 +102,112 @@ func main() {
|
||||||
log.Println("\n=== Программа завершена успешно ===")
|
log.Println("\n=== Программа завершена успешно ===")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasPTAFClients проверяет есть ли хотя бы один клиент с waf_vendor = 'ptaf'
|
||||||
|
// среди всех клиентов на данном хосте (по списку instances)
|
||||||
|
func hasPTAFClients(db *sql.DB, instanceNames []string) bool {
|
||||||
|
for _, instanceName := range instanceNames {
|
||||||
|
clientInfoList, err := getClientInfoByInstance(db, instanceName)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, clientInfo := range clientInfoList {
|
||||||
|
appsSettingsList, err := getAppsSettingsByClientTitle(db, clientInfo.ClientTitle)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if determineVendor(appsSettingsList) == "ptaf" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureDockerImageExists проверяет наличие Docker образа локально.
|
||||||
|
// Если образ отсутствует — скачивает tar-архив по DOCKER_IMAGE_URL и загружает через docker load.
|
||||||
|
func ensureDockerImageExists(config Config) error {
|
||||||
|
if config.DockerImage == "" {
|
||||||
|
log.Printf("⚠ DOCKER_IMAGE не задан, пропускаем проверку образа")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем есть ли образ локально
|
||||||
|
cmd := exec.Command("docker", "image", "inspect", config.DockerImage)
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
log.Printf("✓ Docker образ уже присутствует: %s", config.DockerImage)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(" Docker образ не найден локально: %s", config.DockerImage)
|
||||||
|
|
||||||
|
if config.DockerImageURL == "" {
|
||||||
|
log.Printf("⚠ Docker образ %s отсутствует локально, DOCKER_IMAGE_URL не задан — пропускаем", config.DockerImage)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Путь для tar-архива: /home/install/ptaf-core-nginx-agent_release-X.X.X.tar
|
||||||
|
safeName := strings.ReplaceAll(config.DockerImage, ":", "_")
|
||||||
|
safeName = strings.ReplaceAll(safeName, "/", "_")
|
||||||
|
tarPath := fmt.Sprintf("/home/install/%s.tar", safeName)
|
||||||
|
|
||||||
|
// Скачиваем архив если ещё не скачан
|
||||||
|
if _, err := os.Stat(tarPath); os.IsNotExist(err) {
|
||||||
|
log.Printf(" → Скачивание образа с %s", config.DockerImageURL)
|
||||||
|
log.Printf(" → Сохранение в %s", tarPath)
|
||||||
|
if err := downloadFile(tarPath, config.DockerImageURL); err != nil {
|
||||||
|
return fmt.Errorf("ошибка скачивания образа: %w", err)
|
||||||
|
}
|
||||||
|
log.Printf(" ✓ Архив образа скачан: %s", tarPath)
|
||||||
|
} else {
|
||||||
|
log.Printf(" → Архив уже существует локально: %s", tarPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем образ в Docker
|
||||||
|
log.Printf(" → Загрузка образа в Docker: docker load -i %s", tarPath)
|
||||||
|
loadCmd := exec.Command("docker", "load", "-i", tarPath)
|
||||||
|
output, err := loadCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ошибка docker load: %w\n%s", err, string(output))
|
||||||
|
}
|
||||||
|
log.Printf(" ✓ Образ успешно загружен: %s", config.DockerImage)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadFile скачивает файл по URL и сохраняет в destPath.
|
||||||
|
// Использует общий apiClient с таймаутом 120 сек (для больших tar-архивов может быть мало —
|
||||||
|
// при необходимости увеличь таймаут в apiClient или создай отдельный клиент).
|
||||||
|
func downloadFile(destPath, url string) error {
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Minute, // образы могут быть большими
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("HTTP %d при скачивании %s", resp.StatusCode, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(destPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ошибка создания файла %s: %w", destPath, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
written, err := io.Copy(f, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(destPath) // удаляем неполный файл
|
||||||
|
return fmt.Errorf("ошибка записи файла: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(" → Скачано: %.1f МБ", float64(written)/1024/1024)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// processInstance обрабатывает один instance и возвращает количество обработанных клиентов
|
// processInstance обрабатывает один instance и возвращает количество обработанных клиентов
|
||||||
func processInstance(db *sql.DB, config Config, hostname, instanceName string, portAllocator *PortAllocator) int {
|
func processInstance(db *sql.DB, config Config, hostname, instanceName string, portAllocator *PortAllocator) int {
|
||||||
log.Printf("\n=== Обработка instance: %s ===", instanceName)
|
log.Printf("\n=== Обработка instance: %s ===", instanceName)
|
||||||
|
|
@ -117,8 +233,8 @@ func processInstance(db *sql.DB, config Config, hostname, instanceName string, p
|
||||||
return len(clientInfoList)
|
return len(clientInfoList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// processClient обрабатывает одного клиента. Возвращает true если клиент был обработан.
|
// processClient обрабатывает одного клиента.
|
||||||
// Роутер по waf_vendor: направляет клиента в PTAF или SW процессор
|
// Роутер по waf_vendor: направляет клиента в PTAF или SW процессор.
|
||||||
func processClient(db *sql.DB, config Config, hostname, instanceName string, clientInfo ClientInfo, portAllocator *PortAllocator) bool {
|
func processClient(db *sql.DB, config Config, hostname, instanceName string, clientInfo ClientInfo, portAllocator *PortAllocator) bool {
|
||||||
log.Printf("\n--- Обработка клиента: %s (instance: %s) ---", clientInfo.ClientTitle, instanceName)
|
log.Printf("\n--- Обработка клиента: %s (instance: %s) ---", clientInfo.ClientTitle, instanceName)
|
||||||
|
|
||||||
|
|
@ -151,8 +267,8 @@ func processClient(db *sql.DB, config Config, hostname, instanceName string, cli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// determineVendor определяет vendor из списка apps_settings
|
// determineVendor определяет vendor из списка apps_settings.
|
||||||
// Все ресурсы клиента должны иметь одинаковый waf_vendor
|
// Все ресурсы клиента должны иметь одинаковый waf_vendor.
|
||||||
func determineVendor(appsSettingsList []AppsSettings) string {
|
func determineVendor(appsSettingsList []AppsSettings) string {
|
||||||
for _, apps := range appsSettingsList {
|
for _, apps := range appsSettingsList {
|
||||||
if apps.WAFVendor.Valid && apps.WAFVendor.String != "" {
|
if apps.WAFVendor.Valid && apps.WAFVendor.String != "" {
|
||||||
|
|
@ -165,11 +281,6 @@ func determineVendor(appsSettingsList []AppsSettings) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// REMOVED: старая логика processClient перенесена в ptaf/ptaf_processor.go
|
|
||||||
// Ниже идут вспомогательные функции которые используются обоими vendor
|
|
||||||
|
|
||||||
// processClient_REMOVED_PLACEHOLDER - вся логика обработки теперь в vendor-specific модулях
|
|
||||||
// Этот блок оставлен для ясности что код был перенесён, не удалён
|
|
||||||
// handleClientMigration обрабатывает миграцию клиента с текущего хоста
|
// handleClientMigration обрабатывает миграцию клиента с текущего хоста
|
||||||
func handleClientMigration(clientInfo ClientInfo, hostname string) {
|
func handleClientMigration(clientInfo ClientInfo, hostname string) {
|
||||||
log.Printf("⚠ Клиент %s должен быть на instance '%s', текущий хост '%s' не принадлежит этому instance",
|
log.Printf("⚠ Клиент %s должен быть на instance '%s', текущий хост '%s' не принадлежит этому instance",
|
||||||
|
|
@ -247,45 +358,81 @@ func cleanStaleDrainMarkers(clientInfo ClientInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchResourcesData REMOVED: логика перенесена в vendor-specific модули
|
// resolvePortsForClient загружает сохранённые порты или выделяет новые.
|
||||||
// - PTAF: ptaf/ptaf_processor.go → fetchPTAFResourcesData()
|
// При увеличении ContainersCount сохраняет порты существующих контейнеров
|
||||||
// - SW: sw/sw_processor.go → fetchSWResourcesData() (TODO)
|
// и доделывает только новые, не трогая уже работающие.
|
||||||
|
|
||||||
// resolvePortsForClient загружает сохранённые порты или выделяет новые
|
|
||||||
func resolvePortsForClient(clientInfo ClientInfo, resourcesData []ResourceData, portAllocator *PortAllocator) (map[int][]PortMapping, error) {
|
func resolvePortsForClient(clientInfo ClientInfo, resourcesData []ResourceData, portAllocator *PortAllocator) (map[int][]PortMapping, error) {
|
||||||
log.Printf("Чтение сохранённых портов для %s", clientInfo.ClientTitle)
|
log.Printf("Чтение сохранённых портов для %s", clientInfo.ClientTitle)
|
||||||
savedPortMap := make(map[int][]PortMapping)
|
|
||||||
allPortsFound := true
|
// Инициализируем итоговый map с нужным количеством слотов
|
||||||
|
resourcePortMap := make(map[int][]PortMapping)
|
||||||
|
for _, res := range resourcesData {
|
||||||
|
resourcePortMap[res.L7ResourceID] = make([]PortMapping, clientInfo.ContainersCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Собираем уже занятые порты из существующих контейнеров
|
||||||
|
existingPorts := make(map[int]bool)
|
||||||
|
lastLoadedContainer := 0
|
||||||
|
|
||||||
for containerNum := 1; containerNum <= clientInfo.ContainersCount; containerNum++ {
|
for containerNum := 1; containerNum <= clientInfo.ContainersCount; containerNum++ {
|
||||||
containerName := fmt.Sprintf("%s-ptaf-agent%03d", clientInfo.ClientTitle, containerNum)
|
containerName := fmt.Sprintf("%s-ptaf-agent%03d", clientInfo.ClientTitle, containerNum)
|
||||||
containerDir := filepath.Join("/home/install/ptaf", clientInfo.ClientTitle, containerName)
|
containerDir := filepath.Join("/home/install/ptaf", clientInfo.ClientTitle, containerName)
|
||||||
|
|
||||||
if portMappings, found := loadContainerPorts(containerDir, resourcesData); found {
|
portMappings, found := loadContainerPorts(containerDir, resourcesData)
|
||||||
if containerNum == 1 {
|
if !found {
|
||||||
for _, res := range resourcesData {
|
log.Printf(" → Файл портов для контейнера %d не найден — будут выделены новые порты", containerNum)
|
||||||
savedPortMap[res.L7ResourceID] = make([]PortMapping, clientInfo.ContainersCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for idx, res := range resourcesData {
|
|
||||||
if idx < len(portMappings) {
|
|
||||||
savedPortMap[res.L7ResourceID][containerNum-1] = portMappings[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Printf(" → Файл портов для контейнера %d не найден или невалиден", containerNum)
|
|
||||||
allPortsFound = false
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сохраняем порты этого контейнера
|
||||||
|
for idx, res := range resourcesData {
|
||||||
|
if idx < len(portMappings) {
|
||||||
|
resourcePortMap[res.L7ResourceID][containerNum-1] = portMappings[idx]
|
||||||
|
// Помечаем порты как занятые
|
||||||
|
for _, dockerPort := range portMappings[idx].HTTPPorts {
|
||||||
|
existingPorts[dockerPort] = true
|
||||||
|
portAllocator.markAsUsed(dockerPort)
|
||||||
|
}
|
||||||
|
for _, dockerPort := range portMappings[idx].HTTPSPorts {
|
||||||
|
existingPorts[dockerPort] = true
|
||||||
|
portAllocator.markAsUsed(dockerPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastLoadedContainer = containerNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Все контейнеры загружены из файлов — ничего дополнительно выделять не нужно
|
||||||
|
if lastLoadedContainer == clientInfo.ContainersCount {
|
||||||
|
log.Printf(" → Используются сохранённые порты из .ports.json файлов (%d контейнеров)", clientInfo.ContainersCount)
|
||||||
|
return resourcePortMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нужно выделить порты для контейнеров с lastLoadedContainer+1 по ContainersCount
|
||||||
|
newContainersCount := clientInfo.ContainersCount - lastLoadedContainer
|
||||||
|
if lastLoadedContainer == 0 {
|
||||||
|
// Ни одного файла не найдено — выделяем всё заново
|
||||||
|
log.Printf(" → Выделение новых портов для %d ресурсов × %d контейнеров", len(resourcesData), clientInfo.ContainersCount)
|
||||||
|
return portAllocator.allocatePortsForResources(resourcesData, clientInfo.ContainersCount, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(" → Сохранены порты для %d существующих контейнеров, выделяю порты для %d новых (контейнеры %d-%d)",
|
||||||
|
lastLoadedContainer, newContainersCount, lastLoadedContainer+1, clientInfo.ContainersCount)
|
||||||
|
|
||||||
|
newPortMap, err := portAllocator.allocatePortsForResources(resourcesData, newContainersCount, existingPorts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Объединяем: существующие порты + новые
|
||||||
|
for _, res := range resourcesData {
|
||||||
|
newMappings := newPortMap[res.L7ResourceID]
|
||||||
|
for i, mapping := range newMappings {
|
||||||
|
resourcePortMap[res.L7ResourceID][lastLoadedContainer+i] = mapping
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if allPortsFound && len(savedPortMap) > 0 {
|
return resourcePortMap, nil
|
||||||
log.Printf(" → Используются сохранённые порты из .ports.json файлов (%d контейнеров)", clientInfo.ContainersCount)
|
|
||||||
return savedPortMap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf(" → Выделение новых портов для %d ресурсов × %d контейнеров", len(resourcesData), clientInfo.ContainersCount)
|
|
||||||
return portAllocator.allocatePortsForResources(resourcesData, clientInfo.ContainersCount, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkPortListening проверяет что порт слушает на указанном адресе
|
// checkPortListening проверяет что порт слушает на указанном адресе
|
||||||
|
|
|
||||||
|
|
@ -496,16 +496,30 @@ func writeSWPublicHTTPServer(f *os.File, config Config, res ResourceData, port i
|
||||||
|
|
||||||
// writeSWPublicHTTPSServer пишет публичный HTTPS server блок
|
// writeSWPublicHTTPSServer пишет публичный HTTPS server блок
|
||||||
func writeSWPublicHTTPSServer(f *os.File, config Config, res ResourceData, port int, domainsStr string, serverDirectives string, locationDirectives string, isFullLocationBlock bool) {
|
func writeSWPublicHTTPSServer(f *os.File, config Config, res ResourceData, port int, domainsStr string, serverDirectives string, locationDirectives string, isFullLocationBlock bool) {
|
||||||
|
sslEnabled := true
|
||||||
|
if res.AppsSettings.SSLEnabled.Valid {
|
||||||
|
sslEnabled = res.AppsSettings.SSLEnabled.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
if sslEnabled {
|
||||||
|
log.Printf(" └─ SW HTTPS порт %d: SSL включен", port)
|
||||||
|
} else {
|
||||||
|
log.Printf(" └─ SW HTTPS порт %d: SSL отключен (plain HTTP)", port)
|
||||||
|
}
|
||||||
|
|
||||||
f.WriteString("# Proxying HTTPS requests to SolidWall WAF\n")
|
f.WriteString("# Proxying HTTPS requests to SolidWall WAF\n")
|
||||||
f.WriteString("server {\n")
|
f.WriteString("server {\n")
|
||||||
f.WriteString(" include static/server.conf;\n")
|
f.WriteString(" include static/server.conf;\n")
|
||||||
f.WriteString(fmt.Sprintf(" listen %d ssl;\n", port))
|
if sslEnabled {
|
||||||
|
f.WriteString(fmt.Sprintf(" listen %d ssl;\n", port))
|
||||||
|
} else {
|
||||||
|
f.WriteString(fmt.Sprintf(" listen %d;\n", port))
|
||||||
|
}
|
||||||
f.WriteString(fmt.Sprintf(" server_name %s;\n\n", domainsStr))
|
f.WriteString(fmt.Sprintf(" server_name %s;\n\n", domainsStr))
|
||||||
|
|
||||||
f.WriteString(fmt.Sprintf(" ssl_certificate %s;\n", SWSSLCertPath))
|
if sslEnabled {
|
||||||
f.WriteString(fmt.Sprintf(" ssl_certificate_key %s;\n", SWSSLKeyPath))
|
writeSWSSLSettings(f, res.AppsSettings.CustomSWNginxSSL)
|
||||||
f.WriteString(" ssl_protocols TLSv1.3 TLSv1.2;\n")
|
}
|
||||||
f.WriteString(" ssl_prefer_server_ciphers on;\n\n")
|
|
||||||
|
|
||||||
writeSWRealIPConfig(f, config)
|
writeSWRealIPConfig(f, config)
|
||||||
writeSWStandardSettings(f, res.AppsSettings)
|
writeSWStandardSettings(f, res.AppsSettings)
|
||||||
|
|
@ -594,16 +608,22 @@ func generateSWCustomServerBlock(f *os.File, config Config, res ResourceData, bl
|
||||||
}
|
}
|
||||||
log.Printf(" └─ SW SERVER_BLOCK: порт %d, server_name %s%s", block.Port, block.ServerName, aliasInfo)
|
log.Printf(" └─ SW SERVER_BLOCK: порт %d, server_name %s%s", block.Port, block.ServerName, aliasInfo)
|
||||||
|
|
||||||
|
sslEnabled := true
|
||||||
|
if res.AppsSettings.SSLEnabled.Valid {
|
||||||
|
sslEnabled = res.AppsSettings.SSLEnabled.Bool
|
||||||
|
}
|
||||||
|
|
||||||
f.WriteString("# Custom SERVER_BLOCK for SW\n")
|
f.WriteString("# Custom SERVER_BLOCK for SW\n")
|
||||||
f.WriteString("server {\n")
|
f.WriteString("server {\n")
|
||||||
f.WriteString(" include static/server.conf;\n")
|
f.WriteString(" include static/server.conf;\n")
|
||||||
|
|
||||||
if block.IsHTTPS {
|
if block.IsHTTPS {
|
||||||
f.WriteString(fmt.Sprintf(" listen %d ssl;\n", block.Port))
|
if sslEnabled {
|
||||||
f.WriteString(fmt.Sprintf(" ssl_certificate %s;\n", SWSSLCertPath))
|
f.WriteString(fmt.Sprintf(" listen %d ssl;\n", block.Port))
|
||||||
f.WriteString(fmt.Sprintf(" ssl_certificate_key %s;\n", SWSSLKeyPath))
|
writeSWSSLSettings(f, res.AppsSettings.CustomSWNginxSSL)
|
||||||
f.WriteString(" ssl_protocols TLSv1.3 TLSv1.2;\n")
|
} else {
|
||||||
f.WriteString(" ssl_prefer_server_ciphers on;\n\n")
|
f.WriteString(fmt.Sprintf(" listen %d;\n", block.Port))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
f.WriteString(fmt.Sprintf(" listen %d;\n", block.Port))
|
f.WriteString(fmt.Sprintf(" listen %d;\n", block.Port))
|
||||||
}
|
}
|
||||||
|
|
@ -652,6 +672,26 @@ func generateSWCustomServerBlock(f *os.File, config Config, res ResourceData, bl
|
||||||
f.WriteString("}\n\n")
|
f.WriteString("}\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeSWSSLSettings записывает SSL настройки для SW nginx
|
||||||
|
func writeSWSSLSettings(f *os.File, customSSL sql.NullString) {
|
||||||
|
if customSSL.Valid && strings.TrimSpace(customSSL.String) != "" {
|
||||||
|
log.Printf(" └─ Использование custom_sw_nginx_ssl (кастомные SSL настройки)")
|
||||||
|
lines := strings.Split(strings.TrimSpace(customSSL.String), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
if trimmed != "" {
|
||||||
|
f.WriteString(" " + trimmed + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.WriteString("\n")
|
||||||
|
} else {
|
||||||
|
f.WriteString(fmt.Sprintf(" ssl_certificate %s;\n", SWSSLCertPath))
|
||||||
|
f.WriteString(fmt.Sprintf(" ssl_certificate_key %s;\n", SWSSLKeyPath))
|
||||||
|
f.WriteString(" ssl_protocols TLSv1.3 TLSv1.2;\n")
|
||||||
|
f.WriteString(" ssl_prefer_server_ciphers on;\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// writeSWRealIPConfig пишет real_ip конфигурацию (antibot networks)
|
// writeSWRealIPConfig пишет real_ip конфигурацию (antibot networks)
|
||||||
func writeSWRealIPConfig(f *os.File, config Config) {
|
func writeSWRealIPConfig(f *os.File, config Config) {
|
||||||
f.WriteString(" # antibot networks\n")
|
f.WriteString(" # antibot networks\n")
|
||||||
|
|
|
||||||
1
types.go
1
types.go
|
|
@ -36,6 +36,7 @@ type AppsSettings struct {
|
||||||
CustomAngieSSL sql.NullString // Кастомные SSL директивы для Angie - полностью заменяют дефолтные если заполнено
|
CustomAngieSSL sql.NullString // Кастомные SSL директивы для Angie - полностью заменяют дефолтные если заполнено
|
||||||
MaxFails sql.NullInt64 // Количество неудачных попыток к origin (дефолт 5)
|
MaxFails sql.NullInt64 // Количество неудачных попыток к origin (дефолт 5)
|
||||||
FailTimeout sql.NullInt64 // Время в секундах после которого origin снова доступен (дефолт 15)
|
FailTimeout sql.NullInt64 // Время в секундах после которого origin снова доступен (дефолт 15)
|
||||||
|
CustomSWNginxSSL sql.NullString // Кастомные SSL директивы для SW nginx - полностью заменяют дефолтные если заполнено
|
||||||
}
|
}
|
||||||
|
|
||||||
// Структура для таблицы client_info (бывшая nodes)
|
// Структура для таблицы client_info (бывшая nodes)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue