Introduction

USB ports are one of the most overlooked attack surfaces in self-hosted environments. A malicious USB device — whether a BadUSB keystroke injector, a USB Killer that physically destroys hardware, or a storage device exfiltrating data — can compromise a server in seconds. For self-hosted servers in colocation facilities, remote offices, or shared spaces, physical USB access represents a significant security risk that software firewalls cannot address.

Linux provides several layers of USB access control, ranging from broad kernel-level authorization to fine-grained per-device policies. The three primary approaches are USBGuard (a purpose-built USB device authorization framework), udev rules (kernel device manager policies), and USB/IP (network-based USB device sharing that removes physical access requirements entirely).

This guide compares these three approaches for self-hosted environments, with practical configuration examples for securing your server’s USB subsystem.

Understanding Linux USB Authorization

The Linux kernel’s USB subsystem has a built-in authorization mechanism. By default, all USB devices are authorized upon connection. However, the kernel can be configured to default-deny new USB devices, requiring explicit authorization before they can communicate with the system.

This is controlled via the authorized_default sysfs parameter:

1
2
3
4
5
6
7
# Check current default (1 = authorize all, 0 = deny all)
cat /sys/bus/usb/devices/usb1/authorized_default

# Set default-deny for all USB controllers
for i in /sys/bus/usb/devices/usb*/authorized_default; do
    echo 0 > "$i"
done

When default-deny is active, every new USB device appears in the device tree but is blocked from communicating. It must be explicitly authorized:

1
2
# Authorize a specific device
echo 1 > /sys/bus/usb/devices/1-1/authorized

This kernel mechanism is the foundation that all higher-level tools build upon.

Tool Comparison: USBGuard vs udev Rules vs USB/IP

USBGuard — Policy-Based USB Authorization

USBGuard (1,300+ GitHub stars, last updated January 2026) is the most comprehensive USB device authorization framework for Linux. It provides a daemon that enforces device-specific policies based on attributes like vendor ID, product ID, device class, and serial number.

Installation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Debian/Ubuntu
sudo apt install usbguard

# RHEL/Fedora
sudo dnf install usbguard

# Generate initial policy from currently connected devices
sudo usbguard generate-policy > /etc/usbguard/rules.conf

# Start the daemon
sudo systemctl enable --now usbguard

Policy examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# /etc/usbguard/rules.conf

# Allow a specific YubiKey by serial number
allow serial "0123456789" name "YubiKey 5C" with-interface { 03:01:01 }

# Allow all keyboards and mice (HID class)
allow with-interface { 03:01:01 }  # Keyboard
allow with-interface { 03:01:02 }  # Mouse

# Block all mass storage devices
block with-interface { 08:*:* }

# Allow a specific USB hub by vendor/product ID
allow id 05e3:0610 name "Genesys Logic USB 3.0 Hub"

# Default policy: block everything not explicitly allowed
# (This is USBGuard's default behavior — anything not matched is blocked)

Key features:

  • Device-specific policies — match on vendor ID, product ID, serial, interface class, and more
  • Runtime policy changesusbguard allow-device and usbguard block-device without restarting
  • Device presence monitoring — logs all device connect/disconnect events
  • Integration with desktop notifications — useful for servers with local administrators
  • IPC interface — can be controlled programmatically via D-Bus

udev Rules — Kernel-Level Device Management

udev is the Linux kernel’s device manager. While not specifically designed for USB security, udev rules can enforce powerful access control policies by setting device permissions, triggering scripts on device insertion, or outright ignoring unauthorized devices.

Example udev rule for USB authorization:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# /etc/udev/rules.d/99-usb-security.rules

# Default-deny all USB devices
ACTION=="add", SUBSYSTEM=="usb", ATTR{authorized}="0"

# Allow specific keyboard by vendor/product ID
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c31c", ATTR{authorized}="1"

