From f2de5347347787c61e26d964000aaaf19eb1ac02 Mon Sep 17 00:00:00 2001 From: Magnus Root Date: Thu, 16 Apr 2026 15:21:59 +0300 Subject: [PATCH] Renew docs --- ARCHITECTURE.md | 1218 ++++++++++++++------------------------------ DATABASE_SCHEMA.md | 954 ++++++++++++---------------------- README.md | 90 +++- 3 files changed, 764 insertions(+), 1498 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7f1bc34..4aa750f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -6,17 +6,20 @@ - Автоматически разворачивает и управляет Docker-контейнерами с Nginx - Настраивает балансировщик Angie для распределения трафика -- Интегрируется с API для получения конфигурации +- Интегрируется с API ServicePipe для получения конфигурации - Поддерживает graceful shutdown при масштабировании и миграции - Обеспечивает изоляцию клиентов через отдельные контейнеры +- Поддерживает два WAF-вендора: **PTAF** (Docker-контейнеры) и **SW/SolidWall** (нативный процесс) ### Ключевые особенности: - **Многоклиентовая архитектура** - каждый клиент имеет свои контейнеры -- **Динамическое управление портами** - автоматическое выделение портов из пулов +- **Поддержка двух вендоров** - PTAF (Docker) и SolidWall (нативный nginx) +- **Динамическое управление портами** - автоматическое выделение портов из пулов с учётом всех состояний сокетов - **Кэширование данных** - минимизация обращений к API - **Failover БД** - автоматическое переключение между primary/secondary - **Graceful draining** - безопасное удаление контейнеров без потери трафика +- **Умный lock-файл** - автоматическая очистка при падении процесса --- @@ -46,10 +49,41 @@ drain.go # Graceful shutdown и миграция ``` nginx.go # Генерация конфигов Nginx и Angie ports.go # Управление портами +ptaf_processor.go # Обработка PTAF-клиентов +sw_processor.go # Обработка SW/SolidWall-клиентов ``` --- +## Поддерживаемые WAF-вендоры + +### PTAF (Positive Technologies Application Firewall) + +**Схема проксирования:** +``` +Клиент → Angie (хост) → Docker-контейнер (PTAF nginx) → Origin +``` + +- Контейнеры поднимаются через docker-compose +- Angie — фронтенд-балансировщик, конфиги в `/etc/angie/http.d/angie-ptaf-{clientTitle}.conf` +- Nginx внутри контейнера — WAF-агент, конфиги в `/home/install/conf/ptaf-nginx/` +- Порты выделяются автоматически из диапазонов 18000-20999 (HTTP) и 21000-23999 (HTTPS) + +### SW / SolidWall + +**Схема проксирования:** +``` +Клиент → solidwall-nginx (WAF, публичный) → solidwall-nginx (внутренний) → Origin +``` + +- Работает без Docker, напрямую на хосте +- Двойное проксирование через внутренние порты 8000 (HTTP) и 9000 (HTTPS) +- Конфиги: `/etc/solidwall-nginx/sites-available/solidwall-{clientTitle}-{l7ResourceID}.conf` +- Симлинки в `sites-enabled/` +- Утилита **не трогает**: `nginx.conf`, директорию `ssl/`, `default.conf` + +--- + ## Архитектура данных ### База данных PostgreSQL @@ -70,15 +104,21 @@ waf-host-02 | instance-c ```sql Поля: -- client_title # Уникальный идентификатор клиента -- containers_count # Количество контейнеров (1-20) -- ptaf_config # Дополнительная конфигурация PTAF -- fluent_bit_port # Порт для FluentBit логирования -- waf_instance # Instance для привязки к хосту +- client_title # Уникальный идентификатор клиента +- containers_count # Количество контейнеров (0 = drain всех) +- ptaf_config # Строка подключения к PTAF-серверу (CONNECTION_STRING) +- fluent_bit_port # Порт для FluentBit логирования +- waf_instance # Instance для привязки к хосту +- docker_image # Docker-образ для контейнеров клиента (переопределяет глобальный) +- docker_image_download # URL для скачивания образа +- debug # Режим отладки (bool, влияет на лог-уровень) +- shm_size # Размер /dev/shm в GB (дефолт: 1) ``` **Важно:** `waf_instance` определяет на каком хосте должен работать клиент +**containers_count = 0:** все контейнеры клиента помечаются для drain, Angie-конфиг удаляется + #### Таблица `apps_settings` Настройки каждого L7 ресурса (домена) @@ -87,30 +127,37 @@ waf-host-02 | instance-c - l7resourceid # ID ресурса из ServicePipe API - client_title # Связь с client_info - waf_enabled # Включен ли WAF +- waf_vendor # 'ptaf' или 'sw' - mode # auto/manual/disabled Порты: -- custom_input_http_ports[] # HTTP порты для прослушивания (Angie) -- custom_input_https_ports[] # HTTPS порты для прослушивания (Angie) +- custom_input_http_ports[] # HTTP порты для прослушивания (Angie/SW) +- custom_input_https_ports[] # HTTPS порты для прослушивания (Angie/SW) - custom_output_http_ports[] # HTTP порты для проксирования к origin - custom_output_https_ports[] # HTTPS порты для проксирования к origin -Кастомизация Nginx: +Кастомизация Nginx (PTAF): - upstream_nginx_custom # Кастомные upstream директивы -- server_nginx_custom # Кастомный server блок +- server_nginx_custom # Кастомный server блок / SERVER_BLOCK - location_nginx_custom # Кастомные location директивы - server_directives_nginx_custom # Дополнительные server директивы Кастомизация Angie: - upstream_angie_custom # Кастомные upstream директивы -- server_angie_custom # Кастомный server блок +- server_angie_custom # Кастомный server блок / SERVER_BLOCK - location_angie_custom # Кастомные location директивы - server_directives_angie_custom # Дополнительные server директивы - custom_angie_ssl # Полная замена SSL директив +- custom_sw_nginx_ssl # Кастомные SSL директивы для SW Специальные настройки: - sni # 0 = keepalive on, 1 = keepalive off - ssl_enabled # true/false (по умолчанию true) +- max_fails # Попыток до пометки сервера недоступным (дефолт 5) +- fail_timeout # Время восстановления сервера (дефолт 15с) +- balancing_method # Метод балансировки: least_conn, ip_hash, random и др. +- proxy_next_upstream_codes # HTTP-коды для proxy_next_upstream (дефолт: 500,502,503,504) +- ptaf_fallback_code # Код ответа ptaf_fallback: число или 'pass' (дефолт: 418) ``` #### Таблица `sp_info` (кэш API данных) @@ -161,15 +208,22 @@ main() → initConfig() → connectToDB() ``` **Шаги:** -1. Подключение к БД (primary → secondary fallback) -2. Загрузка IP сетей из таблицы `ips` -3. Получение hostname текущего сервера -4. Определение instances для этого хоста +1. Создание lock-файла `/var/lock/ptaf-main.lock` с PID (умная проверка — автоматически удаляет если процесс уже не существует) +2. Подключение к БД (primary → secondary fallback) +3. Загрузка IP сетей из таблицы `ips` +4. Получение hostname текущего сервера +5. Определение instances для этого хоста +6. Для PTAF-хостов: проверка/скачивание Docker-образов (по одному на клиента) ### 2. Основной цикл обработки ``` ┌─────────────────────────────────────────┐ +│ Глобальная проверка drain-контейнеров │ +│ (processAllDrainingContainersGlobally) │ +└─────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────┐ │ Для каждого instance на этом хосте │ └─────────────────────────────────────────┘ ↓ @@ -179,21 +233,64 @@ main() → initConfig() → connectToDB() └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ -│ Для каждого клиента: │ -│ 1. Получить apps_settings │ -│ 2. Собрать данные о ресурсах │ -│ 3. Выделить порты │ -│ 4. Сгенерировать конфиги │ -│ 5. Управление контейнерами │ +│ Определить vendor (ptaf/sw) │ +│ → processPTAFClient / processSWClient │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ -│ Глобальная проверка drain-контейнеров │ -│ (processAllDrainingContainersGlobally) │ +│ Финальная проверка drain Angie-конфигов│ +│ (processDrainingAngieConfigs) │ └─────────────────────────────────────────┘ ``` -### 3. Сбор данных о ресурсах +### 3. Обработка PTAF-клиента + +``` +1. Проверка принадлежности хоста к WAF instance + ├─ Не принадлежит → handleClientMigration (drain всех контейнеров + Angie конфига) + └─ Принадлежит → продолжаем + +2. containers_count = 0? + └─ Да → drain всех контейнеров + Angie конфига, выход + +3. cleanStaleDrainMarkers — очистка устаревших маркеров + +4. fetchPTAFResourcesData — получение данных ресурсов из API/кеша + +5. resolvePortsForClient — загрузка/выделение портов + ├─ .ports.json существует → переиспользуем порты + ├─ Scale up → сохраняем старые порты, добавляем новые + └─ Нет файла → выделяем все заново + +6. generateAngieConfigsWithoutReload — генерация Angie конфига + +7. setupContainers — генерация конфигов и управление контейнерами + ├─ Запущен недавно, порты не слушают → пересоздаём + ├─ Compose изменился → пересоздаём + ├─ Конфиг изменился → nginx -s reload + └─ Новый → запускаем (с ожиданием освобождения портов) + +8. Проверка портов → reload Angie если нужно + +9. processDrainingContainers — drain лишних контейнеров +``` + +### 4. Обработка SW-клиента + +``` +1. Проверка принадлежности хоста к WAF instance + └─ Не принадлежит → пропускаем (миграция не реализована для SW) + +2. Создание директорий: sites-available, sites-enabled, /var/log/solidwall + +3. Для каждого ресурса: + - Генерация конфига solidwall-{clientTitle}-{l7ResourceID}.conf + - Создание симлинка в sites-enabled + +4. solidwall-nginx -t + solidwall-nginx -s reload (если конфиги изменились) +``` + +### 5. Сбор данных о ресурсах Для каждого ресурса определяется режим работы через поле `mode`: @@ -237,35 +334,35 @@ const ( ) ``` +**Важно:** На хостах необходимо зарезервировать этот диапазон чтобы ядро не использовало его для исходящих ephemeral-соединений: +```bash +sysctl -w net.ipv4.ip_local_reserved_ports=18000-23999 +``` + ### Алгоритм выделения портов ``` -┌────────────────────────────────────────┐ -│ 1. Получить занятые порты (ss -tuln) │ -└────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ 1. Получить занятые порты (ss -tuanl) │ +│ Включает: LISTEN, ESTAB, TIME-WAIT │ +└────────────────────────────────────────────────┘ ↓ -┌────────────────────────────────────────┐ -│ 2. Загрузить сохраненные порты │ -│ из .ports.json (если есть) │ -└────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ 2. Загрузить сохраненные порты │ +│ из .ports.json (если есть) │ +│ ├─ Scale up → сохранить старые, добавить │ +│ └─ Нет файла → выделить все заново │ +└────────────────────────────────────────────────┘ ↓ -┌────────────────────────────────────────┐ -│ 3. Исключить порты клиента │ -│ (для переиспользования) │ -└────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ 3. Для каждого ресурса × контейнера: │ +│ - Определить нужные порты │ +│ - Выделить свободные Docker порты │ +└────────────────────────────────────────────────┘ ↓ -┌────────────────────────────────────────┐ -│ 4. Для каждого ресурса: │ -│ - Определить нужные порты из │ -│ custom_input_http_ports[] или │ -│ custom_input_https_ports[] │ -│ - Выделить свободные Docker порты │ -│ - Повторить для каждого контейнера │ -└────────────────────────────────────────┘ - ↓ -┌────────────────────────────────────────┐ -│ 5. Сохранить в .ports.json │ -└────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ 4. Сохранить в .ports.json │ +└────────────────────────────────────────────────┘ ``` ### Структура портов @@ -277,26 +374,45 @@ type PortMapping struct { } ``` -**Пример:** -```json -{ - "container_num": 1, - "resources": [ - { - "l7resourceid": 12345, - "http_ports": { - "80": 18000, - "8080": 18001 - }, - "https_ports": { - "443": 21000, - "8443": 21001 - } - } - ] -} +--- + +## Docker-образ + +Образ задаётся на двух уровнях с приоритетом клиента над глобальным: + +| Уровень | Источник | +|---------|----------| +| Глобальный | `DOCKER_IMAGE` в `/etc/magos/magos.env` | +| Клиентский | `docker_image` в таблице `client_info` | + +Если образ отсутствует локально — скачивается tar-архив по URL из `docker_image_download` (или `DOCKER_IMAGE_URL`) и загружается через `docker load`. Одинаковые образы проверяются только один раз за запуск. + +--- + +## Режим отладки (debug) + +Поле `debug` в `client_info`. При `debug = true`: + +**docker-compose.yml:** +```yaml +- PTAF_DEBUG=True +- ERROR_LOG_LEVEL=debug ``` +**nginx.conf:** +```nginx +env PTAF_LOG_LEVEL=DEBUG; +env DEBUG=true; +error_log stderr debug; +``` + +**Angie server-блоки:** +```nginx +client_body_buffer_size 1m; +``` + +**Важно:** включение debug режима вызывает пересоздание контейнера. + --- ## Graceful Shutdown и миграция @@ -305,677 +421,104 @@ type PortMapping struct { #### 1. Масштабирование вниз (Scale Down) **Timeout:** 90 секунд - ``` -Триггер: containers_count уменьшен -Процесс: -1. Контейнер помечается маркером /tmp/ptaf-drain-{name} -2. Ожидание 90 секунд -3. Удаление контейнера +Триггер: containers_count уменьшен в БД +Действие: лишние контейнеры помечаются маркером /tmp/ptaf-drain-ptaf_{client}_{num} +Через 90с: docker-compose down + удаление файлов ``` -#### 2. Миграция между хостами +#### 2. Отключение клиента (containers_count = 0) +**Timeout:** 90 секунд +``` +Триггер: containers_count = 0 +Действие: все контейнеры + Angie-конфиг помечаются для drain +Через 90с: удаление контейнеров и конфига Angie +``` + +#### 3. Миграция между хостами **Timeout:** 3600 секунд (1 час) - ``` -Триггер: waf_instance изменен -Процесс: -1. Старый хост помечает контейнеры маркером (isMigration=true) -2. Новый хост создает контейнеры -3. Через 1 час старый хост удаляет контейнеры -4. Проверка актуальности перед удалением +Триггер: waf_instance изменился в БД +Действие: контейнеры и Angie-конфиг помечаются маркером :migration +Через 1ч: проверка актуальности → удаление если клиент не вернулся ``` -### Структура маркеров - -```bash -# Контейнеры -/tmp/ptaf-drain-ptaf_client-name_001 - -# Angie конфиги -/tmp/ptaf-angie-drain-client-name -``` - -**Формат маркера:** -``` -{timestamp}:{migration_flag} -1738845600:1 # migration -1738845600:0 # scale down -``` - -### Процесс проверки drain +### Формат маркеров ``` -processDrainingContainers() - ↓ -Для каждого контейнера с маркером: - ├─ Прошло времени < timeout? - │ └─ Логировать оставшееся время - └─ Прошло времени >= timeout? - ├─ Migration? - │ ├─ Проверить актуальность (checkHostBelongsToInstance) - │ ├─ Клиент вернулся? → отменить удаление - │ └─ Клиент ушел? → удалить контейнер - └─ Scale down? - └─ Удалить контейнер +/tmp/ptaf-drain-ptaf_{clientTitle}_{001} — контейнер (содержит timestamp или timestamp:migration) +/tmp/ptaf-angie-drain-{clientTitle} — Angie конфиг (аналогичный формат) ``` --- -## Генерация конфигураций +## Кастомизация конфигов через SERVER_BLOCK -### Структура файлов +Формат для полей `server_nginx_custom` и `server_angie_custom`: ``` -/home/install/ptaf/{client-title}/ -├── {client-title}-ptaf-agent001/ -│ ├── docker-compose.yml -│ └── .ports.json -├── {client-title}-ptaf-agent002/ -│ ├── docker-compose.yml -│ └── .ports.json -└── ... - -/home/install/conf/ptaf-nginx/{client-title}/ -├── ptaf-agent001/ -│ ├── nginx.conf -│ ├── mime.types -│ └── conf.d/ -│ ├── {client-title}_12345.conf -│ ├── {client-title}_67890.conf -│ └── ... -└── ... - -/var/log/ptaf_nginx/{client-title}/ -└── ptaf-agent001/ - ├── access.log - └── error.log - -/etc/angie/http.d/ -└── angie-ptaf-{client-title}.conf +[SERVER_BLOCK:443:example.com:www.example.com] +proxy_ssl_certificate /etc/ssl/certs/example.crt; +proxy_ssl_certificate_key /etc/ssl/private/example.key; +proxy_ssl_server_name on; +[/SERVER_BLOCK] ``` -### 1. Генерация nginx.conf +Синтаксис: `[SERVER_BLOCK:{порт}:{server_name}:{alias1}:{alias2}...]` -**Функция:** `generateNginxConf()` - -**Содержимое:** -```nginx -env POD_IP={extracted_or_127.0.0.1}; - -user nginx; -worker_processes {config.WorkerProcesses}; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections {config.WorkerConnections}; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - # Logging - log_format main '$remote_addr - $remote_user [$time_local] ...'; - access_log /var/log/nginx/access.log main; - - # Performance - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - - # PTAF config (если указан) - {ptafConfig} - - # Include resource configs - include /etc/nginx/conf.d/*.conf; -} -``` - -**Особенности:** -- POD_IP извлекается из существующего конфига (для сохранения при обновлении) -- ptafConfig берется из `client_info.ptaf_config` - -### 2. Генерация конфигов ресурсов (Nginx) - -**Функция:** `generateNginxResourceConfig()` - -#### Структура конфига ресурса: - -```nginx -# Upstream блоки для каждого output порта -upstream {clientTitle}_{l7ResourceID}_http_{outputPort} { - least_conn; - {для каждого origin:} - server {origin.IP}:{outputPort} weight={origin.Weight} max_fails=3 fail_timeout=30s; - - # Custom upstream или дефолтные keepalive (если sni != 1) - keepalive 60; - keepalive_timeout 70s; -} - -upstream {clientTitle}_{l7ResourceID}_https_{outputPort} { - least_conn; - {для каждого origin:} - server {origin.IP}:{outputPort} weight={origin.Weight} max_fails=3 fail_timeout=30s; - - # Custom upstream или дефолтные keepalive (если sni != 1) - keepalive 60; - keepalive_timeout 70s; -} - -# Server блоки для каждого input порта -server { - listen {dockerPort}; - server_name {serverName} {aliases с www.}; - - # Custom server directives (если есть) - {server_directives_nginx_custom} - - # Custom location или дефолтный - location / { - proxy_pass http://{upstream_name}; - proxy_http_version 1.1; - proxy_set_header Connection ""; - # ... стандартные proxy заголовки - } -} - -server { - listen {dockerPort} ssl; - server_name {serverName} {aliases с www.}; - - # SSL сертификаты - ssl_certificate /opt/ptaf/ssl/{sanitizedServerName}.crt; - ssl_certificate_key /opt/ptaf/ssl/{sanitizedServerName}.key; - - # Custom server directives (если есть) - {server_directives_nginx_custom} - - # Custom location или дефолтный - location / { - proxy_pass https://{upstream_name}; - proxy_http_version 1.1; - proxy_set_header Connection ""; - # ... стандартные proxy заголовки - } -} - -# Custom SERVER_BLOCK блоки (если есть) -server { - listen {customPort}; - server_name {customServerName} {customAliases}; - {customContent} -} -``` - -#### Логика кастомизации: - -1. **Custom Upstream** (`upstream_nginx_custom`): - - Если заполнено → заменяет keepalive директивы - - Если пусто → используются дефолтные (keepalive 60 + keepalive_timeout 70s) - - Исключение: если `sni = 1`, keepalive отключен - -2. **Custom Server** (`server_nginx_custom`): - - Полная замена всего server блока - - Используется вместо генерации стандартных блоков - -3. **Custom Location** (`location_nginx_custom`): - - Заменяет дефолтный location / - - proxy_pass корректируется автоматически - -4. **Custom Server Directives** (`server_directives_nginx_custom`): - - Добавляются внутрь server блока - - Поддержка [SERVER_BLOCK:PORT:DOMAIN] синтаксиса - -5. **SERVER_BLOCK директивы**: - ``` - [SERVER_BLOCK:8080:example.com:alias1.com:alias2.com] - location / { - proxy_pass http://backend; - } - [/SERVER_BLOCK] - ``` - - Создают отдельные server блоки - - Домены из SERVER_BLOCK исключаются из основных блоков - -### 3. Генерация конфига Angie - -**Функция:** `generateAngieConfig()` - -#### Структура конфига Angie: - -```nginx -# Upstream блоки для контейнеров -upstream angie_{clientTitle}_{l7ResourceID}_http_{customPort} { - least_conn; - {для каждого контейнера:} - server 127.0.0.1:{dockerPort} weight=1 max_fails=3 fail_timeout=30s; - - # Custom upstream или дефолтные keepalive (если sni != 1) - keepalive 60; - keepalive_timeout 70s; -} - -upstream angie_{clientTitle}_{l7ResourceID}_https_{customPort} { - least_conn; - {для каждого контейнера:} - server 127.0.0.1:{dockerPort} weight=1 max_fails=3 fail_timeout=30s; - - # Custom upstream или дефолтные keepalive (если sni != 1) - keepalive 60; - keepalive_timeout 70s; -} - -# Server блоки -server { - listen {customPort}; - server_name {serverName} {aliases с www.}; - - # Custom server directives (если есть) - {server_directives_angie_custom} - - # Custom location или дефолтный - location / { - proxy_pass http://angie_{upstream_name}; - proxy_http_version 1.1; - proxy_set_header Connection ""; - # ... стандартные proxy заголовки - } -} - -server { - listen {customPort} ssl; - server_name {serverName} {aliases с www.}; - - # SSL сертификаты - ssl_certificate /etc/ssl/certs/{sanitizedServerName}.crt; - ssl_certificate_key /etc/ssl/private/{sanitizedServerName}.key; - - # Custom SSL или дефолтный - {если custom_angie_ssl заполнен:} - {custom_angie_ssl} - {иначе:} - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - ssl_prefer_server_ciphers on; - ssl_session_cache shared:SSL:10m; - ssl_session_timeout 10m; - - # Custom server directives (если есть) - {server_directives_angie_custom} - - # Custom location или дефолтный - location / { - proxy_pass http://angie_{upstream_name}; - proxy_http_version 1.1; - proxy_set_header Connection ""; - # ... стандартные proxy заголовки - } -} - -# Custom SERVER_BLOCK блоки (если есть) -server { - listen {customPort}; - server_name {customServerName} {customAliases}; - {customContent} -} -``` - -#### Особенности Angie конфига: - -1. **Балансировка между контейнерами:** - - Upstream указывает на локальные Docker порты (127.0.0.1:18xxx) - - Количество серверов = containers_count - -2. **SSL настройки:** - - `custom_angie_ssl` **полностью заменяет** дефолтные директивы - - Если не заполнено → используются безопасные дефолты - -3. **Санитизация доменов:** - - В Angie: `_` заменяется на `-` - - В Nginx: домены без изменений - - Причина: совместимость и единообразие - -4. **mTLS поддержка:** - - Автоматическая замена путей `/etc/ssl/` → `/opt/ptaf/conf/main/` - - Применяется в custom директивах - -### 4. Генерация docker-compose.yml - -**Функция:** `generateDockerCompose()` - -```yaml -version: '3.8' - -services: - ptaf_{clientTitle}_{containerNum:03d}: - image: {config.DockerImage} - container_name: ptaf_{clientTitle}_{containerNum:03d} - restart: unless-stopped - - ports: - # Для каждого ресурса и каждого custom порта: - - "{dockerPort}:{customPort}" # HTTP - - "{dockerPort}:{customPort}" # HTTPS - - volumes: - - /home/install/conf/ptaf-nginx/{clientTitle}/ptaf-agent{containerNum:03d}:/etc/nginx:ro - - /var/log/ptaf_nginx/{clientTitle}/ptaf-agent{containerNum:03d}:/var/log/nginx - - /home/install/ssl:/opt/ptaf/ssl:ro - - /home/install/mtls:/opt/ptaf/conf/main:ro - - networks: - - ptaf-network - - logging: - driver: "fluentd" - options: - fluentd-address: "127.0.0.1:{fluentBitPort}" - tag: "ptaf.{clientTitle}.agent{containerNum:03d}" - fluentd-async: "true" - fluentd-max-retries: "3" - fluentd-retry-wait: "1s" - -networks: - ptaf-network: - driver: bridge -``` - -**Важно:** -- Порты сортируются для детерминированного вывода -- Volume с конфигами монтируется read-only -- SSL сертификаты общие для всех контейнеров -- FluentBit для централизованного логирования +- Поддерживает произвольное количество алиасов +- Несколько SERVER_BLOCK в одном поле — поддерживается +- Для Angie: SERVER_BLOCK создаёт отдельный server-блок с проксированием на контейнеры +- Для nginx: SERVER_BLOCK создаёт отдельный server-блок с проксированием на origin --- -## API интеграция +## Метод балансировки -### ServicePipe API +Поле `balancing_method` в `apps_settings`. Записывается **без** точки с запятой: -**Base URL:** `https://api.servicepipe.ru/api/v1/` - -#### Endpoints: - -1. **Получение ресурса:** -```http -GET /l7/resource/{l7ResourceID}/global -Authorization: Bearer {token} - -Response: -{ - "data": { - "result": { - "l7ResourceId": 12345, - "l7ResourceName": "example.com", - ... - } - } -} +```sql +UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345; ``` -2. **Получение aliases:** -```http -GET /l7/alias/global?limit=1000&l7ResourceId={l7ResourceID} -Authorization: Bearer {token} +Допустимые значения: `least_conn`, `ip_hash`, `random`, `random two least_conn` и др. -Response: -{ - "data": { - "result": { - "items": [ - { - "id": 1, - "domain": "www.example.com", - "l7ResourceId": 12345, - ... - } - ] - } - } -} -``` - -3. **Получение origins:** -```http -GET /l7/origin/global?limit=1000&l7ResourceId={l7ResourceID} -Authorization: Bearer {token} - -Response: -{ - "data": { - "result": { - "items": [ - { - "id": 1, - "ip": "192.168.1.100", - "weight": 100, - "mode": "active", - ... - } - ] - } - } -} -``` - -### Retry механизм - -```go -maxRetries := 3 -для attempt := 1 до maxRetries: - попытка запроса - если успех: - return результат - если ошибка: - waitTime = attempt² секунд // 1s, 4s, 9s - sleep(waitTime) - -return ошибка -``` - -### Кэширование - -**Стратегия:** -1. Проверка `sp_info` (TTL 10 минут) -2. Если данные свежие → использовать -3. Если устарели или отсутствуют → API запрос + сохранение - -**Преимущества:** -- Снижение нагрузки на API -- Устойчивость к временным сбоям API -- Быстрая работа при повторных запусках +Применяется первой строкой в upstream-блоке для Angie, nginx и SW. --- -## Схема взаимодействия +## proxy_next_upstream -### Полный flow обработки клиента +Поле `proxy_next_upstream_codes` в `apps_settings`. Записывается через запятую **без** `http_` префикса: -``` -┌─────────────────────────────────────────────────────────────┐ -│ НАЧАЛО ОБРАБОТКИ │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 1. Получение данных из БД │ -│ - client_info (containers_count, waf_instance) │ -│ - apps_settings (все ресурсы для client_title) │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 2. Для каждого ресурса (apps_settings): │ -│ ┌──────────────────────────────────────────┐ │ -│ │ mode = disabled? → SKIP │ │ -│ │ mode = manual? → manual_info │ │ -│ │ mode = auto? → sp_info cache или API │ │ -│ └──────────────────────────────────────────┘ │ -│ Результат: ResourceData { │ -│ L7ResourceID, ServerName, Aliases, Origins │ -│ } │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 3. Выделение портов (PortAllocator) │ -│ - Сканирование занятых портов (ss -tuln) │ -│ - Попытка загрузки .ports.json │ -│ - Выделение свободных портов из пулов │ -│ - Сохранение в .ports.json │ -│ │ -│ Результат: map[l7ResourceID][]PortMapping │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 4. Генерация конфигов для каждого контейнера │ -│ Для containerNum := 1 до containers_count: │ -│ ├─ nginx.conf (основной конфиг) │ -│ ├─ mime.types │ -│ └─ conf.d/*.conf (для каждого ресурса) │ -│ │ -│ Отслеживание изменений: containerChanges[containerNum] │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 5. Генерация docker-compose.yml для каждого контейнера │ -│ - Маппинг портов │ -│ - Volumes │ -│ - Logging (FluentBit) │ -│ │ -│ Отслеживание изменений: composeChanges[containerNum] │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 6. Управление контейнерами │ -│ Для containerNum := 1 до containers_count: │ -│ ┌──────────────────────────────────────────┐ │ -│ │ Контейнер существует? │ │ -│ │ ├─ НЕТ → docker-compose up -d │ │ -│ │ └─ ДА: │ │ -│ │ ├─ composeChanges? → recreate │ │ -│ │ ├─ !running? → start │ │ -│ │ └─ containerChanges? │ │ -│ │ ├─ nginx -t (test) │ │ -│ │ └─ nginx -s reload │ │ -│ └──────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 7. Генерация Angie конфига │ -│ /etc/angie/http.d/angie-ptaf-{clientTitle}.conf │ -│ - Upstream для всех контейнеров │ -│ - Server блоки для всех портов │ -│ - SSL настройки │ -│ │ -│ Если изменился → angie -t && angie -s reload │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ 8. Обработка лишних контейнеров (drain) │ -│ Для containerNum := containers_count+1 до MAX: │ -│ ├─ Контейнер существует? │ -│ │ ├─ Уже помечен для drain? │ -│ │ │ └─ Проверить timeout → удалить │ -│ │ └─ Не помечен → пометить для drain │ -│ └─ Контейнер не существует → очистить маркер │ -└─────────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────────┐ -│ ЗАВЕРШЕНИЕ ОБРАБОТКИ │ -└─────────────────────────────────────────────────────────────┘ +```sql +-- Убрать 500 из списка (бэкенд вернул 500, Angie не должен делать retry) +UPDATE apps_settings SET proxy_next_upstream_codes = '502,503,504' WHERE l7resourceid = 12345; ``` -### Схема миграции клиента - -``` -СТАРЫЙ ХОСТ (waf-host-01) НОВЫЙ ХОСТ (waf-host-02) - │ │ - │ waf_instance изменен в БД │ - │ client_title: waf-02 (было: waf-01) │ - ↓ ↓ -┌───────────────────┐ ┌───────────────────┐ -│ Проверка │ │ Проверка │ -│ принадлежности: │ │ принадлежности: │ -│ НЕ принадлежит │ │ Принадлежит │ -└───────────────────┘ └───────────────────┘ - ↓ ↓ -┌───────────────────┐ ┌───────────────────┐ -│ Пометка всех │ │ Создание новых │ -│ контейнеров для │ │ контейнеров │ -│ drain (migration) │ │ ├─ Выделить порты │ -│ timeout: 1 час │ │ ├─ Генерация │ -└───────────────────┘ │ │ конфигов │ - ↓ │ └─ docker-compose │ -┌───────────────────┐ │ up -d │ -│ Пометка Angie │ └───────────────────┘ -│ конфига для │ ↓ -│ удаления │ ┌───────────────────┐ -└───────────────────┘ │ Генерация Angie │ - ↓ │ конфига │ -┌───────────────────┐ │ ├─ angie -t │ -│ Ожидание 1 час... │ │ └─ angie -s │ -│ │ │ reload │ -│ [трафик все еще │ └───────────────────┘ -│ обрабатывается] │ ↓ -└───────────────────┘ ┌───────────────────┐ - ↓ │ Новые контейнеры │ -┌───────────────────┐ │ принимают трафик │ -│ Через 1 час: │ └───────────────────┘ -│ - Проверка │ -│ актуальности │ -│ - Клиент вернулся?│ -│ → ОТМЕНА │ -│ - Клиент ушел? │ -│ → УДАЛЕНИЕ │ -└───────────────────┘ - ↓ -┌───────────────────┐ -│ Удаление: │ -│ ├─ docker-compose │ -│ │ down │ -│ ├─ rm -rf dirs │ -│ └─ rm angie.conf │ -└───────────────────┘ +Дефолт: `500,502,503,504`. Итоговая строка в конфиге: +```nginx +proxy_next_upstream error timeout invalid_header http_502 http_503 http_504; ``` -### Схема масштабирования +--- -``` -containers_count: 3 → 2 (уменьшение) +## Запуск контейнеров -КОНТЕЙНЕР 1 КОНТЕЙНЕР 2 КОНТЕЙНЕР 3 - │ │ │ - │ │ │ - ↓ ↓ ↓ -┌────────┐ ┌────────┐ ┌────────┐ -│ Обновл.│ │ Обновл.│ │ Помечен│ -│ конфиг │ │ конфиг │ │ drain │ -│ и порты│ │ и порты│ │timeout:│ -└────────┘ └────────┘ │ 90 сек │ - │ │ └────────┘ - │ │ │ - ↓ ↓ ↓ -┌────────┐ ┌────────┐ [Ожидание] -│ reload │ │ reload │ [активных] -│ nginx │ │ nginx │ [соединений] -└────────┘ └────────┘ ↓ - │ │ ┌────────┐ - │ │ │ Через │ - ↓ ↓ │ 90 сек │ -[Обрабатывают] [Обрабатывают] └────────┘ - трафик трафик ↓ - ┌────────┐ - │ docker-│ - │ compose│ - │ down │ - └────────┘ - ↓ - ┌────────┐ - │ Удален │ - └────────┘ -``` +### Ожидание освобождения портов + +Перед запуском контейнера утилита проверяет занятость портов через `ss -tuanl` (все состояния включая TIME-WAIT и ESTAB). Порт считается занятым если: +- Есть `LISTEN` сокет +- Есть `ESTAB` от `docker-proxy` (старый контейнер ещё не остановился) + +После остановки контейнера при пересоздании: +1. Ожидаем закрытия ESTAB соединений Angie (до 30 сек) +2. Ожидаем полного освобождения портов (до 180 сек) +3. Запуск нового контейнера (до 3 попыток с паузой 3 сек) + +### Проверка после запуска + +Если контейнер запущен менее 5 минут и порты не слушают — автоматическое пересоздание. --- @@ -986,57 +529,41 @@ containers_count: 3 → 2 (уменьшение) ```go DefaultWorkerProcesses = 4 // Nginx worker processes DefaultWorkerConnections = 64000 // Соединений на worker -``` - -### Ограничения - -```go -MaxContainersPerClient = 20 // Максимум контейнеров на клиента +worker_rlimit_nofile = 1048576 // Лимит файловых дескрипторов ``` ### Timeouts ```go -ContainerDrainTimeout = 90 // Секунд для scale down +ContainerDrainTimeout = 90 // Секунд для scale down / отключения MigrationDrainTimeout = 3600 // Секунд для миграции (1 час) APITimeout = 120 // Секунд для API запросов CacheTTL = 600 // Секунд (10 минут) для sp_info +MaxLockAge = 15 мин // После этого lock считается устаревшим ``` -### Retry +### Буферы (Angie и nginx) -```go -MaxAPIRetries = 3 // Попыток для API запросов -RetryWaitFormula = attempt² // 1s, 4s, 9s +```nginx +proxy_buffers 8 64k; +proxy_buffer_size 64k; +proxy_busy_buffers_size 128k; +large_client_header_buffers 4 128k; ``` +### shm_size + +По умолчанию `1gb`. Управляется через поле `shm_size` в `client_info`. + --- ## Безопасность ### Фильтрация IP адресов -**WAF Networks** - исключаются из origins: -``` -109.238.89.0/24 -89.20.63.0/24 -``` +**WAF Networks** - исключаются из origins (предотвращение routing loops) -Причина: предотвращение routing loops (WAF не должен проксировать на себя) - -### Antibot Networks - -``` -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 -+ -сети заказчиков у которых свой АнтиДДОС -``` - -Используется для идентификации antibot трафика +**Antibot Networks** - используются для идентификации antibot трафика ### SSL/TLS @@ -1049,7 +576,8 @@ ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ``` -**Кастомизация через `custom_angie_ssl`** - полная замена дефолтов +**Кастомизация через `custom_angie_ssl`** — полная замена дефолтов для Angie +**Кастомизация через `custom_sw_nginx_ssl`** — полная замена дефолтов для SW ### mTLS @@ -1061,79 +589,71 @@ ssl_session_timeout 10m; Применяется во всех custom директивах, содержащих `proxy_ssl_certificate` +### Lock-файл + +При старте утилита: +1. Читает PID из существующего lock-файла +2. Если процесс не существует → удаляет устаревший lock и продолжает +3. Если процесс работает > 15 минут → предупреждение и подсказка как убить +4. При завершении → lock удаляется через `defer` + +--- + +## Логирование + +### Структура логов nginx + +``` +/var/log/ptaf_nginx/{client-title}/ptaf-agent001/ +├── {sid}_{domain}_access.log +└── {sid}_{domain}_error.log +``` + +### Формат access лога (JSON) + +```json +{ + "SID": 12345, + "server_name": "example.com", + "request_id": "abc123", + "app_name": "example.com", + "proxyed_to": "192.168.1.1:443", + "upstream_status": "200", + "response_status_code": 200, + "request_time": 0.056 +} +``` + +Поле `request_id` берётся из заголовка `X-Request-ID`. + +### Уровни логирования программы + +``` +✓ — Успешная операция +○ — Информация (без изменений) +⚠ — Предупреждение +→ — Действие/переход +└─ — Детали операции +❌ — Критическая ошибка +ℹ — Информационное сообщение +``` + --- ## Мониторинг и логирование -### Логирование контейнеров +### FluentBit интеграция + +Активируется через поле `fluent_bit_port` в `client_info`: -**FluentBit интеграция:** ```yaml -logging: - driver: "fluentd" - options: - fluentd-address: "127.0.0.1:{fluentBitPort}" - tag: "ptaf.{clientTitle}.agent{containerNum:03d}" - fluentd-async: "true" - fluentd-max-retries: "3" - fluentd-retry-wait: "1s" +environment: + - EVENT_SENDER_ENABLED=1 + - EVENT_SENDER_HOST=172.17.0.1 + - EVENT_SENDER_PORT={fluent_bit_port} + - EVENT_SENDER_TIMEOUT=3 ``` -### Структура логов - -``` -/var/log/ptaf_nginx/{client-title}/ptaf-agent001/ -├── access.log # HTTP access logs -└── error.log # Nginx error logs -``` - -### Логирование программы - -**Уровни:** -- `✓` - Успешная операция -- `○` - Информация (без изменений) -- `⚠` - Предупреждение -- `→` - Действие/переход -- `└─` - Детали операции - -**Примеры:** -``` -✓ nginx.conf изменен: /path/to/nginx.conf -○ nginx.conf не изменился: /path/to/nginx.conf -⚠ Данные в кеше устарели (возраст: 15.3 мин) -→ Запуск нового контейнера client-ptaf-agent001 -└─ Используются кастомные SSL директивы для Angie -``` - ---- - -## Оптимизации - -### 1. Кэширование API данных -- TTL: 10 минут -- Снижение нагрузки на API -- Устойчивость к сбоям - -### 2. Переиспользование портов -- Сохранение в `.ports.json` -- Проверка актуальности (список ресурсов, порядок) -- Минимизация изменений docker-compose - -### 3. Детерминированная генерация -- Хеширование содержимого (SHA256) -- Запись только при изменениях -- Сортировка портов для стабильного вывода - -### 4. Graceful reload -- `nginx -t` перед reload -- Минимизация перезапусков контейнеров -- Изолированный reload (по контейнерам) - -### 5. Batch обработка -- Все клиенты instance обрабатываются за один запуск -- Одно подключение к БД -- Один reload Angie для всех изменений - --- ## Troubleshooting @@ -1143,22 +663,24 @@ logging: **Проверить:** 1. `docker logs ptaf_{client}_{num}` 2. Наличие конфигов в `/home/install/conf/ptaf-nginx/` -3. Занятость портов: `ss -tuln | grep {port}` +3. Занятость портов: `ss -tuanl | grep {port}` 4. Права на `/var/log/ptaf_nginx/` (должны быть 0777) - -### Проблема: Nginx не перезагружается - -**Проверить:** -1. `docker exec ptaf_{client}_{num} nginx -t` -2. Синтаксис в `conf.d/*.conf` -3. Логи: `/var/log/ptaf_nginx/{client}/ptaf-agent{num}/error.log` +5. Зомби docker-proxy процессы: `ps aux | grep docker-proxy | grep -v grep` ### Проблема: Порты конфликтуют **Действия:** -1. Удалить `.ports.json` -2. Перезапустить программу (порты будут перевыделены) -3. Проверить диапазоны: 18000-20999 (HTTP), 21000-23999 (HTTPS) +1. Проверить `ss -tuanl | grep :{port}` — все состояния сокетов +2. Проверить `cat /proc/net/tcp | grep -i "$(printf '%04X' {port})"` — таблица ядра +3. Убедиться что `ip_local_reserved_ports=18000-23999` установлен на хосте +4. Удалить `.ports.json` — порты будут перевыделены при следующем запуске + +### Проблема: Lock-файл не удалился + +**Действия:** +1. Magos автоматически удалит lock если процесс уже не существует +2. Если процесс завис: `kill {PID} && rm /var/lock/ptaf-main.lock` +3. PID записан в lock-файле: `cat /var/lock/ptaf-main.lock` ### Проблема: Клиент не мигрирует @@ -1168,65 +690,71 @@ logging: 3. Наличие маркеров `/tmp/ptaf-drain-*` 4. Логи программы на обоих хостах -### Проблема: API данные не обновляются +### Проблема: SW nginx не перезагружается -**Действия:** -1. Проверить `sp_info.updated_at` -2. Если > 10 минут → должен быть API запрос -3. Проверить `mode` в `apps_settings`: - - `disabled` → пропускается - - `manual` → использует `manual_info` - - `auto` → кэш или API +**Проверить:** +1. `solidwall-nginx -t` +2. Наличие симлинков в `sites-enabled/` +3. Синтаксис в `sites-available/*.conf` --- ## Расширение системы -### Добавление нового клиента +### Добавление нового клиента (PTAF) ```sql --- 1. Добавить в client_info INSERT INTO client_info (client_title, containers_count, waf_instance) VALUES ('new-client', 2, 'instance-a'); --- 2. Добавить ресурсы в apps_settings -INSERT INTO apps_settings (l7resourceid, client_title, waf_enabled, mode) -VALUES (12345, 'new-client', true, 'auto'); - --- 3. (Опционально) Для ручного режима добавить в manual_info -INSERT INTO manual_info (sid, domain_name, aliases, origins) -VALUES (12345, 'example.com', '["www.example.com"]', '[{"ip":"192.168.1.100","weight":100,"mode":"active"}]'); +INSERT INTO apps_settings (l7resourceid, client_title, waf_vendor, waf_enabled, mode) +VALUES (12345, 'new-client', 'ptaf', true, 'auto'); ``` -### Добавление нового instance +### Добавление нового клиента (SW) ```sql -INSERT INTO instances_new (hostname, instance) -VALUES ('waf-host-03', 'instance-d'); +INSERT INTO client_info (client_title, containers_count, waf_instance) +VALUES ('new-sw-client', 0, 'sw-instance-a'); -- containers_count игнорируется для SW + +INSERT INTO apps_settings (l7resourceid, client_title, waf_vendor, waf_enabled, mode) +VALUES (12346, 'new-sw-client', 'sw', true, 'auto'); ``` -### Кастомизация портов +### Включение debug-режима для клиента ```sql -UPDATE apps_settings -SET - custom_input_http_ports = ARRAY[80, 8080], - custom_input_https_ports = ARRAY[443, 8443], - custom_output_http_ports = ARRAY[80], - custom_output_https_ports = ARRAY[443] -WHERE l7resourceid = 12345; +UPDATE client_info SET debug = true WHERE client_title = 'CLIENT001'; ``` -### Переопределение SSL +### Задать индивидуальный Docker-образ для клиента ```sql -UPDATE apps_settings -SET custom_angie_ssl = 'ssl_protocols TLSv1.3; -ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384; -ssl_prefer_server_ciphers off; -ssl_session_cache shared:SSL:50m; -ssl_session_timeout 1d; -ssl_stapling on; -ssl_stapling_verify on;' -WHERE l7resourceid = 12345; +UPDATE client_info +SET + docker_image = 'ptaf-core-nginx-agent:release-4.3.1.431839-debian-bullseye-nginx-1.28.0', + docker_image_download = 'https://example.com/image.tar' +WHERE client_title = 'CLIENT001'; +``` + +### Увеличить shared memory для контейнера + +```sql +UPDATE client_info SET shm_size = 8 WHERE client_title = 'CLIENT001'; +``` + +### Настроить балансировку + +```sql +UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345; +``` + +### Настроить ptaf_fallback + +```sql +-- Пропускать трафик если WAF недоступен +UPDATE apps_settings SET ptaf_fallback_code = 'pass' WHERE l7resourceid = 12345; + +-- Вернуть 503 вместо 418 +UPDATE apps_settings SET ptaf_fallback_code = '503' WHERE l7resourceid = 12345; ``` diff --git a/DATABASE_SCHEMA.md b/DATABASE_SCHEMA.md index 8c7b868..3454c49 100644 --- a/DATABASE_SCHEMA.md +++ b/DATABASE_SCHEMA.md @@ -4,15 +4,15 @@ ## Обзор -Magos работает с PostgreSQL базой данных `waf_info`, которая содержит конфигурации WAF платформы. Программа использует 5 основных таблиц для получения информации о хостах, клиентах, приложениях и backend серверах. +Magos работает с PostgreSQL базой данных, которая содержит конфигурации WAF платформы. Программа использует несколько основных таблиц для получения информации о хостах, клиентах, приложениях и backend серверах. ### Используемые таблицы -1. **instances** - информация о хостах/серверах +1. **instances_new** - информация о хостах/серверах 2. **client_info** - данные клиентов и контейнеров 3. **apps_settings** - настройки приложений и кастомные директивы -4. **origins** - backend серверы (получаются через REST API) -5. **aliases** - дополнительные домены (получаются через REST API) +4. **sp_info** - кэш данных из API (origins, aliases) +5. **manual_info** - ручные данные (альтернатива API) 6. **ips** - IP адреса для WAF и antibot сетей --- @@ -26,25 +26,25 @@ DB_USER=login DB_PASSWORD=password DB_NAME=waf_info DB_PORT=5432 -PRIMARY_DB_HOST=10.10.10.8 -SECONDARY_DB_HOST=10.10.10.5 +PRIMARY_DB_HOST=10.100.10.8 +SECONDARY_DB_HOST=10.100.13.5 ``` ### Failover логика -1. Попытка подключения к Primary DB (10.10.10.8) -2. Если неудача → подключение к Secondary DB (10.10.10.5) +1. Попытка подключения к Primary DB +2. Если неудача → подключение к Secondary DB 3. Если обе недоступны → программа останавливается **Функция:** `connectToDB()` --- -## Таблица: instances +## Таблица: instances_new ### Назначение -Хранит информацию о хостах/серверах в кластере PTAF. +Хранит информацию о хостах/серверах в кластере WAF. ### Структура @@ -55,154 +55,129 @@ SECONDARY_DB_HOST=10.10.10.5 ### Используемые запросы -#### 1. Получение instance по hostname +#### Получение instances по hostname ```sql -SELECT instance -FROM instances +SELECT instance +FROM instances_new WHERE hostname = $1; ``` -**Функция:** `getInstanceByHostname(hostname string)` +**Функция:** `getInstancesByHostname(hostname string)` -**Использование:** При старте программы для определения какой это инстанс. +Один хост может принадлежать нескольким instances. -**Пример:** -```go -hostname := "host01.example.com" -instance, err := getInstanceByHostname(db, hostname) -// instance = "waf-prod-01" -``` - ---- - -#### 2. Проверка принадлежности хоста к инстансу +#### Проверка принадлежности хоста к инстансу ```sql -SELECT COUNT(*) -FROM instances +SELECT COUNT(*) +FROM instances_new WHERE hostname = $1 AND instance = $2; ``` **Функция:** `checkHostBelongsToInstance(hostname, instance string)` -**Использование:** Валидация что хост принадлежит данному инстансу. - -**Пример:** -```go -hostname := "host01.example.com" -instance := "waf-prod-01" -err := checkHostBelongsToInstance(db, hostname, instance) -// err == nil если хост принадлежит инстансу -``` - ---- - -### Примеры данных - -```sql -INSERT INTO instances (hostname, instance) VALUES -('host01.example.com', 'waf-prod-01'), -('host02.example.com', 'waf-prod-01'), -('host03.example.com', 'waf-prod-02'); -``` - --- ## Таблица: client_info ### Назначение -Хранит информацию о клиентах: количество контейнеров, привязка к инстансу. +Хранит информацию о клиентах: количество контейнеров, Docker-образ, режим отладки и привязка к инстансу. ### Структура | Поле | Тип | Описание | Пример | |------|-----|----------|--------| | `client_title` | TEXT | Идентификатор клиента | `CLIENT001` | -| `containers_count` | INTEGER | Количество контейнеров | `3` | -| `ptaf_config` | TEXT | Конфигурация PTAF (JSON) | `{"key": "value"}` | +| `containers_count` | INTEGER | Количество контейнеров (0 = drain всех) | `3` | +| `ptaf_config` | TEXT | Строка подключения к PTAF-серверу | `{...}` | | `fluent_bit_port` | INTEGER | Порт Fluent Bit | `24224` | -| `waf_instance` | TEXT | Instance для apps_settings | `waf-prod-01` | +| `waf_instance` | TEXT | Instance для привязки к хосту | `waf-prod-01` | +| `docker_image` | TEXT | Docker-образ (переопределяет глобальный) | `ptaf-core-nginx-agent:release-4.3.1...` | +| `docker_image_download` | TEXT | URL для скачивания tar-архива образа | `https://...` | +| `debug` | BOOLEAN | Режим отладки | `false` | +| `shm_size` | INTEGER | Размер /dev/shm в GB | `1` | ### Используемые запросы -#### 1. Получение клиентов по instance +#### Получение клиентов по instance ```sql -SELECT - containers_count, - ptaf_config, - client_title, - fluent_bit_port, - waf_instance +SELECT + containers_count, + ptaf_config, + client_title, + fluent_bit_port, + waf_instance, + docker_image, + docker_image_download, + debug, + shm_size FROM client_info WHERE waf_instance = $1; ``` **Функция:** `getClientInfoByInstance(instance string)` -**Использование:** Получение всех клиентов для данного инстанса. - -**Пример:** -```go -instance := "waf-prod-01" -clients, err := getClientInfoByInstance(db, instance) -// clients = [{ContainersCount: 3, ClientTitle: "CLIENT001", ...}, ...] -``` - ---- - -#### 2. Получение клиента по client_title +#### Получение клиента по client_title ```sql -SELECT - containers_count, - ptaf_config, - client_title, - fluent_bit_port, - waf_instance +SELECT + containers_count, + ptaf_config, + client_title, + fluent_bit_port, + waf_instance, + docker_image, + docker_image_download, + debug, + shm_size FROM client_info WHERE client_title = $1; ``` **Функция:** `getClientInfoByClientTitle(clientTitle string)` -**Использование:** Получение информации о конкретном клиенте. - ---- - -### Примеры данных +### Миграции ```sql -INSERT INTO client_info -(client_title, containers_count, ptaf_config, fluent_bit_port, waf_instance) -VALUES -('CLIENT001', 3, '{"version": "1.0"}', 24224, 'waf-prod-01'), -('CLIENT002', 5, '{"version": "1.0"}', 24225, 'waf-prod-01'), -('CLIENT003', 2, '{"version": "1.0"}', 24226, 'waf-prod-02'); +ALTER TABLE client_info + ADD COLUMN IF NOT EXISTS docker_image TEXT DEFAULT NULL; + +ALTER TABLE client_info + ADD COLUMN IF NOT EXISTS docker_image_download TEXT DEFAULT NULL; + +ALTER TABLE client_info + ADD COLUMN IF NOT EXISTS debug BOOLEAN DEFAULT FALSE; + +ALTER TABLE client_info + ADD COLUMN IF NOT EXISTS shm_size INTEGER DEFAULT 1; ``` ---- +### Ключевые поля -### Ключевое поле: containers_count +#### containers_count -**Назначение:** Определяет сколько Docker контейнеров нужно создать для клиента. - -**Логика:** - `containers_count = 3` → создать ptaf_client_001, ptaf_client_002, ptaf_client_003 -- При увеличении → создаются новые контейнеры -- При уменьшении → лишние контейнеры помечаются на graceful shutdown (90s) +- При увеличении → новые контейнеры добавляются, существующие не трогаются +- При уменьшении → лишние контейнеры помечаются на graceful drain (90с) +- `containers_count = 0` → все контейнеры и Angie-конфиг помечаются на drain -**Пример изменения:** -```sql --- Увеличить до 5 контейнеров -UPDATE client_info -SET containers_count = 5 -WHERE client_title = 'CLIENT001'; +#### docker_image / docker_image_download --- Manager автоматически создаст ptaf_client_004 и ptaf_client_005 -``` +Если заполнены — используются вместо глобальных настроек `DOCKER_IMAGE` и `DOCKER_IMAGE_URL` из `magos.env`. Позволяют иметь разные версии агента для разных клиентов. + +#### debug + +При `true`: +- В docker-compose добавляются `PTAF_DEBUG=True` и `ERROR_LOG_LEVEL=debug` +- В nginx.conf: `PTAF_LOG_LEVEL=DEBUG`, `DEBUG=true`, `error_log stderr debug` +- В Angie server-блоки добавляется `client_body_buffer_size 1m` + +#### shm_size + +Размер разделяемой памяти `/dev/shm` для Docker-контейнера в гигабайтах. Результат в docker-compose: `shm_size: '2gb'`. Дефолт: 1. --- @@ -217,46 +192,64 @@ WHERE client_title = 'CLIENT001'; | Поле | Тип | Описание | Значение по умолчанию | |------|-----|----------|----------------------| | `l7resourceid` | INTEGER | ID ресурса | Первичный ключ | -| `client_title` | TEXT | Клиент (FK) | `CLIENT001` | +| `client_title` | TEXT | Клиент (FK) | — | | `waf_enabled` | BOOLEAN | WAF включен | `true` | +| `waf_vendor` | TEXT | Вендор: 'ptaf' или 'sw' | — | +| `mode` | TEXT | auto/manual/disabled | `auto` | | `sni` | INTEGER | SNI режим (0=keepalive on, 1=keepalive off) | `0` | -| `custom_input_http_ports` | INTEGER[] | HTTP порты для прослушивания | `{80, 8080}` | -| `custom_input_https_ports` | INTEGER[] | HTTPS порты для прослушивания | `{443, 8443}` | +| `ssl_enabled` | BOOLEAN | SSL на HTTPS портах | `true` | +| `custom_input_http_ports` | INTEGER[] | HTTP порты для прослушивания | `{80}` | +| `custom_input_https_ports` | INTEGER[] | HTTPS порты для прослушивания | `{443}` | | `custom_output_http_ports` | INTEGER[] | HTTP порты к backend | `{80}` | | `custom_output_https_ports` | INTEGER[] | HTTPS порты к backend | `{443}` | | `upstream_angie_custom` | TEXT | Кастомные upstream директивы (Angie) | `NULL` | -| `upstream_nginx_custom` | TEXT | Кастомные upstream директивы (Nginx) | `NULL` | -| `server_angie_custom` | TEXT | Полная замена server блока (Angie) | `NULL` | -| `server_nginx_custom` | TEXT | Полная замена server блока (Nginx) | `NULL` | +| `upstream_nginx_custom` | TEXT | Кастомные upstream директивы (Nginx/SW) | `NULL` | +| `server_angie_custom` | TEXT | Кастомный server блок (Angie) | `NULL` | +| `server_nginx_custom` | TEXT | Кастомный server блок (Nginx/SW) | `NULL` | | `server_directives_angie_custom` | TEXT | Кастомные server директивы (Angie) | `NULL` | -| `server_directives_nginx_custom` | TEXT | Кастомные server директивы (Nginx) | `NULL` | +| `server_directives_nginx_custom` | TEXT | Кастомные server директивы (Nginx/SW) | `NULL` | | `location_angie_custom` | TEXT | Кастомные location блоки (Angie) | `NULL` | -| `location_nginx_custom` | TEXT | Кастомные location блоки (Nginx) | `NULL` | - ---- +| `location_nginx_custom` | TEXT | Кастомные location блоки (Nginx/SW) | `NULL` | +| `custom_angie_ssl` | TEXT | Замена SSL директив (Angie) | `NULL` | +| `custom_sw_nginx_ssl` | TEXT | Замена SSL директив (SW) | `NULL` | +| `max_fails` | INTEGER | Попыток до пометки сервера недоступным | `5` | +| `fail_timeout` | INTEGER | Время восстановления сервера (сек) | `15` | +| `balancing_method` | TEXT | Метод балансировки upstream | `NULL` | +| `proxy_next_upstream_codes` | TEXT | HTTP-коды для proxy_next_upstream | `'500,502,503,504'` | +| `ptaf_fallback_code` | TEXT | Код ответа ptaf_fallback | `'418'` | ### Используемые запросы #### Получение настроек по client_title ```sql -SELECT - l7resourceid, +SELECT + l7resourceid, waf_enabled, - custom_input_http_ports, + waf_vendor, + mode, + custom_input_http_ports, custom_input_https_ports, - custom_output_http_ports, + custom_output_http_ports, custom_output_https_ports, - upstream_angie_custom, - server_nginx_custom, - server_angie_custom, - client_title, + upstream_angie_custom, upstream_nginx_custom, - location_angie_custom, + server_nginx_custom, + server_angie_custom, + location_angie_custom, location_nginx_custom, - server_directives_angie_custom, + server_directives_angie_custom, server_directives_nginx_custom, - sni + custom_angie_ssl, + custom_sw_nginx_ssl, + client_title, + sni, + ssl_enabled, + max_fails, + fail_timeout, + balancing_method, + proxy_next_upstream_codes, + ptaf_fallback_code FROM apps_settings WHERE client_title = $1 ORDER BY l7resourceid ASC; @@ -264,649 +257,332 @@ ORDER BY l7resourceid ASC; **Функция:** `getAppsSettingsByClientTitle(clientTitle string)` -**Использование:** Получение всех настроек приложений для клиента. - ---- - -### Примеры данных +### Миграции ```sql -INSERT INTO apps_settings -(l7resourceid, client_title, waf_enabled, sni, - custom_input_http_ports, custom_input_https_ports, - custom_output_http_ports, custom_output_https_ports) -VALUES -(12323, 'CLIENT001', true, 0, '{80}', '{443}', '{80}', '{443}'), -(12324, 'CLIENT001', true, 0, '{80,8080}', '{443,8443}', '{80}', '{443}'), -(15000, 'CLIENT002', true, 0, '{80}', '{443}', '{80}', '{443}'); +ALTER TABLE apps_settings + ADD COLUMN IF NOT EXISTS custom_sw_nginx_ssl TEXT DEFAULT NULL; + +ALTER TABLE apps_settings + ADD COLUMN IF NOT EXISTS balancing_method TEXT DEFAULT NULL; + +ALTER TABLE apps_settings + ADD COLUMN IF NOT EXISTS proxy_next_upstream_codes TEXT DEFAULT '500,502,503,504'; + +ALTER TABLE apps_settings + ADD COLUMN IF NOT EXISTS ptaf_fallback_code TEXT DEFAULT '418'; ``` ---- - ### Кастомные поля (подробно) +#### balancing_method + +Метод балансировки upstream. Записывается **без** точки с запятой. Вставляется первой строкой в upstream-блок. + +```sql +-- Включить least_conn +UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345; + +-- Вернуть round-robin (дефолт) +UPDATE apps_settings SET balancing_method = NULL WHERE l7resourceid = 12345; +``` + +Допустимые значения: `least_conn`, `ip_hash`, `random`, `random two least_conn` + +#### proxy_next_upstream_codes + +Коды HTTP ответов при которых Angie/nginx переключается на следующий upstream-сервер. Записывается через запятую **без** префикса `http_`. + +```sql +-- Убрать 500 из retry (бэкенд вернул 500, не нужно делать retry) +UPDATE apps_settings +SET proxy_next_upstream_codes = '502,503,504' +WHERE l7resourceid = 12345; + +-- Вернуть дефолт +UPDATE apps_settings +SET proxy_next_upstream_codes = NULL +WHERE l7resourceid = 12345; +``` + +Итоговая строка в конфиге: +```nginx +proxy_next_upstream error timeout invalid_header http_502 http_503 http_504; +``` + +#### ptaf_fallback_code + +Код HTTP ответа который PTAF возвращает когда WAF-модуль недоступен. + +```sql +-- Пропускать трафик если WAF недоступен +UPDATE apps_settings SET ptaf_fallback_code = 'pass' WHERE l7resourceid = 12345; + +-- Вернуть 503 вместо 418 +UPDATE apps_settings SET ptaf_fallback_code = '503' WHERE l7resourceid = 12345; + +-- Вернуть дефолт (418) +UPDATE apps_settings SET ptaf_fallback_code = NULL WHERE l7resourceid = 12345; +``` + #### upstream_angie_custom / upstream_nginx_custom **Режим:** REPLACE (полная замена keepalive директив) -**NULL значение:** -```nginx -upstream backend { - server 127.0.0.1:21000; - - # Default upstream directives - keepalive 60; - keepalive_timeout 70s; -} -``` - -**НЕ NULL значение:** -```nginx -upstream backend { - server 127.0.0.1:21000; - - # Custom upstream directives (replace defaults) - keepalive 200; - keepalive_timeout 90s; - least_conn; -} -``` - -**Пример SQL:** ```sql --- Изменить балансировку на least_conn +-- Кастомный keepalive UPDATE apps_settings -SET upstream_angie_custom = 'keepalive 60; -keepalive_timeout 70s; -least_conn;' -WHERE l7resourceid = 12323; - --- Вернуть дефолты -UPDATE apps_settings -SET upstream_angie_custom = NULL -WHERE l7resourceid = 12323; +SET upstream_angie_custom = 'keepalive 200; +keepalive_timeout 90s;' +WHERE l7resourceid = 12345; ``` ---- - #### server_directives_angie_custom / server_directives_nginx_custom -**Режим:** REPLACE (замена else блока в server) +**Режим:** REPLACE (замена блока дефолтных таймаутов/буферов) -**NULL значение:** -```nginx -server { - listen 443 ssl; - - # Default directives - keepalive_timeout 80s; - send_timeout 60s; - proxy_connect_timeout 60s; - proxy_read_timeout 120s; - proxy_send_timeout 120s; - - large_client_header_buffers 4 128k; - proxy_buffers 4 32k; - proxy_buffer_size 16k; - proxy_busy_buffers_size 32k; -} -``` - -**НЕ NULL значение:** -```nginx -server { - listen 443 ssl; - - # Custom directives (replace defaults) - keepalive_timeout 120s; - client_max_body_size 100m; - # Нужно указать ВСЕ директивы, включая буферы! -} -``` - -**Пример SQL:** -```sql --- Увеличить таймауты -UPDATE apps_settings -SET server_directives_angie_custom = 'keepalive_timeout 120s; -send_timeout 90s; -proxy_connect_timeout 90s; -proxy_read_timeout 180s; -proxy_send_timeout 180s; - -large_client_header_buffers 4 128k; -proxy_buffers 4 32k; -proxy_buffer_size 16k;' -WHERE l7resourceid = 12323; -``` - ---- +**Важно:** при использовании кастома нужно указать ВСЕ нужные директивы, включая `proxy_set_header X-Request-ID`. #### server_angie_custom / server_nginx_custom -**Режим:** SERVER_BLOCK (полная замена всего server блока) +Поддерживает формат SERVER_BLOCK для создания дополнительных server-блоков: -**NULL значение:** Используется стандартный шаблон server блока - -**НЕ NULL значение:** Полная замена server блока - -**Пример SQL:** -```sql --- Создать полностью кастомный server блок -UPDATE apps_settings -SET server_angie_custom = 'server { - listen 9999; - server_name special.example.com; - - location / { - return 200 "Custom response"; - } -}' -WHERE l7resourceid = 12323; +``` +[SERVER_BLOCK:443:report.example.com:www.report.example.com] +proxy_read_timeout 3600s; +proxy_send_timeout 3600s; +[/SERVER_BLOCK] ``` ---- +Синтаксис: `[SERVER_BLOCK:{порт}:{server_name}:{alias1}:{alias2}...]` -#### location_angie_custom / location_nginx_custom +#### custom_angie_ssl / custom_sw_nginx_ssl -**Режим:** APPEND (добавление к дефолтному location) - -**NULL значение:** Только стандартный `location / { proxy_pass }` - -**НЕ NULL значение:** Добавляются дополнительные location блоки - -**Пример SQL:** -```sql --- Добавить location для API -UPDATE apps_settings -SET location_nginx_custom = 'location /api { - proxy_pass http://api.backend.com; - proxy_set_header X-API-Key secret; -} - -location /static { - alias /var/www/static; -}' -WHERE l7resourceid = 12323; -``` - ---- - -#### sni (Server Name Indication) - -**Значения:** -- `0` - Keepalive ВКЛЮЧЕН (дефолт) -- `1` - Keepalive ОТКЛЮЧЕН - -**Когда sni = 1:** -- Директивы `keepalive` и `keepalive_timeout` НЕ добавляются в upstream -- Используется для SNI passthrough (прямая передача без терминации SSL) - -**Пример SQL:** -```sql --- Отключить keepalive для SNI passthrough -UPDATE apps_settings -SET sni = 1 -WHERE l7resourceid = 12323; -``` - ---- - -#### custom_input_*_ports и custom_output_*_ports - -**custom_input_http_ports / custom_input_https_ports:** -- Порты на которых Angie и Nginx контейнеры будут слушать -- Angie слушает на этих портах (80, 443, 8080, 8443) -- Nginx контейнеры слушают на этих же портах - -**custom_output_http_ports / custom_output_https_ports:** -- Порты к которым Nginx контейнеры будут проксировать на backend -- Обычно {80} для HTTP и {443} для HTTPS -- Могут быть нестандартные порты (8080, 8443) - -**Пример:** -```sql --- Приложение слушает на портах 80, 8080 (HTTP) и 443, 8443 (HTTPS) --- Backend работает на порту 443 -UPDATE apps_settings -SET - custom_input_http_ports = '{80, 8080}', - custom_input_https_ports = '{443, 8443}', - custom_output_http_ports = '{80}', - custom_output_https_ports = '{443}' -WHERE l7resourceid = 12323; -``` - -**Результат:** +Полная замена SSL директив. При NULL используются дефолтные: ```nginx -# Angie -server { - listen 80; # custom_input_http_ports[0] - listen 8080; # custom_input_http_ports[1] - listen 443 ssl; # custom_input_https_ports[0] - listen 8443 ssl; # custom_input_https_ports[1] -} - -# Nginx upstream -upstream backend { - server 185.22.65.148:443; # custom_output_https_ports[0] -} +ssl_certificate ssl/cert.pem; +ssl_certificate_key ssl/key.pem; +ssl_protocols TLSv1.2 TLSv1.3; ``` --- -## Таблица: ips +## Таблица: sp_info (кэш) ### Назначение -Хранит IP адреса для WAF и antibot сетей (белые списки). +Кэширует данные из ServicePipe API. TTL: 10 минут. ### Структура | Поле | Тип | Описание | |------|-----|----------| -| `antibot_networks` | TEXT[] | IP сети для antibot | -| `waf_networks` | TEXT[] | IP сети для WAF | - -### Используемые запросы - -```sql -SELECT antibot_networks, waf_networks -FROM ips; -``` - -**Функция:** `loadIPsFromDB()` - -**Использование:** При старте программы для загрузки IP сетей. - -**Пример данных:** -```sql -INSERT INTO ips (antibot_networks, waf_networks) VALUES -( - '{172.16.0.0/12, 10.0.0.0/8}', - '{172.16.0.0/12, 10.100.0.0/16}' -); -``` - -**Использование в конфигах:** -```nginx -server { - # Доверенные IP для получения реального IP клиента - set_real_ip_from 172.16.0.0/12; - set_real_ip_from 10.100.0.0/16; - real_ip_header X-Forwarded-For; -} -``` +| `sid` | INTEGER | l7resourceid | +| `domain_name` | TEXT | Основной домен | +| `aliases` | JSON | Массив алиасов | +| `origins` | JSON | Массив origin серверов | +| `updated_at` | TIMESTAMP | Время последнего обновления | --- -## REST API (origins и aliases) +## Таблица: manual_info -### Origins (Backend серверы) +### Назначение -**Не хранятся в БД**, получаются через REST API. +Ручное управление данными ресурсов (альтернатива API). Используется при `mode = 'manual'`. -**Эндпоинт:** `/api/waf-proxy/get-apps-and-resourses` +### Структура -**Функция:** `makeAPIRequest()`, `getResourceDataFromCache()` +| Поле | Тип | Описание | +|------|-----|----------| +| `sid` | INTEGER | l7resourceid | +| `domain_name` | TEXT | Основной домен | +| `aliases` | JSON | Массив алиасов | +| `origins` | JSON | Массив origin серверов | -**Пример ответа:** -```json -{ - "data": { - "result": { - "items": [ - { - "id": 1, - "ip": "185.22.65.148", - "weight": 100, - "mode": "active", - "comment": "Primary backend" - }, - { - "id": 2, - "ip": "185.22.65.149", - "weight": 50, - "mode": "backup", - "comment": "Backup backend" - } - ] - } - } -} -``` - -**Использование в конфигах:** -```nginx -upstream backend { - server 185.22.65.148:443 weight=100; - server 185.22.65.149:443 weight=50 backup; -} -``` +Данные не имеют TTL и всегда актуальны. --- -### Aliases (Дополнительные домены) +## Таблица: ips -**Не хранятся в БД**, получаются через REST API. +### Структура -**Эндпоинт:** `/api/waf-proxy/get-apps-and-resourses` - -**Пример ответа:** -```json -{ - "data": { - "result": { - "items": [ - { - "id": 1, - "alias": "www.example.com" - }, - { - "id": 2, - "alias": "app.example.com" - } - ] - } - } -} -``` - -**Использование в конфигах:** -```nginx -server { - listen 443 ssl; - server_name example.com www.example.com app.example.com; -} -``` +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | INTEGER | Всегда = 1 | +| `antibot_networks` | TEXT[] | CIDR сети antibot | +| `waf_networks` | TEXT[] | CIDR сети WAF (исключаются из origins) | --- ## Связи между таблицами ``` -instances +instances_new ↓ (hostname → instance) client_info (waf_instance) ↓ (client_title) apps_settings (client_title) - ↓ (l7resourceid через API) -Origins (от REST API) -Aliases (от REST API) + ↓ (l7resourceid через API/manual_info) +Origins, Aliases ``` -**Поток данных:** - -1. Program запускается на хосте с hostname = `host01.example.com` -2. Запрос к `instances` → получаем `instance = "waf-prod-01"` -3. Запрос к `client_info` WHERE `waf_instance = "waf-prod-01"` → получаем клиентов -4. Для каждого клиента запрос к `apps_settings` WHERE `client_title = "CLIENT001"` -5. Для каждого ресурса API запрос → получаем origins и aliases -6. Генерируем конфиги на основе всех данных - --- -## Примеры типичных запросов +## Примеры типичных операций -### Получить все ресурсы клиента +### Добавить нового PTAF-клиента ```sql -SELECT - a.l7resourceid, - a.custom_input_https_ports, - a.upstream_angie_custom, - c.containers_count -FROM apps_settings a -JOIN client_info c ON a.client_title = c.client_title -WHERE a.client_title = 'CLIENT001' -ORDER BY a.l7resourceid; +INSERT INTO client_info (client_title, containers_count, waf_instance) +VALUES ('NewClient', 2, 'PTAFd_01'); + +INSERT INTO apps_settings (l7resourceid, client_title, waf_vendor, waf_enabled, mode) +VALUES (99999, 'NewClient', 'ptaf', true, 'auto'); ``` ---- +### Добавить нового SW-клиента + +```sql +INSERT INTO client_info (client_title, containers_count, waf_instance) +VALUES ('NewSWClient', 0, 'SWd_01'); + +INSERT INTO apps_settings (l7resourceid, client_title, waf_vendor, waf_enabled, mode) +VALUES (99998, 'NewSWClient', 'sw', true, 'auto'); +``` ### Изменить количество контейнеров ```sql -- Увеличить до 5 +UPDATE client_info SET containers_count = 5 WHERE client_title = 'CLIENT001'; + +-- Отключить клиента (drain всех контейнеров) +UPDATE client_info SET containers_count = 0 WHERE client_title = 'CLIENT001'; +``` + +### Включить debug-режим + +```sql +UPDATE client_info SET debug = true WHERE client_title = 'CLIENT001'; +``` + +### Задать индивидуальный Docker-образ + +```sql UPDATE client_info -SET containers_count = 5 -WHERE client_title = 'CLIENT001'; - --- Проверка -SELECT client_title, containers_count -FROM client_info +SET + docker_image = 'ptaf-core-nginx-agent:release-4.3.1.431839-debian-bullseye-nginx-1.28.0', + docker_image_download = 'https://example.com/image.tar' WHERE client_title = 'CLIENT001'; ``` ---- +### Настроить балансировку -### Добавить кастомную балансировку +```sql +UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345; +``` + +### Убрать 500 из retry ```sql UPDATE apps_settings -SET upstream_nginx_custom = 'keepalive 60; -keepalive_timeout 70s; -least_conn;' -WHERE l7resourceid = 12323; - --- Проверка -SELECT l7resourceid, upstream_nginx_custom -FROM apps_settings -WHERE l7resourceid = 12323; +SET proxy_next_upstream_codes = '502,503,504' +WHERE l7resourceid = 12345; ``` ---- - -### Включить SNI passthrough +### Добавить кастомные SSL для SW ```sql UPDATE apps_settings -SET sni = 1 -WHERE l7resourceid = 12323; - --- Проверка -SELECT l7resourceid, sni -FROM apps_settings -WHERE sni = 1; +SET custom_sw_nginx_ssl = 'ssl_certificate /etc/solidwall-nginx/ssl/custom.crt; +ssl_certificate_key /etc/solidwall-nginx/ssl/custom.key; +ssl_protocols TLSv1.2 TLSv1.3;' +WHERE l7resourceid = 12345; ``` ---- - -### Добавить дополнительные порты +### Добавить SERVER_BLOCK с увеличенными таймаутами ```sql UPDATE apps_settings -SET - custom_input_https_ports = '{443, 8443, 9443}', - custom_output_https_ports = '{443, 8443, 9443}' -WHERE l7resourceid = 12323; - --- Проверка -SELECT l7resourceid, custom_input_https_ports, custom_output_https_ports -FROM apps_settings -WHERE l7resourceid = 12323; -``` - ---- - -## Индексы (рекомендуемые) - -```sql --- Для быстрого поиска по hostname -CREATE INDEX idx_instances_hostname ON instances(hostname); - --- Для быстрого поиска клиентов по instance -CREATE INDEX idx_client_info_waf_instance ON client_info(waf_instance); -CREATE INDEX idx_client_info_client_title ON client_info(client_title); - --- Для быстрого поиска настроек по client_title -CREATE INDEX idx_apps_settings_client_title ON apps_settings(client_title); -CREATE INDEX idx_apps_settings_l7resourceid ON apps_settings(l7resourceid); -``` - ---- - -## Миграции и изменения схемы - -### Добавление нового кастомного поля - -```sql --- Добавить новое поле для header манипуляций -ALTER TABLE apps_settings -ADD COLUMN headers_custom TEXT DEFAULT NULL; - --- Обновить код для чтения этого поля -``` - ---- - -### Изменение типа поля - -```sql --- Изменить тип sni на boolean (если сейчас integer) -ALTER TABLE apps_settings -ALTER COLUMN sni TYPE BOOLEAN -USING (sni::integer = 0); +SET server_angie_custom = '[SERVER_BLOCK:443:report.example.com:www.report.example.com] +proxy_read_timeout 3600s; +proxy_send_timeout 3600s; +[/SERVER_BLOCK]' +WHERE l7resourceid = 12345; ``` --- ## Best Practices -### 1. NULL vs пустая строка +### NULL vs пустая строка -**Правильно:** ```sql -UPDATE apps_settings -SET upstream_angie_custom = NULL -- NULL для дефолтов -WHERE l7resourceid = 12323; +-- Правильно: NULL для дефолтов +UPDATE apps_settings SET upstream_angie_custom = NULL WHERE l7resourceid = 12345; + +-- Неправильно: пустая строка может вызвать ошибки +UPDATE apps_settings SET upstream_angie_custom = '' WHERE l7resourceid = 12345; ``` -**Неправильно:** +### Массивы портов + ```sql -UPDATE apps_settings -SET upstream_angie_custom = '' -- Пустая строка может вызвать ошибки -WHERE l7resourceid = 12323; +-- Правильно: PostgreSQL array +UPDATE apps_settings SET custom_input_https_ports = '{443, 8443}' WHERE l7resourceid = 12345; + +-- Неправильно: строка +UPDATE apps_settings SET custom_input_https_ports = '443,8443' WHERE l7resourceid = 12345; ``` ---- +### balancing_method — без точки с запятой -### 2. Массивы портов - -**Правильно:** ```sql -UPDATE apps_settings -SET custom_input_https_ports = '{443, 8443}' -- PostgreSQL array -WHERE l7resourceid = 12323; -``` +-- Правильно +UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345; -**Неправильно:** -```sql -UPDATE apps_settings -SET custom_input_https_ports = '443,8443' -- Строка, не массив -WHERE l7resourceid = 12323; -``` - ---- - -### 3. Многострочные кастомные директивы - -**Правильно:** -```sql -UPDATE apps_settings -SET server_directives_angie_custom = 'keepalive_timeout 80s; -send_timeout 60s; -proxy_buffers 4 32k;' -WHERE l7resourceid = 12323; -``` - -**Также правильно (с экранированием):** -```sql -UPDATE apps_settings -SET server_directives_angie_custom = E'keepalive_timeout 80s;\nsend_timeout 60s;' -WHERE l7resourceid = 12323; +-- Неправильно +UPDATE apps_settings SET balancing_method = 'least_conn;' WHERE l7resourceid = 12345; ``` --- ## Мониторинг БД -### Проверка соединения - -```sql --- Проверить активные соединения -SELECT - datname, - usename, - client_addr, - state, - query_start -FROM pg_stat_activity -WHERE datname = 'waf_info'; -``` - ---- - -### Проверка данных - ```sql -- Количество клиентов на каждом instance SELECT waf_instance, COUNT(*) as clients_count FROM client_info GROUP BY waf_instance; --- Количество ресурсов на клиента -SELECT client_title, COUNT(*) as resources_count -FROM apps_settings -GROUP BY client_title; +-- Клиенты с debug-режимом +SELECT client_title FROM client_info WHERE debug = true; --- Клиенты с кастомными upstream директивами +-- Клиенты с индивидуальным образом +SELECT client_title, docker_image FROM client_info WHERE docker_image IS NOT NULL; + +-- Ресурсы с кастомной балансировкой +SELECT client_title, l7resourceid, balancing_method +FROM apps_settings +WHERE balancing_method IS NOT NULL; + +-- Ресурсы с кастомными upstream директивами SELECT client_title, l7resourceid FROM apps_settings -WHERE upstream_angie_custom IS NOT NULL +WHERE upstream_angie_custom IS NOT NULL OR upstream_nginx_custom IS NOT NULL; ``` --- -## Резервное копирование +## Индексы (рекомендуемые) -```bash -# Дамп всей БД -pg_dump -h 10.100.10.8 -U install -d waf_info > waf_info_backup.sql - -# Дамп только структуры -pg_dump -h 10.100.10.8 -U install -d waf_info --schema-only > schema.sql - -# Дамп только данных -pg_dump -h 10.100.10.8 -U install -d waf_info --data-only > data.sql - -# Восстановление -psql -h 10.100.10.8 -U install -d waf_info < waf_info_backup.sql +```sql +CREATE INDEX idx_instances_new_hostname ON instances_new(hostname); +CREATE INDEX idx_client_info_waf_instance ON client_info(waf_instance); +CREATE INDEX idx_client_info_client_title ON client_info(client_title); +CREATE INDEX idx_apps_settings_client_title ON apps_settings(client_title); +CREATE INDEX idx_apps_settings_l7resourceid ON apps_settings(l7resourceid); ``` - ---- - -## Итого - -### Основные таблицы - -| Таблица | Записей (примерно) | Частота изменений | -|---------|-------------------|-------------------| -| instances | 10-50 | Редко (при добавлении хостов) | -| client_info | 50-500 | Редко (при добавлении клиентов) | -| apps_settings | 500-5000 | Часто (изменение настроек) | -| ips | 1 | Очень редко | - -### Ключевые связи - -- `instances.instance` → `client_info.waf_instance` -- `client_info.client_title` → `apps_settings.client_title` -- `apps_settings.l7resourceid` → REST API (origins, aliases) - -### Кастомизация - -Все кастомные поля в `apps_settings`: -- 8 полей для кастомных директив -- 4 режима: REPLACE (upstream, server_directives), SERVER_BLOCK, APPEND (location) -- NULL = дефолты, NOT NULL = кастом diff --git a/README.md b/README.md index 89a3588..790cb09 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,88 @@ # Magos -Программа для автоматизированного управления конфигурациями WAF. +Программа для автоматизированного управления конфигурациями Angie, PTAF Nginx, SolidWall Nginx и Docker Compose. ## Описание Программа выполняет следующие задачи: -1. Подключается к PostgreSQL БД (primary/secondary) -2. Определяет instance текущего хоста по hostname -3. Получает биндинги для данного instance из таблицы `apps_settings` -4. Группирует биндинги по `client_title` -5. Для каждого клиента: - - Получает данные из БД или через API (server_name, aliases, origins) +1. Подключается к PostgreSQL БД (primary/secondary fallback) +2. Определяет instances текущего хоста по hostname +3. Для каждого клиента определяет WAF-вендор (PTAF или SolidWall) +4. **Для PTAF-клиентов:** + - Проверяет наличие Docker-образа, при необходимости скачивает - Выделяет свободные порты для контейнеров - - Генерирует конфигурационные файлы Angie - - Создает директории для контейнеров - - Генерирует конфигурационные файлы Nginx для контейнеров - - Генерирует docker-compose.yml файлы - - Запускает контейнеры через docker-compose + - Генерирует конфигурационные файлы Angie и Nginx + - Создаёт docker-compose.yml и запускает контейнеры + - Управляет graceful drain при масштабировании и миграции +5. **Для SolidWall-клиентов:** + - Генерирует конфигурационные файлы solidwall-nginx + - Выполняет reload solidwall-nginx при изменениях +6. Поддерживает идемпотентность — повторный запуск без изменений в БД не вносит изменений + +## Конфигурация + +Конфигурационный файл: `/etc/magos/magos.env` + +```bash +# База данных +DB_USER=login +DB_PASSWORD=password +DB_NAME=waf_info +DB_PORT=5432 +PRIMARY_DB_HOST=10.100.10.8 +SECONDARY_DB_HOST=10.100.13.5 + +# Docker-образ PTAF (глобальный) +DOCKER_IMAGE=ptaf-core-nginx-agent:release-4.3.1.431839-debian-bullseye-nginx-1.28.0 +DOCKER_IMAGE_URL=https://example.com/image.tar + +# Параметры Nginx +WORKER_PROCESSES=4 +WORKER_CONNECTIONS=64000 +``` + +## Требования к хосту + +Для корректной работы на PTAF-хостах необходимо зарезервировать диапазон портов: + +```bash +# Добавить в /etc/sysctl.conf +net.ipv4.ip_local_reserved_ports=18000-23999 + +# Применить немедленно +sysctl -w net.ipv4.ip_local_reserved_ports=18000-23999 +``` ## Подробности -Изучить как работает программа можно в файле ARCHITECTURE.md. +Изучить как работает программа можно в файле **ARCHITECTURE.md**. -Посмотреть зависимость работы программы от БД в файле DATABASE_SCHEMA.md +Посмотреть зависимость работы программы от БД в файле **DATABASE_SCHEMA.md**. + +## Диагностика + +### Lock-файл завис + +```bash +# Просмотреть PID +cat /var/lock/ptaf-main.lock + +# Если процесс не существует — magos удалит lock автоматически при следующем запуске +# Если процесс завис — завершить вручную +kill {PID} && rm /var/lock/ptaf-main.lock +``` + +### Переназначить порты контейнера + +```bash +rm /home/install/ptaf/{clientTitle}/{clientTitle}-ptaf-agent*/.ports.json +# При следующем запуске magos порты будут перевыделены +``` + +### Принудительно пересоздать контейнер + +```bash +# Удалить контейнер — magos создаст новый при следующем запуске +docker-compose -f /home/install/ptaf/{clientTitle}/{clientTitle}-ptaf-agent001/docker-compose.yml down +```