Skip to content

Commit d09fcb3

Browse files
levan-mtbavelier
andauthored
Avoid RBAC errors when Operator can't list or watch ConfigMaps (#2889)
* Avoid RBAC errors when Operator can't list or watch ConfigMaps * Update access flag only if informer succeeded (#2894) --------- Co-authored-by: Timothée Bavelier <97530782+tbavelier@users.noreply.github.com>
1 parent 9678903 commit d09fcb3

1 file changed

Lines changed: 67 additions & 48 deletions

File tree

pkg/controller/utils/metadata/helm_metadata.go

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ type HelmMetadataForwarder struct {
6767
// Value: *ReleaseEntry
6868
releaseSnapshots sync.Map
6969

70-
// secretAccessEnabled tracks whether the operator has permission to read Secrets.
70+
// configMapAccessEnabled tracks whether the operator has permission to list/watch ConfigMaps.
71+
// Set once in Start() and used to skip the ConfigMap path in processKey.
72+
configMapAccessEnabled bool
73+
74+
// secretAccessEnabled tracks whether the operator has permission to list/watch Secrets.
7175
// Set once in Start() and used to skip the Secret path in processKey.
7276
secretAccessEnabled bool
7377
}
@@ -160,19 +164,19 @@ func NewHelmMetadataForwarderWithManager(logger logr.Logger, mgr manager.Manager
160164
}
161165
}
162166

