# Архитектура Magos ## Обзор системы **Magos** - это система управления WAF (Web Application Firewall) контейнерами на базе Nginx/Angie, которая: - Автоматически разворачивает и управляет Docker-контейнерами с Nginx - Настраивает балансировщик Angie для распределения трафика - Интегрируется с API ServicePipe для получения конфигурации - Поддерживает graceful shutdown при масштабировании и миграции - Обеспечивает изоляцию клиентов через отдельные контейнеры ### Ключевые особенности: - **Многоклиентовая архитектура** - каждый клиент имеет свои контейнеры - **Динамическое управление портами** - автоматическое выделение портов из пулов - **Кэширование данных** - минимизация обращений к API - **Failover БД** - автоматическое переключение между primary/secondary - **Graceful draining** - безопасное удаление контейнеров без потери трафика --- ## Компоненты системы ### 1. Основные файлы программы ``` main.go # Точка входа, основной цикл обработки config.go # Управление конфигурацией db.go # Работа с PostgreSQL api.go # Интеграция с ServicePipe API types.go # Структуры данных utils.go # Утилиты и вспомогательные функции ``` ### 2. Управление контейнерами ``` containers_part1.go # Создание и настройка контейнеров containers_part2.go # Управление жизненным циклом контейнеров drain.go # Graceful shutdown и миграция ``` ### 3. Генерация конфигураций ``` nginx.go # Генерация конфигов Nginx и Angie ports.go # Управление портами ``` --- ## Архитектура данных ### База данных PostgreSQL #### Таблица `instances_new` Связь hostname → instance (waf_instance) ```sql hostname | instance ------------|------------ waf-host-01 | instance-a waf-host-01 | instance-b waf-host-02 | instance-c ``` #### Таблица `client_info` Основная информация о клиентах ```sql Поля: - client_title # Уникальный идентификатор клиента - containers_count # Количество контейнеров (1-20) - ptaf_config # Дополнительная конфигурация PTAF - fluent_bit_port # Порт для FluentBit логирования - waf_instance # Instance для привязки к хосту ``` **Важно:** `waf_instance` определяет на каком хосте должен работать клиент #### Таблица `apps_settings` Настройки каждого L7 ресурса (домена) ```sql Основные поля: - l7resourceid # ID ресурса из ServicePipe API - client_title # Связь с client_info - waf_enabled # Включен ли WAF - mode # auto/manual/disabled Порты: - custom_input_http_ports[] # HTTP порты для прослушивания (Angie) - custom_input_https_ports[] # HTTPS порты для прослушивания (Angie) - custom_output_http_ports[] # HTTP порты для проксирования к origin - custom_output_https_ports[] # HTTPS порты для проксирования к origin Кастомизация Nginx: - upstream_nginx_custom # Кастомные upstream директивы - server_nginx_custom # Кастомный server блок - location_nginx_custom # Кастомные location директивы - server_directives_nginx_custom # Дополнительные server директивы Кастомизация Angie: - upstream_angie_custom # Кастомные upstream директивы - server_angie_custom # Кастомный server блок - location_angie_custom # Кастомные location директивы - server_directives_angie_custom # Дополнительные server директивы - custom_angie_ssl # Полная замена SSL директив Специальные настройки: - sni # 0 = keepalive on, 1 = keepalive off - ssl_enabled # true/false (по умолчанию true) ``` #### Таблица `sp_info` (кэш API данных) Кэширует данные из ServicePipe API ```sql Поля: - sid # l7resourceid - domain_name # Основной домен - aliases # JSON массив алиасов - origins # JSON массив origin серверов - updated_at # Timestamp обновления ``` **TTL кэша:** 10 минут #### Таблица `manual_info` Ручное управление (альтернатива API) ```sql Поля: - sid # l7resourceid - domain_name # Основной домен - aliases # JSON массив алиасов - origins # JSON массив origin серверов ``` **Примечание:** Данные из `manual_info` не имеют TTL и всегда актуальны #### Таблица `ips` Настройки IP сетей ```sql Поля: - id (always = 1) - antibot_networks[] # Массив CIDR для antibot - waf_networks[] # Массив CIDR для WAF (фильтруются из origins) ``` --- ## Процесс работы ### 1. Инициализация ```go main() → initConfig() → connectToDB() ``` **Шаги:** 1. Подключение к БД (primary → secondary fallback) 2. Загрузка IP сетей из таблицы `ips` 3. Получение hostname текущего сервера 4. Определение instances для этого хоста ### 2. Основной цикл обработки ``` ┌─────────────────────────────────────────┐ │ Для каждого instance на этом хосте │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ Получить всех клиентов для instance │ │ (getClientInfoByInstance) │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ Для каждого клиента: │ │ 1. Получить apps_settings │ │ 2. Собрать данные о ресурсах │ │ 3. Выделить порты │ │ 4. Сгенерировать конфиги │ │ 5. Управление контейнерами │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ Глобальная проверка drain-контейнеров │ │ (processAllDrainingContainersGlobally) │ └─────────────────────────────────────────┘ ``` ### 3. Сбор данных о ресурсах Для каждого ресурса определяется режим работы через поле `mode`: #### Режим `auto` (по умолчанию) ``` 1. Проверка кэша sp_info (TTL 10 мин) ├─ Данные свежие → использовать └─ Данные устарели → API запрос 2. API запросы (с retry × 3): ├─ GET /l7/resource/{id}/global → domain_name ├─ GET /l7/alias/global?l7ResourceId={id} → aliases └─ GET /l7/origin/global?l7ResourceId={id} → origins 3. Фильтрация origins (удалить WAF IP) 4. Сохранение в sp_info ``` #### Режим `manual` ``` 1. Чтение из таблицы manual_info 2. Фильтрация origins (удалить WAF IP) 3. API и кэш игнорируются ``` #### Режим `disabled` ``` Ресурс полностью пропускается ``` --- ## Управление портами ### Диапазоны портов ```go const ( HTTPPortStart = 18000 // HTTP: 18000-20999 (3000 портов) HTTPPortEnd = 20999 HTTPSPortStart = 21000 // HTTPS: 21000-23999 (3000 портов) HTTPSPortEnd = 23999 ) ``` ### Алгоритм выделения портов ``` ┌────────────────────────────────────────┐ │ 1. Получить занятые порты (ss -tuln) │ └────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────┐ │ 2. Загрузить сохраненные порты │ │ из .ports.json (если есть) │ └────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────┐ │ 3. Исключить порты клиента │ │ (для переиспользования) │ └────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────┐ │ 4. Для каждого ресурса: │ │ - Определить нужные порты из │ │ custom_input_http_ports[] или │ │ custom_input_https_ports[] │ │ - Выделить свободные Docker порты │ │ - Повторить для каждого контейнера │ └────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────┐ │ 5. Сохранить в .ports.json │ └────────────────────────────────────────┘ ``` ### Структура портов ```go type PortMapping struct { HTTPPorts map[int]int // map[customPort]dockerPort HTTPSPorts map[int]int // map[customPort]dockerPort } ``` **Пример:** ```json { "container_num": 1, "resources": [ { "l7resourceid": 12345, "http_ports": { "80": 18000, "8080": 18001 }, "https_ports": { "443": 21000, "8443": 21001 } } ] } ``` --- ## Graceful Shutdown и миграция ### Типы drain операций #### 1. Масштабирование вниз (Scale Down) **Timeout:** 90 секунд ``` Триггер: containers_count уменьшен Процесс: 1. Контейнер помечается маркером /tmp/ptaf-drain-{name} 2. Ожидание 90 секунд 3. Удаление контейнера ``` #### 2. Миграция между хостами **Timeout:** 3600 секунд (1 час) ``` Триггер: waf_instance изменен Процесс: 1. Старый хост помечает контейнеры маркером (isMigration=true) 2. Новый хост создает контейнеры 3. Через 1 час старый хост удаляет контейнеры 4. Проверка актуальности перед удалением ``` ### Структура маркеров ```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? └─ Удалить контейнер ``` --- ## Генерация конфигураций ### Структура файлов ``` /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 ``` ### 1. Генерация nginx.conf **Функция:** `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 для централизованного логирования --- ## API интеграция ### ServicePipe API **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", ... } } } ``` 2. **Получение aliases:** ```http GET /l7/alias/global?limit=1000&l7ResourceId={l7ResourceID} Authorization: Bearer {token} 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 - Быстрая работа при повторных запусках --- ## Схема взаимодействия ### Полный flow обработки клиента ``` ┌─────────────────────────────────────────────────────────────┐ │ НАЧАЛО ОБРАБОТКИ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 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 │ │ └─ Контейнер не существует → очистить маркер │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ ЗАВЕРШЕНИЕ ОБРАБОТКИ │ └─────────────────────────────────────────────────────────────┘ ``` ### Схема миграции клиента ``` СТАРЫЙ ХОСТ (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 │ └───────────────────┘ ``` ### Схема масштабирования ``` containers_count: 3 → 2 (уменьшение) КОНТЕЙНЕР 1 КОНТЕЙНЕР 2 КОНТЕЙНЕР 3 │ │ │ │ │ │ ↓ ↓ ↓ ┌────────┐ ┌────────┐ ┌────────┐ │ Обновл.│ │ Обновл.│ │ Помечен│ │ конфиг │ │ конфиг │ │ drain │ │ и порты│ │ и порты│ │timeout:│ └────────┘ └────────┘ │ 90 сек │ │ │ └────────┘ │ │ │ ↓ ↓ ↓ ┌────────┐ ┌────────┐ [Ожидание] │ reload │ │ reload │ [активных] │ nginx │ │ nginx │ [соединений] └────────┘ └────────┘ ↓ │ │ ┌────────┐ │ │ │ Через │ ↓ ↓ │ 90 сек │ [Обрабатывают] [Обрабатывают] └────────┘ трафик трафик ↓ ┌────────┐ │ docker-│ │ compose│ │ down │ └────────┘ ↓ ┌────────┐ │ Удален │ └────────┘ ``` --- ## Константы и лимиты ### Производительность ```go DefaultWorkerProcesses = 4 // Nginx worker processes DefaultWorkerConnections = 64000 // Соединений на worker ``` ### Ограничения ```go MaxContainersPerClient = 20 // Максимум контейнеров на клиента ``` ### Timeouts ```go ContainerDrainTimeout = 90 // Секунд для scale down MigrationDrainTimeout = 3600 // Секунд для миграции (1 час) APITimeout = 120 // Секунд для API запросов CacheTTL = 600 // Секунд (10 минут) для sp_info ``` ### Retry ```go MaxAPIRetries = 3 // Попыток для API запросов RetryWaitFormula = attempt² // 1s, 4s, 9s ``` --- ## Безопасность ### Фильтрация IP адресов **WAF Networks** - исключаются из origins: ``` 109.238.89.0/24 89.20.63.0/24 ``` Причина: предотвращение 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 трафика ### SSL/TLS **Дефолтные настройки (Angie):** ```nginx 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_angie_ssl`** - полная замена дефолтов ### mTLS **Автоматическая замена путей:** ``` /etc/ssl/certs/ → /opt/ptaf/conf/main/ /etc/ssl/private/ → /opt/ptaf/conf/main/ ``` Применяется во всех custom директивах, содержащих `proxy_ssl_certificate` --- ## Мониторинг и логирование ### Логирование контейнеров **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" ``` ### Структура логов ``` /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 ### Проблема: Контейнер не запускается **Проверить:** 1. `docker logs ptaf_{client}_{num}` 2. Наличие конфигов в `/home/install/conf/ptaf-nginx/` 3. Занятость портов: `ss -tuln | 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` ### Проблема: Порты конфликтуют **Действия:** 1. Удалить `.ports.json` 2. Перезапустить программу (порты будут перевыделены) 3. Проверить диапазоны: 18000-20999 (HTTP), 21000-23999 (HTTPS) ### Проблема: Клиент не мигрирует **Проверить:** 1. `waf_instance` в `client_info` 2. Записи в `instances_new` 3. Наличие маркеров `/tmp/ptaf-drain-*` 4. Логи программы на обоих хостах ### Проблема: API данные не обновляются **Действия:** 1. Проверить `sp_info.updated_at` 2. Если > 10 минут → должен быть API запрос 3. Проверить `mode` в `apps_settings`: - `disabled` → пропускается - `manual` → использует `manual_info` - `auto` → кэш или API --- ## Расширение системы ### Добавление нового клиента ```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"}]'); ``` ### Добавление нового instance ```sql INSERT INTO instances_new (hostname, instance) VALUES ('waf-host-03', 'instance-d'); ``` ### Кастомизация портов ```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; ``` ### Переопределение SSL ```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; ```