9.2 The 4C's of Cloud Native Security & Supply-Chain Trust
Key Takeaways
- The 4C's of cloud native security are Cloud, Cluster, Container, and Code, layered so that each outer ring's weaknesses cannot be fixed by hardening only the inner ones.
- A securityContext sets per-Pod and per-container hardening such as runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation false, and dropping all Linux capabilities.
- Policy engines such as OPA Gatekeeper and Kyverno run as validating admission webhooks and reject non-compliant objects before they reach etcd.
- Supply-chain trust rests on generating an SBOM, scanning for CVEs, signing the image digest with Sigstore Cosign, and verifying that signature at admission.
- Falco is the CNCF graduated runtime security project that detects suspicious syscall behaviour inside running containers, which scanning at build time cannot catch.
9.2 The 4C's of Cloud Native Security & Supply-Chain Trust
Quick Answer: Cloud native security is taught as four nested layers — the 4C's: Cloud, Cluster, Container, Code. Each ring contains the ones inside it, so a weakness in an outer layer cannot be patched by hardening an inner one: a container running as non-root is still exposed if the cluster's API server is open to the internet. Around those layers sits the software supply chain: SBOMs, vulnerability scanning, Sigstore/Cosign signing, admission-time verification, and Falco for runtime detection.
Section 9.1 covered authentication, RBAC, and admission — who may do what. This section covers the rest of the security surface, which is where most real-world compromises actually happen.
1. The 4C's Model
┌───────────────────────────────────────────────────────────┐
│ CLOUD (or datacenter / colo) │
│ network perimeter, IAM, VPC, encryption, physical access │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CLUSTER │ │
│ │ API server exposure, RBAC, admission, NetworkPolicy│ │
│ │ etcd encryption, audit logging, component TLS │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ CONTAINER │ │ │
│ │ │ image provenance, scanning, non-root user, │ │ │
│ │ │ minimal base, seccomp / AppArmor, no privileges│ │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │ │
│ │ │ │ CODE │ │ │ │
│ │ │ │ TLS in transit, input validation, │ │ │ │
│ │ │ │ dependency hygiene, secrets management, │ │ │ │
│ │ │ │ static analysis │ │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
The governing principle: you can only secure an inner layer as well as the outer layers permit. Perfect application code inside a container running as root, on a node whose kubelet API is unauthenticated, in a VPC with an open security group, is not secure. KCNA asks this as a layering question — given a described weakness, which C is it?
| Layer | Owner, typically | Representative controls |
|---|---|---|
| Cloud | Platform/infra team | Private control plane endpoints, IAM least privilege, network segmentation, encrypted disks, node OS patching |
| Cluster | Cluster operators | RBAC, Pod Security Admission, NetworkPolicy, etcd encryption at rest, audit logs, restricted kubelet API |
| Container | Application + platform | Minimal signed images, non-root user, read-only root filesystem, dropped capabilities, seccomp profile |
| Code | Developers | Dependency scanning, TLS everywhere, no hard-coded secrets, input validation |
2. Hardening the Container: securityContext
The practical expression of the Container layer is the securityContext, settable at Pod level (applies to all containers) and container level (overrides the Pod).
spec:
securityContext: # Pod level
runAsNonRoot: true
runAsUser: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: registry.example.com/api@sha256:9f2c…
securityContext: # container level
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
privileged: false
capabilities:
drop: ["ALL"]
| Field | Why it matters |
|---|---|
runAsNonRoot: true | The kubelet refuses to start the container if the image's user is UID 0. Container root is still host root in the absence of user namespaces. |
allowPrivilegeEscalation: false | Blocks setuid binaries and sudo from gaining more privilege than the parent process |
readOnlyRootFilesystem: true | An attacker cannot drop a binary into the container filesystem. Writable paths come from explicit emptyDir mounts. |
capabilities.drop: ["ALL"] | Removes every Linux capability; add back only what is genuinely needed (NET_BIND_SERVICE for ports below 1024) |
privileged: false | privileged: true grants effectively full host access and is the single most dangerous field in a Pod spec |
seccompProfile: RuntimeDefault | Applies the runtime's syscall filter, blocking dozens of rarely needed and frequently abused syscalls |
These settings are exactly what the Restricted Pod Security Standard enforces, which is why enabling Pod Security Admission with enforce: restricted on a namespace is the fastest way to apply them cluster-wide.
3. Policy Engines
Pod Security Admission covers Pod hardening well but nothing else. For organisation-specific rules — "every image must come from our registry", "every namespace must carry a cost-centre label", "no Service of type LoadBalancer outside the edge namespace" — you need a policy engine running as a validating admission webhook.
| Engine | Policy language | Notes |
|---|---|---|
| OPA Gatekeeper | Rego (Open Policy Agent) | CNCF graduated OPA; extremely expressive, steeper learning curve |
| Kyverno | YAML | CNCF incubating; policies look like Kubernetes manifests, so adoption is faster. Can validate, mutate, and generate |
Both run in the admission chain described in section 9.1, so a violating object is rejected before it is ever persisted. Both support an audit or warn mode first, which is how you roll a policy out without breaking every existing workload on day one.
4. Software Supply Chain Security
The supply chain is the path from source commit to running container, and it has been the source of the most damaging recent incidents. Four controls, in order:
a) Generate an SBOM
A Software Bill of Materials enumerates every package and version inside an artifact, in a standard format (SPDX or CycloneDX). Its value is retrospective: when a new CVE is announced, an SBOM answers "are we affected, and where?" in seconds instead of days. Tools: Syft, Trivy.
b) Scan
Scan images at build time and continuously afterwards — an image that was clean on Monday is vulnerable on Friday when a CVE is published against a library it already contains. Tools: Trivy, Grype, Clair, and registry-integrated scanners in Harbor and Quay.
c) Sign and Attest
Sigstore — an OpenSSF graduated project under the Linux Foundation, not a CNCF one — is what made signing practical. Its Cosign tool signs an image digest and, with keyless signing, binds the signature to an OIDC identity (a GitHub Actions workflow, for example) recorded in the Rekor transparency log — so there is no long-lived private key to steal.
cosign sign --yes registry.example.com/api@sha256:9f2c…
cosign verify --certificate-identity-regexp '.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
registry.example.com/api@sha256:9f2c…
Alongside signatures, in-toto attestations and SLSA provenance record how an artifact was built, so a consumer can require "built by our pipeline from our repository" rather than merely "signed by someone".
d) Verify at Admission
Signing achieves nothing unless the cluster checks it. A Kyverno or Gatekeeper policy — or the Sigstore policy controller — verifies the signature at admission and rejects unsigned or unverifiable images. That is the step that closes the loop.
commit ─► CI build ─► SBOM ─► scan ─► sign (Cosign) ─► push
│
admission verify ▼
cluster accepts or rejects
5. Runtime Security
Every control above happens before the container runs. Runtime security watches what it does afterwards — necessary because a zero-day, a compromised dependency, or a live exploit produces no build-time signal.
Falco is the CNCF graduated runtime security project. It taps kernel syscalls (via eBPF or a kernel module), enriches them with Kubernetes metadata, and evaluates them against rules:
- a shell spawned inside a production container,
- an unexpected write to
/etcor a binary directory, - an outbound connection to an unknown IP from a database Pod,
- a container attempting to read the ServiceAccount token it should not need.
Falco alerts; it does not block by default. Response is typically wired into an incident pipeline or a Falco Talon / Falcosidekick action.
Complementary runtime controls include Tetragon (eBPF-based observability and enforcement) and sandboxed runtimes — gVisor and Kata Containers from section 6.1 — which reduce the blast radius when an escape is attempted.
6. A Practical Checklist
| Do | Instead of |
|---|---|
| Pin images by digest and verify signatures at admission | Pulling latest from a public registry |
enforce: restricted Pod Security Admission on app namespaces | Trusting each team to set securityContext |
| Default-deny NetworkPolicy plus explicit allows | A flat, fully open Pod network |
| Encryption at rest for etcd, ideally KMS-backed | Relying on base64 to protect Secrets |
| Short-lived projected ServiceAccount tokens | Long-lived static token Secrets |
| Scan continuously and act on the SBOM | Scanning once at build and forgetting |
| Falco (or equivalent) for runtime detection | Assuming build-time scanning is sufficient |
In the 4C's model of cloud native security, which statement captures the layering principle?
A team signs every image with Cosign in their CI pipeline, but a deliberately unsigned image is deployed to the cluster and runs normally. What is missing?
Which capability does Falco provide that container image scanning cannot?