From 7114824c0646e551b3d7319880498cf5f9e0320a Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Thu, 4 Jun 2026 12:21:13 +0000 Subject: [PATCH] fix(rbac): tighten dashboard SA cluster-read to namespaces+nodes only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit namespace-owners could read all tenants' pods/configmaps/etc cluster-wide (read-only) via the broad namespace_owner_readonly role. Give the dashboard SAs a dedicated dashboard-nav-readonly ClusterRole = namespaces + nodes (list) only — enough for the dashboard namespace-picker/Nodes view, but no cross-tenant resource reads. Own-namespace access (admin) unchanged. Verified: gheorghe can list namespaces/nodes + full vabbit81, but list pods/configmaps -A = no, other namespaces = no. Co-Authored-By: Claude Opus 4.8 --- stacks/rbac/modules/rbac/dashboard-sa.tf | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/stacks/rbac/modules/rbac/dashboard-sa.tf b/stacks/rbac/modules/rbac/dashboard-sa.tf index 5d6e9f8b..bb41c91d 100644 --- a/stacks/rbac/modules/rbac/dashboard-sa.tf +++ b/stacks/rbac/modules/rbac/dashboard-sa.tf @@ -44,7 +44,23 @@ resource "kubernetes_role_binding" "dashboard_owner_admin" { } } -# Cluster-wide read-only so the dashboard nav (namespaces, nodes, etc.) renders. +# Minimal cluster-read for the dashboard nav ONLY: the namespace picker needs to +# list namespaces, and the Nodes view needs nodes. Deliberately does NOT grant +# cluster-wide read of pods/services/configmaps/etc — a namespace-owner can see +# the namespace LIST but can only read resources INSIDE their own namespace +# (where they have `admin`). Keeps tenants from reading each other's workloads +# and configmaps. (Separate from the broader OIDC `namespace_owner_readonly`.) +resource "kubernetes_cluster_role" "dashboard_nav_readonly" { + metadata { + name = "dashboard-nav-readonly" + } + rule { + api_groups = [""] + resources = ["namespaces", "nodes"] + verbs = ["get", "list", "watch"] + } +} + resource "kubernetes_cluster_role_binding" "dashboard_owner_readonly" { for_each = nonsensitive({ for pair in local.namespace_owner_pairs : "${pair.user_key}-${pair.namespace}" => pair }) @@ -55,7 +71,7 @@ resource "kubernetes_cluster_role_binding" "dashboard_owner_readonly" { role_ref { api_group = "rbac.authorization.k8s.io" kind = "ClusterRole" - name = kubernetes_cluster_role.namespace_owner_readonly.metadata[0].name + name = kubernetes_cluster_role.dashboard_nav_readonly.metadata[0].name } subject {