OpenVAS é um scanner de vulnerabilidades completo. As suas funcionalidades incluem testes autenticados e não autenticados, diversos protocolos de internet e industriais de alto e baixo nível, otimização de desempenho para scans em larga escala e uma poderosa linguagem de programação interna para implementar qualquer tipo de teste de vulnerabilidades.
O scanner obtém os testes para deteção de vulnerabilidades de um feed [1], seja do COMMUNITY FEED (gratuito) ou do ENTERPRISE FEED (pago), que tem um longo histórico e atualizações diárias.
OpenVAS é desenvolvido e mantido pela empresa Greenbone [2] desde 2006. Como parte da família de produtos comerciais de gestão de vulnerabilidades OpenVAS SCAN, o scanner forma a OpenVAS EDIÇÃO COMUNITÁRIA [3] com outros módulos de código aberto.
A documentação seguinte descreve como implementar o OpenVAS num sistema baseado em Debian utilizando Docker containers. Inclui também scripts para automatizar as atualizações diárias do feed e para ativar o suporte à scan IPv6.
Índice:
Requisitos de Hardware
Para implementar o OpenVAS, a Greenbone define os seguintes requisitos de hardware.
- Mínimo:
- CPU Cores: 2
- Random-Access Memory: 4GB
- Hard Disk: 20GB free
- Recomendado:
- CPU Cores: 4
- Random-Access Memory: 8GB
- Hard Disk: 60GB free
Containers
A tabela seguinte descreve detalhadamente os Containers Comunitários oficiais da Greenbone [4] e os seus serviços.
Container |
Serviço |
Descrição |
|---|---|---|
| redis-server | Redis Server | Um servidor Redis com configuração ajustada. Utilizado para armazenar dados VT e resultados do scanner. |
| pg-gvm | postgresql | Um cluster de base de dados PostgreSQL configurado para utilização com o gvmd. Os dados reais são armazenados no volume psql_data_vol. |
| pg-gvm-migrator | Um container para migrar a base de dados de uma versão principal do PostgreSQL para outra. | |
| gvmd | gvmd | Um container para o gvmd que utiliza sockets Unix em volumes para comunicar com a base de dados PostgreSQL e o scanner ospd-openvas. Os dados do feed descarregados são armazenados no volume gvmd_data_vol. Para verificar os dados do feed, é utilizado o keyring GPG do gpg_data_vol. |
| gsad | gsad | Um container que executa o serviço gsad para fornecer a web API. Traduz entre http e GMP. Para a comunicação com o gvmd, é utilizado um socket Unix num volume. |
| gsa | Um container que cópia o conteúdo estático da aplicação web GSA para o volume gsa_data_vol no arranque. | |
| gvm-config | Um container que fornece a configuração do nginx e certificados auto-assinados para comunicação https. | |
| nginx | nginx | Um servidor web nginx que fornece GSA. Encaminha pedidos de API para o serviço gsad. |
| ospd-openvas | ospd-openvas | Um container que fornece o scanner de vulnerabilidades. Os dados VT do feed são armazenados no volume vt_data_vol. Para verificar os dados do feed, é utilizado o keyring GPG do volume gpg_data_vol. A ligação ao servidor Redis é estabelecida através de um socket Unix num volume. |
| gvm-tools | Um container que fornece à CLI gvm-tools para consultar e controlar o gvmd e o ospd-openvas. | |
| gpg-data | Um container que cópia um keyring GPG com as chaves públicas de assinatura do Greenbone para o volume gpg_data_vol no arranque. É encerrado em seguida. | |
| vulnerability-tests | Um container que cópia os testes de vulnerabilidade (VTs) para o volume vt_data_vol no arranque. Apresenta a licença e termina de seguida. | |
| notus-data | Um container que cópia a informação de vulnerabilidade para o notus-scanner no volume notus_data_vol no arranque. Apresenta a licença e termina de seguida. | |
| scap-data | Um container que cópia os dados CVE e CPE para o volume scap_data_vol no arranque. Apresenta a licença e termina de seguida. | |
| cert-bund-data | Um container que cópia os dados do CERT-Bund para o volume cert_data_vol no arranque. Apresenta a licença e termina de seguida. | |
| dfn-cert-data | Um container que cópia os dados do DFN-CERT para o volume cert_data_vol no arranque. Apresenta a licença e termina de seguida. | |
| data-objects | Um container que cópia as definições de scan, as políticas de conformidade e as listas de portas para o volume data_objects_vol no arranque. Apresenta a licença e termina de seguida. | |
| report-formats | Um container que cópia os formatos de relatório para o volume data_objects_vol no arranque. Apresenta a licença e termina de seguida. | |
| configure-openvas | Um container para configurar o OpenVAS Scanner. | |
| openvas | Um container que apresenta os registos do OpenVAS Scanner. | |
| openvasd | openvasd | Um container para o OpenVASD. Fornece a funcionalidade do Notus, um mecanismo estático de vulnerabilidades. |
Implementação
Notas:
Antes de avançar com a documentação, o sistema deve ser atualizado.
1. Instalação do Docker e dos componentes necessários.
O Docker é necessário para executar os serviços dentro dos containers. Pode ser instalado no Debian através dos seguintes comandos:
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
Para outras distribuições, consulte o Guia Oficial de Instalação do Docker [5].
2. Encriptação TLS
O próximo passo é preparar o OpenVAS para utilizar a encriptação TLS. Para tal, crie a seguinte diretoria que irá armazenar os ficheiros do Docker Compose.
mkdir openvas
cd openvas
Após aceder a diretoria, crie o ficheiro docker-compose-nginx-setup.yml e adicione o seguinte conteúdo ao ficheiro.
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
Estes containers só precisam de ser executados uma vez para configurar o Nginx com encriptação TLS.
Os certificados auto-assinados serão gerados e armazenados no volume Docker nginx_certificates_vol.
O volume gsa_data_vol irá armazenar os ficheiros estáticos do OpenVAS. Estes ficheiros serão fornecidos pelo Nginx.
O volume nginx_config_vol armazena a configuração do Nginx.
Execute os seguintes comandos para iniciar os containers.
sudo docker compose -f docker-compose-nginx-setup.yml up -d
Aguarde alguns segundos e execute os seguintes comandos.
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
Estes comandos interrompem os containers e removem as imagens Docker que já não são necessárias no sistema.
3. Deploy do OpenVAS
Dentro da diretoria criada anteriormente, adicione o seguinte conteúdo ao ficheiro docker-compose.yml.
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
deploy:
restart_policy:
condition: any
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
Em seguida, execute o seguinte comando para transferir todas as imagens do Docker. Tenha em conta que este passo pode levar algum tempo.
sudo docker compose pull
De seguida, crie o script start.py e adicione o seguinte código dentro do ficheiro.
#!/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")
Para que o OpenVAS possa analisar alvos IPv6, os containers devem ter endereços IPv6 atribuídos.
O script seguinte cria uma rede bridge Docker com IPv6 ativo e configura as regras de NAT do ip6tables para fornecer conectividade IPv6 aos containers.
Como resultado, apenas a máquina host necessita de ter um endereço IPv6 válido.
Para iniciar o OpenVAS, execute o script com os seguintes comandos.
sudo python3 start.py <ipv6_interface>
Certifique-se de substituir <ipv6_interface> pelo nome da interface de rede que contém um endereço IPv6.
Se o sistema não tiver um endereço IPv6, execute o seguinte comando.
sudo python3 start.py
A interface web do OpenVAS pode ser acedida no porto 9392, com as credenciais padrão admin:admin.
De seguida, crie o script stop.py e adicione o seguinte código dentro do ficheiro.
#!/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 o seguinte comando para parar o OpenVAS.
sudo python3 stop.py <ipv6_interface>
Caso o sistema não possua um endereço IPv6, execute o seguinte comando.
sudo python3 stop.py
Automação
O OpenVAS atualiza o seu feed diariamente durante a semana. Isto significa que alguns dos seus containers são recriados quase todos os dias. Para automatizar o processo de atualização do feed e das imagens Docker, foi criado o seguinte script.
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")
O script update.py verifica se está disponível uma nova versão dos containers. Nesse caso, elimina as versões antigas do sistema e transfere as mais recentes.
Se a palavra-chave daily for fornecida durante a invocação, o script apenas atualizará o feed. Caso contrário, atualizará todos os containers.
Este script pode ser combinado com o cron para criar uma tarefa que atualize o OpenVAS automaticamente.
Lembre-se de que não podem ser realizados scans enquanto o feed estiver a ser sincronizado.
Para atualizar o feed ou as imagens do Docker, não é necessário parar a execução do OpenVAS.
Execute o seguinte comando para atualizar o feed do OpenVAS.
sudo python3 update.py daily
Para atualizar as imagens do OpenVAS, execute o seguinte comando.
sudo python3 update.py