Overview
Greenbone Community Edition (GCE) is the open-source stack behind OpenVAS. It authenticates against hosts, probes services and matches findings against a large feed of network vulnerability tests (NVTs). Unlike a template-driven scanner such as Nuclei, it performs full host-level scanning: open ports, service versions, missing patches and weak configurations.
In this project you will:
- Run GCE with the official Greenbone community containers.
- Create scan targets, a recurring schedule and a task with
python-gvm, so the setup lives in version control rather than in the web UI. - Export a PDF or CSV report after each scan.
Only scan networks and hosts you own or have written authorisation to test. Full-and-fast scans are noisy and can stress fragile devices such as printers and IoT gear.
Architecture
GCE is a set of cooperating containers:
| Component | Role |
|---|---|
gvmd | Management daemon. It holds targets, tasks and results, and speaks GMP. |
pg-gvm | PostgreSQL database for gvmd. |
ospd-openvas + openvas | The scanner and its OSP wrapper. |
redis-server | Scratch store for scanner state. |
gsad + gsa | Web interface on port 9392. |
gvm-tools | CLI container for scripting GMP. |
| Data containers | Vulnerability tests, SCAP and CERT data, and notus data (feed content). |
gvmd and the scanner communicate over Unix sockets in shared volumes. The scripts below attach to the same socket.
python-gvm script ──GMP/socket──> gvmd ──> pg-gvm
│
└─OSP/socket──> ospd-openvas ──> targets
browser ──:9392──> gsad ──> gvmdPrerequisites
- A Linux host with Docker Engine and the Compose plugin. Plan for at least 4 vCPU, 8 GB RAM and 30 GB of disk. The first feed sync is large.
- Outbound access to the Greenbone community feed.
- A scan network reachable from the host. For VLANs, use a host interface or macvlan on that VLAN.
Step 1: Deploy the stack
Greenbone publishes a reference compose file in its community containers documentation. Always start from the current version of that file, because image tags and service names change.
mkdir -p ~/greenbone && cd ~/greenbone
curl -fsSL https://greenbone.github.io/docs/latest/_static/compose.yaml -o compose.yaml
docker compose -f compose.yaml pull
docker compose -f compose.yaml up -dBy default gsad binds to 127.0.0.1:9392. Keep it that way and put it behind a reverse proxy with TLS (see the Traefik project) rather than exposing it directly.
The first start downloads the feeds, which can take a long time. Watch progress:
docker compose -f compose.yaml logs -f gvmd vulnerability-testsDo not run scans until the feed import has finished. Early scans silently use incomplete NVT data.
Step 2: Set the admin password
docker compose -f compose.yaml exec -u gvmd gvmd \
gvmd --user=admin --new-password='<long-random-password>'Store the password in your password manager. Then sign in at http://127.0.0.1:9392 and confirm that the NVT count under Administration > Feed Status is non-zero.
Step 3: Create targets, schedule and task with python-gvm
Doing this in code makes the scan configuration reviewable and repeatable. The script finds the built-in scan config, scanner and port list by name, because their UUIDs differ between versions.
Create provision.py:
import os
from gvm.connections import UnixSocketConnection
from gvm.protocols.gmp import Gmp
from gvm.transforms import EtreeCheckCommandTransform
SOCKET = os.environ.get("GVM_SOCKET", "/run/gvmd/gvmd.sock")
USER = os.environ["GVM_USER"]
PASSWORD = os.environ["GVM_PASSWORD"]
HOSTS = os.environ.get("SCAN_HOSTS", "192.168.10.0/24").split(",")
def find_id(response, xpath_name, wanted):
for el in response.xpath(xpath_name):
if el.findtext("name") == wanted:
return el.get("id")
raise SystemExit(f"Not found: {wanted}")
with Gmp(UnixSocketConnection(path=SOCKET), transform=EtreeCheckCommandTransform()) as gmp:
gmp.authenticate(USER, PASSWORD)
config_id = find_id(gmp.get_scan_configs(), "scan_config", "Full and fast")
scanner_id = find_id(gmp.get_scanners(), "scanner", "OpenVAS Default")
ports_id = find_id(gmp.get_port_lists(), "port_list", "All IANA assigned TCP")
target = gmp.create_target(
name="lab-lan",
hosts=HOSTS,
port_list_id=ports_id,
)
target_id = target.get("id")
# Sundays at 02:00 local time
ical = (
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//cosmicbytez//EN\r\n"
"BEGIN:VEVENT\r\nUID:weekly-lab-scan\r\nDTSTAMP:20261101T000000Z\r\n"
"DTSTART:20261108T020000\r\nDURATION:PT6H\r\n"
"RRULE:FREQ=WEEKLY;BYDAY=SU\r\nEND:VEVENT\r\nEND:VCALENDAR"
)
schedule = gmp.create_schedule(
name="weekly-sunday-0200",
icalendar=ical,
timezone="America/Edmonton",
)
task = gmp.create_task(
name="Weekly lab LAN scan",
config_id=config_id,
target_id=target_id,
scanner_id=scanner_id,
schedule_id=schedule.get("id"),
)
print("Created task", task.get("id"))Run it from the gvm-tools container, or any container that mounts the gvmd socket volume. Check the exact volume and service names in your compose.yaml:
docker compose -f compose.yaml run --rm \
-e GVM_USER=admin -e GVM_PASSWORD='<password>' \
-v "$PWD/provision.py:/provision.py:ro" \
--entrypoint python3 gvm-tools /provision.pyIf the gvm-tools image does not include python-gvm on the default interpreter path, use gvm-script socket instead. It supplies the authenticated gmp object for you.
Step 4: Add credentialed scanning
Unauthenticated scans only see the network surface. Credentialed scans find missing patches and local misconfigurations, and they usually give far better results.
- Create a dedicated, low-privilege scan account on each host (SSH key for Linux, a local non-admin account with the required remote-registry and WMI rights for Windows).
- In GSA, go to Configuration > Credentials and add the credential.
- Edit the target and attach the SSH or SMB credential.
Never reuse a domain admin account for scanning.
Step 5: Export reports automatically
Add a second script that finds the newest finished report for the task and saves it:
import base64, os, sys
from gvm.connections import UnixSocketConnection
from gvm.protocols.gmp import Gmp
from gvm.transforms import EtreeCheckCommandTransform
with Gmp(UnixSocketConnection(path="/run/gvmd/gvmd.sock"),
transform=EtreeCheckCommandTransform()) as gmp:
gmp.authenticate(os.environ["GVM_USER"], os.environ["GVM_PASSWORD"])
tasks = gmp.get_tasks(filter_string='name="Weekly lab LAN scan"')
last = tasks.xpath("task/last_report/report")[0].get("id")
fmts = gmp.get_report_formats()
csv_id = next(f.get("id") for f in fmts.xpath("report_format")
if f.findtext("name") == "CSV Results")
rep = gmp.get_report(last, report_format_id=csv_id, ignore_pagination=True,
details=True)
blob = rep.xpath("report/text()")[0]
with open(sys.argv[1], "wb") as fh:
fh.write(base64.b64decode(blob))Feed the CSV into DefectDojo, or diff it week over week to highlight new findings only.
Testing
- Feed health: Administration > Feed Status shows all feeds as current.
- Smoke test: create a one-host target pointing at a lab VM you control and start the task manually from GSA. A test VM with an outdated SSH daemon or an exposed test service is ideal.
- Schedule test: temporarily set
DTSTARTa few minutes ahead and confirm the task starts on its own. - Detection check: deploy a deliberately vulnerable VM (Metasploitable or similar) and confirm it produces high-severity results. If it shows nothing, the feed sync probably did not finish.
- Report check: run the export script and open the CSV.
Deployment and hardening
- Keep
gsadon loopback and publish it through a TLS reverse proxy with SSO. - Pin images by digest after you have validated a release, and update on a schedule:
docker compose pull && docker compose up -d. - Back up the PostgreSQL volume and
gvmddata volume (Backrest/restic works well). - Run scans in an off-hours window and limit concurrency for slow or fragile networks.
- Alert on failed tasks by polling
get_tasksand checkingstatusforInterruptedorStopped. - Restrict who can reach the scanner host: it holds credentials for every credentialed scan target.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Scan finishes instantly with no results | Feed sync incomplete, or the target is unreachable from the scanner container |
| Task stuck at "Requested" | ospd-openvas not connected to the socket or Redis |
| Credentialed scan shows "login failed" | Wrong credential type or insufficient rights on the target |
| Very slow scans | Too many parallel hosts or NVTs for the allotted CPU and RAM |
Extensions
- Use the Greenbone Ansible collection to manage targets, credentials and tasks as code.
- Import weekly results into DefectDojo for deduplication and SLA tracking.
- Send high-severity findings to Discord or TheHive via an n8n workflow.
- Add separate tasks for each VLAN with tuned port lists and schedules.
- Combine with Nuclei for application-layer checks, so OpenVAS covers hosts and services while Nuclei covers web exposure.