Add iss#sub uri in OIDC certificates.
Admin will use the CR template if none is provided.
This commit is contained in:
parent
9bd576af2c
commit
71be83b25e
2 changed files with 40 additions and 21 deletions
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue