diff --git a/bin/omarchy-remove-service-ssh-agent b/bin/omarchy-remove-service-ssh-agent new file mode 100755 index 00000000..9201ebf4 --- /dev/null +++ b/bin/omarchy-remove-service-ssh-agent @@ -0,0 +1,16 @@ +#!/bin/bash + +# omarchy:summary=Disable the gcr-ssh-agent SSH agent and its environment override. + +# The file goes before disable reloads the manager, or the environment generator keeps exporting it. +rm -f "$HOME/.config/environment.d/90-gcr-ssh-agent.conf" +systemctl --user disable --now gcr-ssh-agent.socket 2>/dev/null || true +systemctl --user stop gcr-ssh-agent.service 2>/dev/null || true + +# The socket exported SSH_AUTH_SOCK to the user manager, which outlives the agent. +if systemctl --user show-environment | grep -Fqx "SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/gcr/ssh"; then + systemctl --user unset-environment SSH_AUTH_SOCK +fi + +echo "" +echo "SSH agent has been disabled. Log out and back in for it to take full effect." diff --git a/bin/omarchy-setup-security-ssh-agent b/bin/omarchy-setup-security-ssh-agent new file mode 100755 index 00000000..0c646f72 --- /dev/null +++ b/bin/omarchy-setup-security-ssh-agent @@ -0,0 +1,17 @@ +#!/bin/bash + +# omarchy:summary=Enable an SSH agent (gcr-ssh-agent) that prompts for key passphrases graphically. + +set -e + +ENV_FILE="$HOME/.config/environment.d/90-gcr-ssh-agent.conf" + +systemctl --user enable --now gcr-ssh-agent.socket + +mkdir -p "$(dirname "$ENV_FILE")" +echo 'SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/gcr/ssh' >"$ENV_FILE" + +echo "" +echo "SSH agent has been enabled. Log out and back in to make it available everywhere." +echo "The first use of a passphrase-protected key opens a dialog, with the option to" +echo "remember the passphrase in the keyring." diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index 450bc5ac..161504f2 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -183,6 +183,7 @@ "setup.security.fingerprint": {"icon":"󰈷","label":"Fingerprint","when":"omarchy-hw-fingerprint","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fingerprint"}, "setup.security.fido2": {"icon":"","label":"Fido2","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-fido2"}, "setup.security.sshd": {"icon":"󰣀","label":"SSHD","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-sshd"}, + "setup.security.ssh-agent": {"icon":"󰣀","label":"SSH Agent","when":"! systemctl --user -q is-enabled gcr-ssh-agent.socket","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-ssh-agent"}, "setup.security.passwordless-sudo": {"icon":"󰟵","label":"Passwordless Sudo","action":"omarchy-launch-floating-terminal-with-presentation omarchy-sudo-passwordless"}, "setup.security.sudoless-docker": {"icon":"󰡨","label":"Sudoless Docker","when":"omarchy-sudo-docker --configured","action":"omarchy-launch-floating-terminal-with-presentation omarchy-setup-security-sudoless-docker"}, "setup.config.hyprland": {"icon":"","label":"Hyprland","action":"omarchy-launch-config-editor \"$HOME/.config/hypr/hyprland.lua\""}, @@ -310,6 +311,7 @@ "remove.browser.zen": {"icon":"","label":"Zen","when":"omarchy-pkg-present zen-browser-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser zen'"}, "remove.service.dropbox": {"icon":"","label":"Dropbox","when":"omarchy-pkg-present dropbox","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-dropbox"}, "remove.service.tailscale": {"icon":"","label":"Tailscale","when":"omarchy-pkg-present tailscale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-tailscale"}, + "remove.service.ssh-agent": {"icon":"󰣀","label":"SSH Agent","when":"systemctl --user -q is-enabled gcr-ssh-agent.socket","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-ssh-agent"}, "remove.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-chatgpt"}, "remove.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","when":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-claude"}, "remove.ai.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"}, diff --git a/test/shell.d/ssh-agent-service-remove-test.sh b/test/shell.d/ssh-agent-service-remove-test.sh new file mode 100755 index 00000000..ff528eb1 --- /dev/null +++ b/test/shell.d/ssh-agent-service-remove-test.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +mock_bin="$test_tmp/bin" +mkdir -p "$mock_bin" + +# A user manager with systemd's two environment blocks: set-environment's, which +# outlives the socket that wrote it, and the generator's, re-read from +# environment.d on every reload, which disable triggers. +cat >"$mock_bin/systemctl" <<'SH' +#!/bin/bash +state=$OMARCHY_TEST_MANAGER +case $2 in + disable) + if [[ -f $HOME/.config/environment.d/90-gcr-ssh-agent.conf ]]; then + echo "SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/gcr/ssh" >"$state/generated" + else + : >"$state/generated" + fi + ;; + unset-environment) : >"$state/explicit" ;; + show-environment) cat "$state/generated" "$state/explicit" ;; +esac +exit 0 +SH +chmod +x "$mock_bin/systemctl" + +run_remove() { + local explicit="$1" + + export HOME="$test_tmp/home" XDG_RUNTIME_DIR="$test_tmp/run" OMARCHY_TEST_MANAGER="$test_tmp/manager" + rm -rf "$HOME" "$OMARCHY_TEST_MANAGER" + mkdir -p "$HOME/.config/environment.d" "$OMARCHY_TEST_MANAGER" + echo 'SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/gcr/ssh' >"$HOME/.config/environment.d/90-gcr-ssh-agent.conf" + echo "SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/gcr/ssh" >"$OMARCHY_TEST_MANAGER/generated" + echo "$explicit" >"$OMARCHY_TEST_MANAGER/explicit" + + PATH="$mock_bin:$PATH" "$ROOT/bin/omarchy-remove-service-ssh-agent" >/dev/null + PATH="$mock_bin:$PATH" systemctl --user show-environment +} + +env_after=$(run_remove "SSH_AUTH_SOCK=$test_tmp/run/gcr/ssh") +[[ -z $env_after ]] || fail "removal leaves no SSH_AUTH_SOCK pointing at the stopped agent" "$env_after" +pass "removal leaves no SSH_AUTH_SOCK pointing at the stopped agent" + +env_after=$(run_remove "SSH_AUTH_SOCK=$test_tmp/run/other-agent.sock") +[[ $env_after == "SSH_AUTH_SOCK=$test_tmp/run/other-agent.sock" ]] || fail "removal keeps an SSH_AUTH_SOCK another agent set" "$env_after" +pass "removal keeps an SSH_AUTH_SOCK another agent set"