forked from openshift/library-go
/
cabundle.go
120 lines (104 loc) · 4.1 KB
/
cabundle.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package certrotation
import (
"crypto/x509"
"fmt"
"reflect"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1informers "k8s.io/client-go/informers/core/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/util/cert"
"k8s.io/klog"
"github.com/openshift/library-go/pkg/certs"
"github.com/openshift/library-go/pkg/crypto"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
)
// CABundleRotation maintains a CA bundle config map, but adding new CA certs and removing expired old ones.
type CABundleRotation struct {
Namespace string
Name string
Informer corev1informers.ConfigMapInformer
Lister corev1listers.ConfigMapLister
Client corev1client.ConfigMapsGetter
EventRecorder events.Recorder
}
func (c CABundleRotation) ensureConfigMapCABundle(signingCertKeyPair *crypto.CA) ([]*x509.Certificate, error) {
// by this point we have current signing cert/key pair. We now need to make sure that the ca-bundle configmap has this cert and
// doesn't have any expired certs
originalCABundleConfigMap, err := c.Lister.ConfigMaps(c.Namespace).Get(c.Name)
if err != nil && !apierrors.IsNotFound(err) {
return nil, err
}
caBundleConfigMap := originalCABundleConfigMap.DeepCopy()
if apierrors.IsNotFound(err) {
// create an empty one
caBundleConfigMap = &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Namespace: c.Namespace, Name: c.Name}}
}
updatedCerts, err := manageCABundleConfigMap(caBundleConfigMap, signingCertKeyPair.Config.Certs[0])
if err != nil {
return nil, err
}
if originalCABundleConfigMap == nil || originalCABundleConfigMap.Data == nil || !equality.Semantic.DeepEqual(originalCABundleConfigMap.Data, caBundleConfigMap.Data) {
c.EventRecorder.Eventf("CABundleUpdateRequired", "%q in %q requires a new cert", c.Name, c.Namespace)
LabelAsManagedConfigMap(caBundleConfigMap, CertificateTypeCABundle)
actualCABundleConfigMap, modified, err := resourceapply.ApplyConfigMap(c.Client, c.EventRecorder, caBundleConfigMap)
if err != nil {
return nil, err
}
if modified {
klog.V(2).Infof("Updated ca-bundle.crt configmap %s/%s with:\n%s", certs.CertificateBundleToString(updatedCerts), caBundleConfigMap.Namespace, caBundleConfigMap.Name)
}
caBundleConfigMap = actualCABundleConfigMap
}
caBundle := caBundleConfigMap.Data["ca-bundle.crt"]
if len(caBundle) == 0 {
return nil, fmt.Errorf("configmap/%s -n%s missing ca-bundle.crt", caBundleConfigMap.Name, caBundleConfigMap.Namespace)
}
certificates, err := cert.ParseCertsPEM([]byte(caBundle))
if err != nil {
return nil, err
}
return certificates, nil
}
// manageCABundleConfigMap adds the new certificate to the list of cabundles, eliminates duplicates, and prunes the list of expired
// certs to trust as signers
func manageCABundleConfigMap(caBundleConfigMap *corev1.ConfigMap, currentSigner *x509.Certificate) ([]*x509.Certificate, error) {
if caBundleConfigMap.Data == nil {
caBundleConfigMap.Data = map[string]string{}
}
certificates := []*x509.Certificate{}
caBundle := caBundleConfigMap.Data["ca-bundle.crt"]
if len(caBundle) > 0 {
var err error
certificates, err = cert.ParseCertsPEM([]byte(caBundle))
if err != nil {
return nil, err
}
}
certificates = append([]*x509.Certificate{currentSigner}, certificates...)
certificates = crypto.FilterExpiredCerts(certificates...)
finalCertificates := []*x509.Certificate{}
// now check for duplicates. n^2, but super simple
for i := range certificates {
found := false
for j := range finalCertificates {
if reflect.DeepEqual(certificates[i].Raw, finalCertificates[j].Raw) {
found = true
break
}
}
if !found {
finalCertificates = append(finalCertificates, certificates[i])
}
}
caBytes, err := crypto.EncodeCertificates(finalCertificates...)
if err != nil {
return nil, err
}
caBundleConfigMap.Data["ca-bundle.crt"] = string(caBytes)
return finalCertificates, nil
}