Skip to content

fix(ssh-policy): look up policy by gateway-qualified role#42

Merged
sashyo merged 1 commit into
mainfrom
fix/ssh-policy-role-lookup
May 20, 2026
Merged

fix(ssh-policy): look up policy by gateway-qualified role#42
sashyo merged 1 commit into
mainfrom
fix/ssh-policy-role-lookup

Conversation

@sashyo

@sashyo sashyo commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary

Committed SSH policies are written under the JWT's gateway-qualified role (ssh:<gatewayId>:<serverId>:<sshUser>, e.g. ssh:Tide-GW:My Server:demo), but the SSH connect path was fetching by the short-form ssh:<user> (e.g. ssh:demo). The lookup either 404'd or returned a stale row from an older code revision, which then failed Sign #2 with "Policy signature could not be verified" because the bytes the ORK saw were never associated with the freshly-committed signature.

What changed

  • client/src/lib/sshClient.tsSSHSignatureRequest gains gatewayId?: string; the DelegatingPublicKeyAlgorithm callback forwards this.options.gatewayId into the signer.
  • client/src/lib/tideSsh.ts — both createTideSshSigner and createDynamicTideSshSigner now build the gateway-qualified role and fetch via api.sshPolicies.getByRole(roleId) (existing endpoint with exact lookup). Non-gateway flows (missing gatewayId/serverId) skip policy fetch with a warning instead of crashing.

No server-side changes — the existing GET /api/ssh-policies/committed/:roleId endpoint already does exact role lookup, which is what we need.

Verification via the trace logs from #41

Before this fix:

[PolicyTrace SIGN]   role=ssh:Tide-GW:My Server:demo dtv_sha=793dc9e7…
[PolicyTrace USE]    role=ssh:demo                   dtv_sha=d2946e66…   ← different row
[PolicyTrace ATTACH] role=ssh:demo                   dtv_sha=d2946e66…

After this fix, all three should carry the same role and identical dtv_sha.

Pre-deploy: delete the orphan row

The pre-fix code-path may have left an ssh:<user> row in ssh_policies that no longer corresponds to a real JWT role:

DELETE FROM ssh_policies WHERE role_id NOT LIKE 'ssh:%:%:%';

Test plan

  • Deploy keylessh web app
  • Delete stale short-form ssh_policies rows (optional cleanup)
  • Commit an SSH policy via Admin Approvals — confirm [PolicyTrace SIGN] with gateway-qualified role
  • Connect SSH to that server as the relevant user — confirm [PolicyTrace USE] and [PolicyTrace ATTACH] carry the same role and same dtv_sha as SIGN
  • SSH session succeeds (no "Policy signature could not be verified")

Committed SSH policies are stored under the JWT's gateway-qualified role
(e.g. ssh:Tide-GW:My Server:demo), but the SSH connect path was fetching
by short-form 'ssh:<user>'. The lookup either 404'd or returned a stale
row from an older code version, which then failed "Policy signature
could not be verified" because the stored bytes were never associated
with the freshly-committed signature.

Plumb gatewayId through SSHSignatureRequest and resolve the policy via
the existing /api/ssh-policies/committed/:roleId endpoint using the full
role: ssh:<gatewayId>:<serverId>:<sshUser>. Non-gateway flows (missing
gatewayId) skip the policy fetch with a warning instead of crashing.
@sashyo sashyo merged commit ca23c14 into main May 20, 2026
2 checks passed
sashyo added a commit that referenced this pull request May 20, 2026
…endpoint too

After PR #42 the client fetches via /api/ssh-policies/committed/:roleId
instead of the legacy /for-ssh-user/. The USE trace was only on the
legacy endpoint, so a fresh round-trip after #42 produced no server-side
USE line to compare against SIGN/ATTACH. Add the same trace to the new
endpoint so all three logs appear on every fetch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant