@raishin/vanguard-frontier-agentic 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/kubernetes/README.md +10 -1
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/AGENT.md +12 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/harnesses/claude-code.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/harnesses/copilot.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/harnesses/cursor.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/harnesses/gemini.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/harnesses/kiro-cli.agent.json +1 -1
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/harnesses/kiro-ide.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/metadata.json +5 -3
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/references/least-privilege-rbac.yaml +98 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/references/rbac-pre-flight.md +108 -0
- package/agents/kubernetes/kubernetes-live-admission-policy-guard-agent/references/refusal-list.md +112 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/AGENT.md +13 -1
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/harnesses/claude-code.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/harnesses/copilot.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/harnesses/cursor.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/harnesses/gemini.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/harnesses/kiro-cli.agent.json +1 -1
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/harnesses/kiro-ide.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/metadata.json +5 -3
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/references/least-privilege-rbac.yaml +92 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/references/rbac-pre-flight.md +108 -0
- package/agents/kubernetes/kubernetes-live-argocd-sync-guard-agent/references/refusal-list.md +112 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/AGENT.md +13 -1
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/harnesses/claude-code.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/harnesses/copilot.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/harnesses/cursor.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/harnesses/gemini.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/harnesses/kiro-cli.agent.json +1 -1
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/harnesses/kiro-ide.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/metadata.json +5 -3
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/references/least-privilege-rbac.yaml +101 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/references/rbac-pre-flight.md +106 -0
- package/agents/kubernetes/kubernetes-live-mesh-policy-guard-agent/references/refusal-list.md +102 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/AGENT.md +71 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/claude-code.agent.md +54 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/codex.toml +38 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/copilot.agent.md +54 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/cursor.agent.md +54 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/gemini.agent.md +54 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/kiro-cli.agent.json +5 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/harnesses/kiro-ide.agent.md +54 -0
- package/agents/kubernetes/kubernetes-live-network-architecture-mutation-guard-agent/metadata.json +44 -0
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/AGENT.md +14 -2
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/harnesses/claude-code.agent.md +13 -1
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/harnesses/copilot.agent.md +13 -1
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/harnesses/cursor.agent.md +13 -1
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/harnesses/gemini.agent.md +13 -1
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/harnesses/kiro-cli.agent.json +1 -1
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/harnesses/kiro-ide.agent.md +13 -1
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/metadata.json +5 -3
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/references/least-privilege-rbac.yaml +101 -0
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/references/rbac-pre-flight.md +106 -0
- package/agents/kubernetes/kubernetes-live-network-policy-guard-agent/references/refusal-list.md +102 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/AGENT.md +12 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/harnesses/claude-code.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/harnesses/copilot.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/harnesses/cursor.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/harnesses/gemini.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/harnesses/kiro-cli.agent.json +1 -1
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/harnesses/kiro-ide.agent.md +12 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/metadata.json +6 -3
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/references/least-privilege-rbac.yaml +92 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/references/rbac-pre-flight.md +115 -0
- package/agents/kubernetes/kubernetes-live-rbac-mutation-guard-agent/references/refusal-list.md +132 -0
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/AGENT.md +15 -3
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/claude-code.agent.md +15 -3
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/codex.toml +2 -2
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/copilot.agent.md +15 -3
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/cursor.agent.md +15 -3
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/gemini.agent.md +15 -3
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/kiro-cli.agent.json +1 -1
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/harnesses/kiro-ide.agent.md +15 -3
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/metadata.json +6 -4
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/references/least-privilege-rbac.yaml +92 -0
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/references/rbac-pre-flight.md +109 -0
- package/agents/kubernetes/kubernetes-live-velero-restore-guard-agent/references/refusal-list.md +122 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/AGENT.md +65 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/claude-code.agent.md +48 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/codex.toml +37 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/copilot.agent.md +48 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/cursor.agent.md +48 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/gemini.agent.md +48 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/kiro-cli.agent.json +5 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/harnesses/kiro-ide.agent.md +48 -0
- package/agents/kubernetes/kubernetes-network-architecture-review-agent/metadata.json +44 -0
- package/catalog/agents.json +78 -12
- package/catalog/install-roles.json +8 -4
- package/catalog/skill-manifest.json +111 -12
- package/catalog/skills.json +67 -0
- package/package.json +1 -1
- package/skills/kubernetes/README.md +5 -1
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/SKILL.md +82 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/metadata.json +33 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/references/least-privilege-rbac.yaml +210 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/references/official-sources.md +41 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/references/permitted-mutations.md +173 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/references/rbac-pre-flight.md +252 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/references/refusal-list.md +313 -0
- package/skills/kubernetes/kubernetes-live-network-architecture-mutation-guard/references/rollback-patterns.md +103 -0
- package/skills/kubernetes/kubernetes-maestro/references/safety-checklist.md +1 -1
- package/skills/kubernetes/kubernetes-maestro/references/workflow-and-output.md +57 -5
- package/skills/kubernetes/kubernetes-network-architecture-review/SKILL.md +84 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/metadata.json +34 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/dataplane-and-cni.md +89 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/dns-and-discovery.md +120 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/mcp-and-evidence.md +53 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/multi-cluster-and-egress.md +69 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/official-sources.md +54 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/service-gateway-routing.md +108 -0
- package/skills/kubernetes/kubernetes-network-architecture-review/references/troubleshooting-playbook.md +100 -0
- package/skills/velero/velero-backup-restore-guard/SKILL.md +2 -2
- package/skills/velero/velero-backup-restore-guard/references/safety-checklist.md +1 -1
- package/skills/velero/velero-backup-restore-guard/references/workflow-and-output.md +17 -8
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# RBAC pre-flight self-check
|
|
2
|
+
|
|
3
|
+
This is the mandatory first action of every session. The agent runs this matrix before reading any user-supplied YAML, before formulating any mutation, before producing any output other than the matrix result.
|
|
4
|
+
|
|
5
|
+
The matrix is grounded against `kubernetes.io/docs/concepts/security/rbac-good-practices` and `kubernetes.io/docs/reference/kubectl/generated/kubectl_auth/kubectl_auth_can-i`. The canonical authoring contract is `docs/least-privilege-rbac.md`.
|
|
6
|
+
|
|
7
|
+
If any **must-not-be-yes** check returns `yes`, or any **must-be-yes** check returns `no`, the agent refuses to act and tells the user the binding is over- or under-scoped.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Required RBAC manifest
|
|
12
|
+
|
|
13
|
+
Apply `references/least-privilege-rbac.yaml` (shipped with this skill) before invoking the agent. The manifest creates `ServiceAccount/vanguard-network-arch-guard` in namespace `vanguard-system`, a `ClusterRole` with the verbs documented in `permitted-mutations.md`, and a `ClusterRoleBinding`.
|
|
14
|
+
|
|
15
|
+
Per upstream `kubernetes.io/docs/concepts/security/rbac-good-practices`:
|
|
16
|
+
|
|
17
|
+
> *Avoid wildcard permissions, especially to all resources, as this grants access to current and future object types.*
|
|
18
|
+
|
|
19
|
+
The manifest enumerates each resource and verb. Review the deliberately-omitted block before applying.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Operator principal check (run first)
|
|
24
|
+
|
|
25
|
+
This checks the operator's own kubeconfig — the principal whose `--as` we are about to use:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# If yes: operator is in system:masters or cluster-admin. Refuse.
|
|
29
|
+
kubectl auth can-i '*' '*' --all-namespaces
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If this returns `yes`, the operator must switch to a kubeconfig with `impersonate` rights only and re-invoke. Per upstream RBAC good practices:
|
|
33
|
+
|
|
34
|
+
> *Administrators should avoid using `cluster-admin` accounts and instead provide low-privileged accounts with impersonation rights to prevent accidental modification of cluster resources.*
|
|
35
|
+
|
|
36
|
+
> *Do not add users to the `system:masters` group, as this bypasses all RBAC checks.*
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Agent ServiceAccount must-not-be-yes matrix
|
|
41
|
+
|
|
42
|
+
Run with `--as=system:serviceaccount:vanguard-system:vanguard-network-arch-guard`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
SA="system:serviceaccount:vanguard-system:vanguard-network-arch-guard"
|
|
46
|
+
|
|
47
|
+
# Cluster-admin equivalence
|
|
48
|
+
kubectl auth can-i '*' '*' --all-namespaces --as=$SA
|
|
49
|
+
|
|
50
|
+
# Namespace destruction
|
|
51
|
+
kubectl auth can-i delete namespaces --as=$SA
|
|
52
|
+
kubectl auth can-i delete namespaces/kube-system --as=$SA
|
|
53
|
+
kubectl auth can-i delete namespaces/cilium --as=$SA
|
|
54
|
+
kubectl auth can-i delete namespaces/istio-system --as=$SA
|
|
55
|
+
|
|
56
|
+
# kube-system control plane destruction
|
|
57
|
+
kubectl auth can-i delete daemonsets -n kube-system --as=$SA
|
|
58
|
+
kubectl auth can-i delete deployments -n kube-system --as=$SA
|
|
59
|
+
kubectl auth can-i patch daemonsets/cilium -n kube-system --as=$SA
|
|
60
|
+
kubectl auth can-i patch daemonsets/kube-proxy -n kube-system --as=$SA
|
|
61
|
+
|
|
62
|
+
# Pod execution / mutation in kube-system
|
|
63
|
+
kubectl auth can-i delete pods -n kube-system --as=$SA
|
|
64
|
+
kubectl auth can-i create pods/exec -n kube-system --as=$SA
|
|
65
|
+
|
|
66
|
+
# CRD operations
|
|
67
|
+
kubectl auth can-i create customresourcedefinitions --as=$SA
|
|
68
|
+
kubectl auth can-i delete customresourcedefinitions --as=$SA
|
|
69
|
+
|
|
70
|
+
# Broad secret access
|
|
71
|
+
kubectl auth can-i get secrets --all-namespaces --as=$SA
|
|
72
|
+
kubectl auth can-i list secrets --all-namespaces --as=$SA
|
|
73
|
+
|
|
74
|
+
# Privilege escalation
|
|
75
|
+
kubectl auth can-i create clusterrolebindings --as=$SA
|
|
76
|
+
kubectl auth can-i create clusterroles --as=$SA
|
|
77
|
+
kubectl auth can-i escalate roles --as=$SA
|
|
78
|
+
kubectl auth can-i bind roles --as=$SA
|
|
79
|
+
kubectl auth can-i impersonate users --as=$SA
|
|
80
|
+
kubectl auth can-i impersonate groups --as=$SA
|
|
81
|
+
kubectl auth can-i impersonate serviceaccounts --as=$SA
|
|
82
|
+
|
|
83
|
+
# Node lifecycle (refusal-list.md "Node operations")
|
|
84
|
+
kubectl auth can-i delete nodes --as=$SA
|
|
85
|
+
kubectl auth can-i patch nodes --as=$SA
|
|
86
|
+
kubectl auth can-i update nodes --as=$SA
|
|
87
|
+
kubectl auth can-i create pods/eviction --as=$SA
|
|
88
|
+
kubectl auth can-i get nodes/proxy --as=$SA
|
|
89
|
+
kubectl auth can-i create nodes/proxy --as=$SA
|
|
90
|
+
|
|
91
|
+
# Lease objects (refusal-list.md "Lease objects in kube-node-lease")
|
|
92
|
+
kubectl auth can-i patch leases.coordination.k8s.io -n kube-node-lease --as=$SA
|
|
93
|
+
kubectl auth can-i delete leases.coordination.k8s.io -n kube-node-lease --as=$SA
|
|
94
|
+
|
|
95
|
+
# Admission webhook configs (refusal-list.md "Admission webhook configurations")
|
|
96
|
+
kubectl auth can-i create mutatingwebhookconfigurations.admissionregistration.k8s.io --as=$SA
|
|
97
|
+
kubectl auth can-i patch mutatingwebhookconfigurations.admissionregistration.k8s.io --as=$SA
|
|
98
|
+
kubectl auth can-i delete mutatingwebhookconfigurations.admissionregistration.k8s.io --as=$SA
|
|
99
|
+
kubectl auth can-i create validatingwebhookconfigurations.admissionregistration.k8s.io --as=$SA
|
|
100
|
+
kubectl auth can-i patch validatingwebhookconfigurations.admissionregistration.k8s.io --as=$SA
|
|
101
|
+
kubectl auth can-i delete validatingwebhookconfigurations.admissionregistration.k8s.io --as=$SA
|
|
102
|
+
|
|
103
|
+
# APIService aggregation (refusal-list.md "APIService aggregation")
|
|
104
|
+
kubectl auth can-i create apiservices.apiregistration.k8s.io --as=$SA
|
|
105
|
+
kubectl auth can-i patch apiservices.apiregistration.k8s.io --as=$SA
|
|
106
|
+
kubectl auth can-i delete apiservices.apiregistration.k8s.io --as=$SA
|
|
107
|
+
|
|
108
|
+
# Pod and node subresources (refusal-list.md "Pod and node subresources")
|
|
109
|
+
kubectl auth can-i create pods/exec -n kube-system --as=$SA
|
|
110
|
+
kubectl auth can-i create pods/portforward --all-namespaces --as=$SA
|
|
111
|
+
kubectl auth can-i create pods/proxy --all-namespaces --as=$SA
|
|
112
|
+
kubectl auth can-i create pods/binding --all-namespaces --as=$SA
|
|
113
|
+
|
|
114
|
+
# CSR approval and TokenRequest minting (refusal-list.md "CSR approval and TokenRequest minting")
|
|
115
|
+
kubectl auth can-i update certificatesigningrequests.certificates.k8s.io --subresource=approval --as=$SA
|
|
116
|
+
kubectl auth can-i create certificatesigningrequests.certificates.k8s.io --as=$SA
|
|
117
|
+
kubectl auth can-i create serviceaccounts/token --all-namespaces --as=$SA
|
|
118
|
+
|
|
119
|
+
# Manual Endpoints / EndpointSlices writes (refusal-list.md "Manual Endpoints / EndpointSlices writes")
|
|
120
|
+
kubectl auth can-i create endpoints --all-namespaces --as=$SA
|
|
121
|
+
kubectl auth can-i patch endpoints --all-namespaces --as=$SA
|
|
122
|
+
kubectl auth can-i create endpointslices.discovery.k8s.io --all-namespaces --as=$SA
|
|
123
|
+
kubectl auth can-i patch endpointslices.discovery.k8s.io --all-namespaces --as=$SA
|
|
124
|
+
|
|
125
|
+
# kube-system ConfigMap writes outside the resourceName-locked allowlist
|
|
126
|
+
# (refusal-list.md "kube-system ConfigMap writes outside the resourceName-locked allowlist")
|
|
127
|
+
kubectl auth can-i patch configmaps/cilium-config -n kube-system --as=$SA # MUST be no
|
|
128
|
+
kubectl auth can-i patch configmaps/kube-proxy -n kube-system --as=$SA # MUST be no
|
|
129
|
+
kubectl auth can-i patch configmaps/kubelet-config -n kube-system --as=$SA # MUST be no
|
|
130
|
+
kubectl auth can-i patch configmaps/cluster-info -n kube-public --as=$SA # MUST be no
|
|
131
|
+
|
|
132
|
+
# PriorityClass / IngressClass / StorageClass (refusal-list.md "PriorityClass and IngressClass")
|
|
133
|
+
kubectl auth can-i delete priorityclasses.scheduling.k8s.io --as=$SA
|
|
134
|
+
kubectl auth can-i patch priorityclasses.scheduling.k8s.io --as=$SA
|
|
135
|
+
kubectl auth can-i delete ingressclasses.networking.k8s.io --as=$SA
|
|
136
|
+
kubectl auth can-i patch ingressclasses.networking.k8s.io --as=$SA
|
|
137
|
+
kubectl auth can-i patch storageclasses.storage.k8s.io --as=$SA
|
|
138
|
+
|
|
139
|
+
# Finalizer-stripping path (every resource exposes metadata.finalizers via patch;
|
|
140
|
+
# admission policy is the cleanest enforcement, but verify the binding does not
|
|
141
|
+
# grant patch on `namespaces` or `customresourcedefinitions` finalizers subresource):
|
|
142
|
+
kubectl auth can-i update namespaces/finalize --as=$SA
|
|
143
|
+
kubectl auth can-i update customresourcedefinitions/finalize --as=$SA
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Every line above must print `no`. Any `yes` means the binding is over-scoped — refuse to run and tell the operator which line failed.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Agent ServiceAccount must-be-yes matrix
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
SA="system:serviceaccount:vanguard-system:vanguard-network-arch-guard"
|
|
154
|
+
|
|
155
|
+
# Read state across the architecture surface
|
|
156
|
+
kubectl auth can-i get services --all-namespaces --as=$SA
|
|
157
|
+
kubectl auth can-i list services --all-namespaces --as=$SA
|
|
158
|
+
kubectl auth can-i get endpointslices --all-namespaces --as=$SA
|
|
159
|
+
kubectl auth can-i get nodes --as=$SA
|
|
160
|
+
kubectl auth can-i get configmaps -n kube-system --as=$SA
|
|
161
|
+
|
|
162
|
+
# Service spec patches (the agent's actual mutation surface)
|
|
163
|
+
kubectl auth can-i patch services --all-namespaces --as=$SA
|
|
164
|
+
|
|
165
|
+
# CoreDNS Corefile (resourceName-locked in the manifest)
|
|
166
|
+
kubectl auth can-i patch configmaps/coredns -n kube-system --as=$SA
|
|
167
|
+
kubectl auth can-i get configmaps/coredns -n kube-system --as=$SA
|
|
168
|
+
|
|
169
|
+
# Gateway API resources (write OK — Gateway API resources are user-owned, not control plane)
|
|
170
|
+
kubectl auth can-i create gateways.gateway.networking.k8s.io --all-namespaces --as=$SA
|
|
171
|
+
kubectl auth can-i patch gateways.gateway.networking.k8s.io --all-namespaces --as=$SA
|
|
172
|
+
kubectl auth can-i create httproutes.gateway.networking.k8s.io --all-namespaces --as=$SA
|
|
173
|
+
kubectl auth can-i create grpcroutes.gateway.networking.k8s.io --all-namespaces --as=$SA
|
|
174
|
+
kubectl auth can-i create referencegrants.gateway.networking.k8s.io --all-namespaces --as=$SA
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Every line above must print `yes`.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## resourceName-scoped binding verification (positive AND negative)
|
|
182
|
+
|
|
183
|
+
`kubectl auth can-i` does **not** by default surface `resourceNames` constraints. A binding that grants `patch configmaps` only on `resourceNames: ["coredns"]` may return ambiguous results when checked with the resource type alone. Always test BOTH the allowed and the denied resource name.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
SA="system:serviceaccount:vanguard-system:vanguard-network-arch-guard"
|
|
187
|
+
|
|
188
|
+
# Positive — the bound resourceName MUST return yes
|
|
189
|
+
kubectl auth can-i patch configmaps/coredns -n kube-system --as=$SA # expect: yes
|
|
190
|
+
|
|
191
|
+
# Negative — adjacent ConfigMaps in the same namespace MUST return no
|
|
192
|
+
kubectl auth can-i patch configmaps/cilium-config -n kube-system --as=$SA # expect: no
|
|
193
|
+
kubectl auth can-i patch configmaps/kube-proxy -n kube-system --as=$SA # expect: no
|
|
194
|
+
kubectl auth can-i patch configmaps/extension-apiserver-authentication -n kube-system --as=$SA # expect: no
|
|
195
|
+
|
|
196
|
+
# Negative — same resourceName in a different namespace MUST return no
|
|
197
|
+
kubectl auth can-i patch configmaps/coredns -n default --as=$SA # expect: no
|
|
198
|
+
kubectl auth can-i patch configmaps/coredns -n kube-public --as=$SA # expect: no
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
If any negative-test row returns `yes`, the binding is over-scoped — typically because `resourceNames` was omitted from the `ClusterRole` rule. Reapply the manifest from `references/least-privilege-rbac.yaml` and re-test.
|
|
202
|
+
|
|
203
|
+
This pattern generalises: any verb the agent is granted on a resourceName-locked resource MUST be tested with at least one positive (allowed name) and two negatives (one different name in the same namespace, one same name in a different namespace).
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Programmatic alternative — SubjectAccessReview API
|
|
208
|
+
|
|
209
|
+
Harnesses that cannot shell out to `kubectl` should call the `authorization.k8s.io/v1` `SubjectAccessReview` API directly:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
curl -s -X POST $KUBE_API_SERVER/apis/authorization.k8s.io/v1/subjectaccessreviews \
|
|
213
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
214
|
+
-H "Content-Type: application/json" \
|
|
215
|
+
-d '{
|
|
216
|
+
"apiVersion": "authorization.k8s.io/v1",
|
|
217
|
+
"kind": "SubjectAccessReview",
|
|
218
|
+
"spec": {
|
|
219
|
+
"user": "system:serviceaccount:vanguard-system:vanguard-network-arch-guard",
|
|
220
|
+
"resourceAttributes": {
|
|
221
|
+
"verb": "delete",
|
|
222
|
+
"resource": "namespaces"
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}'
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Parse `.status.allowed` from the response. Same semantics as `kubectl auth can-i`.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## What the agent does with the matrix output
|
|
233
|
+
|
|
234
|
+
If every must-not row is `no` and every must-be row is `yes`:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Pre-flight: PASS
|
|
238
|
+
Bound principal: system:serviceaccount:vanguard-system:vanguard-network-arch-guard
|
|
239
|
+
RBAC posture: scoped per docs/least-privilege-rbac.md
|
|
240
|
+
Proceeding to next step.
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
If any row fails:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
Pre-flight: FAIL
|
|
247
|
+
Failing check: <verb> <resource> <namespace>
|
|
248
|
+
Expected: no | Actual: yes (this verb is over-scoped on the bound ServiceAccount)
|
|
249
|
+
Action: refusing to proceed. Re-apply the manifest from references/least-privilege-rbac.yaml or scope down the existing binding before re-invoking.
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
No exceptions. No retries. The pre-flight is the gate.
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Hard refusal list — one-way doors
|
|
2
|
+
|
|
3
|
+
This document is the explicit `REFUSE` list. The agent must respond with `REFUSED — <rule>` and no execution attempt for any of the following. The cluster-side RBAC binding shipped with this skill also denies these verbs at the API server level — defense in depth.
|
|
4
|
+
|
|
5
|
+
> **Scope-of-defense clarification.** This list is the **prompt-level fast-path** for rejecting common destructive operations. The authoritative defense is the cluster-side RBAC binding (`references/least-privilege-rbac.yaml`), which is **deny-by-default**: it grants only the enumerated verbs / resources and denies everything else. New attack vectors (Kubernetes adds APIs every release) may not appear in this list immediately, but the binding rejects them automatically because they are not on the allow list. If you find a destructive operation not in this list, that does **not** mean the agent will execute it — but please open an issue so the prompt-level rejection is added.
|
|
6
|
+
|
|
7
|
+
The format for each entry is: **what is refused**, **why it's a one-way door**, **what the user should do instead**, **cluster-side blast radius if the prompt-level refusal is bypassed**.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## CNI replacement or uninstall
|
|
12
|
+
|
|
13
|
+
**Refused operations**: any `kubectl apply / delete` of a CNI's `DaemonSet`, ConfigMap, or CRD set (Cilium, Calico, Flannel, Weave, Antrea, Cilium chaining mode change). Includes `helm uninstall cilium`, `cilium uninstall`, equivalent for other CNIs.
|
|
14
|
+
|
|
15
|
+
**Why**: Replacing the CNI requires draining and re-IPAMing every Pod in the cluster. Many CNIs hold node state that the replacement does not understand. Cilium-to-Calico, Calico-to-Cilium, and any hybrid migration are full cluster rebuilds in practice, not cluster operations.
|
|
16
|
+
|
|
17
|
+
**Instead**: The architecture-review agent (`kubernetes-network-architecture-review-agent`) can produce a CNI-replacement cutover plan. Execution must be human-led with workload drain windows, fresh node groups, and a fallback cluster.
|
|
18
|
+
|
|
19
|
+
**Cluster-side blast radius if bypassed**: Pod-to-Pod connectivity stops for every workload until the replacement IPAM stabilises. NetworkPolicy enforcement disappears. mTLS in some service meshes (ambient Cilium-aware modes) breaks until the new CNI is fully up.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## kube-proxy mode swap
|
|
24
|
+
|
|
25
|
+
**Refused operations**: any change to `kube-proxy` `mode` ConfigMap (`iptables` ↔ `ipvs` ↔ `nftables`), and any change to or from Cilium kube-proxy replacement.
|
|
26
|
+
|
|
27
|
+
**Why**: Existing TCP connections rely on stable conntrack entries. Swapping the mode invalidates the in-kernel rules during the transition. Sessions on `sessionAffinity: ClientIP` Services may persist past the swap and route to the wrong endpoint. Some flows survive, some do not, deterministically by neither protocol nor application.
|
|
28
|
+
|
|
29
|
+
**Instead**: Plan a mode change as a per-node-pool rolling drain with explicit cordon, conntrack flush on the receiving traffic boundaries, and full session-tracking cutover. The architecture-review agent can produce the plan.
|
|
30
|
+
|
|
31
|
+
**Cluster-side blast radius if bypassed**: Service traffic stalls for some workloads, succeeds for others; debugging is hours-to-days because the failure is mode-transition state, not config.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Node MTU change
|
|
36
|
+
|
|
37
|
+
**Refused operations**: any change to node interface MTU, CNI overlay MTU (Cilium `--mtu`, Calico `veth_mtu`, Flannel `MTU`), or VXLAN / Geneve / WireGuard encapsulation MTU.
|
|
38
|
+
|
|
39
|
+
**Why**: TCP handshake (small packets) succeeds, then the first response above the new path-MTU stalls because Path-MTU-Discovery ICMP is filtered by most cloud underlays. A wrong MTU causes silent payload-stall failure mode — connections look "alive" but never deliver.
|
|
40
|
+
|
|
41
|
+
**Instead**: Architecture review can produce the correct MTU calculation. Apply changes during a planned maintenance window with the encapsulation overhead pre-computed (VXLAN 50B, Geneve 60B, WireGuard 60B with IPsec extra) and verified per-node with `ping -M do -s <bytes>`.
|
|
42
|
+
|
|
43
|
+
**Cluster-side blast radius if bypassed**: Every Pod-to-Pod request larger than the new path-MTU stalls until the user discovers and reverts. Tail-latency dashboards spike; logs show no errors because TCP doesn't surface MTU drops.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Pod CIDR or Service CIDR resize
|
|
48
|
+
|
|
49
|
+
**Refused operations**: any modification to `kube-controller-manager` flags (`--cluster-cidr`, `--service-cluster-ip-range`), Cilium IPAM CIDR pool resize that overlaps existing pool, any kube-apiserver flag flip on Service CIDR.
|
|
50
|
+
|
|
51
|
+
**Why**: Pod CIDR is allocated to nodes at node registration; existing nodes cannot be re-IPAMed without restart. Service CIDR is encoded into every existing Service's `spec.clusterIP`. Changing these requires a cluster rebuild for most CNIs.
|
|
52
|
+
|
|
53
|
+
**Instead**: Plan capacity at cluster creation with growth headroom. If overlap is unavoidable, use per-cluster NAT (Submariner Globalnet, ClusterMesh `policy-default-local-cluster`) — these are *workarounds*, not resizes.
|
|
54
|
+
|
|
55
|
+
**Cluster-side blast radius if bypassed**: CIDR collisions silently route traffic to the wrong workloads in multi-cluster setups. Services with allocated ClusterIPs outside the new range become unreachable. New Pods either fail to allocate IPs or get IPs that conflict with existing routes.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Namespace deletion
|
|
60
|
+
|
|
61
|
+
**Refused operations**: `kubectl delete namespace ...` for any namespace.
|
|
62
|
+
|
|
63
|
+
**Why**: Namespace deletion cascades to every resource in the namespace. Deleting `kube-system`, `cilium`, `istio-system`, `linkerd`, `gateway-system`, `gke-gateway-system`, `kube-public`, `kube-node-lease` removes the cluster's control-plane operator. Even deleting a workload namespace is irreversible without a backup; if the namespace contained a `PersistentVolumeClaim`, the underlying `PersistentVolume` is lost when the PVC is finalized.
|
|
64
|
+
|
|
65
|
+
**Instead**: Delete specific resources within a namespace. Use `kubectl delete -n <ns> -l <selector>` with explicit selectors. Validate with `--dry-run=client -o yaml` before execution.
|
|
66
|
+
|
|
67
|
+
**Cluster-side blast radius if bypassed**: Deleting `kube-system` ends the cluster. Deleting `cilium` removes Pod-to-Pod networking. Deleting `istio-system` collapses mesh policy enforcement. Deleting a workload namespace deletes data.
|
|
68
|
+
|
|
69
|
+
The cluster-side RBAC binding for this guard explicitly omits `apiGroups: [""], resources: ["namespaces"]` for any verb. The API server returns `forbidden` on any namespace operation regardless of what the LLM emits.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## kube-system DaemonSet / Deployment writes
|
|
74
|
+
|
|
75
|
+
**Refused operations**: any `kubectl apply / patch / delete` on `DaemonSets` or `Deployments` in `kube-system`, including but not limited to `cilium`, `kube-proxy`, `coredns` (Deployment), `node-local-dns`, `metrics-server`, cloud-controller-manager.
|
|
76
|
+
|
|
77
|
+
**Why**: These workloads are the cluster control plane. A wrong replicas: 0 patch on `coredns` ends DNS for every Pod. A wrong tolerations change on `cilium` causes the Pod to evict and Pod-to-Pod connectivity to stop on the affected nodes. There is no fast rollback for a stopped CNI agent.
|
|
78
|
+
|
|
79
|
+
**Instead**: For DaemonSet-level changes, follow the upstream operator's documented upgrade path (Helm chart values, the operator's own CRD). For CoreDNS, use the `ConfigMap/coredns` path (permitted, see `permitted-mutations.md`) which exercises only the Corefile, never the Deployment.
|
|
80
|
+
|
|
81
|
+
**Cluster-side blast radius if bypassed**: Cluster-wide outage. Recovery requires kubectl access from a different machine with cluster-admin and a backup of the original DaemonSet/Deployment manifest.
|
|
82
|
+
|
|
83
|
+
The cluster-side RBAC binding omits write verbs on `apps/daemonsets` and `apps/deployments` in `kube-system` (and any namespace where the cluster's control plane runs).
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## CustomResourceDefinition operations
|
|
88
|
+
|
|
89
|
+
**Refused operations**: `kubectl create / apply / delete` on any `apiextensions.k8s.io/v1.CustomResourceDefinition`, including Gateway API CRDs, Cilium CRDs, Istio CRDs, cert-manager CRDs.
|
|
90
|
+
|
|
91
|
+
**Why**: Deleting a CRD cascades-deletes every custom resource of that kind cluster-wide. Some CRDs (Cilium `CiliumIdentity`, Istio `WorkloadEntry`) carry runtime state that cannot be recreated from manifests. Installing a CRD at the wrong version creates a schema mismatch with running controllers, leading to admission failures on every subsequent apply of that kind.
|
|
92
|
+
|
|
93
|
+
**Instead**: CRD installs are performed by the upstream Helm chart or operator manifest at install time. Upgrades follow the operator's documented version-skew policy.
|
|
94
|
+
|
|
95
|
+
**Cluster-side blast radius if bypassed**: Deleting a CRD ends the corresponding feature; cascading deletion of CRs may delete production policy or routing config. The cluster-side RBAC binding omits `apiextensions.k8s.io` group entirely.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Broad Secret operations
|
|
100
|
+
|
|
101
|
+
**Refused operations**: any `kubectl get / list` on `Secrets` outside the explicit allowlist of namespaces (`vanguard-system` for the agent's own SA, `kube-system` only for the specific ClusterMesh peer Secret name documented in `permitted-mutations.md`).
|
|
102
|
+
|
|
103
|
+
**Why**: Secrets carry credentials. A broad `kubectl get secrets --all-namespaces` exposes every cached ServiceAccount token, every ImagePullSecret, every TLS key in the cluster — to the agent's session, to its log, to whatever the harness does with response context.
|
|
104
|
+
|
|
105
|
+
**Instead**: Read only the specific Secret needed by name. The pre-flight self-check confirms `kubectl auth can-i get secrets --all-namespaces` returns `no` for the principal.
|
|
106
|
+
|
|
107
|
+
**Cluster-side blast radius if bypassed**: Credential leak; any cached token in any namespace becomes available to whoever has the agent's transcript.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Cluster-admin equivalence
|
|
112
|
+
|
|
113
|
+
**Refused operations**: any operation when `kubectl auth can-i '*' '*' --all-namespaces` returns `yes` for the operator's principal or for the agent's bound ServiceAccount.
|
|
114
|
+
|
|
115
|
+
**Why**: `cluster-admin` and `system:masters` group membership bypass all RBAC checks. Every other rule in this document is enforced by RBAC; if RBAC is bypassed, the prompt is the only remaining defense, and prompt rules are advisory.
|
|
116
|
+
|
|
117
|
+
**Instead**: Operators must use a low-privileged kubeconfig with `impersonate` rights on the agent's ServiceAccount. See `docs/least-privilege-rbac.md` for the canonical pattern.
|
|
118
|
+
|
|
119
|
+
**Cluster-side blast radius if bypassed**: Every other refusal in this document is bypassable.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Node operations
|
|
124
|
+
|
|
125
|
+
**Refused operations**: `kubectl delete node <name>`, `kubectl drain <node>`, `kubectl cordon <node>`, `kubectl uncordon <node>`, any `patch` on `nodes/spec.unschedulable`, any `patch` on `nodes/spec.taints` that affects scheduling.
|
|
126
|
+
|
|
127
|
+
**Why**: Node deletion drops the node's Pod CIDR allocation; pods on the node lose IPs and any PV with `volumeAttachment` to that node may leak its underlying volume. Draining all nodes (or even half) is a cluster-wide outage. Cordoning a node redirects Pod placement, which silently shifts traffic patterns and may overload the remaining nodes' egress paths (NAT GW, conntrack, NodeLocal DNSCache).
|
|
128
|
+
|
|
129
|
+
**Instead**: Node lifecycle is the cluster-administrator's responsibility, exercised through the cluster's node-pool management (cloud-provider controlled or kubeadm-managed). Use the architecture-review agent to surface node-imbalance findings; let humans drain.
|
|
130
|
+
|
|
131
|
+
**Cluster-side blast radius if bypassed**: Per upstream `kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm`, `kubectl delete node` removes the Node object after drain — but if executed without prior drain, every Pod on the node becomes Pending forever (no IP allocation source). `kubectl drain` with `--ignore-daemonsets --force --delete-emptydir-data` mass-evicts; if scripted across all nodes, every workload becomes unscheduled simultaneously.
|
|
132
|
+
|
|
133
|
+
The cluster-side RBAC binding for this guard omits write verbs (`patch`, `update`, `delete`) on `apiGroups: [""], resources: ["nodes"]`. The eviction subresource (`pods/eviction`) is also omitted.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Admission webhook configurations
|
|
138
|
+
|
|
139
|
+
**Refused operations**: any `create` / `patch` / `update` / `delete` on `admissionregistration.k8s.io/v1` `MutatingWebhookConfiguration` or `ValidatingWebhookConfiguration` resources.
|
|
140
|
+
|
|
141
|
+
**Why**: Admission webhooks are the cluster's enforcement boundary. A malicious or misconfigured `MutatingWebhookConfiguration` can:
|
|
142
|
+
- Drop or rewrite arbitrary Service / Deployment specs cluster-wide
|
|
143
|
+
- Inject malicious sidecars into every new Pod
|
|
144
|
+
- Cause infinite admission-loop and brick the cluster (each create triggers a mutation that triggers another mutation)
|
|
145
|
+
|
|
146
|
+
A deletion of an existing webhook config silently removes whatever defense it provided — Kyverno policies stop enforcing, cert-manager stops injecting, sidecar mesh injection stops, sealed-secrets stops decoding. Per the upstream `DELETE /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations` API, the operation cascades-deletes per-namespace bindings.
|
|
147
|
+
|
|
148
|
+
**Instead**: Admission webhook lifecycle is the platform-team's responsibility, governed by the operator's Helm chart (Kyverno, cert-manager, etc.) or a dedicated GitOps pipeline. Modifying webhooks is never a network-architecture concern.
|
|
149
|
+
|
|
150
|
+
**Cluster-side blast radius if bypassed**: An attacker-controlled `MutatingWebhookConfiguration` with `failurePolicy: Ignore` and a `clientConfig.url` pointing at an attacker endpoint silently observes (and optionally mutates) every API request to selected resources. With `failurePolicy: Fail`, it can wedge the cluster.
|
|
151
|
+
|
|
152
|
+
The cluster-side RBAC binding omits `apiGroups: ["admissionregistration.k8s.io"]` entirely.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## APIService aggregation
|
|
157
|
+
|
|
158
|
+
**Refused operations**: any `create` / `patch` / `update` / `delete` on `apiregistration.k8s.io/v1` `APIService` resources.
|
|
159
|
+
|
|
160
|
+
**Why**: Registered `APIService`s aggregate API groups into the kube-apiserver. Modifying or deleting an APIService can:
|
|
161
|
+
- Hijack API calls for an entire group (e.g. `metrics.k8s.io`, `external.metrics.k8s.io`, `custom.metrics.k8s.io`) by pointing them at an attacker-controlled service
|
|
162
|
+
- Break HPA / VPA cluster-wide by removing the metrics service registration
|
|
163
|
+
- Break aggregated extension APIs like Knative, Service-Catalog, the Cilium ClusterMesh apiserver
|
|
164
|
+
|
|
165
|
+
**Instead**: APIService lifecycle is owned by the controller's Helm chart at install time. Never modified ad-hoc.
|
|
166
|
+
|
|
167
|
+
**Cluster-side blast radius if bypassed**: Hijacking `metrics.k8s.io` lets an attacker feed false metrics to HPA, triggering arbitrary scale events. Hijacking a custom-metrics APIService used by an autoscaler means the autoscaler routes through the attacker.
|
|
168
|
+
|
|
169
|
+
The cluster-side RBAC binding omits `apiGroups: ["apiregistration.k8s.io"]` entirely.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Finalizer manipulation
|
|
174
|
+
|
|
175
|
+
**Refused operations**: any `patch` / `update` that removes finalizers from a resource — particularly `Namespaces`, `PersistentVolumes`, `CustomResourceDefinitions`, or any controller-managed CR with finalizers.
|
|
176
|
+
|
|
177
|
+
The pattern looks innocuous: `kubectl patch ns kube-system --type=merge -p '{"metadata":{"finalizers":[]}}'`. The agent must recognize this as a finalizer-stripping operation, not a routine metadata edit.
|
|
178
|
+
|
|
179
|
+
**Why**: Finalizers are the controller pattern that prevents premature deletion. A `Namespace` finalizer (`kubernetes`) blocks the delete until all child resources are reaped; stripping it bypasses cleanup. A `PersistentVolume` finalizer (`kubernetes.io/pv-protection`) prevents PV deletion while a PVC still references it; stripping it leaves the PVC orphaned. CRD finalizers (`customresourcecleanup.apiextensions.k8s.io`) block CRD deletion while custom resources of that kind still exist; stripping bypasses the cascade-delete safety.
|
|
180
|
+
|
|
181
|
+
**Instead**: Wait for the controller to drain the resource. If the controller is stuck, restart the controller or fix its underlying issue. Finalizer manipulation is a last-resort emergency operation, never a routine patch.
|
|
182
|
+
|
|
183
|
+
**Cluster-side blast radius if bypassed**: A namespace deleted with stripped finalizers leaves orphan PVs, lingering CRs, and abandoned controller state. PVs deleted with stripped finalizers leak underlying cloud volumes. CRDs deleted with stripped finalizers cascade-delete every CR of that kind without controller cleanup.
|
|
184
|
+
|
|
185
|
+
The cluster-side RBAC binding's `patch` verb on Services and the resourceName-locked CoreDNS ConfigMap is field-narrow enough that finalizer paths are not a primary attack surface here, but the agent must still refuse on principle. For a defense-in-depth admission policy, a Kyverno rule denying `patch` on `metadata.finalizers` for any non-platform principal is recommended.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Pod and node subresources (privilege escalation paths)
|
|
190
|
+
|
|
191
|
+
**Refused operations**:
|
|
192
|
+
- `pods/exec` — `kubectl exec` into any Pod, especially in `kube-system`, `cilium`, `istio-system`, `linkerd`
|
|
193
|
+
- `pods/portforward` — `kubectl port-forward` to any Pod
|
|
194
|
+
- `pods/proxy` — `kubectl proxy --pod` or direct API proxy
|
|
195
|
+
- `pods/binding` — manual binding of a Pod to a Node, bypassing the scheduler
|
|
196
|
+
- `nodes/proxy` — direct kubelet API access via the apiserver proxy
|
|
197
|
+
|
|
198
|
+
**Why**:
|
|
199
|
+
- `pods/exec` into a `kube-system` pod gives shell access in the cluster's control-plane namespace, with the pod's mounted ServiceAccount token (often broadly privileged for control-plane components).
|
|
200
|
+
- `pods/portforward` bypasses NetworkPolicy entirely — the operator's local machine becomes a Pod-network-addressable peer.
|
|
201
|
+
- `pods/proxy` and `nodes/proxy` route requests through the API server to the kubelet, which has its own authorization model that may differ from the API server's RBAC.
|
|
202
|
+
- `pods/binding` lets the caller place a Pod on any Node regardless of node selector, taints, or affinity rules — bypass scheduler entirely.
|
|
203
|
+
|
|
204
|
+
**Instead**: For debugging, use `kubectl debug` with an ephemeral container and `--profile=netadmin` (or `--profile=restricted` for unprivileged debugging). For Service introspection, use a sanctioned debug pod the operator has stood up. Never `pods/exec` from an automated agent.
|
|
205
|
+
|
|
206
|
+
**Cluster-side blast radius if bypassed**: `pods/exec` into the cilium-agent pod is functionally cluster-admin equivalence — the pod has CAP_NET_ADMIN, host network, and the cilium-agent ServiceAccount token. `pods/portforward` can bridge attacker traffic into the Pod network. `pods/binding` defeats every scheduling guarantee the cluster makes.
|
|
207
|
+
|
|
208
|
+
The cluster-side RBAC binding omits all of `pods/exec`, `pods/portforward`, `pods/proxy`, `pods/binding`, `nodes/proxy`, and `pods/eviction`.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## CSR approval and TokenRequest minting
|
|
213
|
+
|
|
214
|
+
**Refused operations**:
|
|
215
|
+
- `update` on `certificates.k8s.io/v1` `CertificateSigningRequest` `approval` subresource (CSR approval)
|
|
216
|
+
- `create` on `certificates.k8s.io/v1` `CertificateSigningRequest` resources with a `subject` containing `O=system:masters` or any `system:` group
|
|
217
|
+
- `create` on `serviceaccounts/token` (TokenRequest API) for any ServiceAccount outside the agent's own.
|
|
218
|
+
|
|
219
|
+
**Why**:
|
|
220
|
+
- A CSR approved with subject `O=system:masters, CN=...` produces a client certificate that authenticates as a member of `system:masters` — RBAC bypassed entirely. This is the Kubernetes equivalent of issuing a root token.
|
|
221
|
+
- `TokenRequest` mints a fresh time-bounded token for any ServiceAccount the caller has `create` permission on the token subresource for. If the bound principal can call `tokenrequests` on `system:serviceaccount:kube-system:cilium`, it becomes the cilium-agent for the token's lifetime.
|
|
222
|
+
|
|
223
|
+
**Instead**: CSR approval is a cluster-admin operation. ServiceAccount tokens are minted at pod creation by the kubelet via `TokenRequestProjection`, never ad-hoc.
|
|
224
|
+
|
|
225
|
+
**Cluster-side blast radius if bypassed**: Approving a `system:masters` CSR is a permanent cluster-takeover — the cert is valid until expiry and not revocable without rotating the cluster CA. Minting a token for a privileged SA gives time-bounded but full access to whatever that SA can do.
|
|
226
|
+
|
|
227
|
+
The cluster-side RBAC binding omits `apiGroups: ["certificates.k8s.io"]` entirely and omits `serviceaccounts/token` create.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Manual Endpoints / EndpointSlices writes
|
|
232
|
+
|
|
233
|
+
**Refused operations**: any `create` / `patch` / `update` / `delete` on `v1.Endpoints` or `discovery.k8s.io/v1.EndpointSlice` resources outside of the controller-managed lifecycle.
|
|
234
|
+
|
|
235
|
+
**Why**: `EndpointSlice` is owned by the EndpointSlice controller, which derives slices from `Service` selectors and Pod readiness. Manual writes race with the controller — the controller will eventually reconcile and overwrite the manual change. But during the window between manual write and reconciliation (tens of seconds to minutes), Service traffic routes to whatever IPs the manual write specified. An attacker-controlled IP in an EndpointSlice for a sensitive Service gets traffic for that window.
|
|
236
|
+
|
|
237
|
+
`v1.Endpoints` (the older, controller-managed API) has the same race-condition concern.
|
|
238
|
+
|
|
239
|
+
**Instead**: To redirect Service traffic, change the Service's selector or the backend Pods' labels. Use ExternalName Services for cross-cluster targets. Never manual EndpointSlice writes.
|
|
240
|
+
|
|
241
|
+
**Cluster-side blast radius if bypassed**: Transient man-in-the-middle of any selected Service. Even after reconciliation, log analysis may show the malicious window if not searched for explicitly.
|
|
242
|
+
|
|
243
|
+
The cluster-side RBAC binding grants only `get`/`list`/`watch` on `endpoints` and `endpointslices` — never write.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## kube-system ConfigMap writes outside the resourceName-locked allowlist
|
|
248
|
+
|
|
249
|
+
**Refused operations**: any `create` / `patch` / `update` / `delete` on ConfigMaps in `kube-system` (or any namespace where the cluster's control plane runs) **other than** the explicitly resourceName-locked `ConfigMap/coredns` permitted in `permitted-mutations.md`.
|
|
250
|
+
|
|
251
|
+
This includes:
|
|
252
|
+
- `ConfigMap/cilium-config` — Cilium agent runtime configuration; changing it may switch endpoint routing modes or disable encryption
|
|
253
|
+
- `ConfigMap/kube-proxy` — kube-proxy mode and behavior
|
|
254
|
+
- `ConfigMap/kubelet-config` — kubelet flags (applied on next kubelet restart)
|
|
255
|
+
- `ConfigMap/cluster-info` (in `kube-public`) — cluster bootstrapping reference
|
|
256
|
+
- Any `ConfigMap` with the label `addonmanager.kubernetes.io/mode: Reconcile` — addon-manager-owned
|
|
257
|
+
|
|
258
|
+
**Why**: These ConfigMaps drive the behavior of the cluster's control-plane components. Modifying them is functionally equivalent to a control-plane reconfiguration. The CoreDNS ConfigMap is the one exception, and only because we have a tight reload-and-verify protocol for it — `permitted-mutations.md` documents that protocol explicitly.
|
|
259
|
+
|
|
260
|
+
**Instead**: Control-plane reconfiguration goes through the cluster's bootstrapping tooling (kubeadm, the cloud-provider's managed control plane, or the operator's own CRD-driven config). Never ad-hoc.
|
|
261
|
+
|
|
262
|
+
**Cluster-side blast radius if bypassed**: Patching `cilium-config` to disable encryption silently removes pod-to-pod mTLS. Patching `kube-proxy` ConfigMap and triggering a DaemonSet restart cycles every node's kube-proxy with the new config — this is the kube-proxy mode swap one-way door (above) by another path.
|
|
263
|
+
|
|
264
|
+
The cluster-side RBAC binding restricts `patch` on `configmaps` to the literal `resourceNames: ["coredns"]`. All other `kube-system` ConfigMaps are denied at the API server level.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## PriorityClass and IngressClass cluster-scoped resources
|
|
269
|
+
|
|
270
|
+
**Refused operations**:
|
|
271
|
+
- any `delete` / `update` / `patch` on `scheduling.k8s.io/v1.PriorityClass` resources, especially `system-cluster-critical` and `system-node-critical`
|
|
272
|
+
- any `delete` / `update` / `patch` on `networking.k8s.io/v1.IngressClass` resources
|
|
273
|
+
|
|
274
|
+
**Why**:
|
|
275
|
+
- Per upstream `kubernetes.io/docs/reference/kubernetes-api/workload-resources/priority-class-v1`, deleting a PriorityClass is supported but the `system-cluster-critical` and `system-node-critical` classes are critical to control-plane eviction order. Removing them, or lowering their priority, can cause control-plane Pods to be evicted first under node pressure.
|
|
276
|
+
- Per upstream `kubernetes.io/docs/reference/kubernetes-api`, `IngressClass` is the binding between an Ingress controller and the Ingress resources it claims. Deleting it leaves every Ingress in that class without a controller — traffic stops.
|
|
277
|
+
|
|
278
|
+
**Instead**: PriorityClass and IngressClass are install-time concerns of the operator's chart. Never modified ad-hoc.
|
|
279
|
+
|
|
280
|
+
**Cluster-side blast radius if bypassed**: `system-cluster-critical` deletion: kube-apiserver, kube-scheduler, etcd, control-plane addons can be evicted under pressure, escalating a node-level resource problem to a cluster-level outage. `IngressClass` deletion: every external HTTP route in the cluster fails until the IngressClass is restored and the controller's reconcile loop re-binds.
|
|
281
|
+
|
|
282
|
+
The cluster-side RBAC binding omits `apiGroups: ["scheduling.k8s.io"]` and `apiGroups: ["networking.k8s.io"], resources: ["ingressclasses"]` entirely (`ingresses` itself is read-only on this guard since Gateway API supersedes it).
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Lease objects in `kube-node-lease`
|
|
287
|
+
|
|
288
|
+
**Refused operations**: any `create` / `patch` / `update` / `delete` on `coordination.k8s.io/v1.Lease` objects in the `kube-node-lease` namespace.
|
|
289
|
+
|
|
290
|
+
**Why**: Each Node has a Lease object in `kube-node-lease` whose `spec.renewTime` is updated by the kubelet to signal liveness. The node-controller marks a Node as `NotReady` after a configurable grace period (default 40 seconds) without a renewed Lease. Manipulating these:
|
|
291
|
+
- Deleting a Node's Lease may not immediately mark it `NotReady` (the Node controller has its own grace) but can interfere with the kubelet's heartbeat path.
|
|
292
|
+
- Patching `spec.renewTime` to far-future makes a dead node appear alive, which makes Pod scheduling place workloads on a node that will never run them.
|
|
293
|
+
- Patching to far-past forces premature `NotReady`, triggering Pod eviction (if `node.kubernetes.io/not-ready` toleration is short).
|
|
294
|
+
|
|
295
|
+
**Instead**: Node liveness is the kubelet's responsibility. Never manual Lease edits.
|
|
296
|
+
|
|
297
|
+
**Cluster-side blast radius if bypassed**: False-Ready nodes accept Pod placement that never executes; false-NotReady nodes trigger workload churn.
|
|
298
|
+
|
|
299
|
+
The cluster-side RBAC binding omits `apiGroups: ["coordination.k8s.io"]` entirely.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Refusal response format
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
REFUSED — <rule-section-header-from-this-document>
|
|
307
|
+
|
|
308
|
+
Reason: <one-sentence explanation grounded in this document>
|
|
309
|
+
What you can do instead: <pointer to permitted-mutations.md or to architecture-review-agent for cutover plan>
|
|
310
|
+
RBAC enforcement: <whether the cluster-side binding also denies this verb (yes / no / depends on operator's principal)>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
No retry. No "well actually". No partial execution. The refusal is the response.
|