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 <mario@tailscale.com>
This commit is contained in:
Mario Minardi
2026-06-11 16:29:54 -06:00
committed by Mario Minardi
parent 317201375f
commit f368a96e01
2 changed files with 18 additions and 0 deletions

View File

@@ -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)
}

View File

@@ -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{