Перейти к содержанию

Архитектура

Страница описывает, из чего состоит решение целиком, как компоненты связаны между собой и какие из них обязательны. Расчёт мощности вынесен в Расчёт ресурсов, поведение при отказах — в Отказоустойчивость.

Общая схема

flowchart TD
    Browser["Браузер аналитика"]

    subgraph HubBox["Security Hub"]
        Frontend["Frontend<br/>nginx со статикой"]
        Backend["Backend<br/>REST API, RBAC, планировщики"]
        Worker["Worker<br/>фоновые задачи"]
    end

    DB[("PostgreSQL<br/>данные и очередь задач")]
    Files[("Хранилище отчётов<br/>файлы")]

    subgraph ExtBox["Внешние системы"]
        IdP["Провайдер SSO"]
        Tracker["Трекер задач"]
        Chat["Мессенджеры и SMTP"]
        LLM["LLM-сервис"]
    end

    Browser --> Frontend
    Browser --> Backend
    Frontend -.-> |"отдаёт только статику"| Browser
    Backend <--> DB
    Worker <--> DB
    Backend --> Files
    Worker --> Files
    Backend <--> IdP
    Worker --> |"через плагины"| Tracker
    Worker --> Chat
    Worker --> LLM

    classDef hub fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#111827
    classDef ext fill:#f3f4f6,stroke:#9ca3af,stroke-width:1px,stroke-dasharray:4 3,color:#111827
    classDef store fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#111827
    class Frontend,Backend,Worker hub
    class IdP,Tracker,Chat,LLM,Browser ext
    class DB,Files store

Ключевое свойство: очередь фоновых задач живёт в той же PostgreSQL. Отдельного брокера сообщений решение не требует — ни Redis, ни RabbitMQ, ни Kafka. Это сокращает список того, что нужно разворачивать и поддерживать, но означает, что база данных — единственная точка, потеря которой останавливает всё.

Состав компонентов

Обязательные

Компонент Что делает Технология Порт
backend REST API, разграничение доступа, приём отчётов, периодические планировщики Go, Gin 8080 в коде, 8082 в чарте
worker Фоновые задачи: разбор отчётов, задачи в трекере, уведомления, AI-триаж, очистка Go —
frontend Веб-интерфейс: отдача собранной статики React, TypeScript, nginx 3000
postgres Данные и очередь задач PostgreSQL 15 5432

Backend и worker собираются из одной кодовой базы и читают одни и те же переменные окружения. Различия такие:

  • backend дополнительно обслуживает HTTP и запускает периодические планировщики;
  • worker занимается только фоновыми задачами.

При этом обработчик очереди работает в обоих процессах: backend не просто ставит задачи, он и сам их выполняет. Практическое следствие — установка без отдельного worker формально работоспособна: отчёты разбираются, уведомления уходят. Отдельный worker нужен, чтобы фоновая нагрузка не конкурировала с обслуживанием запросов и масштабировалась независимо; для любой установки сверх ознакомительной он рекомендуется.

Обратная сторона: параметры параллелизма очередей действуют в каждом процессе. Если не хотите, чтобы backend брал на себя тяжёлые задачи, задайте ему нулевое или малое значение соответствующей переменной — так, например, поступают с разбором находок языковой моделью (LLM_WORKERS=0 в backend).

Опциональные

Компонент Когда нужен
Провайдер SSO Если вход не по локальным учётным записям. Может быть внешним — своего разворачивать не обязательно
DomainScope + собственная PostgreSQL Обследование внешнего периметра. Базу с Hub не разделяет
OpenVAS Проверка известных уязвимостей по узлам, найденным DomainScope
OWASP ZAP Активная проверка веб-приложений
Сканер конфигураций инфраструктуры Проверка репозиториев с описанием инфраструктуры
Grafana Просмотр метрик
LLM-сервис Разбор находок языковой моделью. Внешний или развёрнутый у вас

Как устроены проекты и продукты

Прежде чем разворачивать, стоит понять модель — от неё зависят и права доступа, и дедупликация, и настройки интеграций.

