How to Configure USBGuard on Linux: Protect Servers and Workstations from BadUSB Attacks

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Your USB Port Is an Open Door — Until You Close It

After my server got hit by SSH brute-force attacks at midnight, I started treating every attack surface seriously — not just network-facing services. SSH hardening, Fail2Ban, firewall rules… those are the obvious ones. But there’s a physical vector that most Linux admins completely ignore: the USB port.

Imagine a cleaner plugging in a USB drive to charge their phone on a machine in your server room. Or a colleague connecting a keyboard that’s actually a Rubber Ducky — an $80 HID injection device from Hak5 that looks exactly like a standard USB drive. These attacks are real, cheap to execute, and completely invisible to your firewall.

USBGuard solves this directly. Define which USB devices are allowed on your machine — by vendor, serial number, or port — and everything else gets blocked before the kernel can authorize it.

What Makes BadUSB So Dangerous

BadUSB isn’t a virus in the traditional sense. It’s an attack that reprograms a USB device’s firmware to impersonate another device type — usually a keyboard or network adapter. When you plug it in, the OS sees a legitimate HID device and grants it full keyboard access. Within seconds, it can type commands, download payloads, or exfiltrate data.

Three properties make it particularly nasty:

  • No antivirus detects it — it lives in firmware, not in a file
  • It works on any OS — Linux, macOS, Windows all trust HID devices by default
  • It’s fast — a full payload can execute in under 10 seconds

Even without malicious intent, uncontrolled USB access creates compliance headaches. HIPAA, PCI-DSS Requirement 12.3.1, and SOC 2 CC6.7 all address removable media controls on systems handling sensitive data.

Core Concepts: How USBGuard Works

USBGuard runs as a system daemon and intercepts USB device authorization events at the kernel level. When a device connects, USBGuard checks it against a ruleset. If it matches an allow rule, the device gets authorized. If it matches a block rule — or matches nothing when the default policy is block — the device stays unauthorized and the OS can’t use it.

Three things worth knowing before you touch the config:

Device Identity

USBGuard identifies devices by vendor ID, product ID, serial number, device class, and USB port. Rules can be as broad as “allow all keyboards from this vendor” or as narrow as “allow only this specific USB drive by serial number on this exact port.”

Policy Targets

Each rule ends in one of three targets: allow, block, or reject. Block keeps the device unauthorized but lets it stay connected. Reject kicks it off the bus entirely — the device disappears from lsusb.

Implicit Policy

The implicit policy is what happens when no rule matches. Setting it to block means unknown devices are silently blocked. This is the default you want on a server.

Installation

USBGuard is in the standard repos for most distributions:

# Debian / Ubuntu
sudo apt install usbguard

# RHEL / CentOS / Rocky Linux
sudo dnf install usbguard

# Arch Linux
sudo pacman -S usbguard

After installation, the daemon isn’t running yet — intentionally. You need to generate a baseline policy before starting it, or you’ll lock yourself out of your own keyboard and mouse.

Hands-On Practice: Locking Down USB Access

Step 1 — Generate a Policy from Currently Connected Devices

Before starting USBGuard, plug in everything that should be permanently allowed: keyboard, mouse, internal USB hubs, YubiKey, whatever you use day-to-day. Then generate rules for all currently connected devices:

sudo usbguard generate-policy > /etc/usbguard/rules.conf

This creates allow rules for every device currently on the bus. Open the file and inspect what it generated:

cat /etc/usbguard/rules.conf

You’ll see entries like:

allow id 046d:c52b serial "" name "USB Receiver" hash "abc123..." parent-hash "def456..." via-port "1-1.2"

Each line is a rule. The hash is a fingerprint of the device descriptor — not the firmware, unfortunately, but it does prevent casual swapping of devices with the same VID/PID.

Step 2 — Enable and Start the Daemon

sudo systemctl enable --now usbguard

Verify it’s running:

sudo systemctl status usbguard

Now try plugging in a USB drive that wasn’t in the original policy. It should show up in dmesg but not mount — the kernel sees it but USBGuard hasn’t authorized it.

