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.
This commit is contained in:
parent
9499aed6d1
commit
4e544344f9
3 changed files with 45 additions and 26 deletions
|
@ -67,7 +67,6 @@ func New(config *Config, opts ...Option) (*Authority, error) {
|
||||||
var a = &Authority{
|
var a = &Authority{
|
||||||
config: config,
|
config: config,
|
||||||
certificates: new(sync.Map),
|
certificates: new(sync.Map),
|
||||||
provisioners: provisioner.NewCollection(config.getAudiences()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply options.
|
// 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
|
// NewEmbedded initializes an authority that can be embedded in a different
|
||||||
// project without the limitations of the config.
|
// project without the limitations of the config.
|
||||||
func NewEmbedded(opts ...Option) (*Authority, error) {
|
func NewEmbedded(opts ...Option) (*Authority, error) {
|
||||||
config := &Config{
|
|
||||||
DNSNames: []string{"localhost", "127.0.0.1", "::1"},
|
|
||||||
AuthorityConfig: defaultAuthConfig,
|
|
||||||
TLS: &DefaultTLSOptions,
|
|
||||||
}
|
|
||||||
a := &Authority{
|
a := &Authority{
|
||||||
config: config,
|
config: &Config{},
|
||||||
certificates: new(sync.Map),
|
certificates: new(sync.Map),
|
||||||
provisioners: provisioner.NewCollection(config.getAudiences()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply options.
|
// Apply options.
|
||||||
|
@ -108,6 +101,8 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
|
||||||
|
|
||||||
// Validate required options
|
// Validate required options
|
||||||
switch {
|
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():
|
case len(a.rootX509Certs) == 0 && a.config.Root.HasEmpties():
|
||||||
return nil, errors.New("cannot create an authority without a root certificate")
|
return nil, errors.New("cannot create an authority without a root certificate")
|
||||||
case a.x509Issuer == nil && a.config.IntermediateCert == "":
|
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")
|
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.
|
// Initialize authority from options or configuration.
|
||||||
if err := a.init(); err != nil {
|
if err := a.init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -271,9 +269,11 @@ func (a *Authority) init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Initialize provisioners
|
// Initialize provisioners
|
||||||
|
audiences := a.config.getAudiences()
|
||||||
|
a.provisioners = provisioner.NewCollection(audiences)
|
||||||
config := provisioner.Config{
|
config := provisioner.Config{
|
||||||
Claims: claimer.Claims(),
|
Claims: claimer.Claims(),
|
||||||
Audiences: a.config.getAudiences(),
|
Audiences: audiences,
|
||||||
DB: a.db,
|
DB: a.db,
|
||||||
SSHKeys: &provisioner.SSHKeys{
|
SSHKeys: &provisioner.SSHKeys{
|
||||||
UserKeys: sshKeys.UserKeys,
|
UserKeys: sshKeys.UserKeys,
|
||||||
|
|
|
@ -207,6 +207,7 @@ func TestNewEmbedded(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{[]Option{WithX509RootBundle(caPEM), WithX509Signer(crt, key.(crypto.Signer))}}, false},
|
{"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 file", args{[]Option{WithConfigFile("../ca/testdata/ca.json")}}, false},
|
||||||
{"ok config", args{[]Option{WithConfig(&Config{
|
{"ok config", args{[]Option{WithConfig(&Config{
|
||||||
Root: []string{"testdata/certs/root_ca.crt"},
|
Root: []string{"testdata/certs/root_ca.crt"},
|
||||||
|
@ -216,6 +217,7 @@ func TestNewEmbedded(t *testing.T) {
|
||||||
AuthorityConfig: &AuthConfig{},
|
AuthorityConfig: &AuthConfig{},
|
||||||
})}}, false},
|
})}}, false},
|
||||||
{"fail options", args{[]Option{WithX509RootBundle([]byte("bad data"))}}, true},
|
{"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 root", args{[]Option{WithX509Signer(crt, key.(crypto.Signer))}}, true},
|
||||||
{"fail missing signer", args{[]Option{WithX509RootBundle(caPEM)}}, true},
|
{"fail missing signer", args{[]Option{WithX509RootBundle(caPEM)}}, true},
|
||||||
{"fail missing root file", args{[]Option{WithConfig(&Config{
|
{"fail missing root file", args{[]Option{WithConfig(&Config{
|
||||||
|
|
|
@ -75,13 +75,20 @@ type AuthConfig struct {
|
||||||
Backdate *provisioner.Duration `json:"backdate,omitempty"`
|
Backdate *provisioner.Duration `json:"backdate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultAuthConfig used when skipping validation.
|
// init initializes the required fields in the AuthConfig if they are not
|
||||||
var defaultAuthConfig = &AuthConfig{
|
// provided.
|
||||||
Provisioners: provisioner.List{},
|
func (c *AuthConfig) init() {
|
||||||
Template: &x509util.ASN1DN{},
|
if c.Provisioners == nil {
|
||||||
Backdate: &provisioner.Duration{
|
c.Provisioners = provisioner.List{}
|
||||||
|
}
|
||||||
|
if c.Template == nil {
|
||||||
|
c.Template = &x509util.ASN1DN{}
|
||||||
|
}
|
||||||
|
if c.Backdate == nil {
|
||||||
|
c.Backdate = &provisioner.Duration{
|
||||||
Duration: defaultBackdate,
|
Duration: defaultBackdate,
|
||||||
},
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the authority configuration.
|
// Validate validates the authority configuration.
|
||||||
|
@ -90,6 +97,9 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error {
|
||||||
return errors.New("authority cannot be undefined")
|
return errors.New("authority cannot be undefined")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize required fields.
|
||||||
|
c.init()
|
||||||
|
|
||||||
// Check that only one K8sSA is enabled
|
// Check that only one K8sSA is enabled
|
||||||
var k8sCount int
|
var k8sCount int
|
||||||
for _, p := range c.Provisioners {
|
for _, p := range c.Provisioners {
|
||||||
|
@ -101,17 +111,9 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error {
|
||||||
return errors.New("cannot have more than one kubernetes service account provisioner")
|
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 {
|
if c.Backdate.Duration < 0 {
|
||||||
return errors.New("authority.backdate cannot be less than 0")
|
return errors.New("authority.backdate cannot be less than 0")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
c.Backdate = defaultAuthConfig.Backdate
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -133,6 +135,21 @@ func LoadConfiguration(filename string) (*Config, error) {
|
||||||
return &c, nil
|
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.
|
// Save saves the configuration to the given filename.
|
||||||
func (c *Config) Save(filename string) error {
|
func (c *Config) Save(filename string) error {
|
||||||
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
|
Loading…
Reference in a new issue