Skip to content

Latest commit

 

History

44 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

USBGuardGUI

A desktop interface for USBGuard: see which USB devices are connected, allow or block them, and edit the policy — without root.

Sponsor Linux Rust 1.85+ GTK 4 + libadwaita CI Status Documentation License

Features • Requirements • Installation • Usage • How It Works • Verification • Contribute


Plug in a device, see it appear as blocked, allow it for this session with one click — and when something does not work, be told exactly which layer refused and what to run to fix it.

The device list: every connected device, its state, and Allow, Block, and Reject buttons

Note

USBGuardGUI is a front end. The protection comes from the USBGuard daemon, which decides what the kernel lets through; this program only asks it, with your authorization, to change its mind. If USBGuard is not installed and running, there is nothing for this program to protect you with.

Why USBGuardGUI?

USBGuard's own interface is the usbguard command, which is complete but leaves the hard part to you: a command that fails may have been stopped by the D-Bus bus policy, by Polkit, or by the daemon's own access control, the error text rarely says which, and the fix is different in each case.

No privileges of its own No setuid bit, no helper daemon, no privileged code path. Every change is made by the USBGuard daemon on its own authority, after Polkit has asked you for a password.
Failures are diagnosed, not reported Eight distinct failure states — bridge not installed, bridge stopped, refused by the bus policy, by Polkit, by the daemon's access control, no Polkit agent, and more — each with its specific remedy and the exact command for your distribution.
Safe by default Every device action asks: this session only, or permanently? The preselection is the one whose effect disappears when USBGuard restarts. Reject asks for confirmation.
Rules removed by identity, not by number USBGuard rule numbers shift whenever the ruleset changes. A rule is identified by its text and re-resolved just before removal, so a rule deleted from another terminal is reported as gone instead of a different rule being removed.
Hostile device names are just text Device names and serial numbers come from the device itself. The rule parser treats them as untrusted bytes, is fuzzed, and escapes them on the way back, so a crafted name cannot become rule syntax.

Features

  • Live device list — authorization state with icon and text, name, vendor:product, serial, and port, sorted by physical topology. Insertion bursts are merged, so a hub full of devices does not freeze the window.
  • Device actions — allow, block, reject; this session only or permanently; a Cancel button while a password prompt is open, which never times out on its own.
  • Policy view — the ruleset in the order the daemon evaluates it, a rule editor with a guided mode and a text mode, live preview and validation before anything is sent, and explicit placement ("at the end" or "after rule N").
  • Runtime parameters — ImplicitPolicyTarget and InsertedDevicePolicy, changed with confirmation.
  • Notifications — when a device that is not authorized is plugged in, with an Allow for this session button where the notification server supports it.
  • Background mode — keeps announcing new devices after the window is closed, with a status icon where the desktop has one, and an optional start at login.
  • usbguard-gui --diagnose — the whole access check as one command whose output you can paste into a bug report.

Requirements

  • Linux with a graphical session (Wayland or X11) and a Polkit authentication agent — every desktop environment ships one.
  • USBGuard 1.1.0 or later, running, with its D-Bus bridge:
    • Debian, Ubuntu, Arch: included in the usbguard package;
    • Fedora, RHEL: the separate usbguard-dbus package.
  • GTK 4.14+ and libadwaita 1.5+ (Ubuntu 24.04, Debian 13, Fedora 40, and current Arch, or later).
  • No root. Run it as your normal user.

Installation

1. Native packages (recommended)

Download the package for your distribution from the latest release and install it:

  • Debian / Ubuntu: sudo apt install ./usbguard-gui_0.1.1-1_amd64.deb
  • Fedora / RHEL: sudo dnf install ./usbguard-gui-0.1.1-1.x86_64.rpm
  • Arch Linux: sudo pacman -U ./usbguard-gui-0.1.1-1-x86_64.pkg.tar.zst, or from a clone, cd packaging/arch && makepkg -si
  • Any distribution, as a Flatpak: flatpak install --user ./usbguard-gui-0.1.1.flatpak. USBGuard itself must still come from your distribution: the Flatpak only talks to it over D-Bus.
  • With Cargo: cargo install usbguard-gui (needs the GTK development packages listed under From source).