163-
// canListWatchSecrets checks if the operator has permission to list and watch Secrets
164-
func (hmf *HelmMetadataForwarder) canListWatchSecrets(ctx context.Context) bool {
167+
// canListWatch checks if the operator has permission to list and watch the given resource
168+
func (hmf *HelmMetadataForwarder) canListWatch(ctx context.Context, resource string) bool {
165169
for _, verb := range []string{"list", "watch"} {
166170
sar := &authorizationv1.SelfSubjectAccessReview{
167171
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
168172
ResourceAttributes: &authorizationv1.ResourceAttributes{
169173
Verb: verb,
170-
Resource: "secrets",
174+
Resource: resource,
171175
},
172176
},
173177
}
174178
if err := hmf.mgr.GetClient().Create(ctx, sar); err != nil {
175-
hmf.logger.V(1).Info("Failed to check Secret RBAC permission", "verb", verb, "error", err)
179+
hmf.logger.V(1).Info("Failed to check RBAC permission", "resource", resource, "verb", verb, "error", err)
176180
return false
177181
}
178182
if !sar.Status.Allowed {
@@ -188,41 +192,46 @@ func (hmf *HelmMetadataForwarder) canListWatchSecrets(ctx context.Context) bool
188192
// https://github.com/kubernetes/client-go/blob/v0.35.0/tools/cache/shared_informer.go#L693-L697
189193
// Errors are logged but do not prevent the operator from starting
190194
func (hmf *HelmMetadataForwarder) Start(ctx context.Context) error {
191-
cmInformer, err := hmf.mgr.GetCache().GetInformer(ctx, &corev1.ConfigMap{})
192-
if err != nil {
193-
hmf.logger.Info("Unable to get ConfigMap informer, Helm metadata collection will be disabled", "error", err)
194-
return nil
195-
}
196-
_, err = cmInformer.AddEventHandler(toolscache.FilteringResourceEventHandler{
197-
FilterFunc: func(obj any) bool {
198-
cm, ok := obj.(*corev1.ConfigMap)
199-
return ok &&
200-
cm.Labels["owner"] == "helm" &&
201-
strings.HasPrefix(cm.Name, releasePrefix)
202-
},
203-
Handler: toolscache.ResourceEventHandlerFuncs{
204-
AddFunc: func(obj any) {
205-
if key, keyErr := toolscache.MetaNamespaceKeyFunc(obj); keyErr == nil {
206-
hmf.queue.Add(key)
207-
hmf.logger.V(2).Info("Enqueued ConfigMap for processing", "key", key)
208-
}
209-
},
210-
DeleteFunc: func(obj any) {
211-
if key, keyErr := toolscache.DeletionHandlingMetaNamespaceKeyFunc(obj); keyErr == nil {
212-
hmf.queue.Add(deletePrefix + key)
213-
hmf.logger.V(2).Info("Enqueued ConfigMap deletion for processing", "key", key)
214-
}
215-
},
216-
},
217-
})
218-
219-
if err != nil {
220-
hmf.logger.Info("Unable to add ConfigMap event handler, Helm metadata collection will be disabled", "error", err)
221-
return nil
195+
hmf.configMapAccessEnabled = false
196+
if hmf.canListWatch(ctx, "configmaps") {
197+
cmInformer, err := hmf.mgr.GetCache().GetInformer(ctx, &corev1.ConfigMap{})
198+
if err != nil {
199+
hmf.logger.Info("Unable to get ConfigMap informer, Helm metadata collection from ConfigMaps will be disabled", "error", err)
200+
} else {
201+
_, err = cmInformer.AddEventHandler(toolscache.FilteringResourceEventHandler{
202+
FilterFunc: func(obj any) bool {
203+
cm, ok := obj.(*corev1.ConfigMap)
204+
return ok &&
205+
cm.Labels["owner"] == "helm" &&
206+
strings.HasPrefix(cm.Name, releasePrefix)
207+
},
208+
Handler: toolscache.ResourceEventHandlerFuncs{
209+
AddFunc: func(obj any) {
210+
if key, keyErr := toolscache.MetaNamespaceKeyFunc(obj); keyErr == nil {
211+
hmf.queue.Add(key)
212+
hmf.logger.V(2).Info("Enqueued ConfigMap for processing", "key", key)
213+
}
214+
},
215+
DeleteFunc: func(obj any) {
216+
if key, keyErr := toolscache.DeletionHandlingMetaNamespaceKeyFunc(obj); keyErr == nil {
217+
hmf.queue.Add(deletePrefix + key)
218+
hmf.logger.V(2).Info("Enqueued ConfigMap deletion for processing", "key", key)
219+
}
220+
},
221+
},
222+
})
223+
if err != nil {
224+
hmf.logger.Info("Unable to add ConfigMap event handler, Helm metadata collection from ConfigMaps will be disabled", "error", err)
225+
} else {
226+
hmf.configMapAccessEnabled = true
227+
}
228+
}
229+
} else {
230+
hmf.logger.Info("No permission to list/watch ConfigMaps, Helm metadata collection from ConfigMaps will be disabled")
222231
}
223232

224-
hmf.secretAccessEnabled = hmf.canListWatchSecrets(ctx)
225-
if hmf.secretAccessEnabled {
233+
hmf.secretAccessEnabled = false
234+
if hmf.canListWatch(ctx, "secrets") {
226235
secretInformer, secretErr := hmf.mgr.GetCache().GetInformer(ctx, &corev1.Secret{})
227236
if secretErr != nil {
228237
hmf.logger.Info("Unable to get Secret informer, Helm metadata collection from Secrets will be disabled", "error", secretErr)
@@ -249,6 +258,8 @@ func (hmf *HelmMetadataForwarder) Start(ctx context.Context) error {
249258
})
250259
if secretErr != nil {
251260
hmf.logger.Info("Unable to add Secret event handler, Helm metadata collection from Secrets will be disabled", "error", secretErr)
261+
} else {
262+
hmf.secretAccessEnabled = true
252263
}
253264
}
254265
} else {
@@ -322,30 +333,38 @@ func (hmf *HelmMetadataForwarder) processKey(ctx context.Context, key string) er
322333
return fmt.Errorf("invalid key format: %w", err)
323334
}
324335

325-
// Try to get as ConfigMap first
326-
cm := &corev1.ConfigMap{}
327-
err = hmf.k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, cm)
328-
if err == nil && cm.Labels["owner"] == "helm" {
329-
hmf.handleHelmResource(ctx, cm.Name, cm.Namespace, string(cm.UID), []byte(cm.Data["release"]))
330-
return nil
336+
var lastErr error
337+
338+
// Try to get as ConfigMap first (only if we have permission, to avoid lazily registering a ConfigMap informer)
339+
if hmf.configMapAccessEnabled {
340+
cm := &corev1.ConfigMap{}
341+
lastErr = hmf.k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, cm)
342+
if lastErr == nil && cm.Labels["owner"] == "helm" {
343+
hmf.handleHelmResource(ctx, cm.Name, cm.Namespace, string(cm.UID), []byte(cm.Data["release"]))
344+
return nil
345+
}
331346
}
332347

333348
// Try as Secret (only if we have permission, to avoid lazily registering a Secret informer)
334349
if hmf.secretAccessEnabled {
335350
secret := &corev1.Secret{}
336-
err = hmf.k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, secret)
337-
if err == nil && secret.Labels["owner"] == "helm" {
351+
lastErr = hmf.k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, secret)
352+
if lastErr == nil && secret.Labels["owner"] == "helm" {
338353
hmf.handleHelmResource(ctx, secret.Name, secret.Namespace, string(secret.UID), secret.Data["release"])
339354
return nil
340355
}
341356
}
342357

343358
// If not found, likely a race condition with deletion - ignore it
344-
if errors.IsNotFound(err) {
359+
if errors.IsNotFound(lastErr) {
345360
return nil
346361
}
347362

348-
return fmt.Errorf("failed to get resource: %w", err)
363+
if lastErr != nil {
364+
return fmt.Errorf("failed to get resource: %w", lastErr)
365+
}
366+
367+
return nil
349368
}
350369

351370
// handleDelete handles deletion of a Helm release

0 commit comments

Comments
 (0)