Merge pull request #9240 from omacom/fix/acceptance-security-coverage
Fix the bar visibility toggle and make the acceptance suite cover 4.0.2 security hardening
This commit is contained in:
@@ -5,3 +5,9 @@
|
||||
# omarchy:examples=omarchy toggle bar | omarchy toggle bar off | omarchy toggle bar on
|
||||
|
||||
omarchy-toggle bar-off "${1:-toggle}"
|
||||
|
||||
# The shell's watch on the toggles directory can miss flag changes that land in
|
||||
# quick succession, stranding the bar off screen until the shell restarts.
|
||||
# Nudge the bar to re-read the flag; quiet best-effort so the toggle still
|
||||
# works when the shell is not up.
|
||||
omarchy-shell -q omarchy.bar syncHidden
|
||||
|
||||
@@ -948,6 +948,21 @@ Item {
|
||||
onFileChanged: barHiddenProbe.running = true
|
||||
}
|
||||
|
||||
// The directory watch can permanently stop delivering events after flag
|
||||
// changes land in quick succession, stranding the bar off screen until the
|
||||
// shell restarts. `omarchy-toggle-bar` nudges this after flipping the flag
|
||||
// so the probe re-reads it even when the watch has gone quiet.
|
||||
IpcHandler {
|
||||
target: "omarchy.bar"
|
||||
|
||||
// Start rather than restart: a probe already in flight was launched by the
|
||||
// directory watch after the flag flipped, so its answer is current, and
|
||||
// killing it here can swallow the result entirely.
|
||||
function syncHidden(): void {
|
||||
barHiddenProbe.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
|
||||
+16
-1
@@ -19,7 +19,22 @@ mkdir -p "$OMARCHY_ACCEPTANCE_DIR"
|
||||
# the session environment is inherited.
|
||||
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||
export DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-unix:path=$XDG_RUNTIME_DIR/bus}"
|
||||
export OMARCHY_PATH="${OMARCHY_PATH:-$ROOT}"
|
||||
# The suite acts on the running session, and qs matches shell instances by
|
||||
# config path — pointed at any other tree, every omarchy-shell call reads as
|
||||
# "not running". So default OMARCHY_PATH to the tree the session was started
|
||||
# from, then this checkout for standalone use, then the installed tree for
|
||||
# when only test/ was synced in (omarchy-iso-test's --sync-omarchy).
|
||||
if [[ -z ${OMARCHY_PATH:-} ]]; then
|
||||
OMARCHY_PATH=$(systemctl --user show-environment 2>/dev/null | sed -n 's/^OMARCHY_PATH=//p' | tail -1)
|
||||
fi
|
||||
if [[ -z $OMARCHY_PATH ]]; then
|
||||
OMARCHY_PATH=$ROOT
|
||||
fi
|
||||
if [[ ! -f $OMARCHY_PATH/shell/shell.qml && -f /usr/share/omarchy/shell/shell.qml ]]; then
|
||||
OMARCHY_PATH=/usr/share/omarchy
|
||||
fi
|
||||
export OMARCHY_PATH
|
||||
|
||||
export PATH="$OMARCHY_PATH/bin:$PATH"
|
||||
|
||||
if [[ -z ${DISPLAY:-} ]]; then
|
||||
|
||||
@@ -36,7 +36,9 @@ screen_contains() {
|
||||
local text="$1"
|
||||
local snapshot="/tmp/omarchy-acceptance-ocr-$$.png"
|
||||
|
||||
if ! timeout 10 grim "$snapshot" 2>/dev/null; then
|
||||
# Capture at 2x scale: tesseract routinely drops small caption text at
|
||||
# native resolution (the weather panel's detail labels, for one).
|
||||
if ! timeout 10 grim -s 2 "$snapshot" 2>/dev/null; then
|
||||
rm -f "$snapshot"
|
||||
return 1
|
||||
fi
|
||||
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Verifies the security posture of an installed system: the unprivileged
|
||||
# session-to-root paths closed for 4.0.2 (blanket input-group grant, shipped
|
||||
# asdcontrol sudoers authorization) and the SSH hardening flow.
|
||||
#
|
||||
# The sshd section reconfigures the machine (enables sshd, opens the firewall,
|
||||
# disables password logins), so it demands explicit opt-in: it only runs when
|
||||
# OMARCHY_ACCEPTANCE_SUDO_PASSWORD is set, which omarchy-iso-test does for its
|
||||
# throwaway VMs. A cached sudo timestamp alone never triggers it, so running
|
||||
# the suite on a machine you care about cannot reconfigure sshd by accident.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
# Membership of `input` gives raw access to /dev/input/event*: any process
|
||||
# running as the user could log keystrokes. Only the opt-in controller and
|
||||
# ydotool features may grant it.
|
||||
verify_input_group() {
|
||||
if id -nG | grep -qw input; then
|
||||
if pacman -Q xpadneo-dkms &>/dev/null || pacman -Q ydotool &>/dev/null; then
|
||||
pass "input group membership is backed by an opt-in feature"
|
||||
else
|
||||
fail "user is not in the input group" "no controller or ydotool support installed to justify it"
|
||||
fi
|
||||
else
|
||||
pass "user is not in the input group"
|
||||
fi
|
||||
}
|
||||
|
||||
sudo_available() {
|
||||
if sudo -n true 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n ${OMARCHY_ACCEPTANCE_SUDO_PASSWORD:-} ]]; then
|
||||
printf '%s\n' "$OMARCHY_ACCEPTANCE_SUDO_PASSWORD" | sudo -S -v 2>/dev/null
|
||||
return $?
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_asdcontrol_sudoers() {
|
||||
# Omarchy used to ship a passwordless sudoers grant for asdcontrol; that
|
||||
# authorization now belongs to the package alone.
|
||||
if sudo -n test -e /etc/sudoers.d/omarchy-asdcontrol; then
|
||||
fail "no omarchy asdcontrol sudoers grant is shipped" "/etc/sudoers.d/omarchy-asdcontrol exists"
|
||||
fi
|
||||
pass "no omarchy asdcontrol sudoers grant is shipped"
|
||||
}
|
||||
|
||||
verify_sshd_hardening() {
|
||||
local key_file=/tmp/omarchy-acceptance-sshd-key
|
||||
local effective_config
|
||||
|
||||
rm -f "$key_file" "$key_file.pub"
|
||||
ssh-keygen -t ed25519 -N "" -q -C "omarchy-acceptance" -f "$key_file"
|
||||
|
||||
if ! omarchy-setup-security-sshd --key="$(cat "$key_file.pub")" >"$ARTIFACTS/setup-security-sshd.log" 2>&1; then
|
||||
fail "omarchy-setup-security-sshd completes unattended" "$(tail -5 "$ARTIFACTS/setup-security-sshd.log")"
|
||||
fi
|
||||
pass "omarchy-setup-security-sshd completes unattended"
|
||||
|
||||
systemctl is-active sshd.service >/dev/null || fail "sshd is running after setup"
|
||||
pass "sshd is running after setup"
|
||||
|
||||
grep -qxF "$(cat "$key_file.pub")" "$HOME/.ssh/authorized_keys" || fail "the key is authorized"
|
||||
pass "the key is authorized"
|
||||
|
||||
# The command verifies its own hardening before keeping it, but assert the
|
||||
# effective config independently: sshd honors the first value it reads, and
|
||||
# regressions here reopen password logins. Keywords match case-insensitively
|
||||
# because OpenSSH 9.x dumps them lowercase and 10.x in CamelCase.
|
||||
effective_config=$(sudo -n sshd -T) || fail "sshd reports its effective config"
|
||||
grep -qixF "passwordauthentication no" <<<"$effective_config" || fail "password authentication is off"
|
||||
pass "password authentication is off"
|
||||
grep -qixF "kbdinteractiveauthentication no" <<<"$effective_config" || fail "keyboard-interactive authentication is off"
|
||||
pass "keyboard-interactive authentication is off"
|
||||
|
||||
if omarchy-cmd-present ufw; then
|
||||
sudo -n ufw status | grep -qE '^22/tcp\s+LIMIT' || fail "the SSH port is rate limited in the firewall"
|
||||
pass "the SSH port is rate limited in the firewall"
|
||||
fi
|
||||
|
||||
# Leave the machine as found where cheap: the throwaway key stays useless
|
||||
# once removed, while the hardening itself is the state under test.
|
||||
sed -i "\#$(cat "$key_file.pub" | cut -d' ' -f2)#d" "$HOME/.ssh/authorized_keys"
|
||||
rm -f "$key_file" "$key_file.pub"
|
||||
}
|
||||
|
||||
verify_input_group
|
||||
|
||||
if sudo_available; then
|
||||
verify_asdcontrol_sudoers
|
||||
else
|
||||
pass "asdcontrol sudoers check skipped: sudo needs a password"
|
||||
fi
|
||||
|
||||
if [[ -n ${OMARCHY_ACCEPTANCE_SUDO_PASSWORD:-} ]] && sudo_available; then
|
||||
verify_sshd_hardening
|
||||
else
|
||||
pass "sshd hardening exercise skipped: set OMARCHY_ACCEPTANCE_SUDO_PASSWORD to run it"
|
||||
fi
|
||||
@@ -8,12 +8,17 @@ status=0
|
||||
|
||||
verify_core_packages() {
|
||||
local package
|
||||
local manifest="$OMARCHY_PATH/install/omarchy-base.packages"
|
||||
local -a missing=()
|
||||
|
||||
# Without this, a missing manifest reads as an empty package list and the
|
||||
# audit passes having checked nothing.
|
||||
[[ -f $manifest ]] || fail "all Omarchy core packages are installed" "package manifest not found: $manifest"
|
||||
|
||||
while IFS= read -r package; do
|
||||
[[ -z $package || $package == \#* ]] && continue
|
||||
pacman -Q "$package" >/dev/null 2>&1 || missing+=("$package")
|
||||
done <"$OMARCHY_PATH/install/omarchy-base.packages"
|
||||
done <"$manifest"
|
||||
|
||||
(( ${#missing[@]} == 0 )) || fail "all Omarchy core packages are installed" "missing packages: ${missing[*]}"
|
||||
pass "all Omarchy core packages are installed (${#missing[@]} missing)"
|
||||
|
||||
Reference in New Issue
Block a user