OpenVAS is a full-featured vulnerability scanner. Its capabilities include unauthenticated and authenticated testing, various high-level and low-level internet and industrial protocols, performance tuning for large-scale scans and a powerful internal programming language to implement any type of vulnerability test.
The scanner obtains the tests for detecting vulnerabilities from a feed [1], either from the free COMMUNITY FEED or the paid ENTERPRISE FEED, that has a long history and daily updates.
OpenVAS has been developed and driven forward by the company Greenbone [2] since 2006. As part of the commercial vulnerability management product family OpenVAS SCAN, the scanner forms the OpenVAS COMMUNITY EDITION [3] together with other open-source modules.
The following documentation describes how to deploy OpenVAS on a Debian-based system using Docker containers. It also includes scripts to automate daily feed updates and to enable IPv6 scanning support.
Table of contents:
Hardware Requirements
In order to implement OpenVAS, Greenbone defines the following hardware requirements.
- Minimal:
- CPU Cores: 2
- Random-Access Memory: 4GB
- Hard Disk: 20GB free
- Recommended:
- CPU Cores: 4
- Random-Access Memory: 8GB
- Hard Disk: 60GB free
Containers
The following table describes the official Greenbone Community Containers [4] and their services in detail.
Container |
Service |
Description |
|---|---|---|
| redis-server | Redis Server | A redis server with an adjusted config. Used to store VT data and scan results by the scanner. |
| pg-gvm | postgresql | A PostgreSQL database cluster setup for use with gvmd. The actual data is stored in the psql_data_vol volume. |
| pg-gvm-migrator | A container for migrating the database from one PostgreSQL major version to another. | |
| gvmd | gvmd | A container for gvmd that uses unix sockets in volumes to communicate with the PostgreSQL database and ospd-openvas scanner. The downloaded feed data is stored in the gvmd_data_vol volume. To verify the feed data, the GPG keyring from the gpg_data_vol is used. |
| gsad | gsad | A container running the gsad service for providing the web API. It translates between http and GMP. For communication with gvmd, a unix socket in a volume is used. |
| gsa | A container that copies the static content for the web application GSA to the gsa_data_vol volume on startup. | |
| gvm-config | A container providing the nginx config and self-signed certificates for https communication. | |
| nginx | nginx | A nginx web server providing GSA. It forwards API requests to the gsad service. |
| ospd-openvas | ospd-openvas | A container providing the vulnerability scanner. The VT data from the feed is stored in the vt_data_vol volume. To verify the feed data, the GPG keyring from the gpg_data_vol is used. The connection to the redis server is established via a unix socket in a volume. |
| gvm-tools | A container providing the gvm-tools CLI to query and control gvmd and ospd-openvas. | |
| gpg-data | A container that copies a GPG keyring with Greenbone’s public signing keys into the gpg_data_vol volume on startup. It exits afterwards. | |
| vulnerability-tests | A container that copies vulnerability tests (VTs) into the vt_data_vol volume on startup. Shows the license and exits afterwards. | |
| notus-data | A container that copies vulnerability information for notus-scanner into the notus_data_vol volume on startup. Shows the license and exits afterwards. | |
| scap-data | A container that copies CVE and CPE data into the scap_data_vol volume on startup. Shows the license and exits afterwards. | |
| cert-bund-data | A container that copies CERT-Bund data into the cert_data_vol volume on startup. Shows the license and exits afterwards. | |
| dfn-cert-data | A container that copies DFN-CERT data into the cert_data_vol volume on startup. Shows the license and exits afterwards. | |
| data-objects | A container that copies scan configs, compliance policies and port lists into the data_objects_vol volume on startup. Shows the license and exits afterwards. | |
| report-formats | A container that copies report formats into the data_objects_vol volume on startup. Shows the license and exits afterwards. | |
| configure-openvas | A container for setting up the configuration for OpenVAS Scanner. | |
| openvas | A container that shows the logs of OpenVAS Scanner. | |
| openvasd | openvasd | A container for openvasd. It is providing notus, a static vulnerability engine, functionality. |
Implementation
Notes:
Before proceeding with the documentation, the system should be updated.
1. Installing Docker and the required packages
Docker is required for running the services within containers. Docker can be installed on Debian by running the following commands:
sudo apt update
sudo apt install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin skopeo jq
For other distributions, consult the Docker Official Installation Guide [5].
2. TLS Encryption
The next step is to prepare OpenVAS to use TLS encryption. In order to do so, create a new directory that will store the Docker Compose files.
mkdir openvas
cd openvas
After accessing the directory, create the docker-compose-nginx-setup.yml file and paste the following content inside it.
volumes:
nginx_config_vol:
nginx_certificates_vol:
gsa_data_vol:
services:
nginx:
image: nginx:latest
volumes:
- nginx_config_vol:/etc/nginx/conf.d
- nginx_certificates_vol:/etc/nginx/certs
- gsa_data_vol:/usr/share/nginx/html:ro
command:
- /bin/bash
- -c
- |
openssl genrsa 4096 > /etc/nginx/certs/ca-key.pem
openssl req -new -x509 -nodes -subj "/C=PT/ST=PT/O=GREENBONE/CN=OPENVAS.GREENBONE.PT" -days 3650 -key /etc/nginx/certs/ca-key.pem -out /etc/nginx/certs/ca-cert.pem
openssl req -newkey rsa:4096 -nodes -subj "/C=PT/ST=PT/O=GREENBONE/CN=MY.OPENVAS.GREENBONE.PT" -keyout /etc/nginx/certs/privkey.pem -x509 -days 3650 -out /etc/nginx/certs/fullchain.pem -CA /etc/nginx/certs/ca-cert.pem -CAkey /etc/nginx/certs/ca-key.pem
openssl dhparam -dsaparam -out /etc/nginx/certs/dhparams.pem 4096
sed -i -z 's| ssl_certificate /etc/nginx/certs/server.cert.pem;\n ssl_certificate_key /etc/nginx/certs/server.key;| ssl_certificate /etc/nginx/certs/fullchain.pem;\n ssl_certificate_key /etc/nginx/certs/privkey.pem;\n ssl_dhparam /etc/nginx/certs/dhparams.pem;\n\n ssl_protocols TLSv1.2 TLSv1.3;\n ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;\n\n ssl_prefer_server_ciphers on;\n ssl_session_cache shared:SSL:20m;\n ssl_session_timeout 180m;\n\n error_page 497 301 =307 https://\x24host:\x24server_port\x24request_uri;|' /etc/nginx/conf.d/default.conf
gvm-config:
image: registry.community.greenbone.net/community/gvm-config:latest
environment:
ENABLE_NGINX_CONFIG: true
ENABLE_TLS_GENERATION: false
volumes:
- nginx_config_vol:/mnt/nginx/configs
- nginx_certificates_vol:/mnt/nginx/certs
gsa:
image: registry.community.greenbone.net/community/gsa:stable-slim
environment:
MOUNT_PATH: "/mnt/web"
KEEP_ALIVE: 1
volumes:
- gsa_data_vol:/mnt/web
These containers only need to be executed once in order to set up Nginx with TLS encryption.
Self-signed certificates will be generated and stored inside the nginx_certificates_vol Docker volume.
The gsa_data_vol volume will store OpenVAS static files. These files will be provided by Nginx.
The nginx_config_vol volume stores the nginx configuration.
Execute the following commands in order to run the containers.
sudo docker compose -f docker-compose-nginx-setup.yml up -d
Wait a couple of seconds, and then execute the following commands.
sudo docker compose -f docker-compose-nginx-setup.yml down
sudo docker image rm registry.community.greenbone.net/community/gsa:stable-slim registry.community.greenbone.net/community/gvm-config:latest
These commands stop the containers and remove the Docker images that are no longer required from the system.
3. Deploy OpenVAS
Inside the previously created directory, add the following contents into the docker-compose.yml file.
networks:
openvas_net:
name: openvas_net
external: true
volumes:
gpg_data_vol:
scap_data_vol:
cert_data_vol:
data_objects_vol:
gvmd_data_vol:
psql_data_vol:
vt_data_vol:
notus_data_vol:
psql_socket_vol:
gvmd_socket_vol:
ospd_openvas_socket_vol:
redis_socket_vol:
redis_data_vol:
openvas_data_vol:
openvas_log_data_vol:
gsa_data_vol:
nginx_config_vol:
nginx_certificates_vol:
services:
nginx:
image: nginx:latest
networks:
- openvas_net
ports:
- "9392:443"
volumes:
- nginx_config_vol:/etc/nginx/conf.d:ro
- nginx_certificates_vol:/etc/nginx/certs:ro
- gsa_data_vol:/usr/share/nginx/html:ro
deploy:
restart_policy:
condition: any
sysctls:
- net.ipv6.conf.all.disable_ipv6=1
depends_on:
gsad:
condition: service_started
vulnerability-tests:
image: registry.community.greenbone.net/community/vulnerability-tests
environment:
FEED_RELEASE: "24.10"
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- vt_data_vol:/mnt
notus-data:
image: registry.community.greenbone.net/community/notus-data
environment:
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- notus_data_vol:/mnt
scap-data:
image: registry.community.greenbone.net/community/scap-data
environment:
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- scap_data_vol:/mnt
cert-bund-data:
image: registry.community.greenbone.net/community/cert-bund-data
environment:
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- cert_data_vol:/mnt
dfn-cert-data:
image: registry.community.greenbone.net/community/dfn-cert-data
environment:
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- cert_data_vol:/mnt
depends_on:
cert-bund-data:
condition: service_healthy
data-objects:
image: registry.community.greenbone.net/community/data-objects
environment:
FEED_RELEASE: "24.10"
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- data_objects_vol:/mnt
report-formats:
image: registry.community.greenbone.net/community/report-formats
environment:
FEED_RELEASE: "24.10"
KEEP_ALIVE: 1
networks:
- openvas_net
volumes:
- data_objects_vol:/mnt
depends_on:
data-objects:
condition: service_healthy
gpg-data:
image: registry.community.greenbone.net/community/gpg-data
networks:
- openvas_net
volumes:
- gpg_data_vol:/mnt
redis-server:
image: registry.community.greenbone.net/community/redis-server
networks:
- openvas_net
deploy:
restart_policy:
condition: any
volumes:
- redis_socket_vol:/run/redis/
- redis_data_vol:/data/:rw
pg-gvm:
image: registry.community.greenbone.net/community/pg-gvm:stable
deploy:
restart_policy:
condition: any
networks:
- openvas_net
volumes:
- psql_data_vol:/var/lib/postgresql
- psql_socket_vol:/var/run/postgresql
depends_on:
pg-gvm-migrator:
condition: service_completed_successfully
pg-gvm-migrator:
image: registry.community.greenbone.net/community/pg-gvm-migrator:stable
deploy:
restart_policy:
condition: none
networks:
- openvas_net
volumes:
- psql_data_vol:/var/lib/postgresql
- psql_socket_vol:/var/run/postgresql
gvmd:
image: registry.community.greenbone.net/community/gvmd:stable
networks:
- openvas_net
restart: on-failure
volumes:
- gvmd_data_vol:/var/lib/gvm:rw
- scap_data_vol:/var/lib/gvm/scap-data/
- cert_data_vol:/var/lib/gvm/cert-data
- data_objects_vol:/var/lib/gvm/data-objects/gvmd
- vt_data_vol:/var/lib/openvas/plugins
- psql_data_vol:/var/lib/postgresql
- gvmd_socket_vol:/run/gvmd
- ospd_openvas_socket_vol:/run/ospd
- psql_socket_vol:/var/run/postgresql
depends_on:
pg-gvm:
condition: service_started
scap-data:
condition: service_healthy
cert-bund-data:
condition: service_healthy
dfn-cert-data:
condition: service_healthy
data-objects:
condition: service_healthy
report-formats:
condition: service_healthy
gsad:
image: registry.community.greenbone.net/community/gsad:stable
deploy:
restart_policy:
condition: any
environment:
GSAD_HTTP_ONLY: "true"
GSAD_API_ONLY: "true"
GSAD_FOREGROUND: "true"
volumes:
- gvmd_socket_vol:/run/gvmd
networks:
- openvas_net
depends_on:
gvmd:
condition: service_started
configure-openvas:
image: registry.community.greenbone.net/community/openvas-scanner:stable
volumes:
- openvas_data_vol:/mnt
- openvas_log_data_vol:/var/log/openvas
networks:
- openvas_net
command:
- /bin/sh
- -c
- |
printf "table_driven_lsc = yes\nopenvasd_server = http://openvasd:80\n" > /mnt/openvas.conf
sed "s/127/128/" /etc/openvas/openvas_log.conf | sed 's/gvm/openvas/' > /mnt/openvas_log.conf
chmod 644 /mnt/openvas.conf
chmod 644 /mnt/openvas_log.conf
touch /var/log/openvas/openvas.log
chmod 666 /var/log/openvas/openvas.log
openvas:
image: registry.community.greenbone.net/community/openvas-scanner:stable
volumes:
- openvas_data_vol:/etc/openvas
- openvas_log_data_vol:/var/log/openvas
command:
- /bin/sh
- -c
- |
cat /etc/openvas/openvas.conf
tail -f /var/log/openvas/openvas.log
networks:
- openvas_net
depends_on:
configure-openvas:
condition: service_completed_successfully
openvasd:
image: registry.community.greenbone.net/community/openvas-scanner:stable
deploy:
restart_policy:
condition: any
environment:
OPENVASD_MODE: service_notus
GNUPGHOME: /etc/openvas/gnupg
LISTENING: 0.0.0.0:80
volumes:
- openvas_data_vol:/etc/openvas
- openvas_log_data_vol:/var/log/openvas
- gpg_data_vol:/etc/openvas/gnupg
- notus_data_vol:/var/lib/notus
depends_on:
vulnerability-tests:
condition: service_healthy
notus-data:
condition: service_healthy
configure-openvas:
condition: service_completed_successfully
gpg-data:
condition: service_completed_successfully
networks:
openvas_net:
aliases:
- openvasd
ospd-openvas:
image: registry.community.greenbone.net/community/ospd-openvas:stable
deploy:
restart_policy:
condition: any
hostname: ospd-openvas.local
init: true
cap_add:
- NET_ADMIN # for capturing packages in promiscuous mode
- NET_RAW # for raw sockets e.g. used for the boreas alive detection
security_opt:
- seccomp=unconfined
- apparmor=unconfined
command:
[
"ospd-openvas",
"-f",
"--config",
"/etc/gvm/ospd-openvas.conf",
"--notus-feed-dir",
"/var/lib/notus/advisories",
"-m",
"666",
]
networks:
- openvas_net
volumes:
- gpg_data_vol:/etc/openvas/gnupg
- vt_data_vol:/var/lib/openvas/plugins
- notus_data_vol:/var/lib/notus
- ospd_openvas_socket_vol:/run/ospd
- redis_socket_vol:/run/redis/
- openvas_data_vol:/etc/openvas/
- openvas_log_data_vol:/var/log/openvas
depends_on:
redis-server:
condition: service_started
gpg-data:
condition: service_completed_successfully
configure-openvas:
condition: service_completed_successfully
vulnerability-tests:
condition: service_healthy
notus-data:
condition: service_healthy
Then, execute the following command to pull all the Docker images. Keep in mind that this can take some time.
sudo docker compose pull
Next, create the start.py script and paste the following code inside the file.
#!/usr/bin/python3
import os
import sys
import subprocess
try:
netipv6 = str(sys.argv[1])
os.system("docker network create --ipv6 --subnet=172.10.20.0/24 --subnet=2001:db8::/64 openvas_net")
result = subprocess.run(str("netstat -ie | grep -B1 \"172.10.20.1\" | head -n1 | awk '{split($1,a,\":\"); print a[1]}'"), shell=True, capture_output=True, text=True)
os.system("sysctl -w net.ipv6.conf.all.forwarding=1")
os.system("ip6tables --policy FORWARD ACCEPT")
os.system(f"ip6tables -t nat -A POSTROUTING -o {netipv6} -j MASQUERADE")
os.system(f"ip6tables -t nat -A POSTROUTING -o {result.stdout.strip()} -j MASQUERADE")
t = True
except:
os.system("docker network create --subnet=172.10.20.0/24 openvas_net")
os.system("docker compose up -d")
In order for OpenVAS to scan IPv6 targets, the containers must be assigned IPv6 addresses.
The following script creates a Docker bridge network with IPv6 enabled and configures ip6tables NAT rules to provide IPv6 connectivity for the containers.
As a result, only the host machine needs to have a valid IPv6 address.
In order to start OpenVAS, execute the script with the following commands.
sudo python3 start.py <ipv6_interface>
Make sure to replace <ipv6_interface> with the name of the network interface that contains an IPv6 address.
If the system does not have an IPv6 address, execute the following command instead.
sudo python3 start.py
OpenVAS web interface can be accessed on port 9392, with the default credentials of admin:admin.
Next, create the stop.py script and paste the following code inside the file.
#!/usr/bin/python3
import os
import sys
import subprocess
try:
netipv6 = str(sys.argv[1])
result = subprocess.run(str("netstat -ie | grep -B1 \"172.10.20.1\" | head -n1 | awk '{split($1,a,\":\"); print a[1]}'"), shell=True, capture_output=True, text=True)
os.system("ip6tables --policy FORWARD DROP")
os.system(f"ip6tables -t nat -D POSTROUTING -o {netipv6} -j MASQUERADE")
os.system(f"ip6tables -t nat -D POSTROUTING -o {result.stdout.strip()} -j MASQUERADE")
except:
pass
os.system("docker compose down")
os.system("docker network rm openvas_net")
Execute the following command in order to stop OpenVAS.
sudo python3 stop.py <ipv6_interface>
If the system does not have an IPv6 address, execute the following command instead.
sudo python3 stop.py
Automation
OpenVAS updates its feed daily during the week. This means that some of its containers are recreated almost every day. In order to automate the process of updating the feed and Docker images, the following script was created.
update.py
#!/usr/bin/env python3
import os
import sys
import subprocess
def get_digest(container):
result = subprocess.run(f"skopeo inspect --no-tags docker://{container} | jq -r '.Digest'", stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True)
skopeo_output_stderr = str(result.stderr)
skopeo_output_stdout = str(result.stdout)
result = subprocess.run(f"docker inspect {container} | jq -r '.[0].RepoDigests[0]' | cut -d'@' -f2", stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True)
docker_output = str(result.stdout)
return(skopeo_output_stderr, skopeo_output_stdout.strip(), docker_output.strip())
try:
method = str(sys.argv[1])
except:
method = str()
update = list()
if method == "daily":
containers = list(["notus-data", "vulnerability-tests", "scap-data", "dfn-cert-data", "cert-bund-data", "report-formats", "data-objects"])
for x in containers:
skopeo_output_stderr, skopeo_output_stdout, docker_output = get_digest(f"registry.community.greenbone.net/community/{x}:latest")
if "Error" in skopeo_output_stderr:
continue
elif skopeo_output_stdout != docker_output:
update.append(x)
if len(update) != 0:
command = str(f"docker compose down {' '.join(update)}")
os.system(command)
command = str(f"docker image rm registry.community.greenbone.net/community/{' registry.community.greenbone.net/community/'.join(update)}")
os.system(command)
command = str(f"docker compose pull {' '.join(update)}")
os.system(command)
os.system(f"docker compose up -d")
else:
containers = list()
result = subprocess.run(["docker", "images", "--format", "'{{.Repository}}:{{.Tag}}'"], stdout=subprocess.PIPE, text=True)
for x in str(result.stdout).split("\n"):
if "greenbone" in x:
update.append(str(x).split("'")[1])
for x in update:
skopeo_output_stderr, skopeo_output_stdout, docker_output = get_digest(x)
if "Error" in skopeo_output_stderr or skopeo_output_stdout == docker_output:
continue
else:
containers.append(x)
if len(containers) != 0:
command = str("docker compose down")
os.system(command)
command = str(f"docker image rm {' '.join(containers)}")
os.system(command)
os.system("docker image rm nginx:latest")
os.system(f"docker compose up -d")
The update.py script checks if a new version of the containers is available. If so, it deletes the old versions from the system and downloads the newest ones.
If the keyword daily is provided during invocation, the script will only update the feed. If no keyword is provided, then it updates all the containers.
This script can be combined with cron in order to create a task to automatically update OpenVAS.
Keep in mind that scans can’t be active while the feed is syncing.
In order to update the feed or the Docker images, OpenVAS does not need to be stopped.
Execute the following command in order to update the OpenVAS feed.
sudo python3 update.py daily
To update OpenVAS images, execute the following command.
sudo python3 update.py