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_TOKEN | JWT-токен для аутентификации в 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_TOKEN | JWT-токен для аутентификации, если она происходит через PT MC в API PT NAD |
Структура пайплайнов для PT NAD
Pipeline 1: feeds-api (загрузка данных)
| Компонент | Параметры | Описание |
|---|---|---|
FeedsAPIGenerator | server = 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-ip | ip | orange | SUSPICIOUS |
malicious-ip | ip | red | MALICIOUS |
suspicious-domains | dn | orange | SUSPICIOUS |
malicious-domains | dn | red | MALICIOUS |
suspicious-urls | uri | orange | SUSPICIOUS |
malicious-urls | uri | red | MALICIOUS |
suspicious-hashes | md5 | orange | SUSPICIOUS |
malicious-hashes | md5 | red | MALICIOUS |
Параметры компонентов для каждого пайплайна:
| Компонент | Параметры | Описание |
|---|---|---|
FeedsDBGenerator | schedule = templates.scheduleCronMinCfg | Чтение индикаторов из БД |
delay = 10.s | ||
FeedsFailedDBGenerator | feedGenName = "<listName>-feeds-nad-generator" | Повторная обработка индикаторов, отправка которых ранее завершилась ошибкой |
schedule = templates.scheduleCronMinCfg | ||
delay = 1.min | ||
Filter | filters – условие вида:upper(type) in ["IPV4","IPV6","SOCKETV4","SOCKETV6"] && upper(zone) in ["SUSPICIOUS"](для каждого списка – свой набор типов и зона) | Оставляет только индикаторы, соответствующие типу и зоне |
Mapper | jsonpath – извлечение actions из поля feeds | Преобразует данные: вычисляет итоговое действие (UPDATE или DELETE) и выбирает поля value, action |
| ||
NADSink | server – 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)
| Параметр | Тип данных | Обязательность | Описание | Пример |
|---|---|---|---|---|
credentials | BasicAuth | Да | Аутентификация по логину/паролю. | credentials = new BasicAuth { username = read("env:TIC_AGENT_SERVICE_USER") password = read("env:TIC_AGENT_SERVICE_PASSWORD") } |
address | String | Да | Полный URL API PT NAD | address = read("env:TIC_AGENT_SERVICE_URL") |
insecureSkipVerify | Boolean | Нет | Отключение проверки SSL-сертификата | insecureSkipVerify = true |
Параметры NADSink
| Параметр | Тип данных | Обязательность | Описание |
|---|---|---|---|
server | ServerCfg | Да | Конфигурация подключения (адрес и учётные данные) |
color | String | Да | Цвет репутационного списка, в который загружаются индикаторы (orange или red) |
type | String | Да | Тип индикаторов (ip, dn, uri, md5) |
externalKey | String | Да | Идентификатор внешнего списка в 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}.