Skip to content

SSH client must prove key ownership before proceeding with MFA checks#64308

Open
cthach wants to merge 16 commits intomasterfrom
cthach/use-verified-pk
Open

SSH client must prove key ownership before proceeding with MFA checks#64308
cthach wants to merge 16 commits intomasterfrom
cthach/use-verified-pk

Conversation

@cthach
Copy link
Contributor

@cthach cthach commented Mar 5, 2026

Resolves #61557.

This change integrates our regular, forward and git SSH servers with x/crypto/ssh#ServiceConfig.VerifiedPublicKeyCallback.

Backport?

No, since in-band MFA is going to be introduced in v19, this change doesn't benefit v18 or v17.

Manual Test Plan

Test Environment

Deployed locally to my Mac since I can use my Mac as my target SSH host.

I used a trial GitHub org named cthach-dev for my tsh git ... tests.

Test Cases

List the various tests that were run to validate the change. Cases should be as exhaustive as possible.

Per-session MFA NOT required, node recording

Per-session MFA required, node recording

Per-session MFA NOT required, proxy recording

Per-session MFA required, proxy recording

This scenario doesn't currently work with the legacy public key auth method (see #8843), so no tests performed for this scenario.

…eck cert/key in PublicKeyCallback.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>
@cthach cthach force-pushed the cthach/use-verified-pk branch from a28e45d to bd9251e Compare March 5, 2026 16:20
cthach added 2 commits March 5, 2026 16:46
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
@cthach cthach requested review from espadolini and rosstimothy March 5, 2026 22:30
@cthach
Copy link
Contributor Author

cthach commented Mar 5, 2026

@espadolini @rosstimothy can you both take a preliminary look just so I can confirm whether this approach is sound to integrating x/crypto/ssh#ServerConfig. VerifiedPublicKeyCallback?

I have still have automated tests to write and manual tests to do, but before I do that, I wanted your input before barreling forward.

@cthach cthach changed the title WIP: Only grant SSH access after key ownership has been verified 🔑 WIP: Only grant SSH access to keys that have been owner verified 🔑 Mar 5, 2026
@cthach cthach changed the title WIP: Only grant SSH access to keys that have been owner verified 🔑 WIP: Only grant SSH access to keys that the client proves that it owns 🔑 Mar 5, 2026
@espadolini
Copy link
Contributor

I don't think this approach is sound; it will work without being too problematic as long as signatures are not gated behind user confirmation (which they might be when using PIV, or if someone has an agent configured to always ask for confirmation) but it will still cause additional roundtrips for any key that passes the initial check.

