Skip to content

Commit e4e10d7

Browse files
Adds a CA bundle injector
This adds the ability to inject a combined CA bundle into configmaps that are setup to want them
1 parent 0b6b46a commit e4e10d7

File tree

5 files changed

+219
-17
lines changed

5 files changed

+219
-17
lines changed

Gopkg.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/controller/add_networkconfig.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package controller
22

33
import (
44
"github.com/openshift/cluster-network-operator/pkg/controller/clusterconfig"
5+
"github.com/openshift/cluster-network-operator/pkg/controller/configmap_ca_injector"
56
"github.com/openshift/cluster-network-operator/pkg/controller/operconfig"
67
"github.com/openshift/cluster-network-operator/pkg/controller/proxyconfig"
78
)
@@ -13,5 +14,6 @@ func init() {
1314
operconfig.Add,
1415
clusterconfig.Add,
1516
operconfig.AddConfigMapReconciler,
17+
configmapcainjector.Add,
1618
)
1719
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package configmapcainjector
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
8+
configv1 "github.com/openshift/api/config/v1"
9+
"github.com/openshift/cluster-network-operator/pkg/controller/statusmanager"
10+
"github.com/openshift/cluster-network-operator/pkg/names"
11+
"github.com/openshift/cluster-network-operator/pkg/util/validation"
12+
apierrors "k8s.io/apimachinery/pkg/api/errors"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/labels"
15+
"k8s.io/apimachinery/pkg/types"
16+
17+
corev1 "k8s.io/api/core/v1"
18+
"k8s.io/apimachinery/pkg/api/equality"
19+
"k8s.io/apimachinery/pkg/api/errors"
20+
"k8s.io/apimachinery/pkg/runtime"
21+
"k8s.io/client-go/util/retry"
22+
"sigs.k8s.io/controller-runtime/pkg/client"
23+
"sigs.k8s.io/controller-runtime/pkg/controller"
24+
"sigs.k8s.io/controller-runtime/pkg/event"
25+
"sigs.k8s.io/controller-runtime/pkg/handler"
26+
"sigs.k8s.io/controller-runtime/pkg/manager"
27+
"sigs.k8s.io/controller-runtime/pkg/predicate"
28+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
29+
"sigs.k8s.io/controller-runtime/pkg/source"
30+
)
31+
32+
func Add(mgr manager.Manager, status *statusmanager.StatusManager) error {
33+
reconciler := newReconciler(mgr, status)
34+
if reconciler == nil {
35+
return fmt.Errorf("failed to create reconciler")
36+
}
37+
38+
return add(mgr, reconciler)
39+
}
40+
41+
func newReconciler(mgr manager.Manager, status *statusmanager.StatusManager) reconcile.Reconciler {
42+
if err := configv1.Install(mgr.GetScheme()); err != nil {
43+
return nil
44+
}
45+
46+
return &ReconcileConfigMapInjector{client: mgr.GetClient(), scheme: mgr.GetScheme(), status: status}
47+
}
48+
49+
func add(mgr manager.Manager, r reconcile.Reconciler) error {
50+
// Create a new controller.
51+
c, err := controller.New("configmap-trust-bundle-injector-controller", mgr, controller.Options{Reconciler: r})
52+
if err != nil {
53+
return err
54+
}
55+
56+
// The events fire for changes/creation of the trusted-ca-bundle and any configmaps with the
57+
// label "config.openshift.io/inject-trusted-cabundle".
58+
pred := predicate.Funcs{
59+
UpdateFunc: func(e event.UpdateEvent) bool {
60+
return shouldUpdateConfigMaps(e.MetaNew)
61+
},
62+
DeleteFunc: func(e event.DeleteEvent) bool {
63+
return false
64+
},
65+
CreateFunc: func(e event.CreateEvent) bool {
66+
return shouldUpdateConfigMaps(e.Meta)
67+
},
68+
GenericFunc: func(e event.GenericEvent) bool {
69+
return shouldUpdateConfigMaps(e.Meta)
70+
},
71+
}
72+
73+
err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, &handler.EnqueueRequestForObject{}, pred)
74+
if err != nil {
75+
return err
76+
}
77+
78+
return nil
79+
}
80+
81+
var _ reconcile.Reconciler = &ReconcileConfigMapInjector{}
82+
83+
type ReconcileConfigMapInjector struct {
84+
client client.Client
85+
scheme *runtime.Scheme
86+
status *statusmanager.StatusManager
87+
}
88+
89+
// Reconcile expects requests to refers to configmaps of two different types.
90+
// 1. a configmap named trusted-ca-bundle in namespace openshift-config-managed and will ensure that all configmaps with the label
91+
// config.openshift.io/inject-trusted-cabundle = true have the certificate information stored in trusted-ca-bundle's ca-bundle.crt entry.
92+
// 2. a configmap in any namespace with the label config.openshift.io/inject-trusted-cabundle = true and will insure that it contains the ca-bundle.crt
93+
// entry in the configmap named trusted-ca-bundle in namespace openshift-config-managed.
94+
func (r *ReconcileConfigMapInjector) Reconcile(request reconcile.Request) (reconcile.Result, error) {
95+
log.Printf("Reconciling configmap from %s/%s\n", request.Name, request.Namespace)
96+
97+
trustedCAbundleConfigMap := &corev1.ConfigMap{}
98+
trustedCAbundleConfigMapName := types.NamespacedName{
99+
Namespace: names.TRUSTED_CA_BUNDLE_CONFIGMAP_NS,
100+
Name: names.TRUSTED_CA_BUNDLE_CONFIGMAP,
101+
}
102+
err := r.client.Get(context.TODO(), trustedCAbundleConfigMapName, trustedCAbundleConfigMap)
103+
if err != nil {
104+
if errors.IsNotFound(err) {
105+
return reconcile.Result{}, nil
106+
}
107+
log.Println(err)
108+
return reconcile.Result{}, err
109+
}
110+
_, trustedCAbundleData, err := validation.TrustBundleConfigMap(trustedCAbundleConfigMap)
111+
112+
if err != nil {
113+
return reconcile.Result{}, err
114+
}
115+
// Build a list of configMaps.
116+
configMapsToChange := []corev1.ConfigMap{}
117+
118+
// The trusted-ca-bundle changed.
119+
if request.Name == names.TRUSTED_CA_BUNDLE_CONFIGMAP && request.Namespace == names.TRUSTED_CA_BUNDLE_CONFIGMAP_NS {
120+
121+
configMapList := &corev1.ConfigMapList{}
122+
selector := labels.Set(map[string]string{names.TRUSTED_CA_BUNDLE_CONFIGMAP_LABEL: "true"}).AsSelector()
123+
err = r.client.List(context.TODO(), &client.ListOptions{LabelSelector: selector}, configMapList)
124+
if err != nil {
125+
log.Println(err)
126+
return reconcile.Result{}, err
127+
}
128+
configMapsToChange = configMapList.Items
129+
log.Printf("%s changed, updating %d configMaps", names.TRUSTED_CA_BUNDLE_CONFIGMAP, len(configMapsToChange))
130+
} else {
131+
// Changing a single labeled configmap.
132+
133+
// Get the requested object.
134+
requestedCAbundleConfigMap := &corev1.ConfigMap{}
135+
requestedCAbundleConfigMapName := types.NamespacedName{
136+
Namespace: request.Namespace,
137+
Name: request.Name,
138+
}
139+
err = r.client.Get(context.TODO(), requestedCAbundleConfigMapName, requestedCAbundleConfigMap)
140+
if err != nil {
141+
log.Println(err)
142+
if apierrors.IsNotFound(err) {
143+
return reconcile.Result{}, nil
144+
}
145+
return reconcile.Result{}, err
146+
}
147+
configMapsToChange = append(configMapsToChange, *requestedCAbundleConfigMap)
148+
}
149+
150+
errs := []error{}
151+
152+
for _, configMap := range configMapsToChange {
153+
err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
154+
retrievedConfigMap := &corev1.ConfigMap{}
155+
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: configMap.Namespace, Name: configMap.Name}, retrievedConfigMap)
156+
if err != nil {
157+
if errors.IsNotFound(err) {
158+
return nil
159+
}
160+
log.Println(err)
161+
return err
162+
}
163+
configMapToUpdate := retrievedConfigMap.DeepCopy()
164+
if configMapToUpdate.Data == nil {
165+
configMapToUpdate.Data = map[string]string{names.TRUSTED_CA_BUNDLE_CONFIGMAP_KEY: string(trustedCAbundleData)}
166+
} else {
167+
configMapToUpdate.Data[names.TRUSTED_CA_BUNDLE_CONFIGMAP_KEY] = string(trustedCAbundleData)
168+
}
169+
if equality.Semantic.DeepEqual(configMapToUpdate, retrievedConfigMap) {
170+
// Nothing to update the new and old configmap object would be the same.
171+
return nil
172+
}
173+
err = r.client.Update(context.TODO(), configMapToUpdate)
174+
if err != nil {
175+
log.Println(err)
176+
return err
177+
}
178+
return nil
179+
})
180+
if err != nil {
181+
errs = append(errs, err)
182+
if len(errs) > 5 {
183+
return reconcile.Result{}, fmt.Errorf("Too many errors attempting to update configmaps with CA cert. data")
184+
}
185+
}
186+
}
187+
if len(errs) > 0 {
188+
return reconcile.Result{}, fmt.Errorf("some configmaps didn't fully update with CA cert. data")
189+
}
190+
return reconcile.Result{}, nil
191+
}
192+
193+
func shouldUpdateConfigMaps(meta metav1.Object) bool {
194+
return meta.GetLabels()[names.TRUSTED_CA_BUNDLE_CONFIGMAP_LABEL] == "true" ||
195+
(meta.GetName() == names.TRUSTED_CA_BUNDLE_CONFIGMAP && meta.GetNamespace() == names.TRUSTED_CA_BUNDLE_CONFIGMAP_NS)
196+
}

