Skip to main content

vlan-probe 🛡️

VLAN Isolation & Network Permission Probe Tool — verify your firewall rules by probing target VLAN subnets, IPs, and ports from the host, and get alerted to any unauthorized inter-VLAN access.

PyPI - Version PyPI - Python Versions PyPI - License CI/CD Coverage


✨ Features

  • Multi-protocol probingtcp, udp (DNS-aware on port 53), icmp (ping), and sctp.
  • Policy-first configuration — declare what should be blocked vs. reachable; every deviation is reported as a violation 🔴.
  • Flexible output — human-friendly table, structured json, or streaming ndjson.
  • Alerting built-in--strict exit codes for automation, plus scheduled MQTT reporting via systemd timers 📡.
  • Boring & reliable — typed Python 3.10+, zero long-running daemons, 100% test coverage.

📑 Table of contents


Requirements

  • Python 3.10 or newer
  • Linux (uses the ip and ping utilities)

Installation

Install with pipx (recommended)

vlan-probe is a command-line tool — pipx runs it in an isolated environment while keeping the vlan-probe command on your PATH.

pipx install vlan-probe

Upgrade to a newer release:

pipx upgrade vlan-probe

Don't have pipx yet? Install it first:

brew install pipx && pipx ensurepath      # macOS/Homebrew
apt install pipx && pipx ensurepath       # Debian/Ubuntu
pacman -S python-pipx && pipx ensurepath  # Arch

Install from source

pipx install .          # via pipx
uv tool install .       # via uv
pip install .           # into the active environment

Quick start

# 1. copy the example config and edit it to match your network
cp vlan_probe.toml.example /etc/vlan_probe.toml

# 2. probe everything and show a human-friendly table
vlan-probe -f table

# 3. or go headless: JSON to stdout, exit 1 on any violation
vlan-probe -f json -s

Configuration

Configuration lives in a single TOML file — readable, diff-able, and easy to keep in git. By default it is read from /etc/vlan_probe.toml; use -c <path> for another location.

[[targets]]
name = "Internal - Device A SSH"
vlan = "Internal"
ip = "10.10.1.10"
port = 22
protocol = "tcp"
expected_blocked = true

[[targets]]
name = "External - Public DNS"
vlan = "External"
ip = "8.8.8.8"
port = 53
protocol = "udp"
expected_blocked = false

[[targets]]
name = "Internal - Gateway Ping"
vlan = "Internal"
ip = "10.10.1.1"
port = 0
protocol = "icmp"
expected_blocked = true

[[targets]]
name = "Internal - Diameter Signalling"
vlan = "Internal"
ip = "10.10.1.20"
port = 3868
protocol = "sctp"
expected_blocked = false

Each target declares the firewall policy it expects:

expected_blocked Policy Reported when…
true deny target is reachable → violation 🔴
false allow target is unreachable → failure 🔴

Supported protocols: tcp · udp (probed with a real DNS query on port 53) · icmp (via ping) · sctp.

Environment variables

Every setting that isn't a probe target can come from an environment variable instead of a flag or the config file. CLI flags win over env vars; env vars win over the [mqtt] config section. This keeps secrets out of the config file and makes containerized/systemd deployments trivial.

CLI defaults:

Env var Controls Default
VLAN_PROBE_CONFIG config file path /etc/vlan_probe.toml
VLAN_PROBE_TIMEOUT probe timeout (seconds) 2.0
VLAN_PROBE_FORMAT output format ndjson
VLAN_PROBE_STRICT strict mode (exit 1) false

MQTT ([mqtt] section, overridable per-key):

Env var [mqtt] key
VLAN_PROBE_MQTT_HOST host
VLAN_PROBE_MQTT_PORT port
VLAN_PROBE_MQTT_USERNAME username
VLAN_PROBE_MQTT_PASSWORD password
VLAN_PROBE_MQTT_TLS tls
VLAN_PROBE_MQTT_CA_CERTS ca_certs
VLAN_PROBE_MQTT_INSECURE insecure
VLAN_PROBE_MQTT_TOPIC_PREFIX topic_prefix
VLAN_PROBE_MQTT_RETAIN retain
VLAN_PROBE_MQTT_QOS qos
VLAN_PROBE_MQTT_CONNECT_TIMEOUT connect_timeout

