magos/DATABASE_SCHEMA.md
2026-02-24 12:08:59 +03:00

912 lines
24 KiB
Markdown
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 работает с PostgreSQL базой данных `waf_info`, которая содержит конфигурации WAF платформы. Программа использует 5 основных таблиц для получения информации о хостах, клиентах, приложениях и backend серверах.
### Используемые таблицы
1. **instances** - информация о хостах/серверах
2. **client_info** - данные клиентов и контейнеров
3. **apps_settings** - настройки приложений и кастомные директивы
4. **origins** - backend серверы (получаются через REST API)
5. **aliases** - дополнительные домены (получаются через REST API)
6. **ips** - IP адреса для WAF и antibot сетей
---
## Подключение к БД
### Параметры подключения
```bash
DB_USER=login
DB_PASSWORD=password
DB_NAME=waf_info
DB_PORT=5432
PRIMARY_DB_HOST=10.10.10.8
SECONDARY_DB_HOST=10.10.10.5
```
### Failover логика
1. Попытка подключения к Primary DB (10.10.10.8)
2. Если неудача → подключение к Secondary DB (10.10.10.5)
3. Если обе недоступны → программа останавливается
**Функция:** `connectToDB()`
---
## Таблица: instances
### Назначение
Хранит информацию о хостах/серверах в кластере PTAF.
### Структура
| Поле | Тип | Описание | Пример |
|------|-----|----------|--------|
| `hostname` | TEXT | Hostname сервера | `host01.example.com` |
| `instance` | TEXT | Идентификатор инстанса | `waf-prod-01` |
### Используемые запросы
#### 1. Получение instance по hostname
```sql
SELECT instance
FROM instances
WHERE hostname = $1;
```
**Функция:** `getInstanceByHostname(hostname string)`
**Использование:** При старте программы для определения какой это инстанс.
**Пример:**
```go
hostname := "host01.example.com"
instance, err := getInstanceByHostname(db, hostname)
// instance = "waf-prod-01"
```
---
#### 2. Проверка принадлежности хоста к инстансу
```sql
SELECT COUNT(*)
FROM instances
WHERE hostname = $1 AND instance = $2;
```
**Функция:** `checkHostBelongsToInstance(hostname, instance string)`
**Использование:** Валидация что хост принадлежит данному инстансу.
**Пример:**
```go
hostname := "host01.example.com"
instance := "waf-prod-01"
err := checkHostBelongsToInstance(db, hostname, instance)
// err == nil если хост принадлежит инстансу
```
---
### Примеры данных
```sql
INSERT INTO instances (hostname, instance) VALUES
('host01.example.com', 'waf-prod-01'),
('host02.example.com', 'waf-prod-01'),
('host03.example.com', 'waf-prod-02');
```
---
## Таблица: client_info
### Назначение
Хранит информацию о клиентах: количество контейнеров, привязка к инстансу.
### Структура
| Поле | Тип | Описание | Пример |
|------|-----|----------|--------|
| `client_title` | TEXT | Идентификатор клиента | `CLIENT001` |
| `containers_count` | INTEGER | Количество контейнеров | `3` |
| `ptaf_config` | TEXT | Конфигурация PTAF (JSON) | `{"key": "value"}` |
| `fluent_bit_port` | INTEGER | Порт Fluent Bit | `24224` |
| `waf_instance` | TEXT | Instance для apps_settings | `waf-prod-01` |
### Используемые запросы
#### 1. Получение клиентов по instance
```sql
SELECT
containers_count,
ptaf_config,
client_title,
fluent_bit_port,
waf_instance
FROM client_info
WHERE waf_instance = $1;
```
**Функция:** `getClientInfoByInstance(instance string)`
**Использование:** Получение всех клиентов для данного инстанса.
**Пример:**
```go
instance := "waf-prod-01"
clients, err := getClientInfoByInstance(db, instance)
// clients = [{ContainersCount: 3, ClientTitle: "CLIENT001", ...}, ...]
```
---
#### 2. Получение клиента по client_title
```sql
SELECT
containers_count,
ptaf_config,
client_title,
fluent_bit_port,
waf_instance
FROM client_info
WHERE client_title = $1;
```
**Функция:** `getClientInfoByClientTitle(clientTitle string)`
**Использование:** Получение информации о конкретном клиенте.
---
### Примеры данных
```sql
INSERT INTO client_info
(client_title, containers_count, ptaf_config, fluent_bit_port, waf_instance)
VALUES
('CLIENT001', 3, '{"version": "1.0"}', 24224, 'waf-prod-01'),
('CLIENT002', 5, '{"version": "1.0"}', 24225, 'waf-prod-01'),
('CLIENT003', 2, '{"version": "1.0"}', 24226, 'waf-prod-02');
```
---
### Ключевое поле: containers_count
**Назначение:** Определяет сколько Docker контейнеров нужно создать для клиента.
**Логика:**
- `containers_count = 3` → создать ptaf_client_001, ptaf_client_002, ptaf_client_003
- При увеличении → создаются новые контейнеры
- При уменьшении → лишние контейнеры помечаются на graceful shutdown (90s)
**Пример изменения:**
```sql
-- Увеличить до 5 контейнеров
UPDATE client_info
SET containers_count = 5
WHERE client_title = 'CLIENT001';
-- Manager автоматически создаст ptaf_client_004 и ptaf_client_005
```
---
## Таблица: apps_settings
### Назначение
Основная таблица с настройками приложений и кастомными директивами Nginx/Angie.
### Структура
| Поле | Тип | Описание | Значение по умолчанию |
|------|-----|----------|----------------------|
| `l7resourceid` | INTEGER | ID ресурса | Первичный ключ |
| `client_title` | TEXT | Клиент (FK) | `CLIENT001` |
| `waf_enabled` | BOOLEAN | WAF включен | `true` |
| `sni` | INTEGER | SNI режим (0=keepalive on, 1=keepalive off) | `0` |
| `custom_input_http_ports` | INTEGER[] | HTTP порты для прослушивания | `{80, 8080}` |
| `custom_input_https_ports` | INTEGER[] | HTTPS порты для прослушивания | `{443, 8443}` |
| `custom_output_http_ports` | INTEGER[] | HTTP порты к backend | `{80}` |
| `custom_output_https_ports` | INTEGER[] | HTTPS порты к backend | `{443}` |
| `upstream_angie_custom` | TEXT | Кастомные upstream директивы (Angie) | `NULL` |
| `upstream_nginx_custom` | TEXT | Кастомные upstream директивы (Nginx) | `NULL` |
| `server_angie_custom` | TEXT | Полная замена server блока (Angie) | `NULL` |
| `server_nginx_custom` | TEXT | Полная замена server блока (Nginx) | `NULL` |
| `server_directives_angie_custom` | TEXT | Кастомные server директивы (Angie) | `NULL` |
| `server_directives_nginx_custom` | TEXT | Кастомные server директивы (Nginx) | `NULL` |
| `location_angie_custom` | TEXT | Кастомные location блоки (Angie) | `NULL` |
| `location_nginx_custom` | TEXT | Кастомные location блоки (Nginx) | `NULL` |
---
### Используемые запросы
#### Получение настроек по client_title
```sql
SELECT
l7resourceid,
waf_enabled,
custom_input_http_ports,
custom_input_https_ports,
custom_output_http_ports,
custom_output_https_ports,
upstream_angie_custom,
server_nginx_custom,
server_angie_custom,
client_title,
upstream_nginx_custom,
location_angie_custom,
location_nginx_custom,
server_directives_angie_custom,
server_directives_nginx_custom,
sni
FROM apps_settings
WHERE client_title = $1
ORDER BY l7resourceid ASC;
```
**Функция:** `getAppsSettingsByClientTitle(clientTitle string)`
**Использование:** Получение всех настроек приложений для клиента.
---
### Примеры данных
```sql
INSERT INTO apps_settings
(l7resourceid, client_title, waf_enabled, sni,
custom_input_http_ports, custom_input_https_ports,
custom_output_http_ports, custom_output_https_ports)
VALUES
(12323, 'CLIENT001', true, 0, '{80}', '{443}', '{80}', '{443}'),
(12324, 'CLIENT001', true, 0, '{80,8080}', '{443,8443}', '{80}', '{443}'),
(15000, 'CLIENT002', true, 0, '{80}', '{443}', '{80}', '{443}');
```
---
### Кастомные поля (подробно)
#### upstream_angie_custom / upstream_nginx_custom
**Режим:** REPLACE (полная замена keepalive директив)
**NULL значение:**
```nginx
upstream backend {
server 127.0.0.1:21000;
# Default upstream directives
keepalive 60;
keepalive_timeout 70s;
}
```
**НЕ NULL значение:**
```nginx
upstream backend {
server 127.0.0.1:21000;
# Custom upstream directives (replace defaults)
keepalive 200;
keepalive_timeout 90s;
least_conn;
}
```
**Пример SQL:**
```sql
-- Изменить балансировку на least_conn
UPDATE apps_settings
SET upstream_angie_custom = 'keepalive 60;
keepalive_timeout 70s;
least_conn;'
WHERE l7resourceid = 12323;
-- Вернуть дефолты
UPDATE apps_settings
SET upstream_angie_custom = NULL
WHERE l7resourceid = 12323;
```
---
#### server_directives_angie_custom / server_directives_nginx_custom
**Режим:** REPLACE (замена else блока в server)
**NULL значение:**
```nginx
server {
listen 443 ssl;
# Default directives
keepalive_timeout 80s;
send_timeout 60s;
proxy_connect_timeout 60s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
large_client_header_buffers 4 128k;
proxy_buffers 4 32k;
proxy_buffer_size 16k;
proxy_busy_buffers_size 32k;
}
```
**НЕ NULL значение:**
```nginx
server {
listen 443 ssl;
# Custom directives (replace defaults)
keepalive_timeout 120s;
client_max_body_size 100m;
# Нужно указать ВСЕ директивы, включая буферы!
}
```
**Пример SQL:**
```sql
-- Увеличить таймауты
UPDATE apps_settings
SET server_directives_angie_custom = 'keepalive_timeout 120s;
send_timeout 90s;
proxy_connect_timeout 90s;
proxy_read_timeout 180s;
proxy_send_timeout 180s;
large_client_header_buffers 4 128k;
proxy_buffers 4 32k;
proxy_buffer_size 16k;'
WHERE l7resourceid = 12323;
```
---
#### server_angie_custom / server_nginx_custom
**Режим:** SERVER_BLOCK (полная замена всего server блока)
**NULL значение:** Используется стандартный шаблон server блока
**НЕ NULL значение:** Полная замена server блока
**Пример SQL:**
```sql
-- Создать полностью кастомный server блок
UPDATE apps_settings
SET server_angie_custom = 'server {
listen 9999;
server_name special.example.com;
location / {
return 200 "Custom response";
}
}'
WHERE l7resourceid = 12323;
```
---
#### location_angie_custom / location_nginx_custom
**Режим:** APPEND (добавление к дефолтному location)
**NULL значение:** Только стандартный `location / { proxy_pass }`
**НЕ NULL значение:** Добавляются дополнительные location блоки
**Пример SQL:**
```sql
-- Добавить location для API
UPDATE apps_settings
SET location_nginx_custom = 'location /api {
proxy_pass http://api.backend.com;
proxy_set_header X-API-Key secret;
}
location /static {
alias /var/www/static;
}'
WHERE l7resourceid = 12323;
```
---
#### sni (Server Name Indication)
**Значения:**
- `0` - Keepalive ВКЛЮЧЕН (дефолт)
- `1` - Keepalive ОТКЛЮЧЕН
**Когда sni = 1:**
- Директивы `keepalive` и `keepalive_timeout` НЕ добавляются в upstream
- Используется для SNI passthrough (прямая передача без терминации SSL)
**Пример SQL:**
```sql
-- Отключить keepalive для SNI passthrough
UPDATE apps_settings
SET sni = 1
WHERE l7resourceid = 12323;
```
---
#### custom_input_*_ports и custom_output_*_ports
**custom_input_http_ports / custom_input_https_ports:**
- Порты на которых Angie и Nginx контейнеры будут слушать
- Angie слушает на этих портах (80, 443, 8080, 8443)
- Nginx контейнеры слушают на этих же портах
**custom_output_http_ports / custom_output_https_ports:**
- Порты к которым Nginx контейнеры будут проксировать на backend
- Обычно {80} для HTTP и {443} для HTTPS
- Могут быть нестандартные порты (8080, 8443)
**Пример:**
```sql
-- Приложение слушает на портах 80, 8080 (HTTP) и 443, 8443 (HTTPS)
-- Backend работает на порту 443
UPDATE apps_settings
SET
custom_input_http_ports = '{80, 8080}',
custom_input_https_ports = '{443, 8443}',
custom_output_http_ports = '{80}',
custom_output_https_ports = '{443}'
WHERE l7resourceid = 12323;
```
**Результат:**
```nginx
# Angie
server {
listen 80; # custom_input_http_ports[0]
listen 8080; # custom_input_http_ports[1]
listen 443 ssl; # custom_input_https_ports[0]
listen 8443 ssl; # custom_input_https_ports[1]
}
# Nginx upstream
upstream backend {
server 185.22.65.148:443; # custom_output_https_ports[0]
}
```
---
## Таблица: ips
### Назначение
Хранит IP адреса для WAF и antibot сетей (белые списки).
### Структура
| Поле | Тип | Описание |
|------|-----|----------|
| `antibot_networks` | TEXT[] | IP сети для antibot |
| `waf_networks` | TEXT[] | IP сети для WAF |
### Используемые запросы
```sql
SELECT antibot_networks, waf_networks
FROM ips;
```
**Функция:** `loadIPsFromDB()`
**Использование:** При старте программы для загрузки IP сетей.
**Пример данных:**
```sql
INSERT INTO ips (antibot_networks, waf_networks) VALUES
(
'{172.16.0.0/12, 10.0.0.0/8}',
'{172.16.0.0/12, 10.100.0.0/16}'
);
```
**Использование в конфигах:**
```nginx
server {
# Доверенные IP для получения реального IP клиента
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 10.100.0.0/16;
real_ip_header X-Forwarded-For;
}
```
---
## REST API (origins и aliases)
### Origins (Backend серверы)
**Не хранятся в БД**, получаются через REST API.
**Эндпоинт:** `/api/waf-proxy/get-apps-and-resourses`
**Функция:** `makeAPIRequest()`, `getResourceDataFromCache()`
**Пример ответа:**
```json
{
"data": {
"result": {
"items": [
{
"id": 1,
"ip": "185.22.65.148",
"weight": 100,
"mode": "active",
"comment": "Primary backend"
},
{
"id": 2,
"ip": "185.22.65.149",
"weight": 50,
"mode": "backup",
"comment": "Backup backend"
}
]
}
}
}
```
**Использование в конфигах:**
```nginx
upstream backend {
server 185.22.65.148:443 weight=100;
server 185.22.65.149:443 weight=50 backup;
}
```
---
### Aliases (Дополнительные домены)
**Не хранятся в БД**, получаются через REST API.
**Эндпоинт:** `/api/waf-proxy/get-apps-and-resourses`
**Пример ответа:**
```json
{
"data": {
"result": {
"items": [
{
"id": 1,
"alias": "www.example.com"
},
{
"id": 2,
"alias": "app.example.com"
}
]
}
}
}
```
**Использование в конфигах:**
```nginx
server {
listen 443 ssl;
server_name example.com www.example.com app.example.com;
}
```
---
## Связи между таблицами
```
instances
↓ (hostname → instance)
client_info (waf_instance)
↓ (client_title)
apps_settings (client_title)
↓ (l7resourceid через API)
Origins (от REST API)
Aliases (от REST API)
```
**Поток данных:**
1. Program запускается на хосте с hostname = `host01.example.com`
2. Запрос к `instances` → получаем `instance = "waf-prod-01"`
3. Запрос к `client_info` WHERE `waf_instance = "waf-prod-01"` → получаем клиентов
4. Для каждого клиента запрос к `apps_settings` WHERE `client_title = "CLIENT001"`
5. Для каждого ресурса API запрос → получаем origins и aliases
6. Генерируем конфиги на основе всех данных
---
## Примеры типичных запросов
### Получить все ресурсы клиента
```sql
SELECT
a.l7resourceid,
a.custom_input_https_ports,
a.upstream_angie_custom,
c.containers_count
FROM apps_settings a
JOIN client_info c ON a.client_title = c.client_title
WHERE a.client_title = 'CLIENT001'
ORDER BY a.l7resourceid;
```
---
### Изменить количество контейнеров
```sql
-- Увеличить до 5
UPDATE client_info
SET containers_count = 5
WHERE client_title = 'CLIENT001';
-- Проверка
SELECT client_title, containers_count
FROM client_info
WHERE client_title = 'CLIENT001';
```
---
### Добавить кастомную балансировку
```sql
UPDATE apps_settings
SET upstream_nginx_custom = 'keepalive 60;
keepalive_timeout 70s;
least_conn;'
WHERE l7resourceid = 12323;
-- Проверка
SELECT l7resourceid, upstream_nginx_custom
FROM apps_settings
WHERE l7resourceid = 12323;
```
---
### Включить SNI passthrough
```sql
UPDATE apps_settings
SET sni = 1
WHERE l7resourceid = 12323;
-- Проверка
SELECT l7resourceid, sni
FROM apps_settings
WHERE sni = 1;
```
---
### Добавить дополнительные порты
```sql
UPDATE apps_settings
SET
custom_input_https_ports = '{443, 8443, 9443}',
custom_output_https_ports = '{443, 8443, 9443}'
WHERE l7resourceid = 12323;
-- Проверка
SELECT l7resourceid, custom_input_https_ports, custom_output_https_ports
FROM apps_settings
WHERE l7resourceid = 12323;
```
---
## Индексы (рекомендуемые)
```sql
-- Для быстрого поиска по hostname
CREATE INDEX idx_instances_hostname ON instances(hostname);
-- Для быстрого поиска клиентов по instance
CREATE INDEX idx_client_info_waf_instance ON client_info(waf_instance);
CREATE INDEX idx_client_info_client_title ON client_info(client_title);
-- Для быстрого поиска настроек по client_title
CREATE INDEX idx_apps_settings_client_title ON apps_settings(client_title);
CREATE INDEX idx_apps_settings_l7resourceid ON apps_settings(l7resourceid);
```
---
## Миграции и изменения схемы
### Добавление нового кастомного поля
```sql
-- Добавить новое поле для header манипуляций
ALTER TABLE apps_settings
ADD COLUMN headers_custom TEXT DEFAULT NULL;
-- Обновить код для чтения этого поля
```
---
### Изменение типа поля
```sql
-- Изменить тип sni на boolean (если сейчас integer)
ALTER TABLE apps_settings
ALTER COLUMN sni TYPE BOOLEAN
USING (sni::integer = 0);
```
---
## Best Practices
### 1. NULL vs пустая строка
**Правильно:**
```sql
UPDATE apps_settings
SET upstream_angie_custom = NULL -- NULL для дефолтов
WHERE l7resourceid = 12323;
```
**Неправильно:**
```sql
UPDATE apps_settings
SET upstream_angie_custom = '' -- Пустая строка может вызвать ошибки
WHERE l7resourceid = 12323;
```
---
### 2. Массивы портов
**Правильно:**
```sql
UPDATE apps_settings
SET custom_input_https_ports = '{443, 8443}' -- PostgreSQL array
WHERE l7resourceid = 12323;
```
**Неправильно:**
```sql
UPDATE apps_settings
SET custom_input_https_ports = '443,8443' -- Строка, не массив
WHERE l7resourceid = 12323;
```
---
### 3. Многострочные кастомные директивы
**Правильно:**
```sql
UPDATE apps_settings
SET server_directives_angie_custom = 'keepalive_timeout 80s;
send_timeout 60s;
proxy_buffers 4 32k;'
WHERE l7resourceid = 12323;
```
**Также правильно (с экранированием):**
```sql
UPDATE apps_settings
SET server_directives_angie_custom = E'keepalive_timeout 80s;\nsend_timeout 60s;'
WHERE l7resourceid = 12323;
```
---
## Мониторинг БД
### Проверка соединения
```sql
-- Проверить активные соединения
SELECT
datname,
usename,
client_addr,
state,
query_start
FROM pg_stat_activity
WHERE datname = 'waf_info';
```
---
### Проверка данных
```sql
-- Количество клиентов на каждом instance
SELECT waf_instance, COUNT(*) as clients_count
FROM client_info
GROUP BY waf_instance;
-- Количество ресурсов на клиента
SELECT client_title, COUNT(*) as resources_count
FROM apps_settings
GROUP BY client_title;
-- Клиенты с кастомными upstream директивами
SELECT client_title, l7resourceid
FROM apps_settings
WHERE upstream_angie_custom IS NOT NULL
OR upstream_nginx_custom IS NOT NULL;
```
---
## Резервное копирование
```bash
# Дамп всей БД
pg_dump -h 10.100.10.8 -U install -d waf_info > waf_info_backup.sql
# Дамп только структуры
pg_dump -h 10.100.10.8 -U install -d waf_info --schema-only > schema.sql
# Дамп только данных
pg_dump -h 10.100.10.8 -U install -d waf_info --data-only > data.sql
# Восстановление
psql -h 10.100.10.8 -U install -d waf_info < waf_info_backup.sql
```
---
## Итого
### Основные таблицы
| Таблица | Записей (примерно) | Частота изменений |
|---------|-------------------|-------------------|
| instances | 10-50 | Редко (при добавлении хостов) |
| client_info | 50-500 | Редко (при добавлении клиентов) |
| apps_settings | 500-5000 | Часто (изменение настроек) |
| ips | 1 | Очень редко |
### Ключевые связи
- `instances.instance``client_info.waf_instance`
- `client_info.client_title``apps_settings.client_title`
- `apps_settings.l7resourceid` → REST API (origins, aliases)
### Кастомизация
Все кастомные поля в `apps_settings`:
- 8 полей для кастомных директив
- 4 режима: REPLACE (upstream, server_directives), SERVER_BLOCK, APPEND (location)
- NULL = дефолты, NOT NULL = кастом