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