-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x/crypto/ssh: server side multi-step authentication #61447
Comments
Adding methods to interfaces is ordinarily not a backwards compatible change, with rare exceptions where interfaces were used somewhere they shouldn't have, and there is no plausible application-side implementation. Is there precedent for extending ConnMetadata? Do applications reimplement it, for example to shim some methods? In general, I am not a fan of state management in the first approach: if an application needs to keep any other state than "this method was attempted and succeeded" they will do it in some value or closure that we can't reach to reset if the username changes. It's not great that PartialSuccess needs to replicate all the callbacks from ServerConfig, but it does make application behavior for second steps more explicit. If no one has a better idea, I am happy with it. |
This proposal has been added to the active column of the proposals project |
The first approach was backward incompatible, sorry for that. We can achieve the same in a different way and also without adding We can export an error struct for the partial error
and we can extend the ConnMetadata so the change is backward compatible
The applications using the library return the authentication methods allowed to continue within the error and, in the authentication callbacks, can get the list of partial success errors to check the sequence. |
Hello, some proof of concept implementations here: https://github.com/drakkan/crypto/tree/multi_step1 I will continue to compare the proposed alternatives in the coming days. |
I did some comparative tests using both proposed implementations in SFTPGo. The closures approach also simplifies application code. I am very happy with it. I think this is the way to go. |
Change https://go.dev/cl/516355 mentions this issue: |
@hanwen, do you have any thoughts about this? Anyone else? |
I agree that approach 2 (golang/crypto@master...drakkan:crypto:multi_step2) looks better, because the next auth steps are explicit, and no need to extend ConnMetadata interface. The dup of the callbacks is unfortunate, but I don't see any way to better without breaking backward compat. |
note to @drakkan : you put your fork of golang/crypto under AGPLv3, and looking at the code to possibly put it into upstream go gives me the beejeebies. |
This was done before I was a member of the project. I've explicitly granted a license exception for the upstream inclusion, if that's not enough I can remove the license change or limit it to code that will never be merged upstream (e.g. server-side diffie-hellman-group-exchange) |
i am happy if https://github.com/tg123/sshpiper.crypto can be merged to upstream let me know if anything i can help |
Hello @tg123, thank you for your contribution. Can you please provide more details on what is implemented in your crypto fork related to this proposal? This proposal is about server-side multi step authentication (e.g. public key and then password). Multi-factor authentication, for example password and then TOTP code as per RFC 6238, can already be implemented using keyboard interactive authentication. |
the private fork impl first approach by adding CreateCtx it allows users to create a ctx when connection starts auth callback simply return err and put some flag into ctx i believe problem can be solved by introducing createctxcallback |
please be more explicit so that your proposal can be better evaluated. In your fork I see these API changes:
and also change every callback to include
Thank you |
the bottom line of my fork is to restrict all changes inside drop-in file sshpiper.go, no original ssh code will be touched to avoid conflict. allow sdk user to store something with connection, so things can be shared across callbacks
add 2 new callbacks like approach 1.
example code
|
Thank you @tg123, it's clear what you mean now. So we need to extend ConnMetadata and add:
I think I think allowing adding user-defined data to ConnMetadata can be useful in general, but perhaps it should be discussed in a different proposal. Your suggested approach is quite similar to what I have used in SFTPGo until now, personally I prefer the second approach now, but let see what other developers think about. I understand that the second approach requires some code changes on your part, but other than that if it's not applicable to your use case, can you explain why? It would be useful to know before making a decision. Thank you! |
my fork is to proxy ssh conn, so no change to me both 1 and 2 |
@tg123 what you mean is the initial presentation of allowed methods to the user, based on the username sent in the first "none" authentication? I.e. user "a" and user "b" could possibly receive another list of methods. This is at this point also not included in current proposal 1 and I'm not entirely sure that such a scenario is covered by RFC4252 as the client is free to change both the username and the method in a next authentication attempt. This seems to suggest the list of useful authentication methods returned by the server probably needs to include all methods usable by all users. Although I agree that I had another fork at some point to do exactly that: to reveal only pubkey authentication for a set of users, and password authentication for another set. But it might be better to cover this in a different proposal. Btw user-defined data can be kept by calling |
The idea of context had me thinking of the following: what if we extend ServerConfig to have stateful flavors of the callbacks?
We could document that the XxxxStateCallback override the XxxxCallback callbacks when provided. This would let us insert a struct into the server auth process. It provides a mechanism to set state (eg. for multi-step auth) and gives us more flexibility for adding future extensions. |
While I agree that we need a way to allow library users to add some contextual data. I would add them to
but I think this should be discussed in a different proposal. As for multi-step authentication, I think keeping the state within the library is more complex than the proposed callback approach because the library needs to store successful authentications and reset them if the username changes, also users of the library need to compare successful authentications with the expected flow and instruct Using the callback approach the application just return a partial success error with the new callbacks. For example, think of a flow where password authentication is only allowed after a successful public key authentication:
|
No new comments here recently; have we converged on an API proposal? |
I have expressed my opinion and preference, however I suggest waiting a little longer to get other opinions, e.g. from @FiloSottile |
Here is the updated proposal after also considering #64974
|
Any comments on the proposed API in #61447 (comment) ? |
Replied on the CL, but I like the API 👍 |
Based on the discussion above, this proposal seems like a likely accept. Proposal details in #61447 (comment) |
No change in consensus, so accepted. 🎉 Proposal details in #61447 (comment) |
Add support for sending back partial success to the client while handling authentication in the server. This is implemented by a special error that can be returned by any of the authentication methods, which contains the authentication methods to offer next. This patch is based on CL 399075 with some minor changes and the addition of test cases. Fixes golang/go#17889 Fixes golang/go#61447 Fixes golang/go#64974 Change-Id: I05c8f913bb407d22c2e41c4cbe965e36ab4739b0
Add support for sending back partial success to the client while handling authentication in the server. This is implemented by a special error that can be returned by any of the authentication methods, which contains the authentication methods to offer next. This patch is based on CL 399075 with some minor changes and the addition of test cases. Fixes golang/go#17889 Fixes golang/go#61447 Fixes golang/go#64974 Change-Id: I05c8f913bb407d22c2e41c4cbe965e36ab4739b0
RFC 4252 section 5.1 defines the partial success boolean for failed authentication requests.
If set, it means the authentication method is ok but one or more steps are required to complete the authentication.
We currently support multi-step authentication on the client side but not on the server side.
Server side support is a common request, see #17889.
Several patches are already available, basically we have to return a partial success error and the method/s allowed to continue.
Additionally, according to RFC 4252 section 5:
I would like to discuss a few different solutions for adding this support in x/crypto/ssh.
First approach
Based on some patches discussed in #17889.
Export
ErrPartialSuccess
like thisAdd the following to
ConnMetadata
interfaceso we keep the ordered list of authentication methods that returned ErrPartialSuccess.
If the username changes we can reset this list or disconnet the client.
Add the following to
ServerConfig
structThis is the flow:
ErrPartialSuccess
NextAuthMethodsCallback
is called and the application returns the allowed authentication methods, the application can usePartialSuccessMethods
to get the ordered list of authentication methods that returnedErrPartialSuccess
Limitations: we can't implement very specific use cases, such as enabling multi-step authentication for a specific public key (not sure if this is a real use case) or things like that. We just store the list of auth methods that returned
ErrPartialSuccess
Second approach
Based on CL 399075 (cc @peterverraedt), export a
PartialSuccess
struct implementing the Error interface like thisThe application using the library can implement the multi-step logic using closures.
I have a slight preference for this solution.
cc @golang/security
The text was updated successfully, but these errors were encountered: