Skip to content

Commit 3fbc205

Browse files
authored
[Improvement] Reduce TLS rotation time (#592)
1 parent 9159f70 commit 3fbc205

File tree

13 files changed

+325
-143
lines changed

13 files changed

+325
-143
lines changed

pkg/deployment/reconcile/action_tls_ca_append.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (a *appendTLSCACertificateAction) Start(ctx context.Context) (bool, error)
7878
return true, nil
7979
}
8080

81-
ca, _, err := getKeyCertFromSecret(a.log, caSecret, resources.CACertName, resources.CAKeyName)
81+
ca, _, err := resources.GetKeyCertFromSecret(a.log, caSecret, resources.CACertName, resources.CAKeyName)
8282
if err != nil {
8383
a.log.Warn().Err(err).Msgf("Cert %s is invalid", resources.GetCASecretName(a.actionCtx.GetAPIObject()))
8484
return true, nil

pkg/deployment/reconcile/action_tls_ca_clean.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (a *cleanTLSCACertificateAction) Start(ctx context.Context) (bool, error) {
7979
return true, nil
8080
}
8181

82-
ca, _, err := getKeyCertFromSecret(a.log, caSecret, resources.CACertName, resources.CAKeyName)
82+
ca, _, err := resources.GetKeyCertFromSecret(a.log, caSecret, resources.CACertName, resources.CAKeyName)
8383
if err != nil {
8484
a.log.Warn().Err(err).Msgf("Cert %s is invalid", resources.GetCASecretName(a.actionCtx.GetAPIObject()))
8585
return true, nil

pkg/deployment/reconcile/action_tls_status_update.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,20 @@ func (a *tlsKeyStatusUpdateAction) Start(ctx context.Context) (bool, error) {
6565
keyHashes := secretKeysToListWithPrefix("sha256:", f)
6666

6767
if err = a.actionCtx.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
68+
r := false
6869
if len(keyHashes) == 1 {
6970
if s.Hashes.TLS.CA == nil || *s.Hashes.TLS.CA != keyHashes[0] {
7071
s.Hashes.TLS.CA = util.NewString(keyHashes[0])
71-
return true
72+
r = true
7273
}
7374
}
7475

7576
if !util.CompareStringArray(keyHashes, s.Hashes.TLS.Truststore) {
7677
s.Hashes.TLS.Truststore = keyHashes
77-
return true
78+
r = true
7879
}
7980

80-
return false
81+
return r
8182
}); err != nil {
8283
return false, err
8384
}

pkg/deployment/reconcile/plan_builder_tls.go

Lines changed: 6 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,9 @@
2323
package reconcile
2424

2525
import (
26-
"bytes"
2726
"context"
2827
"crypto/tls"
2928
"crypto/x509"
30-
"encoding/pem"
3129
"fmt"
3230
"net/http"
3331
"net/url"
@@ -37,129 +35,16 @@ import (
3735
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
3836
"github.com/arangodb/kube-arangodb/pkg/util/constants"
3937

40-
"github.com/arangodb-helper/go-certificates"
38+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
4139
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
4240
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
4341
"github.com/arangodb/kube-arangodb/pkg/util"
44-
"github.com/pkg/errors"
45-
core "k8s.io/api/core/v1"
46-
47-
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
4842
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
4943
"github.com/rs/zerolog"
5044
)
5145

5246
const CertificateRenewalMargin = 7 * 24 * time.Hour
5347

54-
type Certificates []*x509.Certificate
55-
56-
func (c Certificates) Contains(cert *x509.Certificate) bool {
57-
for _, localCert := range c {
58-
if !localCert.Equal(cert) {
59-
return false
60-
}
61-
}
62-
63-
return true
64-
}
65-
66-
func (c Certificates) ContainsAll(certs Certificates) bool {
67-
if len(certs) == 0 {
68-
return true
69-
}
70-
71-
for _, cert := range certs {
72-
if !c.Contains(cert) {
73-
return false
74-
}
75-
}
76-
77-
return true
78-
}
79-
80-
func (c Certificates) ToPem() ([]byte, error) {
81-
bytes := bytes.NewBuffer([]byte{})
82-
83-
for _, cert := range c {
84-
if err := pem.Encode(bytes, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
85-
return nil, err
86-
}
87-
}
88-
89-
return bytes.Bytes(), nil
90-
}
91-
92-
func (c Certificates) AsCertPool() *x509.CertPool {
93-
cp := x509.NewCertPool()
94-
95-
for _, cert := range c {
96-
cp.AddCert(cert)
97-
}
98-
99-
return cp
100-
}
101-
102-
func getCertsFromData(log zerolog.Logger, caPem []byte) Certificates {
103-
certs := make([]*x509.Certificate, 0, 2)
104-
105-
for {
106-
pem, rest := pem.Decode(caPem)
107-
if pem == nil {
108-
break
109-
}
110-
111-
caPem = rest
112-
113-
cert, err := x509.ParseCertificate(pem.Bytes)
114-
if err != nil {
115-
// This error should be ignored
116-
log.Error().Err(err).Msg("Unable to parse certificate")
117-
continue
118-
}
119-
120-
certs = append(certs, cert)
121-
}
122-
123-
return certs
124-
}
125-
126-
func getCertsFromSecret(log zerolog.Logger, secret *core.Secret) Certificates {
127-
caPem, exists := secret.Data[core.ServiceAccountRootCAKey]
128-
if !exists {
129-
return nil
130-
}
131-
132-
return getCertsFromData(log, caPem)
133-
}
134-
135-
func getKeyCertFromCache(log zerolog.Logger, cachedStatus inspector.Inspector, spec api.DeploymentSpec, certName, keyName string) (Certificates, interface{}, error) {
136-
caSecret, exists := cachedStatus.Secret(spec.TLS.GetCASecretName())
137-
if !exists {
138-
return nil, nil, errors.Errorf("CA Secret does not exists")
139-
}
140-
141-
return getKeyCertFromSecret(log, caSecret, keyName, certName)
142-
}
143-
144-
func getKeyCertFromSecret(log zerolog.Logger, secret *core.Secret, certName, keyName string) (Certificates, interface{}, error) {
145-
ca, exists := secret.Data[certName]
146-
if !exists {
147-
return nil, nil, errors.Errorf("Key %s missing in secret", certName)
148-
}
149-
150-
key, exists := secret.Data[keyName]
151-
if !exists {
152-
return nil, nil, errors.Errorf("Key %s missing in secret", keyName)
153-
}
154-
155-
cert, keys, err := certificates.LoadFromPEM(string(ca), string(key))
156-
if err != nil {
157-
return nil, nil, err
158-
}
159-
160-
return cert, keys, nil
161-
}
162-
16348
// createTLSStatusUpdate creates plan to update ca info
16449
func createTLSStatusUpdate(ctx context.Context,
16550
log zerolog.Logger, apiObject k8sutil.APIObject,
@@ -228,7 +113,7 @@ func createCAAppendPlan(ctx context.Context,
228113
return nil
229114
}
230115

231-
ca, _, err := getKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
116+
ca, _, err := resources.GetKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
232117
if err != nil {
233118
log.Warn().Err(err).Str("secret", spec.TLS.GetCASecretName()).Msg("CA Secret does not contains Cert")
234119
return nil
@@ -281,7 +166,7 @@ func createCARenewalPlan(ctx context.Context,
281166
return nil
282167
}
283168

284-
cas, _, err := getKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
169+
cas, _, err := resources.GetKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
285170
if err != nil {
286171
log.Warn().Err(err).Str("secret", spec.TLS.GetCASecretName()).Msg("CA Secret does not contains Cert")
287172
return nil
@@ -312,7 +197,7 @@ func createCACleanPlan(ctx context.Context,
312197
return nil
313198
}
314199

315-
ca, _, err := getKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
200+
ca, _, err := resources.GetKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
316201
if err != nil {
317202
log.Warn().Err(err).Str("secret", spec.TLS.GetCASecretName()).Msg("CA Secret does not contains Cert")
318203
return nil
@@ -461,7 +346,7 @@ func createKeyfileRenewalPlanMode(
461346
return mode
462347
}
463348

464-
func checkServerValidCertRequest(ctx context.Context, apiObject k8sutil.APIObject, group api.ServerGroup, member api.MemberStatus, ca Certificates) (*tls.ConnectionState, error) {
349+
func checkServerValidCertRequest(ctx context.Context, apiObject k8sutil.APIObject, group api.ServerGroup, member api.MemberStatus, ca resources.Certificates) (*tls.ConnectionState, error) {
465350
endpoint := fmt.Sprintf("https://%s:%d", k8sutil.CreatePodDNSName(apiObject, group.AsRole(), member.ID), k8sutil.ArangoPort)
466351

467352
tlsConfig := &tls.Config{
@@ -493,7 +378,7 @@ func keyfileRenewalRequired(ctx context.Context,
493378
return false, false
494379
}
495380

496-
ca, _, err := getKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
381+
ca, _, err := resources.GetKeyCertFromSecret(log, caSecret, resources.CACertName, resources.CAKeyName)
497382
if err != nil {
498383
log.Warn().Err(err).Str("secret", spec.TLS.GetCASecretName()).Msg("CA Secret does not contains Cert")
499384
return false, false
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2020 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Adam Janikowski
21+
//
22+
23+
package resources
24+
25+
import (
26+
"bytes"
27+
"crypto/x509"
28+
"encoding/pem"
29+
30+
"github.com/arangodb-helper/go-certificates"
31+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
32+
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
33+
"github.com/pkg/errors"
34+
"github.com/rs/zerolog"
35+
core "k8s.io/api/core/v1"
36+
)
37+
38+
type Certificates []*x509.Certificate
39+
40+
func (c Certificates) Contains(cert *x509.Certificate) bool {
41+
for _, localCert := range c {
42+
if !localCert.Equal(cert) {
43+
return false
44+
}
45+
}
46+
47+
return true
48+
}
49+
50+
func (c Certificates) ContainsAll(certs Certificates) bool {
51+
if len(certs) == 0 {
52+
return true
53+
}
54+
55+
for _, cert := range certs {
56+
if !c.Contains(cert) {
57+
return false
58+
}
59+
}
60+
61+
return true
62+
}
63+
64+
func (c Certificates) ToPem() ([]byte, error) {
65+
bytes := bytes.NewBuffer([]byte{})
66+
67+
for _, cert := range c {
68+
if err := pem.Encode(bytes, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
69+
return nil, err
70+
}
71+
}
72+
73+
return bytes.Bytes(), nil
74+
}
75+
76+
func (c Certificates) AsCertPool() *x509.CertPool {
77+
cp := x509.NewCertPool()
78+
79+
for _, cert := range c {
80+
cp.AddCert(cert)
81+
}
82+
83+
return cp
84+
}
85+
86+
func GetCertsFromData(log zerolog.Logger, caPem []byte) Certificates {
87+
certs := make([]*x509.Certificate, 0, 2)
88+
89+
for {
90+
pem, rest := pem.Decode(caPem)
91+
if pem == nil {
92+
break
93+
}
94+
95+
caPem = rest
96+
97+
cert, err := x509.ParseCertificate(pem.Bytes)
98+
if err != nil {
99+
// This error should be ignored
100+
log.Error().Err(err).Msg("Unable to parse certificate")
101+
continue
102+
}
103+
104+
certs = append(certs, cert)
105+
}
106+
107+
return certs
108+
}
109+
110+
func GetCertsFromSecret(log zerolog.Logger, secret *core.Secret) Certificates {
111+
caPem, exists := secret.Data[core.ServiceAccountRootCAKey]
112+
if !exists {
113+
return nil
114+
}
115+
116+
return GetCertsFromData(log, caPem)
117+
}
118+
119+
func GetKeyCertFromCache(log zerolog.Logger, cachedStatus inspector.Inspector, spec api.DeploymentSpec, certName, keyName string) (Certificates, interface{}, error) {
120+
caSecret, exists := cachedStatus.Secret(spec.TLS.GetCASecretName())
121+
if !exists {
122+
return nil, nil, errors.Errorf("CA Secret does not exists")
123+
}
124+
125+
return GetKeyCertFromSecret(log, caSecret, keyName, certName)
126+
}
127+
128+
func GetKeyCertFromSecret(log zerolog.Logger, secret *core.Secret, certName, keyName string) (Certificates, interface{}, error) {
129+
ca, exists := secret.Data[certName]
130+
if !exists {
131+
return nil, nil, errors.Errorf("Key %s missing in secret", certName)
132+
}
133+
134+
key, exists := secret.Data[keyName]
135+
if !exists {
136+
return nil, nil, errors.Errorf("Key %s missing in secret", keyName)
137+
}
138+
139+
cert, keys, err := certificates.LoadFromPEM(string(ca), string(key))
140+
if err != nil {
141+
return nil, nil, err
142+
}
143+
144+
return cert, keys, nil
145+
}

0 commit comments

Comments
 (0)