diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index dcddc7ff..a454a84f 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -309,7 +309,7 @@ func (o *OIDC) authorizeSSHSign(claims *openIDPayload) ([]SignOption, error) { if o.IsAdmin(claims.Email) { signOptions = append(signOptions, &sshCertificateOptionsValidator{}) } else { - name := principalFromEmail(claims.Email) + name := SanitizeSSHPrincipal(claims.Email) if !sshUserRegex.MatchString(name) { return nil, errors.Errorf("invalid principal '%s' from email address '%s'", name, claims.Email) } @@ -338,21 +338,3 @@ func getAndDecode(uri string, v interface{}) error { } return nil } - -func principalFromEmail(email string) string { - if i := strings.LastIndex(email, "@"); i >= 0 { - email = email[:i] - } - return strings.Map(func(r rune) rune { - switch { - case r >= 'a' && r <= 'z': - return r - case r >= '0' && r <= '9': - return r - case r == '.': // drop dots - return -1 - default: - return '_' - } - }, strings.ToLower(email)) -} diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 0fd3f440..bb59bbb9 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -11,8 +11,6 @@ import ( "github.com/pkg/errors" ) -var sshUserRegex = regexp.MustCompile("^[a-z][-a-z0-9_]*$") - // Interface is the interface that all provisioner types must implement. type Interface interface { GetID() string @@ -164,3 +162,29 @@ func (l *List) UnmarshalJSON(data []byte) error { return nil } + +var sshUserRegex = regexp.MustCompile("^[a-z][-a-z0-9_]*$") + +// SanitizeSSHPrincipal grabs an email or a string with the format local@domain +// and returns a sanitized version of the local, valid to be used as a user +// name. If the email starts with a letter between a and z, the resulting string +// will match the regular expression `^[a-z][-a-z0-9_]*$`. +func SanitizeSSHPrincipal(email string) string { + if i := strings.LastIndex(email, "@"); i >= 0 { + email = email[:i] + } + return strings.Map(func(r rune) rune { + switch { + case r >= 'a' && r <= 'z': + return r + case r >= '0' && r <= '9': + return r + case r == '-': + return '-' + case r == '.': // drop dots + return -1 + default: + return '_' + } + }, strings.ToLower(email)) +} diff --git a/authority/provisioner/sign_ssh_options.go b/authority/provisioner/sign_ssh_options.go index 6825131d..8039aabb 100644 --- a/authority/provisioner/sign_ssh_options.go +++ b/authority/provisioner/sign_ssh_options.go @@ -211,6 +211,9 @@ type sshCertificateOptionsValidator struct { // Valid implements SSHCertificateOptionsValidator and returns nil if both // SSHOptions match. func (v *sshCertificateOptionsValidator) Valid(got SSHOptions) error { + if v.Want == nil { + return nil + } return v.Want.match(got) }