Skip to content

Add SSH connector with RFC 8693 token exchange#4527

Open
nikogura wants to merge 9 commits intodexidp:masterfrom
nikogura:master
Open

Add SSH connector with RFC 8693 token exchange#4527
nikogura wants to merge 9 commits intodexidp:masterfrom
nikogura:master

Conversation

@nikogura
Copy link

Summary

  • Add SSH connector (connector/ssh) enabling SSH key-based authentication for Dex
  • Support two authentication modes: JWT-based token exchange (RFC 8693) and challenge/response
  • Users authenticate with existing SSH keys (ed25519, RSA, etc.) — no passwords or browser flows required
  • Primary use case: passwordless kubectl authentication via SSH keys

Design

Security model: Identity comes from admin-configured mappings, not JWT claims. The connector
uses a secure 2-pass JWT verification process — claims are untrusted until cryptographic signature
verification succeeds against administrator-configured SSH public keys. This prevents key injection
attacks where clients embed their own verification keys.

Dual-audience tokens: JWTs use aud for the Dex instance ID and target_audience for the
final consumer (e.g., kubectl), following RFC 8693 semantics.

Challenge/response mode: Alternative to JWT-based auth for environments where clients can't
create JWTs. Dex issues a cryptographic challenge, the client signs it with their SSH key, and
Dex verifies the signature. Includes user enumeration prevention (identical responses for
valid/invalid users).

Changes

File Description
connector/ssh/ssh.go SSH connector implementation (TokenIdentityConnector + CallbackConnector)
connector/ssh/ssh_test.go Unit tests (~1030 lines, covers JWT validation, challenge/response, security)
connector/ssh/README.md Connector documentation
server/server.go Register SSH connector type
server/handlers.go Add RFC 8693 audience parameter support to token exchange handler
server/token_exchange_integration_test.go Integration tests: SSH token exchange through SQLite, Postgres, MySQL, and LDAP coexistence
go.mod / go.sum Add golang.org/x/crypto/ssh dependency

Test plan

  • go test ./connector/ssh/... — unit tests (JWT validation, challenge/response, security fixes, timing attack prevention)
  • go test -run TestTokenExchangeSSH_SQLite ./server/ — full token exchange flow through SQLite
  • go test -run TestTokenExchangeSSH_InMemory ./server/ — full flow through in-memory storage
  • go test -run TestTokenExchangeSSH_LDAPCoexistence ./server/ — connector routing with LDAP coexistence
  • go test -run TestTokenExchangeSSH_Postgres ./server/ — requires DEX_POSTGRES_ENT_HOST
  • go test -run TestTokenExchangeSSH_MySQL ./server/ — requires DEX_MYSQL_ENT_HOST
  • make lint passes (golangci-lint v2.4.0, 0 issues)
  • go test -race ./server/... — no race conditions

Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
Signed-off-by: Nik Ogura <nik.ogura@gmail.com>
@nikogura
Copy link
Author

nikogura commented Feb 11, 2026

govulncheck shows zero vulnerabilities, the only known CVE (CVE-2025-30204) was fixed in v5.2.2, and we're on v5.3.1.

I have no visibility into the snyk report. Happy to fix any problems if you'll tell me what they are.

@nabokihms
Copy link
Member

@nikogura Thank you for the PR! The idea looks awesome. I personally will give it a try!)

Process-wise, currently, we are holding off on merging all new connectors to the main branch until resolving the related issue. You can find more details here: #4578

@nikogura
Copy link
Author

Oh, that's lovely! I'll happily wait for that.

Thank you!

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.

2 participants