forked from TrueCloudLab/certificates
Merge pull request #248 from smallstep/embedded-config-init
Initialize the required config fields on embedded authorities
This commit is contained in:
commit
e855707dc2
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{}
|
||||||
Duration: defaultBackdate,
|
}
|
||||||
},
|
if c.Template == nil {
|
||||||
|
c.Template = &x509util.ASN1DN{}
|
||||||
|
}
|
||||||
|
if c.Backdate == nil {
|
||||||
|
c.Backdate = &provisioner.Duration{
|
||||||
|
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,16 +111,8 @@ 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 {
|
if c.Backdate.Duration < 0 {
|
||||||
c.Template = defaultAuthConfig.Template
|
return errors.New("authority.backdate cannot be less than 0")
|
||||||
}
|
|
||||||
|
|
||||||
if c.Backdate != nil {
|
|
||||||
if c.Backdate.Duration < 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