Step 3 — List and Manage Connected Devices

USBGuard ships its own CLI for talking to the daemon:

# List all USB devices and their authorization state
sudo usbguard list-devices

# Allow a specific device by its ID
sudo usbguard allow-device <id>

# Block a device
sudo usbguard block-device <id>

# Reject and remove a device from the bus
sudo usbguard reject-device <id>

When you plug in a new device and want to allow it temporarily, grab its ID from list-devices and run allow-device. To make it permanent, add a rule to rules.conf.

Step 4 — Write Custom Rules

The rules file uses a simple syntax. Here are practical patterns:

# Allow a specific USB drive by serial number only
allow id 0781:5583 serial "AA01234567890" name "SanDisk Ultra"

# Allow any device from a trusted vendor (broad — use carefully)
allow id 046d:*

# Block all USB storage devices (mass storage class = 08)
block class 08:*:*

# Allow USB hubs but nothing else with class 09
allow class 09:00:00

# Reject anything presenting as a network adapter
reject class 02:*:*

Rules are evaluated top-to-bottom. First match wins. Put your most specific rules first.

After editing rules.conf, reload without restarting the daemon:

sudo usbguard reload

Step 5 — Set the Implicit Policy

Open the main config file:

sudo nano /etc/usbguard/usbguard-daemon.conf

Find and set these two lines:

ImplicitPolicyTarget=block
PresentControllerPolicy=keep

ImplicitPolicyTarget=block ensures anything not explicitly allowed gets blocked. PresentControllerPolicy=keep tells USBGuard to leave already-connected devices in their current state when it starts — this prevents disconnecting your keyboard when the daemon restarts.

Step 6 — Set Up Audit Logging

Blocking a device is half the job. The other half is knowing it happened — with a timestamp and port identifier. Enable audit logging in the config:

AuditBackend=LinuxAudit

Then restart the daemon:

sudo systemctl restart usbguard

USBGuard events now appear in the system audit log. Query them with:

sudo ausearch -ts recent -m USER_DEVICE

Running a SIEM or log shipper? These events integrate cleanly with auditd’s existing pipeline — no extra configuration needed.

Step 7 — Grant Non-Root Users Permission to Manage Devices

By default, only root can run usbguard allow-device. On a workstation where a regular user needs to plug in their own peripherals, delegate specific permissions via the IPC ACL:

sudo nano /etc/usbguard/IPCAccessControl.d/90-user.conf
# Allow the 'wheel' group to list and allow devices
[wheel]
Devices=list,allow,block

This is cleaner than giving sudo rights to the entire usbguard binary.

Testing Your Setup

Grab any USB drive that’s not in your rules.conf and plug it in. Run:

sudo usbguard list-devices | grep -i block

You should see the drive listed with a block target. It won’t mount. lsblk won’t show a new device. That’s the expected behavior.

To verify audit logging:

sudo journalctl -u usbguard -f

Plug and unplug the device — you’ll see real-time log entries for each event.

Server vs. Workstation Considerations

On a server with no physical peripherals at all, the most aggressive policy makes sense: generate a minimal ruleset (maybe just the internal USB hub), then set everything else to reject. Nobody should be plugging anything into production servers anyway — and if they try, you want to know immediately.

Workstations need a bit more give. Use block instead of reject as your implicit policy, so users can still see blocked devices in list-devices and request authorization. Combine this with the IPC ACL to let them self-serve for known-good devices without needing admin approval every time.

Conclusion

Once the initial policy is set, USBGuard runs quietly in the background — no ongoing maintenance required. Write the rules, start the daemon, and USB security becomes automatic. BadUSB, Rubber Ducky, O.MG cables — they all hit a hard wall at the driver layer before a single command runs.

The whole setup takes about 15 minutes. Hoping nobody plugs in something they shouldn’t isn’t a security strategy — it’s optimism. For anything running in a shared space, a datacenter, or a compliance-regulated environment, USB whitelisting belongs on your baseline hardening checklist right next to SSH keys and automatic updates.

Share: