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

Архитектура

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

Общая схема

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"]
        Jira["Jira"]
        Chat["Мессенджеры и SMTP"]
        LLM["LLM-сервис"]
    end

    Browser --> Frontend
    Browser --> Backend
    Frontend -.-> |"отдаёт только статику"| Browser
    Backend <--> DB
    Worker <--> DB
    Backend --> Files
    Worker --> Files
    Backend <--> IdP
    Worker --> Jira
    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,Jira,Chat,LLM,Browser ext
    class DB,Files store

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

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

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

Компонент Что делает Технология Порт
backend REST API, разграничение доступа, приём отчётов, периодические планировщики Go, Gin 8080 в коде, 8082 в чарте
worker Фоновые задачи: разбор отчётов, задачи в Jira, уведомления, 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-сервис Разбор находок языковой моделью. Внешний или развёрнутый у вас

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

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

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

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

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

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

Очередь Назначение Воркеров по умолчанию
по умолчанию Диспетчер событий, разбор отчётов 10
jira_sync Создание и обновление задач в Jira 4
jira_reverse_sync Перенос статуса из Jira в Hub 1
telegram_notifications Уведомления в Telegram 5
mattermost_notifications Уведомления в Mattermost 10
maxru_notifications Уведомления в MAX 5
email_notifications Уведомления по электронной почте 5
finding_copy Копирование находок между продуктами 4
dual_verify Перепроверка находки вторым источником 2
llm_analysis Разбор языковой моделью 5
finding_enrichment Обогащение находок плагинами 3 — на единицу меньше предела одновременных запусков одного плагина
plugin_finding_source_sync Опрос внешних источников находок плагинами 4

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

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

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

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

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

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

Вход возможен в двух режимах, задаётся 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 Создание и обновление задач, опрос статусов worker
Telegram, MAX, Mattermost, SMTP Уведомления worker
LLM-сервис Разбор находок worker
NetBox Синхронизация периметра backend
Реестр плагинов Установка и обновление плагинов backend
Сервер лицензий Периодическая проверка, если адрес задан backend

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

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

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

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

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

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

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

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