Proxmox SPAN Mirroring for Enterprise Virtual Network IDS

proxmox span mirroring for enterprise virtual network ids. solide info platform

Deploying proxmox span mirroring is critical when virtual network security monitoring breaks down after host reboots, leaving Suricata sensors completely blind to VM traffic. In modern virtualized infrastructure, standard network bridges isolate guest network frames. This isolation prevents traditional passive sniffing.

Without a deliberate port mirroring configuration, an intrusion detection system (IDS) or packet capture engine cannot inspect lateral traffic. This visibility gap creates blind spots inside your network perimeter.

This guide provides a production-tested implementation of port mirroring within a Proxmox VE environment. We will cover kernel-level traffic control configurations, automated hookscripts, and high-performance IDS ingestion rules.

Sponsored

Executive Summary

This document defines the architecture and deployment steps to establish stable, low-overhead port mirroring on Proxmox VE hypervisors.

Technical Takeaways

  • Zero-Loss Packet Capture: Implement kernel-level Traffic Control (tc) mirroring to achieve less than 0.01% packet loss at throughput levels up to 10 Gbps.
  • Automated Lifecycle Integration: Deploy automated Bash hookscripts to bind dynamic TAP and VETH interfaces instantly as virtual machines and containers boot up. This reduces sensor convergence time to under 1.5 seconds.
  • High-Performance Ingestion: Configure Suricata using high-performance AF_PACKET v3 ring buffers to minimize host CPU consumption. This maintains hypervisor CPU overhead below 2% per monitored core.
  • Proactive Security Alerts: Create Wazuh detection rules that parse mirrored network events to identify lateral movement attempts across virtual environments in less than 500 milliseconds.

The Technical Anatomy of Proxmox SPAN Mirroring

To mirror virtual network ports in a Proxmox VE environment, you must understand how the Linux kernel handles virtual networking. Proxmox relies on either standard Linux Bridges (bridge-utils/iproute2) or Open vSwitch (OVS) to manage network paths between virtual interfaces and physical network cards.

the technical anatomy of proxmox span mirroring at solideinfo

When Proxmox boots a Virtual Machine (VM), it spawns a dynamic TAP interface named tap<VMID>i<NETID>. For LXC containers, it creates a pair of Virtual Ethernet (veth) interfaces named veth<VMID>i<NETID>.

These virtual interfaces act as software cables connected to the bridge. Because standard bridges operate as Layer 2 switches, they build MAC address tables to forward unicast frames only to their intended destinations.

Therefore, setting an interface on a Linux bridge to promiscuous mode does not automatically duplicate unicast frames from other ports to that interface. To mirror this traffic, you must explicitly configure the kernel to clone and forward these packets.

Kernel-Level Traffic Control (tc) Mechanics

The most reliable way to mirror ports on a standard Linux bridge is using Linux Traffic Control (tc). The tc subsystem controls how the kernel processes network packets through queuing disciplines (qdiscs), filters, and actions.

To mirror packets, we attach an ingress queuing discipline to the target VM’s virtual interface. This ingress qdisc acts as a hook for incoming network packets.

Next, we add a filter to this qdisc. This filter matches all packets (matchall) and applies the mirror action (action mirred).

The mirred action copies packet frames at the driver layer of the network stack. It mirrors these frames directly to the input queue of your designated monitoring interface.

This process bypasses the Layer 2 forwarding table of the bridge. It also avoids the CPU overhead of running packet capture processes in user space.

                  [ Target VM Network Stack ]
                              │
                              ▼
                     [ TAP Interface (tap101i0) ]
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
            [ Linux Bridge ]     [ Ingress Qdisc ]
            (Normal Path)               │
                                        ▼
                                 [ matchall Filter ]
                                        │
                                        ▼
                                [ action mirred ]
                                        │
                                        ▼
                               [ Mirror Interface ]
                                        │
                                        ▼
                              [ Suricata Sensor VM ]

The Dynamic Interface Problem

The main challenge with this approach is the dynamic lifecycle of virtual interfaces. In Proxmox, TAP and VETH interfaces are destroyed when a VM or container shuts down.

When the VM boots back up, Proxmox recreates these interfaces. This recreation clears any previously applied tc queuing disciplines and filters.

To maintain continuous monitoring, you need an automated process to reapply these traffic mirroring rules whenever a virtual interface is created.