pkg/names/names.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,22 @@ const SERVICE_CA_CONFIGMAP = "openshift-service-ca"
3737
// that is used in multus admission controller deployment
3838
const MULTUS_VALIDATING_WEBHOOK = "multus.openshift.io"
3939

40-
// TRUST_BUNDLE_CONFIGMAP_KEY is the name of the data key containing
40+
// TRUSTED_CA_BUNDLE_CONFIGMAP_KEY is the name of the data key containing
4141
// the PEM encoded trust bundle.
42-
const TRUST_BUNDLE_CONFIGMAP_KEY = "ca-bundle.crt"
42+
const TRUSTED_CA_BUNDLE_CONFIGMAP_KEY = "ca-bundle.crt"
4343

44-
// TRUST_BUNDLE_CONFIGMAP is the name of the ConfigMap
44+
// TRUSTED_CA_BUNDLE_CONFIGMAP is the name of the ConfigMap
4545
// containing the combined user/system trust bundle.
46-
const TRUST_BUNDLE_CONFIGMAP = "trusted-ca-bundle"
46+
const TRUSTED_CA_BUNDLE_CONFIGMAP = "trusted-ca-bundle"
4747

48-
// TRUST_BUNDLE_CONFIGMAP_NS is the namespace that hosts the
48+
// TRUSTED_CA_BUNDLE_CONFIGMAP_NS is the namespace that hosts the
4949
// ADDL_TRUST_BUNDLE_CONFIGMAP and TRUST_BUNDLE_CONFIGMAP
5050
// ConfigMaps.
51-
const TRUST_BUNDLE_CONFIGMAP_NS = "openshift-config-managed"
51+
const TRUSTED_CA_BUNDLE_CONFIGMAP_NS = "openshift-config-managed"
5252