# Allow all devices from a specific USB hub (by port path)
ACTION=="add", SUBSYSTEM=="usb", DEVPATH=="*/usb1/1-1/*", ATTR{authorized}="1"

# Block all storage devices and log the attempt
ACTION=="add", SUBSYSTEM=="block", ENV{ID_BUS}=="usb",     RUN+="/usr/bin/logger 'Blocked USB storage device: $env{ID_MODEL}'",     ATTR{authorized}="0"

Key features:

  • Built into the kernel — no additional daemon required
  • Extremely flexible — can trigger any script, set any device attribute
  • Performance — rules are evaluated at kernel level with minimal overhead
  • Persistence — rules survive reboots naturally
  • Complexity — requires understanding of udev’s rule matching syntax and device attributes

USB/IP — Network-Based USB Device Access

USB/IP is a kernel-level framework (built into Linux since 3.17) that allows USB devices to be shared over a network. Instead of plugging a device directly into a server, it can be connected to a separate machine and accessed remotely via TCP.

1
2
3
4
5
6
7
8
# Server side (machine with physical USB device):
sudo modprobe usbip-host
sudo usbipd -D                           # Start daemon
sudo usbip bind --busid=1-2              # Bind device to usbip

# Client side (server accessing remote USB device):
sudo modprobe vhci-hcd                   # Virtual USB host controller
sudo usbip attach -r 192.168.1.100 -b 1-2  # Attach remote device

Key features:

  • Physical separation — USB device does not need to be physically connected to the server
  • Kernel-level — device appears as a local USB device to all software
  • Security through isolation — the USB device connects to a hardened bastion host, not the production server
  • Useful for niche hardware — software license dongles, hardware security modules (HSMs), specialized instruments

Comparison Table

FeatureUSBGuardudev RulesUSB/IP
ApproachPolicy daemonKernel device rulesNetwork device sharing
GranularityPer-device (vendor, product, serial, class)Per-attribute (any sysfs property)Per-bus (entire USB tree branch)
Runtime changesYes (usbguard allow-device)Yes (udevadm trigger)Yes (usbip attach/detach)
Default-denyYes (built-in)Manual configurationN/A (network-only)
Dependenciesusbguard daemonNone (kernel built-in)Kernel modules (usbip-host, vhci-hcd)
LoggingStructured logs to journaldCustom via RUN+= scriptsKernel messages
ComplexityModerateHigh (rule syntax)Low for basic use, high for TLS
Physical securityDefends against local USBDefends against local USBRemoves need for physical USB
GitHub Stars1,339N/A (kernel)N/A (kernel)

Why Self-Host Your USB Access Control?

Physical USB security is often neglected in self-hosted environments because servers are assumed to be in “secure” locations. But self-hosted servers in colocation cages, branch offices, or even home offices are accessible to cleaning staff, maintenance workers, family members, or shared-office colleagues. A single moment of unattended physical access is all an attacker needs.

The BadUSB threat is real. Modern USB microcontrollers can impersonate keyboards and inject keystrokes at superhuman speed. A BadUSB device plugged into a server for 3 seconds can open a terminal, download a reverse shell, and disconnect — leaving no trace except the shell session. Default-deny USB policies prevent this entire attack class by requiring authorization before the device can communicate.

Data exfiltration via USB storage is another concern. Even on servers without a graphical desktop, a plugged-in USB drive will be detected by the kernel and can be mounted by any user with sufficient privileges. Blocking USB storage at the device authorization level (before the block layer even sees it) is far more robust than relying on mount restrictions.

For related Linux security hardening, see our AppArmor management guide and our SSH security auditing comparison. For a broader look at authorization frameworks, check our fine-grained authorization engines guide.

Deployment Strategy for Self-Hosted Servers

For self-hosted environments, a layered approach provides the best security:

Layer 1: USBGuard with Default-Deny

Start with USBGuard in default-deny mode. Generate a baseline policy from currently connected devices, then lock it down:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Install and generate baseline
sudo apt install usbguard
sudo usbguard generate-policy > /etc/usbguard/rules.conf

