Validate audiences in the default provisioner.

This commit is contained in:
Mariano Cano 2019-03-06 18:32:56 -08:00
parent 33c1449360
commit 2d00cd0933
6 changed files with 50 additions and 30 deletions

View file

@ -4,7 +4,6 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/hex" "encoding/hex"
"fmt"
"sync" "sync"
"time" "time"
@ -25,7 +24,6 @@ type Authority struct {
ottMap *sync.Map ottMap *sync.Map
startTime time.Time startTime time.Time
provisioners *provisioner.Collection provisioners *provisioner.Collection
audiences []string
// Do not re-initialize // Do not re-initialize
initOnce bool initOnce bool
} }
@ -37,19 +35,11 @@ func New(config *Config) (*Authority, error) {
return nil, err return nil, err
} }
// Define audiences: legacy + possible urls without the ports.
// The CA might have proxies in front so we cannot rely on the port.
audiences := []string{legacyAuthority}
for _, name := range config.DNSNames {
audiences = append(audiences, fmt.Sprintf("https://%s/sign", name), fmt.Sprintf("https://%s/1.0/sign", name))
}
var a = &Authority{ var a = &Authority{
config: config, config: config,
certificates: new(sync.Map), certificates: new(sync.Map),
ottMap: new(sync.Map), ottMap: new(sync.Map),
provisioners: provisioner.NewCollection(audiences), provisioners: provisioner.NewCollection(config.getAudiences()),
audiences: audiences,
} }
if err := a.init(); err != nil { if err := a.init(); err != nil {
return nil, err return nil, err

View file

@ -84,7 +84,7 @@ func (a *Authority) Authorize(ott string) ([]provisioner.SignOption, error) {
// This method will also validate the audiences for JWK provisioners. // This method will also validate the audiences for JWK provisioners.
p, ok := a.provisioners.LoadByToken(token, &claims.Claims) p, ok := a.provisioners.LoadByToken(token, &claims.Claims)
if !ok { if !ok {
return nil, &apiError{errors.Errorf("authorize: provisioner not found"), return nil, &apiError{errors.New("authorize: provisioner not found or invalid audience"),
http.StatusUnauthorized, errContext} http.StatusUnauthorized, errContext}
} }

View file

@ -2,6 +2,7 @@ package authority
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net" "net"
"os" "os"
"time" "time"
@ -58,9 +59,8 @@ type AuthConfig struct {
} }
// Validate validates the authority configuration. // Validate validates the authority configuration.
func (c *AuthConfig) Validate() error { func (c *AuthConfig) Validate(audiences []string) error {
var err error var err error
if c == nil { if c == nil {
return errors.New("authority cannot be undefined") return errors.New("authority cannot be undefined")
} }
@ -71,11 +71,18 @@ func (c *AuthConfig) Validate() error {
if c.Claims, err = c.Claims.Init(&globalProvisionerClaims); err != nil { if c.Claims, err = c.Claims.Init(&globalProvisionerClaims); err != nil {
return err return err
} }
// Initialize provisioners
config := provisioner.Config{
Claims: *c.Claims,
Audiences: audiences,
}
for _, p := range c.Provisioners { for _, p := range c.Provisioners {
if err := p.Init(c.Claims); err != nil { if err := p.Init(config); err != nil {
return err return err
} }
} }
if c.Template == nil { if c.Template == nil {
c.Template = &x509util.ASN1DN{} c.Template = &x509util.ASN1DN{}
} }
@ -154,5 +161,16 @@ func (c *Config) Validate() error {
c.TLS.Renegotiation = c.TLS.Renegotiation || DefaultTLSOptions.Renegotiation c.TLS.Renegotiation = c.TLS.Renegotiation || DefaultTLSOptions.Renegotiation
} }
return c.AuthorityConfig.Validate() return c.AuthorityConfig.Validate(c.getAudiences())
}
// getAudiences returns the legacy and possible urls without the ports that will
// be used as the default provisioner audiences. The CA might have proxies in
// front so we cannot rely on the port.
func (c *Config) getAudiences() []string {
audiences := []string{legacyAuthority}
for _, name := range c.DNSNames {
audiences = append(audiences, fmt.Sprintf("https://%s/sign", name), fmt.Sprintf("https://%s/1.0/sign", name))
}
return audiences
} }

View file

@ -23,6 +23,7 @@ type JWT struct {
Key *jose.JSONWebKey `json:"key,omitempty"` Key *jose.JSONWebKey `json:"key,omitempty"`
EncryptedKey string `json:"encryptedKey,omitempty"` EncryptedKey string `json:"encryptedKey,omitempty"`
Claims *Claims `json:"claims,omitempty"` Claims *Claims `json:"claims,omitempty"`
audiences []string
} }
// GetID returns the provisioner unique identifier. The name and credential id // GetID returns the provisioner unique identifier. The name and credential id
@ -47,7 +48,7 @@ func (p *JWT) GetEncryptedKey() (string, string, bool) {
} }
// Init initializes and validates a the fields of Provisioner type. // Init initializes and validates a the fields of Provisioner type.
func (p *JWT) Init(global *Claims) (err error) { func (p *JWT) Init(config Config) (err error) {
switch { switch {
case p.Name == "": case p.Name == "":
return errors.New("provisioner name cannot be empty") return errors.New("provisioner name cannot be empty")
@ -58,10 +59,12 @@ func (p *JWT) Init(global *Claims) (err error) {
case p.Key == nil: case p.Key == nil:
return errors.New("provisioner key cannot be empty") return errors.New("provisioner key cannot be empty")
} }
p.Claims, err = p.Claims.Init(global) p.Claims, err = p.Claims.Init(&config.Claims)
p.audiences = config.Audiences
return err return err
} }
// Authorize validates the given token.
func (p *JWT) Authorize(token string) ([]SignOption, error) { func (p *JWT) Authorize(token string) ([]SignOption, error) {
jwt, err := jose.ParseSigned(token) jwt, err := jose.ParseSigned(token)
if err != nil { if err != nil {
@ -81,10 +84,10 @@ func (p *JWT) Authorize(token string) ([]SignOption, error) {
return nil, errors.Wrapf(err, "invalid token") return nil, errors.Wrapf(err, "invalid token")
} }
// if !matchesAudience(claims.Audience, a.audiences) { // validate audiences with the defaults
// return nil, &apiError{errors.New("authorize: token audience invalid"), http.StatusUnauthorized, if !matchesAudience(claims.Audience, p.audiences) {
// errContext} return nil, errors.New("invalid token: invalid audience claim (aud)")
// } }
if claims.Subject == "" { if claims.Subject == "" {
return nil, errors.New("token subject cannot be empty") return nil, errors.New("token subject cannot be empty")

View file

@ -73,7 +73,7 @@ func (o *OIDC) GetEncryptedKey() (kid string, key string, ok bool) {
} }
// Init validates and initializes the OIDC provider. // Init validates and initializes the OIDC provider.
func (o *OIDC) Init(global *Claims) (err error) { func (o *OIDC) Init(config Config) (err error) {
switch { switch {
case o.Name == "": case o.Name == "":
return errors.New("name cannot be empty") return errors.New("name cannot be empty")
@ -84,7 +84,7 @@ func (o *OIDC) Init(global *Claims) (err error) {
} }
// Update claims with global ones // Update claims with global ones
if o.Claims, err = o.Claims.Init(global); err != nil { if o.Claims, err = o.Claims.Init(&config.Claims); err != nil {
return err return err
} }
// Decode openid-configuration endpoint // Decode openid-configuration endpoint

View file

@ -14,7 +14,7 @@ type Interface interface {
GetName() string GetName() string
GetType() Type GetType() Type
GetEncryptedKey() (kid string, key string, ok bool) GetEncryptedKey() (kid string, key string, ok bool)
Init(claims *Claims) error Init(config Config) error
Authorize(token string) ([]SignOption, error) Authorize(token string) ([]SignOption, error)
AuthorizeRenewal(cert *x509.Certificate) error AuthorizeRenewal(cert *x509.Certificate) error
AuthorizeRevoke(token string) error AuthorizeRevoke(token string) error
@ -31,11 +31,20 @@ const (
TypeOIDC Type = 2 TypeOIDC Type = 2
) )
// Config defines the default parameters used in the initialization of
// provisioners.
type Config struct {
// Claims are the default claims.
Claims Claims
// Audiences are the audiences used in the default provisioner, (JWK).
Audiences []string
}
type provisioner struct { type provisioner struct {
Type string `json:"type"` Type string `json:"type"`
} }
// Provisioner implmements the provisioner.Interface on a base provisioner. It // Provisioner implements the provisioner.Interface on a base provisioner. It
// also implements custom marshalers and unmarshalers so different provisioners // also implements custom marshalers and unmarshalers so different provisioners
// can be represented in a configuration type. // can be represented in a configuration type.
type Provisioner struct { type Provisioner struct {
@ -76,8 +85,8 @@ func (p *Provisioner) GetType() Type {
} }
// Init initializes the base provisioner with the given claims. // Init initializes the base provisioner with the given claims.
func (p *Provisioner) Init(claims *Claims) error { func (p *Provisioner) Init(c Config) error {
return p.base.Init(claims) return p.base.Init(c)
} }
// Authorize validates the given token on the base provisioner returning a list // Authorize validates the given token on the base provisioner returning a list
@ -107,7 +116,7 @@ func (p *Provisioner) MarshalJSON() ([]byte, error) {
func (p *Provisioner) UnmarshalJSON(data []byte) error { func (p *Provisioner) UnmarshalJSON(data []byte) error {
var typ provisioner var typ provisioner
if err := json.Unmarshal(data, &typ); err != nil { if err := json.Unmarshal(data, &typ); err != nil {
return errors.Errorf("error unmarshalling provisioner") return errors.Errorf("error unmarshaling provisioner")
} }
switch strings.ToLower(typ.Type) { switch strings.ToLower(typ.Type) {
@ -119,7 +128,7 @@ func (p *Provisioner) UnmarshalJSON(data []byte) error {
return errors.Errorf("provisioner type %s not supported", typ.Type) return errors.Errorf("provisioner type %s not supported", typ.Type)
} }
if err := json.Unmarshal(data, &p.base); err != nil { if err := json.Unmarshal(data, &p.base); err != nil {
return errors.Errorf("error unmarshalling provisioner") return errors.Errorf("error unmarshaling provisioner")
} }
return nil return nil
} }