Booleans accept 1/true/yes/on. A VLAN_PROBE_MQTT_HOST alone is enough to enable MQTT — no [mqtt] section needed:

export VLAN_PROBE_MQTT_HOST=mqtt.example.com
export VLAN_PROBE_MQTT_USERNAME=vlan-probe
export VLAN_PROBE_MQTT_PASSWORD='s3cret'
export VLAN_PROBE_MQTT_TLS=true
vlan-probe --mqtt

Usage

-c, --config PATH         Path to config TOML file (default: /etc/vlan_probe.toml)
-f, --format FORMAT       Output format: ndjson, json, or table (default: ndjson)
-t, --timeout SECONDS     Socket connection timeout (default: 2.0)
-s, --strict              Exit with code 1 if any violations occur
--mqtt                    Publish results to MQTT (requires [mqtt] section or VLAN_PROBE_MQTT_HOST)
--color [auto|always|never]  Colorize output (default: auto)
# table output
vlan-probe -c ./vlan_probe.toml -f table

# json output
vlan-probe -f json

# strict mode with a custom timeout
vlan-probe -s -t 5.0

# no color
vlan-probe -f table --color never

Exit codes

Code Meaning
0 all checks passed
1 violations detected (with --strict)
2 configuration error or MQTT delivery failure

Output formats

Table (IPs are fictional):

VLAN         TARGET                         ENDPOINT               STATUS   DETAILS
------------------------------------------------------------------------------------------
Internal     Internal - Device A SSH        10.10.1.10:22 (tcp)    PASS     OK
Internal     Internal - DNS Server          10.10.1.1:53 (udp)     PASS     OK
IoT          IoT - Device B                 10.10.2.50:80 (tcp)    PASS     OK
DMZ          DMZ - Web Server               10.10.4.50:80 (tcp)    FAIL     EXPECTED_CONNECTIVITY_FAILED: Failed to connect to DMZ - Web Server (10.10.4.50:80)
External     External - Public DNS          8.8.8.8:53 (udp)       PASS     OK

JSON — a run summary with totals and violations:

{
  "timestamp": "2026-08-15T07:56:17.397044+00:00",
  "total_probed": 12,
  "passed": 10,
  "failed": 2,
  "violations": [
    {
      "vlan": "DMZ",
      "target": "DMZ - Web Server",
      "ip": "10.10.4.50",
      "port": 80,
      "error": "EXPECTED_CONNECTIVITY_FAILED: Failed to connect to DMZ - Web Server (10.10.4.50:80)"
    }
  ]
}

NDJSON — one object per probed target, ideal for streaming/ingestion:

{"timestamp": "2026-08-15T07:55:15.797713+00:00", "target_name": "Internal - Device A SSH", "target_vlan": "Internal", "target_ip": "10.10.1.10", "port": 22, "protocol": "tcp", "reachable": false, "expected_blocked": true, "status": "PASS", "latency_ms": 1001.0, "error": null}

Strict mode (-s) writes failures to stderr and exits with code 1:

🚨 2 unauthorized connection(s) detected!
  - EXPECTED_CONNECTIVITY_FAILED: Failed to connect to DMZ - Web Server (10.10.4.50:80)
  - EXPECTED_CONNECTIVITY_FAILED: Failed to connect to DMZ - Web Server HTTPS (10.10.4.50:443)

Scheduled MQTT reporting ⏱️

Run the probe on a schedule and publish results to an MQTT broker so dashboards and automations can track VLAN isolation state over time. A systemd timer wakes a short-lived process that probes, publishes, and exits — no daemon, no state to babysit.

Add an [mqtt] section to the config:

[mqtt]
host = "mqtt.example.com"
port = 8883
username = "vlan-probe"               # optional
password = "s3cret"                   # optional
tls = true                            # optional, default false
ca_certs = "/etc/ssl/certs/ca-certificates.crt"  # optional
insecure = false                      # optional
topic_prefix = "vlan-probe"           # optional, default "vlan-probe"
retain = true                         # optional, default true
qos = 1                               # optional, default 1
connect_timeout = 5.0                 # optional, default 5.0

…and run with --mqtt:

vlan-probe --mqtt

Topics published (all retained at QoS 1):

  • vlan-probe/<hostname>/summary — run summary (totals + violations)
  • vlan-probe/<hostname>/targets/<vlan>/<target> — one message per target

A failed MQTT delivery is fatal (exit code 2); probe violations in strict mode still exit 1. --mqtt without an [mqtt] section or VLAN_PROBE_MQTT_HOST is a config error.

systemd timer

Ship the example units and enable the timer (adjust the interval in vlan-probe.timer, default every 5 minutes):

# system-wide (root)
sudo cp deploy/systemd/vlan-probe.service deploy/systemd/vlan-probe.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now vlan-probe.timer

# user (no root)
mkdir -p ~/.config/systemd/user
cp deploy/systemd/user/vlan-probe.service deploy/systemd/user/vlan-probe.timer ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now vlan-probe.timer

Ops tips:

  • journalctl -u vlan-probe — probe + delivery logs per run
  • systemctl list-timers vlan-probe — next scheduled run
  • Broker credentials: the [mqtt] config section (chmod 600) or the VLAN_PROBE_MQTT_* environment variables in a systemd EnvironmentFile

Development 🧑‍💻

The repository ships a Makefile wrapping uv — that's the supported way to work in this project.

Command What it does
make venv create the virtualenv and install dev dependencies
make lint ruff lint and format check
make format auto-format the code with ruff
make test mypy type-check + pytest with 100% coverage gate
make run run the CLI, e.g. make run ARGS="-f table"
make build build sdist + wheel
make publish build and publish to PyPI
make install install the package into a uv tool environment
make clean remove the venv and build/cache artifacts
make venv    # one-time setup
make lint    # before pushing
make test    # before pushing

Test coverage 🛡️

Every code path is covered by a meaningful test — real loopback sockets and real config files, mocking only system boundaries. pytest fails if coverage drops below 100% (--cov-fail-under=100). Run the suite with a coverage report:

make test                      # mypy + pytest with coverage
make run ARGS="-f table"       # try it out

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vlan_probe-0.4.1.tar.gz (71.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vlan_probe-0.4.1-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file vlan_probe-0.4.1.tar.gz.

File metadata

  • Download URL: vlan_probe-0.4.1.tar.gz
  • Upload date:
  • Size: 71.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vlan_probe-0.4.1.tar.gz
Algorithm Hash digest
SHA256 f489adaa11f6adde6bbd3e31ac0ddee0ce4f8dfdfa76b44eb7b0be4fdbc968d7
MD5 070edb6d29e6d4e1732a48148c547856
BLAKE2b-256 2f338eaccc0abfb268ad138353aa03d9c377036f79e33f89d90a047fa73f40b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for vlan_probe-0.4.1.tar.gz:

Publisher: ci.yml on hellqvio86/vlan-probe

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vlan_probe-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: vlan_probe-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vlan_probe-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6e110bdab43b9fc8d4b7a632cdd1db19c2462707c3c1b68796182ad7d56ab7f8
MD5 a671a8eac3aa4f747edc2ee440310859
BLAKE2b-256 a877983475597f7cf2a84921bd093f7f25665f54920dd9789a7453ef5968fbf7

See more details on using hashes here.

Provenance

The following attestation bundles were made for vlan_probe-0.4.1-py3-none-any.whl:

Publisher: ci.yml on hellqvio86/vlan-probe

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.6.0

2 files

0.5.0

2 files

This release

0.4.1 This release

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page