# Review the generated rules and remove any you don't explicitly need
sudo nano /etc/usbguard/rules.conf

# Add explicit allow rules for required devices (keyboard, YubiKey, etc.)

# Enable with default-deny
sudo systemctl enable --now usbguard

Layer 2: Kernel-Level Authorization Default

Set the kernel’s USB authorization default to deny as a second line of defense:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# /etc/systemd/system/usb-default-deny.service
sudo tee /etc/systemd/system/usb-default-deny.service << 'EOF'
[Unit]
Description=Set USB default authorization to deny
DefaultDependencies=no
Before=usbguard.service

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'for i in /sys/bus/usb/devices/usb*/authorized_default; do echo 0 > $i; done'
RemainAfterExit=yes

[Install]
WantedBy=sysinit.target
EOF

sudo systemctl enable usb-default-deny

Layer 3: udev Monitoring for Anomaly Detection

Add a udev rule that logs all USB device insertion attempts for audit trails:

1
2
3
4
5
6
sudo tee /etc/udev/rules.d/99-usb-audit.rules << 'EOF'
# Log all USB device additions with device details
ACTION=="add", SUBSYSTEM=="usb",     RUN+="/usr/bin/logger -t usb-audit 'USB device added: ID $env{ID_VENDOR_ID}:$env{ID_MODEL_ID} $env{ID_MODEL_FROM_DATABASE} serial=$env{ID_SERIAL_SHORT}'"
EOF

sudo udevadm control --reload-rules

FAQ

Can USBGuard protect against USB Killer devices?

Partially. USB Killer devices operate by charging capacitors and discharging high voltage into the data lines — they don’t wait for OS authorization. USBGuard cannot prevent the electrical attack, but it can limit the post-attack damage by preventing the device from communicating if the hardware survives. Physical USB port blockers or galvanic isolators are the only fully effective defense against USB Killer attacks.

How does USBGuard compare to just blocking USB storage in fstab?

Blocking USB storage at the mount level (via fstab or autofs) only prevents filesystem mounting — the device is still recognized by the kernel, still loads its driver, and can still interact with the system at a low level. USBGuard blocks the device at the USB authorization layer, preventing the kernel from communicating with it entirely. USBGuard is significantly more secure for this reason.

Will default-deny break my keyboard and mouse?

Yes, unless you add explicit allow rules. This is intentional. When you enable USBGuard’s default-deny policy, you must explicitly allow the devices you need. Generate the initial policy while all required devices are connected, then review and trim the rules to include only what’s necessary. For headless servers, you may not need any input devices at all.

Can I use USB/IP to access a USB device on a different machine securely?

USB/IP traffic is unencrypted by default. For secure operation, tunnel it through SSH or WireGuard:

1
2
3
# Tunnel USB/IP through SSH
ssh -L 3240:localhost:3240 remote-host
sudo usbip attach -r localhost -b 1-2

This encrypts the USB traffic and provides authentication. Never expose raw USB/IP to untrusted networks.

How do I temporarily allow a USB device without permanently changing policy?

With USBGuard, use runtime commands:

1
2
3
4
5
6
7
8
# List currently connected but unauthorized devices
sudo usbguard list-devices --blocked

# Temporarily allow a device (resets on daemon restart)
sudo usbguard allow-device <device-id>

# To make it permanent
sudo usbguard allow-device --permanent <device-id>

For udev, you can manually authorize a device via sysfs without changing rules:

1
echo 1 | sudo tee /sys/bus/usb/devices/1-2/authorized

💰 Want to test your market judgment? I use Polymarket for prediction market trading — the world’s largest prediction market platform. From election outcomes to regulatory timelines, you can bet on anything you have an information edge on. Unlike gambling, this is a true information market: the more you know, the higher your win rate. I’ve made good returns predicting technology-related events. Sign up with my referral link: Polymarket.com