flowchart TD
    Proj["Проект<br/>единица политики"]
    P1["Продукт<br/>объект защиты"]
    P2["Продукт"]
    F1["Находки"]
    F2["Находки"]

    Proj --> P1
    Proj --> P2
    P1 --> F1
    P2 --> F2

    classDef hub fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#111827
    classDef leaf fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#111827
    class Proj,P1,P2 hub
    class F1,F2 leaf

Проект — уровень, на котором задаются правила. У проекта свои:

  • сроки устранения по критичности;
  • провайдер учёта задач и его настройки;
  • каналы уведомлений — они же плагины: Telegram, Slack, Teams, Mattermost, MAX, почта;
  • периметр сканирования;
  • владелец и права доступа;
  • источник описаний инфраструктуры для проверки конфигураций.

Продукт — то, что защищаем: репозиторий, сервис, группа узлов. У продукта свои адрес репозитория, ветка по умолчанию, метки и — главное — находки и загруженные отчёты.

Что из этого следует на практике:

Вопрос Ответ
Где живут находки В продукте
В каких границах работает дедупликация В пределах одного продукта
Где задаются сроки и интеграции В проекте — сразу для всех его продуктов
Куда загружаются отчёты В конкретный продукт
На каком уровне выдаются права На проект — тогда действует на все его продукты — либо на отдельный продукт

Главное следствие для планирования: один объект защиты должен быть одним продуктом. Если тот же узел или репозиторий заведён в двух продуктах, вы получите две независимые находки с двумя жизненными циклами. Подробнее: Жизненный цикл находки.

Разумная отправная точка — проект на команду или направление, продукт на репозиторий или сервис.

Разделение по слоям

Приём и хранение

Отчёт приходит на backend, сохраняется в файловое хранилище (STORAGE_PATH), и в базе создаётся запись отчёта в состоянии «ожидает». Разбор в тот же HTTP-запрос не выполняется: backend ставит задачу в очередь и отвечает 202 Accepted.

Файлы отчётов и база — два независимых состояния, и оба нужно резервировать. Файлы удаляются по срокам хранения (CLEANUP_RETENTION_*), а файлы без записи в базе — как «осиротевшие», по истечении заданного числа часов.

Очередь задач

Очередь реализована поверх PostgreSQL. Задачи разложены по именованным очередям со своей степенью параллелизма:

Очередь Назначение Воркеров по умолчанию
по умолчанию Диспетчер событий и прочие задания 10
sarif_processing Разбор загруженных отчётов 4
report_cleanup Очистка отчётов по срокам хранения 1
email_notifications Уведомления по электронной почте встроенным путём 5
finding_copy Копирование находок между продуктами 4
dual_verify Перепроверка находки вторым источником 2
llm_analysis Разбор языковой моделью 5
finding_enrichment Обогащение находок плагинами 3 — на единицу меньше предела одновременных запусков одного плагина
plugin_finding_source_sync Опрос внешних источников находок плагинами 4

Три последние очереди создаются только тогда, когда соответствующая возможность включена. Доставка уведомлений и заведение задач с 0.33 идут не своими очередями, а общим механизмом событий плагинов. Все значения меняются переменными окружения — см. Hub — backend и worker.

Периодические задачи

Планировщики запускаются только в процессе backend и ставят задачи в очередь. Исполнить задачу может любой процесс, читающий очередь, — и worker, и сам backend.

Отсюда следствие для эксплуатации: остановка backend прекращает не только обслуживание запросов, но и постановку всех периодических задач. Остановка worker обработку не прекращает — она продолжится силами backend, конкурируя с обслуживанием запросов.

Планировщик Что делает
Проверка сроков Ежечасно ищет находки с истекающим и нарушенным сроком
Возврат принятых рисков Возвращает на пересмотр находки, у которых истёк срок принятия риска или ожидания
Очистка отчётов Удаляет отчёты и файлы по срокам хранения
Очистка токенов обновления Удаляет просроченные токены
Синхронизация периметра Обмен данными периметра с внешним инвентарём
Опрос источников-плагинов Периодический сбор данных плагинами-источниками
Опрос статусов задач Спрашивает у провайдера состояние заведённых задач — второй канал закрытия там, где трекер не дотягивается до Hub
Перепроверка вторым источником Постановка кандидатов на повторную проверку
Проверка лицензии Периодическое обращение к серверу лицензий, если он задан

