Skip to content

Commit fa8d0a6

Browse files
authored
Merge pull request smallstep#1169 from smallstep/root-bundle
Allow root and federated root bundles
2 parents e0215e7 + ddd5057 commit fa8d0a6

File tree

2 files changed

+135
-8
lines changed

2 files changed

+135
-8
lines changed

authority/authority.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -413,13 +413,13 @@ func (a *Authority) init() error {
413413

414414
// Read root certificates and store them in the certificates map.
415415
if len(a.rootX509Certs) == 0 {
416-
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
417-
for i, path := range a.config.Root {
418-
crt, err := pemutil.ReadCertificate(path)
416+
a.rootX509Certs = make([]*x509.Certificate, 0, len(a.config.Root))
417+
for _, path := range a.config.Root {
418+
crts, err := pemutil.ReadCertificateBundle(path)
419419
if err != nil {
420420
return err
421421
}
422-
a.rootX509Certs[i] = crt
422+
a.rootX509Certs = append(a.rootX509Certs, crts...)
423423
}
424424
}
425425
for _, crt := range a.rootX509Certs {
@@ -434,13 +434,13 @@ func (a *Authority) init() error {
434434

435435
// Read federated certificates and store them in the certificates map.
436436
if len(a.federatedX509Certs) == 0 {
437-
a.federatedX509Certs = make([]*x509.Certificate, len(a.config.FederatedRoots))
438-
for i, path := range a.config.FederatedRoots {
439-
crt, err := pemutil.ReadCertificate(path)
437+
a.federatedX509Certs = make([]*x509.Certificate, 0, len(a.config.FederatedRoots))
438+
for _, path := range a.config.FederatedRoots {
439+
crts, err := pemutil.ReadCertificateBundle(path)
440440
if err != nil {
441441
return err
442442
}
443-
a.federatedX509Certs[i] = crt
443+
a.federatedX509Certs = append(a.federatedX509Certs, crts...)
444444
}
445445
}
446446
for _, crt := range a.federatedX509Certs {

authority/authority_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"crypto/sha256"
77
"crypto/x509"
88
"encoding/hex"
9+
"encoding/pem"
910
"net"
1011
"os"
12+
"path/filepath"
1113
"reflect"
1214
"testing"
1315
"time"
@@ -18,6 +20,7 @@ import (
1820
"github.com/smallstep/certificates/authority/provisioner"
1921
"github.com/smallstep/certificates/db"
2022
"go.step.sm/crypto/jose"
23+
"go.step.sm/crypto/minica"
2124
"go.step.sm/crypto/pemutil"
2225
)
2326

@@ -172,6 +175,130 @@ func TestAuthorityNew(t *testing.T) {
172175
}
173176
}
174177

178+
func TestAuthorityNew_bundles(t *testing.T) {
179+
ca0, err := minica.New()
180+
if err != nil {
181+
t.Fatal(err)
182+
}
183+
ca1, err := minica.New()
184+
if err != nil {
185+
t.Fatal(err)
186+
}
187+
ca2, err := minica.New()
188+
if err != nil {
189+
t.Fatal(err)
190+
}
191+
192+
rootPath := t.TempDir()
193+
writeCert := func(fn string, certs ...*x509.Certificate) error {
194+
var b []byte
195+
for _, crt := range certs {
196+
b = append(b, pem.EncodeToMemory(&pem.Block{
197+
Type: "CERTIFICATE",
198+
Bytes: crt.Raw,
199+
})...)
200+
}
201+
return os.WriteFile(filepath.Join(rootPath, fn), b, 0600)
202+
}
203+
writeKey := func(fn string, signer crypto.Signer) error {
204+
_, err := pemutil.Serialize(signer, pemutil.ToFile(filepath.Join(rootPath, fn), 0600))
205+
return err
206+
}
207+
208+
if err := writeCert("root0.crt", ca0.Root); err != nil {
209+
t.Fatal(err)
210+
}
211+
if err := writeCert("int0.crt", ca0.Intermediate); err != nil {
212+
t.Fatal(err)
213+
}
214+
if err := writeKey("int0.key", ca0.Signer); err != nil {
215+
t.Fatal(err)
216+
}
217+
if err := writeCert("root1.crt", ca1.Root); err != nil {
218+
t.Fatal(err)
219+
}
220+
if err := writeCert("int1.crt", ca1.Intermediate); err != nil {
221+
t.Fatal(err)
222+
}
223+
if err := writeKey("int1.key", ca1.Signer); err != nil {
224+
t.Fatal(err)
225+
}
226+
if err := writeCert("bundle0.crt", ca0.Root, ca1.Root); err != nil {
227+
t.Fatal(err)
228+
}
229+
if err := writeCert("bundle1.crt", ca1.Root, ca2.Root); err != nil {
230+
t.Fatal(err)
231+
}
232+
233+
tests := []struct {
234+
name string
235+
config *config.Config
236+
wantErr bool
237+
}{
238+
{"ok ca0", &config.Config{
239+
Address: "127.0.0.1:443",
240+
Root: []string{filepath.Join(rootPath, "root0.crt")},
241+
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
242+
IntermediateKey: filepath.Join(rootPath, "int0.key"),
243+
DNSNames: []string{"127.0.0.1"},
244+
AuthorityConfig: &AuthConfig{},
245+
}, false},
246+
{"ok bundle", &config.Config{
247+
Address: "127.0.0.1:443",
248+
Root: []string{filepath.Join(rootPath, "bundle0.crt")},
249+
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
250+
IntermediateKey: filepath.Join(rootPath, "int0.key"),
251+
DNSNames: []string{"127.0.0.1"},
252+
AuthorityConfig: &AuthConfig{},
253+
}, false},
254+
{"ok federated ca1", &config.Config{
255+
Address: "127.0.0.1:443",
256+
Root: []string{filepath.Join(rootPath, "root0.crt")},
257+
FederatedRoots: []string{filepath.Join(rootPath, "root1.crt")},
258+
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
259+
IntermediateKey: filepath.Join(rootPath, "int0.key"),
260+
DNSNames: []string{"127.0.0.1"},
261+
AuthorityConfig: &AuthConfig{},
262+
}, false},
263+
{"ok federated bundle", &config.Config{
264+
Address: "127.0.0.1:443",
265+
Root: []string{filepath.Join(rootPath, "root0.crt")},
266+
FederatedRoots: []string{filepath.Join(rootPath, "bundle1.crt")},
267+
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
268+
IntermediateKey: filepath.Join(rootPath, "int0.key"),
269+
DNSNames: []string{"127.0.0.1"},
270+
AuthorityConfig: &AuthConfig{},
271+
}, false},
272+
{"fail root", &config.Config{
273+
Address: "127.0.0.1:443",
274+
Root: []string{filepath.Join(rootPath, "missing.crt")},
275+
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
276+
IntermediateKey: filepath.Join(rootPath, "int0.key"),
277+
DNSNames: []string{"127.0.0.1"},
278+
AuthorityConfig: &AuthConfig{},
279+
}, true},
280+
{"fail federated", &config.Config{
281+
Address: "127.0.0.1:443",
282+
Root: []string{filepath.Join(rootPath, "root0.crt")},
283+
FederatedRoots: []string{filepath.Join(rootPath, "missing.crt")},
284+
IntermediateCert: filepath.Join(rootPath, "int0.crt"),
285+
IntermediateKey: filepath.Join(rootPath, "int0.key"),
286+
DNSNames: []string{"127.0.0.1"},
287+
AuthorityConfig: &AuthConfig{},
288+
}, true},
289+
}
290+
291+
for _, tt := range tests {
292+
t.Run(tt.name, func(t *testing.T) {
293+
_, err := New(tt.config)
294+
if (err != nil) != tt.wantErr {
295+
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
296+
return
297+
}
298+
})
299+
}
300+
}
301+
175302
func TestAuthority_GetDatabase(t *testing.T) {
176303
auth := testAuthority(t)
177304
authWithDatabase, err := New(auth.config, WithDatabase(auth.db))

0 commit comments

Comments
 (0)