ssh/tailssh: accept passwords and public keys

Some clients don't request 'none' authentication. Instead, they immediately supply
a password or public key. This change allows them to do so, but ignores the supplied
credentials and authenticates using Tailscale instead.

Updates #14922

Signed-off-by: Percy Wegmann <percy@tailscale.com>
This commit is contained in:
Percy Wegmann
2025-02-10 11:43:08 -06:00
committed by Percy Wegmann
parent f2f7fd12eb
commit db231107a2
6 changed files with 289 additions and 108 deletions

View File

@@ -239,6 +239,14 @@ type ClientConfig struct {
//
// A Timeout of zero means no timeout.
Timeout time.Duration
// SkipNoneAuth allows skipping the initial "none" auth request. This is unusual
// behavior, but it is allowed by [RFC4252 5.2](https://datatracker.ietf.org/doc/html/rfc4252#section-5.2),
// and some clients in the wild behave like this. One such client is the paramiko Python
// library, which is used in pgadmin4 via the sshtunnel library.
// When SkipNoneAuth is true, the client will attempt all configured
// [AuthMethod]s until one works, or it runs out.
SkipNoneAuth bool
}
// InsecureIgnoreHostKey returns a function that can be used for

View File

@@ -68,7 +68,16 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
var lastMethods []string
sessionID := c.transport.getSessionID()
for auth := AuthMethod(new(noneAuth)); auth != nil; {
var auth AuthMethod
if !config.SkipNoneAuth {
auth = AuthMethod(new(noneAuth))
} else if len(config.Auth) > 0 {
auth = config.Auth[0]
for _, a := range config.Auth {
lastMethods = append(lastMethods, a.method())
}
}
for auth != nil {
ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
if err != nil {
// On disconnect, return error immediately