-
Notifications
You must be signed in to change notification settings - Fork 87
Expand file tree
/
Copy pathstarter.go
More file actions
174 lines (147 loc) · 5.65 KB
/
starter.go
File metadata and controls
174 lines (147 loc) · 5.65 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package cabundleinjector
import (
"context"
"fmt"
"os"
"strings"
"time"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"github.com/openshift/library-go/pkg/controller/controllercmd"
"github.com/openshift/library-go/pkg/controller/factory"
)
type caBundleInjectorConfig struct {
config *rest.Config
defaultResync time.Duration
caBundle []byte
kubeClient *kubernetes.Clientset
kubeInformers kubeinformers.SharedInformerFactory
// legacyVulnerableCABundle is a CA bundle which included more certificates than are needed for verifying service
// serving certificates. This was addressed in new installs of 4.8, but migrated clusters continue to have the old
// content inside of their bound tokens for the service-ca.crt.
// This CA bundle should only be used for specifically named configmaps which explicitly indicate their desire.
// This makes it impossible for customers to use and being to rely upon.
legacyVulnerableCABundle []byte
}
type startInformersFunc func(stopChan <-chan struct{})
type controllerConfig struct {
name string
sync factory.SyncFunc
informer cache.SharedIndexInformer
startInformers startInformersFunc
annotationsChecker factory.EventFilterFunc
namespaced bool
}
type configBuilderFunc func(config *caBundleInjectorConfig) controllerConfig
func StartCABundleInjector(ctx context.Context, controllerContext *controllercmd.ControllerContext) error {
// TODO(marun) Detect and respond to changes in this path rather than
// depending on the operator for redeployment
caBundleFile := "/var/run/configmaps/signing-cabundle/ca-bundle.crt"
caBundleContent, err := os.ReadFile(caBundleFile)
if err != nil {
return err
}
// this construction matches what the old kube controller manager did. It added the entire ca.crt to the service-ca.crt.
vulnerableLegacyCABundleContent, err := os.ReadFile(caBundleFile)
if err != nil {
return err
}
saTokenCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
saTokenCABundleContent, err := os.ReadFile(saTokenCAFile)
if err != nil && !os.IsNotExist(err) {
return err
}
if len(saTokenCABundleContent) > 0 {
vulnerableLegacyCABundleContent = append(vulnerableLegacyCABundleContent, saTokenCABundleContent...)
vulnerableLegacyCABundleContent = append(vulnerableLegacyCABundleContent, []byte("\n")...)
}
client := kubernetes.NewForConfigOrDie(controllerContext.ProtoKubeConfig)
defaultResync := 20 * time.Minute
informers := kubeinformers.NewSharedInformerFactory(client, defaultResync)
injectorConfig := &caBundleInjectorConfig{
config: controllerContext.ProtoKubeConfig,
defaultResync: defaultResync,
caBundle: caBundleContent,
legacyVulnerableCABundle: vulnerableLegacyCABundleContent,
kubeClient: client,
kubeInformers: informers,
}
stopChan := ctx.Done()
configConstructors := []configBuilderFunc{
newAPIServiceInjectorConfig,
newConfigMapInjectorConfig,
newCRDInjectorConfig,
newMutatingWebhookInjectorConfig,
newValidatingWebhookInjectorConfig,
newVulnerableLegacyConfigMapInjectorConfig, // this has to be kept for cluster migrated from before 4.7
}
injectionControllers := []factory.Controller{}
for _, configConstructor := range configConstructors {
ctlConfig := configConstructor(injectorConfig)
queueFn := clusterObjToQueueKey
if ctlConfig.namespaced {
queueFn = namespacedObjToQueueKey
}
injectionControllers = append(injectionControllers,
factory.New().
WithSync(ctlConfig.sync).
WithFilteredEventsInformersQueueKeyFunc(queueFn, ctlConfig.annotationsChecker, ctlConfig.informer).
ToController(ctlConfig.name, controllerContext.EventRecorder),
)
// Start non-core informers
if ctlConfig.startInformers != nil {
ctlConfig.startInformers(stopChan)
}
}
// Start core informers
informers.Start(stopChan)
// Start injector controllers once all informers have started
for _, controllerRunner := range injectionControllers {
go controllerRunner.Run(ctx, 5)
}
return nil
}
func annotationsChecker(supportedAnnotations ...string) factory.EventFilterFunc {
return func(obj interface{}) bool {
metaObj, ok := obj.(metav1.Object)
// this block handles the case of a deleted object without panic-ing. We try to use the last known status,
// but it's only best effort. if we're being deleted, there isn't a whole lot to be done.
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
return false
}
metaObj, ok = tombstone.Obj.(metav1.Object)
if !ok {
utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a metav1.Object %#v", obj))
return false
}
}
annotations := metaObj.GetAnnotations()
for _, key := range supportedAnnotations {
if strings.EqualFold(annotations[key], "true") {
return true
}
}
return false
}
}
func namespacedObjToQueueKey(obj runtime.Object) string {
metaObj := obj.(metav1.Object)
return fmt.Sprintf("%s/%s", metaObj.GetNamespace(), metaObj.GetName())
}
func clusterObjToQueueKey(obj runtime.Object) string {
metaObj := obj.(metav1.Object)
return metaObj.GetName()
}
func namespacedObjectFromQueueKey(qKey string) (string, string) {
nsName := strings.SplitN(qKey, "/", 2)
// happilly panic on index errors if someone tried to use this on non-namespaced objects
return nsName[0], nsName[1]
}