From f368a96e01711c65a7fe12c7e932ef7cd40a9bca Mon Sep 17 00:00:00 2001 From: Mario Minardi Date: Thu, 11 Jun 2026 16:29:54 -0600 Subject: [PATCH] ssh/tailssh: dissallow purely numeric usernames for SSH Dissallow purely numeric usernames for SSH as these are ambiguous with numeric UID values. Updates https://github.com/tailscale/corp/issues/43245 Signed-off-by: Mario Minardi --- ssh/tailssh/tailssh.go | 7 +++++++ ssh/tailssh/tailssh_test.go | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/ssh/tailssh/tailssh.go b/ssh/tailssh/tailssh.go index 49dc0daf1..9ce3d1991 100644 --- a/ssh/tailssh/tailssh.go +++ b/ssh/tailssh/tailssh.go @@ -22,6 +22,7 @@ "os" "os/exec" "path/filepath" + "regexp" "runtime" "strconv" "strings" @@ -63,6 +64,8 @@ // hookSSHLoginSuccess is called after successful SSH authentication. // It is set by platform-specific code (e.g., auditd_linux.go). hookSSHLoginSuccess feature.Hook[func(logf logger.Logf, c *conn)] + + uidRegex = regexp.MustCompile("^[0-9]+$") ) const ( @@ -341,6 +344,10 @@ func (c *conn) clientAuth(cm ssh.ConnMetadata) (perms *ssh.Permissions, retErr e return &ssh.Permissions{}, nil } + if uidRegex.MatchString(cm.User()) { + return nil, c.errBanner(fmt.Sprintf("rejecting username %q. Usernames that consist of only digits are not allowed as they are ambiguous with numerical UIDs", cm.User()), nil) + } + if err := c.setInfo(cm); err != nil { return nil, c.errBanner("failed to get connection info", err) } diff --git a/ssh/tailssh/tailssh_test.go b/ssh/tailssh/tailssh_test.go index 04c9cd2f5..ec14405e7 100644 --- a/ssh/tailssh/tailssh_test.go +++ b/ssh/tailssh/tailssh_test.go @@ -831,6 +831,17 @@ func TestSSHAuthFlow(t *testing.T) { authErr: true, wantBanners: []string{`tailscale: tailnet policy does not permit you to SSH as user "alice"` + "\n"}, }, + { + name: "digit-only-username", + sshUser: "321", + state: &localState{ + sshEnabled: true, + varRoot: varRoot, + matchingRule: bobRule, + }, + authErr: true, + wantBanners: []string{`tailscale: rejecting username "321". Usernames that consist of only digits are not allowed as they are ambiguous with numerical UIDs` + "\n"}, + }, { name: "accept", state: &localState{