Automated permission synchronization for Greenbone Vulnerability Management (GVM) using project tags.
gvmsync solves the lack of native multi-tenancy in Greenbone Community Edition.
It uses project:* tags on GVM resources (tasks, scanners, reports) to
automatically synchronize group-based permissions, enabling clean multi-client
isolation without manual permission management.
- Tag resources in the GVM web UI with
project:<ClientName>(e.g.,project:Acme,project:GlobalCorp). - Run gvmsync — it discovers all tagged resources, ensures a GVM group exists for each project, and grants the minimum required permissions.
- Garbage collection (optional) — revokes orphaned permissions when tags are removed or resources are deleted.
| Resource Type | Permissions Granted |
|---|---|
| Scanner | get_scanners |
| Task | get_tasks, start_task, stop_task |
| Report | get_reports |
Resources without a project:* tag are never touched.
Both the standalone CLI and the gvm-script variant have been verified
end to end against a live instance:
| Component | Version |
|---|---|
| Greenbone Community Edition | 22.4 (container deployment) |
| gvmd | 26.36.1 (DB revision 281) |
| python-gvm | 24.x |
Verified behaviour: resource discovery, group creation, least-privilege permission granting, untagged resources left untouched, idempotent re-runs, dry-run making no writes, and garbage collection revoking only the permissions whose tag was removed.
pip install gvmsyncgit clone https://github.com/TDianaAle/gvmsync.git
cd gvmsync
pip install .pip install uv
uv sync
uv run autohooks activate --force- Python >= 3.10
- Greenbone Community Edition >= 24.10
- python-gvm >= 24.0.0
- Access to the
gvmdUnix socket
# Basic sync
gvmsync --admin admin --admin-pass secret
# Dry-run (simulate without changes)
gvmsync --admin admin --admin-pass secret --dry-run
# Sync with garbage collection
gvmsync --admin admin --admin-pass secret --enable-cleanup
# Show inventory of all resources and their tags
gvmsync --admin admin --admin-pass secret --all
# Using environment variables
export GVM_ADMIN_USER=admin
export GVM_ADMIN_PASS=secret
gvmsyncCopy scripts/sync-permissions-by-tags.gmp.py to your system and run it
with gvm-tools:
gvm-script --gmp-username admin --gmp-password secret \
ssh --hostname gvm-host \
sync-permissions-by-tags.gmp.py
# With options
gvm-script ... sync-permissions-by-tags.gmp.py --dry-run
gvm-script ... sync-permissions-by-tags.gmp.py --cleanup
gvm-script ... sync-permissions-by-tags.gmp.py --all# Copy the script into the gvmd container
docker cp scripts/sync-permissions-by-tags.gmp.py \
greenbone-community-edition-gvmd-1:/scripts/
# Execute inside the container
docker exec -it greenbone-community-edition-gvmd-1 bash
pip install python-gvm lxml
python3 /scripts/sync-permissions-by-tags.gmp.py \
--admin admin --admin-pass secret| Option | Description |
|---|---|
--admin USERNAME |
GVM admin username (or GVM_ADMIN_USER env var) |
--admin-pass PASS |
GVM admin password (or GVM_ADMIN_PASS env var) |
--socket PATH |
Unix socket path (default: /run/gvmd/gvmd.sock) |
--timeout SECONDS |
Connection timeout (default: 60) |
--dry-run |
Simulate changes without applying them |
--enable-cleanup |
Remove orphaned permissions (garbage collection) |
--all |
Show inventory of all resources with their tags |
-v, --verbose |
Enable debug logging |
--version |
Show version and exit |
gvmsync/
_cli.py # CLI entry point, argparse, logging
_sync.py # Orchestrator: sequences phases 1-4
_resources.py # Phase 1: extract tagged resources
_groups.py # Phase 2: ensure groups exist
_permissions.py # Phase 3: grant permissions + Phase 4: cleanup
_inventory.py # --all mode: full resource listing
_xml.py # XML parsing + retry utilities
_errors.py # Custom exception hierarchy
-
Resource Extraction — Queries GVM for all scanners, tasks, and reports. Filters for resources carrying
project:*tags and groups them by project name. -
Group Verification — For each discovered project, checks whether a GVM group with that name exists. Creates it if missing.
-
Permission Configuration — For each project group, grants the required permissions on each tagged resource. Skips permissions that already exist (idempotent).
-
Garbage Collection (optional) — Iterates all permissions of each group. If the underlying resource has been deleted or its
project:*tag removed, the permission is revoked.
The PERMISSION_CONFIG dictionary in _resources.py defines which permissions
are granted per resource type. To add support for new resource types (e.g.,
targets), extend this dictionary:
PERMISSION_CONFIG = {
"scanner": ["get_scanners"],
"task": ["get_tasks", "start_task", "stop_task"],
"report": ["get_reports"],
"target": ["get_targets"], # new
}You also need to add the resource type to RESOURCE_TYPES and provide the
corresponding gmp.get_* method mapping.
pytest tests/ -vCopyright 2025-2026 Diana Tichy
Licensed under the GNU General Public License v3.0 or later. See LICENSE for the full text.