Other than factors outside of our direct influence, PublicKeyCallback and VerifiedPublicKeyCallback should let the same keys through; VerifiedPublicKeyCallback should be used if we actively have to do something in response to a key being used (since a key being proposed through PublicKeyCallback doesn't actually prove ownership) such as opening a connection to a remote server to forward a connection for example - and, since it happens during auth, it lets us steer the auth towards some additional auth methods, for example if said remote server asks for in-band MFA or maybe straight up PAM challenge-response things.

@espadolini
Copy link
Contributor

I'm also not sure if there's SSH clients in the wild that will react poorly to being told that a key is valid at the initial check but rejected after the signature - since that's something that normally never happens, I bet that we're going to hit some strange behaviors by doing this.

@cthach
Copy link
Contributor Author

cthach commented Mar 6, 2026

Really stellar feedback, @espadolini, thank you 🏆

Taking your feedback into account, I've come up with a counter proposal to address your points and just to make sure I'm interpreting your feedback correctly, I put it into bullet points.

New approach:

  1. PublicKeyCallback continues to do full cert and RBAC checks like we already currently do
  2. We extract the auth steering (e.g., keyboard-interactive) and put it into VerifiedPublicKeyCallback
  3. PublicKeyCallback and VerifiedPublicKeyCallback WILL NOT duplicate each others auth logic
  4. VerifiedPublicKeyCallback WILL NOT reject a key that was accepted by PublicKeyCallback
  5. PublicKeyCallback will pass permits and other auth metadata via ssh.Permissions struct like we currently already do

I don't think this approach is sound; it will work without being too problematic as long as signatures are not gated behind user confirmation (which they might be when using PIV, or if someone has an agent configured to always ask for confirmation) but it will still cause additional roundtrips for any key that passes the initial check.

Because we're keeping the status quo in PublicKeyCallback and it continues to do the same core auth logic, I believe this is no longer a concern.

Other than factors outside of our direct influence, PublicKeyCallback and VerifiedPublicKeyCallback should let the same keys through;

Points 1, 2, 3, 4 should cover this this. PublicKeyCallback does core auth and VerifiedPublicKeyCallback does post-verification auth steering.

VerifiedPublicKeyCallback should be used if we actively have to do something in response to a key being used (since a key being proposed through PublicKeyCallback doesn't actually prove ownership) such as opening a connection to a remote server to forward a connection for example - and, since it happens during auth, it lets us steer the auth towards some additional auth methods, for example if said remote server asks for in-band MFA or maybe straight up PAM challenge-response things.

Yes, covered in point 2

I'm also not sure if there's SSH clients in the wild that will react poorly to being told that a key is valid at the initial check but rejected after the signature - since that's something that normally never happens, I bet that we're going to hit some strange behaviors by doing this.

Shouldn't be an issue anymore with this new approach primarily because of point 4.

I'm just being super thorough to make sure I covered all your concerns. Let me know if I missed anything.

I'll start working on refactoring.

@espadolini
Copy link
Contributor

That should be good, the entire point of passing the ssh.Permissions to VerifiedPublicKeyCallback was to reuse "trusted" data that was already calculated.

If during your refactor you find it convenient to get rid of the stupid serialization/deserialization we're currently doing to shove data through ssh.Permissions now that we have a whole map[any]any to work with, that'd be very appreciated. 🙏

cthach added 2 commits March 6, 2026 11:55
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
@cthach
Copy link
Contributor Author

cthach commented Mar 6, 2026

That should be good, the entire point of passing the ssh.Permissions to VerifiedPublicKeyCallback was to reuse "trusted" data that was already calculated.

If during your refactor you find it convenient to get rid of the stupid serialization/deserialization we're currently doing to shove data through ssh.Permissions now that we have a whole map[any]any to work with, that'd be very appreciated. 🙏

I pushed another revision. I left out the refactor of the serialization/deserialization of passing data through ssh.Permissions. It would have been much more change and risk given how much it is used.

I'll proceed with adding automated tests and doing manual regression testing according to the defined test plan in the description. If everything passes, I'll open this for review.

cthach added 5 commits March 6, 2026 12:08
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
cthach and others added 5 commits March 12, 2026 09:57
Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Signed-off-by: Chris Thach <chris.thach@goteleport.com>
@cthach cthach added the no-changelog Indicates that a PR does not require a changelog entry label Mar 12, 2026
@cthach cthach changed the title WIP: Only grant SSH access to keys that the client proves that it owns 🔑 WIP: SSH client must prove key ownership before proceeding with MFA checks Mar 12, 2026
@cthach
Copy link
Contributor Author

cthach commented Mar 12, 2026

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0b6de15bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +748 to +750
return h.KeyboardInteractiveAuth(
context.Background(),
permit.GetPreconditions(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid re-triggering MFA on fallback publickey auth

This unconditionally sends any permit with preconditions back through KeyboardInteractiveAuth, but x/crypto/ssh invokes VerifiedPublicKeyCallback after every successful public-key callback (including the Next.PublicKeyCallback from a prior PartialSuccessError). In the legacy fallback path, the second public-key attempt therefore gets converted into another partial success instead of completing, which can trap clients without keyboard-interactive support in an auth loop and break per-session-MFA logins that previously relied on the compatibility callback.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really good feedback. It is true there is an auth loop in certain cases. Working on a fix now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 69c2480

Signed-off-by: Chris Thach <chris.thach@goteleport.com>
@cthach cthach changed the title WIP: SSH client must prove key ownership before proceeding with MFA checks SSH client must prove key ownership before proceeding with MFA checks Mar 12, 2026
@cthach cthach marked this pull request as ready for review March 12, 2026 22:24
@cthach cthach requested review from rosstimothy and removed request for rhammonds-teleport and strideynet March 12, 2026 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog Indicates that a PR does not require a changelog entry security Security Issues size/md

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSH service uses VerifiedPublicKeyCallback instead of PublicKeyCallback

3 participants