From 4e544344f9cc786bdd9b7e63ece7dfde55aba34f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 6 May 2020 13:00:42 -0700 Subject: [PATCH] Initialize the required config fields on embedded authorities. This change is to make easier the use of embedded authorities. It can be difficult for third parties to know what fields are required. The new init methods will define the minimum usable configuration. --- authority/authority.go | 18 ++++++------- authority/authority_test.go | 2 ++ authority/config.go | 51 ++++++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 411787b6..828adf2f 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -67,7 +67,6 @@ func New(config *Config, opts ...Option) (*Authority, error) { var a = &Authority{ config: config, certificates: new(sync.Map), - provisioners: provisioner.NewCollection(config.getAudiences()), } // Apply options. @@ -88,15 +87,9 @@ func New(config *Config, opts ...Option) (*Authority, error) { // NewEmbedded initializes an authority that can be embedded in a different // project without the limitations of the config. func NewEmbedded(opts ...Option) (*Authority, error) { - config := &Config{ - DNSNames: []string{"localhost", "127.0.0.1", "::1"}, - AuthorityConfig: defaultAuthConfig, - TLS: &DefaultTLSOptions, - } a := &Authority{ - config: config, + config: &Config{}, certificates: new(sync.Map), - provisioners: provisioner.NewCollection(config.getAudiences()), } // Apply options. @@ -108,6 +101,8 @@ func NewEmbedded(opts ...Option) (*Authority, error) { // Validate required options switch { + case a.config == nil: + return nil, errors.New("cannot create an authority without a configuration") case len(a.rootX509Certs) == 0 && a.config.Root.HasEmpties(): return nil, errors.New("cannot create an authority without a root certificate") case a.x509Issuer == nil && a.config.IntermediateCert == "": @@ -116,6 +111,9 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return nil, errors.New("cannot create an authority without an issuer signer") } + // Initialize config required fields. + a.config.init() + // Initialize authority from options or configuration. if err := a.init(); err != nil { return nil, err @@ -271,9 +269,11 @@ func (a *Authority) init() error { return err } // Initialize provisioners + audiences := a.config.getAudiences() + a.provisioners = provisioner.NewCollection(audiences) config := provisioner.Config{ Claims: claimer.Claims(), - Audiences: a.config.getAudiences(), + Audiences: audiences, DB: a.db, SSHKeys: &provisioner.SSHKeys{ UserKeys: sshKeys.UserKeys, diff --git a/authority/authority_test.go b/authority/authority_test.go index b8cab30c..3ab3e142 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -207,6 +207,7 @@ func TestNewEmbedded(t *testing.T) { wantErr bool }{ {"ok", args{[]Option{WithX509RootBundle(caPEM), WithX509Signer(crt, key.(crypto.Signer))}}, false}, + {"ok empty config", args{[]Option{WithConfig(&Config{}), WithX509RootBundle(caPEM), WithX509Signer(crt, key.(crypto.Signer))}}, false}, {"ok config file", args{[]Option{WithConfigFile("../ca/testdata/ca.json")}}, false}, {"ok config", args{[]Option{WithConfig(&Config{ Root: []string{"testdata/certs/root_ca.crt"}, @@ -216,6 +217,7 @@ func TestNewEmbedded(t *testing.T) { AuthorityConfig: &AuthConfig{}, })}}, false}, {"fail options", args{[]Option{WithX509RootBundle([]byte("bad data"))}}, true}, + {"fail missing config", args{[]Option{WithConfig(nil), WithX509RootBundle(caPEM), WithX509Signer(crt, key.(crypto.Signer))}}, true}, {"fail missing root", args{[]Option{WithX509Signer(crt, key.(crypto.Signer))}}, true}, {"fail missing signer", args{[]Option{WithX509RootBundle(caPEM)}}, true}, {"fail missing root file", args{[]Option{WithConfig(&Config{ diff --git a/authority/config.go b/authority/config.go index 5d951853..a26d19ad 100644 --- a/authority/config.go +++ b/authority/config.go @@ -75,13 +75,20 @@ type AuthConfig struct { Backdate *provisioner.Duration `json:"backdate,omitempty"` } -// defaultAuthConfig used when skipping validation. -var defaultAuthConfig = &AuthConfig{ - Provisioners: provisioner.List{}, - Template: &x509util.ASN1DN{}, - Backdate: &provisioner.Duration{ - Duration: defaultBackdate, - }, +// init initializes the required fields in the AuthConfig if they are not +// provided. +func (c *AuthConfig) init() { + if c.Provisioners == nil { + c.Provisioners = provisioner.List{} + } + if c.Template == nil { + c.Template = &x509util.ASN1DN{} + } + if c.Backdate == nil { + c.Backdate = &provisioner.Duration{ + Duration: defaultBackdate, + } + } } // Validate validates the authority configuration. @@ -90,6 +97,9 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error { return errors.New("authority cannot be undefined") } + // Initialize required fields. + c.init() + // Check that only one K8sSA is enabled var k8sCount int for _, p := range c.Provisioners { @@ -101,16 +111,8 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error { return errors.New("cannot have more than one kubernetes service account provisioner") } - if c.Template == nil { - c.Template = defaultAuthConfig.Template - } - - if c.Backdate != nil { - if c.Backdate.Duration < 0 { - return errors.New("authority.backdate cannot be less than 0") - } - } else { - c.Backdate = defaultAuthConfig.Backdate + if c.Backdate.Duration < 0 { + return errors.New("authority.backdate cannot be less than 0") } return nil @@ -133,6 +135,21 @@ func LoadConfiguration(filename string) (*Config, error) { return &c, nil } +// initializes the minimal configuration required to create an authority. This +// is mainly used on embedded authorities. +func (c *Config) init() { + if c.DNSNames == nil { + c.DNSNames = []string{"localhost", "127.0.0.1", "::1"} + } + if c.TLS == nil { + c.TLS = &DefaultTLSOptions + } + if c.AuthorityConfig == nil { + c.AuthorityConfig = &AuthConfig{} + } + c.AuthorityConfig.init() +} + // Save saves the configuration to the given filename. func (c *Config) Save(filename string) error { f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)