Keep USB approvals portable and preserve approval prompts

This commit is contained in:
acrogenesis committed 2026-09-29 10:16:56 -06:00
1 parent 60d6b255da
commit c3cdcb1e82
15 files changed
+535 -52

No files matched your search

+75 -9
View File
@@ -1,20 +1,22 @@
#!/bin/bash
# omarchy:summary=Ask before new USB devices can access the system
# omarchy:args=[--boot] [--yes]
# omarchy:args=[--boot] [--yes] [--review-existing]
# omarchy:requires-sudo=true
set -euo pipefail
assume_yes=0
from_boot=0
review_existing=0
while (( $# > 0 )); do
case "$1" in
--yes) assume_yes=1 ;;
--boot) from_boot=1 ;;
--review-existing) review_existing=1 ;;
-h | --help)
echo "Usage: omarchy-setup-security-usb-authorization [--boot] [--yes]"
echo "Usage: omarchy-setup-security-usb-authorization [--boot] [--yes] [--review-existing]"
echo
echo "Trusts USB devices that are connected now, then blocks new devices"
echo "until you approve them from an Omarchy notification."
@@ -25,6 +27,9 @@ while (( $# > 0 )); do
echo "Older snapshots without USBGuard keep all USB disabled after startup,"
echo "including keyboards, network adapters, and storage. Use --boot only if"
echo "disk unlock and recovery work without any USB devices."
echo
echo "--review-existing reviews connected devices with older port-bound approvals."
echo "Each portable approval requires confirmation; existing rules are preserved."
exit 0
;;
*)
@@ -43,6 +48,58 @@ daemon_config=/etc/usbguard/usbguard-daemon.conf
source "$OMARCHY_PATH/install/helpers/usb-authorization.sh"
if (( review_existing )); then
# Older Omarchy rules have no per-rule ownership marker. They cannot safely
# be distinguished from intentionally restricted administrator rules.
(( ! assume_yes && ! from_boot )) || { echo "Review requires individual confirmation; do not combine it with --yes or --boot." >&2; exit 2; }
policy=$(usbguard list-rules)
declare -A reviewed=()
# Keep stdin attached to the terminal for gum (and any sudo prompt).
mapfile -t policy_lines <<<"$policy"
for line in "${policy_lines[@]}"; do
if [[ $line =~ ^[0-9]+:[[:space:]] ]]; then
rule=${line#*: }
[[ $rule == allow\ * ]] || continue
portable=$(usb_authorization_portable_rule "$rule") || continue
[[ ${portable% label *} != "$rule" ]] || continue
devices=$(usbguard list-devices "$rule")
mapfile -t candidates <<<"$devices"
for device in "${candidates[@]}"; do
[[ $device =~ ^([0-9]+):[[:space:]](allow[[:space:]].*)$ ]] || continue
id=${BASH_REMATCH[1]} current=${BASH_REMATCH[2]}
[[ ! -v reviewed[$id] ]] || continue
reviewed[$id]=1
echo
printf '%s\n' "$current" | LC_ALL=C tr -c '\11\12\15\40-\176' '?'
echo "This device has a location-bound approval. Details are supplied by the device."
if gum confirm "Trust this device on any USB port or hub?"; then
sudo /usr/bin/omarchy-usb-authorization-approve "$id" "$current"
fi
done
fi
done
echo "Review complete. Disconnected devices can be approved when reconnected."
exit 0
fi
# The watcher runs in the session's environment, which may differ from a
# command invoked in a checkout. Fail before enabling blocking on mismatched
# or incomplete installations, including dangling dev-link unit symlinks.
session_environment=$(systemctl --user show-environment)
grep -Fqx -- "OMARCHY_PATH=$OMARCHY_PATH" <<<"$session_environment" || {
echo "The session and this command use different Omarchy installations. Update the session before enabling USB protection." >&2
exit 1
}
for required in "$unit_source" /usr/share/omarchy/install/helpers/usb-authorization-policy.sh; do
[[ -r $required ]] || { echo "USB authorization installation is incomplete: $required" >&2; exit 1; }
done
for required in "$OMARCHY_PATH/bin/omarchy-usb-authorization-watch" \
"$OMARCHY_PATH/bin/omarchy-usb-authorization-event" \
"$OMARCHY_PATH/bin/omarchy-usb-authorization-review" \
/usr/bin/omarchy-usb-authorization-approve; do
[[ -r $required && -x $required ]] || { echo "USB authorization command is unavailable: $required" >&2; exit 1; }
done
if (( ! assume_yes )); then
if (( from_boot )); then
gum style --foreground 212 --bold "USB authorization from boot"
@@ -93,19 +150,27 @@ sudo usbguard add-user "$USER" \
--policy=list \
--exceptions=listen
sudo install -Dm644 "$OMARCHY_PATH/default/polkit/org.omarchy.usb.policy" /usr/share/polkit-1/actions/org.omarchy.usb.policy
# Install a real file: a link into a checkout can become dangling on a branch
# switch. Start the waiting watcher before USBGuard can block new devices.
unit_temporary=$(mktemp "${unit}.XXXXXXXXXX")
install -m644 "$unit_source" "$unit_temporary"
mv -fT "$unit_temporary" "$unit"
systemctl --user daemon-reload
systemctl --user enable omarchy-usb-authorization.service
systemctl --user restart omarchy-usb-authorization.service
systemctl --user is-active --quiet omarchy-usb-authorization.service
sudo systemctl enable usbguard.service
# add-user ACL changes are loaded only when the daemon starts, so restart an
# existing service as well as starting a fresh one.
sudo systemctl restart usbguard.service
ln -sfn "$unit_source" "$unit"
systemctl --user daemon-reload
systemctl --user enable --now omarchy-usb-authorization.service
systemctl is-active --quiet usbguard.service
systemctl --user is-active --quiet omarchy-usb-authorization.service
if (( from_boot )); then
# Take the trust snapshot only after USBGuard is enforcing the policy. Every
# ID is resolved by the daemon, and no device-controlled descriptor text is
# passed to a privileged command.
# The fixed helper independently verifies each full live device snapshot.
usb_authorization_trust_present_devices
sudo omarchy-usb-authorization-boot enable
fi
@@ -113,6 +178,7 @@ fi
echo
echo "USB device authorization is enabled."
echo "Devices connected during setup remain trusted; new devices require approval."
echo "To review older port-bound approvals, run: omarchy-setup-security-usb-authorization --review-existing"
if (( from_boot )); then
echo "Boot-time authorization is enabled and takes effect after reboot."
fi