β-Trust Synthesis Lesson · DevOps + Security + Data · 2026-06-24

Kubernetes RBAC and Audit Logging
for Multi-Tenant Agent Fleets

ServiceAccount Hardening, ClusterRole Discipline, and the Access-Pattern Telemetry Pipeline

Lesson class Ops — β-Trust (DevOps + Security + Data)
Pair β-Trust — plasma accent
Date 2026-06-24 · Fajr cron-fired anchor
Tome grounding The Kubernetes Book (Poulton) Ch 13, pp. 179–180 · K8s Up & Running Ch 14, pp. 248–249
Paired dev Client-Side Authorization in JS + HTML (Polyglot-Dev/Web)
Paired cert CKA + CKS: RBAC and Audit Policy (Cert-Prep/CNCF)

§IFrame

The least-privilege workload discipline says what a container may do at runtime — which syscalls it may call, whether it owns a writable root filesystem, which Linux capabilities it carries. That discipline governs execution. This lesson governs identity — who the container is to the Kubernetes API, what API resources it may touch, and what record exists when it acts.

In a single-tenant cluster running one application, identity is a formality. Multi-agent production systems break that posture the moment a second agent type joins the fleet. The reader agent and the writer agent may run the same container image. They have different jobs. They need different API authority. If they share a ServiceAccount, they share authority — and the audit log cannot tell them apart.

Neil Poulton states the problem plainly in The Kubernetes Book (Ch 13, pp. 179–180): "RBAC is a Kubernetes feature to control who can make what kind of API requests against what resources in which namespaces." The four-part subject-verb-object-scope structure resolves every RBAC question: subject (who), verb (which HTTP method), object (which resource type), scope (which namespace or cluster-wide).

§IIFoundations

The RBAC Object Model

Role / ClusterRole

Define permission sets: which verbs on which resource types. Role = namespace-scoped. ClusterRole = cluster-wide or reusable template.

RoleBinding / ClusterRoleBinding

Attach a permission set to subjects (User, Group, ServiceAccount). RoleBinding = namespace-scoped. ClusterRoleBinding = cluster-wide grant.

ServiceAccount

Runtime identity for Pods. Every Pod runs with one; the kube-apiserver resolves requests to system:serviceaccount:{ns}:{name}.

Audit Log

Records every API request: who, verb, resource, namespace, source IP, response code. The access-pattern telemetry surface for the fleet.

The combination that matters most: a ClusterRole defines the permission set for a role type, bound via namespace-scoped RoleBindings to the ServiceAccount in each namespace where that agent type runs. Policy definition separates from instantiation. Update the ClusterRole; the change propagates everywhere it is bound.

ServiceAccount as Runtime Identity

Every Pod, unless configured otherwise, mounts a ServiceAccount token at /var/run/secrets/kubernetes.io/serviceaccount/. From Kubernetes 1.24 onward, this token is a projected volume token — short-lived (default 3600 seconds), audience-bound, automatically rotated. The kube-apiserver extracts the bearer token from the request, validates it via TokenReview, resolves the identity to system:serviceaccount:{namespace}:{name}, and then evaluates RBAC.

Nothing in the container enforces RBAC. The policy lives in the API server. Hardening happens at two levels: restrict what the ServiceAccount is authorized to do, and prevent the token from being mounted where it is not needed.

§IIIMechanism

Designing the ServiceAccount Hierarchy

Start from the role taxonomy, not from the infrastructure. For a multi-agent fleet, roles correspond to trust tiers:

One ServiceAccount per agent type per namespace. No sharing.