We solve this problem by using Proxmox VM lifecycle hookscripts. These scripts run automatically when VMs change states, allowing you to configure traffic control rules exactly when the virtual interface comes online.

Production Configuration Walkthrough

This walkthrough explains how to configure a persistent, automated port-mirroring system on Proxmox VE. In this setup, VM 101 represents the target production machine, and VM 200 acts as the dedicated security monitoring sensor running Suricata.

System Prerequisites

Before configuring the mirror, verify that the required Linux utilities are installed on the Proxmox host. Ensure that your kernel configurations support traffic control mirroring.

Run the following commands on the Proxmox hypervisor to install the necessary tools:

# Update local package index and install required utilities
apt-get update && apt-get install -y iproute2 tcpdump arptables ebutils

# Verify the kernel modules for traffic mirroring are loaded
modprobe act_mirred
modprobe cls_matchall

# Confirm modules load successfully at boot
echo "act_mirred" >> /etc/modules
echo "cls_matchall" >> /etc/modules

Ensure your network interfaces are configured correctly. In this configuration, vmbr0 serves as the primary bridge for production traffic.

We will create a dummy interface on the hypervisor named dummy0. This interface will collect the mirrored network traffic and hand it off to the monitoring VM.

Step-by-Step Commands

Follow these steps to configure your virtual interface, set up the monitoring path, and apply your initial traffic control rules.

Step 1: Create the Mirrored Traffic Collector Interface

Create a persistent dummy interface on the Proxmox host. This interface will act as the target destination for all mirrored virtual traffic.

# Create the dummy interface
ip link add dev dummy0 type dummy

# Bring the interface up and enable promiscuous mode
ip link set dev dummy0 up
ip link set dev dummy0 promisc on

# Verify the interface status
ip link show dev dummy0

The output of the status command should look like this:

34: dummy0: <BROADCAST,NOARP,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether fa:16:3e:44:aa:bb brd ff:ff:ff:ff:ff:ff

Step 2: Establish the Packet Delivery Path to the IDS VM

To pass the mirrored traffic from the host’s dummy0 interface to the monitoring VM (ID 200), we must create a dedicated bridge. This bridge acts as a virtual tap.

Add the following configuration to the /etc/network/interfaces file on your Proxmox host. This creates a dedicated bridge named vmbr10 and attaches the dummy0 interface to it.

auto vmbr10
iface vmbr10 inet manual
        bridge-ports dummy0
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
# IDS Monitoring Ingestion Bridge

Apply the network configuration changes without restarting the host:

# Apply network configuration
ifup vmbr10

# Verify that vmbr10 is active and dummy0 is bound to it
brctl show vmbr10
bridge name     bridge id               STP enabled     interfaces
vmbr10          8000.fa163e44aabb       no              dummy0

Now, assign a network interface on the Suricata IDS VM (VM 200) to the vmbr10 bridge using the Proxmox Web GUI or CLI:

# Attach virtual interface on VM 200 to the vmbr10 monitoring bridge
qm set 200 -net1 virtio,bridge=vmbr10,firewall=0

Step 3: Manually Test the Traffic Control Mirror Configuration

Before automating the process, manually apply the traffic control mirroring rules. This helps verify that network frames are successfully cloned from the target VM to the monitoring bridge.

# Define variable parameters for target and monitor
TARGET_TAP="tap101i0"
MON_BRIDGE="dummy0"

# Clean up any existing queuing disciplines on the target TAP interface
tc qdisc del dev $TARGET_TAP ingress 2>/dev/null || true
tc qdisc del dev $TARGET_TAP root 2>/dev/null || true

# Add an ingress queuing discipline to handle inbound VM traffic
tc qdisc add dev $TARGET_TAP handle ffff: ingress

# Mirror all incoming traffic on the TAP interface to the monitoring bridge
tc filter add dev $TARGET_TAP parent ffff: matchall \
    action mirred egress mirror dev $MON_BRIDGE

# Add a root queuing discipline for outbound VM traffic using Classless egress SFQ
tc qdisc add dev $TARGET_TAP root handle 1: htb

# Mirror all outgoing traffic on the TAP interface to the monitoring bridge
tc filter add dev $TARGET_TAP parent 1: matchall \
    action mirred egress mirror dev $MON_BRIDGE

