Set default SSH options if no user options are given.

This commit is contained in:
Mariano Cano 2019-07-31 17:03:33 -07:00
parent c17375a10a
commit a8f4ad1b8e
6 changed files with 67 additions and 27 deletions

View file

@ -448,13 +448,18 @@ func (p *AWS) authorizeSSHSign(claims *awsPayload) ([]SignOption, error) {
sshCertificateKeyIDModifier(claims.Subject), sshCertificateKeyIDModifier(claims.Subject),
} }
signOptions = append(signOptions, &sshCertificateOptionsValidator{&SSHOptions{ // Default to host + known IPs/hostnames
defaults := SSHOptions{
CertType: SSHHostCert, CertType: SSHHostCert,
Principals: []string{ Principals: []string{
doc.PrivateIP, doc.PrivateIP,
fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region), fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region),
}, },
}}) }
// Validate user options
signOptions = append(signOptions, sshCertificateOptionsValidator(defaults))
// Set defaults if not given as user options
signOptions = append(signOptions, sshCertificateDefaultsModifier(defaults))
return append(signOptions, return append(signOptions,
// set the default extensions // set the default extensions

View file

@ -308,10 +308,15 @@ func (p *Azure) authorizeSSHSign(claims azurePayload, name string) ([]SignOption
sshCertificateKeyIDModifier(name), sshCertificateKeyIDModifier(name),
} }
signOptions = append(signOptions, &sshCertificateOptionsValidator{&SSHOptions{ // Default to host + known hostnames
defaults := SSHOptions{
CertType: SSHHostCert, CertType: SSHHostCert,
Principals: []string{name}, Principals: []string{name},
}}) }
// Validate user options
signOptions = append(signOptions, sshCertificateOptionsValidator(defaults))
// Set defaults if not given as user options
signOptions = append(signOptions, sshCertificateDefaultsModifier(defaults))
return append(signOptions, return append(signOptions,
// set the default extensions // set the default extensions

View file

@ -360,13 +360,18 @@ func (p *GCP) authorizeSSHSign(claims *gcpPayload) ([]SignOption, error) {
sshCertificateKeyIDModifier(ce.InstanceName), sshCertificateKeyIDModifier(ce.InstanceName),
} }
signOptions = append(signOptions, &sshCertificateOptionsValidator{&SSHOptions{ // Default to host + known hostnames
defaults := SSHOptions{
CertType: SSHHostCert, CertType: SSHHostCert,
Principals: []string{ Principals: []string{
fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID), fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID),
fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID), fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID),
}, },
}}) }
// Validate user options
signOptions = append(signOptions, sshCertificateOptionsValidator(defaults))
// Set defaults if not given as user options
signOptions = append(signOptions, sshCertificateDefaultsModifier(defaults))
return append(signOptions, return append(signOptions,
// set the default extensions // set the default extensions

View file

@ -178,7 +178,7 @@ func (p *JWK) authorizeSSHSign(claims *jwtPayload) ([]SignOption, error) {
opts := claims.Step.SSH opts := claims.Step.SSH
signOptions := []SignOption{ signOptions := []SignOption{
// validates user's SSHOptions with the ones in the token // validates user's SSHOptions with the ones in the token
&sshCertificateOptionsValidator{opts}, sshCertificateOptionsValidator(*opts),
// set the key id to the token subject // set the key id to the token subject
sshCertificateKeyIDModifier(claims.Subject), sshCertificateKeyIDModifier(claims.Subject),
} }
@ -197,6 +197,9 @@ func (p *JWK) authorizeSSHSign(claims *jwtPayload) ([]SignOption, error) {
signOptions = append(signOptions, sshCertificateValidBeforeModifier(opts.ValidBefore.RelativeTime(t).Unix())) signOptions = append(signOptions, sshCertificateValidBeforeModifier(opts.ValidBefore.RelativeTime(t).Unix()))
} }
// Default to a user certificate with no principals if not set
signOptions = append(signOptions, sshCertificateDefaultsModifier{CertType: SSHUserCert})
return append(signOptions, return append(signOptions,
// set the default extensions // set the default extensions
&sshDefaultExtensionModifier{}, &sshDefaultExtensionModifier{},

View file

@ -303,20 +303,25 @@ func (o *OIDC) authorizeSSHSign(claims *openIDPayload) ([]SignOption, error) {
sshCertificateKeyIDModifier(claims.Email), sshCertificateKeyIDModifier(claims.Email),
} }
// Non-admins are only able to sign user certificates name := SanitizeSSHUserPrincipal(claims.Email)
if o.IsAdmin(claims.Email) { if !sshUserRegex.MatchString(name) {
signOptions = append(signOptions, &sshCertificateOptionsValidator{}) return nil, errors.Errorf("invalid principal '%s' from email address '%s'", name, claims.Email)
} else {
name := SanitizeSSHUserPrincipal(claims.Email)
if !sshUserRegex.MatchString(name) {
return nil, errors.Errorf("invalid principal '%s' from email address '%s'", name, claims.Email)
}
signOptions = append(signOptions, &sshCertificateOptionsValidator{&SSHOptions{
CertType: SSHUserCert,
Principals: []string{name},
}})
} }
// Admin users will default to user + name but they can be changed by the
// user options. Non-admins are only able to sign user certificates.
defaults := SSHOptions{
CertType: SSHUserCert,
Principals: []string{name},
}
if !o.IsAdmin(claims.Email) {
signOptions = append(signOptions, sshCertificateOptionsValidator(defaults))
}
// Default to a user with name as principal if not set
signOptions = append(signOptions, sshCertificateDefaultsModifier(defaults))
return append(signOptions, return append(signOptions,
// set the default extensions // set the default extensions
&sshDefaultExtensionModifier{}, &sshDefaultExtensionModifier{},

View file

@ -143,6 +143,27 @@ func (m sshCertificateValidBeforeModifier) Modify(cert *ssh.Certificate) error {
return nil return nil
} }
// sshCertificateDefaultModifier implements a SSHCertificateModifier that
// modifies the certificate with the given options if they are not set.
type sshCertificateDefaultsModifier SSHOptions
// Modify implements the SSHCertificateModifier interface.
func (m sshCertificateDefaultsModifier) Modify(cert *ssh.Certificate) error {
if cert.CertType == 0 {
cert.CertType = sshCertTypeUInt32(m.CertType)
}
if len(cert.ValidPrincipals) == 0 {
cert.ValidPrincipals = m.Principals
}
if cert.ValidAfter == 0 && !m.ValidAfter.IsZero() {
cert.ValidAfter = uint64(m.ValidAfter.Unix())
}
if cert.ValidBefore == 0 && !m.ValidBefore.IsZero() {
cert.ValidBefore = uint64(m.ValidBefore.Unix())
}
return nil
}
// sshDefaultExtensionModifier implements an SSHCertificateModifier that sets // sshDefaultExtensionModifier implements an SSHCertificateModifier that sets
// the default extensions in an SSH certificate. // the default extensions in an SSH certificate.
type sshDefaultExtensionModifier struct{} type sshDefaultExtensionModifier struct{}
@ -212,17 +233,13 @@ func (m *sshCertificateValidityModifier) Modify(cert *ssh.Certificate) error {
// sshCertificateOptionsValidator validates the user SSHOptions with the ones // sshCertificateOptionsValidator validates the user SSHOptions with the ones
// usually present in the token. // usually present in the token.
type sshCertificateOptionsValidator struct { type sshCertificateOptionsValidator SSHOptions
Want *SSHOptions
}
// Valid implements SSHCertificateOptionsValidator and returns nil if both // Valid implements SSHCertificateOptionsValidator and returns nil if both
// SSHOptions match. // SSHOptions match.
func (v *sshCertificateOptionsValidator) Valid(got SSHOptions) error { func (v sshCertificateOptionsValidator) Valid(got SSHOptions) error {
if v.Want == nil { want := SSHOptions(v)
return nil return want.match(got)
}
return v.Want.match(got)
} }
// sshCertificateDefaultValidator implements a simple validator for all the // sshCertificateDefaultValidator implements a simple validator for all the