The packages pull in USBGuard and its bridge, and change no system configuration: no post-install script runs, and the example Polkit rule is installed as documentation, never activated.

On a fresh USBGuard installation, make sure the services are running. Debian and Ubuntu enable them on install; elsewhere, enable them yourself — but first give the daemon a policy:

Caution

USBGuard started with an empty policy blocks every USB device, including your keyboard and mouse. Generate a policy that allows what is connected right now before starting it:

sudo sh -c 'umask 077; usbguard generate-policy > /etc/usbguard/rules.conf'
sudo systemctl enable --now usbguard.service usbguard-dbus.service

For verifying release assets, see the Release Verification Guide.

2. From source

# Build dependencies
sudo dnf install gtk4-devel libadwaita-devel gcc gettext                    # Fedora
sudo apt install libgtk-4-dev libadwaita-1-dev build-essential gettext       # Debian / Ubuntu
sudo pacman -S gtk4 libadwaita base-devel gettext                            # Arch

git clone https://github.com/onyks-os/USBGuardGUI.git
cd USBGuardGUI
cargo run --release

make package-deb and make package-rpm build the packages above from the tree.

Usage

Open USBGuard from your application menu, or run usbguard-gui. Every option, setting, and exit code is listed in the External Interfaces Reference.

  • Check that everything is in place (daemon, bridge, permissions):

    usbguard-gui --diagnose
  • Print the device list or the ruleset without opening a window:

    usbguard-gui --list-devices
    usbguard-gui --list-rules

If --diagnose reports anything but connected, it names the cause and the command that fixes it.

Changing policy without typing the administrator password every time

By default USBGuard lets any active local user read devices and rules, and asks for the administrator password for every change. An administrator who wants members of the admin group to confirm changes with their own password instead can install the example rule shipped with the package — after reading it:

sudo install -m 0644 /usr/share/doc/usbguard-gui/70-usbguard-gui.rules.example \
     /etc/polkit-1/rules.d/70-usbguard-gui.rules

This is deliberately not done by the package: widening who may change USB policy is the administrator's decision.

How It Works

flowchart LR
    GUI["usbguard-gui<br/>(your user)"] -- "system D-Bus" --> Bridge["usbguard-dbus<br/>(root)"]
    Bridge -- "IPC socket" --> Daemon["usbguard-daemon<br/>(root)"]
    Daemon --> Kernel["kernel USB<br/>authorization"]
Loading
  1. One path to the daemon: the program talks only to the USBGuard D-Bus bridge on the system bus. On the way, a request passes the bus policy, then Polkit (which may ask for a password), then the daemon's own access control — and a refusal at each is told apart.
  2. Two loops that never share state: a Tokio runtime owns every D-Bus call; the GTK main loop owns every widget. They exchange immutable events over one channel, so the compiler — not code review — keeps them apart.
  3. Bursts are merged, not delayed: device signals are coalesced by device within a fixed latency ceiling; above forty devices in flight, one fresh device list replaces the deltas.
  4. Nothing stale survives a reconnection: when the bridge disappears the list is greyed out; when it returns, everything is re-read.
  5. Writes are requests: the view changes when the daemon reports the change, never before.

For the complete design — the upstream contract, the access model, concurrency, and the failure analysis — see the Technical Architecture & Design Guide.

Known Behavior & Limitations

Warning

  • Cancel stops waiting, not the daemon. Cancelling an operation while the password prompt is open stops the program from waiting; if the daemon had already acted, the list shows what it actually did.
  • Removing a rule has a tiny unavoidable race. The rule is re-resolved by its text just before removal; closing the window completely needs a USBGuard API change.
  • No status icon on stock GNOME. GNOME shows tray icons only with the AppIndicator extension. Without it, background mode still announces devices, and launching the application again reopens the window.
  • Configuration files are never edited. usbguard-daemon.conf, rules.conf, and the IPC access-control files are root-only; the program diagnoses them and tells you what to run. The program's own code reads and writes nothing under /etc, /var, or /sys (the system libraries it uses, such as GTK, still read their own configuration).

