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

Интеграционный слой

Внутри поставки имеется гибкий инструмент для организации интеграции между Платформой Радар и любой другой системой, с которой можно взаимодействовать через Json API.

Концепция интеграционного слоя

Основной логикой является наблюдение за изменением данных в структуре хранения информации PostgreSQL и запуск некоторых действий при наступлении того или иного изменения.

Наблюдение за изменениями

Основной для реагирования на изменение в файле /opt/pangeoradar/configs/pangeoradar-pgr-wal-listener.yaml является секция radar-tables-trigger. По сути является перечнем наблюдателей, реагирующих на изменения в базе данных.

radar-tables-trigger: &radar-tables-trigger
  - name: create_incident
    table: service_asset_findings
    fields: [ ]
    kind: insert
    sql: "SELECT ..."
    sql_vars:
      id: float64toInt64
    outputs:
      - *rvision_insert

Доступные триггеры, поле kind:

  • insert
  • update
  • delete

При срабатывании тригера на выбранной таблице указанной в секции table запускается sql скрипт собирающий основной объект для передачи запуска сборки объекта отправляемого в интегрируемую системы. Если sql скрипт не указан будет взята строка из наблюдаемой таблицы.

Также есть поле fields, заполнив которое можно указать те поля, при изменении которых должен запускать триггер.

поле sql_vars необходимо для приведения типов:

  • bool
  • int
  • int32
  • int64
  • string
  • float32
  • float64
  • float64toInt64

поле outputs указывает необходимые каналы для дальнейшей интеграции с внешними системами.

Отправка изменений

Для организации канала отправки объекта соответствия, сформированного после срабатывания триггера, необходимо создать секцию, в которую направить вывод в поле outputs

Секция имеет следующую структуру:

rvision_update: &rvision_update
  type: http
  url: "https://IP/api/v2/incidents"
  method: POST
  content_type: "application/json"
  headers:
    PgrApiKey: "test"
  prepend:
    - type: http
      url: "https://IP/api/v2/incidents"
      method: GET
      content_type: "application/json"
      query:
        token: "SECRET"
        fields: "identifier"
        filter: '[{"operator":"=","value":%d,"property":"id_siem"}]'
      query_vars_from_trigger:
        filter:
          field: "id"
          type: "float64toInt64"
      append_to_mapping:
        identifier: "data.result.0.identifier"
  mappging: *rvision_mapping
  • type - транспорт, на данны момент поддерживается только http;
  • url - адрес ендпоинта для отправки запроса;
  • method - тип запроса (GET, POST, PUT);
  • content_type - значение заголовка content-type;
  • headers - дополнительные заголовки в формате ключ: значение;
  • mapping - объект соответствия;
  • prepend - дополнительная секция, которая будет выполнена перед отправкой основного запроса. Полезна, если нужно сделать запрос на получение доп. информации и обогащения объекта соответствия перед отправкой.

В секции prepend доступны те же основные поля, что и в основной. В дополнении к ней доступны опции:

  • query - строка запроса;
  • query_vars_from_trigger - шаблонизация для строки запроса;
  • append_to_mapping - обогащение объекта соответветствия по ключу и значению из ответа.

Объект соответствия

Объект соответствия - это объект, который будет собран после срабатывания триггера и передан в канал на отправку.

Структура объекта:

rvision_mapping: &rvision_mapping
  token:
    type: "manual"
    value: "SECRET_KEY"
  status_siem:
    type: "map"
    value: "status"
  STATUS:
    type: "active_map"
    value: "status"
    map:
      new: "Создан"
      risk_accepted: "Зарегистрирован"
      assigned_customer: "Назначен"
      working_customer: "Обработка"
      feedback_required: "Раследование"
      closed: "Закрыт"

type - тип соответствия, доступны:

  • manual - заданное ручное значение в ключе value;
  • map - ключу объекта будет соответствовать поле указанного в значение ключа value из триггера изменений;
  • active_map - ключу объекта будет соответствовать значение из объекта map, найденного при совпадении ключа, и значение поля из триггера изменений, указанного в ключе value.

Пример интеграции с SOAR RVision

Ниже представлен конфигурационный файл /opt/pangeoradar/configs/pangeoradar-pgr-wal-listener.yaml, настроенный на обмен информацией об инцидентах с SOAR RVision.

