magos/ARCHITECTURE.md
2026-06-10 16:21:30 +03:00

918 lines
No EOL
39 KiB
Markdown
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

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

# Архитектура Magos
## Обзор системы
**Magos** - это система управления WAF (Web Application Firewall) контейнерами на базе Nginx/Angie, которая:
- Автоматически разворачивает и управляет Docker-контейнерами с Nginx
- Настраивает балансировщик Angie для распределения трафика
- Интегрируется с API ServicePipe для получения конфигурации
- Поддерживает graceful shutdown при масштабировании и миграции
- Обеспечивает изоляцию клиентов через отдельные контейнеры
- Поддерживает два WAF-вендора: **PTAF** (Docker-контейнеры) и **SW/SolidWall** (нативный процесс)
### Ключевые особенности:
- **Многоклиентовая архитектура** - каждый клиент имеет свои контейнеры
- **Поддержка двух вендоров** - PTAF (Docker) и SolidWall (нативный nginx)
- **Динамическое управление портами** - автоматическое выделение портов из пулов с учётом всех состояний сокетов
- **Кэширование данных** - минимизация обращений к API
- **Failover БД** - автоматическое переключение между primary/secondary
- **Graceful draining** - безопасное удаление контейнеров без потери трафика
- **Умный lock-файл** - автоматическая очистка при падении процесса
---
## Компоненты системы
### 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 # Управление портами
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
#### Таблица `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 # Количество контейнеров (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)
- ptaf_fallback_code # Код ответа ptaf_fallback: число или 'pass' (дефолт: 418)
- sp_antiddos # Использовать антиддос сети в set_real_ip (дефолт: true)
- real_ip_networks # Кастомные сети для set_real_ip если sp_antiddos = false (inet[])
- worker_processes # Количество worker процессов nginx внутри контейнера (дефолт: 4)
- one_container # Все SID в один контейнер (дефолт: true). При false блочный режим
- sid_block # Блоки SID для разбивки по контейнерам: {sid1,sid2}{sid3} (TEXT)
```
**Важно:** `waf_instance` определяет на каком хосте должен работать клиент
**containers_count = 0:** все контейнеры клиента помечаются для drain, Angie-конфиг удаляется
#### Таблица `apps_settings`
Настройки каждого L7 ресурса (домена)
```sql
Основные поля:
- 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/SW)
- custom_input_https_ports[] # HTTPS порты для прослушивания (Angie/SW)
- custom_output_http_ports[] # HTTP порты для проксирования к origin
- custom_output_https_ports[] # HTTPS порты для проксирования к origin
Кастомизация Nginx (PTAF):
- upstream_nginx_custom # Кастомные upstream директивы
- 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_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)
- x_sid_check # Маршрутизация в Angie по X-Sid заголовку (дефолт: false)
```
#### Таблица `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. Создание 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 на этом хосте │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Получить всех клиентов для instance │
│ (getClientInfoByInstance) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Определить vendor (ptaf/sw) │
│ → processPTAFClient / processSWClient │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Финальная проверка drain Angie-конфигов│
│ (processDrainingAngieConfigs) │
└─────────────────────────────────────────┘
```
### 3. Обработка PTAF-клиента
```
1. Проверка принадлежности хоста к WAF instance
├─ Не принадлежит → handleClientMigration (drain всех контейнеров + Angie конфига)
└─ Принадлежит → продолжаем
2. containers_count = 0?
└─ Да → drain всех контейнеров + Angie конфига, выход
3. cleanStaleDrainMarkers — очистка устаревших маркеров
4. fetchPTAFResourcesData — получение данных ресурсов из API/кеша
5. one_container = false И sid_block задан?
├─ Да → processPTAFClientBlocks (блочный режим, см. ниже)
└─ Нет → стандартный режим:
6. resolvePortsForClient — загрузка/выделение портов
├─ .ports.json существует → переиспользуем порты
├─ Scale up → сохраняем старые порты, добавляем новые
└─ Нет файла → выделяем все заново
7. generateAngieConfigsWithoutReload — генерация Angie конфига
8. setupContainers — генерация конфигов и управление контейнерами
├─ Запущен недавно, порты не слушают → пересоздаём
├─ Compose изменился → пересоздаём
├─ Конфиг изменился → nginx -s reload
└─ Новый → запускаем (с ожиданием освобождения портов)
9. Проверка портов → reload Angie если нужно
10. processDrainingContainers — drain лишних контейнеров
```
### 4. Блочный режим (one_container = false)
Используется когда разные группы SID должны обслуживаться разными наборами контейнеров.
```
sid_block = '{10111,10231}{10112}{10113}'
Блок 1 → SID 10111, 10231 → контейнеры {clientTitle}_block1_AZ3_hn01_a001..a{N}
Блок 2 → SID 10112 → контейнеры {clientTitle}_block2_AZ3_hn01_a001..a{N}
Блок 3 → SID 10113 → контейнеры {clientTitle}_block3_AZ3_hn01_a001..a{N}
```
- `containers_count` применяется к каждому блоку независимо
- Каждый блок получает свои Angie конфиги и nginx конфиги только со своими SID
- SID не указанные ни в одном блоке не обрабатываются
- Имя контейнера: `{clientTitle}_block{N}_{AZx}_{hn/n}{NN}_a{001}`
- Каждый блок обрабатывается через стандартный `resolvePortsForClient``generateAngieConfigsWithoutReload``setupContainers`
### 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`:
#### Режим `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
)
```
**Важно:** На хостах необходимо зарезервировать этот диапазон чтобы ядро не использовало его для исходящих ephemeral-соединений:
```bash
sysctl -w net.ipv4.ip_local_reserved_ports=18000-23999
```
### Алгоритм выделения портов
```
┌────────────────────────────────────────────────┐
│ 1. Получить занятые порты (ss -tuanl) │
│ Включает: LISTEN, ESTAB, TIME-WAIT │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ 2. Загрузить сохраненные порты │
│ из .ports.json (если есть) │
│ ├─ Scale up → сохранить старые, добавить │
│ └─ Нет файла → выделить все заново │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ 3. Для каждого ресурса × контейнера: │
│ - Определить нужные порты │
│ - Выделить свободные Docker порты │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ 4. Сохранить в .ports.json │
└────────────────────────────────────────────────┘
```
### Структура портов
```go
type PortMapping struct {
HTTPPorts map[int]int // map[customPort]dockerPort
HTTPSPorts map[int]int // map[customPort]dockerPort
}
```
---
## 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 и миграция
### Типы drain операций
#### 1. Масштабирование вниз (Scale Down)
**Timeout:** 90 секунд
```
Триггер: containers_count уменьшен в БД
Действие: лишние контейнеры помечаются маркером /tmp/ptaf-drain-ptaf_{client}_{num}
Через 90с: docker-compose down + удаление файлов
```
#### 2. Отключение клиента (containers_count = 0)
**Timeout:** 90 секунд
```
Триггер: containers_count = 0
Действие: все контейнеры + Angie-конфиг помечаются для drain
Через 90с: удаление контейнеров и конфига Angie
```
#### 3. Миграция между хостами
**Timeout:** 3600 секунд (1 час)
```
Триггер: waf_instance изменился в БД
Действие: контейнеры и Angie-конфиг помечаются маркером :migration
Через 1ч: проверка актуальности → удаление если клиент не вернулся
```
### Формат маркеров
```
/tmp/ptaf-drain-ptaf_{clientTitle}_{001} — контейнер (содержит timestamp или timestamp:migration)
/tmp/ptaf-angie-drain-{clientTitle} — Angie конфиг (аналогичный формат)
```
---
## Кастомизация конфигов через SERVER_BLOCK
Формат для полей `server_nginx_custom` и `server_angie_custom`:
```
[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]
```
Синтаксис: `[SERVER_BLOCK:{порт}:{server_name}:{alias1}:{alias2}...]`
- Поддерживает произвольное количество алиасов
- Несколько SERVER_BLOCK в одном поле — поддерживается
- Для Angie: SERVER_BLOCK создаёт отдельный server-блок с проксированием на контейнеры
- Для nginx: SERVER_BLOCK создаёт отдельный server-блок с проксированием на origin
---
## Метод балансировки
Поле `balancing_method` в `apps_settings`. Записывается **без** точки с запятой:
```sql
UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345;
```
Допустимые значения: `least_conn`, `ip_hash`, `random`, `random two least_conn` и др.
Применяется первой строкой в upstream-блоке для Angie, nginx и SW.
---
## proxy_next_upstream
Поле `proxy_next_upstream_codes` в `apps_settings`. Записывается через запятую **без** `http_` префикса:
```sql
-- Убрать 500 из списка (бэкенд вернул 500, Angie не должен делать retry)
UPDATE apps_settings SET proxy_next_upstream_codes = '502,503,504' WHERE l7resourceid = 12345;
```
Дефолт: `500,502,503,504`. Итоговая строка в конфиге:
```nginx
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
```
---
## Запуск контейнеров
### Ожидание освобождения портов
Перед запуском контейнера утилита проверяет занятость портов через `ss -tuanl` (все состояния включая TIME-WAIT и ESTAB). Порт считается занятым если:
- Есть `LISTEN` сокет
- Есть `ESTAB` от `docker-proxy` (старый контейнер ещё не остановился)
После остановки контейнера при пересоздании:
1. Ожидаем закрытия ESTAB соединений Angie (до 30 сек)
2. Ожидаем полного освобождения портов (до 180 сек)
3. Запуск нового контейнера (до 3 попыток с паузой 3 сек)
### Сохранение портов
`.ports.json` сохраняется **только после успешного запуска контейнера**:
- При пересоздании контейнера (`recreateContainer`) — сохраняется если `recreateContainer` вернул `true`
- При первом запуске нового контейнера — сохраняется если контейнер успешно запустился
- При неизменённом compose — сохраняется сразу (порты не менялись)
Если контейнер не запустился — `.ports.json` остаётся старым, и при следующем запуске magos порты будут перевыделены заново.
### Проверка после запуска
Если контейнер запущен менее 5 минут и порты не слушают — автоматическое пересоздание.
---
## Маршрутизация по X-Sid
### Проблема
Когда два разных тенанта WAF имеют одинаковый FQDN — возникает конфликт:
- На уровне Angie: два server-блока с одинаковым `server_name` — Angie выберет первый
- На уровне nginx внутри контейнера: два server-блока с одинаковым `server_name` — nginx выберет первый
### Решение
Используется комбинация двух механизмов:
**1. `x_sid_check = true` в `apps_settings` — решает проблему на уровне Angie:**
Вместо `server_name domain.ru` генерируется `server_name _` и map блок:
```nginx
map $http_x_sid $x_sid_upstream_https_443 {
default "";
"10111" "secure10111_443_domain.ru";
"10112" "secure10112_443_domain.ru";
}
server {
listen 443 ssl;
server_name _;
location / {
if ($x_sid_upstream_https_443 = "") { return 444; }
proxy_pass http://$x_sid_upstream_https_443;
}
}
```
Клиент должен передавать заголовок `X-Sid: {l7ResourceID}` в каждом запросе.
**Ограничения `x_sid_check`:**
- `proxy_pass` с переменной не использует keepalive пул upstream
- max_fails/fail_timeout для контейнера не работают
- Для Angie → контейнер (`127.0.0.1`) это некритично
**2. Блочный режим (`one_container = false`) — решает проблему внутри контейнера:**
Конфликтующие SID разносятся по разным контейнерам. Каждый контейнер содержит только свои SID — конфликта server_name внутри контейнера нет.
При блочном режиме также генерируются отдельные Angie конфиги:
- `angie-ptaf-{clientTitle}_block1.conf` — SID блока 1
- `angie-ptaf-{clientTitle}_block2.conf` — SID блока 2
Если FQDN одинаковый в разных блоках — нужно включить `x_sid_check = true` для этих ресурсов чтобы Angie корректно маршрутизировал между блоками.
### Итоговая схема при одинаковых FQDN
```
x_sid_check = true + one_container = false + sid_block
Клиент (X-Sid: 10111, Host: domain.ru)
→ Angie: map[$http_x_sid] → upstream блока 1 → контейнер block1
→ nginx block1: server_name domain.ru → upstream к origin тенанта 1
Клиент (X-Sid: 10112, Host: domain.ru)
→ Angie: map[$http_x_sid] → upstream блока 2 → контейнер block2
→ nginx block2: server_name domain.ru → upstream к origin тенанта 2
```
Имя Docker-контейнера формируется динамически на основе hostname хоста:
```
PTAF-TB-docker-AZ3-HW-node01 → {clientTitle}_AZ3_hn01_a001
PTAF-TB-docker-AZ1-node04 → {clientTitle}_AZ1_n04_a001
```
**Правила парсинга hostname:**
- `AZ{N}` — номер зоны доступности
- Наличие `HW` в hostname → префикс `hn` (hardware node), отсутствие → `n`
- `node{NN}` — номер ноды
- `a{001}` — порядковый номер агента
**Fallback:** если hostname не соответствует формату — используется старый формат `ptaf_{clientTitle}_{001}`.
**Важно:** директории на диске (`/home/install/ptaf/{clientTitle}/{clientTitle}-ptaf-agent{001}/`) не меняются — только `container_name` и `hostname` в Docker.
---
### Производительность
```go
DefaultWorkerProcesses = 4 // Nginx worker processes (дефолт, переопределяется в client_info)
DefaultWorkerConnections = 64000 // Соединений на worker
worker_rlimit_nofile = 1048576 // Лимит файловых дескрипторов
```
### Timeouts
```go
ContainerDrainTimeout = 90 // Секунд для scale down / отключения
MigrationDrainTimeout = 3600 // Секунд для миграции (1 час)
APITimeout = 120 // Секунд для API запросов
CacheTTL = 600 // Секунд (10 минут) для sp_info
MaxLockAge = 15 мин // После этого lock считается устаревшим
```
### Буферы (Angie и nginx)
```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 (предотвращение routing loops). Всегда добавляются в `set_real_ip_from` в Angie.
**Antibot Networks** - используются для `set_real_ip_from` в Angie по умолчанию (`sp_antiddos = true`).
**Custom Real IP Networks** - если у клиента `sp_antiddos = false` и заполнено поле `real_ip_networks` в `client_info` — используются вместо Antibot Networks в `set_real_ip_from`. Применяется для клиентов без антиддоса перед Angie.
Внутри контейнера nginx всегда использует `set_real_ip_from 172.16.0.0/12` (Docker bridge сеть Angie).
### 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`** — полная замена дефолтов для Angie
**Кастомизация через `custom_sw_nginx_ssl`** — полная замена дефолтов для SW
### mTLS
**Автоматическая замена путей:**
```
/etc/ssl/certs/ → /opt/ptaf/conf/main/
/etc/ssl/private/ → /opt/ptaf/conf/main/
```
Применяется во всех 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`:
```yaml
environment:
- EVENT_SENDER_ENABLED=1
- EVENT_SENDER_HOST=172.17.0.1
- EVENT_SENDER_PORT={fluent_bit_port}
- EVENT_SENDER_TIMEOUT=3
```
---
## Troubleshooting
### Проблема: Контейнер не запускается
**Проверить:**
1. `docker logs ptaf_{client}_{num}`
2. Наличие конфигов в `/home/install/conf/ptaf-nginx/`
3. Занятость портов: `ss -tuanl | grep {port}`
4. Права на `/var/log/ptaf_nginx/` (должны быть 0777)
5. Зомби docker-proxy процессы: `ps aux | grep docker-proxy | grep -v grep`
### Проблема: Порты конфликтуют
**Действия:**
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`
### Проблема: Клиент не мигрирует
**Проверить:**
1. `waf_instance` в `client_info`
2. Записи в `instances_new`
3. Наличие маркеров `/tmp/ptaf-drain-*`
4. Логи программы на обоих хостах
### Проблема: SW nginx не перезагружается
**Проверить:**
1. `solidwall-nginx -t`
2. Наличие симлинков в `sites-enabled/`
3. Синтаксис в `sites-available/*.conf`
---
## Расширение системы
### Добавление нового клиента (PTAF)
```sql
INSERT INTO client_info (client_title, containers_count, waf_instance)
VALUES ('new-client', 2, 'instance-a');
INSERT INTO apps_settings (l7resourceid, client_title, waf_vendor, waf_enabled, mode)
VALUES (12345, 'new-client', 'ptaf', true, 'auto');
```
### Добавление нового клиента (SW)
```sql
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 client_info SET debug = true WHERE client_title = 'CLIENT001';
```
### Задать индивидуальный Docker-образ для клиента
```sql
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;
```
### Включить маршрутизацию по X-Sid для ресурса
```sql
-- Включить x_sid_check для конкретного ресурса
UPDATE apps_settings
SET x_sid_check = true
WHERE l7resourceid = 10111;
```
Клиент должен передавать заголовок `X-Sid: 10111` в каждом запросе.
```sql
-- Три блока SID, каждый в своём наборе контейнеров
UPDATE client_info
SET one_container = false,
sid_block = '{10111,10231}{10112}{10113}'
WHERE client_title = 'CLIENT001';
```
Формат `sid_block`: `{sid1,sid2}{sid3}{sid4,sid5,sid6}` — каждая пара `{}` это один блок.
```sql
-- Пропускать трафик если WAF недоступен
UPDATE client_info SET ptaf_fallback_code = 'pass' WHERE client_title = 'CLIENT001';
-- Вернуть 503 вместо 418
UPDATE client_info SET ptaf_fallback_code = '503' WHERE client_title = 'CLIENT001';
```
### Настроить количество worker процессов
```sql
UPDATE client_info SET worker_processes = 8 WHERE client_title = 'CLIENT001';
```
Изменение вызывает `nginx -s reload` внутри контейнера без пересоздания контейнера.
```sql
UPDATE client_info
SET sp_antiddos = false,
real_ip_networks = '{10.0.0.0/8, 192.168.1.0/24}'
WHERE client_title = 'CLIENT001';
```