The full list of residual risks and the threat model are in docs/security-assessment.md.

Verification

What the program assumes about USBGuard was observed, not taken from the documentation — and three of those assumptions turned out to be wrong in the documentation:

  • Against a real daemon (usbguard 1.1.4): the D-Bus interfaces were introspected, the signal and target numbers recorded while a real device was plugged in, blocked, allowed, rejected, and removed, and the identity USBGuard's access control checks was tested directly. Results and the introspection data are in docs/architecture.md §13.4 and docs/dbus-introspection/.
  • Across distributions: how Debian, Ubuntu, Arch, and Fedora package USBGuard was checked in clean containers, and the .deb and .rpm were installed and run in clean Debian, Ubuntu, and Fedora containers.
  • The rule parser handles text that ultimately comes from the USB device itself. It is property-tested (render → parse is the identity, and no device name can become syntax) and was fuzzed with 10⁶ inputs per target without a finding; CI fuzzes it on every pull request.
  • Failure scenarios — a 40-device burst, a bridge restart, a rule removed from another terminal, identical rules, hostile device names — run against a mock USBGuard bridge on a private D-Bus connection, never against your system's daemon.
make verify        # formatting, lints (warnings are errors), all tests, dependency audit
make fuzz-parser   # cargo-fuzz the parser (nightly toolchain + cargo-fuzz)

Development & Testing

Important

Always run make verify before pushing code. If it fails, the change is not ready.

Command Goal
make test Unit and integration tests (no privileges, no daemon needed).
make lint rustfmt, Clippy with warnings as errors, ShellCheck, markdownlint.
make verify Lint + tests + dependency audit — the pre-push gate.
make fuzz-parser Fuzz the rule parser; FUZZ_RUNS=… sets the count.
make package-deb / make package-rpm Build native packages into dist/.
make help Every available target.

cargo build --no-default-features builds the headless commands without GTK.

Project Structure

├── src/
│   ├── model/        # Domain types — depend on nothing else in the program
│   ├── rules/        # Rule-language lexer, parser, renderer, and guided builder
│   ├── dbus/         # Proxies, client, event worker, supervisor, diagnostics
│   ├── ui/           # GTK 4 / libadwaita interface (Cargo feature `gui`)
│   └── cli.rs        # --diagnose, --list-devices, --list-rules
├── tests/            # Property tests, and D-Bus tests against a mock bridge
├── fuzz/             # cargo-fuzz targets for the parser
├── data/             # Desktop file, AppStream metainfo, GSettings schema, icons
├── packaging/        # Arch PKGBUILD, Flatpak manifest, example Polkit rule
├── assets/           # Logo
└── docs/             # Architecture, interfaces, threat model, Phase 0 results

Contributing

Contributions are welcome. The areas where help matters most:

  1. USBGuard on other distributions — behaviour of the D-Bus bridge and its Polkit defaults on systems other than the ones already checked.
  2. GTK 4 / libadwaita — accessibility, keyboard navigation, and adaptive layouts.
  3. Packaging — packages in the distributions' own repositories.
  4. Translations — the interface is in English and Italian; docs/translating.md explains how to add a language, no Rust needed.

Start with CONTRIBUTING.md. Every commit needs a DCO Signed-off-by line.

Bugs and feature requests GitHub Issues
Security vulnerabilities SECURITY.md — please do not open a public issue
Version support and EOL SUPPORT.md
Releases and packages GitHub Releases

This project is maintained in free time. A star helps others find it; sponsorship helps it keep going.

License

MIT. See LICENSE for more information.

About

Unprivileged GTK4 desktop client for managing USB device authorization policy through the USBGuard daemon.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages