Перейти к основному содержимому
Версия: 2.0

PT NAD

Интеграция с Positive Technologies Network Attack Discovery (PT NAD)

Данное руководство описывает быструю настройку интеграции Solar TI Feeds Agent с PT NAD для автоматической загрузки индикаторов компрометации (IoC) через промежуточное хранение в локальной базе данных.


Предварительные требования

  • Установленный и настроенный Solar TI Feeds Agent
  • Учётные данные для доступа к API PT NAD (логин и пароль), если учетная запись была создана в PT NAD или JWT-токен, если аутентификация происходит через PT MC
  • Доступ к серверу Solar TI Feeds с действительным JWT-токеном

Конфигурация интеграции

Интеграция реализована несколькими пайплайнами, которые обеспечивают надёжное получение, промежуточное хранение и доставку индикаторов в PT NAD, включая повторную обработку неудачных попыток. Аутентификация в PT NAD выполняется через HTTP Basic Auth (логин/пароль).

Основные компоненты интеграции:

КомпонентНазначение
FeedsAPIGeneratorЗагрузка данных из Solar TI Feeds API
FeedsDBSinkСохранение данных в локальную SQLite-базу
FeedsDBGeneratorЧтение данных из базы для обработки
FeedsFailedDBGeneratorПовторная обработка ошибочных записей
FilterФильтрация индикаторов по заданным условиям (тип и зона)
MapperПреобразование JSON-данных в структуру, ожидаемую PT NAD
NADSinkОтправка подготовленных данных в PT NAD

Переменные окружения

Для работы интеграции необходимо определить следующие переменные окружения в docker-compose.yml:

Подключение к Solar TI Feeds API

ПеременнаяНазначение
TIC_AGENT_API_URLАдрес сервера Solar TI Feeds API
TIC_AGENT_API_TOKENJWT-токен для аутентификации в Feeds API

Подключение к PT NAD

ПеременнаяНазначение
TIC_AGENT_SERVICE_URLПолный URL сервера PT NAD
TIC_AGENT_SERVICE_USERИмя пользователя для Basic Auth в API PT NAD
TIC_AGENT_SERVICE_PASSWORDПароль для Basic Auth в API PT NAD
TIC_AGENT_SERVICE_TOKENJWT-токен для аутентификации, если она происходит через PT MC в API PT NAD

Структура пайплайнов для PT NAD

Pipeline 1: feeds-api (загрузка данных)

КомпонентПараметрыОписание
FeedsAPIGeneratorserver = templates.serverJwtApiCfgЗапрос индикаторов из Solar TI Feeds API
schedule = templates.scheduleCronMinCfg
filter.types = ["ipv4","ipv6","socketv4","socketv6","domain","url","md5"]
FeedsDBSinkСохранение всех полученных данных в SQLite

Pipeline 2–9: *-feeds-nad (обработка и выгрузка)

Для каждого репутационного списка создаётся отдельный пайплайн с именем вида <listName>-feeds-nad.

Список создаваемых репутационных списков:

listNameТип индикатораЦветУсловие фильтрации (зона)
suspicious-ipiporangeSUSPICIOUS
malicious-ipipredMALICIOUS
suspicious-domainsdnorangeSUSPICIOUS
malicious-domainsdnredMALICIOUS
suspicious-urlsuriorangeSUSPICIOUS
malicious-urlsuriredMALICIOUS
suspicious-hashesmd5orangeSUSPICIOUS
malicious-hashesmd5redMALICIOUS

Параметры компонентов для каждого пайплайна:

КомпонентПараметрыОписание
FeedsDBGeneratorschedule = templates.scheduleCronMinCfgЧтение индикаторов из БД
delay = 10.s
FeedsFailedDBGeneratorfeedGenName = "<listName>-feeds-nad-generator"Повторная обработка индикаторов, отправка которых ранее завершилась ошибкой
schedule = templates.scheduleCronMinCfg
delay = 1.min
Filterfilters – условие вида:
upper(type) in ["IPV4","IPV6","SOCKETV4","SOCKETV6"] && upper(zone) in ["SUSPICIOUS"]
(для каждого списка – свой набор типов и зона)
Оставляет только индикаторы, соответствующие типу и зоне
Mapperjsonpath – извлечение actions из поля feedsПреобразует данные: вычисляет итоговое действие (UPDATE или DELETE) и выбирает поля value, action

