forked from TrueCloudLab/certificates
Add support for user provisioner certificates on OIDC provisioners.
OIDC provisioners create an SSH certificate with two principals. This was avoiding the creationg of user provisioner certificates for those provisioners. Fixes smallstep/cli#268
This commit is contained in:
parent
00998d053d
commit
b0ff731d18
3 changed files with 52 additions and 6 deletions
|
@ -296,7 +296,7 @@ func (h *caHandler) SSHSign(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addUserCertificate *SSHCertificate
|
var addUserCertificate *SSHCertificate
|
||||||
if addUserPublicKey != nil && cert.CertType == ssh.UserCert && len(cert.ValidPrincipals) == 1 {
|
if addUserPublicKey != nil && authority.IsValidForAddUser(cert) == nil {
|
||||||
addUserCert, err := h.Authority.SignSSHAddUser(ctx, addUserPublicKey, cert)
|
addUserCert, err := h.Authority.SignSSHAddUser(ctx, addUserPublicKey, cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteError(w, errs.ForbiddenErr(err))
|
WriteError(w, errs.ForbiddenErr(err))
|
||||||
|
|
|
@ -442,16 +442,37 @@ func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidForAddUser checks if a user provisioner certificate can be issued to
|
||||||
|
// the given certificate.
|
||||||
|
func IsValidForAddUser(cert *ssh.Certificate) error {
|
||||||
|
if cert.CertType != ssh.UserCert {
|
||||||
|
return errors.New("certificate is not a user certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(cert.ValidPrincipals) {
|
||||||
|
case 0:
|
||||||
|
return errors.New("certificate does not have any principals")
|
||||||
|
case 1:
|
||||||
|
return nil
|
||||||
|
case 2:
|
||||||
|
// OIDC provisioners adds a second principal with the email address.
|
||||||
|
// @ cannot be the first character.
|
||||||
|
if strings.Index(cert.ValidPrincipals[1], "@") > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("certificate does not have only one principal")
|
||||||
|
default:
|
||||||
|
return errors.New("certificate does not have only one principal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SignSSHAddUser signs a certificate that provisions a new user in a server.
|
// SignSSHAddUser signs a certificate that provisions a new user in a server.
|
||||||
func (a *Authority) SignSSHAddUser(ctx context.Context, key ssh.PublicKey, subject *ssh.Certificate) (*ssh.Certificate, error) {
|
func (a *Authority) SignSSHAddUser(ctx context.Context, key ssh.PublicKey, subject *ssh.Certificate) (*ssh.Certificate, error) {
|
||||||
if a.sshCAUserCertSignKey == nil {
|
if a.sshCAUserCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("signSSHAddUser: user certificate signing is not enabled")
|
return nil, errs.NotImplemented("signSSHAddUser: user certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
if subject.CertType != ssh.UserCert {
|
if err := IsValidForAddUser(subject); err != nil {
|
||||||
return nil, errs.Forbidden("signSSHAddUser: certificate is not a user certificate")
|
return nil, errs.Wrap(http.StatusForbidden, err, "signSSHAddUser")
|
||||||
}
|
|
||||||
if len(subject.ValidPrincipals) != 1 {
|
|
||||||
return nil, errs.Forbidden("signSSHAddUser: certificate does not have only one principal")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce, err := randutil.ASCII(32)
|
nonce, err := randutil.ASCII(32)
|
||||||
|
|
|
@ -917,3 +917,28 @@ func TestAuthority_RekeySSH(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsValidForAddUser(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
cert *ssh.Certificate
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", args{&ssh.Certificate{CertType: ssh.UserCert, ValidPrincipals: []string{"john"}}}, false},
|
||||||
|
{"ok oidc", args{&ssh.Certificate{CertType: ssh.UserCert, ValidPrincipals: []string{"jane", "jane@smallstep.com"}}}, false},
|
||||||
|
{"fail host", args{&ssh.Certificate{CertType: ssh.HostCert, ValidPrincipals: []string{"john"}}}, true},
|
||||||
|
{"fail principals", args{&ssh.Certificate{CertType: ssh.UserCert, ValidPrincipals: []string{"john", "jane"}}}, true},
|
||||||
|
{"fail no principals", args{&ssh.Certificate{CertType: ssh.UserCert, ValidPrincipals: []string{}}}, true},
|
||||||
|
{"fail extra principals", args{&ssh.Certificate{CertType: ssh.UserCert, ValidPrincipals: []string{"john", "jane", "doe"}}}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := IsValidForAddUser(tt.args.cert); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("IsValidForAddUser() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue