
TL;DR
Testing AI agent authorization on Kubernetes requires more than a successful policy function or an impersonated permission check. Use actual workload credentials, distinguish native authorization from admission validation, and read the resulting state through a separately permissioned identity. A request rejected because the client cannot authenticate is not evidence that an action-specific restriction worked.
This companion supplies scoped manifests and a credential-setup helper for a disposable cluster. The target is an unconsumed ConfigMap, not a backup system. Twelve offline helper tests passed locally; Kubernetes deployment, real token issuance, and admission behavior were not executed here. The live outcomes below are acceptance criteria to demonstrate, not reported results.
Prove which identity reached which control, what that control decided, and what changed afterward.
Introduction
The offline retention lab rejects seven days and accepts forty-five. The engineering team now wants to replace its in-memory target with a Kubernetes object.
The first prohibited request fails. Everyone is pleased until the error reveals that the test credential was invalid. The request never reached authorization, much less the admission rule intended to reject the value.
Another attempt runs under the setup administrator. It reaches the API successfully, but now the test says little about the agent’s actual permissions.
Those are integration failures in the measurement process. The expected state may remain unchanged while the claimed control remains untested.
The previous companion established how to challenge an execution gate with a forced favorable review. This installment replaces selected simulation assumptions with a real Kubernetes API exercise: three service accounts, resource-scoped permissions, a fixed admission invariant, and a separate readback path. It does not port the earlier lab’s full controller, approval lifecycle, or sixteen scenarios.
Decide What This Integration Can Establish
The retained teaching values are thirty days initially, forty-five permitted, and seven prohibited. Here they are strings in a ConfigMap called retention-fixture in the namespace rtb-boundary-lab.
No application, operator, or backup service may consume that object. The name describes the teaching example; changing it must have no retention effect outside the fixture.
A second ConfigMap, out-of-scope-fixture, provides a target the executor must not modify. Both start with only retentionDays: "30". The service accounts represent future application roles; no model or agent process is deployed.
| Boundary | Proposed implementation | Claim to test |
|---|---|---|
| Caller identity | Three actual Kubernetes service-account credentials. | The API recognizes the intended caller, not the setup administrator. |
| Resource authority | Narrow Roles and RoleBindings. | Only the executor can patch the named target through its assigned permissions. |
| Content constraint | A ValidatingAdmissionPolicy and binding. | Matching updates must produce the permitted data value. |
| Observation | A read-only observer identity. | The relevant object can be inspected without executor credentials. |
| Suspension | Removal of the executor’s RoleBinding. | Subsequent patch requests lose the tested permission. |
A successful exercise would support these bounded findings. It would not establish correct backup retention, action-expiry enforcement, duplicate prevention, tenant-wide isolation, or recovery safety.
The supplied Kubernetes Boundary Kit leaves the previous archives unchanged. Its policy is a fixed content invariant, not an approval service disguised as a manifest.
Establish a Disposable Cluster and a Protected Client
Use an explicitly authorized, disposable cluster running a supported Kubernetes release that exposes the admissionregistration.k8s.io/v1 policy and binding resources. Confirm the relevant admission functionality is enabled. The operator needs a compatible kubectl, permission to create the scoped fixtures and cluster-scoped admission resources, and a supported audit-collection path.
The optional credential helper requires Python 3.10 or later and POSIX filesystem permissions. Local checks used Python 3.13.5 on Linux. The helper uses only Python’s standard library; those checks did not validate a Kubernetes distribution.
The admission objects are cluster-scoped even though their matching condition is narrow. Do not install this exercise in a shared or production cluster. A reassuring context name is not proof of the destination.
Apply the AI agent zero-trust architecture to the test client too. Keep the administrator kubeconfig, executor credentials, observer credentials, and evidence directory outside any agent workspace. A prompt telling an agent not to open those files is not isolation.
One trusted operator can demonstrate distinct API identities sequentially. That does not establish independence from compromise of that operator or workstation. Stronger assurance requires separately administered clients and evidence custody.
Record the Environment Before Creating Objects
From the extracted rtb-kubernetes-boundary-kit directory, replace these values with the exact, verified lab kubeconfig and context. Use a new evidence directory for every run.
export ADMIN_KUBECONFIG="$HOME/.kube/rtb-disposable" export LAB_CONTEXT='rtb-disposable' umask 077 mkdir evidence-01 kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" version -o json > evidence-01/versions.json kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" api-resources --api-group=admissionregistration.k8s.io
Confirm both required resource kinds are available. Record the distribution, relevant admission configuration, and existing authorization grants. Resource discovery establishes availability of an API resource, not that the forthcoming policy will match or enforce correctly.
Give Each Identity Only Its Assigned Role
The kit creates rtb-agent, rtb-executor, and rtb-observer service accounts. The agent can read the primary fixture. The executor can read and patch it. The observer can read both fixtures but cannot patch either through the supplied roles.
The executor’s complete Role is small:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: rtb-executor-patch
namespace: rtb-boundary-lab
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["retention-fixture"]
verbs: ["get", "patch"]The accompanying RoleBinding assigns that Role to the executor service account. The manifest does not grant workload creation, secret access, credential issuance, or control administration.
Kubernetes role-based access control (RBAC) permissions are additive. Another binding can expand these identities’ effective access. A narrow Role therefore needs examination alongside the actual assignments, not in isolation.
The native grant also does not constrain retentionDays to forty-five. That is the next control’s responsibility.
Put the Value Constraint in Admission
Kubernetes admission operates after authentication and authorization and before persistence of the object. A request can therefore pass RBAC and still be rejected because its proposed content violates admission policy.
The distinction is visible in the request path. The observer reads from the same API server; it is separate from the executor’s credentials, not independent of a compromised control plane.

