Skip to content

Commit e6585da

Browse files
authored
Merge pull request #78 from knoppiks/multiple-ca-certs
Allow multiple (intermediate) CA certs
2 parents 4c1ac9b + 6cc9a67 commit e6585da

File tree

5 files changed

+70
-21
lines changed

5 files changed

+70
-21
lines changed

factory/ca.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,28 @@ func GenCA() (*x509.Certificate, crypto.Signer, error) {
2525
return caCert, caKey, nil
2626
}
2727

28+
// Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs
2829
func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) {
29-
cert, key, err := loadCA()
30+
chain, signer, err := LoadOrGenCAChain()
31+
if err != nil {
32+
return nil, nil, err
33+
}
34+
return chain[0], signer, err
35+
}
36+
37+
func LoadOrGenCAChain() ([]*x509.Certificate, crypto.Signer, error) {
38+
certs, key, err := loadCA()
3039
if err == nil {
31-
return cert, key, nil
40+
return certs, key, nil
3241
}
3342

34-
cert, key, err = GenCA()
43+
cert, key, err := GenCA()
3544
if err != nil {
3645
return nil, nil, err
3746
}
47+
certs = []*x509.Certificate{cert}
3848

39-
certBytes, keyBytes, err := Marshal(cert, key)
49+
certBytes, keyBytes, err := MarshalChain(key, certs...)
4050
if err != nil {
4151
return nil, nil, err
4252
}
@@ -53,14 +63,22 @@ func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) {
5363
return nil, nil, err
5464
}
5565

56-
return cert, key, nil
66+
return certs, key, nil
5767
}
5868

59-
func loadCA() (*x509.Certificate, crypto.Signer, error) {
60-
return LoadCerts("./certs/ca.pem", "./certs/ca.key")
69+
func loadCA() ([]*x509.Certificate, crypto.Signer, error) {
70+
return LoadCertsChain("./certs/ca.pem", "./certs/ca.key")
6171
}
6272

6373
func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) {
74+
chain, signer, err := LoadCAChain(caPem, caKey)
75+
if err != nil {
76+
return nil, nil, err
77+
}
78+
return chain[0], signer, nil
79+
}
80+
81+
func LoadCAChain(caPem, caKey []byte) ([]*x509.Certificate, crypto.Signer, error) {
6482
key, err := cert.ParsePrivateKeyPEM(caKey)
6583
if err != nil {
6684
return nil, nil, err
@@ -70,15 +88,24 @@ func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) {
7088
return nil, nil, fmt.Errorf("key is not a crypto.Signer")
7189
}
7290

73-
cert, err := ParseCertPEM(caPem)
91+
certs, err := cert.ParseCertsPEM(caPem)
7492
if err != nil {
7593
return nil, nil, err
7694
}
7795

78-
return cert, signer, nil
96+
return certs, signer, nil
7997
}
8098

99+
// Deprecated: Use LoadCertsChain instead as it supports intermediate CAs
81100
func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, error) {
101+
chain, signer, err := LoadCertsChain(certFile, keyFile)
102+
if err != nil {
103+
return nil, nil, err
104+
}
105+
return chain[0], signer, err
106+
}
107+
108+
func LoadCertsChain(certFile, keyFile string) ([]*x509.Certificate, crypto.Signer, error) {
82109
caPem, err := ioutil.ReadFile(certFile)
83110
if err != nil {
84111
return nil, nil, err
@@ -88,5 +115,5 @@ func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, erro
88115
return nil, nil, err
89116
}
90117

91-
return LoadCA(caPem, caKey)
118+
return LoadCAChain(caPem, caKey)
92119
}

factory/gen.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ var (
3333
)
3434

3535
type TLS struct {
36-
CACert *x509.Certificate
36+
CACert []*x509.Certificate
3737
CAKey crypto.Signer
3838
CN string
3939
Organization []string
@@ -178,7 +178,7 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e
178178
return nil, false, err
179179
}
180180

181-
keyBytes, certBytes, err := MarshalChain(privateKey, newCert, t.CACert)
181+
keyBytes, certBytes, err := MarshalChain(privateKey, append([]*x509.Certificate{newCert}, t.CACert...)...)
182182
if err != nil {
183183
return nil, false, err
184184
}
@@ -226,14 +226,16 @@ func (t *TLS) Verify(secret *v1.Secret) error {
226226
x509.ExtKeyUsageAny,
227227
},
228228
}
229-
verifyOpts.Roots.AddCert(t.CACert)
229+
for _, c := range t.CACert {
230+
verifyOpts.Roots.AddCert(c)
231+
}
230232

