Skip to content

Commit 83e864c

Browse files
authored
Rename the Cloud AI group to radar:ai:reader before 1.16.0 (#1973)
## Why The group Radar Cloud's person-less AI runs read as was `radar:ai`. A write-capable AI identity will need its own opt-in group one day; with `radar:ai` as the reader, that second group (e.g. `radar:ai:operator`) would sit awkwardly beside it. Naming the reader `radar:ai:reader` puts the read-only contract in the name and leaves a clean sibling for later. `radar:ai` hasn't been released (latest tag v1.15.0), so this is a straight rename with no compatibility code. It has to land before v1.16.0 is tagged. ## What - The chart binds `radar:ai:reader` (was `radar:ai`) in `cloud-rbac-ai.yaml` and the cluster-read and integration-read add-ons. Binding names and `cloud.aiRbac` are unchanged. - Chart README, `values.yaml` comments and `docs/authentication.md` name the group, state that it is permanently read-only, and say a future write-capable AI gets its own opt-in group. - The identity filter's comment mentions the AI username `radar:ai:<org>`; a test checks `radar:ai:reader` is accepted in Cloud mode. - Chart unit tests, `scripts/test-chart.sh` and the integration-read Go test expect the new group. `radar:system` is unchanged. ## Tests - `scripts/test-chart.sh` and `helm unittest`; the 2 `service_test.yaml` failures also fail on `main` locally, while CI's Helm chart job passes. - `go build ./...`, the `internal/k8s`, `internal/helm` and `internal/mcp` suites, and `pkg/auth` (separate module). - Rendered with `cloud.enabled=true`: every AI binding names `radar:ai:reader`. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > This changes the Kubernetes group name for automatic Diagnose RBAC; clusters upgrading need the chart (and Cloud hub) aligned or automatic Diagnose loses effective permissions until bindings are updated. > > **Overview** > Renames the Radar Cloud **automatic Diagnose** Kubernetes RBAC group from `radar:ai` to **`radar:ai:reader`**, and documents that those runs impersonate username **`radar:ai:<org>`** with that group only. Chart default bindings (`cloud.aiRbac`, role names) are unchanged; only the **Group** subject in the AI read, cluster-read, and integration-read templates is updated, including the integration-read template’s special case for the `ai` tier. > > Docs (`README`, `values.yaml`, `authentication.md`) now state the reader group stays **read-only** and that a future write-capable AI would use a separate group (e.g. `radar:ai:operator`). **`pkg/auth`** adds a Cloud-mode test that **`radar:ai:<org>`** with **`radar:ai:reader`** is allowed; Helm unit tests, `scripts/test-chart.sh`, and the integration-read Go test expect the new group name. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 5f2587d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 2a4ffb1 commit 83e864c

13 files changed

Lines changed: 72 additions & 61 deletions

‎deploy/helm/radar/README.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,16 @@ integration-read add-ons, and cluster-wide `get/list/watch` on Secrets.
170170
Helm release alerts and Secret changes in the timeline need Secret read;
171171
Helm stores releases as Secrets. The grant includes no writes.
172172

173-
### Radar Cloud automatic Diagnose (`radar:ai`)
173+
### Radar Cloud automatic Diagnose (`radar:ai:reader`)
174174

175175
Manual Diagnose turns read as the person who started them. Radar Cloud forwards
176-
that person's identity and groups; `radar:ai` is not involved. MCP clients also
177-
read with the user's own permissions.
176+
that person's identity and groups; `radar:ai:reader` is not involved. MCP clients
177+
also read with the user's own permissions.
178178

179-
Automatic (alert-triggered) Diagnose runs read as `radar:ai` only, a permanent
180-
read-only background diagnostic reader. Never grant this group write
181-
permissions; a future write-capable feature needs its own group.
179+
Automatic (alert-triggered) Diagnose runs use username `radar:ai:<org>` and
180+
group `radar:ai:reader` only, a permanent read-only background diagnostic reader.
181+
Never grant this group write permissions; a future write-capable AI identity
182+
needs a separate opt-in group (e.g. `radar:ai:operator`).
182183
`cloud.aiRbac` (default `true`) creates the chart's default grant: a chart-owned
183184
role aggregated to match `view` plus the cluster-read and integration-read
184185
add-ons. With the standard `view` role this grant includes no Kubernetes Secret

‎deploy/helm/radar/templates/_helpers.tpl‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ false
8989
{{- end -}}
9090

9191
{{/*
92-
Whether the default read grant for automatic Diagnose (radar:ai) renders.
92+
Whether the default read grant for automatic Diagnose (radar:ai:reader) renders.
9393
An absent value means OFF, as with radar.cloudSystemRbac: a
9494
`--reuse-values` upgrade from a release that predates the key never gains a
9595
new reader without someone choosing it.

‎deploy/helm/radar/templates/cloud-rbac-ai.yaml‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{{- /*
22
Read-only access for automatic (alert-triggered) Radar Cloud Diagnose runs.
33

4-
Automatic runs read as radar:ai only. Manual turns read as the person who
5-
started them, with that person's identity and groups forwarded by Radar Cloud;
6-
radar:ai is not involved. Keep radar:ai permanently read-only; any future
7-
write-capable feature needs its own group.
4+
Automatic runs use username radar:ai:<org> and group radar:ai:reader only.
5+
Manual turns read as the person who started them, with that person's identity
6+
and groups forwarded by Radar Cloud; radar:ai:reader is not involved. Keep
7+
radar:ai:reader permanently read-only; a future write-capable AI identity needs
8+
a separate opt-in group (e.g. radar:ai:operator).
89

910
The default grant is view plus the cluster-read and integration-read add-ons.
1011
With the standard view role it grants no Kubernetes Secret permission; it
@@ -44,6 +45,6 @@ roleRef:
4445
name: {{ include "radar.fullname" . }}-cloud-ai-read
4546
subjects:
4647
- kind: Group
47-
name: radar:ai
48+
name: radar:ai:reader
4849
apiGroup: rbac.authorization.k8s.io
4950
{{- end -}}

‎deploy/helm/radar/templates/cloud-rbac-cluster-read.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ roleRef:
206206
name: {{ $fullname }}-cluster-read
207207
subjects:
208208
- kind: Group
209-
name: radar:ai
209+
name: radar:ai:reader
210210
apiGroup: rbac.authorization.k8s.io
211211
{{- end }}
212212
{{- end -}}

‎deploy/helm/radar/templates/cloud-rbac-integration-read.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ roleRef:
5151
name: {{ $name }}
5252
subjects:
5353
- kind: Group
54-
name: radar:{{ $tier }}
54+
name: radar:{{ if eq $tier "ai" }}ai:reader{{ else }}{{ $tier }}{{ end }}
5555
apiGroup: rbac.authorization.k8s.io
5656
{{- if not (has $tier (list "system" "ai")) }}
5757
- kind: Group

‎deploy/helm/radar/tests/cloud_rbac_ai_test.yaml‎

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
suite: Cloud radar:ai RBAC
1+
suite: Cloud radar:ai:reader RBAC
22
templates:
33
- cloud-rbac-ai.yaml
44
- cloud-rbac-cluster-read.yaml
@@ -35,7 +35,7 @@ tests:
3535
- notExists:
3636
path: metadata.labels["rbac.authorization.k8s.io/aggregate-to-admin"]
3737

38-
- it: binds only radar:ai to the owned read role
38+
- it: binds only radar:ai:reader to the owned read role
3939
template: cloud-rbac-ai.yaml
4040
documentIndex: 1
4141
asserts:
@@ -54,10 +54,10 @@ tests:
5454
path: subjects
5555
value:
5656
- kind: Group
57-
name: radar:ai
57+
name: radar:ai:reader
5858
apiGroup: rbac.authorization.k8s.io
5959

60-
- it: keeps radar:ai's read grant when the tier bindings are off
60+
- it: keeps radar:ai:reader's read grant when the tier bindings are off
6161
set:
6262
cloud.defaultRbac.create: false
6363
template: cloud-rbac-ai.yaml
@@ -67,7 +67,7 @@ tests:
6767
count: 2
6868
- equal:
6969
path: subjects[0].name
70-
value: radar:ai
70+
value: radar:ai:reader
7171

7272
# --reuse-values from a release predating aiRbac renders with the key
7373
# absent: nothing renders, so an existing install never gains a new reader
@@ -104,7 +104,7 @@ tests:
104104
- hasDocuments:
105105
count: 0
106106

107-
- it: binds radar:ai to no Secret role
107+
- it: binds radar:ai:reader to no Secret role
108108
set:
109109
cloud.defaultRbac.create: false
110110
template: cloud-rbac-ai.yaml
@@ -118,7 +118,7 @@ tests:
118118
path: roleRef.name
119119
value: RELEASE-NAME-radar-cloud-ai-read
120120

121-
- it: gives radar:ai the cluster-read add-on without the tiers
121+
- it: gives radar:ai:reader the cluster-read add-on without the tiers
122122
set:
123123
cloud.defaultRbac.create: false
124124
cloud.systemRbac: false
@@ -142,11 +142,11 @@ tests:
142142
path: subjects
143143
value:
144144
- kind: Group
145-
name: radar:ai
145+
name: radar:ai:reader
146146
apiGroup: rbac.authorization.k8s.io
147147
documentIndex: 1
148148

149-
- it: gives radar:ai the cluster-read add-on beside the tiers
149+
- it: gives radar:ai:reader the cluster-read add-on beside the tiers
150150
template: cloud-rbac-cluster-read.yaml
151151
documentSelector:
152152
path: metadata.name
@@ -156,7 +156,7 @@ tests:
156156
path: roleRef.name
157157
value: RELEASE-NAME-radar-cluster-read
158158

159-
- it: drops radar:ai's cluster-read binding when aiRbac is false
159+
- it: drops radar:ai:reader's cluster-read binding when aiRbac is false
160160
set:
161161
cloud.aiRbac: false
162162
template: cloud-rbac-cluster-read.yaml
@@ -165,11 +165,11 @@ tests:
165165
path: subjects
166166
content:
167167
kind: Group
168-
name: radar:ai
168+
name: radar:ai:reader
169169
apiGroup: rbac.authorization.k8s.io
170170
any: true
171171

172-
- it: gives radar:ai both integration-read add-ons without the tiers
172+
- it: gives radar:ai:reader both integration-read add-ons without the tiers
173173
set:
174174
cloud.defaultRbac.create: false
175175
cloud.systemRbac: false
@@ -181,7 +181,7 @@ tests:
181181
path: subjects
182182
value:
183183
- kind: Group
184-
name: radar:ai
184+
name: radar:ai:reader
185185
apiGroup: rbac.authorization.k8s.io
186186
documentSelector:
187187
path: metadata.name
@@ -190,13 +190,13 @@ tests:
190190
path: subjects
191191
value:
192192
- kind: Group
193-
name: radar:ai
193+
name: radar:ai:reader
194194
apiGroup: rbac.authorization.k8s.io
195195
documentSelector:
196196
path: metadata.name
197197
value: RELEASE-NAME-radar-cloud-ai-integration-read-cluster
198198

199-
- it: gives radar:ai the integration-read add-ons beside the tiers
199+
- it: gives radar:ai:reader the integration-read add-ons beside the tiers
200200
template: cloud-rbac-integration-read.yaml
201201
documentSelector:
202202
path: metadata.name

‎deploy/helm/radar/tests/cloud_rbac_integration_read_test.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set:
66
cloud.token: dummy
77
cloud.url: wss://cloud.example
88
cloud.clusterName: test
9-
# Tier behavior only; radar:system's and radar:ai's own bindings are covered
9+
# Tier behavior only; radar:system's and radar:ai:reader's own bindings are covered
1010
# below, in cloud_rbac_system_test.yaml and in cloud_rbac_ai_test.yaml.
1111
cloud.systemRbac: false
1212
cloud.aiRbac: false

‎deploy/helm/radar/values.yaml‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,14 +529,16 @@ cloud:
529529
# False or absent (e.g. an older --reuse-values install) creates no default
530530
# grant; customer bindings remain. This is not a feature switch.
531531
systemRbac: true
532-
# Create the default read grant for radar:ai, used only by automatic
533-
# (alert-triggered) Diagnose runs. Manual turns read as the person who started
534-
# them; Radar Cloud forwards that person's identity and groups.
532+
# Create the default read grant for radar:ai:reader, used only by automatic
533+
# (alert-triggered) Diagnose runs with username radar:ai:<org>. Manual turns
534+
# read as the person who started them; Radar Cloud forwards that person's
535+
# identity and groups.
535536
# Includes view via a chart-owned aggregated role and the cluster-read and
536537
# integration-read add-ons. No Kubernetes Secret permission with the standard
537538
# view role (it inherits anything added to view via aggregate-to-view); pod
538-
# logs and ConfigMaps are readable and may contain sensitive data. Keep radar:ai
539-
# permanently read-only; future write-capable features need their own group.
539+
# logs and ConfigMaps are readable and may contain sensitive data. Keep
540+
# radar:ai:reader permanently read-only; a future write-capable AI identity
541+
# needs a separate opt-in group (e.g. radar:ai:operator).
540542
# Independent of defaultRbac. False or absent (e.g. an older --reuse-values
541543
# install) creates no default grant; customer bindings remain. Radar Cloud
542544
# controls whether Diagnose runs; this is not a feature switch.

‎docs/authentication.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ Under cloud-mode (`RADAR_CLOUD_MODE=true`, set automatically by the chart when `
233233
- Ships three default ClusterRoleBindings mapping Cloud's `radar:owner` / `radar:member` / `radar:viewer` groups to the standard K8s `admin` / `edit` / `view` ClusterRoles. Configurable via `cloud.defaultRbac.*` in `values.yaml`.
234234
- Adds a **cluster-read add-on** (`cloud.defaultRbac.clusterScopedRead.{viewer,member,owner}`, each default on) granting `get/list/watch` on infrastructure the built-in `view`/`edit`/`admin` roles exclude — Nodes, PersistentVolumes, StorageClasses, IngressClasses, PriorityClasses, RuntimeClasses, ClusterTrustBundles, CRDs, and admission webhook configurations — plus list-only Upgrade impact evidence for CSIDrivers, CSIStorageCapacities, legacy PodSecurityPolicies, and API flow-control configuration. When the matching collection is enabled, it also grants APIServices, PrometheusRules, Karpenter kinds, and node metrics. It does not grant Secrets, RBAC objects, API-server metrics, or kubelet proxy access. It's an independent axis per tier: set a tier `false` to make it namespaced-only (e.g. `clusterScopedRead.viewer: false`). Owner node cordon/drain is a separate cluster-scoped *write*, off by default (`cloud.defaultRbac.nodeOps`).
235235
- Creates the default read grant for `radar:system`, Radar Cloud's alerts worker and timeline puller: a chart-owned role aggregated to match `view`, the cluster-read and integration-read add-ons, and cluster-wide Secret read. Helm release alerts and Secret changes in the timeline need Secret read; Helm stores releases as Secrets. No writes. Controlled by `cloud.systemRbac` (default `true`).
236-
- Creates the default read grant for automatic (alert-triggered) Diagnose runs, which read as `radar:ai` only: a chart-owned role aggregated to match `view` plus the cluster-read and integration-read add-ons. No Kubernetes Secret permission or writes with the standard `view` role (the aggregated role also inherits anything your cluster adds to `view`); pod logs and ConfigMaps are readable and may contain sensitive data. Keep `radar:ai` permanently read-only; a future write-capable feature needs its own group. Controlled by `cloud.aiRbac` (default `true`). Manual Diagnose turns read as the person who started them, with that person's identity and groups forwarded by Radar Cloud; `radar:ai` is not involved. MCP clients also read with the user's own permissions.
236+
- Creates the default read grant for automatic (alert-triggered) Diagnose runs, which use username `radar:ai:<org>` and group `radar:ai:reader` only: a chart-owned role aggregated to match `view` plus the cluster-read and integration-read add-ons. No Kubernetes Secret permission or writes with the standard `view` role (the aggregated role also inherits anything your cluster adds to `view`); pod logs and ConfigMaps are readable and may contain sensitive data. Keep `radar:ai:reader` permanently read-only; a future write-capable AI identity needs a separate opt-in group (e.g. `radar:ai:operator`). Controlled by `cloud.aiRbac` (default `true`). Manual Diagnose turns read as the person who started them, with that person's identity and groups forwarded by Radar Cloud; `radar:ai:reader` is not involved. MCP clients also read with the user's own permissions.
237237
- Treats `cloud.aiRbac` and `cloud.systemRbac` as controls for the chart's default grants, independent of `cloud.defaultRbac`. False or absent (including a plain `--reuse-values` upgrade from an older chart) creates no default grant and leaves customer-created bindings untouched. These are not feature switches; Radar Cloud controls whether Diagnose runs. Without any grant, the background group has no cluster access; automatic Diagnose has no viewer fallback.
238238
- Requires cluster-admin-equivalent authority or `escalate` on ClusterRoles to create or update the AI/system aggregated roles, in addition to normal chart installation permissions. Restricted installers can set `cloud.aiRbac=false` and `cloud.systemRbac=false` and supply their own read-only group bindings.
239239
- Uses the Cloud role (`radar:owner` / `radar:member` / `radar:viewer`) only to gate Radar's own configuration (settings, integrations, audit settings, registered OCI chart sources, self-upgrade), which runs as Radar's ServiceAccount. A Cloud request with no role group (such as the Hub's `radar:system` background identity) is refused there. Operations that run as the impersonated user — Helm reads and writes, in-cluster probe pods — are decided by Kubernetes RBAC alone.

‎internal/k8s/integration_read_baseline_test.go‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ func TestIntegrationReadBindings(t *testing.T) {
174174
name, overrides string
175175
namespaced, cluster []string
176176
removeMap, removeKey bool
177-
// radar:system and radar:ai are bound whenever cloud mode renders
177+
// radar:system and radar:ai:reader are bound whenever cloud mode renders
178178
// RBAC at all, independent of the tier settings.
179179
noSystem, noAI bool
180180
}{
@@ -231,8 +231,12 @@ func TestIntegrationReadBindings(t *testing.T) {
231231
continue
232232
}
233233
found = true
234-
wantSubjects := []rbacv1.Subject{{Kind: "Group", Name: "radar:" + tier, APIGroup: rbacv1.GroupName}}
235-
// radar:system and radar:ai have no legacy cloud:* twin.
234+
group := "radar:" + tier
235+
if tier == "ai" {
236+
group = "radar:ai:reader"
237+
}
238+
wantSubjects := []rbacv1.Subject{{Kind: "Group", Name: group, APIGroup: rbacv1.GroupName}}
239+
// radar:system and radar:ai:reader have no legacy cloud:* twin.
236240
if tier != "system" && tier != "ai" {
237241
wantSubjects = append(wantSubjects, rbacv1.Subject{Kind: "Group", Name: "cloud:" + tier, APIGroup: rbacv1.GroupName})
238242
}

0 commit comments

Comments
 (0)