forked from TrueCloudLab/certificates
Allow root and federated root bundles
This commit changes the parsing of root and federated roots to support a bundle of certificates, this makes easier to configure a root rotation when using helm charts, just appending the old root.
This commit is contained in:
parent
e0215e7243
commit
ddd5057f63
2 changed files with 135 additions and 8 deletions
|
@ -413,13 +413,13 @@ func (a *Authority) init() error {
|
||||||
|
|
||||||
// Read root certificates and store them in the certificates map.
|
// Read root certificates and store them in the certificates map.
|
||||||
if len(a.rootX509Certs) == 0 {
|
if len(a.rootX509Certs) == 0 {
|
||||||
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
a.rootX509Certs = make([]*x509.Certificate, 0, len(a.config.Root))
|
||||||
for i, path := range a.config.Root {
|
for _, path := range a.config.Root {
|
||||||
crt, err := pemutil.ReadCertificate(path)
|
crts, err := pemutil.ReadCertificateBundle(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.rootX509Certs[i] = crt
|
a.rootX509Certs = append(a.rootX509Certs, crts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, crt := range a.rootX509Certs {
|
for _, crt := range a.rootX509Certs {
|
||||||
|
@ -434,13 +434,13 @@ func (a *Authority) init() error {
|
||||||
|
|
||||||
// Read federated certificates and store them in the certificates map.
|
// Read federated certificates and store them in the certificates map.
|
||||||
if len(a.federatedX509Certs) == 0 {
|
if len(a.federatedX509Certs) == 0 {
|
||||||
a.federatedX509Certs = make([]*x509.Certificate, len(a.config.FederatedRoots))
|
a.federatedX509Certs = make([]*x509.Certificate, 0, len(a.config.FederatedRoots))
|
||||||
for i, path := range a.config.FederatedRoots {
|
for _, path := range a.config.FederatedRoots {
|
||||||
crt, err := pemutil.ReadCertificate(path)
|
crts, err := pemutil.ReadCertificateBundle(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.federatedX509Certs[i] = crt
|
a.federatedX509Certs = append(a.federatedX509Certs, crts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, crt := range a.federatedX509Certs {
|
for _, crt := range a.federatedX509Certs {
|
||||||
|
|
|
@ -6,8 +6,10 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -18,6 +20,7 @@ import (
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
|
"go.step.sm/crypto/minica"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -172,6 +175,130 @@ func TestAuthorityNew(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthorityNew_bundles(t *testing.T) {
|
||||||
|
ca0, err := minica.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ca1, err := minica.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ca2, err := minica.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootPath := t.TempDir()
|
||||||
|
writeCert := func(fn string, certs ...*x509.Certificate) error {
|
||||||
|
var b []byte
|
||||||
|
for _, crt := range certs {
|
||||||
|
b = append(b, pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: crt.Raw,
|
||||||
|
})...)
|
||||||
|
}
|
||||||
|
return os.WriteFile(filepath.Join(rootPath, fn), b, 0600)
|
||||||
|
}
|
||||||
|
writeKey := func(fn string, signer crypto.Signer) error {
|
||||||
|
_, err := pemutil.Serialize(signer, pemutil.ToFile(filepath.Join(rootPath, fn), 0600))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeCert("root0.crt", ca0.Root); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeCert("int0.crt", ca0.Intermediate); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeKey("int0.key", ca0.Signer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeCert("root1.crt", ca1.Root); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeCert("int1.crt", ca1.Intermediate); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeKey("int1.key", ca1.Signer); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeCert("bundle0.crt", ca0.Root, ca1.Root); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writeCert("bundle1.crt", ca1.Root, ca2.Root); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
config *config.Config
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok ca0", &config.Config{
|
||||||
|
Address: "127.0.0.1:443",
|
||||||
|
Root: []string{filepath.Join(rootPath, "root0.crt")},
|
||||||
|
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
|
||||||
|
IntermediateKey: filepath.Join(rootPath, "int0.key"),
|
||||||
|
DNSNames: []string{"127.0.0.1"},
|
||||||
|
AuthorityConfig: &AuthConfig{},
|
||||||
|
}, false},
|
||||||
|
{"ok bundle", &config.Config{
|
||||||
|
Address: "127.0.0.1:443",
|
||||||
|
Root: []string{filepath.Join(rootPath, "bundle0.crt")},
|
||||||
|
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
|
||||||
|
IntermediateKey: filepath.Join(rootPath, "int0.key"),
|
||||||
|
DNSNames: []string{"127.0.0.1"},
|
||||||
|
AuthorityConfig: &AuthConfig{},
|
||||||
|
}, false},
|
||||||
|
{"ok federated ca1", &config.Config{
|
||||||
|
Address: "127.0.0.1:443",
|
||||||
|
Root: []string{filepath.Join(rootPath, "root0.crt")},
|
||||||
|
FederatedRoots: []string{filepath.Join(rootPath, "root1.crt")},
|
||||||
|
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
|
||||||
|
IntermediateKey: filepath.Join(rootPath, "int0.key"),
|
||||||
|
DNSNames: []string{"127.0.0.1"},
|
||||||
|
AuthorityConfig: &AuthConfig{},
|
||||||
|
}, false},
|
||||||
|
{"ok federated bundle", &config.Config{
|
||||||
|
Address: "127.0.0.1:443",
|
||||||
|
Root: []string{filepath.Join(rootPath, "root0.crt")},
|
||||||
|
FederatedRoots: []string{filepath.Join(rootPath, "bundle1.crt")},
|
||||||
|
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
|
||||||
|
IntermediateKey: filepath.Join(rootPath, "int0.key"),
|
||||||
|
DNSNames: []string{"127.0.0.1"},
|
||||||
|
AuthorityConfig: &AuthConfig{},
|
||||||
|
}, false},
|
||||||
|
{"fail root", &config.Config{
|
||||||
|
Address: "127.0.0.1:443",
|
||||||
|
Root: []string{filepath.Join(rootPath, "missing.crt")},
|
||||||
|
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
|
||||||
|
IntermediateKey: filepath.Join(rootPath, "int0.key"),
|
||||||
|
DNSNames: []string{"127.0.0.1"},
|
||||||
|
AuthorityConfig: &AuthConfig{},
|
||||||
|
}, true},
|
||||||
|
{"fail federated", &config.Config{
|
||||||
|
Address: "127.0.0.1:443",
|
||||||
|
Root: []string{filepath.Join(rootPath, "root0.crt")},
|
||||||
|
FederatedRoots: []string{filepath.Join(rootPath, "missing.crt")},
|
||||||
|
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
|
||||||
|
IntermediateKey: filepath.Join(rootPath, "int0.key"),
|
||||||
|
DNSNames: []string{"127.0.0.1"},
|
||||||
|
AuthorityConfig: &AuthConfig{},
|
||||||
|
}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := New(tt.config)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthority_GetDatabase(t *testing.T) {
|
func TestAuthority_GetDatabase(t *testing.T) {
|
||||||
auth := testAuthority(t)
|
auth := testAuthority(t)
|
||||||
authWithDatabase, err := New(auth.config, WithDatabase(auth.db))
|
authWithDatabase, err := New(auth.config, WithDatabase(auth.db))
|
||||||
|
|
Loading…
Reference in a new issue