Read-Only Service Account¶
By default, Robusta's runner service account has permissions to create, update, and delete Kubernetes resources. This guide explains how to restrict the runner to read-only permissions for environments where you want to prevent any modifications to cluster resources.
Why Read-Only Mode?¶
Read-only mode is useful in scenarios where you want to:
Prevent accidental modifications: Ensure that even if a playbook or investigation logic has a bug, no cluster resources will be modified
Comply with security policies: Meet organizational requirements for read-only access in certain environments
Prevent node operations: Prevent users from draining or restarting nodes through investigations
Audit-only mode: Run Holmes for investigation and diagnostics without remediation capabilities
Limitations of Read-Only Mode¶
When using read-only permissions, the following Robusta features will not be available:
Auto-remediation: Playbooks that automatically fix issues (restart pods, scale deployments, drain nodes, etc.)
Silence management: Creating or deleting alert silences
Pod debugging: Live debugging tools that require container execution
Resource modification: Any playbook or action that modifies Kubernetes resources
These features require write permissions and will gracefully fail if attempted with read-only service account.
Read-only mode is ideal for: Investigation, diagnostics, log analysis, metric enrichment, and reporting.
Implementation: Using overrideClusterRoles¶
Robusta's Helm chart supports the runner.overrideClusterRoles parameter. When set, the rules you
provide fully replace the built-in runner ClusterRole rules, so only the permissions you list are granted.
Note
Do not confuse this with runner.customClusterRoleRules. That parameter adds rules on top of the
built-in rules (which include write verbs), so it cannot be used to make the runner read-only.
Use runner.overrideClusterRoles for read-only mode.
To use read-only mode, create a custom values file with the following configuration:
runner:
overrideClusterRoles:
# Core API resources - read-only
- apiGroups:
- ""
resources:
- configmaps
- daemonsets
- deployments
- events
- namespaces
- persistentvolumes
- persistentvolumeclaims
- pods
- pods/status
- pods/log
- replicasets
- replicationcontrollers
- services
- serviceaccounts
- endpoints
verbs:
- get
- list
- watch
# Nodes - read-only
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
# Apps API - read-only
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
verbs:
- get
- list
- watch
# Batch API - read-only
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- get
- list
- watch
# Autoscaling - read-only
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- get
- list
- watch
# RBAC - read-only
- apiGroups:
- rbac.authorization.k8s.io
resources:
- clusterroles
- clusterrolebindings
- roles
- rolebindings
verbs:
- get
- list
- watch
# Networking - read-only
- apiGroups:
- networking.k8s.io
resources:
- ingresses
- networkpolicies
verbs:
- get
- list
- watch
# Events - read-only
- apiGroups:
- events.k8s.io
resources:
- events
verbs:
- get
- list
# CRDs - read-only
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- list
- get
# API Registration - read-only
- apiGroups:
- apiregistration.k8s.io
resources:
- apiservices
verbs:
- get
- list
# Policy - read-only
- apiGroups:
- policy
resources:
- poddisruptionbudgets
- podsecuritypolicies
verbs:
- get
- list
# Monitoring (optional) - read-only
- apiGroups:
- monitoring.coreos.com
resources:
- prometheusrules
- servicemonitors
- podmonitors
- alertmanagers
- silences
verbs:
- get
- list
- watch
# Argo CD (optional) - read-only
- apiGroups:
- argoproj.io
resources:
- applications
- applicationsets
- appprojects
- workflows
- workflowtemplates
- cronworkflows
- rollouts
- analysisruns
- analysistemplates
- experiments
verbs:
- get
- list
- watch
Then install or upgrade Robusta with this values file:
helm upgrade --install robusta robusta/robusta \
-f generated_values.yaml \
-f read-only-values.yaml \
-n robusta-system --create-namespace
Verifying Read-Only Permissions¶
After installation, verify that the runner service account has only read permissions. The ClusterRole is cluster-scoped, so no namespace flag is needed:
# Inspect the ClusterRole - the rules should only contain "get", "list", "watch" verbs,
# and NOT "create", "delete", "patch", or "update".
kubectl describe clusterrole robusta-runner-cluster-role
Testing Write Protection¶
Use kubectl auth can-i to confirm what the runner service account can and cannot do
(replace robusta-system with your release namespace):
SA=system:serviceaccount:robusta-system:robusta-runner-service-account
kubectl auth can-i list pods --as=$SA -n default # -> yes
kubectl auth can-i delete pods --as=$SA -n default # -> no
kubectl auth can-i patch deployments --as=$SA -n default # -> no
kubectl auth can-i create pods/exec --as=$SA -n default # -> no
The read verbs should return yes while all write/exec verbs return no, confirming the runner is read-only.
Notes and Recommendations¶
CRD Permissions: If you have custom operators (Argo, Flux, Kafka, KEDA, etc.), add their CRD groups to the read-only rules above with only
get,list,watchverbsPerformance: Read-only mode may improve performance slightly since no write operations are performed
Logging: Monitor Robusta logs for any "permission denied" errors to identify features that require write access