Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d6db910
Let Kubernetes RBAC, not the Cloud role, decide user-run operations
arlenvasconcelos Sep 25, 2026
e23185f
Explain an empty cluster to users who can read no namespace
arlenvasconcelos Sep 25, 2026
7da7e0b
Drop the RBAC snippet from the no-access banner
arlenvasconcelos Sep 25, 2026
00e502d
chart: bind only radar:system for hub background identities
arlenvasconcelos Sep 27, 2026
5b3fea7
Show every group in the no-access banner
arlenvasconcelos Sep 27, 2026
e3622c5
Leave the RBAC snippet's subject to the admin
arlenvasconcelos Sep 27, 2026
acbdbad
chart: radar:system is one all-or-nothing grant, and Cloud Helm no lo…
arlenvasconcelos Sep 29, 2026
55fc3ef
Let Helm writes check the signed-in user, not Radar's ServiceAccount
arlenvasconcelos Sep 29, 2026
e51efe0
Answer /auth/me's no-namespace flag from the permission cache only
arlenvasconcelos Sep 29, 2026
c401888
chart: radar:system is for the alerts worker and timeline puller only
arlenvasconcelos Sep 29, 2026
6eb752a
Keep asking about namespace access until the server knows
arlenvasconcelos Sep 29, 2026
af4afc7
Keep the no-access banner's group IDs out of session replay
arlenvasconcelos Sep 29, 2026
c0f51b9
Revert "Keep asking about namespace access until the server knows"
arlenvasconcelos Sep 29, 2026
48c83d5
Gate Helm values preview like the write it previews
arlenvasconcelos Sep 29, 2026
60ae376
Render the no-access banner's groups with the shared Badge
arlenvasconcelos Sep 29, 2026
812e48f
Debounce the Helm write check and drop leftovers from the role gate
arlenvasconcelos Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion deploy/helm/radar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,27 @@ Radar binary (including Radar Cloud self-upgrade) does not update RBAC. Missing
on an older `--reuse-values` installation default to enabled; set explicit false
before upgrading if the added visibility is unwanted.

### Radar Cloud background identities (`radar:system`)

The hub's alerts worker and timeline puller call Radar
as the `radar:system` group, not as a user. `cloud.systemRbac` (default `true`)
binds that group to a read-only set: `view`, the cluster-read and
integration-read add-ons above, and `get/list/watch` on Secrets. Secret read is
what Helm release alerts and Secret changes in the hub timeline need, because
Helm stores each release as a Secret.

It is independent of `cloud.defaultRbac`, so turning the role bindings off
(`cloud.defaultRbac.create=false`, for example when IdP groups decide cluster
access) leaves alerts and the hub timeline working. It is all or nothing:
`cloud.systemRbac=false` removes the whole grant, and alerts and the hub
timeline then see nothing on clusters without the role bindings unless
you bind `radar:system` yourself. Change it through this value: a binding
edited or deleted with `kubectl` comes back on the next upgrade.

An upgrade with `--reuse-values` from a release that predates the key leaves it
off, so an existing install never gains Secret read without choosing it. Set
`cloud.systemRbac=true` on those installs.

### Connecting to Argo CD (GitOps deep diff)

Radar's GitOps pages show a Git-rendered desired-vs-live diff when connected to
Expand Down Expand Up @@ -347,7 +368,7 @@ Disabled by default for security:
| Terminal | `rbac.podExec: true` | Shell access to pods |
| Port Forward | `rbac.portForward: true` | Port forwarding to pods. Also the fallback for traffic sources (Hubble/Caretta) — Radar dials the relay/metrics Service directly first, so in-cluster installs only need this when a NetworkPolicy or routing blocks Radar's namespace from reaching the service |
| Logs | `rbac.podLogs: true` | View pod logs (**enabled by default**) |
| Helm Write | `rbac.helm: true` | Install/upgrade/rollback/uninstall Helm releases. Under auth or cloud-mode, also emits a split helm add-on ClusterRole — `radar-helm` (member-safe: CRDs, storage, namespaces) and `radar-helm-admin` (owner-only: RBAC, webhooks, ApiServices) |
| Helm Write | `rbac.helm: true` | Install/upgrade/rollback/uninstall Helm releases. No-auth installs: grants Radar's ServiceAccount create/update/patch/delete on all resource types, since Helm runs as it. Under auth or cloud-mode Helm runs as the signed-in user, so the ServiceAccount gets no write grant; instead a split helm add-on ClusterRole is emitted — `radar-helm` (member-safe: CRDs, storage, namespaces) and `radar-helm-admin` (owner-only: RBAC, webhooks, ApiServices) |
| RBAC view | `rbac.viewRBAC: true` | Show ClusterRoles, ClusterRoleBindings, Roles, RoleBindings in the resource browser. Off by default — cache-served reads bypass per-user RBAC, so this exposes the cluster's authorization graph to every authenticated Radar user. Auto-enabled under auth or cloud mode (every read is re-checked per user there). |
| Webhooks view | `rbac.viewWebhooks: true` | Show MutatingWebhookConfigurations and ValidatingWebhookConfigurations in the resource browser. Off by default — the configurations reveal which admission controls are enforced (Gatekeeper / Kyverno policies, image scanners, DLP) and where the gaps are, which is recon value for a low-trust viewer. Auto-enabled under auth or cloud mode. |
| Node runtime evidence | `rbac.viewNodeRuntime: true` | Let upgrade-impact checks inspect kubelet metrics and effective configuration through `nodes/proxy`. Off by default because this exposes node-level runtime and configuration details to anyone who can reach a no-auth Radar install. Under auth, grant `get` on `nodes/proxy` to each Kubernetes identity that should inspect this evidence. |
Expand Down
16 changes: 16 additions & 0 deletions deploy/helm/radar/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,19 @@ Create the name of the service account to use
{{- end -}}
{{- $settings | toPrettyJson -}}
{{- end -}}