53-
// TRUST_BUNDLE_CONFIGMAP_ANNOTATION is the name of the annotation that
53+
// TRUSTED_CA_BUNDLE_CONFIGMAP_LABEL is the name of the label that
5454
// determines whether or not to inject the combined ca certificate
55-
const TRUST_BUNDLE_CONFIGMAP_ANNOTATION = "config.openshift.io/inject-trusted-cabundle"
55+
const TRUSTED_CA_BUNDLE_CONFIGMAP_LABEL = "config.openshift.io/inject-trusted-cabundle"
5656

5757
// Proxy returns the namespaced name "cluster" in the
5858
// default namespace.
@@ -62,11 +62,11 @@ func Proxy() types.NamespacedName {
6262
}
6363
}
6464

65-
// TrustBundleConfigMap returns the namespaced name of the ConfigMap
66-
// containing the merged user/system trust bundle.
67-
func TrustBundleConfigMap() types.NamespacedName {
65+
// AddlTrustBundleConfigMapNS returns the namespaced name of the
66+
// namespace containing the user-provided trust bundle ConfigMap.
67+
func AddlTrustBundleConfigMap() types.NamespacedName {
6868
return types.NamespacedName{
69-
Namespace: TRUST_BUNDLE_CONFIGMAP_NS,
70-
Name: TRUST_BUNDLE_CONFIGMAP,
69+
Namespace: TRUSTED_CA_BUNDLE_CONFIGMAP_NS,
70+
Name: TRUSTED_CA_BUNDLE_CONFIGMAP,
7171
}
7272
}

pkg/util/validation/trustbundle.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ const (
2020
// contains one or more valid PEM encoded certificates, returning
2121
// a byte slice of "ca-bundle.crt" contents upon success.
2222
func TrustBundleConfigMap(cfgMap *corev1.ConfigMap) ([]*x509.Certificate, []byte, error) {
23-
if _, ok := cfgMap.Data[names.TRUST_BUNDLE_CONFIGMAP_KEY]; !ok {
24-
return nil, nil, fmt.Errorf("ConfigMap %q is missing %q", cfgMap.Name, names.TRUST_BUNDLE_CONFIGMAP_KEY)
23+
if _, ok := cfgMap.Data[names.TRUSTED_CA_BUNDLE_CONFIGMAP_KEY]; !ok {
24+
return nil, nil, fmt.Errorf("ConfigMap %q is missing %q", cfgMap.Name, names.TRUSTED_CA_BUNDLE_CONFIGMAP_KEY)
2525
}
26-
trustBundleData := []byte(cfgMap.Data[names.TRUST_BUNDLE_CONFIGMAP_KEY])
26+
trustBundleData := []byte(cfgMap.Data[names.TRUSTED_CA_BUNDLE_CONFIGMAP_KEY])
2727
if len(trustBundleData) == 0 {
28-
return nil, nil, fmt.Errorf("data key %q is empty from ConfigMap %q", names.TRUST_BUNDLE_CONFIGMAP_KEY, cfgMap.Name)
28+
return nil, nil, fmt.Errorf("data key %q is empty from ConfigMap %q", names.TRUSTED_CA_BUNDLE_CONFIGMAP_KEY, cfgMap.Name)
2929
}
3030
certBundle, _, err := CertificateData(trustBundleData)
3131
if err != nil {

0 commit comments

Comments
 (0)