Repository navigation
Operator: manager role lacks watch on clusterroles/clusterrolebindings, informers fail with "cannot watch resource" #7061
Description
Expected Behavior
The operator's RBAC covers every API call the operator makes. The controller-manager log has no permission errors.
Current Behavior
When a FeatureStore uses Kubernetes authorization (the default), the operator logs these errors every few seconds, with client-go backoff:
ERROR controller-runtime.cache.UnhandledError Failed to watch {"reflector": "go/pkg/mod/k8s.io/client-go@v0.35.2/tools/cache/reflector.go:289", "type": "*v1.ClusterRole", "error": "clusterroles.rbac.authorization.k8s.io is forbidden: User \"system:serviceaccount:feast-operator-system:feast-operator-controller-manager\" cannot watch resource \"clusterroles\" in API group \"rbac.authorization.k8s.io\" at the cluster scope"}
ERROR controller-runtime.cache.UnhandledError Failed to watch {"reflector": "go/pkg/mod/k8s.io/client-go@v0.35.2/tools/cache/reflector.go:289", "type": "*v1.ClusterRoleBinding", "error": "clusterrolebindings.rbac.authorization.k8s.io is forbidden: User \"system:serviceaccount:feast-operator-system:feast-operator-controller-manager\" cannot watch resource \"clusterrolebindings\" in API group \"rbac.authorization.k8s.io\" at the cluster scope"}
Reconciliation still succeeds, and the FeatureStore becomes Ready. The informers fill from the initial list, but they never receive watch events. As a result, the operator reads ClusterRoles and ClusterRoleBindings from a cache that goes stale, and the log fills with errors (55 in about 17 minutes with one FeatureStore).
Cause
- The authz code reads and writes ClusterRoles and ClusterRoleBindings through the manager client with
controllerutil.CreateOrUpdateandGet. SeecreateFeastClusterRole,createFeastClusterRoleBinding,createOidcClusterRole,createOidcClusterRoleBindingandcleanupKubernetesClusterRbacininternal/controller/authz/authz.go. cmd/main.golists many types inClient.Cache.DisableFor, but notrbacv1.ClusterRoleorrbacv1.ClusterRoleBinding(L192). Reads of these two types therefore go through the cache, and controller-runtime starts an informer for each type on first use. An informer needslistandwatch.- The RBAC markers grant only
get;list;create;update;deleteon clusterrolebindings andcreate;get;liston clusterroles (featurestore_controller.goL74-L76). Neither marker includeswatch.
master has the same markers and the same DisableFor list.
Steps to reproduce
- Install the operator from
infra/feast-operator/dist/install.yaml(v0.67.0). - Create a
FeatureStorewithoutauthz(so it defaults to Kubernetes authorization). kubectl -n feast-operator-system logs deploy/feast-operator-controller-manager | grep 'Failed to watch'
Specifications
- Version: Feast operator v0.67.0 (
quay.io/feastdev/feast-operator:0.67.0), controller-runtime v0.23.3 - Platform: vanilla Kubernetes v1.35.6 (not OpenShift)
- Subsystem: feast-operator, RBAC / authz
Possible Solution
Either change fixes the errors:
- Add
watchto the two markers:The role already grants// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterrolebindings,verbs=get;list;watch;create;update;delete // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles,verbs=create;get;list;watch
list, sowatchexposes no additional data. - Add
&rbacv1.ClusterRole{}and&rbacv1.ClusterRoleBinding{}toClient.Cache.DisableFor. Reads of these two types then go directly to the API server, as reads ofRoleandRoleBindingalready do. With this change, no cluster-wide RBAC informers start at all.
As a workaround, we append a watch-only rule to feast-operator-manager-role with a kustomize patch:
- target:
kind: ClusterRole
name: feast-operator-manager-role
patch: |-
- op: add
path: /rules/-
value:
apiGroups: [rbac.authorization.k8s.io]
resources: [clusterrolebindings, clusterroles]
verbs: [watch]After a restart, the operator logs no Failed to watch errors.
Activity