🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)
🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)

🔧 Programmierung 🕛 vor 3 Monaten 9 Min Lesezeit
0

Migrating to zsh Broke Obsidian Git.

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




Introduction



In the previous post, I quit .bashrc and gave bash and zsh separate responsibilities. The terminal environment was clean.



The next day, I opened Obsidian and Git sync had stopped.




CODE
[email protected]: Permission denied (publickey).
fatal: Could not read from remote repository.






It looked like "switching to zsh broke Obsidian." But the shell wasn't the real culprit.

The ssh-agent's scope simply wasn't reaching GUI applications.



This is the record of how I tracked down that cause and fought my way through the Flatpak sandbox to fix it.







The Structure of the Problem



Checking from the terminal, the SSH keys were visible.




CODE
echo "$SSH_AUTH_SOCK"   # /run/user/1000/gcr/ssh
ssh-add -l # work-github / personal-github






Yet Obsidian Git plugin kept asking for a passphrase every single time.




CODE
Terminal (zsh) → OK
Obsidian (GUI) → NG






The problem broke down like this:




CODE
ssh-agent launched from a shell
→ only valid within that shell's process tree

Obsidian launched by the GUI
→ never reads .zshrc
→ never reaches the ssh-agent






In other words, this wasn't a shell configuration problem — it was a login session design problem.









Why Did It Work Before?



A question immediately surfaced: when everything was crammed into .bashrc, Obsidian Git worked fine. Why?



The answer turned out to involve GNOME Keyring (gcr).



gcr-ssh-agent integrates with the GNOME login session and automatically exposes SSH keys when the login keyring is unlocked. That meant Obsidian had always been talking to gcr directly, without going through the shell at all.



It wasn't that passphrases were unnecessary — it was that GNOME Keyring had been silently providing the previously-saved passphrase all along.









Attempting gcr-ssh-agent Unification (and Failing)



My first plan was to leverage this mechanism and route everything through gcr.




CODE
gcr-ssh-agent
→ /run/user/1000/gcr/ssh
→ shared by Terminal / Editor / Obsidian / Flatpak






I exposed the gcr socket to the Flatpak Obsidian sandbox.




CODE
flatpak override --user \
--filesystem=xdg-run/gcr \
--env=SSH_AUTH_SOCK=/run/user/1000/gcr/ssh \
md.obsidian.Obsidian






It worked — temporarily. GitHub authentication went through.



Then I rebooted. The problem came back.






Signing hangs






CODE
ssh-add -l              # personal-github / work-github → visible
ssh -T github-personal # → hangs (no response)






The verbose log showed GitHub was accepting the key.




CODE
Offering public key: id_ed25519_personal
Server accepts key: id_ed25519_personal
signing using ssh-ed25519 ← hangs here






The signing request sent to gcr-ssh-agent was never coming back.






Keys resurrect after ssh-add -D






CODE
ssh-add -D   # All identities removed.
ssh-add -l # personal-github / work-github ← instantly back






gcr was auto-publishing keys from its keyring storage. And that auto-restored state was broken for signing.






Cleaning up via Seahorse was risky



When I tried to delete SSH key entries in Seahorse (GNOME's keyring manager), I realized it looked like it would delete the actual private key files (~/.ssh/id_ed25519_personal) along with them.



That was the end of the gcr unification attempt.









What Remained Unexplained



I never found the root cause of gcr-ssh-agent's signing hang. Here's what I was able to rule out:




CODE
The key files were not corrupted
The public keys on GitHub were registered correctly
Exposing the Flatpak socket was not the sole cause
~/.ssh/config Host aliases were not misconfigured

→ gcr-ssh-agent's auto-restored state was unstable at signing time
→ Root cause: unknown






This isn't a verdict that gcr-ssh-agent is broken. This problem happened in this environment. Given that, I chose to prioritize determinism and switch to OpenSSH ssh-agent.









Pinning OpenSSH ssh-agent as a systemd User Service



I decided to manage a plain OpenSSH ssh-agent with a fixed socket path under systemd user.




CODE
mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/ssh-agent.service <<'EOF'
[Unit]
Description=OpenSSH key agent

[Service]
Type=simple
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
ExecStart=/usr/bin/ssh-agent -D -a %t/ssh-agent.socket

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable --now ssh-agent.service






The socket is now pinned at /run/user/1000/ssh-agent.socket.






Updating ~/.ssh/config



I added explicit IdentityAgent directives so SSH connections don't depend on the current value of SSH_AUTH_SOCK.




CODE
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
AddKeysToAgent yes
IdentityAgent /run/user/1000/ssh-agent.socket

Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
AddKeysToAgent yes
IdentityAgent /run/user/1000/ssh-agent.socket









Normalizing the socket in bash and zsh



Added to both .bashrc and .zshrc:




CODE
# OpenSSH ssh-agent (shared between .bashrc and .zshrc)
if [ -S "$XDG_RUNTIME_DIR/ssh-agent.socket" ]; then
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"
fi












Dealing with Flatpak



Flatpak's Obsidian runs inside its own sandbox. The host socket needs to be explicitly exposed.




CODE
flatpak override --user \
--filesystem=xdg-run/ssh-agent.socket \
--env=SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket \
md.obsidian.Obsidian

# Remove gcr access
flatpak override --user \
--nofilesystem=xdg-run/gcr \
md.obsidian.Obsidian









The Obsidian Git wrapper



Since the Obsidian Git plugin uses /app/bin/git inside the Flatpak, I wrapped it.




CODE
cat > ~/.local/bin/obsidian-git <<'EOF'
#!/usr/bin/env bash

SOCK="/run/user/1000/ssh-agent.socket"

export SSH_AUTH_SOCK="
$SOCK"
export GIT_SSH_COMMAND="ssh
\
-F /home/rbcn2000/.ssh/config
\
-o IdentityAgent=
$SOCK \
-o BatchMode=yes
\
-o ConnectTimeout=10
\
-o ServerAliveInterval=5
\
-o ServerAliveCountMax=1"

exec /app/bin/git "
$@"
EOF

chmod +x ~/.local/bin/obsidian-git






BatchMode=yes is the critical part — it prevents the automated commit/sync process from silently hanging while waiting for a passphrase prompt that can never appear.



In the Obsidian Git plugin settings, set Custom Git binary path to /home/rbcn2000/.local/bin/obsidian-git.









Automating Key Loading After Reboot



Unlike gcr, OpenSSH ssh-agent does not restore keys automatically. After a reboot, the agent starts empty.



I considered bringing keychain back, but decided against it — it risks creating duplicate agents. Instead, I wrote a minimal script whose only job is loading the keys.




CODE
cat > ~/.local/bin/ssh-load-github <<'EOF'
#!/usr/bin/env bash

set -u

SOCK="/run/user/1000/ssh-agent.socket"
export SSH_AUTH_SOCK="
$SOCK"

if [ ! -S "
$SOCK" ]; then
systemctl --user start ssh-agent.service 2>/dev/null || true
fi

if [ ! -S "
$SOCK" ]; then
echo "ssh-agent socket not found:
$SOCK" >&2
exit 1
fi

# Don't wait for passphrase input in non-interactive environments
if [ ! -t 0 ]; then
exit 0
fi

CURRENT_KEYS="
$(ssh-add -l 2>/dev/null || true)"

if ! printf '%s
\n' "$CURRENT_KEYS" | grep -q 'personal-github'; then
ssh-add "
$HOME/.ssh/id_ed25519_personal" || exit 1
fi

CURRENT_KEYS="
$(ssh-add -l 2>/dev/null || true)"

if ! printf '%s
\n' "$CURRENT_KEYS" | grep -q 'work-github'; then
ssh-add "
$HOME/.ssh/id_ed25519_work" || exit 1
fi
EOF

chmod +x ~/.local/bin/ssh-load-github






Call it from .zshrc and .bashrc on interactive shell startup:




CODE
if [[ $- == *i* ]] && [ -t 0 ] && [ "${SSH_LOAD_GITHUB_ON_SHELL:-1}" = "1" ]; then
"$HOME/.local/bin/ssh-load-github" 2>/dev/null || true
fi






Now, the first time a terminal opens after reboot, you enter the passphrase once — and that's it.









The .desktop Launcher Trap



I thought it was solved. There was one more problem.



After a reboot, launching Obsidian first meant Git didn't work.



The culprit was the TTY check inside ssh-load-github:




CODE
if [ ! -t 0 ]; then
exit 0 # No TTY → do nothing
fi






When Obsidian launches from a .desktop file, there is no TTY. So ssh-load-github exits immediately, keys never get loaded, and Obsidian starts up with an empty agent.






The fix: SSH_ASKPASS for a GUI dialog



OpenSSH has a built-in mechanism for exactly this situation: SSH_ASKPASS.




CODE
sudo apt install ssh-askpass-gnome






I wrote a dedicated launcher that uses it:




CODE
cat > ~/.local/bin/open-obsidian <<'EOF'
#!/usr/bin/env bash

SOCK="/run/user/1000/ssh-agent.socket"
export SSH_AUTH_SOCK="
$SOCK"

if [ ! -S "
$SOCK" ]; then
systemctl --user start ssh-agent.service 2>/dev/null || true
fi

if [ ! -S "
$SOCK" ]; then
echo "ssh-agent socket not found:
$SOCK" >&2
exit 1
fi

# Accept passphrase input via GTK dialog even without a TTY
export SSH_ASKPASS="/usr/lib/openssh/gnome-ssh-askpass"
export SSH_ASKPASS_REQUIRE=force

CURRENT_KEYS="
$(ssh-add -l 2>/dev/null || true)"

if ! printf '%s
\n' "$CURRENT_KEYS" | grep -q 'personal-github'; then
ssh-add "
$HOME/.ssh/id_ed25519_personal" || exit 1
fi

CURRENT_KEYS="
$(ssh-add -l 2>/dev/null || true)"

if ! printf '%s
\n' "$CURRENT_KEYS" | grep -q 'work-github'; then
ssh-add "
$HOME/.ssh/id_ed25519_work" || exit 1
fi

exec flatpak run md.obsidian.Obsidian "
$@"
EOF

chmod +x ~/.local/bin/open-obsidian









Overriding the .desktop file



The system-side Flatpak .desktop file can't be edited directly — Flatpak updates would overwrite it. Instead, copy it to the user applications directory and redirect the Exec line.




CODE
cp /var/lib/flatpak/exports/share/applications/md.obsidian.Obsidian.desktop \
~/.local/share/applications/md.obsidian.Obsidian.desktop

sed -i 's|^Exec=.*|Exec=/home/rbcn2000/.local/bin/open-obsidian %U|' \
~/.local/share/applications/md.obsidian.Obsidian.desktop

update-desktop-database ~/.local/share/applications/






Now, launching Obsidian from the app launcher shows a GTK passphrase dialog first, then starts Obsidian.









The Final Architecture






CODE
systemd user

OpenSSH ssh-agent.service

/run/user/1000/ssh-agent.socket
├─ bash / zsh (ssh-load-github on shell startup)
├─ ~/.ssh/config (IdentityAgent explicit)
└─ ~/.local/bin/open-obsidian ← launched from .desktop
└─ SSH_ASKPASS=gnome-ssh-askpass (GTK dialog)

Flatpak Obsidian

~/.local/bin/obsidian-git (BatchMode=yes)

/app/bin/git






After a reboot, behavior is now fully symmetric:




CODE
Opening a terminal first:
→ ssh-load-github runs
→ passphrase entered once
→ shared across bash / zsh / Obsidian for the rest of the session

Opening Obsidian first:
→ open-obsidian runs
→ GTK dialog prompts for passphrase
→ shared across bash / zsh / Obsidian for the rest of the session












Separation of Concerns



In the end, the responsibilities around ssh-agent sorted themselves into a clear table:
































Responsibility Handled by
Agent startup systemd user ssh-agent.service
Agent selection
SSH_AUTH_SOCK / IdentityAgent
Key loading (terminal)
ssh-load-github (TTY present)
Key loading (GUI launch)
open-obsidian + SSH_ASKPASS (no TTY)
Unattended Git operations BatchMode=yes


Tools like keychain that handle everything in one shot are convenient, but they make it easy to accidentally create duplicate agents or fail to reach GUI applications. Separating the responsibilities made it clear exactly what was happening at every step.









Lessons Learned




An environment variable is not a "setting" — it is the result of process inheritance.




Even if SSH_AUTH_SOCK is correctly set somewhere, it only reaches a child process if that child is spawned from a process that has it. Configuration written in .zshrc simply does not reach GUI applications. That was the root of everything.



And an additional lesson:




Sometimes a fixed socket with a simple agent is more deterministic than a polished GUI integration.




gcr-ssh-agent's desktop integration is elegant. But in this environment, its auto-restore mechanism turned out to be unstable at signing time. A plain OpenSSH ssh-agent on a fixed socket was far more predictable.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Hackers Just Poisoned the Rust Supply Chain | Threat Wire
1 Quelle
Hackers Found a Way Into Humanoid Robots | Threat Wire
1 Quelle
Bits und so #1021 (Passwort für Laufwerk)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Migrating to zsh Broke Obsidian Git.

Thematisch verwandte Begriffe: Migrating, Broke, Obsidian · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...