Skip to content

Proxmox vSensor

Firewall feeds cover routed traffic. To see inside a VLAN you need packets, and on a hypervisor you can get them without any extra hardware: mirror the traffic of selected guests into an isolated bridge that the appliance listens on.

This is the one change in the whole product that touches a hypervisor’s networking, so it is built to be narrow and reversible.

guest tap ──(tc mirror, ingress)──► ndr veth ──► vmbrMON ──► appliance capture NIC ──► Zeek
no IP, no route, no uplink
  • An isolated monitor bridge with no address, no route and no uplink.
  • One veth pair per mirrored access VLAN, injecting into a port whose PVID is that VLAN. The appliance’s port on the bridge is a tagged trunk.
  • Learning disabled on the injection ports, so known-unicast forwarding cannot decide to skip the sensor.
  • A separate trunk injection pair for tagged uplink traffic.

Access taps are untagged at the point where traffic is captured, which is why each access VLAN gets its own PVID port rather than everything being dumped into one bridge and hoped for.

Ingress on every non-sensor guest port, plus ingress on the host uplink. That combination yields one observation per layer-2 segment for same-host traffic and also catches the outside-to-guest direction that guest-tap ingress alone misses.

Explicitly excluded, by construction:

  • Every interface belonging to the sensor guest itself.
  • The configured sensor addresses and MAC addresses, filtered at the capture hook, and again in the backend as defence in depth.
  • Any source you list as excluded — and exclusions are logged on every reconciliation, so an omission stays visible.

A tagged guest trunk is rejected during preflight unless you list it explicitly as excluded. Silently omitting a port you cannot handle correctly is worse than refusing to start.

The tooling has three commands and the first two change nothing:

Terminal window
ndr-preflight proxmox # inspect and report
install-proxmox-mirror # print the exact actions
rollback-proxmox-mirror # print the exact undo

Before applying, record the current state: interface detail, bridge VLAN membership, the queueing disciplines and filters on every source, and the guests’ current connectivity. Archive that output.

The apply path backs up the network configuration and the sensor guest’s configuration, adds only a drop-in file, brings up only the monitor bridge, attaches the sensor guest’s passive interface, and enables a reconciliation timer. It never reloads the main bridge and never reloads networking as a whole.

It also reserves exactly one capture filter preference and handle, and preflight aborts if something else already owns them. The cleanup path reads its own state file and deletes only those exact filters plus its own veth devices — it deliberately leaves every shared queueing discipline and every foreign filter in place. That is what makes it safe to run on a host that has other tooling installed.

After applying, compare against the state you recorded, then run a bidirectional packet capture check during both idle and backup load. Capture loss must stay below one percent.

Start small. Two active guests are enough for a first proof, and the global reconciliation should stay disabled until each additional source has been reviewed.

A hypervisor mirror does not see:

  • physical devices talking to each other on a switch,
  • other hypervisors,
  • guests you did not select.

Firewall NetFlow and Suricata cover routed traffic, not those blind spots.

One more known gap worth stating: whether a VLAN tag survives into the metadata depends on what the packet-level source writes. If the tag is not in the source record, the flow’s VLAN list is empty even though the capture itself proves the tag was on the wire. The VLAN comes from the source or it comes from nowhere; Nyxtrace does not infer it.