Add iss#sub uri in OIDC certificates.

Admin will use the CR template if none is provided.
This commit is contained in:
Mariano Cano 2020-07-14 18:30:04 -07:00
parent 9bd576af2c
commit 71be83b25e
2 changed files with 40 additions and 21 deletions

View file

@ -74,10 +74,12 @@ type OIDC struct {
// IsAdmin returns true if the given email is in the Admins allowlist, false
// otherwise.
func (o *OIDC) IsAdmin(email string) bool {
email = sanitizeEmail(email)
for _, e := range o.Admins {
if email == sanitizeEmail(e) {
return true
if email != "" {
email = sanitizeEmail(email)
for _, e := range o.Admins {
if email == sanitizeEmail(e) {
return true
}
}
}
return false
@ -205,13 +207,8 @@ func (o *OIDC) ValidatePayload(p openIDPayload) error {
return errs.Unauthorized("validatePayload: failed to validate oidc token payload: invalid azp")
}
// Enforce an email claim
if p.Email == "" {
return errs.Unauthorized("validatePayload: failed to validate oidc token payload: email not found")
}
// Validate domains (case-insensitive)
if !o.IsAdmin(p.Email) && len(o.Domains) > 0 {
if p.Email != "" && len(o.Domains) > 0 && !o.IsAdmin(p.Email) {
email := sanitizeEmail(p.Email)
var found bool
for _, d := range o.Domains {
@ -304,15 +301,38 @@ func (o *OIDC) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
}
// Certificate templates
data := x509util.CreateTemplateData(claims.Subject, []string{claims.Email})
sans := []string{}
if claims.Email != "" {
sans = append(sans, claims.Email)
}
// Add uri SAN with iss#sub if issuer is a URL with schema.
//
// According to https://openid.net/specs/openid-connect-core-1_0.html the
// iss value is a case sensitive URL using the https scheme that contains
// scheme, host, and optionally, port number and path components and no
// query or fragment components.
if iss, err := url.Parse(claims.Issuer); err == nil && iss.Scheme != "" {
iss.Fragment = claims.Subject
sans = append(sans, iss.String())
}
data := x509util.CreateTemplateData(claims.Subject, sans)
data.SetToken(claims)
templateOptions, err := TemplateOptions(o.Options, data)
// Use the default template unless no-templates are configured and email is
// an admin, in that case we will use the CR template.
defaultTemplate := x509util.DefaultLeafTemplate
if !o.Options.HasTemplate() && o.IsAdmin(claims.Email) {
defaultTemplate = x509util.CertificateRequestTemplate
}
templateOptions, err := CustomTemplateOptions(o.Options, data, defaultTemplate)
if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "oidc.AuthorizeSign")
}
so := []SignOption{
return []SignOption{
templateOptions,
// modifiers / withOptions
newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID),
@ -320,13 +340,7 @@ func (o *OIDC) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
// validators
defaultPublicKeyValidator{},
newValidityValidator(o.claimer.MinTLSCertDuration(), o.claimer.MaxTLSCertDuration()),
}
// Admins should be able to authorize any SAN
if o.IsAdmin(claims.Email) {
return so, nil
}
return append(so, emailOnlyIdentity(claims.Email)), nil
}, nil
}
// AuthorizeRenew returns an error if the renewal is disabled.

View file

@ -35,6 +35,11 @@ type ProvisionerOptions struct {
TemplateData json.RawMessage `json:"templateData"`
}
// HasTemplate returns true if a template is defined in the provisioner options.
func (o *ProvisionerOptions) HasTemplate() bool {
return o != nil && (o.Template != "" || o.TemplateFile != "")
}
// TemplateOptions generates a CertificateOptions with the template and data
// defined in the ProvisionerOptions, the provisioner generated data, and the
// user data provided in the request. If no template has been provided,
@ -63,7 +68,7 @@ func CustomTemplateOptions(o *ProvisionerOptions, data x509util.TemplateData, de
return certificateOptionsFunc(func(so Options) []x509util.Option {
// We're not provided user data without custom templates.
if o == nil || (o.Template == "" && o.TemplateFile == "") {
if !o.HasTemplate() {
return []x509util.Option{
x509util.WithTemplate(defaultTemplate, data),
}