Monorepo for Tangled tangled.org

knot2 rejects push as default git user after failing to match past first SSH key #759

Open pluie.me
Open opened by pluie.me
assigned to oyster.cafe

Debugged over Discord at https://discord.com/channels/1361963801993285692/1361963802848919676/1548074022833492135

In a situation when the user has multiple SSH keys enrolled with their SSH agent, knot2 falsely rejects pushes if the first offered SSH key does not match with any key that can be used to push to a repository. It should try all offered keys before rejecting the push.

This directly contradicts what the error message implies, which is that a) the knot knows which users can push and what keys they have, and b) all keys have been tested and none of them matches. Either the error message or the behavior has to be changed:

git: knot: this ssh key doesn't match any key published by the accounts that may push here. Authorized: @pluie.me. If your agent offers several keys, add -o IdentitiesOnly=yes so it offers your registered key.

This can be best demonstrated by the behavior of ssh -v git@tangled.org: (slightly edited)

git: debug1: Authentications that can continue: publickey
git: debug1: Next authentication method: publickey
git: debug1: get_agent_identities: agent returned 2 keys
git: debug1: Will attempt key: WRONG KEY ED25519 SHA256:xH4X1GvCECzu+HdrYotvo5VyZLbec5bX/H2x4NCddP0 agent
git: debug1: Will attempt key: correct key ED25519 SHA256:YtI1D7vlcZ4obaiJ4tQihtswcMhHKdfYZuc4whOX2y8 agent
git: debug1: Will attempt key: /home/leah/.ssh/id_rsa
git: debug1: Will attempt key: /home/leah/.ssh/id_ecdsa
git: debug1: Will attempt key: /home/leah/.ssh/id_ecdsa_sk
git: debug1: Will attempt key: /home/leah/.ssh/id_ed25519
git: debug1: Will attempt key: /home/leah/.ssh/id_ed25519_sk
git: debug1: Will attempt key: /home/leah/.ssh/id_mldsa44_ed25519
git: debug1: Offering public key: WRONG KEY ED25519 SHA256:xH4X1GvCECzu+HdrYotvo5VyZLbec5bX/H2x4NCddP0 agent
git: debug1: Server accepts key: WRONG KEY ED25519 SHA256:xH4X1GvCECzu+HdrYotvo5VyZLbec5bX/H2x4NCddP0 agent
git: Authenticated to tangled.org ([2a04:3541:1000:500:24de:d2ff:fe7c:749b]:22) using "publickey".

The offer sequence should look more like this (which matches old knot & GitHub behavior):

debug1: Offering public key: WRONG KEY ED25519 SHA256:xH4X1GvCECzu+HdrYotvo5VyZLbec5bX/H2x4NCddP0 agent
debug1: Authentications that can continue: publickey
debug1: Offering public key: correct key ED25519 SHA256:YtI1D7vlcZ4obaiJ4tQihtswcMhHKdfYZuc4whOX2y8 agent
debug1: Server accepts key: correct key ED25519 SHA256:YtI1D7vlcZ4obaiJ4tQihtswcMhHKdfYZuc4whOX2y8 agent
git: Authenticated to tangled.org ([2a04:3541:1000:500:24de:d2ff:fe7c:749b]:22) using "publickey".

Update: the correct key does get chosen when running ssh -v pluie.me@tangled.org (replace pluie.me with your Tangled username of course). So I assume the key-picking step is simply ignored with the Git user in particular.

Update^2: Pushing to a git remote like pluie.me@tangled.org:pluie.me/site also works, but IMO this looks really comical and it's not reasonable to expect everyone with multiple keys to change their remotes.

as an intermediate fix before @oyster.cafe gets to investigating this, you don't have to specify the user + host in each git remote, you can specify that in your ssh config block for tangled.org! so this could further be shortened to tangled.org:pluie.me/site along with the following config block:

  Host tangled.org
      Hostname tangled.org
      User pluie.me
      ...

I had this exact same issue yesterday. I don't know if it is related, but I was trying to use the same origin push refs (instead of separating origins).

I eventually had to change ~/.ssh/config to point to both GitHub and Tangled:

Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github (change to your key name) IdentitiesOnly yes

Host tangled.org HostName tangled.org User git IdentityFile ~/.ssh/id_ed25519_tangled (change to your key name) IdentitiesOnly yes

But this only didn't work at the time. After lots of attempts of removing the origin URLs and adding them back, it eventually worked. Still unsure why.

as an intermediate fix, you can modify your ssh config block to set User lucca.fyi instead of User git!

+1 Claude found the same workaround

Claude asked me to add this comment, hope it's helpful:

Adding a repro that rules out the multi-key hypothesis, plus what I think is the actual cause.

Repro: no ssh-agent running (ssh-add -l → "Could not open a connection"), exactly one key, offered explicitly with -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519. The key was registered and confirmed present three ways: the sh.tangled.publicKey record in my PDS, https://tangled.org/<handle>.keys serving it, and the fingerprint shown in Settings → Keys.

git@tangled.org:did:plc: → Permission denied (publickey) @tangled.org:did:plc: → works immediately

So it isn't about which key gets offered first — a single, correct, explicitly-selected key is still refused as git.

The cause looks like username parsing. In knot-ssh/src/server.rs, decide() passes the SSH username through OwnerRef::parse(user), and knot-types/src/ids.rs rejects "git": not a DID, and it fails Handle::new_owned for having no dot, so parse returns None.

In knot-ssh/src/identity.rs, verify() with claim: None skips against_claim (which resolves the account and reads its published keys live from the PDS) and goes to against_key_set, which only consults the local index — state.index.owner_of_key(key, now) — and returns Verdict::Refused once Coverage::Ready with no match.

That index is filled by keyfill on an hourly lease, so as git a key is unusable until the next fill, while a DID/handle username works instantly. I think that also explains the "inconsistent" results others reported: it depends entirely on whether keyfill has run since the key was added.

Worth noting the empty-repo page and the Code button both advertise the git@ form, so a new user's first push fails for up to an hour with an error that points at their key.

Sign up or Sign in to add to the discussion.