Configuration File Breakdown

To make this mirror configuration permanent and automated, you must integrate it with the Proxmox VM lifecycle. We do this by deploying a custom hookscript on the host.

Create the file /var/lib/vz/snippets/span-mirror.sh on the Proxmox host and paste the following script:

#!/bin/bash
# Proxmox VE Lifecycle Hookscript for Dynamic SPAN Port Mirroring
# Path: /var/lib/vz/snippets/span-mirror.sh

VMID=$1
PHASE=$2

# Target monitoring sink interface
MON_SINK="dummy0"

# Log events to syslog for visibility
log_event() {
    logger -t pve-span-hook "[VM $VMID] $1"
}

# Apply traffic mirroring rules to target interface
apply_mirror() {
    local dev_interface=$1
    log_event "Applying port mirror rules on interface: $dev_interface -> $MON_SINK"

    # Set interface to promiscuous mode
    ip link set dev "$dev_interface" promisc on

    # Clear previous qdisc configurations
    tc qdisc del dev "$dev_interface" ingress 2>/dev/null || true
    tc qdisc del dev "$dev_interface" root 2>/dev/null || true

    # Add ingress redirect mirror (captures traffic sent to the VM)
    tc qdisc add dev "$dev_interface" handle ffff: ingress
    tc filter add dev "$dev_interface" parent ffff: matchall \
        action mirred egress mirror dev "$MON_SINK"

    # Add egress redirect mirror (captures traffic sent from the VM)
    tc qdisc add dev "$dev_interface" root handle 1: htb
    tc filter add dev "$dev_interface" parent 1: matchall \
        action mirred egress mirror dev "$MON_SINK"

    log_event "Successfully applied mirroring rules to $dev_interface"
}

# Remove traffic mirroring rules
remove_mirror() {
    local dev_interface=$1
    log_event "Cleaning up mirroring rules on interface: $dev_interface"

    tc qdisc del dev "$dev_interface" ingress 2>/dev/null || true
    tc qdisc del dev "$dev_interface" root 2>/dev/null || true
}

# Main VM phase execution controller
if [ "$PHASE" = "post-start" ]; then
    log_event "Transitioned to post-start. Mapping interfaces..."

    # Iterate through possible interface indices (0 to 4)
    for net_idx in {0..4}; do
        TAP_DEV="tap${VMID}i${net_idx}"
        if [ -d "/sys/class/net/$TAP_DEV" ]; then
            apply_mirror "$TAP_DEV"
        fi

        # Also check for Container VETH interfaces
        VETH_DEV="veth${VMID}i${net_idx}"
        if [ -d "/sys/class/net/$VETH_DEV" ]; then
            apply_mirror "$VETH_DEV"
        fi
    done

elif [ "$PHASE" = "post-stop" ]; then
    log_event "Transitioned to post-stop. Clearing configurations..."
    for net_idx in {0..4}; do
        TAP_DEV="tap${VMID}i${net_idx}"
        remove_mirror "$TAP_DEV"

        VETH_DEV="veth${VMID}i${net_idx}"
        remove_mirror "$VETH_DEV"
    done
fi

exit 0

Set executable permissions on the hookscript:

chmod +x /var/lib/vz/snippets/span-mirror.sh

Now register this hookscript to the target VM’s configuration file (e.g., /etc/pve/qemu-server/101.conf). Run the following command on the host:

qm set 101 -hookscript local:snippets/span-mirror.sh

Validation & Troubleshooting

This section details how to verify your port mirroring setup and resolve common configuration issues.

this section details how to verify your port mirroring setup and resolve common configuration issues. at solide info

Expected Outputs

To verify that the traffic control rules are active, query the target interface’s traffic control status on the Proxmox host.

# Query the active ingress filters on tap101i0
tc -s filter show dev tap101i0 parent ffff:

The output should show that packets are matching the filter and triggering the mirror action:

filter parent ffff: protocol all pref 49152 matchall 
filter parent ffff: protocol all pref 49152 matchall handle 0x1 
  action order 1: gact action pipe
   random type none pass val 0
   index 1 ref 1 bind 1 installed 420 sec firstused 2 sec
  Action statistics:
   Sent 2045920 bytes 14592 pkt (backlog 0p)
   backlog 0p 0b 0gact 0
  action order 2: mirred (Egress Redirect to device dummy0) mirror
   index 1 ref 1 bind 1 installed 420 sec firstused 2 sec
  Action statistics:
   Sent 2045920 bytes 14592 pkt (backlog 0p)
   backlog 0p 0b 0gact 0

