USBGuard: USB Device Authorization on Production Linux Hosts
Problem
Most production Linux hosts default-trust every USB device. The kernel sees a new device, asks the bus to enumerate it, then loads any driver that matches the device’s claimed VID/PID. A malicious USB device — BadUSB-class HID injectors, mass-storage devices that auto-mount, ethernet adapters that spoof DHCP — can attack a host within seconds of being plugged in.
Server fleets aren’t immune just because they’re in datacenters. Threat scenarios:
- Insider plugging in a USB stick to exfiltrate data or deliver malware.
- Janitor/cleaner with physical access plugging in a “lost” drive.
- Vendor technicians performing maintenance with potentially-compromised tools.
- Malicious hardware in supply chain — replacement components with USB connections.
- HID-injection attacks (Rubber Ducky, Bash Bunny) that type commands faster than humans can react.
The kernel-level defense is USBGuard: a userspace daemon that intercepts USB device authorization requests and applies an allowlist policy before the kernel binds the device. Authorized devices work normally; unauthorized devices stay unauthorized — drivers don’t load, mount events don’t fire, HID inputs don’t reach the console.
By 2026 USBGuard ships in most distributions (Ubuntu, RHEL, Debian, Fedora, Arch). Production adoption is uneven; the typical reaction is “we have physical security, we don’t need it.” Physical security defends against opportunistic attackers; USBGuard defends against sophisticated and trusted-but-compromised actors.
The specific gaps in default Linux:
- New USB devices auto-authorize and bind to drivers.
- USB mass-storage devices auto-mount via udisks2 or systemd.
- HID devices (keyboards, mice) immediately accept input.
- Network interfaces brought up by USB ethernet/WiFi adapters appear automatically.
- Audit log of USB events exists but is not gated.
This article covers USBGuard installation, policy design (deny-by-default with allowlisted device IDs / serial numbers), integration with udev and systemd, audit logging, and the operational patterns for laptops vs. servers vs. specialized devices (KVM-attached USB-passthrough, USB-printer servers).
Target systems: USBGuard 1.1+, Linux kernel with CONFIG_USB, systemd-based distros. Compatible with Ubuntu 22.04+, RHEL 9+, Debian 12+, Fedora 38+.
Threat Model
- Adversary 1 — Physical access attacker (drop attack): plugs in a USB device while no one is watching. Wants the device to execute or exfiltrate without triggering alerts.
- Adversary 2 — Insider with maintenance access: has legitimate physical access; uses USB to exfiltrate data or deploy malware.
- Adversary 3 — Compromised cable: USB-C cable with embedded electronics (O.MG cable) types commands or hijacks data.
- Adversary 4 — Replacement-component attacker: a piece of hardware that comes with USB connections (KVM extender, replacement keyboard) is malicious.
- Adversary 5 — HID injection: plug-in device pretending to be a keyboard, types commands faster than humans react.
- Access level: all adversaries have physical-access capability for a brief window. None have prior credentials.
- Objective: Execute commands on the host; mount filesystems for read/write; capture HID input; extract host data.
- Blast radius: Without USBGuard, plugging in a malicious device equals immediate code execution at root or user level. With USBGuard, device cannot authorize without explicit allowlist match; alerts fire on attempts.
Configuration
Step 1: Install and Initial Setup
# Debian / Ubuntu.
sudo apt install usbguard
# RHEL / Rocky.
sudo dnf install usbguard
# Generate initial policy from currently-attached devices (the "trusted" baseline).
sudo usbguard generate-policy > /etc/usbguard/rules.conf
sudo chmod 600 /etc/usbguard/rules.conf
# Start the daemon.
sudo systemctl enable --now usbguard
The generated policy contains explicit allow id ... rules for every currently-attached device (keyboards, root hubs, internal devices). Inspect:
sudo cat /etc/usbguard/rules.conf
# allow id 1d6b:0002 serial "0000:00:14.0" name "xHCI Host Controller" ...
# allow id 04f2:b7c8 serial "" name "USB2.0 HD UVC WebCam" ...
# allow id 8087:0024 serial "" name "Integrated Hub" ...
Each rule pins on USB vendor ID + product ID, optionally serial number, optionally device name. Internal devices (root hubs, integrated peripherals) are typically pinned by serial.
Step 2: Set Default Policy
The RuleSet config decides what happens to unmatched devices. The hard mode for production:
# /etc/usbguard/usbguard-daemon.conf
RuleFolder=/etc/usbguard/rules.d/
RuleFile=/etc/usbguard/rules.conf
ImplicitPolicyTarget=block
PresentDevicePolicy=apply-policy
PresentControllerPolicy=keep
InsertedDevicePolicy=apply-policy
RestoreControllerDeviceState=false
DeviceManagerBackend=uevent
IPCAllowedUsers=root
IPCAllowedGroups=plugdev
IPCAccessControlFiles=/etc/usbguard/IPCAccessControl.d/
DeviceRulesWithPort=false
AuthorizedDefault=none
ImplicitPolicyTarget=block defaults to denying everything not explicitly allowed. AuthorizedDefault=none ensures even the kernel default authorization is overridden.
For a more lenient initial deployment (allow-by-default with logging while you build the allowlist):
ImplicitPolicyTarget=allow # less safe; use only during baseline-building
Run in allow mode for 1-2 weeks while building the explicit allowlist; switch to block.
Step 3: Server-Specific Policy
For servers in datacenters, the typical legitimate USB devices are:
- iLO / BMC USB ports (out-of-band management).
- KVM-over-USB switches.
- The internal disk’s USB-attached management interface (rare).
# /etc/usbguard/rules.conf for a typical server.
# Allow internal root hubs (always).
allow id 1d6b:0002 with-interface 09:*:* # USB 2.0 hub
allow id 1d6b:0003 with-interface 09:*:* # USB 3.0 hub
# Allow specific iLO management.
allow id 03f0:7029 name "HP iLO Virtual USB" with-interface { 03:00:00 08:06:50 }
# Block everything else.
block id *:* with-interface { 03:01:01 } # explicitly block HID keyboards
block id *:* with-interface { 03:01:02 } # block HID mice
block id *:* with-interface { 08:*:* } # block mass storage
block id *:* with-interface { 02:02:00 } # block USB-CDC modems
Servers should never need a USB keyboard or mouse plugged in during normal operation. Block at this layer; allow only via explicit operator action (covered below).
Step 4: Operator-Workstation Policy
Workstations / laptops have legitimate USB use. The pattern: allow specific known devices by serial, ask for explicit authorization on new devices.
# Trusted: my company's allowlisted YubiKey, by serial.
allow id 1050:0407 serial "1234567" name "YubiKey 5 NFC"
# Block everything else; manual authorize via usbguard CLI.
Operator inserts a new device:
sudo usbguard list-devices
# 19: block id 1050:0407 serial "8901234" name "YubiKey 5 NFC" ...
# Manually authorize for this session only.
sudo usbguard allow-device 19
# Or persistently.
sudo usbguard allow-device --permanent 19
For more frictionless UX, USBGuard ships a Qt UI applet that pops up on insert.
Step 5: Per-Interface Allowlisting (HID Mitigation)
The strongest BadUSB mitigation: allow-list at the USB interface level rather than device level. A device claims to be a flash drive (interface class 08 mass storage) but secretly also has interface class 03 (HID keyboard). With device-level allow:
allow id 0781:5530 name "Cruzer Glide"
The whole device is authorized — including the secret keyboard interface.
With interface-level allow:
allow id 0781:5530 with-interface 08:06:50 # only the mass-storage interface
The device’s secret keyboard interface remains blocked. The mass-storage interface works. This is the structural defense against BadUSB-class attacks.
For the kernel to enforce per-interface authorization, ensure:
echo '0' | sudo tee /sys/bus/usb/drivers/usb/authorized_default
(USBGuard handles this automatically when configured correctly.)
Step 6: Auditing and Alerts
USBGuard logs every event:
sudo journalctl -u usbguard -f
# usbguard[1234]: USB device blocked: id=0781:5530 serial=12345 hash=...
Forward to your SIEM. Detection rules:
- New device blocked from a specific source — alert (possible drop-in attack).
- New device allowed by serial without explicit authorization — alert (rule too broad).
- Manual
usbguard allow-deviceinvoked — log; review correlation with operational tickets.
For per-host alerting:
# Falco rule (or your-detection-engine equivalent).
- rule: USB device blocked on production server
desc: USBGuard blocked an unexpected USB device
condition: process_name=usbguard and event.payload contains "blocked"
output: "USBGuard blocked device %device_id on %hostname (severity: medium)"
priority: WARNING
tags: [usb, physical-attack]
Step 7: Centralized Policy Distribution
For fleets, distribute USBGuard policy via configuration management (Ansible, Salt, Puppet):
# Ansible role excerpt.
- name: Install USBGuard
apt:
name: usbguard
state: present
- name: Deploy USBGuard policy
template:
src: usbguard-rules.conf.j2
dest: /etc/usbguard/rules.conf
owner: root
mode: '0600'
notify: restart usbguard
- name: Set USBGuard daemon config
template:
src: usbguard-daemon.conf.j2
dest: /etc/usbguard/usbguard-daemon.conf
notify: restart usbguard
Different host classes (servers, workstations, build machines) get different policies via Ansible host vars or per-host inventory groups.
Step 8: KVM / Out-of-Band Considerations
USB-passthrough KVM systems present USB devices that the host treats as locally-attached. Some servers expose iLO/BMC’s “virtual media” as USB. The policy must allowlist these by their specific VID/PID:
# HP iLO virtual media.
allow id 03f0:7029 name "HP iLO Virtual USB"
# Dell iDRAC virtual media.
allow id 0413:6122 name "Dell iDRAC Virtual USB"
# KVM-over-IP devices' emulated USB.
allow id 18ba:0018 name "Aten KVM USB Composite"
Test before deploying widely; out-of-band tools may use multiple VID/PIDs across firmware versions.
Expected Behaviour
| Signal | Without USBGuard | With USBGuard |
|---|---|---|
| Plug in USB stick | Auto-mount; auto-authorize | Blocked; log entry; alert |
| BadUSB HID-injection device | Types commands at console | Keyboard interface blocked; mass-storage allowed if it claims to be one |
| O.MG cable | Captures keystrokes / types commands | Cable’s HID interface blocked |
| Vendor-spoofed USB ethernet | Auto-loads; brings up interface | Blocked unless explicitly allowed |
| Plug in operator’s authorized YubiKey | Works | Works (allowlisted by serial) |
| Audit trail | None / kernel log only | Structured per-event log with disposition |
Verify the protection holds:
# Plug in an unknown USB device.
sudo journalctl -u usbguard -n 5
# usbguard[1234]: USB device blocked: id=09da:0018 serial="..." name="..."
# Confirm the device is not authorized.
ls /dev/disk/by-id/usb-* 2>/dev/null
# (no output if mass storage was blocked)
Trade-offs
| Aspect | Benefit | Cost | Mitigation |
|---|---|---|---|
| Block-by-default policy | Strongest default | Operators frustrated by friction | Per-host-class policies; lab / workstation more permissive than production. |
| Per-interface allowlisting | Defeats BadUSB | More complex policy syntax | Use the with-interface clause for sensitive devices. |
| Serial-number allowlist | Specific to known good devices | Replacing devices requires policy update | Standardize on specific device models; enrolled per ID. |
| Permanent vs. session authorization | Session-only is safer | UX friction for operators | UI applet for workstation use; CLI for servers. |
| Centralized policy distribution | Fleet uniformity | Configuration management overhead | Standard pattern; reuse existing CM. |
| Audit pipeline integration | Full visibility | Log volume | Modest; one event per insertion. |
Failure Modes
| Failure | Symptom | Detection | Recovery |
|---|---|---|---|
| Policy too restrictive | Legitimate device blocked | Operator reports; usbguard log shows block | Add device to policy; for server fleet, distribute via CM. |
| iLO / BMC virtual media blocked | Out-of-band rescue fails | Cannot mount ISO via virtual media | Allow specific iLO/BMC virtual-USB IDs explicitly; test before fleet rollout. |
Operator bypasses with allow-device for non-emergency |
Drift toward looser policy | Audit log shows manual authorizations | Quarterly review of allow-device usage; tighten policy to legitimate cases. |
| Internal device misidentified, blocked | Server hangs at boot waiting for blocked device | Console logs / iLO console | Boot to single-user; disable usbguard (systemctl mask usbguard); fix policy; re-enable. |
| HID-injection in time window before usbguard active | Brief window during boot when USB is auto-authorized | Hard to detect once damage done | Use usb-storage and hid-generic driver blacklisting at initramfs; only allow via usbguard after start. |
| Daemon crash | New devices unauthorized; existing keep state | systemd / log monitoring | systemd auto-restarts; verify with monitoring. |
| Policy rules out-of-date as devices change | Blocks legitimate replacement device | Operator reports | Tag policy with last-updated date; review on hardware refresh. |