231233
_, err = certificates[0].Verify(verifyOpts)
232234
return err
233235
}
234236

235237
func (t *TLS) newCert(domains []string, ips []net.IP, privateKey crypto.Signer) (*x509.Certificate, error) {
236-
return NewSignedCert(privateKey, t.CACert, t.CAKey, t.CN, t.Organization, domains, ips)
238+
return NewSignedCert(privateKey, t.CACert[0], t.CAKey, t.CN, t.Organization, domains, ips)
237239
}
238240

239241
func populateCN(secret *v1.Secret, cn ...string) *v1.Secret {

listener.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ type SetFactory interface {
3434
SetFactory(tls TLSFactory)
3535
}
3636

37+
// Deprecated: Use NewListenerWithChain instead as it supports intermediate CAs
3738
func NewListener(l net.Listener, storage TLSStorage, caCert *x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) {
39+
return NewListenerWithChain(l, storage, []*x509.Certificate{caCert}, caKey, config)
40+
}
41+
42+
func NewListenerWithChain(l net.Listener, storage TLSStorage, caCert []*x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) {
3843
if config.CN == "" {
3944
config.CN = "dynamic"
4045
}

server/server.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
)
2222

2323
type ListenOpts struct {
24+
CAChain []*x509.Certificate
25+
// Deprecated: Use CAChain instead
2426
CA *x509.Certificate
2527
CAKey crypto.Signer
2628
Storage dynamiclistener.TLSStorage
@@ -132,21 +134,25 @@ func getTLSListener(ctx context.Context, tcp net.Listener, handler http.Handler,
132134
return nil, nil, err
133135
}
134136

135-
listener, dynHandler, err := dynamiclistener.NewListener(tcp, storage, caCert, caKey, opts.TLSListenerConfig)
137+
listener, dynHandler, err := dynamiclistener.NewListener2(tcp, storage, caCert, caKey, opts.TLSListenerConfig)
136138
if err != nil {
137139
return nil, nil, err
138140
}
139141

140142
return listener, wrapHandler(dynHandler, handler), nil
141143
}
142144

143-
func getCA(opts ListenOpts) (*x509.Certificate, crypto.Signer, error) {
144-
if opts.CA != nil && opts.CAKey != nil {
145-
return opts.CA, opts.CAKey, nil
145+
func getCA(opts ListenOpts) ([]*x509.Certificate, crypto.Signer, error) {
146+
if opts.CAKey != nil {
147+
if opts.CAChain != nil {
148+
return opts.CAChain, opts.CAKey, nil
149+
} else if opts.CA != nil {
150+
return []*x509.Certificate{opts.CA}, opts.CAKey, nil
151+
}
146152
}
147153

148154
if opts.Secrets == nil {
149-
return factory.LoadOrGenCA()
155+
return factory.LoadOrGenCAChain()
150156
}
151157

152158
if opts.CAName == "" {
@@ -161,7 +167,7 @@ func getCA(opts ListenOpts) (*x509.Certificate, crypto.Signer, error) {
161167
opts.CANamespace = "kube-system"
162168
}
163169

164-
return kubernetes.LoadOrGenCA(opts.Secrets, opts.CANamespace, opts.CAName)
170+
return kubernetes.LoadOrGenCAChain(opts.Secrets, opts.CANamespace, opts.CAName)
165171
}
166172

167173
func newStorage(ctx context.Context, opts ListenOpts) dynamiclistener.TLSStorage {

storage/kubernetes/ca.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,21 @@ import (
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1212
)
1313

14+
// Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs
1415
func LoadOrGenCA(secrets v1controller.SecretClient, namespace, name string) (*x509.Certificate, crypto.Signer, error) {
16+
chain, signer, err := LoadOrGenCAChain(secrets, namespace, name)
17+
if err != nil {
18+
return nil, nil, err
19+
}
20+
return chain[0], signer, err
21+
}
22+
23+
func LoadOrGenCAChain(secrets v1controller.SecretClient, namespace, name string) ([]*x509.Certificate, crypto.Signer, error) {
1524
secret, err := getSecret(secrets, namespace, name)
1625
if err != nil {
1726
return nil, nil, err
1827
}
19-
return factory.LoadCA(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey])
28+
return factory.LoadCAChain(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey])
2029
}
2130

2231
func LoadOrGenClient(secrets v1controller.SecretClient, namespace, name, cn string, ca *x509.Certificate, key crypto.Signer) (*x509.Certificate, crypto.Signer, error) {

0 commit comments

Comments
 (0)