Skip to content
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

proposal: x/crypto/ssh: add callbacks to dynamically change server and client configuration #61650

Open
drakkan opened this issue Jul 29, 2023 · 10 comments
Labels
Proposal Proposal-Crypto Proposal related to crypto packages or other security issues
Milestone

Comments

@drakkan
Copy link
Member

drakkan commented Jul 29, 2023

I propose to add the following callbacks.

// ServerConfig holds server specific configuration data.
type ServerConfig struct {
        ...
        // ConfigForClientCallback, if not nil, is called after receiving the
	// version from the client. It may return a non-nil ServerConfig in order to
	// change the ServerConfig that will be used to handle this connection. If
	// the returned ServerConfig is nil, the original ServerConfig will be used.
	// The ServerConfig returned by this callback may not be subsequently
	// modified. If an error is returned the handshake will fail.
	ConfigForClientCallback func(conn ConnMetadata) (*ServerConfig, error)
}
// A ClientConfig structure is used to configure a Client. It must not be
// modified after having been passed to an SSH function.
type ClientConfig struct {
        ...
        // ConfigForServerCallback, if not nil, is called after receiving the
	// version from the server. It may return a non-nil ClientConfig in order to
	// change the ClientConfig that will be used to handle this connection. If
	// the returned ClientConfig is nil, the original ClientConfig will be used.
	// The ClientConfig returned by this callback may not be subsequently
	// modified. If an error is returned the handshake will fail.
	ConfigForServerCallback func(conn ConnMetadata) (*ClientConfig, error)

These additions enable many advanced use cases, for example we can enable/disable algorithms, authentication methods, banners etc. depending on the client or server version.

cc @golang/security

@gopherbot gopherbot added this to the Proposal milestone Jul 29, 2023
@ianlancetaylor ianlancetaylor added the Proposal-Crypto Proposal related to crypto packages or other security issues label Jul 29, 2023
@ianlancetaylor
Copy link
Contributor

A style note: we tend to avoid a leading Get on function/method names.

@drakkan
Copy link
Member Author

drakkan commented Jul 29, 2023

A style note: we tend to avoid a leading Get on function/method names.

proposal updated, thank you!

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/519096 mentions this issue: ssh: add callbacks to dynamically change server and client configuration

@neild
Copy link
Contributor

neild commented Aug 13, 2023

On naming: The equivalent in crypto/tls is Config.GetConfigForClient. There are a number of other tls.Config callbacks with a Get prefix.

ssh.ClientConfig and ssh.ServerConfig contain a number of existing fields named ...Callback.

I think that package-internal consistency is more important than global consistency, so the names in the current proposal of ConfigForClientCallback and ConfigForServerCallback seems good to me.

(The general idea of the proposal also seems reasonable to me, but someone more familiar with crypto/ssh would be a better judge of that.)

@hanwen
Copy link
Contributor

hanwen commented Sep 21, 2023

is this really necessary? I think you could wrap the connection into something that gobbles the client version strings, creates a Config and then replays the client version string.

Or maybe we can further decompose NewServerConn into something that takes the Client version as well as the net.Conn.

@hanwen
Copy link
Contributor

hanwen commented Sep 21, 2023

Adding a callback in the Config that returns a Config is a very confusing API, as it's not obvious in which step the config gets swapped out for something else.

@neil-ca

This comment was marked as spam.

@hanwen
Copy link
Contributor

hanwen commented Oct 30, 2023

See https://go-review.git.corp.google.com/c/crypto/+/538555 for what I mean.

Also note (as documented in the change description) that there is really no need to have anything in the ClientConfig. Since the client picks the algorithms to use, the client can just specify all algorithms in preference order and still be both secure (for newer servers) and interoperable with older servers.

@drakkan
Copy link
Member Author

drakkan commented Oct 30, 2023

@hanwen, thank you. So an alternative proposal is this one

// ServerConfig holds server specific configuration data.
type ServerConfig struct {
   ...
   // ClientVersion is the client version to assume. If set, the
   // version handshake is skipped. In this case, callers must
   // explicitly call ExchangeVersions to obtain the remote version.
   ClientVersion string
}

maybe add the same to ClientConfig.

// A ClientConfig structure is used to configure a Client. It must not be
// modified after having been passed to an SSH function.
type ClientConfig struct {
   ...
   // ServerVersion is the server version to assume. If set, the
   // version handshake is skipped. In this case, callers must
   // explicitly call ExchangeVersions to obtain the remote version.
   ServerVersion string 
}

make the currently private exchangeVersions function public

@hanwen
Copy link
Contributor

hanwen commented Oct 31, 2023

I should add: 1) I understand what this issue is asking for, but 2) I find it hard to coherently explain what security benefit it creates.

As discussed, the client doesn't have to adapt to the server to begin with.

But what use is it for the server to adapt to the client? If the server just supports all algorithms (including the weak ones), a client with a new binary will still get strong algorithms, and an old client will get the weak algorithms.

So, what this feature would achieve is that the server looks like it only supports strong algorithms during cursory inspection (eg. probing the server), but it actually is less secure because clients can fallback to something insecure if they say the right version number.

The way to increase security is to get clients to upgrade their binaries. Refusing to serve them is the typical way to force this to happen, but if we expose negotiated algorithms (#58523), you could also use a banner message to get that message across.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Proposal Proposal-Crypto Proposal related to crypto packages or other security issues
Projects
Status: Incoming
Development

No branches or pull requests

6 participants