Как запускать сканирование уязвимостей по расписанию?

Вопрос: как запускать сканирование уязвимостей по расписанию?

Линия поддержки
Линия поддержки Ответы на вопросы пользователей
8 октября 2026

Пайплайн через API: сбор отчетов, оповещения и запуск по расписанию.

Изображение записи

Комментарий пользователя

Поставил скрипт сканирования в cron, запускается раз в час. Через сутки обнаружил, что в интерфейсе висит несколько задач на одну и ту же подсеть, а часть из них в непонятном состоянии. Похоже, новый запуск стартует, не дожидаясь, пока закончится предыдущий, и лепит задачи поверх. Увеличивать интервал наугад не хочется, так как сканирование сети и поиск уязвимостей каждый раз занимают разное время. Как сделать правильно?

Кирилл Никитин инженер по ИБ

Ответ специалиста

Здравствуйте, Кирилл!

Вы наткнулись на ограничение cron: он умеет запускать скрипт в заданное время, но не знает, закончилось ли предыдущее сканирование. Увеличение интервала тут не лечит, а маскирует — вы правы, что время выполнения плавает.

Решение — конвейер, в котором состояние каждой задачи хранится между запусками. Планировщик запускает скрипт, тот через API создает задачи, проверяет их состояние, последовательно запускает сетевое сканирование и поиск уязвимостей, генерирует отчет и уже после завершения всей цепочки отправляет уведомление.

В начале каждого запуска выполняется авторизация и обновление баз уязвимостей:


      auth_cookies = authorization()
    if not auth_cookies:
        print("Авторизация не удалась. Программа завершает работу.")
        return
    base_update(auth_cookies)

Затем мы загружаем список проверяемых адресов, который хранится отдельно от скрипта. Это позволяет изменять состав проверяемой инфраструктуры, не меняя код. Для каждого адреса создается ресурс, а его идентификатор сохраняется в локальном файле. Если ресурс уже создан, он не перезаписывается, а пропускается. Файл становится хранилищем состояния для конвейера.


      clients = load_client_list(client_list_file)
    for client in clients:
        client_ip = client.get("ip")
        id_asset = client.get("id_asset")

Затем создается задача сетевого сканирования, и отдельным запросом она запускается:


      task_id = add_netscan_task(auth_cookies, client_name, client_ips)
    if task_id:
        client["id_netscan"] = task_id
        save_client_list(client_list_file, clients)
        run_task(auth_cookies, task_id)

А вот ключевой для вашей ситуации фрагмент. После запуска задачи скрипт записывает состояние задачи как «запущена» и периодически запрашивает ее состояние, проверяя, не завершилась ли она. Если сканирование выполняется, далее по скрипту мы не продвинемся:

  • Антон Дятлов

    Антон Дятлов

    Инженер по защите информации


      task_state = get_task_status(auth_cookies, existing_netscan_task_id)
    if task_state == "completed":
        print(f"Задача Netscan для клиента {client_name} уже завершена. Переходим к созданию Vulnscan.")
    elif task_state in ["running", "progress"]:
        print(f"Задача Netscan для клиента {client_name} еще выполняется. Пропускаем.")
        continue

Это защищает сразу от двух сценариев: скрипт не создаст второе сканирование поверх первого и не побежит дальше, создавая задачи на поиск уязвимостей по хостам, которые еще не досканированы. Тот же принцип применяется и к задаче поиска уязвимостей, и к формированию отчета — каждый этап сначала смотрит, завершен ли предыдущий. После завершения сетевого сканирования создается следующая задача, и все идентификаторы передаются из уже созданных ресурсов:


      vulnscan_task_id = add_vulnscan_task(auth_cookies, client_name, asset_ids)
    if vulnscan_task_id:
        for client in clients:
            if client.get("name") == client_name:
                client["id_vulnscan"] = vulnscan_task_id
        save_client_list(client_list_file, clients)
        run_task(auth_cookies, vulnscan_task_id)

После завершения поиска уязвимостей генерируется отчет. Готовность также фиксируется в списке клиентов:


       if client_status == "done":
    print(f" Отчет для клиента {client_name} уже создан. Пропускаем.")
    continue

После этого завершенные задачи удаляются, список клиентов очищается, и мы получаем оповещение в Telegram:


      all_reports_done = all(client.get("status") == "done" for client in clients)
send_telegram_message("Все отчеты успешно созданы и ресурсы удалены.")

Практический вывод для вашего расписания: оставляйте ежечасный запуск. При такой схеме лишние запуски безвредны — если предыдущий этап не завершен, скрипт просто выйдет, ничего не создав. Параллельных сканирований, которые нагрузят машину, тоже не будет.