From 97e4be57db1538f11532b6a82d6c54799029b5da Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Thu, 12 Feb 2026 10:56:08 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20fix:=20make=20generated=20RBAC?= =?UTF-8?q?=20the=20single=20source=20of=20truth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add MCP kubebuilder RBAC markers and regenerate manager-role permissions - move ServiceAccount/bindings into config/rbac and remove duplicated e2e/deploy RBAC files - update kind/CI/doc flows to apply config/rbac instead of deploy/rbac.yaml --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.93`_ --- .github/workflows/ci.yaml | 7 +- README.md | 2 +- .../bases/coder.com_codercontrolplanes.yaml | 5 +- config/rbac/auth-delegator-binding.yaml | 12 ++ .../rbac/authentication-reader-binding.yaml | 13 +++ .../clusterrolebinding.yaml} | 0 config/rbac/role.yaml | 29 +++++ config/{e2e => rbac}/serviceaccount.yaml | 0 deploy/rbac.yaml | 108 ------------------ docs/how-to/deploy-aggregated-apiserver.md | 4 +- docs/how-to/deploy-controller.md | 4 +- docs/how-to/mcp-server.md | 4 +- examples/cloudnativepg/README.md | 2 +- hack/kind-dev.sh | 5 +- internal/app/mcpapp/server.go | 8 ++ 15 files changed, 76 insertions(+), 127 deletions(-) create mode 100644 config/rbac/auth-delegator-binding.yaml create mode 100644 config/rbac/authentication-reader-binding.yaml rename config/{e2e/clusterrole-binding.yaml => rbac/clusterrolebinding.yaml} (100%) rename config/{e2e => rbac}/serviceaccount.yaml (100%) delete mode 100644 deploy/rbac.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 43c2a507..a8abd201 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -190,15 +190,14 @@ jobs: docker build -f Dockerfile.goreleaser -t ghcr.io/coder/coder-k8s:e2e . kind load docker-image ghcr.io/coder/coder-k8s:e2e --name e2e - - name: Apply CRDs and RBAC + - name: Apply namespace, CRDs, and RBAC run: | + kubectl apply -f config/e2e/namespace.yaml kubectl apply -f config/crd/bases/ kubectl apply -f config/rbac/ - name: Deploy controller - run: | - kubectl apply -f config/e2e/namespace.yaml - kubectl apply -f config/e2e/ + run: kubectl apply -f config/e2e/deployment.yaml - name: Wait for controller run: kubectl wait --for=condition=Available deploy/coder-k8s -n coder-system --timeout=120s diff --git a/README.md b/README.md index c27c33a1..20c6e845 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Mux users: there is an optional agent skill (`kind-dev`) under `.mux/skills/` wi - `api/v1alpha1/` — CRD types and generated deepcopy code - `internal/controller/` — Reconciliation logic - `config/crd/bases/` — Generated CRD manifests -- `config/rbac/` — Generated RBAC manifests +- `config/rbac/` — RBAC manifests (generated role + deployment bindings) - `config/samples/` — Sample custom resources - `hack/` — Code generation and maintenance scripts diff --git a/config/crd/bases/coder.com_codercontrolplanes.yaml b/config/crd/bases/coder.com_codercontrolplanes.yaml index eb2ac2c6..1e9aa82a 100644 --- a/config/crd/bases/coder.com_codercontrolplanes.yaml +++ b/config/crd/bases/coder.com_codercontrolplanes.yaml @@ -340,9 +340,8 @@ spec: bootstrap succeeded. type: boolean operatorTokenSecretRef: - description: |- - OperatorTokenSecretRef points to the Secret key containing the - `coder-k8s-operator` API token. + description: OperatorTokenSecretRef points to the Secret key containing + the `coder-k8s-operator` API token. properties: key: description: Key is the key inside the Secret data map. diff --git a/config/rbac/auth-delegator-binding.yaml b/config/rbac/auth-delegator-binding.yaml new file mode 100644 index 00000000..a06958c8 --- /dev/null +++ b/config/rbac/auth-delegator-binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: coder-k8s-auth-delegator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: + - kind: ServiceAccount + name: coder-k8s + namespace: coder-system diff --git a/config/rbac/authentication-reader-binding.yaml b/config/rbac/authentication-reader-binding.yaml new file mode 100644 index 00000000..91b112b3 --- /dev/null +++ b/config/rbac/authentication-reader-binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: coder-k8s-authentication-reader + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: + - kind: ServiceAccount + name: coder-k8s + namespace: coder-system diff --git a/config/e2e/clusterrole-binding.yaml b/config/rbac/clusterrolebinding.yaml similarity index 100% rename from config/e2e/clusterrole-binding.yaml rename to config/rbac/clusterrolebinding.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 58168493..c428c573 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -10,7 +10,25 @@ rules: - events verbs: - create + - get + - list - patch + - watch +- apiGroups: + - "" + resources: + - namespaces + - pods + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get - apiGroups: - "" resources: @@ -25,6 +43,17 @@ rules: - patch - update - watch +- apiGroups: + - aggregation.coder.com + resources: + - codertemplates + - coderworkspaces + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - apps resources: diff --git a/config/e2e/serviceaccount.yaml b/config/rbac/serviceaccount.yaml similarity index 100% rename from config/e2e/serviceaccount.yaml rename to config/rbac/serviceaccount.yaml diff --git a/deploy/rbac.yaml b/deploy/rbac.yaml deleted file mode 100644 index 478ebf73..00000000 --- a/deploy/rbac.yaml +++ /dev/null @@ -1,108 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: coder-k8s - namespace: coder-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: coder-k8s -rules: - # Controller: manage CoderControlPlane resources - - apiGroups: ["coder.com"] - resources: ["codercontrolplanes"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - - apiGroups: ["coder.com"] - resources: ["codercontrolplanes/status"] - verbs: ["get", "update", "patch"] - - apiGroups: ["coder.com"] - resources: ["codercontrolplanes/finalizers"] - verbs: ["update"] - # Controller: manage WorkspaceProxy resources - - apiGroups: ["coder.com"] - resources: ["workspaceproxies"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - - apiGroups: ["coder.com"] - resources: ["workspaceproxies/status"] - verbs: ["get", "update", "patch"] - - apiGroups: ["coder.com"] - resources: ["workspaceproxies/finalizers"] - verbs: ["update"] - # Controller: manage CoderProvisioner resources - - apiGroups: ["coder.com"] - resources: ["coderprovisioners"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - - apiGroups: ["coder.com"] - resources: ["coderprovisioners/status"] - verbs: ["get", "update", "patch"] - - apiGroups: ["coder.com"] - resources: ["coderprovisioners/finalizers"] - verbs: ["update"] - # Controller: leader election - - apiGroups: ["coordination.k8s.io"] - resources: ["leases"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - # Controller + MCP: events - - apiGroups: [""] - resources: ["events"] - verbs: ["get", "list", "watch", "create", "patch"] - # Controller: manage deployments and services for control planes - - apiGroups: ["apps"] - resources: ["deployments"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - - apiGroups: [""] - resources: ["services"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - # Dynamic provider: read operator token secrets - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] - # MCP: read aggregated API resources - - apiGroups: ["aggregation.coder.com"] - resources: ["coderworkspaces", "codertemplates"] - verbs: ["get", "list", "watch", "update", "patch"] - # MCP: read pods, logs, namespaces - - apiGroups: [""] - resources: ["pods", "pods/log", "namespaces"] - verbs: ["get", "list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: coder-k8s -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: coder-k8s -subjects: - - kind: ServiceAccount - name: coder-k8s - namespace: coder-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: coder-k8s-auth-delegator -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: - - kind: ServiceAccount - name: coder-k8s - namespace: coder-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: coder-k8s-authentication-reader - namespace: kube-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: extension-apiserver-authentication-reader -subjects: - - kind: ServiceAccount - name: coder-k8s - namespace: coder-system diff --git a/docs/how-to/deploy-aggregated-apiserver.md b/docs/how-to/deploy-aggregated-apiserver.md index c17110b8..7c4c6ca7 100644 --- a/docs/how-to/deploy-aggregated-apiserver.md +++ b/docs/how-to/deploy-aggregated-apiserver.md @@ -16,10 +16,10 @@ kubectl create namespace coder-system ## 2. Apply RBAC -The RBAC manifest creates the unified `coder-k8s` ServiceAccount used by all app modes. +Apply generated RBAC manifests (including the shared `coder-k8s` ServiceAccount and bindings): ```bash -kubectl apply -f deploy/rbac.yaml +kubectl apply -f config/rbac/ ``` ## 3. Deploy the service and deployment diff --git a/docs/how-to/deploy-controller.md b/docs/how-to/deploy-controller.md index 64c4b0f3..f396cc6f 100644 --- a/docs/how-to/deploy-controller.md +++ b/docs/how-to/deploy-controller.md @@ -1,6 +1,6 @@ # Deploy the controller (in-cluster) -This guide shows how to deploy the `coder-k8s` **controller** to a Kubernetes cluster using the manifests in `deploy/`. +This guide shows how to deploy the `coder-k8s` **controller** to a Kubernetes cluster using manifests from `config/` and `deploy/`. ## 1. Create the namespace @@ -21,7 +21,7 @@ kubectl apply -f config/crd/bases/ ## 3. Apply RBAC ```bash -kubectl apply -f deploy/rbac.yaml +kubectl apply -f config/rbac/ ``` ## 4. Deploy `coder-k8s` diff --git a/docs/how-to/mcp-server.md b/docs/how-to/mcp-server.md index b7f315a9..bf462f14 100644 --- a/docs/how-to/mcp-server.md +++ b/docs/how-to/mcp-server.md @@ -20,12 +20,12 @@ The MCP server provides tools for inspecting and updating Kubernetes resources m Apply RBAC, deployment, and service manifests: ```bash -kubectl apply -f deploy/rbac.yaml +kubectl apply -f config/rbac/ kubectl apply -f deploy/deployment.yaml kubectl apply -f deploy/mcp-service.yaml ``` -The RBAC manifest creates the unified `coder-k8s` ServiceAccount used by the Deployment. +The RBAC manifests create the shared `coder-k8s` ServiceAccount and bindings used by the Deployment. Port-forward the MCP service: diff --git a/examples/cloudnativepg/README.md b/examples/cloudnativepg/README.md index d0e28e8a..87869a05 100644 --- a/examples/cloudnativepg/README.md +++ b/examples/cloudnativepg/README.md @@ -31,7 +31,7 @@ Follow [Deploy the controller (in-cluster)](../../docs/how-to/deploy-controller. ```bash kubectl create namespace coder-system kubectl apply -f config/crd/bases/ -kubectl apply -f deploy/rbac.yaml +kubectl apply -f config/rbac/ kubectl apply -f deploy/deployment.yaml kubectl rollout status deployment/coder-k8s -n coder-system ``` diff --git a/hack/kind-dev.sh b/hack/kind-dev.sh index f43a526b..ae44726a 100755 --- a/hack/kind-dev.sh +++ b/hack/kind-dev.sh @@ -116,13 +116,10 @@ cmd_up() { kubectl_ctx wait --for=condition=Ready node --all --timeout="${NODE_READY_TIMEOUT}" + kubectl_ctx apply -f config/e2e/namespace.yaml kubectl_ctx apply -f config/crd/bases/ kubectl_ctx apply -f config/rbac/ - kubectl_ctx apply -f config/e2e/namespace.yaml - kubectl_ctx apply -f config/e2e/serviceaccount.yaml - kubectl_ctx apply -f config/e2e/clusterrole-binding.yaml - cmd_ctx echo diff --git a/internal/app/mcpapp/server.go b/internal/app/mcpapp/server.go index 21434581..f0230c01 100644 --- a/internal/app/mcpapp/server.go +++ b/internal/app/mcpapp/server.go @@ -20,6 +20,14 @@ const ( serverImplementationVersion = "dev" ) +// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=pods/log,verbs=get +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch +// +kubebuilder:rbac:groups=aggregation.coder.com,resources=coderworkspaces;codertemplates,verbs=get;list;watch;update;patch + // NewServer creates an MCP server with all tools registered. func NewServer(k8sClient client.Client, clientset kubernetes.Interface) *mcp.Server { if k8sClient == nil {