Greenbone (OpenVAS) в Docker: сканирование по расписанию и контроль завершения

Таблица «что чем сканируем» в любой статье про уязвимости упирается в один вопрос: каким сканером. Бесплатный и достаточно сильный вариант для небольшой компании — Greenbone Community Edition (ядро OpenVAS). Ниже установка в Docker, настройка сканирования внешнего периметра и внутренней сети, расписание «раз в неделю» и «раз в месяц» и проверка, что скан прошёл, а не молча сломался. Если по закону или правилам вам нужны сертифицированные средства, берите их; логика расписания, оповещений и хранения результатов не меняется.
Как читать результат и превращать его в задачи — в разборе отчёта сканера. Куда складывать находки — в реестр уязвимостей.
Где запускать сканер
Сканеров обычно два, и это не прихоть. Для внешнего периметра сканер должен стоять снаружи вашей сети, например на недорогой виртуальной машине у стороннего хостера: только так видно то, что видит атакующий. Для внутренней сети сканер стоит внутри, в сегменте управления, и ему разрешён доступ к узлам по SSH и SMB. Один сканер внутри не заменит внешний: он покажет открытое, но не покажет, что из этого доступно из интернета.
Ресурсы: ориентируйтесь на четыре ядра и 8 ГБ памяти; для небольшого парка хватает и меньшего, но первая синхронизация лент тестов идёт заметно дольше. Диск: несколько десятков гигабайт под ленты и базу. Сканировать можно только свои адреса или адреса, на которые есть письменное разрешение владельца.
Шаг 1. Установка в Docker
Команды из официальной инструкции Greenbone для контейнеров Community Edition. Имя файла compose может смениться с выходом новой серии, поэтому актуальную ссылку проверьте в документации перед запуском.
export DOWNLOAD_DIR=$HOME/greenbone-community-container
mkdir -p "$DOWNLOAD_DIR" && cd "$DOWNLOAD_DIR"
curl -f -L https://greenbone.github.io/docs/latest/_static/docker-compose-22.4.yml -o docker-compose.yml
docker compose -f docker-compose.yml -p greenbone-community-edition pull
docker compose -f docker-compose.yml -p greenbone-community-edition up -d
Ожидаемо: команда pull скачивает с десяток образов, up -d поднимает контейнеры (gvmd, ospd-openvas, gsa, база pg-gvm, redis, брокер сообщений). Веб-интерфейс слушает только 127.0.0.1:9392, и так оставьте: не публикуйте его в интернет.
Если сканер на удалённой машине, заходите в интерфейс через SSH-туннель:
ssh -L 9392:127.0.0.1:9392 admin@scanner.example.com
# затем откройте в браузере http://127.0.0.1:9392
Сразу смените пароль администратора (в компоновке по умолчанию он известен):
docker compose -f docker-compose.yml -p greenbone-community-edition \
exec -u gvmd gvmd gvmd --user=admin --new-password="$NEW_GVM_PASSWORD"
Первая загрузка лент тестов идёт десятки минут, а то и часа. Пока лента не загрузилась, в интерфейсе в разделе Administration → Feed Status статус будет отличаться от «Current», а скан запускать бессмысленно: он будет работать без большей части тестов и выдаст почти пустой отчёт.
Риск: контейнеры с данными лент и базой нужно сохранять в резервной копии или хотя бы не удалять по ошибке командой down -v: она сотрёт тома вместе с историей отчётов.
Шаг 2. Цель, список портов и учётная запись
Все настройки в Configuration. Порядок такой.
| Раздел | Что создать | Замечания |
|---|---|---|
| Targets | Цель «Периметр» с вашими внешними адресами; цель «Внутренняя сеть» | Адреса берите из реестра активов, а не «весь /16». Исключите узлы, которые нельзя трогать (хрупкое оборудование, АСУ ТП), в поле Exclude Hosts |
| Port Lists | Для периметра — все TCP-порты; для внутренней сети — стандартный набор | Полный перебор 65535 портов на каждом узле занимает время, поэтому для внутренней сети обычно хватает порт-листа по умолчанию |
| Credentials | Учётная запись для SSH (логин и ключ), для Windows — SMB-учётка | Выделите отдельную учётку «scanuser» только для сканера. Без учётки сканер видит только баннеры |
Для Linux достаточно непривилегированной учётной записи: список установленных пакетов читается без прав администратора. Права выше нужны только отдельным проверкам конфигурации. Создайте пользователя и ключ без права входа интерактивно с чужих адресов:
sudo useradd -m -s /bin/bash scanuser
sudo install -d -m 700 -o scanuser -g scanuser /home/scanuser/.ssh
# в authorized_keys добавьте публичный ключ сканера и ограничение по адресу
echo 'from="192.0.2.50" ssh-ed25519 ПУБЛИЧНЫЙ_КЛЮЧ_СКАНЕРА greenbone' | sudo tee /home/scanuser/.ssh/authorized_keys
sudo chmod 600 /home/scanuser/.ssh/authorized_keys
sudo chown scanuser:scanuser /home/scanuser/.ssh/authorized_keys
Ожидаемо: вход с адреса сканера по ключу работает, с других адресов — нет. Приватный ключ загрузите в Credentials, на самом сервере он не хранится. Если вы раскатываете учётку на парк, делайте это центральным средством: GPO и Ansible.
Для Windows используйте учётную запись с правом чтения реестра и WMI; точный набор прав описан в документации Greenbone. Не давайте сканеру учётку доменного администратора: компрометация сканера не должна означать компрометацию домена.
Шаг 3. Задача, профиль и нагрузка
В Scans → Tasks создайте задачу: выберите цель, сканер OpenVAS и профиль (Scan Config). Выбор профиля влияет на время и нагрузку.
| Профиль | Когда использовать | Замечание |
|---|---|---|
| Full and fast | Основной профиль для периметра и внутренней сети | Запускает почти все тесты, кроме опасных для устойчивости сервисов |
| Discovery | Инвентаризация: что живо и какие порты открыты | Не ищет уязвимости, хорош для сверки с реестром активов |
| Base | Быстрая проверка «ничего ли не сломали» | Минимум тестов; не заменяет полный скан |
В расширенных настройках задачи ограничьте число одновременно проверяемых узлов и тестов на узел. Для сети с хрупкими устройствами держите значения скромными: агрессивный скан способен положить принтер или старый коммутатор, и первые жалобы придут к вам.
Шаг 4. Расписание
В Configuration → Schedules создайте расписание по формату iCalendar. Для проверки «не реже раза в месяц» и «еженедельно для периметра» нужны два:
| Задача | Периодичность | Когда запускать |
|---|---|---|
| Периметр | Раз в неделю | Ночью по будням, чтобы утром вы уже читали отчёт |
| Внутренняя сеть с учётной записью | Раз в месяц | В начале месяца, за неделю до окна обновлений, чтобы успеть выдать заявки |
| Критичные узлы | Раз в неделю | Отдельной задачей с мягким профилем, если их нельзя сканировать вместе со всеми |
В карточке расписания укажите часовой пояс явно. Типичная причина «скан не запустился»: расписание создано в UTC, а ждали по местному времени, и задача ушла на другие сутки. Привяжите созданное расписание к задаче в её настройках.
Шаг 5. Оповещения и контроль завершения
Требование к расписанию звучит так: недостаточно настроить, надо контролировать, что каждый скан завершился. Для этого нужны два механизма.
Оповещение. В Configuration → Alerts создайте оповещение: событие «Task run status changed» со значением «Done», условие «Always», метод на выбор. Письмо требует настроенной почтовой службы внутри контейнера gvmd, поэтому в контейнерной установке проще использовать другие методы: HTTP Get на ваш вебхук или системный журнал (System Logger), откуда событие попадёт в Wazuh или Zabbix. Привяжите оповещение к задаче.
Контроль «молчания». Оповещение приходит только если скан завершился. Если задача не запустилась или зависла, не придёт ничего, и именно об этом надо узнавать. Скрипт ниже раз в сутки проверяет, что у задачи есть свежий отчёт, и возвращает код 1, если его нет. Его можно запускать из cron или из Zabbix. Он использует утилиту gvm-cli из пакета gvm-tools (установите её на хосте). Путь к сокету gvmd в вашей компоновке смотрите командой docker volume inspect для тома gvmd_socket_vol.
#!/usr/bin/env python3
"""Проверка свежести отчёта задачи Greenbone: код выхода 1, если отчёта нет или он старше N дней.
Использование: check_scan.py ИМЯ_ЗАДАЧИ ДНЕЙ
Пароль берётся из переменной окружения GVM_PASSWORD."""
import os
import subprocess
import sys
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
SOCKET = '/var/lib/docker/volumes/greenbone-community-edition_gvmd_socket_vol/_data/gvmd.sock' # проверьте путь
task, days = sys.argv[1], int(sys.argv[2])
xml = f'<get_tasks filter="name={task} rows=1"/>'
cmd = ['gvm-cli', '--gmp-username', 'admin', '--gmp-password', os.environ['GVM_PASSWORD'],
'socket', '--socketpath', SOCKET, '--xml', xml]
out = subprocess.run(cmd, capture_output=True, text=True, check=True).stdout
root = ET.fromstring(out)
ts = root.findtext('.//last_report/report/timestamp')
if not ts:
print(f'ПРОВАЛ: у задачи {task} нет ни одного отчёта')
sys.exit(1)
age = (datetime.now(timezone.utc) - datetime.fromisoformat(ts.replace('Z', '+00:00'))).days
print(f'отчёт задачи {task}: {ts}, возраст {age} дн.')
sys.exit(1 if age > days else 0)
Ожидаемо: для исправной недельной задачи вывод вида «возраст 3 дн.» и код 0. Для задачи, которая не запускалась, код 1. Запустите вручную и убедитесь, что скрипт возвращает 1 на задаче без отчётов, прежде чем вешать его в мониторинг.
Шаг 6. Отчёт, фильтры и сравнение
Откройте последний отчёт и сразу выставьте фильтр: минимальный QoD 70 (так по умолчанию, но проверьте) и серьёзность не ниже средней. Дальше два полезных приёма.
- Сравнение отчётов. Кнопка «Delta report» показывает, что появилось и что исчезло по сравнению с прошлым сканом. Это самый быстрый способ проверить закрытие после обновления.
- Экспорт. CSV для импорта в реестр, PDF для людей. В имени файла сохраняйте дату и название задачи.
Разбор находок, группировка по действию и подтверждение закрытия описаны в отдельной статье.
Какие артефакты хранить
Для регулярного сканирования подтверждением служат пять вещей. Заведите для них одну папку или раздел трекера.
| Что хранить | Откуда взять | Какое требование подтверждает |
|---|---|---|
| Скриншот или выгрузка списка задач с расписанием | Scans → Tasks, Configuration → Schedules | Скан настроен и запускается регулярно |
| Отчёты за последние три месяца | Reports, экспорт PDF и CSV | Скан реально проходил, а не только настроен |
| Перечень целей с датой актуализации | Targets и реестр активов | Охват: сканируется вся инфраструктура, включая периметр |
| Оповещения о завершении и вывод скрипта контроля | Почта, вебхук, журнал | Завершение каждого скана контролируется |
| Порядок действий при провале скана | Регламент, раздел о сканировании | Кто и за какое время реагирует, если отчёта нет |
Диагностика
| Симптом | Причина и что делать |
|---|---|
| Скан завершается за несколько минут, находок почти нет | Ленты тестов не загружены или сканер не авторизовался. Проверьте Feed Status и статус входа по каждому узлу |
| Задача зависла в состоянии «Running» сутками | Слишком большой диапазон или недоступный узел с долгими тайм-аутами. Разбейте цель, уменьшите число одновременных узлов |
| После обновления контейнеров пропали отчёты | Тома были удалены. Проверьте, что данные базы лежат в сохраняемом томе, и добавьте их в резервное копирование |
| Скан периметра изнутри показывает «всё закрыто» | Он смотрит из-за межсетевого экрана. Периметр сканируйте снаружи |
| Узел внезапно перезагружается во время скана | Агрессивный профиль или хрупкая служба. Вынесите узел в отдельную цель с мягкими настройками |
Где это в модели зрелости
Материал относится к направлению «Управление уязвимостями» (хаб: уязвимости, карта: карта зрелости). Он закрывает вторую ступень: автоматический скан внешнего периметра и всей инфраструктуры, расписание не реже раза в месяц и контроль успешного завершения. Итоговые отчёты и их связь с задачами нужны уже для третьего уровня: реестр.
Как проверить
- В Feed Status все ленты в состоянии «Current».
- Для каждой задачи видно расписание и дату следующего запуска.
- В Reports есть отчёты за последние три месяца без пропущенных периодов.
- Скрипт контроля на тестовой задаче без отчётов возвращает код 1, на исправной — 0.
- Скан внутренней сети с учётной записью показывает версии пакетов, а не только баннеры.
Источники
- Greenbone Community Edition, установка контейнеров: greenbone.github.io/docs.
- Greenbone, настройка сканирования и оповещений: greenbone.github.io/docs.
- gvm-tools и протокол GMP: greenbone.github.io/gvm-tools.
- CIS Control 7: cisecurity.org/controls.


















