Перейти к основному содержимому
Версия: 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:

ПеременнаяНазначение
TIC_AGENT_NAD_ADDRESSПолный URL сервера PT NAD
TIC_AGENT_NAD_USERИмя пользователя для Basic Auth в API PT NAD
TIC_AGENT_NAD_PASSWORDПароль для Basic Auth в API PT NAD
TIC_AGENT_NAD_TOKENJWT-токен для аутентификации, если она происходит через PT MC
TIC_AGENT_NAD_PROXYСтрока подключения к прокси-серверу для PT NAD (например, socks5://user:password@proxy.domain:port)

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

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

КомпонентПараметрыОписание
FeedsAPIGeneratorname = "feeds-api-generator"Запрос индикаторов из Solar TI Feeds API с заданным интервалом и размером пакета. Типы network включают IP-адреса, домены и URL.
server = templates.TICApiCfg (адрес и токен из TIC_AGENT_API_URL / TIC_AGENT_API_TOKEN)
schedule = (read?("env:TIC_AGENT_API_POOL_INTERVAL") ?? 3600).toInt().s
filter.types = new Listing md5
pagination.limit = (read?("env:TIC_AGENT_API_BATCH_SIZE") ?? 1000).toInt()
FeedsDBSinkname = "feeds-db-sink"Сохранение всех полученных данных в SQLite-базу для последующей обработки.
inputs = ["feeds-api-generator"]

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

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

КомпонентПараметрыОписание
FeedsDBGeneratorname = "<listName>-nad-feeds-db-generator"Чтение индикаторов из БД с настраиваемыми интервалом, задержкой и размером пакета.
schedule = (read?("env:TIC_AGENT_DB_POOL_INTERVAL") ?? 600).toInt().s
delay = (read?("env:TIC_AGENT_DB_START_DELAY") ?? 60).toInt().s
batchSize = (read?("env:TIC_AGENT_DB_BATCH_SIZE") ?? 100).toInt()
batchDelay = (read?("env:TIC_AGENT_DB_BATCH_DELAY") ?? 100).toInt().ms
FeedsFailedDBGeneratorname = "<listName>-nad-feeds-db-failed-generator"Повторная обработка индикаторов, отправка которых ранее завершилась ошибкой. Привязан к основному генератору через feedGenName.
feedGenName = "<listName>-nad-feeds-db-generator"
Filtername = "<listName>-filter"Оставляет только индикаторы, соответствующие типу и зоне (например, для suspicious-ip – IP-адреса с зоной SUSPICIOUS).
inputs = ["<listName>-nad-feeds-db-generator", "<listName>-nad-feeds-db-failed-generator"]
filters – условие вида:
upper(type) in ["IPV4","IPV6","SOCKETV4","SOCKETV6"] && upper(zone) in ["SUSPICIOUS"]
(для каждого списка – свой набор типов и зона)
Mappername = "<listName>-mapper"Преобразует данные: вычисляет итоговое действие (UPDATE или DELETE) на основе исходных действий в поле feeds и оставляет поля value и action.
inputs = ["<listName>-filter"]
jsonpath = { "actions" = "$.feeds[*]..action" }
rules = { "value" = "value", "action" = "any(actions, {# in ["UPDATE", "CREATE"]}) ? "UPDATE" : "DELETE"" }
NADSinkname = "<listName>-nad-sink"Отправляет подготовленные индикаторы в PT NAD в соответствующий репутационный список. Аутентификация – Basic Auth.
inputs = ["<listName>-mapper"]
server = new agent.ServerCfg {
  credentials = new agent.BasicAuth {
    username = read("env:TIC_AGENT_NAD_USER")
    password = read("env:TIC_AGENT_NAD_PASSWORD")
  },
  address = read("env:TIC_AGENT_NAD_ADDRESS"),
  insecureSkipVerify = true
}
color = "<listColor>" (orange/red)
type = "<listType>" (ip/dn/uri/md5)
externalKey = "4RAYS-<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_NAD_USER") password = read("env:TIC_AGENT_NAD_PASSWORD") }
addressStringДаПолный URL API PT NADaddress = read("env:TIC_AGENT_NAD_ADDRESS")
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 "../pkl/internal/agent.pkl"
import "../pkl/internal/templates.pkl"

local repLists =
List(
new RepListParams {
listName = "suspicious-ip"
filter =
"""
upper(type) in ["IPV4", "IPV6", "SOCKETV4", "SOCKETV6"] && upper(zone) in ["SUSPICIOUS"]
"""
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 ["SUSPICIOUS"]
"""
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 ["SUSPICIOUS"]
"""
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 ["SUSPICIOUS"]
"""
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"
server = templates.TICApiCfg
schedule = (read?("env:TIC_AGENT_API_POOL_INTERVAL") ?? 3600).toInt().s
filter = new agent.FeedsAPIRequestParams {
types = new Listing {
"network"
"md5"
}
pagination = new agent.Pagination {
limit = (read?("env:TIC_AGENT_API_BATCH_SIZE") ?? 1000).toInt()
}
}
}
}
sinks {
new agent.FeedsDBSink {
name = "feeds-db-sink"
inputs {
"feeds-api-generator"
}
}
}
}

local nadPipelines =
repLists.map((listParam) -> new Components.Pipeline {
name = listParam.listName + "-nad"
inputs {
new agent.FeedsDBGenerator {
name = listParam.listName + "-nad-feeds-db-generator"
schedule = (read?("env:TIC_AGENT_DB_POOL_INTERVAL") ?? 600).toInt().s
delay = (read?("env:TIC_AGENT_DB_START_DELAY") ?? 60).toInt().s
batchSize = (read?("env:TIC_AGENT_DB_BATCH_SIZE") ?? 100).toInt()
batchDelay = (read?("env:TIC_AGENT_DB_BATCH_DELAY") ?? 100).toInt().ms
}
new agent.FeedsDBFailedGenerator {
name = listParam.listName + "-nad-feeds-db-failed-generator"
feedGenName = listParam.listName + "-nad-feeds-db-generator"
}
}
transforms {
new agent.Filter {
name = listParam.listName + "-filter"
inputs {
listParam.listName + "-nad-feeds-db-generator"
listParam.listName + "-nad-feeds-db-failed-generator"
}
filters = new Listing {
listParam.filter
}
}
new agent.Mapper {
name = listParam.listName + "-mapper"
inputs {
listParam.listName + "-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 + "-nad-sink"
inputs {
listParam.listName + "-mapper"
}
server = new agent.ServerCfg {
credentials = new agent.BasicAuth {
username = read("env:TIC_AGENT_NAD_USER")
password = read("env:TIC_AGENT_NAD_PASSWORD")
}
address = read("env:TIC_AGENT_NAD_ADDRESS")
proxy = read?("env:TIC_AGENT_NAD_PROXY")
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}.