No description
  • Go 98.7%
  • Dockerfile 1.3%
Find a file
Kyle Mendell b89a7f44ef
All checks were successful
CI / Go Tests (push) Successful in 16s
CI / Go Linter (push) Successful in 20s
Release / Build And Publish Docker Image (push) Successful in 1m19s
fix: run on startup
2026-03-16 18:33:30 -05:00
.forgejo/workflows update images 2026-03-15 21:54:40 -05:00
cmd/netbox-discovery-agent fix: dockerfile issues 2026-03-16 18:28:40 -05:00
internal fix: run on startup 2026-03-16 18:33:30 -05:00
systemd Initial Commit 2026-03-15 21:21:55 -05:00
.dockerignore chore: add release 2026-03-15 21:49:58 -05:00
.gitignore fix: dockerfile issues 2026-03-16 18:28:40 -05:00
.golangci.yml add workflowS 2026-03-15 21:29:50 -05:00
config.example.yaml Initial Commit 2026-03-15 21:21:55 -05:00
docker-compose.example.yml fix: ping count and timeouts 2026-03-16 18:22:25 -05:00
Dockerfile fix: dockerfile issues 2026-03-16 18:26:01 -05:00
go.mod Initial Commit 2026-03-15 21:21:55 -05:00
go.sum Initial Commit 2026-03-15 21:21:55 -05:00
README.md fix: run on startup 2026-03-16 18:33:30 -05:00

netbox-discovery-agent

netbox-discovery-agent scans one or more IPv4 networks, checks each usable IP with ICMP ping and reverse DNS, and upserts the results into NetBox IPAM with the official go-netbox client.

Each --network value is expected to match an existing NetBox prefix exactly. The program resolves that prefix first and uses its VRF context so the created IP address objects land under the correct prefix/VRF in NetBox.

For each usable IPv4 address in each target network, the program:

  • creates or updates a NetBox IP address record as x.x.x.x/<prefix-mask> using the scanned network mask
  • sets dns_name from reverse DNS when a PTR record exists
  • sets status to active only when ping succeeds
  • removes existing normal IP address records when ping does not succeed, so the address becomes available again
  • skips reserved and DHCP IP ranges before probing, so those addresses are not scanned or updated
  • leaves existing reserved and dhcp IP address records untouched

Requirements

  • Go 1.26.1 or newer
  • a NetBox API token with permission to read and write IP addresses
  • raw ICMP socket access on the machine running the program
  • the target prefix must already exist in NetBox and be uniquely identifiable by the scanned CIDR

If raw ICMP access is not available, the program exits immediately with an error instead of silently degrading. If a scanned CIDR does not resolve to exactly one NetBox prefix, the program exits with an error instead of guessing.

Environment

Set these environment variables before running the program:

export NETBOX_URL="https://netbox.example.com"
export NETBOX_TOKEN="your-netbox-api-token"

The program also tries to load a .env file from the current working directory at startup. Missing .env files are ignored.

Example .env:

NETBOX_URL=https://netbox.example.com
NETBOX_TOKEN=your-netbox-api-token

Authentication scheme handling:

  • tokens starting with nbt_ are sent as Authorization: Bearer ...
  • all other tokens default to Authorization: Token ...
  • you can override this with NETBOX_AUTH_SCHEME=bearer or NETBOX_AUTH_SCHEME=token
  • the YAML config also supports auth_scheme: bearer or auth_scheme: token

Logging

The program uses logrus.

  • default log level is debug
  • override with --log-level or LOG_LEVEL
  • the YAML config also supports log_level
  • NetBox HTTP requests and responses are logged
  • authorization headers are redacted in logs

Example:

LOG_LEVEL=debug ./netbox-discovery-agent --network 172.18.24.0/24 --dry-run

