Skip to content

RBAC Least-Privilege Permissions

Reference documentation for the operator's RBAC (Role-Based Access Control) configuration, describing every ClusterRole rule, the ClusterRoleBinding, and the ServiceAccount.

Source: config/rbac/role.yaml, config/rbac/role_binding.yaml, config/rbac/service_account.yaml

Overview

The memcached-operator follows the principle of least privilege: it requests only the minimum Kubernetes API permissions required for reconciliation. All RBAC rules are generated from +kubebuilder:rbac markers on the MemcachedReconciler in internal/controller/memcached_controller.go. No hand-edited rules exist in config/rbac/role.yaml.

The operator uses a single ClusterRole (manager-role) bound to a single ServiceAccount (controller-manager) via a ClusterRoleBinding (manager-rolebinding). The ClusterRole contains exactly 10 policy rules.


ClusterRole Rules

The manager-role ClusterRole contains the following rules, grouped by purpose.

Memcached Custom Resource

API GroupResourceVerbsPurpose
memcached.c5c3.iomemcachedscreate, delete, get, list, patch, update, watchFull CRUD on the primary custom resource
memcached.c5c3.iomemcacheds/statusget, patch, updateUpdate status subresource with conditions and observed state
memcached.c5c3.iomemcacheds/finalizersupdateAdd/remove finalizers for cleanup logic

Rationale: The reconciler must read, create, update, and delete Memcached CRs. Status updates require separate subresource permissions. Finalizer management requires update on the finalizers subresource.

Owned Resources (Full CRUD)

These are Kubernetes resources the reconciler creates and manages as owned objects of each Memcached CR. Each requires full CRUD verbs so the reconciler can create, update, and clean up owned resources without permission errors.

API GroupResourceVerbsReconciler Method
appsdeploymentscreate, delete, get, list, patch, update, watchreconcileDeployment — manages the Memcached StatefulSet/Deployment
(core)servicescreate, delete, get, list, patch, update, watchreconcileService — manages the headless Service for pod discovery
policypoddisruptionbudgetscreate, delete, get, list, patch, update, watchreconcilePDB — manages the PodDisruptionBudget for availability
networking.k8s.ionetworkpoliciescreate, delete, get, list, patch, update, watchreconcileNetworkPolicy — manages ingress NetworkPolicy
monitoring.coreos.comservicemonitorscreate, delete, get, list, patch, update, watchreconcileServiceMonitor — manages Prometheus ServiceMonitor

Rationale: Each owned resource goes through controllerutil.CreateOrUpdate, which requires get (to check existence), create (for initial creation), and update/patch (for subsequent reconciliation). Delete is needed for garbage collection when the owner reference cascade does not apply. List and watch support the controller's informer-based watch mechanism.

Read-Only Resources

API GroupResourceVerbsPurpose
(core)secretsget, list, watchMount SASL credentials and TLS certificates into Memcached pods

Rationale: The operator reads Secrets to reference them in volume mounts for SASL authentication (spec.security.sasl.secretRef) and TLS encryption (spec.security.tls.certificateSecretRef). It never creates, updates, or deletes Secrets — credential management is the cluster administrator's responsibility. This read-only constraint minimizes the blast radius if the operator pod is compromised.

Event Recording

API GroupResourceVerbsPurpose
(core)eventscreate, patchRecord Kubernetes events during reconciliation

Rationale: The reconciler uses the Kubernetes event recorder to emit events (e.g., resource created, updated, or errored). Event recording requires only create (for new events) and patch (for updating event counts on repeated occurrences). No read or delete access is needed.


Least-Privilege Constraints

The ClusterRole enforces the following constraints:

ConstraintEnforcement
No wildcard verbs (*)Every rule lists explicit verbs
No wildcard resources (*)Every rule names specific resources
No wildcard API groups (*)Every rule names specific API groups (or empty string for core)
Exactly 10 rulesPrevents unintended permission creep; any new rule requires updating the rule-count test
Secrets are read-onlyOnly get, list, watch — no create, update, patch, or delete
Events are write-onlyOnly create, patch — no get, list, watch, or delete

These constraints are enforced by automated tests in internal/controller/memcached_rbac_test.go.


ClusterRoleBinding

The manager-rolebinding ClusterRoleBinding binds the ClusterRole to the operator's ServiceAccount:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: manager-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: manager-role
subjects:
- kind: ServiceAccount
  name: controller-manager
  namespace: system
FieldValueDescription
roleRef.kindClusterRoleReferences the cluster-scoped role
roleRef.namemanager-roleThe ClusterRole containing all operator permissions
subjects[0].kindServiceAccountThe operator runs as a Kubernetes ServiceAccount
subjects[0].namecontroller-managerServiceAccount name used by the operator pod
subjects[0].namespacesystemNamespace where the operator is deployed (kustomize replaces with actual namespace)

ServiceAccount

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: controller-manager
  namespace: system

The controller-manager ServiceAccount is created in the operator's namespace. The operator Deployment references this ServiceAccount in spec.template.spec.serviceAccountName, and Kubernetes automatically mounts a projected token that carries the ClusterRole permissions.


Kubebuilder RBAC Markers

All 10 RBAC rules are generated from markers on the MemcachedReconciler in internal/controller/memcached_controller.go:

go
// +kubebuilder:rbac:groups=memcached.c5c3.io,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=memcached.c5c3.io,resources=memcacheds/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=memcached.c5c3.io,resources=memcacheds/finalizers,verbs=update
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch

These markers are the single source of truth. controller-gen reads them and generates config/rbac/role.yaml.


Regenerating Manifests

To regenerate the ClusterRole from RBAC markers:

bash
make manifests

This runs controller-gen rbac:roleName=manager-role which reads the +kubebuilder:rbac markers and writes config/rbac/role.yaml.

To verify that committed manifests match the generated output:

bash
make verify-manifests

This regenerates manifests and checks for any diff. If the committed files are out of date, the command fails with an error.


Automated Test Coverage

The RBAC configuration is verified by tests in internal/controller/memcached_rbac_test.go:

TestWhat It Verifies
ClusterRole metadata / should have exactly 10 rulesRule count prevents unintended permission creep
Memcached CR permissions / should grant full CRUD on memcachedsAll 7 CRUD verbs on the primary CR
Memcached CR permissions / should grant get, update, patch on memcacheds/statusStatus subresource verbs
Memcached CR permissions / should grant update on memcacheds/finalizersFinalizer subresource verbs
owned resource permissions / DeploymentsFull CRUD on apps/deployments
owned resource permissions / ServicesFull CRUD on core/services
owned resource permissions / PodDisruptionBudgetsFull CRUD on policy/poddisruptionbudgets
owned resource permissions / NetworkPoliciesFull CRUD on networking.k8s.io/networkpolicies
owned resource permissions / ServiceMonitorsFull CRUD on monitoring.coreos.com/servicemonitors
Secrets permission / should grant read-only accessSecrets limited to get, list, watch
events permission / should grant create and patchEvents limited to create, patch
least-privilege constraints / should not contain wildcard verbsNo * in any verb list
least-privilege constraints / should not contain wildcard resourcesNo * in any resource list
least-privilege constraints / should not contain wildcard API groupsNo * in any API group list
ClusterRoleBinding / should reference the manager-role ClusterRoleBinding references correct ClusterRole
ClusterRoleBinding / should bind to controller-manager ServiceAccountBinding targets correct SA in system namespace