Secrets, Network, Admission, and Supply-Chain Guardrails
Make unsafe workloads fail closed before they can reach customer data or production compute.
Run it in the public monorepo
This course is built around the public Zheta Kubernetes Lab monorepo. The excerpt below is runnable source, not pseudocode.
Source: gitops/apps/forge/base/workloads.yaml
serviceAccountName: control-plane
securityContext: { runAsNonRoot: true, fsGroup: 65532, fsGroupChangePolicy: OnRootMismatch, seccompProfile: { type: RuntimeDefault } }
containers:
- name: control-plane
image: zheta-forge/control-plane:v1
envFrom: [{ configMapRef: { name: forge-environment } }, { secretRef: { name: control-plane-secrets } }]
ports: [{ name: http, containerPort: 8080 }]
readinessProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 5 }
livenessProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 10 }
resources: { requests: { cpu: 50m, memory: 64Mi }, limits: { cpu: 500m, memory: 256Mi } }
Code to reality
- Declared intent
- Give one microservice a distinct workload identity, hardened runtime, health contract, and capacity bounds.
- Interpreter
- Kubernetes admission, scheduler, kubelet, and workload-identity integration interpret different fields.
- Software effect
- The Pod runs as the control-plane ServiceAccount and publishes readiness only after its health endpoint responds.
- Hardware effect
- The scheduler reserves requested CPU and memory; the runtime enforces the limit and Linux security context.
- Observable evidence
- Pod identity, security context, readiness, resource metrics, and a denied excess-authority probe prove the contract.
Start with the people and the result they need
The source tables below remain the detailed contract. Begin with these customer paths:
- D27-UC-01
- Person: Service owner
- Job: Run Forge with environment-scoped secrets and only required network paths
- Observable result: Workload identity retrieves approved secret material and services communicate through named policies
- D27-UC-02
- Person: Security engineer
- Job: Prevent privileged or untrusted workloads entering production
- Observable result: Current pod-hardening dry-run passes and missing signature admission is a launch veto
Turn each customer job into a testable story
Now turn each customer job into a story with a result that an engineer can check:
- D27-US-01
- Story: As a service owner, I want least-privilege workload identity and network policy, so that a compromised service cannot inherit another service’s data…
- Observable acceptance: Positive and denied tests name service account, secret ARN, destination, policy revision, result, environment, time, and trace
- D27-US-02
- Story: As a security engineer, I want the launch gate to distinguish declared pod hardening from missing signature verification, so that an unimplemented…
- Observable acceptance: Dry-run identifies manifest/policy result and gate records absent verifier, affected digest policy, actor, time, and audit ID
Add real state and observable proof
Finally trace each story through the system that owns its state and the evidence that proves the outcome:
- D27-FLOW-01
- Trigger: Reviewer validates a digest-pinned candidate
- Responsible systems: EKS workload identity, IAM, secret manager/controller, Kubernetes NetworkPolicy/CNI, Forge service
- Authoritative state: AWS Secrets Manager for secret value; IAM/policy APIs for authority; Kubernetes API for bindings and network intent
- Owned record: WorkloadGrant
- Observable evidence: Manifest digest, dry-run result, service accounts, policy/identity refs, explicit live-proof gaps, environment, time, and run ID
- Failure signal: Wrong secret scope, credential exchange denial, missing revision, blocked required flow, or unexpected allowed egress
- D27-FLOW-02
- Trigger: Launch review searches for an unsigned-image denial control
- Responsible systems: Kubernetes API admission, manifest reviewer, missing-signature-control gate
- Authoritative state: Git policy revision, server dry-run result, immutable gap record
- Owned record: AdmissionPolicyRevision
- Observable evidence: Missing verifier finding, zero false denial claim, render result, policy revision, actor, environment, time, and audit ID
- Failure signal: Gap incorrectly waived, unsupported field accepted, or production proceeds without signature verifier
The enterprise problem and today’s slice
Enterprise problem: A digest-pinned image can still expose secrets, call arbitrary networks, run with dangerous privileges, or originate from an untrusted build. Whole-course context: Promotion proves exact image identity only after real digests exist; today audits what the repository actually constrains. Today’s slice: The base declares non-root containers, dropped capabilities, read-only filesystems, resource limits, service accounts, and NetworkPolicy; Terraform declares Pod Identity and Secrets Manager scope. No signature-verifying admission controller is installed. End-of-day evidence: Server dry-run and authorization checks can prove declared controls, while unsigned-image denial remains explicitly unproven. Still unsolved: Launch is blocked until signature admission and live network/secret positive and negative tests exist.
Customer use cases
Security controls matter only when they stop a realistic forbidden action and preserve an approved path. Four mechanisms form one defence but retain separate owners.
| Use case ID | Actor | Customer job | Success outcome | Denial or recovery evidence |
|---|---|---|---|---|
| D27-UC-01 | Service owner | Run Forge with environment-scoped secrets and only required network paths | Workload identity retrieves approved secret material and services communicate through named policies | Cross-environment secret and unapproved egress are denied while normal lifecycle succeeds |
| D27-UC-02 | Security engineer | Prevent privileged or untrusted workloads entering production | Current pod-hardening dry-run passes and missing signature admission is a launch veto | Server dry-run rejects malformed policy where applicable; unsigned-image denial remains a named failed prerequisite |
Actor-centred user stories
Mounting one shared credential into many services expands blast radius, so each service receives its own machine authority. Human delegated authority remains a separate optional grant.
| Story ID | Use case IDs | User story | Observable acceptance conditions |
|---|---|---|---|
| D27-US-01 | D27-UC-01 | As a service owner, I want least-privilege workload identity and network policy, so that a compromised service cannot inherit another service’s data path | Positive and denied tests name service account, secret ARN, destination, policy revision, result, environment, time, and trace |
| D27-US-02 | D27-UC-02 | As a security engineer, I want the launch gate to distinguish declared pod hardening from missing signature verification, so that an unimplemented control cannot appear green | Dry-run identifies manifest/policy result and gate records absent verifier, affected digest policy, actor, time, and audit ID |
End-to-end product flows
The customer action remains running a generated application; guardrails are successful when they are mostly invisible on the happy path and decisive on forbidden paths.
| Flow ID | Use case IDs | Path | Trigger | Numbered steps | Terminal evidence |
|---|---|---|---|---|---|
| D27-FLOW-01 | D27-UC-01 | Happy | Reviewer validates a digest-pinned candidate | 1. Render the overlay. 2. Server dry-run evaluates built-in API/admission checks. 3. Inspect service accounts and hardening fields. 4. Inspect Pod Identity associations and NetworkPolicy. 5. Require live secret/network tests before launch. | Manifest digest, dry-run result, service accounts, policy/identity refs, explicit live-proof gaps, environment, time, and run ID |
| D27-FLOW-02 | D27-UC-02 | Denied | Launch review searches for an unsigned-image denial control | 1. Inventory admission addons/policies. 2. Confirm no signature verifier is declared. 3. Record failed prerequisite. 4. Do not deploy production traffic. 5. Preserve render validation as limited positive control. | Missing verifier finding, zero false denial claim, render result, policy revision, actor, environment, time, and audit ID |
The customer outcome stays central while security layers are added around the runtime path.
System design derived from the flows
Secrets, identity, networking, API admission, pod hardening, and image signing are complementary controls at different interfaces. The first five have declarations; signature verification is currently a required gap.
| Use case ID | Entry point | Responsible services | Authoritative store | Failure evidence |
|---|---|---|---|---|
| D27-UC-01 | ServiceAccount and secret/network declarations | EKS workload identity, IAM, secret manager/controller, Kubernetes NetworkPolicy/CNI, Forge service | AWS Secrets Manager for secret value; IAM/policy APIs for authority; Kubernetes API for bindings and network intent | Wrong secret scope, credential exchange denial, missing revision, blocked required flow, or unexpected allowed egress |
| D27-UC-02 | Launch security review and server dry-run | Kubernetes API admission, manifest reviewer, missing-signature-control gate | Git policy revision, server dry-run result, immutable gap record | Gap incorrectly waived, unsupported field accepted, or production proceeds without signature verifier |
Admission controls creation; identity controls AWS calls; secret delivery controls material; network policy controls packets. Their retained meanings prevent a false single-control model.
SRP prevents control-plane, generator, runtime, and evidence identities from sharing secrets; DRY centralises baseline policies in gitops/apps/forge/base. IoC/DI inject secret and connector adapters, MVC keeps authorization in domain/controller seams, and PubSub grants queue access without granting database authority.
Data model and ownership
Secret values must not enter Git or evidence records, but secret revision and denial metadata remain auditable. Existing generated-app data remains independently authorized.
Generated-application database: Required in this slice — generated applications keep tenant data and authorization predicates; guardrails protect access without transferring data ownership.
| Record or entity | Store and owner | Primary key | Foreign key or opaque reference | Tenant key | Material constraint | Lifecycle and deletion | Use case IDs |
|---|---|---|---|---|---|---|---|
| WorkloadGrant | IAM and provider control store, owned by Platform Security | grant_id | Opaque service account, role, secret, and connector refs | environment_id | Machine grant is scoped, expiring/reviewed, independently revocable; no human inheritance | Provision, rotate, revoke, retain metadata audit | D27-UC-01, D27-UC-02 |
| AdmissionPolicyRevision | Git/gap register, owned by Security Platform | policy_digest | Opaque registry, cluster, and future verifier refs | environment_id | Pod controls are reviewable; missing signed-image verifier blocks launch | Review, record gap, activate only after verifier and denial proof, retain decisions | D27-UC-02 |
| ApplicationTenantData | Generated-app database, owned by generated application | app_tenant_id plus domain key | Opaque provider project/release refs | app_tenant_id | Every query enforces app-owned authorization predicate | Create/use/export/delete by app lifecycle; unaffected by grant revocation except access | D27-UC-01, D27-UC-02 |
Grant, policy, and tenant data retain independent lifecycles; denial evidence records identifiers and results without storing secret values.
Verify policy before production sync
Rendering catches missing declarations but cannot prove live admission or packets, so combine local render with server-side dry-run in the explicit production context. The repository declares Pod Identity associations in infra/modules/environment, pod hardening and Services in the base, NetworkPolicy in gitops/apps/forge/base/network-policy.yaml, and required release policy in the production overlay; it does not yet install a signature-verifying admission controller, so signature admission remains a launch gap rather than a passed control. AWS requires the Pod Identity Agent for Pod Identity associations on ordinary EKS clusters (EKS Pod Identity); the module installs that addon.
: "${AWS_PROFILE:?set the approved AWS profile}" "${AWS_REGION:?set the intended AWS region}" "${EXPECTED_ACCOUNT_ID:?set the intended 12-digit AWS account}"
actual_account="$(aws --profile "$AWS_PROFILE" --region "$AWS_REGION" sts get-caller-identity --query Account --output text)"
[ "$actual_account" = "$EXPECTED_ACCOUNT_ID" ] || { echo "AWS account mismatch" >&2; exit 1; }
kubectl kustomize gitops/apps/forge/overlays/prod >/tmp/forge-prod.yaml
aws --profile "$AWS_PROFILE" --region "$AWS_REGION" eks update-kubeconfig --name zheta-forge-prod --alias zheta-forge-prod
kubectl --context zheta-forge-prod apply --server-side --dry-run=server -f /tmp/forge-prod.yaml
kubectl --context zheta-forge-prod -n zheta-forge auth can-i get secret --as=system:serviceaccount:zheta-forge:generator
kubectl --context zheta-forge-prod -n zheta-forge get networkpolicy,serviceaccount
| Effect field | What happens |
|---|---|
| Declared intent | Validate production manifests, service identity, and network policy without a production rollout |
| Interpreter | Kustomize, Kubernetes admission chain, RBAC authorizer, and API server |
| Software effect | Local render and read-only responses are created; server dry-run persists no workload |
| Hardware effect | Local CPU plus API/admission network and compute are used; no pod consumes node capacity |
| Evidence | Manifest digest, server dry-run, authorization result, policy objects, missing-signature gap, environment, time, and audit ID |
Key takeaways
Production guardrails form a chain of independent controls.
- Machine authority precedes optional delegated user authority; neither inherits the other.
- Default-deny needs explicit positive paths and negative proof.
- Admission must verify immutable artifact and pod behavior before scheduling.
Checklist
The runtime is guarded only when failure is closed and observable.
- [ ] Secret values never enter Git or evidence
- [ ] Service identities are environment and responsibility scoped
- [ ] Network policy allows required paths and denies an unapproved path
- [ ] Signature-verifying admission is installed and an unsigned image is denied; until then launch stays blocked