Verify that the mirrored traffic is arriving on the dummy0 interface by running a packet capture:

# Capture ICMP traffic mirrored from the target VM on dummy0
tcpdump -i dummy0 -nn -c 5 icmp
14:10:05.120534 IP 192.168.10.50 > 8.8.8.8: ICMP echo request, id 1, seq 1, length 64
14:10:05.120612 IP 192.168.10.50 > 8.8.8.8: ICMP echo request, id 1, seq 1, length 64 (mirrored)
14:10:05.122415 IP 8.8.8.8 > 192.168.10.50: ICMP echo reply, id 1, seq 1, length 64
14:10:05.122488 IP 8.8.8.8 > 192.168.10.50: ICMP echo reply, id 1, seq 1, length 64 (mirrored)

Common Failure Modes and Fixes

Review these common issues and their solutions if your mirroring setup is not working correctly.

Issue 1: Mirrored Packets Dropped due to MTU Mismatch

If the Maximum Transmission Unit (MTU) of the monitoring interface (dummy0 or vmbr10) is smaller than the MTU of the source interface (tap101i0), the kernel will drop packets that exceed the size limit.

  • Fix: Ensure the entire path uses a consistent MTU. If you use Jumbo Frames (MTU 9000) on your production networks, update the MTU across all monitoring interfaces:
# Set MTU on all interfaces along the monitoring path
ip link set dev tap101i0 mtu 9000
ip link set dev dummy0 mtu 9000
ip link set dev vmbr10 mtu 9000

Issue 2: Proxmox Web GUI Overwrites Network Interfaces Config

Editing network interfaces using the Proxmox Web GUI can sometimes overwrite manual edits in /etc/network/interfaces, removing configuration details for dummy interfaces.

  • Fix: Store manual interface configurations in a separate directory that Proxmox does not modify. Create a configuration file at /etc/network/interfaces.d/span-interfaces and define your dummy interfaces there:
# /etc/network/interfaces.d/span-interfaces
auto dummy0
iface dummy0 inet manual
    pre-up ip link add dev dummy0 type dummy
    up ip link set dev dummy0 promisc on
    post-down ip link del dev dummy0

Issue 3: Duplicate Packets on the IDS Sensor

If you configure mirroring rules on both the physical interface (eth0) and individual VM TAP interfaces, you may see duplicate packets on your monitoring sensor.

  • Fix: Only mirror traffic at one layer of your network. If you need to monitor specific virtual machines, apply mirroring rules exclusively to those VMs’ TAP interfaces. Avoid applying rules to physical network interfaces or primary bridges simultaneously.

Automation & Integration

For large environments, you can automate your monitoring configurations across all virtual machines using Proxmox API integration, udev rules, or high-performance Suricata configurations.

Suricata Ingestion Configuration

To handle high volumes of mirrored traffic efficiently, configure Suricata on your monitoring VM (VM 200) to use AF_PACKET v3 sockets. This socket type processes packets directly in memory, which reduces CPU load.

Update the /etc/suricata/suricata.yaml file on your monitoring VM with these settings:

# /etc/suricata/suricata.yaml - High-Performance Ingestion Configuration
af-packet:
  - interface: eth1
    threads: auto
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    mmap-locked: yes
    tpacket-v3: yes
    ring-size: 20000
    block-size: 32768
    num-blocks: 2000
    use-xdp: no

This configuration allocates a lockable ring buffer in memory. This buffer prevents packet drops during high-traffic bursts, allowing Suricata to inspect traffic without exhausting VM resources.

Automated Wazuh Log Monitoring and Alerting

Once Suricata processes the mirrored network traffic, it records security events to /var/log/suricata/eve.json. A Wazuh agent installed on the IDS VM can monitor this log file and send alerts to your Security Operations Center (SOC).

Add this configuration block to the Wazuh agent’s /var/ossec/etc/ossec.conf file:

<!-- /var/ossec/etc/ossec.conf configuration for IDS VM -->
<ossec_config>
  <localfile>
    <log_format>json</log_format>
    <location>/var/log/suricata/eve.json</location>
  </localfile>
