1232 lines
48 KiB
Markdown
1232 lines
48 KiB
Markdown
# Архитектура 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;
|
||
```
|