---

global:
  force_replica_identity: true
  log_level: warning

# Mappings
rvision_mapping: &rvision_mapping
  token:
    type: "manual"
    value: "SECRET_KEY"
  category:
    type: "manual"
    value: "Инцидент из Пагео Радар"
  info_source:
    type: "manual"
    value: "SIEM Пангео"
  type:
    type: "manual"
    value: "Инцидент полученный из Пангео Радар"
  id_siem:
    type: "map"
    value: "id"
  DESCRIPTION:
    type: "map"
    value: "DESCRIPTION"
  risk_impact:
    type: "map"
    value: "risk_impact"
  solution:
    type: "map"
    value: "solution"
  mitigation:
    type: "map"
    value: "mitigation"
  status_siem:
    type: "map"
    value: "status"
  STATUS:
    type: "active_map"
    value: "status"
    map:
      new: "Создан"
      risk_accepted: "Зарегистрирован"
      assigned_customer: "Назначен"
      working_customer: "Обработка"
      feedback_required: "Раследование"
      closed: "Закрыт"
  risklevel:
    type: "map"
    value: "risklevel"
  service_asset_id:
    type: "map"
    value: "service_asset_id"
  DETECTION_DATE:
    type: "map"
    value: "created_at"
  UPDATE:
    type: "map"
    value: "updated_at"
  finding_id:
    type: "map"
    value: "finding_id"
  analysis_output:
    type: "map"
    value: "analysis_output"
  synopsis:
    type: "map"
    value: "synopsis"
  title:
    type: "map"
    value: "title"
  risk:
    type: "map"
    value: "risk"
  OCCUR_DATE:
    type: "map"
    value: "acknowledged_at"
  alert_type:
    type: "map"
    value: "alert_type"
  client_note:
    type: "map"
    value: "client_note"
  internal_note:
    type: "map"
    value: "internal_note"
  external:
    type: "map"
    value: "external"
  immediate_action_score:
    type: "map"
    value: "immediate_action_score"
  throughput_period:
    type: "map"
    value: "throughput_period"
  throughput_period_change:
    type: "map"
    value: "throughput_period_change"
  customer_created:
    type: "map"
    value: "customer_created"
  c_visible_since:
    type: "map"
    value: "c_visible_since"
  c_visible_since_in_days:
    type: "map"
    value: "c_visible_since_in_days"
  c_reopened_count:
    type: "map"
    value: "c_reopened_count"
  c_last_customer_status_change:
    type: "map"
    value: "c_last_customer_status_change"
  c_customer_retention_time:
    type: "map"
    value: "c_customer_retention_time"
  logmule_identifier:
    type: "map"
    value: "logmule_identifier"
  c_remote_exploitable:
    type: "map"
    value: "c_remote_exploitable"
  c_occurrence_count:
    type: "map"
    value: "c_occurrence_count"
  last_occurrence_id:
    type: "map"
    value: "last_occurrence_id"
  itsm_last_synced_at:
    type: "map"
    value: "itsm_last_synced_at"
  itsm_sync_status:
    type: "map"
    value: "itsm_sync_status"
  external_id:
    type: "map"
    value: "external_id"
  itsm_sync_error:
    type: "map"
    value: "itsm_sync_error"
  user_id:
    type: "map"
    value: "user_id"
  updated_by:
    type: "map"
    value: "updated_by"
  group_id:
    type: "map"
    value: "group_id"
  acknowledged_by:
    type: "map"
    value: "acknowledged_by"
  created_by_customer:
    type: "map"
    value: "created_by_customer"
  edited_by:
    type: "map"
    value: "edited_by"
  active_name:
    type: "map"
    value: "active_name"
  IP:
    type: "map_from_json"
    value: "ip"
    from: "inet"
  fqdn:
    type: "map_form_json"
    value: "fqdn"
    from: "varchar_array"


# Outputs
rvision_insert: &rvision_insert
  type: http
  url: "https://IP/api/v2/incidents"
  method: POST
  content_type: "application/json"
  headers:
    PgrApiKey: "test"
  mappging: *rvision_mapping

