244 lines
No EOL
18 KiB
Markdown
Executable file
244 lines
No EOL
18 KiB
Markdown
Executable file
# Архитектура 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 с отключённой проверкой сертификата | |