Appearance
Installation
NetVuln Tool supports macOS (development) and Linux/Debian (deployment, including Raspberry Pi). All scan logic is pure Bash with no compiled binaries, and a Python orchestrator (netvuln) drives the same pipeline with typed data models. There are two supported install paths:
install.sh(this page, deployment path): sets up netvuln-tool for use on a scanning host or agent. It runs a preflight dependency check, installs thenetvulnCLI, provisions a license key, scaffolds a per-engagement config, optionally installs the systemd daemon, and verifies portal connectivity.setup.sh(developer path): sets up a development checkout (git hooks, lint dependencies, editable pip install). Use this when you intend to modify the tool, not just run it.
If you want a container instead of a host install, jump to the Docker guide. For always-on edge scanners, see Raspberry Pi deployment.
Platform Support
| macOS | Linux (Debian/Ubuntu/Raspberry Pi OS) | |
|---|---|---|
| Scanning | Yes | Yes |
| systemd daemon | No (systemd is Linux-only; install.sh prints a notice) | Yes |
| Recommended role | Development | Deployment / agents |
Requirements
install.sh installs everything in this table unprompted by default (apt on Linux, Homebrew on macOS); pass --minimal to skip that and fall back to warn-only checks with install hints instead. The table is still the reference for what each tool is for and how to install it by hand.
| Dependency | Required | macOS | Linux (Debian/Ubuntu/Raspberry Pi) |
|---|---|---|---|
| Bash 4+ | Yes | brew install bash | Pre-installed (verify: bash --version) |
| nmap | Yes | brew install nmap | sudo apt-get install -y nmap |
| jq | Recommended | brew install jq | sudo apt-get install -y jq |
| python3 3.9+ | Recommended | brew install python | Pre-installed on most distros |
| dig | Optional | Pre-installed | sudo apt-get install -y dnsutils |
| whois | Optional | Pre-installed | sudo apt-get install -y whois |
| openssl | Optional | Pre-installed | Pre-installed |
| curl | Optional | Pre-installed | sudo apt-get install -y curl |
| ssh | Optional | Pre-installed | sudo apt-get install -y openssh-client |
| nc | Optional | Pre-installed | sudo apt-get install -y netcat-openbsd |
| traceroute | Optional | Pre-installed | sudo apt-get install -y traceroute |
| smbclient | Optional | brew install samba | sudo apt-get install -y smbclient |
| snmpwalk / snmpget | Optional | brew install net-snmp | sudo apt-get install -y snmp |
| wkhtmltopdf | Optional (PDF export) | brew install wkhtmltopdf | sudo apt-get install -y wkhtmltopdf |
| msmtp | Optional (email alerts) | brew install msmtp | sudo apt-get install -y msmtp |
| iw | Optional (WiFi, Linux only) | n/a | sudo apt-get install -y iw |
| rfkill | Optional (WiFi, Linux only) | n/a | sudo apt-get install -y rfkill |
| nmcli | Optional (WiFi, Linux only) | n/a | sudo apt-get install -y network-manager |
Missing optional tools are gracefully skipped with a warning. iw, rfkill and nmcli are Linux-only and are never auto-installed on macOS.
Dependencies by purpose
| Tool | Status | Purpose |
|---|---|---|
| nmap | Required | Core scanning engine (host discovery, port/service detection, vuln scripts) |
| jq | Recommended | JSON processing, required for HTML reports |
| python3 | Recommended | The netvuln orchestrator CLI (recon/scan/upload/push/diff/status/daemon) |
| dig / host / nslookup | Optional | DNS enumeration |
| whois | Optional | WHOIS / OSINT lookups |
| openssl | Optional | SSL/TLS analysis |
| curl | Optional | HTTP header checks, CVE CVSS lookups, session upload, license validation |
| ssh | Optional | SSH auth-method enumeration |
| nc / ncat | Optional | SSH banner grabbing |
| traceroute | Optional | Hop-distance / route discovery |
| smbclient | Optional | SMB share enumeration |
| snmpwalk / snmpget | Optional | SNMP enumeration |
| wkhtmltopdf | Optional | PDF export for executive summaries |
| msmtp | Optional | Email alerts and scan reminders (one of several accepted mail backends) |
| iw | Optional (WiFi, Linux only) | WiFi interface discovery |
| rfkill | Optional (WiFi, Linux only) | Unblocking a soft-blocked WiFi radio |
| nmcli | Optional (WiFi, Linux only) | WiFi network enumeration |
Deployment install: install.sh
install.sh (repo root) prepares a host to run scans, either interactively (an analyst laptop) or as an unattended agent. It is idempotent and safe to re-run.
What it does
- Preflight: detects the platform (macOS or Linux) and installs the full scanner toolchain unprompted, skipping any binary already on
PATH. On Linux it runs one batchedapt-get update && apt-get install -y <packages>, falling back to installing each package individually (warning on any that fails) if the batch fails; on macOS it installs the Homebrew-formula subset per formula (WiFi toolsiw,rfkillandnmcliare Linux-only and are only reported on macOS). Pass--minimalto skip all of this and fall back to the old warn-only nmap/jq check with install hints instead. - Service account (Linux only): resolves who the daemon runs as and where the tree lives, either the current user (default) or a dedicated
nvtaccount, always on Raspberry Pi and opt-in elsewhere with--service-user nvt. See Where things live below for the exact paths each choice produces. - Python CLI: installs the
netvulnconsole script (skip with--no-python). On an externally managed interpreter (see PEP 668 below) it builds a virtualenv and links the script into~/.local/bin; elsewhere it runspip install -e .directly. The venv path depends on step 2 (see Where things live). - License key: installs the provided key (mode
600); the destination depends on step 2 (see Where things live). See License Provisioning for key formats and tiers. - Config scaffold: creates a per-engagement config from
templates/report_config.conf.example, filling in the API URL, client ID, and targets you passed; the destination depends on step 2 (see Where things live). The portal URL defaults tohttps://portal.netvulntool.com/api/upload(see Portal URL below). - Systemd daemon (Linux only): installs and enables the
netvuln-daemonservice, but leaves scanning off until a config setsDAEMON_ENABLED=truewithTARGETS. As of 4.9.0,install-servicealways renders the unit from explicit--user/--group/--home/--install-dir/--base-dirflags (install.shsupplies them) and refuses to render an implicitUser=rootunit; the daemon runs as the user step 2 resolved (see Where things live) and is granted CAP_NET_RAW (AmbientCapabilities=CAP_NET_RAWon the unit, plus a supplementarysudo setcap cap_net_raw+eipon the nmap binary), so nmap OS fingerprinting (-O) and SNMP/UDP scans (-sU) work without running as root. An nmap package upgrade resets thatsetcapcapability, but this no longer needs a manual fix: the daemon's own scans keep working off the unit'sAmbientCapabilitiesgrant regardless, an apt post-invoke hook reapplies the file capability automatically after everyapt/dpkgrun, and the daemon self-checks the capability at startup and every heartbeat, logging a loud warning and reportingraw_scan_capable: falseif it is ever actually lost. See Daemon Mode for the full picture. On macOS it prints a notice that systemd is Linux-only (uselaunchdor run the daemon in the foreground). Skip with--no-daemon. - Portal verification: checks
/api/healthreachability and sends an authenticated heartbeat to confirm the license key and API URL are valid. - Summary: prints a
pi_validate.sh --jsonreadiness summary and the next steps.
Where things live
Every path above depends on which account the daemon runs as (step 2), decided by --service-user, --pi/--no-pi, or the interactive prompt; see Raspberry Pi for the account itself.
| Path | Default (current user) | nvt path (Raspberry Pi by default, or --service-user nvt) |
|---|---|---|
| Install tree | the checkout you ran install.sh from | /opt/netvuln-tool (owned nvt:nvt) |
| Python venv | ~/.netvuln/venv | /opt/netvuln-tool/venv |
| License key | ~/.netvuln/license.key | /home/nvt/.netvuln/license.key |
| Configs | ~/netvuln-tool/configs/<name>.conf | /opt/netvuln-tool/configs/<name>.conf |
| Daemon runs as | the invoking user | nvt |
PEP 668 and virtualenvs
Raspberry Pi OS (Bookworm and later), Debian 12+, Ubuntu 23.04+ and Homebrew Python mark their system interpreter as externally managed, which makes a system-wide pip install a hard error rather than a warning:
error: externally-managed-environmentinstall.sh detects this and installs into a virtualenv instead, so a stock Pi image needs no manual preparation:
| Path | Purpose |
|---|---|
~/.netvuln/venv | the virtualenv holding the netvuln package |
~/.local/bin/netvuln | symlink to the console script, no sudo required |
If ~/.local/bin is not already on your PATH, the installer says so and prints the line to add:
bash
export PATH="$HOME/.local/bin:$PATH"Debian packages the venv module separately. If the virtualenv cannot be created, install it and re-run:
bash
sudo apt-get install -y python3-venvThe netvuln CLI is optional. The Bash scan pipeline is self-contained, so a Python install that fails leaves scanning fully functional and only costs you the netvuln command.
Flags
| Flag | Purpose |
|---|---|
--license-key <key> | License key to install to ~/.netvuln/license.key (e.g. nvt_pro_<hex>) |
--api-url <url> | Portal upload endpoint. Defaults to https://portal.netvulntool.com/api/upload; pass none for no portal (see Portal URL) |
--client-id <id> | Client identifier injected into the config for portal multi-tenancy |
--targets <spec> | Comma-separated IPs, CIDRs, ranges, or domains for the scaffolded config |
--config-name <name> | Basename for the scaffolded config (see Where things live for the full path) |
--service-user <name> | Run the daemon as a dedicated account (nvt) instead of the current user. Prompted on non-Pi Linux when omitted; automatic (no prompt) on Raspberry Pi |
--pi | Force Raspberry Pi behavior (auto-detected via /proc/device-tree/model otherwise) |
--no-pi | Force non-Pi behavior on a host that looks like a Pi |
--migrate | Delegate to scripts/migrate_to_opt.sh (moves a hand-patched /home/nvt/netvuln-tool install onto the supported /opt/netvuln-tool layout) and exit |
--minimal | Skip the toolchain auto-install in preflight; only check for nmap/jq and print install hints (the pre-#208 behavior) |
--no-daemon | Skip installing/enabling the systemd service |
--no-python | Skip the pip install -e . step (bash-only install) |
--verify-only | Run the portal connectivity + readiness checks only, no install steps |
--full-verify | Run the extended verification (health + authenticated heartbeat + pi_validate) |
--offline | Skip the portal connectivity check (step 7/8). Preflight's toolchain install still runs; use --minimal to skip that too |
--non-interactive, --yes | Assume defaults and never prompt (agent/automation mode); the toolchain install already runs unprompted either way |
-h, --help | Show usage |
Portal URL
--api-url defaults to https://portal.netvulntool.com/api/upload, the hosted portal. The interactive prompt offers it, so Enter accepts it:
Portal upload URL ('none' for no portal) [https://portal.netvulntool.com/api/upload]:The same default applies to --non-interactive runs that pass no --api-url, so an unattended agent is portal-connected out of the box. Three ways to change that:
| You want | Do this |
|---|---|
| A self-hosted portal | Type the URL at the prompt, or pass --api-url <your-portal-host>/api/upload |
| No portal at all | Type none at the prompt, or pass --api-url none. The config gets an empty CONSULTATION_API_URL and step 7 is skipped |
| A portal, but no network calls during install | Pass --offline. The URL is still written to the config; only the verification is skipped |
Because a URL is now always configured by default, step 7 runs on every install rather than being skipped. Uploads still require an API key as well, so a config with a URL and no key does not send anything.
Example: unattended agent install
bash
bash install.sh --non-interactive \
--license-key nvt_pro_<hex> \
--api-url https://portal.netvulntool.com/api/upload \
--client-id acme \
--targets 192.168.1.0/24This installs the CLI, writes the license key, scaffolds ~/netvuln-tool/configs/acme.conf with the URL/client/targets pre-filled, installs the systemd daemon (Linux), verifies portal connectivity, and prints a readiness summary. The daemon stays idle until you set DAEMON_ENABLED=true in that config.
Enable scheduled scanning after install
install.sh installs the daemon but does not start scanning. Edit the scaffolded config, set DAEMON_ENABLED="true" with a SCAN_SCHEDULE and TARGETS, then reload the daemon. See Daemon Mode and Scheduling.
Only scan authorized networks
Provisioning targets in a config does not grant authorization. Only scan hosts and networks you are explicitly authorized to assess.
Developer install: setup.sh
The public repository and the download addresses go live with the first public release of 4.10.0; if a link does not resolve yet, the release has not been published.
Use setup.sh when you are working on netvuln-tool itself. The source is public: clone it directly, or download a release tarball and verify it against its checksum. Clone the repository and run setup.sh once:
bash
git clone https://gitlab.com/netvulntool/netvuln-tool.git
cd netvuln-tool
bash setup.shPrefer a release tarball over a git clone? Download and verify it (replace vX.Y.Z with the release you want; /download/latest and /download/latest.sha256 on the same host always resolve to the current release):
bash
curl -Lfo netvuln-tool-vX.Y.Z.tar.gz https://netvulntool.com/download/vX.Y.Z
curl -Lfo netvuln-tool-vX.Y.Z.tar.gz.sha256 https://netvulntool.com/download/vX.Y.Z.sha256
sha256sum -c netvuln-tool-vX.Y.Z.tar.gz.sha256 # Linux
shasum -a 256 -c netvuln-tool-vX.Y.Z.tar.gz.sha256 # macOSThe /download/ addresses resolve to the assets on the GitLab release page at gitlab.com/netvulntool/netvuln-tool/-/releases, where the same files can be fetched directly if an alias is not yet in place.
The tarball extracts into a single top-level netvuln-tool-vX.Y.Z/ directory.
See the open source boundary for the full license and functional boundary.
setup.sh performs a five-step developer bootstrap:
- Configures git hooks (
git config core.hooksPath .githooks) so the pre-commit ShellCheck lint and pre-push test suite run automatically. - Checks linting dependencies (
shellcheck,bats) and prints install hints for any that are missing. - Checks runtime dependencies (
nmap,jq). - Installs the Python orchestrator in editable mode with dev extras (
pip install -e ".[dev]"), putting thenetvulnCLI on your PATH. Unlikeinstall.sh, the developer path does not build a virtualenv for you: on an externally managed interpreter it prints pip's error and the venv command to run instead (python3 -m venv .venv && .venv/bin/pip install -e '.[dev]'). - Verifies the git hooks are executable.
It does not install a license, scaffold an engagement config, or install the systemd daemon; those belong to the deployment path (install.sh).
Dependency check
At any time, verify your environment with:
bash
bash scripts/dependency_check.shThis detects your platform (macOS or Linux) and prints the correct install command for each missing tool: brew install on macOS, apt-get install on Linux. It reports and prompts; it does not batch-install the way install.sh's default preflight does. Useful for a standalone check outside of a full install.sh run, or after --minimal.
Platform validation
For deployment readiness (especially on Raspberry Pi or other edge devices), use the platform validation script:
bash
bash scripts/pi_validate.sh # Run the deployment checks
bash scripts/pi_validate.sh --json # JSON output (used by install.sh's summary)
bash scripts/pi_validate.sh --fix # Attempt to install missing dependenciesIt checks disk space, memory, required tools, permissions, and portal (/api/health) reachability, and can emit JSON for monitoring integration. Combine flags, for example pi_validate.sh --fix --json.
Next steps
- Quick Start: run your first scan in a few commands.
- Docker: run scans from a container with no host install.
- Raspberry Pi: stand up an always-on scanning appliance.
- Report Configuration: the per-engagement config file in depth.
- Daemon Troubleshooting: fixes for a daemon that fails to start, goes offline, or stops reporting to the portal.