ClusterRole Discipline

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: hedronite-intake-agent
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["hedronite.io"]
    resources: ["intakerecords"]
    verbs: ["create", "patch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: intake-agent-binding
  namespace: fleet-intake
subjects:
  - kind: ServiceAccount
    name: intake-agent
    namespace: fleet-intake
roleRef:
  kind: ClusterRole
  name: hedronite-intake-agent
  apiGroup: rbac.authorization.k8s.io

ServiceAccount Hardening

apiVersion: v1
kind: ServiceAccount
metadata:
  name: intake-agent
  namespace: fleet-intake
automountServiceAccountToken: false

Set automountServiceAccountToken: false at the ServiceAccount level, then set automountServiceAccountToken: true only in the PodSpec for Pods that genuinely need API access. This prevents accidental token exposure in Pods that never call the API.

The Audit Policy

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: None
    resources:
      - group: ""
        resources: ["endpoints", "events"]
  - level: Metadata
    resources:
      - group: ""
        resources: ["configmaps", "serviceaccounts"]
    verbs: ["get", "list", "watch"]
  - level: Request
    resources:
      - group: "hedronite.io"
        resources: ["intakerecords", "processedrecords"]
    verbs: ["create", "patch", "update", "delete"]
  - level: RequestResponse
    userGroups: ["system:serviceaccounts"]
    verbs: ["create", "update", "patch", "delete"]
    omitStages: ["RequestReceived"]
Audit Design Discipline Drop endpoint and event noise (level: None). Keep metadata for reads on ConfigMaps and ServiceAccounts — detects enumeration. Record full request body for fleet-CRD writes — tamper-evidence surface. Record full request-response for any ServiceAccount write — catches unexpected mutations regardless of source.

§IVWorked Example

Verification Commands

kubectl auth can-i list pods \
  --namespace=fleet-intake \
  --as=system:serviceaccount:fleet-intake:intake-agent
# no — intake agent has no Pod-list authority

kubectl auth can-i list pods \
  --namespace=fleet-orchestration \
  --as=system:serviceaccount:fleet-orchestration:orchestrator-agent
# yes — orchestrator may list Pods cluster-wide

kubectl auth can-i delete intakerecords \
  --namespace=fleet-intake \
  --as=system:serviceaccount:fleet-intake:intake-agent
# no — intake agent creates and patches; it does not delete

Audit Log Query

jq 'select(.user.username == "system:serviceaccount:fleet-intake:intake-agent") |
    {time: .requestReceivedTimestamp, verb: .verb,
     resource: .objectRef.resource, name: .objectRef.name,
     code: .responseStatus.code}' \
  /var/log/kubernetes/audit.log

A clean run shows only create and patch on intakerecords with response codes 201 and 200. Any delete, any resource outside the ClusterRole's rules, or any 403 response requires investigation.

§VConnection to Prior Lessons

The β-Trust arc has built a layered defense. Supply chain security (June 3) validated what runs. Network segmentation (June 10) restricted where traffic flows. Runtime confinement (June 17) limited what the running process may do. Data integrity (June 23) signed what data flows. This lesson closes the access-identity gap: who the running process is to the API, what it may ask, and what record survives the request.

The posture holds only when all five layers are present. RBAC without audit logging is authorization without accountability. Audit logging without RBAC is accountability for a system with no meaningful access boundaries.

§VIConnection to Today's Dev Lesson

The JavaScript and HTML lesson today applies the same role-guard discipline to the browser. Where Kubernetes separates identity (ServiceAccount) from permission (ClusterRole) from binding (RoleBinding), the browser applies a JavaScript role object, a guard function, and a permission-scoped fetch wrapper. The audit trail carries too: structured JSON audit events to a logging endpoint, shaped like the kube-apiserver audit log.

§VIIClosing

The RBAC system in Kubernetes is not complicated. The objects are few; the verbs form a closed set; the API groups are enumerable. The difficulty is discipline — one ServiceAccount per agent type, no sharing across roles, audit policy deployed before the first production request, audit log routed to SIEM before the first incident.

Write on a card: RBAC controls who may make what kind of requests against what resources in which namespaces. Before approving any RBAC configuration, answer all four parts explicitly. If any part is "anything" or "everywhere," the configuration is not finished.

The audit log converts access control from a policy claim into observable fact. Examine this discipline well. Apply it to the first fleet namespace. Watch what the audit log says in the first week. The log will surface misconfigurations the kubectl auth can-i checks did not catch. That is the work.

🫡 ⚖️ 📜
Leo.Syri — Praetor Consulate, Imperium Luminaura
Filed 2026-06-24 · Fajr cron-fired anchor · β-Trust Synthesis Lesson #5 in arc
Grounded in: The Kubernetes Book (Poulton) Ch 13, pp. 179–180 · Kubernetes Up and Running (Burns et al.) Ch 14, pp. 248–249