rvision_update: &rvision_update
  type: http
  url: "https://IP/api/v2/incidents"
  method: POST
  content_type: "application/json"
  headers:
    PgrApiKey: "test"
  prepend:
    - type: http
      url: "https://IP/api/v2/incidents"
      method: GET
      content_type: "application/json"
      query:
        token: "SECRET"
        fields: "identifier"
        filter: '[{"operator":"=","value":%d,"property":"id_siem"}]'
      query_vars_from_trigger:
        filter:
          field: "id"
          type: "float64toInt64"
      append_to_mapping:
        identifier: "data.result.0.identifier"
  mappging: *rvision_mapping


radar-tables-trigger: &radar-tables-trigger
  - name: create_incident
    table: service_asset_findings
    fields: [ ]
    kind: insert
    sql: "SELECT t.id, CASE WHEN t.description ISNULL THEN f.description ELSE t.description END, CASE WHEN t.risk_impact ISNULL THEN f.risk_impact ELSE t.risk_impact END, CASE WHEN t.solution ISNULL THEN f.solution ELSE t.solution END, t.mitigation, CASE WHEN t.solution ISNULL THEN f.solution ELSE t.solution END, t.status, t.risklevel, t.service_asset_id, t.created_at, t.updated_at, t.finding_id, t.analysis_output, t.synopsis, CASE WHEN t.synopsis ISNULL THEN f.synopsis ELSE t.synopsis END, t.title, CASE WHEN t.title ISNULL THEN f.title ELSE t.title END, t.risk, t.acknowledged_at, t.alert_type, t.client_note, t.internal_note, t.external, t.immediate_action_score, t.throughput_period, t.throughput_period_change, t.customer_created, t.c_visible_since, t.c_visible_since_in_days, t.c_reopened_count, t.c_last_customer_status_change, t.c_customer_retention_time, t.logmule_identifier, t.c_remote_exploitable, t.c_occurrence_count, t.last_occurrence_id, t.itsm_last_synced_at, t.itsm_sync_status, t.external_id, t.itsm_sync_error, t.user_id, t.updated_by, t.group_id, t.acknowledged_by, t.created_by_customer, t.edited_by, s.name as active_name, ni.ip as ip, ni.fqdn as fqdn FROM public.service_asset_findings t left join findings f on t.finding_id = f.id left join service_assets s on t.service_asset_id = s.id left join network_interfaces ni on s.id = ni.service_asset_id where t.id = $1"
    sql_vars:
      id: float64toInt64
    outputs:
      - *rvision_insert

  - name: update_incident
    table: service_asset_findings
    fields: [ ]
    kind: update
    sql: "SELECT t.id, CASE WHEN t.description ISNULL THEN f.description ELSE t.description END, CASE WHEN t.risk_impact ISNULL THEN f.risk_impact ELSE t.risk_impact END, CASE WHEN t.solution ISNULL THEN f.solution ELSE t.solution END, t.mitigation, CASE WHEN t.solution ISNULL THEN f.solution ELSE t.solution END, t.status, t.risklevel, t.service_asset_id, t.created_at, t.updated_at, t.finding_id, t.analysis_output, t.synopsis, CASE WHEN t.synopsis ISNULL THEN f.synopsis ELSE t.synopsis END, t.title, CASE WHEN t.title ISNULL THEN f.title ELSE t.title END, t.risk, t.acknowledged_at, t.alert_type, t.client_note, t.internal_note, t.external, t.immediate_action_score, t.throughput_period, t.throughput_period_change, t.customer_created, t.c_visible_since, t.c_visible_since_in_days, t.c_reopened_count, t.c_last_customer_status_change, t.c_customer_retention_time, t.logmule_identifier, t.c_remote_exploitable, t.c_occurrence_count, t.last_occurrence_id, t.itsm_last_synced_at, t.itsm_sync_status, t.external_id, t.itsm_sync_error, t.user_id, t.updated_by, t.group_id, t.acknowledged_by, t.created_by_customer, t.edited_by, s.name as active_name, ni.ip as ip, ni.fqdn as fqdn FROM public.service_asset_findings t left join findings f on t.finding_id = f.id left join service_assets s on t.service_asset_id = s.id left join network_interfaces ni on s.id = ni.service_asset_id where t.id = $1"
    sql_vars:
      id: float64toInt64
    outputs:
      - *rvision_update


connections:
  - database: radar
    username: user
    password: secret
    host: 127.0.0.1
    port: 5432
    triggers: *radar-tables-trigger