Доступ и разграничение прав

Вход возможен в двух режимах, задаётся AUTH_MODE:

  • SSO (по умолчанию) — вход через внешний провайдер по протоколу OIDC. Одновременно можно подключить несколько провайдеров: на странице входа появится по кнопке на каждый.
  • LOCAL — локальные учётные записи с паролем. Первый администратор создаётся при старте из LOCAL_ADMIN_PASSWORD.

После успешного входа Hub выдаёт собственный токен доступа — токен провайдера дальше не используется:

Токен Где хранится Срок по умолчанию
Доступа В памяти вкладки браузера 15 минут
Обновления Cookie HttpOnly, ограниченная путём /api/v1/auth 7 суток

Токен обновления одноразовый: при каждом обновлении выдаётся новый, старый отзывается. Предусмотрено короткое окно совместимости, чтобы несколько вкладок не выбивали друг друга. Выход из системы отзывает все токены обновления пользователя.

Права проверяются на двух уровнях: роль пользователя (admin, user, viewer) и принадлежность ресурса — проекта или продукта. Отдельно существуют сервисные учётные записи для систем сборки и внешних сканеров: они работают по ключу в заголовке X-API-Key, а набор их разрешений выбирается из фиксированного списка (загрузка отчётов, чтение находок, управление периметром и другие).

Плагины

Плагины исполняются в отдельном процессе, а не внутри backend. Общение с внешним миром идёт через фиксированный набор обращений к среде исполнения: выполнить HTTP-запрос, получить секрет, получить секрет проекта, записать сообщение в журнал. Адреса, к которым разрешено обращаться, ограничены списком; по умолчанию запрещены обращения к локальным и внутренним адресам.

Плагины распространяются как артефакты реестра образов с подписью; устанавливаются и включаются через административный раздел интерфейса. Подробнее: Плагины.

Потоки данных наружу

Все исходящие обращения выполняет worker или backend — у браузера прямого доступа к внешним системам нет.

Куда Зачем Инициатор
Провайдер SSO Получение метаданных, обмен кода на токен, ключи проверки подписи backend
Трекер задач (Jira, GitHub, Trello) Заведение и ведение задач, опрос статусов — плагином-провайдером worker
Telegram, Slack, Teams, Mattermost, MAX, SMTP Уведомления — канальными плагинами worker
LLM-сервис Разбор находок worker
NetBox Синхронизация периметра backend
Реестр плагинов Установка и обновление плагинов backend
Сервер лицензий Периодическая проверка, если адрес задан backend

Обращения к реестру плагинов, из плагинов и из подсистем (перепроверка, интеграция с системами контроля версий, получение исходников) защищены от подделки запросов на стороне сервера: по умолчанию запрещены схема http и обращения к локальным и внутренним адресам, у каждой подсистемы свой список разрешённых узлов. Плагин, кроме того, может обращаться только к адресам, объявленным в его описании или заданным администратором при установке. В промышленном режиме (APP_ENV=production) адреса, по которым передаются секреты, обязаны использовать https — иначе запуск прерывается.

Хранение времени

Все отметки времени хранятся и передаются в UTC; преобразование в местное время выполняется только при отображении. Колонки с временем используют тип с часовым поясом, сессия базы данных работает в UTC.

Версионирование

Компоненты Hub собираются из одной кодовой базы с общей версией вида X.Y.СБОРКА+КОММИТ, например 0.33.20260909120000+a1b2c3d:

  • X.Y — мажорная и минорная части, общие для всех компонентов;
  • СБОРКА — отметка времени сборки в UTC;
  • КОММИТ — короткий идентификатор ревизии.

Версия видна в нижней части интерфейса, в стартовой записи журнала и по адресу /version. Интерфейс сравнивает свою версию X.Y с версией backend и предупреждает при расхождении. DomainScope версионируется независимо.

Подробнее об обновлении: Обновления.