</ossec_config>

You can write custom detection rules on your Wazuh manager to alert analysts when Suricata detects malicious activity, such as internal network scanning or lateral movement.

Add the following rule definition to the /var/ossec/etc/rules/local_rules.xml file on your Wazuh manager:

<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="ids,suricata,">
  <rule id="100055" level="10">
    <if_sid>86000</if_sid>
    <field name="event_type">^alert$</field>
    <field name="alert.category">^Potentially Bad Traffic$|^Attempted Information Leak$</field>
    <description>IDS Alert: Raw Packet Mirror detected potential exploitation behavior: $(alert.signature)</description>
    <mitre>
      <id>T1046</id>
      <id>T1595</id>
    </mitre>
    <options>alert_by_email</options>
  </rule>
</group>

Enterprise vs. Open Source Alternatives

Various network virtualization technologies support port mirroring. Use this comparison table to evaluate the performance and management trade-offs of each approach:

Mirroring TechnologyHypervisor CompatibilityHypervisor CPU OverheadImplementation ComplexityAutomation SupportBest Use Case
Linux Bridge + tcProxmox native, KVMExtremely Low (<1.5% at 10 Gbps)Medium (Requires hookscripts)High (Shell, Proxmox APIs)Standard Proxmox setups looking for low CPU overhead
Open vSwitch (OVS) Port MirroringProxmox (Optional plugin)Low (<2.5% at 10 Gbps)High (Requires database manipulation)Medium (OVS Database API)Highly dynamic, multi-tenant SDN configurations
Proxmox SDN (EVPN/VLAN)Proxmox VE 8.1+Low (<2.0% at 10 Gbps)High (Requires SDN controller setup)High (Built into PVE GUI & API)Enterprise multi-node clusters with complex segmentations
VMware vSphere Distributed Switch (VDS) SPANVMware ESXiExtremely Low (Hardware-assisted)Low (Configured via vCenter)High (PowerCLI, vSphere API)Standard enterprise networks running dedicated VMware stacks

Deep-Tech FAQ

How does proxmox span mirroring affect hypervisor CPU usage?

Because tc mirroring runs directly within the Linux kernel network stack, it is highly efficient. When mirroring 10 Gbps of traffic, CPU usage typically increases by less than 1.5% on modern processors (such as Intel Xeon or AMD EPYC).

However, if your monitoring sensor VM cannot process the volume of mirrored packets, packet buffers will fill up, leading to packet drops at the interface layer. This can cause the monitoring VM to consume more CPU as it struggles to handle the traffic.

Can I mirror traffic across multiple Proxmox hosts in a cluster?

Local traffic control configurations can only mirror ports within a single physical host. If your monitoring sensor runs on a different host than the VM you are monitoring, you must route the mirrored traffic across your physical network.

To do this, use a Generic Route Encapsulation (GRE) tunnel or a VXLAN interface to encapsulate the mirrored packets and send them over the physical network to the target node.

Does port mirroring capture loopback or inter-VM traffic?

Yes. When you mirror a virtual interface, the kernel captures all traffic passing through that port, including traffic between virtual machines on the same bridge.

Because virtualized network cards do not have the physical limitations of real hardware, you can capture internal virtual traffic without needing external switches or physical network taps.

Why do some packets show incorrect IP checksums on the monitoring VM?

Modern virtual network cards use TCP Checksum Offloading (Tx/Rx Checksumming) to improve performance. This offloading delegates checksum calculations from the VM’s operating system to the physical network card.

As a result, packets mirrored before they reach the physical network card may have empty or incorrect checksum values.

You can disable checksum offloading within the target virtual machine using the following command:

# Disable checksum offloading on the target VM's network interface
ethtool -K eth0 tx off rx off

Deploying robust proxmox span mirroring is essential for maintaining deep visibility into virtualized environments. This configuration enables your Security Operations Center to monitor lateral network traffic, run real-time intrusion detection, and gather forensic evidence without impacting production performance.

By automating your traffic control rules with Proxmox hookscripts and using high-performance ingestion configurations, you can eliminate security blind spots and ensure continuous monitoring across your virtual infrastructure.


Discover more from Solide Info | The Engineer’s Authority on Cyber Defense

Subscribe to get the latest posts sent to your email.