rules –
value = "value"
action = any(actions, { # in ["UPDATE", "CREATE"] }) ? "UPDATE" : "DELETE"

NADSinkserver – BasicAuth с учётными данными из окруженияОтправляет индикаторы в PT NAD в соответствующий репутационный список
color = listParam.listColor
type = listParam.listType
externalKey = "4RAYS-" + listParam.listName

Логика работы:

  • Фильтр пропускает индикаторы, у которых тип соответствует заданному (IP, домен, URL или хеш) и зона равна SUSPICIOUS или MALICIOUS (в зависимости от списка).
  • В маппере вычисляется поле action: если среди исходных действий есть UPDATE или CREATE, итоговое действие становится UPDATE, иначе – DELETE.
  • В зависимости от уровня угрозы объект направляется в репутационный список цветов orange (для подозрительных) или red (для вредоносных). При этом фиксируются тип объекта (ip, dn, uri, md5) и название вида 4RAYS-<listName>.

Справочник параметров для NADSink

Параметры ServerCfg (подключение к PT NAD)

ПараметрТип данныхОбязательностьОписаниеПример
credentialsBasicAuthДаАутентификация по логину/паролю.credentials = new BasicAuth { username = read("env:TIC_AGENT_SERVICE_USER") password = read("env:TIC_AGENT_SERVICE_PASSWORD") }
addressStringДаПолный URL API PT NADaddress = read("env:TIC_AGENT_SERVICE_URL")
insecureSkipVerifyBooleanНетОтключение проверки SSL-сертификатаinsecureSkipVerify = true

Параметры NADSink

ПараметрТип данныхОбязательностьОписание
serverServerCfgДаКонфигурация подключения (адрес и учётные данные)
colorStringДаЦвет репутационного списка, в который загружаются индикаторы (orange или red)
typeStringДаТип индикаторов (ip, dn, uri, md5)
externalKeyStringДаИдентификатор внешнего списка в PT NAD (например, "4RAYS-suspicious-ip")

Полный пример конфигурационного файла для интеграции с PT NAD

📄 Нажмите, чтобы показать/скрыть nad_pipeline.pkl
amends "package://pkg.pkl-lang.org/github.com/pipelane/pipelaner/pipelaner@1.3.1#/Pipelaner.pkl"
import "package://pkg.pkl-lang.org/github.com/pipelane/pipelaner/pipelaner@1.3.1#/source/Components.pkl"
import "../internal/agent.pkl"
import "../internal/templates.pkl"

local repLists = List(
new RepListParams {
listName = "suspicious-ip"
filter = """
upper(type) in ["IPV4", "IPV6", "SOCKETV4", "SOCKETV6"] && upper(zone) in ["SUSPIIOUS"]
"""
listType = "ip"
listColor = "orange"
},
new RepListParams {
listName = "malicious-ip"
filter = """
upper(type) in ["IPV4", "IPV6", "SOCKETV4", "SOCKETV6"] && upper(zone) in ["MALICIOUS"]
"""
listType = "ip"
listColor = "red"
},
new RepListParams {
listName = "suspicious-domains"
filter = """
upper(type) in ["DOMAIN"] && upper(zone) in ["SUSPIIOUS"]
"""
listType = "dn"
listColor = "orange"
},
new RepListParams {
listName = "malicious-domains"
filter = """
upper(type) in ["DOMAIN"] && upper(zone) in ["MALICIOUS"]
"""
listType = "dn"
listColor = "red"
},
new RepListParams {
listName = "suspicious-urls"
filter = """
upper(type) in ["URL"] && upper(zone) in ["SUSPIIOUS"]
"""
listType = "uri"
listColor = "orange"
},
new RepListParams {
listName = "malicious-urls"
filter = """
upper(type) in ["URL"] && upper(zone) in ["MALICIOUS"]
"""
listType = "uri"
listColor = "red"
},
new RepListParams {
listName = "suspicious-hashes"
filter = """
upper(type) in ["MD5"] && upper(zone) in ["SUSPIIOUS"]
"""
listType = "md5"
listColor = "orange"
},
new RepListParams {
listName = "malicious-hashes"
filter = """
upper(type) in ["MD5"] && upper(zone) in ["MALICIOUS"]
"""
listType = "md5"
listColor = "red"
}
)

local class RepListParams {
listName: String
filter: String
listType: agent.NadListType
listColor: agent.NadListColor
}

local feedApiPipeline = new Components.Pipeline {
name = "feeds-api"
inputs {
new agent.FeedsAPIGenerator {
name = "feeds-api-generator"
schedule = templates.scheduleCronMinCfg
server = templates.serverJwtApiCfg
filter {
types {
"ipv4"
"ipv6"
"socketv4"
"socketv6"
"domain"
"url"
"md5"
}
}
}
}
sinks {
new agent.FeedsDBSink {
name = "feeds-db-sink"
inputs {
"feeds-api-generator"
}
}
}
}

local nadPipelines = repLists.map((listParam) -> new Components.Pipeline {
name = listParam.listName + "-feeds-nad"
inputs {
new agent.FeedsDBGenerator {
name = listParam.listName + "-feeds-nad-generator"
schedule = templates.scheduleCronMinCfg
delay = 10.s
}
new agent.FeedsFailedDBGenerator {
name = listParam.listName + "-feeds-failed-nad-generator"
feedGenName = listParam.listName + "-feeds-nad-generator"
schedule = templates.scheduleCronMinCfg
delay = 1.min
}
}
transforms {
new agent.Filter {
name = listParam.listName + "-feeds-nad-filter"
inputs {
listParam.listName + "-feeds-nad-generator"
listParam.listName + "-feeds-failed-nad-generator"
}
filters = new Listing {
listParam.filter
}
}
new agent.Mapper {
name = listParam.listName + "-feeds-nad-mapper"
inputs {
listParam.listName + "-feeds-nad-filter"
}
jsonpath = new Mapping {
["actions"] = "$.feeds[*]..action"
}
rules = new Mapping {
["value"] = "value"
["action"] =
"""
any(actions, {# in ["UPDATE", "CREATE"]}) ? "UPDATE" : "DELETE"
"""
}
}
}
sinks {
new agent.NADSink {
name = listParam.listName + "-feeds-nad-sink"
inputs {
listParam.listName + "-feeds-nad-mapper"
}
server = new agent.ServerCfg {
// credentials = new agent.HeaderAuth {
// name = "Authorization"
// value = read("env:TIC_AGENT_SERVICE_TOKEN")
// }
credentials = new agent.BasicAuth{
username = read("env:TIC_AGENT_SERVICE_USER")
password = read("env:TIC_AGENT_SERVICE_PASSWORD")
}
address = read("env:TIC_AGENT_SERVICE_URL")
insecureSkipVerify = true
}
color = listParam.listColor
type = listParam.listType
externalKey = "4RAYS-" + listParam.listName
}
}
})

pipelines = nadPipelines.add(feedApiPipeline).toListing()

settings = templates.baseSettings

Мониторинг результатов работы интеграции

После запуска агента убедитесь в корректности работы:

ПроверкаДействие
Проверка состояния БДУбедитесь, что SQLite-база наполняется индикаторами, а успешно отправленные записи помечаются соответствующим образом.
Анализ логовПроверьте логи агента (в директории, заданной шаблонами) на наличие ошибок подключения к PT NAD или Feeds API.
Верификация в PT NADВ интерфейсе PT NAD убедитесь, что созданы репутационные списки с заданными externalKey (например, 4RAYS-suspicious-ip, 4RAYS-malicious-domains и т.д.) и что они содержат актуальные индикаторы с соответствующими цветами (orange или red).
Контроль очереди повторовПри временных сбоях индикаторы автоматически повторно отправляются через 1 минуту. Убедитесь, что после восстановления связи они доставляются.

Примечание: Логи агента также можно просмотреть в реальном времени с помощью команды docker logs {CONTAINER_NAME}.