api_sync/ARCHITECTURE.md

244 lines
No EOL
18 KiB
Markdown
Executable file
Raw Permalink 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. Поддерживает два режима запуска: основной и проверка дублей/ошибок API по ключу `--cd`.
---
## Режимы запуска
```bash
./API_sync # основной запуск
./API_sync --cd # проверка дублирующихся доменов/алиасов + ошибок API
```
Пример cron-конфигурации:
```
# Основной запуск каждые 15 минут
*/15 * * * * install cd /home/install && /home/install/API_sync >> /var/log/API_sync.log 2>&1
# Проверка дублей и ошибок API каждый час
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
│ │ └─ ошибка → записывается в apiErrors
│ ├─ GET /l7/origin/global?l7ResourceId={sid} → origins
│ │ └─ ошибка → записывается в apiErrors
│ ├─ GET /l7/alias/global?l7ResourceId={sid} → aliases
│ │ └─ ошибка → записывается в apiErrors
│ ├─ DNS резолв домена → проверка AntiDDOS
│ └─ upsertAPIInfo() → сравнение + запись в API_info + алерты
├─ 5. Отправка сводного алерта об ошибках API (если CHECK_DUPLICATES=true)
├─ 6. Очистка устаревших SID из API_info (cleanupRemovedSIDs)
├─ 7. Проверка дублирующихся доменов/алиасов (если CHECK_DUPLICATES=true)
│ └─ checkDuplicateDomains() → алерт 🔴 если найдены дубли
└─ 8. Обновление 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. Загрузка всех SID (apps_settings WHERE mode = 'auto')
├─ 3. Параллельная проверка доступности API (worker pool)
│ └─ checkSIDAvailability() для каждого SID
│ ├─ GET /l7/resource/{sid}/global
│ ├─ GET /l7/origin/global?l7ResourceId={sid}
│ └─ GET /l7/alias/global?l7ResourceId={sid}
├─ 4. Отправка сводного алерта об ошибках API (если есть)
└─ 5. checkDuplicateDomains() → алерт 🔴 если найдены дубли
```
---
## Модули
### main.go — точка входа и worker pool
При запуске с ключом `--cd` параллельно проверяет доступность API для всех SID через `checkSIDAvailability()`, отправляет сводный алерт об ошибках и выполняет проверку дублей. В основном режиме запускает полный цикл синхронизации с пулом воркеров. Ошибки API собираются в общий map через mutex и отправляются одним алертом после завершения всех воркеров (если `CHECK_DUPLICATES=true`).
### 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 возвращает её наверх — воркер записывает в общий map ошибок.
**`checkSIDAvailability()`** — делает только три API-запроса без записи в БД и без алертов. Используется в режиме `--cd` для быстрой проверки доступности API по каждому SID.
**`sendAPIErrorsAlert()`** — формирует и отправляет один сводный алерт 🔴 со всеми SID у которых была ошибка API. SID отсортированы для стабильного вывода.
**`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 с ошибками и текстом ошибки, время |
| Дублирующиеся домены/алиасы | 🔴 | Список дублей с указанием 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 | Включить проверку дублей и сводный алерт ошибок API в основном режиме |
---
## Зависимости
| Пакет | Назначение |
|---|---|
| `github.com/lib/pq` | PostgreSQL драйвер |
| `github.com/joho/godotenv` | Загрузка `.env` файла |
| `golang.org/x/net` | SOCKS5 прокси для Telegram |
| `net/smtp` | Отправка email (стандартная библиотека) |
| `crypto/tls` | TLS для SMTP с отключённой проверкой сертификата |