185 lines
7.5 KiB
Bash
Executable File
185 lines
7.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Ask before new USB devices can access the system
|
|
# 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] [--review-existing]"
|
|
echo
|
|
echo "Trusts USB devices that are connected now, then blocks new devices"
|
|
echo "until you approve them from an Omarchy notification."
|
|
echo
|
|
echo "--boot also blocks USB devices from the start of kernel boot. Connected"
|
|
echo "devices are added to USBGuard's policy, but remain unavailable before"
|
|
echo "USBGuard starts, including at an encrypted-disk password prompt."
|
|
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
|
|
;;
|
|
*)
|
|
echo "omarchy-setup-security-usb-authorization: unknown option '$1'. Try --help." >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
state_dir="$HOME/.local/state/omarchy/usb-authorization"
|
|
unit_source="$OMARCHY_PATH/default/systemd/user/omarchy-usb-authorization.service"
|
|
unit="$HOME/.config/systemd/user/omarchy-usb-authorization.service"
|
|
rules_file=/etc/usbguard/rules.conf
|
|
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"
|
|
else
|
|
gum style --foreground 212 --bold "USB device authorization"
|
|
fi
|
|
echo
|
|
echo "Devices connected now will be trusted. Disconnect anything you do not trust."
|
|
echo "New USB devices will remain blocked until you approve them."
|
|
if (( from_boot )); then
|
|
echo
|
|
echo "Boot-time protection blocks all USB devices until USBGuard starts."
|
|
echo "A USB keyboard will not work at an encrypted-disk password prompt, even"
|
|
echo "when it is connected now."
|
|
echo
|
|
echo "Older snapshots without USBGuard keep ALL USB disabled after startup,"
|
|
echo "including keyboards, network adapters, and storage."
|
|
echo "Continue only if disk unlock and recovery work without any USB devices."
|
|
fi
|
|
echo
|
|
if (( from_boot )); then
|
|
gum confirm "Enable USB authorization from kernel boot?" || exit 130
|
|
else
|
|
gum confirm "Enable USB device authorization?" || exit 130
|
|
fi
|
|
fi
|
|
|
|
omarchy-pkg-add usbguard
|
|
|
|
usb_authorization_require_secure_settings "$daemon_config"
|
|
|
|
mkdir -p "$state_dir" "${unit%/*}"
|
|
chmod 700 "$state_dir"
|
|
|
|
if ! usb_authorization_rules_present "$rules_file"; then
|
|
policy=$(mktemp "${TMPDIR:-/tmp}/omarchy-usb-policy.XXXXXXXXXX")
|
|
trap 'rm -f "$policy"' EXIT
|
|
chmod 600 "$policy"
|
|
usb_authorization_generate_policy "$policy"
|
|
sudo install -Dm600 -o root -g root "$policy" "$rules_file"
|
|
touch "$state_dir/policy-generated-by-omarchy"
|
|
fi
|
|
|
|
# This user can inspect USBGuard events and approve the device named by an
|
|
# event. Policy and daemon parameters remain unavailable through the IPC ACL.
|
|
sudo usbguard add-user "$USER" \
|
|
--devices=list,listen,modify \
|
|
--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
|
|
systemctl is-active --quiet usbguard.service
|
|
systemctl --user is-active --quiet omarchy-usb-authorization.service
|
|
|
|
if (( from_boot )); then
|
|
# The fixed helper independently verifies each full live device snapshot.
|
|
usb_authorization_trust_present_devices
|
|
sudo omarchy-usb-authorization-boot enable
|
|
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
|