The policy uses Common Expression Language (CEL). Its matching condition names the exact namespace and resource rather than relying on an agent-editable label. The key excerpt is:
matchConditions:
- name: exact-fixture
expression: >-
request.namespace == 'rtb-boundary-lab' &&
request.name == 'retention-fixture'
validations:
- expression: >-
has(object.data) &&
object.data == {'retentionDays': '45'}
message: "RTB_RETENTION_45_ONLY: data must contain only retentionDays=45"
reason: ForbiddenThis excerpt explains the rule; deploy the complete 30-admission.yaml from the kit. That file also rejects nonempty binaryData and making the fixture immutable. Its binding specifies validationActions: [Deny], and the policy specifies failurePolicy: Fail.
Kubernetes requires a corresponding binding for the policy to take effect. Its documentation distinguishes validation failure actions from handling expression and configuration errors. Merely creating the policy object is not proof of enforcement.
Keep the Policy’s Limits Visible
The rule matches updates. Creation of the initial thirty-day fixture happens before the policy is installed. The test identities receive neither create nor delete permission through the supplied RBAC.
It is not a complete metadata restriction, one-time execution permit, or dynamic approval check. The executor may retain metadata capabilities within its patch grant. It can also repeat an allowed update while its permissions remain valid.
Do not describe this as a complete production agent authorization system. It demonstrates a specific native boundary that a larger controller can rely on only after validation.
Create the Fixtures Before Issuing Credentials
Create the resources in order, stopping on any unexpected failure. create deliberately refuses existing objects rather than silently updating another run.
kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" create -f manifests/00-namespace.yaml kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" create -f manifests/10-fixtures.yaml kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" create -f manifests/20-identities.yaml kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" create -f manifests/30-admission.yaml
Preserve and inspect partial setup if a command fails. Do not bulk-apply the directory or overwrite earlier evidence until the baseline appears clean.
Export the installed policy and binding. Examine status.typeChecking when available and investigate expression warnings. Missing status does not establish successful checking, and clean type-checking output still does not prove request matching. The permitted and prohibited request tests remain necessary.
Authenticate with the Actual Test Credentials
The agent identity without human credentials pattern applies to this exercise: the setup administrator provisions access but does not stand in for the actor during tests.
After reviewing the helper, run:
python make_identity_configs.py --admin-kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" --output credentials-01 --confirm-disposable
This command makes live requests when run against the selected cluster. It requests three service-account tokens and creates minimal, token-only kubeconfigs. It copies the selected endpoint and certificate authority, not the administrator’s client key, user entries, or authentication plugin. It then checks each new identity using kubectl auth whoami.
The files use private POSIX permissions and must remain credential material, not benchmark evidence. The helper does not print tokens or place their values in command-line arguments. Shared-account access, privileged host users, filesystem behavior, and backups still require protection.
Kubernetes documents that a requested token lifetime can differ from the lifetime actually issued. The helper requests ten minutes; that is not a guaranteed expiration or an action-approval deadline.
Do not replace actual credential use with administrator impersonation through --as. Impersonation can assist authorization investigation, but it does not test whether the intended service-account credential authenticates through this client path.
Prove the Denial Stage Before the Allowed Mutation
First, read the primary fixture using the observer:
kubectl --kubeconfig credentials-01/observer.json -n rtb-boundary-lab get configmap retention-fixture -o json > evidence-01/before.json
Require the expected initial data, and retain the object’s UID and resource version. Stop if the object or baseline is different. Treat resource versions as opaque identifiers, not mutation counts.
Preserve each request, response, error, exit code, and subsequent observer read. The following denial commands return nonzero when rejection occurs. Run them individually or explicitly capture their status; do not let an automated shell discard the remaining evidence after an expected failure.
Test the Agent’s Resource Permission
Submit the permitted value using the agent credential:
kubectl --kubeconfig credentials-01/agent.json -n rtb-boundary-lab patch configmap retention-fixture --type=merge --patch-file patches/permit-45.json
The expected result is a Kubernetes authorization denial for the agent’s patch request, followed by an observer read still showing thirty days.
A connection failure, expired token, discovery error, or generic nonzero status is not that result. Identify the component that rejected the request. If the caller did not authenticate as rtb-agent, repair the identity test before interpreting the outcome.
Test Admission Using the Authorized Executor
Check the executor’s native patch permission, then submit seven days:
kubectl --kubeconfig credentials-01/executor.json -n rtb-boundary-lab auth can-i patch configmap/retention-fixture kubectl --kubeconfig credentials-01/executor.json -n rtb-boundary-lab patch configmap retention-fixture --type=merge --patch-file patches/reject-7.json
The expected can-i answer is yes. It does not inspect the proposed data or establish admission acceptance.
The actual patch should be rejected by rtb-retention-boundary, with the RTB_RETENTION_45_ONLY message. The observer should still see thirty days on the same object.
This paired test matters: the executor reaches the operation it is entitled to request, but a separate control rejects the prohibited content.
Demonstrate Useful Work and Scope Preservation
Now submit the permitted patch using the executor:
kubectl --kubeconfig credentials-01/executor.json -n rtb-boundary-lab patch configmap retention-fixture --type=merge --patch-file patches/permit-45.json kubectl --kubeconfig credentials-01/observer.json -n rtb-boundary-lab get configmap retention-fixture -o json > evidence-01/after-permitted.json
The acceptance condition is an accepted request and an observer read showing only retentionDays: "45" on the same UID. Preserve the changed resource version and relevant server records rather than accepting the executor’s summary alone.
Then exercise the remaining boundaries using the requests documented in the kit:
| Challenge | Expected finding |
|---|---|
Executor adds approval: self-approved alongside forty-five days. | Full-map validation rejects the additional data key. |
| Observer attempts the otherwise permitted patch. | Native authorization rejects mutation by the observer. |
Executor patches out-of-scope-fixture. | Resource-scoped authorization rejects the other target; observer confirms it remains unchanged. |
| A test identity requests control-administration privileges. | Effective authorization does not grant changes to roles, bindings, admission resources, or credential issuance. |
Permission queries support findings about authorization. Broader claims about indirect administrative reach need corresponding tests of deployment pipelines, workload creation, credential access, and delegated tools.
Do not weaken the live policy to make a test succeed. An unexpected permitted write is a failed boundary requiring investigation, even though the fixture itself is inert.
Suspend New Requests Without Pretending to Cancel the Past
Remove the executor’s RoleBinding using the setup identity, then submit another patch through the executor credential:
kubectl --kubeconfig "$ADMIN_KUBECONFIG" --context "$LAB_CONTEXT" -n rtb-boundary-lab delete rolebinding rtb-executor-patch kubectl --kubeconfig credentials-01/executor.json -n rtb-boundary-lab patch configmap retention-fixture --type=merge --patch-file patches/permit-45.json
The required result is denial of the subsequent request. Confirm the observer still reads the established forty-five-day state.
This is a test of effective permission removal on a new request. It does not demonstrate cancellation of an operation already accepted, invalidation of every token copy, or distributed queue suspension. An identity can remain authenticated while lacking the permission it previously exercised.
If the request remains authorized, inspect other bindings and the actual identity before claiming delayed revocation. The kit’s intended Role is only one part of the effective authorization configuration.
Collect Server Evidence Without Expanding Data Exposure
Use the infrastructure change evidence pattern to join the baseline, submitted operation, server decision, and observed state. Record the active admission objects and their versions alongside the requests they governed.
Kubernetes distinguishes audit records from ordinary Event objects. Its Metadata audit level omits request and response bodies; RequestResponse includes them. Audit configuration and the collection backend determine what is actually retained.
For these synthetic ConfigMaps, appropriately scoped body capture can help establish what the server received. Do not copy that setting indiscriminately to Secrets, token requests, or sensitive application resources. The kit does not modify the cluster’s audit configuration.
Correlate the authenticated user, resource, operation, response status, audit identifier, and timing through the platform owner’s supported collection path. Preserve enough context to distinguish an admission rejection from an earlier authorization failure.
Readback Has an Independence Limit
The observer has different permissions, but it reads from the same control plane. Its response can corroborate state against an executor’s claim without establishing independence from a compromised API server or cluster administrator.
Likewise, a before-and-after read does not prove that no intermediate change occurred. Use the relevant event history and qualified observation coverage for that claim.
An observer read failure leaves evidence unavailable. It is not proof that the object stayed unchanged. Preserve client outcome, rejection stage, observed state, and evidence completeness as separate fields.
Troubleshoot the Boundary, Not the Desired Score
| Symptom | Investigate first | Do not conclude |
|---|---|---|
| Both permitted and prohibited requests fail. | Identity, connectivity, RBAC, admission readiness, and fixture validity. | That the value constraint is effective. |
| Seven days is accepted. | Binding presence, request matching, active validation actions, and the actual destination. | That a stored policy object proves enforcement. |
| Agent or observer can patch. | Effective bindings, credential-file contents, and API-reported identity. | That the supplied Role overrides broader grants. |
| Executor cannot perform the permitted patch. | Exact target, namespace, data shape, policy errors, and other admission controls. | That the only remedy is broader privileges. |
| Observer output is missing or inconsistent. | Read permissions, collection failures, concurrent writers, and object replacement. | That a successful client response completes verification. |
Retain failures and partial evidence. A corrected fixture, changed policy, or repaired client creates a revised test condition. Record that change rather than rewriting the earlier result.
The kit’s offline tests validate selected helper behavior, including refusal of insecure TLS configuration, exclusion of administrator credentials, exclusive file creation, and identity-mismatch handling with mocked responses. They do not evaluate CEL or replace these live investigations.
Close the Exercise Without Leaving Its Authority Behind
Stop new requests and preserve the evidence before cleanup. Remove execution permission before dismantling the gate, then remove the admission objects and the test namespace using the setup identity.
Deleting the namespace does not remove the cluster-scoped policy and binding. The README includes their explicit cleanup commands. Do not strip finalizers or broaden permissions merely to force an unexpectedly stuck cleanup through.
The active policy rejects an update returning the fixture to thirty days. For another clean run, use controlled teardown and reconstruction, with a new evidence directory and newly recorded object identities. Do not teach the executor to bypass its controls to reset the test.
Retire local credential files and the actual test identities through the approved process. Removing a local file alone does not revoke a token copied elsewhere.
Decide What the Evidence Allows Next
A completed exercise can support narrow statements: these identities authenticated, these permission boundaries behaved as observed, this admission rule rejected these values, and this observer collected these states under the recorded configuration.
It does not establish the full Agent Action Evidence Contract. Dynamic approval, expiry, current resource preconditions, durable attempt records, duplicate handling, and independently governed verification remain separate integration work.
Before connecting an actual agent, establish that its effective access cannot exceed the tested identity’s scope through workload creation, another tool, an administrative pipeline, or a shared credential. Do not place the executor kubeconfig beside the agent and then count its lack of a direct RoleBinding as protection.
The platform team owns supported API behavior and policy deployment. Identity owners govern credentials and effective grants. Evaluation engineering owns the requests and expected findings. Evidence custodians own observation and retention. The service owner decides whether the demonstrated boundary is sufficient for the next, explicitly limited integration.
Conclusion
Moving from simulation to Kubernetes should replace assumptions with evidence, not attach a more realistic product name to the same test.
Actual credentials make caller identity testable. Resource-scoped RBAC establishes one permission boundary. Admission checks the proposed content. A separately permissioned observer challenges the executor’s report. Each answers a different question, and each has an administrative dependency that must remain visible.
The supplied kit is a starting point for that integration. Its local checks support the files and helper behavior, while the cluster outcomes remain work to perform in an authorized environment.
The control plane must not grade itself.
Submit one prohibited value through an authenticated, otherwise authorized executor. Can another engineer establish which control rejected it and what the target did afterward?
External References
- Kubernetes: Using RBAC Authorization
- Kubernetes: Admission Control in Kubernetes
- Kubernetes: Validating Admission Policy
- Kubernetes: kubectl create token
- Kubernetes: kubectl auth whoami
- Kubernetes: kubectl auth can-i
- Kubernetes: Auditing
- Kubernetes: Kubernetes API Concepts
Test AI agent execution controls with a sixteen-scenario offline lab. Separate authorization, target effects, and completion evidence before validating a real platform.
The post From Simulation to Kubernetes: Testing Agent Identity and Admission appeared first on Digital Thought Disruption.
