48 KiB
Архитектура Magos
Обзор системы
Magos - это система управления WAF (Web Application Firewall) контейнерами на базе Nginx/Angie, которая:
- Автоматически разворачивает и управляет Docker-контейнерами с Nginx
- Настраивает балансировщик Angie для распределения трафика
- Интегрируется с API для получения конфигурации
- Поддерживает 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)
hostname | instance
------------|------------
waf-host-01 | instance-a
waf-host-01 | instance-b
waf-host-02 | instance-c
Таблица client_info
Основная информация о клиентах
Поля:
- client_title # Уникальный идентификатор клиента
- containers_count # Количество контейнеров (1-20)
- ptaf_config # Дополнительная конфигурация PTAF
- fluent_bit_port # Порт для FluentBit логирования
- waf_instance # Instance для привязки к хосту
Важно: waf_instance определяет на каком хосте должен работать клиент
Таблица apps_settings
Настройки каждого L7 ресурса (домена)
Основные поля:
- 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
Поля:
- sid # l7resourceid
- domain_name # Основной домен
- aliases # JSON массив алиасов
- origins # JSON массив origin серверов
- updated_at # Timestamp обновления
TTL кэша: 10 минут
Таблица manual_info
Ручное управление (альтернатива API)
Поля:
- sid # l7resourceid
- domain_name # Основной домен
- aliases # JSON массив алиасов
- origins # JSON массив origin серверов
Примечание: Данные из manual_info не имеют TTL и всегда актуальны
Таблица ips
Настройки IP сетей
Поля:
- id (always = 1)
- antibot_networks[] # Массив CIDR для antibot
- waf_networks[] # Массив CIDR для WAF (фильтруются из origins)
Процесс работы
1. Инициализация
main() → initConfig() → connectToDB()
Шаги:
- Подключение к БД (primary → secondary fallback)
- Загрузка IP сетей из таблицы
ips - Получение hostname текущего сервера
- Определение 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
Ресурс полностью пропускается
Управление портами
Диапазоны портов
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 │
└────────────────────────────────────────┘
Структура портов
type PortMapping struct {
HTTPPorts map[int]int // map[customPort]dockerPort
HTTPSPorts map[int]int // map[customPort]dockerPort
}
Пример:
{
"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. Проверка актуальности перед удалением
Структура маркеров
# Контейнеры
/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()
Содержимое:
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()
Структура конфига ресурса:
# 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}
}
Логика кастомизации:
-
Custom Upstream (
upstream_nginx_custom):- Если заполнено → заменяет keepalive директивы
- Если пусто → используются дефолтные (keepalive 60 + keepalive_timeout 70s)
- Исключение: если
sni = 1, keepalive отключен
-
Custom Server (
server_nginx_custom):- Полная замена всего server блока
- Используется вместо генерации стандартных блоков
-
Custom Location (
location_nginx_custom):- Заменяет дефолтный location /
- proxy_pass корректируется автоматически
-
Custom Server Directives (
server_directives_nginx_custom):- Добавляются внутрь server блока
- Поддержка [SERVER_BLOCK:PORT:DOMAIN] синтаксиса
-
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:
# 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 конфига:
-
Балансировка между контейнерами:
- Upstream указывает на локальные Docker порты (127.0.0.1:18xxx)
- Количество серверов = containers_count
-
SSL настройки:
custom_angie_sslполностью заменяет дефолтные директивы- Если не заполнено → используются безопасные дефолты
-
Санитизация доменов:
- В Angie:
_заменяется на- - В Nginx: домены без изменений
- Причина: совместимость и единообразие
- В Angie:
-
mTLS поддержка:
- Автоматическая замена путей
/etc/ssl/→/opt/ptaf/conf/main/ - Применяется в custom директивах
- Автоматическая замена путей
4. Генерация docker-compose.yml
Функция: generateDockerCompose()
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:
- Получение ресурса:
GET /l7/resource/{l7ResourceID}/global
Authorization: Bearer {token}
Response:
{
"data": {
"result": {
"l7ResourceId": 12345,
"l7ResourceName": "example.com",
...
}
}
}
- Получение aliases:
GET /l7/alias/global?limit=1000&l7ResourceId={l7ResourceID}
Authorization: Bearer {token}
Response:
{
"data": {
"result": {
"items": [
{
"id": 1,
"domain": "www.example.com",
"l7ResourceId": 12345,
...
}
]
}
}
}
- Получение origins:
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 механизм
maxRetries := 3
для attempt := 1 до maxRetries:
попытка запроса
если успех:
return результат
если ошибка:
waitTime = attempt² секунд // 1s, 4s, 9s
sleep(waitTime)
return ошибка
Кэширование
Стратегия:
- Проверка
sp_info(TTL 10 минут) - Если данные свежие → использовать
- Если устарели или отсутствуют → 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 │
└────────┘
↓
┌────────┐
│ Удален │
└────────┘
Константы и лимиты
Производительность
DefaultWorkerProcesses = 4 // Nginx worker processes
DefaultWorkerConnections = 64000 // Соединений на worker
Ограничения
MaxContainersPerClient = 20 // Максимум контейнеров на клиента
Timeouts
ContainerDrainTimeout = 90 // Секунд для scale down
MigrationDrainTimeout = 3600 // Секунд для миграции (1 час)
APITimeout = 120 // Секунд для API запросов
CacheTTL = 600 // Секунд (10 минут) для sp_info
Retry
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):
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 интеграция:
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
Проблема: Контейнер не запускается
Проверить:
docker logs ptaf_{client}_{num}- Наличие конфигов в
/home/install/conf/ptaf-nginx/ - Занятость портов:
ss -tuln | grep {port} - Права на
/var/log/ptaf_nginx/(должны быть 0777)
Проблема: Nginx не перезагружается
Проверить:
docker exec ptaf_{client}_{num} nginx -t- Синтаксис в
conf.d/*.conf - Логи:
/var/log/ptaf_nginx/{client}/ptaf-agent{num}/error.log
Проблема: Порты конфликтуют
Действия:
- Удалить
.ports.json - Перезапустить программу (порты будут перевыделены)
- Проверить диапазоны: 18000-20999 (HTTP), 21000-23999 (HTTPS)
Проблема: Клиент не мигрирует
Проверить:
waf_instanceвclient_info- Записи в
instances_new - Наличие маркеров
/tmp/ptaf-drain-* - Логи программы на обоих хостах
Проблема: API данные не обновляются
Действия:
- Проверить
sp_info.updated_at - Если > 10 минут → должен быть API запрос
- Проверить
modeвapps_settings:disabled→ пропускаетсяmanual→ используетmanual_infoauto→ кэш или API
Расширение системы
Добавление нового клиента
-- 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
INSERT INTO instances_new (hostname, instance)
VALUES ('waf-host-03', 'instance-d');
Кастомизация портов
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
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;