{{/*
Whether the read-only binding for Radar Cloud's own background identities
(radar:system) renders. It includes cluster-wide Secret read, so an absent
value means OFF: a `--reuse-values` upgrade from a release that predates the
key renders with the previous release's tree and never gains Secret read
Comment on lines +78 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Helm comments retain upgrade history 📘 Rule violation ⚙ Maintainability

The new radar.cloudSystemRbac template comment describes a --reuse-values upgrade from a release
that predates the setting. The same release-history explanation appears in the values file and chart
test, putting migration history in code comments rather than only in release documentation.
Agent Prompt
## Issue description
Added Helm comments describe releases that predate `systemRbac`, contrary to the code-comment rule.

## Fix Focus Areas
- deploy/helm/radar/templates/_helpers.tpl[75-82]
- deploy/helm/radar/values.yaml[534-535]
- deploy/helm/radar/tests/cloud_rbac_system_test.yaml[109-111]

## Recommended Fix
Keep the security rationale and absent-value behavior in code comments, but move release-specific upgrade history to release documentation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it. The comment explains why an absent value means off, which is the logic, not history.

without someone choosing it. Fresh installs, plain upgrades, GitOps and
`--reset-then-reuse-values` get true from values.yaml.
*/}}
{{- define "radar.cloudSystemRbac" -}}
{{- if and .Values.cloud.enabled (eq (toString .Values.cloud.systemRbac) "true") -}}
true
{{- else -}}
false
{{- end -}}
{{- end -}}
24 changes: 21 additions & 3 deletions deploy/helm/radar/templates/cloud-rbac-cluster-read.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ from being truthy.
{{- $_ := set $csrOn $tier (ne (toString (index $csr $tier)) "false") }}
{{- end }}
{{- $rbacDefaults := .Values.cloud.defaultRbac }}
{{- $anyTier := or (and $rbacDefaults.viewer (get $csrOn "viewer")) (and $rbacDefaults.member (get $csrOn "member")) (and $rbacDefaults.owner (get $csrOn "owner")) }}
{{- if and .Values.cloud.enabled .Values.cloud.defaultRbac.create .Values.rbac.create $anyTier -}}
{{- $anyTier := and $rbacDefaults.create (or (and $rbacDefaults.viewer (get $csrOn "viewer")) (and $rbacDefaults.member (get $csrOn "member")) (and $rbacDefaults.owner (get $csrOn "owner"))) }}
{{- $systemOn := include "radar.cloudSystemRbac" . | eq "true" }}
{{- if and .Values.cloud.enabled .Values.rbac.create (or $anyTier $systemOn) -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
Expand Down Expand Up @@ -151,7 +152,7 @@ rules:
{{- $labels := include "radar.labels" . }}
{{- $rbac := .Values.cloud.defaultRbac }}
{{- range $tier := list "viewer" "member" "owner" }}
{{- if and (index $rbac $tier) (get $csrOn $tier) }}
{{- if and $rbac.create (index $rbac $tier) (get $csrOn $tier) }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
Expand All @@ -173,4 +174,21 @@ subjects:
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- end }}
{{- if $systemOn }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ $fullname }}-cloud-system-cluster-read
labels:
{{- $labels | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ $fullname }}-cluster-read
subjects:
- kind: Group
name: radar:system
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- end -}}
10 changes: 8 additions & 2 deletions deploy/helm/radar/templates/cloud-rbac-integration-read.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
{{- $rbac := .Values.cloud.defaultRbac -}}
{{- $integration := $rbac.integrationRead | default dict -}}
{{- $cluster := $rbac.clusterScopedRead | default dict -}}
{{- if and .Values.cloud.enabled .Values.rbac.create $rbac.create -}}
{{- $systemOn := include "radar.cloudSystemRbac" . | eq "true" -}}
{{- if and .Values.cloud.enabled .Values.rbac.create (or $rbac.create $systemOn) -}}
{{- $root := . -}}
{{- $baseline := .Files.Get "files/integration-read-baseline.yaml" | fromYaml -}}
{{- range $scope := list "Namespaced" "Cluster" -}}
{{- $tiers := list -}}
{{- range $tier := list "viewer" "member" "owner" -}}
{{- /* Missing keys in --reuse-values upgrades follow default-on; explicit false survives. */ -}}
{{- if and (index $rbac $tier) (ne (toString (index $integration $tier)) "false") (or (eq $scope "Namespaced") (ne (toString (index $cluster $tier)) "false")) -}}
{{- if and $rbac.create (index $rbac $tier) (ne (toString (index $integration $tier)) "false") (or (eq $scope "Namespaced") (ne (toString (index $cluster $tier)) "false")) -}}
{{- $tiers = append $tiers $tier -}}
{{- end -}}
{{- end -}}
{{- if $systemOn -}}
{{- $tiers = append $tiers "system" -}}
{{- end -}}
{{- $rules := list -}}
{{- range $entry := $baseline.entries -}}
{{- if and (eq $entry.decision "grant") (eq $entry.scope $scope) (or $root.Values.rbac.crdGroups.all (index $root.Values.rbac.crdGroups $entry.collection)) -}}
Expand Down Expand Up @@ -45,10 +49,12 @@ subjects:
- kind: Group
name: radar:{{ $tier }}
apiGroup: rbac.authorization.k8s.io
{{- if ne $tier "system" }}
- kind: Group
name: cloud:{{ $tier }}
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
60 changes: 60 additions & 0 deletions deploy/helm/radar/templates/cloud-rbac-system.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{{- /*
Read-only access for Radar Cloud's own background identities.

The hub's background identities have no user or IdP groups; they assert the
radar:system group. That is the alerts worker and the timeline puller (AI
diagnose reads as radar:viewer). Without a binding of their own they would ride
on radar:owner, which disappears when an org turns the role bindings off
(cloud.defaultRbac.create=false) to let IdP groups decide cluster access —
silently stopping alerts and the hub timeline.

The grant is the viewer set (view here, plus the cluster-read and
integration-read add-ons, which bind radar:system in their own templates) and
Secret read. Secret read is what Helm release alerts and Secret changes in the
hub timeline need: Radar lists Helm releases as the caller, and Helm stores
each release as a Secret. Kubernetes RBAC cannot narrow that to Helm's
Secrets, so it is all Secrets, read-only. No writes. All or nothing:
cloud.systemRbac=false removes the whole grant.
*/}}
{{- if and (include "radar.cloudSystemRbac" . | eq "true") .Values.rbac.create -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "radar.fullname" . }}-cloud-system-view
labels:
{{- include "radar.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: Group
name: radar:system
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "radar.fullname" . }}-system-secrets-read
labels:
{{- include "radar.labels" . | nindent 4 }}
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "radar.fullname" . }}-cloud-system-secrets-read
labels:
{{- include "radar.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "radar.fullname" . }}-system-secrets-read
subjects:
- kind: Group
name: radar:system
apiGroup: rbac.authorization.k8s.io
{{- end -}}
5 changes: 4 additions & 1 deletion deploy/helm/radar/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@ rules:
- podsecuritypolicies
verbs: ["list"]

{{- if .Values.rbac.helm }}
{{- if and .Values.rbac.helm (eq .Values.auth.mode "none") (not .Values.cloud.enabled) }}
# Helm write operations require broad permissions because charts can create
# any Kubernetes resource type. Read access for common resources is granted
# by other rules in this ClusterRole. Secrets read access is auto-enabled below.
# No-auth installs only: under auth or cloud mode every Helm action runs as the
# impersonated user, so the ServiceAccount never needs it there. The per-tier
# add-ons (clusterrole-helm*.yaml) are what users get instead.
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "update", "patch", "delete"]
Expand Down
10 changes: 10 additions & 0 deletions deploy/helm/radar/tests/cloud_rbac_integration_read_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ set:
cloud.token: dummy
cloud.url: wss://cloud.example
cloud.clusterName: test
# Tier behavior only; radar:system's own binding is covered below and in
# cloud_rbac_system_test.yaml.
cloud.systemRbac: false
tests:
- it: keeps both add-ons for radar:system when the tiers are off
set:
cloud.systemRbac: true
cloud.defaultRbac.create: false
asserts:
- hasDocuments:
count: 4
- it: binds both scopes to all three default tiers
asserts:
- hasDocuments:
Expand Down
Loading
Loading