feat(controller): scope MLflow RBAC to per-agent namespaced Role#366
Conversation
Replace the broad ClusterRole binding approach with a least-privilege scoped Role per agent Deployment. Instead of binding each agent SA to the shared mlflow-operator-mlflow-integration ClusterRole, the reconciler now creates: - A namespaced Role granting only get+update on the agent's specific MLflow experiment (locked via resourceNames), preventing access to other experiments, registeredmodels, or gatewayendpoints. - A RoleBinding that binds the agent SA to that scoped Role. Both resources are owned by the Deployment and are garbage-collected on deletion. The operator ClusterRoleBinding for the controller-manager SA is unchanged. RBAC markers and generated role.yaml files are updated accordingly. Signed-off-by: Daniels Nagornuks <dnagornu@redhat.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
pdettori
left a comment
There was a problem hiding this comment.
Well-structured least-privilege RBAC refactor. Replaces a broad ClusterRoleBinding (all agents access all MLflow resources cluster-wide) with namespace-scoped Roles locked to each agent's specific experiment via resourceNames. Both Role and RoleBinding are owned by the Deployment for automatic GC, and the controller watches Role deletions for re-reconciliation.
Areas reviewed: Go controller logic, Helm/K8s YAML (RBAC parity kustomize ↔ chart), tests, docs, security
Commits: 1 commit, signed-off ✓, proper Assisted-By trailer
CI: All 16 checks passing
| @@ -40,9 +40,13 @@ import ( | |||
| ) | |||
|
|
|||
There was a problem hiding this comment.
nit: The kubebuilder marker adds get;list;watch;update for mlflowexperiments on the operator's ClusterRole. Since the controller only verifies experiment existence for resourceNames scoping, list and watch may not be strictly necessary (just get might suffice). Not blocking — broader permissions are fine for controller reliability and future use.
Summary
The MLflow controller previously bound each agent ServiceAccount to the broad
mlflow-operator-mlflow-integrationClusterRole, granting access to all MLflow experiments, registered models, and gateway endpoints cluster-wide. This PR replaces that with a least-privilege namespaced Role per agent Deployment, scoped viaresourceNamesto only the agent's own experiment, and a corresponding RoleBinding — both owned by the Deployment so they are garbage-collected on deletion.Changes
mlflow_controller.go— replaceensureRoleBinding(ClusterRole binding) withensureScopedRBAC, which creates a namespaced Role granting onlyget+updateon the specificmlflowexperimentsresource name, plus a RoleBinding to the agent SA; remove the now-unusedMLflowClusterRolefield andclusterRoleName()helpermlflow_controller.go— addOwns(&rbacv1.Role{})to the controller watch so Role deletions trigger reconciliationconfig/rbac/role.yamlandcharts/.../rbac/role.yaml— addmlflowexperiments(get/list/watch/update) androles(create/get/list/watch/update) RBAC rules, regenerated from kubebuilder markersdocs/mlflow-integration.md— update Authentication section to document the new two-resource scoped RBAC patternmlflow_controller_test.go— update tests to assert scoped Role creation with correctresourceNames, update RoleBinding assertions to checkKind: Role, remove obsoleteclusterRoleNamehelper testsTesting
make test)make lint)resourceNamesscoping on an OpenShift clusterRelated Issues
Resolves: RHAIENG-4446