Документация / Сканирование
Джобы и последовательности
Джоба - шаблон запуска одного контейнера в Radar: образ, команда, переменные и правила поведения. Последовательность - упорядоченный список джоб в Core. Запуск последовательности для одного ассета - пайплайн. Как пайплайн исполняется, описано на странице Как работает Radar.
Где находится:
- джобы - в Radar, раздел "Джобы": "Создать Джобу", "Копировать", "Импорт Джоб";
- последовательности и расписание - в Core: "Radar" → "Последовательности" и "Radar" → "Расписание".
Права в Core: для последовательностей, расписания и запуска аудитов нужна роль с правом "Radar" - "Изменение" и лицензия с доступом к Radar. Подробно - Роли и права, Лицензирование.
Поля джобы
| Поле в интерфейсе | Поле API | Что задаёт | По умолчанию |
|---|---|---|---|
| "Имя джобы" | name |
уникальное имя. По нему последовательности Core ссылаются на джобу | - |
| "Образ" | image |
образ с реестром и тегом, например registry. |
- |
| "Команда запуска" | run_command |
команда, выполняется как sh -c |
- |
| "Переменные окружения:" | environment_ |
переменные джобы, см. ниже | - |
| "Тип джобы" | type |
"Сканнер" (SCANNER) или "Техническая" (TECHNICAL) |
"Сканнер" |
| "Таймаут джобы (в минутах)" | timeout |
предел времени джобы, 0 - без предела. Отрицательное значение не принимается | 0 |
| "Разрешить падение" | allow_failure |
выключено - ненулевой код выхода, таймаут или ошибка джобы останавливают весь пайплайн | включено |
| "Копировать артефакты в следующие джобы из дирректории {directory}" | use_ |
после джобы копировать содержимое /data в общий том /code |
выключено |
| "Перезаписывать Энтрипоинт Контейнера" | overwrite_ |
заменить ENTRYPOINT образа пустым, чтобы команда шла через sh -c |
выключено |
| "Учетные данные" | docker_ |
учётные данные реестра для скачивания образа. Заводятся в "Настройки" → "Уч. Данные Docker", см. Настройки Radar | не заданы |
| "Использовать настройки поставщика" | use_ |
брать "Настройки Поставщика" вместо "Пользовательские Настройки" | выключено |
Пользовательские настройки и настройки поставщика
У джобы два набора полей: "Пользовательские Настройки" и "Настройки Поставщика". Пайплайн берёт пользовательские, если не включено "Использовать настройки поставщика".
- Правьте "Пользовательские Настройки".
- "Настройки Поставщика" - справочный набор стандартной джобы каталога. API Radar перезаписывает его при каждом старте.
- Смена образа в пользовательских настройках удаляет старый образ из демона Docker.
Каталог и перезапуск
При каждом старте API Radar сверяет каталог стандартных джоб:
- недостающие стандартные джобы создаются заново. Удалённая стандартная джоба вернётся после перезапуска;
- "Настройки Поставщика" стандартных джоб перезаписываются;
- ваши джобы и "Пользовательские Настройки" не меняются.
Порядок первичной настройки
Создайте токен доступа Radar до того, как добавлять свои джобы: пока токена нет, API при старте пересоздаёт каталог джоб из поставки. См. Настройки Radar.
Список стандартных джоб - Справочник сканеров.
Типы джоб
| Тип | Что делает | Пример |
|---|---|---|
| "Сканнер" | выполняет команду, затем отправляет файл /data/$REPORT_FILE_NAME в Core |
Gitleaks Scan, Semgrep |
| "Техническая" | только выполняет команду. Отчёт в Core не отправляется | Code Downloader, Httpx |
Code Downloader и пустой код
У Code Downloader в каталоге включено "Разрешить падение". Если клонирование не удалось, джоба получит статус "Джоба завершена", сканеры запустятся на пустом /code и отправят в Core пустые отчёты. Пустой отчёт неотличим от чистого кода, а при включённом автозакрытии закроет найденные ранее уязвимости. Выключите у Code Downloader "Разрешить падение": тогда ошибка клонирования остановит пайплайн.
Таймаут Code Downloader в каталоге - 1 минута. Для больших репозиториев увеличьте его.
Переменные окружения
Свойства переменной
| Свойство | Значения | Что делает |
|---|---|---|
| Видимость | "Видимая" / "Скрытая" | видимая печатается в начале лога джобы целиком. Значение скрытой в логе заменяется на ***MASKED*** |
| Обязательность | "Обязательна для запуска Джобы" / "Не обязательна для запуска Джобы" | пустая обязательная переменная останавливает джобу с сообщением Missing required variables: [...] |
Правила:
- Подстановка. Значение вида
$ИМЯзаменяется значением переменнойИМЯтой же джобы или пайплайна. Сначала ищется среди видимых, затем среди скрытых. Если подставлено значение скрытой переменной, результат тоже становится скрытым. Не нашлось - подставляется пустая строка. Подстановка одноуровневая. Пример из каталога: переменнаяTRIVY_PASSWORDсо значением$DOCKER_REGISTRY_PASSWORD. - Приоритет. Переменная пайплайна перекрывает одноимённую переменную джобы. Core передаёт переменные пайплайна при каждом запуске, см. таблицу ниже.
- Контейнер получает все переменные: джобы и пайплайна, видимые и скрытые.
Секреты
Пароли, токены и ключи храните только в скрытых переменных. Видимая переменная попадает в лог джобы.
Служебные переменные джобы-сканера
Джобе типа "Сканнер" нужны две переменные:
| Переменная | Что задаёт | Пример |
|---|---|---|
REPORT_FILE_NAME |
имя файла отчёта в /data, без подкаталогов. Воркер читает ровно /data/$REPORT_FILE_NAME |
gitleaks.json |
SCAN_TYPE |
имя сканера в Core. По нему Core выбирает импортёр. Должно точно совпадать с именем из "Глобальные настройки" → "Сканеры", иначе Core отклонит отчёт и джоба будет остановлена | Gitleaks Scan |
Список имён - Справочник сканеров.
Переменные, которые передаёт Core
Core добавляет их к каждому пайплайну. Джоба может ссылаться на них в команде ($REPOSITORY) или через подстановку.
| Переменная | Скрытая | Когда передаётся | Что содержит |
|---|---|---|---|
PORTAL_IMPORT_URL |
нет | всегда | адрес приёма отчётов: "Внутренний URL приложения" + api/v1/scan/import/ |
PORTAL_TOKEN |
да | всегда | API-токен пользователя, от имени которого запущен аудит |
AUDIT_ID |
нет | всегда | номер аудита в Core |
PRODUCT_NAME, PRODUCT_TYPE |
нет | всегда | продукт ассета и его тип |
REPOSITORY |
нет | ассет типа "Репозиторий" | SSH URL репозитория |
DOCKER_IMAGE |
нет | ассет типа "Docker-образ" | образ без тега |
DOMAIN |
нет | ассет типа "Домен" | домен |
HOST |
нет | ассет типа "Хост" | хост |
CLOUD_ACCOUNT, CLOUD_KEY_ID |
нет | ассет типа "Облачный аккаунт" | аккаунт и ID ключа |
CLOUD_KEY_SECRET |
да | ассет типа "Облачный аккаунт" | секрет ключа облака |
BRANCH |
нет | если при запуске указана ветка | ветка. Security checker передаёт <ветка>:<sha> |
DOCKER_IMAGE_TAG |
нет | если при запуске указан тег | тег образа |
DOCKER_REGISTRY_USER, DOCKER_REGISTRY_ADDRESS |
нет | если заданы в Core: "Radar" → "Настройки" | пользователь и адрес реестра для сканирования образов |
DOCKER_REGISTRY_PASSWORD |
да | если задан там же | пароль реестра |
PRIVATE_SSH_KEY |
да | если задан там же | SSH-ключ для клонирования репозиториев |
Переменные, которые воркер отправляет в Core
После джобы-сканера воркер берёт метаданные отчёта из переменных джобы и пайплайна: SCAN_TYPE, PRODUCT_NAME, PRODUCT_TYPE, REPOSITORY, DOCKER_IMAGE, DOMAIN, HOST, CLOUD_ACCOUNT, BRANCH, DOCKER_IMAGE_TAG, AUDIT_ID. Адрес и токен - из PORTAL_IMPORT_URL и PORTAL_TOKEN. Соответствие полям Core - Доставка отчёта в Core.
Каталоги /data и /code
| Каталог | Для чего | Правило |
|---|---|---|
/code |
рабочий каталог джобы, общий том пайплайна | здесь код из Code Downloader и файлы, которые передают друг другу джобы |
/data |
личный том джобы | сюда сканер кладёт отчёт. Только файл из /data уходит в Core |
Пример команды сканера: gitleaks detect -s ./$SEC_CODE_FOLDER -r /data/$REPORT_FILE_NAME.
Передать файлы следующим джобам можно двумя способами:
- записать их прямо в
/code- так делает Subfinder сdomains.txtиurls.txt; - записать в
/dataи включить "Копировать артефакты в следующие джобы из дирректории {directory}". После джобы содержимое/dataскопируется в/code, а том джобы удалится.
Последовательности
Последовательность хранит только имена джоб Radar и их порядок. Настройки самих джоб живут в Radar.
Где: Core, "Radar" → "Последовательности", окно "Добавить последовательность сканирования".
| Поле | Что задаёт |
|---|---|
| "Название" | имя последовательности. Им же называется пайплайн в Radar |
| "Установить как последовательность по умолчанию" | последовательность предлагается в форме запуска аудита и используется для ассетов без своей. По умолчанию может быть только одна: новая снимает флаг со старой |
| "Джоб Radar" | имя джобы в Radar, точно как в поле "Имя джобы" |
Порядок джоб меняют пункты "Переместить вверх" и "Переместить вниз" в меню джобы. Джобы выполняются сверху вниз.
Переключатель общих артефактов у джобы в последовательности меняет флаг "Копировать артефакты..." в самой джобе Radar. Он действует во всех последовательностях, где есть эта джоба.
Последовательность по умолчанию после установки: Code Downloader, Trufflehog3 Scan, Gitleaks Scan, Semgrep, Trivy Code Dependencies.
Выбор последовательности при запуске
- Последовательность, указанная при запуске.
- Последовательность, привязанная к ассету.
- Последовательность по умолчанию.
Последовательность применяется к ассету целиком, без учёта его типа. Джоба, которой не хватает переменной ассета (например REPOSITORY для домена), будет остановлена. Создайте отдельные последовательности для репозиториев, образов и доменов и привяжите их к ассетам.
Типовые последовательности
| Тип ассета | Порядок джоб |
|---|---|
| Репозиторий | Code Downloader → сканеры кода: Gitleaks Scan, Trufflehog3 Scan, Semgrep, Bandit, Gosec, ESlint, Checkov, Terrascan, Trivy Code Dependencies, Retirejs |
| Docker-образ | Trivy Image Dependencies |
| Домен | Subfinder → Naabu → Httpx → Nuclei Scan |
Code Downloader всегда первый: без него у сканеров кода пустой /code. Джобы внешнего периметра читают файлы предыдущих, поэтому их порядок фиксирован, см. Справочник сканеров.
Один импортёр в двух джобах
Если две джобы последовательности отправляют отчёт с одним SCAN_TYPE, а у сканера включено автозакрытие, второй отчёт закроет находки первого. Core предупреждает об этом при сборке последовательности.
Удаление последовательности
Вместе с последовательностью удаляются привязанные к ней ассеты, расписания и интеграции реестров контейнеров. Перед удалением привяжите их к другой последовательности.
Переименование джобы
Последовательность ссылается на джобу по имени. После переименования джобы в Radar поправьте последовательности: иначе Radar отклонит запуск, а Core покажет "Radar не содержит джобы с именем {name}".
Расписание
Своего планировщика сканов в Radar нет. Регулярные сканы запускает Core.
Где: Core, "Radar" → "Расписание", окно "Создать запланированное задание".
| Поле | Что задаёт |
|---|---|
| "Название" | имя задания |
| "Активно" | включено ли задание |
| "Последовательность джоб" | какую последовательность запускать |
| "Расписание в формате Crontab" | когда запускать. Конструктор "Настроить расписание" (поля "Повтор" и "Время") или ручной ввод - "Установить вручную" |
| "Тип области видимости" | "Продукты" или "Ассеты" |
| "Глобально" | все текущие и будущие продукты или ассеты выбранных типов. Включить может только роль с доступом ко всем продуктам |
| выбор продуктов или ассетов, "Применить к {target} с тегами", "Типы ассетов" | что сканировать |
Порядок полей Crontab
Порядок полей не как в стандартном cron
Core хранит и показывает выражение в порядке минута час день_недели день_месяца месяц. В стандартном cron третье поле - день месяца, четвёртое - месяц.
| Когда | Выражение в Core | Стандартный cron |
|---|---|---|
| каждый день в 02:30 | 30 2 * * * |
30 2 * * * |
| по понедельникам в 03:00 | 0 3 1 * * |
0 3 * * 1 |
| 1-го числа каждого месяца в 04:00 | 0 4 * 1 * |
0 4 1 * * |
| по будням в 21:00 | 0 21 1-5 * * |
0 21 * * 1-5 |
- Поля разделяйте ровно одним пробелом.
- Время - UTC.
Как выполняется задание
- Аудит запускается от имени автора задания. Его токен уходит в Radar как
PORTAL_TOKEN, поэтому автору нужно право "Уязвимости" - "Изменение". - Без области сканирования задание ничего не запускает.
- При удалении пользователя удаляются и его задания.
- API:
/api/v1/ci/schedules/, полеcrontab_expr.
Способы запуска
| Способ | Где | Что передаётся в Radar |
|---|---|---|
| Кнопка "Запустить аудит" | Core: продукт или ассет. Поля "Ветка (необязательно)", "Тег докер образа (необязательно)", последовательность | один пайплайн на ассет |
| API Core | POST /api/v1/ci/pipelines/run/, POST /api/v1/products/bulk/run-audit/, POST /api/v1/product-assets/bulk/run-audit/ |
то же, см. API Core |
| Расписание | Core: "Radar" → "Расписание" | ассеты из области задания |
| Push в репозиторий | вебхук интеграции системы контроля версий, флаг "Включить сканирование при пуше коммита" | ветка. Хеш коммита Core сохраняет в аудите, а Code Downloader клонирует ветку на момент запуска, см. Системы контроля версий |
| Новый тег образа | интеграция реестра контейнеров GitLab | DOCKER_IMAGE_TAG |
| Security checker | джоба CI | <ветка>:<sha>, см. Security checker |
| Напрямую в Radar | "Пайплайны" → "Запустить новый пайплайн"; API POST /api/v1/pipelines/run/ |
джобы и переменные, которые вы указали |
Запуск напрямую в Radar
Окно "Запуск Пайплайна": выберите "Джобы", задайте "Переменные окружения", нажмите "Запустить пайплайн".
Чтобы отчёт дошёл до Core, передайте переменные, которые обычно подставляет Core. Пример через API, токен - общий токен доступа Radar:
curl -X POST "https://radar.example.ru/api/v1/pipelines/run/" \
-H "Authorization: Bearer <токен Radar>" \
-H "Content-Type: application/json" \
-d '{
"name": "manual-gitleaks",
"jobs": [{"name": "Code Downloader"}, {"name": "Gitleaks Scan"}],
"pipeline_variables": [
{"name": "REPOSITORY", "value": "git@gitlab.example.ru:team/payments.git", "masked": false},
{"name": "BRANCH", "value": "main", "masked": false},
{"name": "PRIVATE_SSH_KEY", "value": "<SSH-ключ>", "masked": true},
{"name": "PORTAL_IMPORT_URL", "value": "https://core.example.ru/api/v1/scan/import/", "masked": false},
{"name": "PORTAL_TOKEN", "value": "<токен Core>", "masked": true},
{"name": "PRODUCT_NAME", "value": "payments", "masked": false},
{"name": "PRODUCT_TYPE", "value": "Common", "masked": false}
]
}'
Ответ 202:
{"message": "Pipeline has been triggered to start!", "ids": [42]}
- Джобы выполняются в порядке списка
jobs. POST /api/v1/pipelines/bulk-run/принимает список таких объектов.- Имя джобы, которой нет в Radar, - ответ 400
Jobs with these names do not exist. - Без
AUDIT_IDCore создаёт отдельный аудит на каждый принятый отчёт с ассетом.
Импорт и экспорт джоб
Импорт
Где: Radar, "Импорт Джоб". Выберите файл .json и нажмите "Отправить {count} джоб". API: POST /api/v1/jobs/import/.
Формат - JSON-массив джоб:
[
{
"name": "My Scanner",
"docker_credentials_id": null,
"user_settings": {
"image": "registry.example.ru/my-scanner:1.0",
"run_command": "my-scanner --output /data/$REPORT_FILE_NAME ./$SEC_CODE_FOLDER",
"environment_variables_dict": "[{\"name\":\"REPORT_FILE_NAME\",\"value\":\"my-scanner.json\",\"masked\":false,\"required\":false},{\"name\":\"SCAN_TYPE\",\"value\":\"My Scanner\",\"masked\":false,\"required\":false},{\"name\":\"SEC_CODE_FOLDER\",\"value\":\"\",\"masked\":false,\"required\":false},{\"name\":\"REPOSITORY\",\"value\":\"\",\"masked\":false,\"required\":true}]",
"use_pipeline_volume": false,
"type": "SCANNER",
"allow_failure": true,
"timeout": 10,
"overwrite_entrypoint": false
}
}
]
environment_variables_dict- строка с JSON, а не массив.docker_credentials_id- id учётных данных реестра в этом Radar илиnull.- Имена должны быть уникальными. Если хотя бы одно имя уже занято, ответ 400 с номерами конфликтующих элементов, и ни одна джоба не создаётся. Успех - 201.
Экспорт
Выгружайте джобы через API:
curl -s "https://radar.example.ru/api/v1/jobs/?name=My%20Scanner" \
-H "Authorization: Bearer <токен Radar>" > jobs.json
Без параметра name возвращаются все джобы. Ответ - массив в том же формате плюс служебные поля (id, use_vendor_settings, vendor_settings), которые импорт пропускает. Перед импортом на другую установку замените docker_credentials_id на null или на id учётных данных целевого Radar.
Чтобы сделать вариант существующей джобы, нажмите "Копировать" в её карточке и поменяйте копию.