api_sync/ARCHITECTURE.md
2026-06-10 18:04:34 +03:00

231 lines
No EOL
17 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.

# Архитектура API Sync
## Обзор
API Sync — однопроходная утилита (не демон), запускаемая по cron. Поддерживает два режима запуска: основной и проверка дублей по ключу `--cd`.
---
## Режимы запуска
```bash
./API_sync # основной запуск
./API_sync --cd # только проверка дублирующихся доменов/алиасов
```
Пример cron-конфигурации:
```
# Основной запуск каждые 15 минут
*/15 * * * * install cd /home/install && /home/install/API_sync >> /var/log/API_sync.log 2>&1
# Проверка дублей каждый час
0 * * * * install cd /home/install && /home/install/API_sync --cd >> /var/log/API_sync.log 2>&1
```
---
## Поток выполнения
### Основной режим
```
main()
├─ 1. Подключение к PostgreSQL
├─ 2. Загрузка списка клиентов (client_info)
├─ 3. Сбор SID (apps_settings WHERE mode = 'auto')
├─ 4. Параллельная обработка SID (worker pool)
│ └─ processSID() для каждого SID
│ ├─ GET /l7/resource/{sid}/global → whois
│ │ └─ ошибка → алерт 🔴 + следующий SID
│ ├─ GET /l7/origin/global?l7ResourceId={sid} → origins
│ │ └─ ошибка → алерт 🔴 + следующий SID
│ ├─ GET /l7/alias/global?l7ResourceId={sid} → aliases
│ │ └─ ошибка → алерт 🔴 + следующий SID
│ ├─ DNS резолв домена → проверка AntiDDOS
│ └─ upsertAPIInfo() → сравнение + запись в API_info + алерты
├─ 5. Очистка устаревших SID из API_info (cleanupRemovedSIDs)
├─ 6. Проверка дублирующихся доменов/алиасов (если CHECK_DUPLICATES=true)
│ └─ checkDuplicateDomains() → алерт 🔴 если найдены дубли
└─ 7. Обновление WAF whitelist (updateWAFWhitelist)
├─ git pull
├─ Загрузка IP из API_info (auto-ресурсы)
├─ Загрузка IP из manual_info (ручные ресурсы)
├─ Объединение и дедупликация IP
├─ Фильтрация WAF-сетей (таблица ips)
├─ Сравнение с текущим whitelist-файлом
├─ Запись файла + git commit + git push
└─ Отправка алерта об изменениях
```
### Режим --cd
```
main() --cd
├─ 1. Подключение к PostgreSQL
└─ 2. checkDuplicateDomains() → алерт 🔴 если найдены дубли
```
---
## Модули
### main.go — точка входа и worker pool
При запуске с ключом `--cd` выполняет только проверку дублей и завершается. В основном режиме инициализирует подключение к БД, собирает все SID и запускает пул горутин. Количество воркеров задаётся через `MAX_CONCURRENT_WORKERS` (по умолчанию 5). SID передаются воркерам через буферизированный канал. Все воркеры работают параллельно — DNS-резолв, запросы к API и запись в БД выполняются одновременно для разных SID.
После завершения всех воркеров последовательно запускаются: очистка устаревших записей, проверка дублей (если не отключена через `CHECK_DUPLICATES=false`) и обновление whitelist.
### config.go — конфигурация
Загружает переменные окружения из `/etc/API_sync/API_sync.env` через `godotenv`. Если файл недоступен — использует системные переменные окружения. Все переменные инициализируются в `init()` до старта `main()`.
### api.go — HTTP-клиент и структуры API
Содержит единственный HTTP-клиент с таймаутом 20 секунд и функцию `apiGet()`, которая выполняет GET-запрос к API ServicePipe с Bearer-авторизацией и десериализует JSON-ответ в переданную структуру.
Определены структуры ответов API: `originItem`, `aliasItem`, `whoisReAPIonse`, `apiList`.
### database.go — работа с PostgreSQL
**`loadClients()`** — загружает список клиентов из `client_info`.
**`loadL7IDs()`** — загружает SID из `apps_settings` для конкретного клиента с `mode = 'auto'`.
**`processSID()`** — оркестрирует обработку одного SID: делает три запроса к API, резолвит домен для проверки AntiDDOS, вызывает `upsertAPIInfo()`. При ошибке любого API-запроса отправляет алерт через `sendAPIErrorAlert()` с указанием SID, endpoint и текста ошибки.
**`sendAPIErrorAlert()`** — формирует и отправляет алерт 🔴 при ошибке запроса к API ServicePipe. Указывает SID, endpoint (`whois`, `origins`, `aliases`) и текст ошибки.
**`upsertAPIInfo()`** — в начале подтягивает из БД `client_title` (из `apps_settings`) и `waf_provider` (из `client_info`) для использования в алертах. Затем сравнивает новые данные со старыми и при наличии изменений формирует алерты:
- изменение домена → строка `Домен изменён: old -> new` + флаг необходимости обновления WAF
- изменение origins — через `compareOrigins()`
- изменение aliases — через `compareAliases()` + флаг обновления WAF
- изменение WAF-настроек (vendor, instance, enabled) — отдельный алерт
Сохранение выполняется через `INSERT ... ON CONFLICT DO UPDATE` (upsert по `sid`).
**`cleanupRemovedSIDs()`** — сравнивает все SID в `API_info` с актуальным списком активных SID. Записи, которых нет в активном списке (ресурс удалён из `apps_settings` или сменил `mode`), удаляются из `API_info`. По каждому удалённому ресурсу отправляется алерт с указанием WAF-вендора (берётся из `apps_settings` через LEFT JOIN).
**`checkDuplicateDomains()`** — загружает все `domain_name` и `aliases` из `API_info` в единую карту. Если одно и то же значение встречается у двух и более разных SID в любой комбинации полей — считается дублем. При наличии дублей отправляется алерт 🔴 с перечислением всех совпадений и их SID. В основном режиме управляется флагом `CHECK_DUPLICATES`, при запуске с `--cd` выполняется всегда.
**`wafVendorName()`** — возвращает читаемое название вендора: `ptaf``PT AF`, `sw``SW`, `wmx``WMX`.
**`wafVendorWarning()`** — возвращает строку предупреждения для алерта об удалении ресурса с указанием конкретного WAF-вендора.
### compare.go — функции сравнения
**`compareOrigins()`** — сравнивает два списка origins по IP-адресу. Определяет добавленные, удалённые и изменённые (mode/weight) записи. Возвращает отформатированную строку с изменениями и текущим состоянием origins.
**`compareAliases()`** — сравнивает два отсортированных списка доменов. Добавленные aliases оформляются как HTML-ссылки. Возвращает строку с изменениями и булев флаг наличия изменений.
### dns.go — DNS-резолвер
Функция `resolveDomain()` возвращает первый IPv4-адрес для заданного домена. Используется в `processSID()` для проверки AntiDDOS: если resolvedIP совпадает с protectedIP из API — AntiDDOS активен.
### notify.go — система уведомлений
**`sendAlert()`** — единая точка отправки алертов. Последовательно отправляет сообщение во все настроенные каналы. Ошибка одного канала не блокирует остальные.
**Telegram**`buildTelegramClient()` при каждой отправке выбирает транспорт в порядке приоритета:
1. HTTP-прокси (`TELEGRAM_HTTP_PROXY`) — если задан и доступен
2. SOCKS5-прокси (`TELEGRAM_SOCKS5_PROXY`) — если задан и доступен
3. Прямое подключение — fallback
Доступность проверяется реальным GET-запросом на `api.telegram.org`. Сообщения отправляются в формате HTML (`parse_mode: HTML`).
**Mattermost** — POST на `/api/v4/posts` с Bearer-токеном. HTML-теги Telegram конвертируются в Markdown через `htmlToMarkdown()`. Отправляется только если заполнены все три переменные: `MATTERMOST_URL`, `MATTERMOST_BOT_TOKEN`, `MATTERMOST_CHANNEL_ID`.
**Email** — через стандартный `net/smtp`. Поддерживает несколько адресов получателей через запятую в `EMAIL_TO`. При `EMAIL_SKIP_TLS_VERIFY=true` использует STARTTLS с отключённой проверкой сертификата. Механизм аутентификации выбирается автоматически по возможностям сервера: если сервер поддерживает `LOGIN` — используется он, иначе `PLAIN`. Реализован собственный `loginAuth` для совместимости с корпоративными SMTP (Exchange, Outlook). Отправляется только если заполнены `EMAIL_SMTP_HOST`, `EMAIL_FROM`, `EMAIL_TO`.
### git.go — Git-операции
**`syncGitRepo()`** — если репозиторий не существует локально — клонирует (`git clone`), иначе обновляет (`git pull`). Автоматически настраивает `user.email` и `user.name` для коммитов.
**`gitCommitAndPush()`** — выполняет `git add <whitelist_file>`, `git commit -m <message>`, `git push`. Если нечего коммитить (`nothing to commit`) — завершается без ошибки.
### whitelist.go — управление whitelist
**`updateWAFWhitelist()`** — основная функция обновления whitelist:
1. Синхронизирует Git-репозиторий
2. Загружает WAF-сети из таблицы `ips`
3. Загружает origin IP из `API_info` (`loadAllOrigins`) и `manual_info` (`loadManualOrigins`), объединяет и дедуплицирует
4. Фильтрует IP попадающие в WAF-сети (`filterNonWAFOrigins`)
5. Сравнивает с текущим содержимым файла (`compareWhitelists`)
6. При наличии изменений — перезаписывает файл, коммитит и пушит, отправляет алерт
**`loadAllOrigins()`** — загружает все origin IP из `API_info` (auto-ресурсы).
**`loadManualOrigins()`** — загружает все записи из `manual_info` как есть, без дополнительных условий. Таблица заполняется администратором вручную и программой не изменяется.
**`filterNonWAFOrigins()`** — исключает из списка IP-адреса, которые принадлежат WAF-сетям из таблицы `ips`. Такие адреса являются узлами самого WAF и не должны попадать в whitelist.
---
## Алерты
Все алерты отправляются через единую функцию `sendAlert()`. Типы алертов:
| Событие | Эмодзи | Содержание |
|---|---|---|
| Изменение origins/aliases/домена | 🟡 | SID, домен, TENANT, WAF provider, детали изменений, предупреждение об обновлении WAF, время |
| Изменение WAF-настроек инстанса | ⚪ | SID, домен, TENANT, изменения waf_enabled/vendor/instance в формате `было -> стало`, время |
| Удаление ресурса из мониторинга | 🗑 | SID, домен, предупреждение с указанием конкретного WAF-вендора |
| Ошибка запроса к API ServicePipe | 🔴 | SID, endpoint, текст ошибки, время |
| Дублирующиеся домены/алиасы | 🔴 | Список дублей с указанием SID, время проверки |
| Обновление whitelist | 🔄 | Добавленные/удалённые IP с привязкой к SID и домену, итоговое количество |
Предупреждение об обновлении WAF формируется динамически в зависимости от вендора: `ptaf` → PT AF, `sw` → SW, `wmx` → WMX.
---
## Переменные окружения
| Переменная | По умолчанию | Описание |
|---|---|---|
| `BEARER_TOKEN` | — | Токен авторизации API ServicePipe |
| `DB_HOST` | localhost | Хост PostgreSQL |
| `DB_PORT` | 5432 | Порт PostgreSQL |
| `DB_USER` | — | Пользователь БД |
| `DB_PASSWORD` | — | Пароль БД |
| `DB_NAME` | waf_info | Имя БД |
| `TELEGRAM_BOT_TOKEN` | — | Токен Telegram-бота |
| `TELEGRAM_CHAT_ID` | — | ID чата для алертов |
| `TELEGRAM_HTTP_PROXY` | — | HTTP-прокси для Telegram |
| `TELEGRAM_SOCKS5_PROXY` | — | SOCKS5-прокси для Telegram |
| `TELEGRAM_SOCKS5_USER` | — | Логин SOCKS5-прокси |
| `TELEGRAM_SOCKS5_PASSWORD` | — | Пароль SOCKS5-прокси |
| `MATTERMOST_URL` | — | URL Mattermost-сервера |
| `MATTERMOST_BOT_TOKEN` | — | Токен Mattermost-бота |
| `MATTERMOST_CHANNEL_ID` | — | ID канала Mattermost |
| `EMAIL_SMTP_HOST` | — | SMTP-сервер |
| `EMAIL_SMTP_PORT` | 587 | SMTP-порт |
| `EMAIL_SMTP_USER` | — | Логин SMTP |
| `EMAIL_SMTP_PASSWORD` | — | Пароль SMTP |
| `EMAIL_FROM` | — | Адрес отправителя |
| `EMAIL_TO` | — | Адреса получателей (через запятую) |
| `EMAIL_SKIP_TLS_VERIFY` | false | Отключить проверку TLS-сертификата SMTP |
| `GIT_REPO_URL` | — | URL Git-репозитория whitelist |
| `GIT_REPO_PATH` | — | Локальный путь к репозиторию |
| `WHITELIST_FILE` | whitelist_ptaf.txt | Имя файла whitelist |
| `MAX_CONCURRENT_WORKERS` | 5 | Количество параллельных воркеров |
| `CHECK_DUPLICATES` | true | Включить проверку дублей в основном режиме |
---
## Зависимости
| Пакет | Назначение |
|---|---|
| `github.com/lib/pq` | PostgreSQL драйвер |
| `github.com/joho/godotenv` | Загрузка `.env` файла |
| `golang.org/x/net` | SOCKS5 прокси для Telegram |
| `net/smtp` | Отправка email (стандартная библиотека) |
| `crypto/tls` | TLS для SMTP с отключённой проверкой сертификата |