Project layout

  • cmd/netbox-discovery-agent — CLI entrypoint
  • internal/app — runtime orchestration and scheduling
  • internal/config — config file loading and CLI/env resolution
  • internal/discovery — network discovery logic split across focused files
  • internal/netbox — NetBox client and sync logic split across focused files

Usage

Run the scanner against one or more IPv4 CIDRs:

go run ./cmd/netbox-discovery-agent --network 192.0.2.0/24

You can specify --network multiple times:

go run ./cmd/netbox-discovery-agent \
  --network 192.0.2.0/24 \
  --network 198.51.100.0/24

Use --dry-run to scan and log the intended NetBox changes without writing them:

go run ./cmd/netbox-discovery-agent --network 192.0.2.0/24 --dry-run

You can also load networks from a YAML config file:

netbox_url: https://netbox.example.com
netbox_token: nbt_your-token-here
auth_scheme: bearer
log_level: info
schedule: '0 * * * *'

networks:
  - 172.18.24.0/24
  - 172.18.25.0/24

An example file is included at config.example.yaml.

Run with:

go run ./cmd/netbox-discovery-agent --config config.yaml

If both --config and --network are used, networks from both sources are merged and duplicates are removed. If both config and environment variables provide NETBOX_URL or NETBOX_TOKEN, the environment variables win. If schedule is set in config or via --schedule, the process stays running and executes scans on that cron schedule.

Build a binary if you prefer:

go build -o netbox-discovery-agent ./cmd/netbox-discovery-agent
./netbox-discovery-agent --network 192.0.2.0/24

Flags

--network        IPv4 CIDR to scan; repeatable and required
--config         Optional YAML config file with a `networks:` list
--schedule       Optional cron schedule for recurring scans
--concurrency    Number of concurrent probes (default: 64)
--ping-attempts  Number of ICMP attempts before marking a host unreachable (default: 3)
--ping-timeout   Timeout per ICMP probe (default: 1s)
--dns-timeout    Timeout per reverse DNS lookup (default: 1s)
--dry-run        Log intended NetBox changes without writing them

Examples

Scan a /24 and write results to NetBox:

NETBOX_URL="https://netbox.example.com" \
NETBOX_TOKEN="token" \
go run ./cmd/netbox-discovery-agent --network 10.0.10.0/24

Scan two networks with lower concurrency:

NETBOX_URL="https://netbox.example.com" \
NETBOX_TOKEN="token" \
go run ./cmd/netbox-discovery-agent \
  --network 10.0.10.0/24 \
  --network 10.0.20.0/24 \
  --ping-attempts 3 \
  --concurrency 16

Run as a long-lived scheduled service every hour:

./netbox-discovery-agent --config config.yaml --schedule "0 * * * *"

Scheduling

Schedules use standard 5-field cron syntax:

minute hour day-of-month month day-of-week

Examples:

0 * * * *      # every hour
*/15 * * * *   # every 15 minutes
0 2 * * *      # every day at 02:00

The process launches one scan immediately in scheduled mode, then waits for the next matching cron time. If a scheduled run is still active when the next time arrives, the next run is skipped.

systemd

An example service unit is included at netbox-discovery-agent.service.example. Setup notes are in systemd/README.md.

Container Image

A multi-stage Alpine container build is included in Dockerfile. The final container runs as root, and the container needs NET_RAW capability so it can send ICMP probes.

Build locally:

docker build -t netbox-discovery-agent:local .

Run locally:

docker run --rm \
  --network host \
  --cap-add NET_RAW \
  -v "$PWD/config.yaml:/work/config.yaml:ro" \
  ghcr.io/kmendell/netbox-discovery-agent:latest \
  --config /work/config.yaml

An example Compose file is included at docker-compose.example.yml.

The release workflow at release.yml runs on Forgejo for tag pushes like v1.0.0 and publishes multi-arch images to ghcr.io/kmendell/netbox-discovery-agent.

Required Forgejo secrets:

  • GHCR_USERNAME
  • GHCR_TOKEN