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/x509"
"encoding/hex"
"fmt"
"sync"
"time"
@ -25,7 +24,6 @@ type Authority struct {
ottMap *sync.Map
startTime time.Time
provisioners *provisioner.Collection
audiences []string
// Do not re-initialize
initOnce bool
}
@ -37,19 +35,11 @@ func New(config *Config) (*Authority, error) {
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{
config: config,
certificates: new(sync.Map),
ottMap: new(sync.Map),
provisioners: provisioner.NewCollection(audiences),
audiences: audiences,
provisioners: provisioner.NewCollection(config.getAudiences()),
}
if err := a.init(); err != nil {
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.
p, ok := a.provisioners.LoadByToken(token, &claims.Claims)
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}
}

View file

@ -2,6 +2,7 @@ package authority
import (
"encoding/json"
"fmt"
"net"
"os"
"time"
@ -58,9 +59,8 @@ type AuthConfig struct {
}
// Validate validates the authority configuration.
func (c *AuthConfig) Validate() error {
func (c *AuthConfig) Validate(audiences []string) error {
var err error
if c == nil {
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 {
return err
}
// Initialize provisioners
config := provisioner.Config{
Claims: *c.Claims,
Audiences: audiences,
}
for _, p := range c.Provisioners {
if err := p.Init(c.Claims); err != nil {
if err := p.Init(config); err != nil {
return err
}
}
if c.Template == nil {
c.Template = &x509util.ASN1DN{}
}
@ -154,5 +161,16 @@ func (c *Config) Validate() error {
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"`
EncryptedKey string `json:"encryptedKey,omitempty"`
Claims *Claims `json:"claims,omitempty"`
audiences []string
}
// 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.
func (p *JWT) Init(global *Claims) (err error) {
func (p *JWT) Init(config Config) (err error) {
switch {
case p.Name == "":
return errors.New("provisioner name cannot be empty")
@ -58,10 +59,12 @@ func (p *JWT) Init(global *Claims) (err error) {
case p.Key == nil:
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
}
// Authorize validates the given token.
func (p *JWT) Authorize(token string) ([]SignOption, error) {
jwt, err := jose.ParseSigned(token)
if err != nil {
@ -81,10 +84,10 @@ func (p *JWT) Authorize(token string) ([]SignOption, error) {
return nil, errors.Wrapf(err, "invalid token")
}
// if !matchesAudience(claims.Audience, a.audiences) {
// return nil, &apiError{errors.New("authorize: token audience invalid"), http.StatusUnauthorized,
// errContext}
// }
// validate audiences with the defaults
if !matchesAudience(claims.Audience, p.audiences) {
return nil, errors.New("invalid token: invalid audience claim (aud)")
}
if claims.Subject == "" {
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.
func (o *OIDC) Init(global *Claims) (err error) {
func (o *OIDC) Init(config Config) (err error) {
switch {
case o.Name == "":
return errors.New("name cannot be empty")
@ -84,7 +84,7 @@ func (o *OIDC) Init(global *Claims) (err error) {
}
// 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
}
// Decode openid-configuration endpoint

View file

@ -14,7 +14,7 @@ type Interface interface {
GetName() string
GetType() Type
GetEncryptedKey() (kid string, key string, ok bool)
Init(claims *Claims) error
Init(config Config) error
Authorize(token string) ([]SignOption, error)
AuthorizeRenewal(cert *x509.Certificate) error
AuthorizeRevoke(token string) error
@ -31,11 +31,20 @@ const (
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 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
// can be represented in a configuration type.
type Provisioner struct {
@ -76,8 +85,8 @@ func (p *Provisioner) GetType() Type {
}
// Init initializes the base provisioner with the given claims.
func (p *Provisioner) Init(claims *Claims) error {
return p.base.Init(claims)
func (p *Provisioner) Init(c Config) error {
return p.base.Init(c)
}
// 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 {
var typ provisioner
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) {
@ -119,7 +128,7 @@ func (p *Provisioner) UnmarshalJSON(data []byte) error {
return errors.Errorf("provisioner type %s not supported", typ.Type)
}
if err := json.Unmarshal(data, &p.base); err != nil {
return errors.Errorf("error unmarshalling provisioner")
return errors.Errorf("error unmarshaling provisioner")
}
return nil
}