Kubernetes Security: The 4Cs, RBAC, Secrets, and Pod Security Admission
Kubernetes security is organized around the 4Cs of Cloud Native Security: Cloud, Cluster, Container, and Code, four nested layers where each inner layer depends on the security of the layers around it. That defense-in-depth frame organizes everything else the KCNA exam asks about security: RBAC and ServiceAccounts grant workloads the least privilege they need, Secrets hold sensitive data but are stored unencrypted in etcd by upstream default, admission control and Pod Security Admission decide which objects the API server accepts, securityContext hardens individual containers, NetworkPolicies restrict traffic, and supply-chain practices such as image scanning, signing and signature verification protect what you ship. The exam stays foundational: it asks what each control is, which layer it belongs to, which threat it does and does not address, and which statement about it is true, for example that PodSecurityPolicy was removed in Kubernetes 1.25 and replaced by Pod Security Admission. This lesson covers each control at exactly that depth.
On this page7 sections
- The 4Cs: defense in depth for cloud native systems
- RBAC and ServiceAccounts: least privilege in the cluster
- Secrets vs ConfigMaps: base64 is not encryption
- Pod Security Admission: Privileged, Baseline, Restricted
- Hardening containers with securityContext
- NetworkPolicy as a security control
- Admission control and supply chain security
- Name the 4Cs of Cloud Native Security, explain why they form defense in depth, and place a given control in its layer
- Apply least privilege to RBAC Roles, ClusterRoles, their bindings, and ServiceAccounts
- Explain how Secrets are stored by default, what RBAC and encryption at rest each protect against, and how encryption at rest is enabled
- Distinguish the Privileged, Baseline, and Restricted Pod Security Standards and the enforce, audit, and warn modes
- Recognize the core securityContext hardening fields and what each one prevents
- Describe the admission sequence and how scanning, signing, signature verification, and SBOMs secure the supply chain
The 4Cs: defense in depth for cloud native systems
The 4Cs of Cloud Native Security model a system as four nested layers: Cloud, Cluster, Container, Code, from outermost to innermost. Your code runs inside a container, the container runs inside a cluster, and the cluster runs on a cloud or datacenter. Each layer can only be as secure as the layers outside it allow: flawless application code does not help if the cluster's API server accepts anonymous requests, and a hardened cluster does not help if the cloud account credentials leak.
Classify a control by asking what it configures: the infrastructure underneath Kubernetes (Cloud), Kubernetes objects and API server settings (Cluster), the image or the container's runtime settings (Container), or the application's own source and dependencies (Code).
| Layer | What it covers | Example controls |
|---|---|---|
| Cloud | The infrastructure the cluster runs on | IAM for the cloud account and for node VMs, firewall rules limiting network access to nodes and to the control-plane endpoint, restricting access to control plane machines |
| Cluster | Kubernetes itself and its configuration | API server authentication settings, RBAC, Pod Security Admission and other admission control, NetworkPolicies, encrypting Secrets at rest, securing etcd |
| Container | The images and runtime settings of workloads | Image scanning, image signing, minimal base images, non-root users, dropped capabilities |
| Code | The application you wrote | Dependency scanning, static analysis, TLS between services, input validation |
The point of the model is defense in depth: no single control is trusted to hold, so each layer adds its own protections and an attacker must defeat several independent barriers. A weakness in an outer layer undermines the inner ones; it does not leave them unaffected. The 4Cs appear in two ways: naming the layers in order, and classifying a given control into its layer. Practice the second form, because most controls in this lesson are Cluster-layer or Container-layer.
RBAC and ServiceAccounts: least privilege in the cluster
RBAC is how Kubernetes decides whether an authenticated subject may perform an action: every API request is first authenticated (who are you?), then authorized (are you allowed to do this?). Role-Based Access Control (RBAC) has four objects in a two-by-two: a Role defines permissions within one namespace, a ClusterRole defines them without a namespace, and a RoleBinding or ClusterRoleBinding attaches those permissions to subjects: users, groups, or ServiceAccounts. Permissions are verbs on resources, such as get and list on pods.
The binding, not the role, decides where permissions apply:
| Binding | Can reference | Grants permissions |
|---|---|---|
| RoleBinding | A Role in its own namespace | In that namespace only |
| RoleBinding | A ClusterRole | In the RoleBinding's namespace only |
| ClusterRoleBinding | A ClusterRole | In every namespace, plus cluster-scoped resources |
A RoleBinding cannot reference a Role from another namespace. Referencing a ClusterRole from RoleBindings is the reuse pattern: define a common set of rules once as a ClusterRole, then create a RoleBinding in each namespace where a team needs them. The team gets those rules only in those namespaces, not cluster-wide, and the rules are maintained in one place.
RBAC is purely additive: rules only ever allow, and there is no deny rule. Anything not granted is refused. That makes least privilege the operating principle: grant the narrowest verbs, on the narrowest resources, in the narrowest scope that lets the subject do its job. A deployment pipeline that only updates Deployments in the shop namespace should hold a Role in that namespace, not a ClusterRoleBinding to cluster-admin, the canonical example of excessive privilege.
Humans are not the only subjects. A ServiceAccount is the identity a Pod uses when its processes call the Kubernetes API. Every namespace has a default ServiceAccount, every Pod runs as one, and by default a token for it is mounted into the containers. Most applications never call the Kubernetes API, so they need no API permissions and no token: setting automountServiceAccountToken: false (on the Pod or the ServiceAccount) means a compromised container finds no API credentials at all. Workloads that do need API access should get a dedicated ServiceAccount bound to a minimal Role.
Secrets vs ConfigMaps: base64 is not encryption
A Secret holds sensitive data such as passwords, API tokens, and TLS keys, while a ConfigMap holds non-sensitive settings; both can be delivered to containers as environment variables or as files in a mounted volume. The difference is protection, not delivery.
| Aspect | ConfigMap | Secret |
|---|---|---|
| Intended contents | Non-sensitive configuration | Credentials, tokens, keys |
| Values in the API object | Plain text | base64-encoded in the data field (an API encoding) |
| Storage in etcd (upstream default) | Unencrypted | Unencrypted |
| Encryption at rest | Not typical | Via an EncryptionConfiguration on the kube-apiserver |
| Delivery to Pods | Env vars or volume files | Env vars or volume files |
base64 is an encoding, not encryption. It only lets binary values travel in the API object; it is neither the protection nor the form in which Secrets are stored. Anyone who can read the Secret object can decode its values with one command, and with upstream API server defaults Secrets sit in etcd unencrypted. Many managed Kubernetes services turn on etcd or envelope encryption for you, so check your provider before assuming either way.
Two controls protect Secrets against two different threats:
- RBAC restricts who can get, list, and watch Secrets through the API. It does nothing against someone who reads etcd directly, such as from a stolen etcd backup or disk.
- Encryption at rest addresses that second threat. You give the kube-apiserver an EncryptionConfiguration that lists a real provider (
aescbc,aesgcm,secretbox, orkmsv2, which uses an external key management service for envelope encryption; upstream documentation recommends kms v2 or secretbox over aescbc) ahead ofidentity, the no-encryption provider. The first provider encrypts new writes; Secrets that already exist stay unencrypted until they are rewritten, for example by replacing every Secret.
Also limit which ServiceAccounts and Pods mount each Secret; external secret managers such as HashiCorp Vault are common at recognition depth. Do not confuse protection with immutable Secrets: immutable: true blocks updates to a Secret (preventing accidental changes and reducing API server watch load) but does not encrypt anything.
Delivery method matters too. Values mounted as volume files are updated in the container when the Secret changes (unless mounted with subPath), while environment variables are fixed at container start and are easier to leak through logs and diagnostic dumps, so volume mounts are generally preferred for sensitive material.
Pod Security Admission: Privileged, Baseline, Restricted
Pod Security Admission (PSA) is the admission controller built into the API server that checks Pods against the three Pod Security Standards. It exists because some Pod specifications are inherently dangerous: privileged containers, host network access, or hostPath mounts can hand a workload control of its node. Its predecessor, PodSecurityPolicy (PSP), proved hard to use. The timeline is a common source of distractors:
| Kubernetes version | Event |
|---|---|
| 1.21 | PodSecurityPolicy deprecated |
| 1.23 | Pod Security Admission beta, enabled by default |
| 1.25 | PodSecurityPolicy removed; Pod Security Admission stable |
| Level | Meaning | Typical use |
|---|---|---|
| Privileged | Unrestricted; allows known privilege escalations | Trusted infrastructure workloads, such as CNI or storage agents |
| Baseline | Minimally restrictive; blocks known escalations like privileged containers, host namespaces and hostPath volumes | Ordinary applications with minimal friction |
| Restricted | Heavily restricted; also requires hardening such as running as non-root, disallowing privilege escalation, dropping all capabilities and a seccomp profile | Security-critical applications, hardened namespaces |
PSA is applied per namespace with pod-security.kubernetes.io/<mode>: <level> labels, in three modes that differ in what happens to a violating Pod:
| Mode | Pod admitted? | User sees a warning? | Recorded in the audit log? |
|---|---|---|---|
| enforce | No, rejected | Yes, as the rejection error | As a rejected request |
| audit | Yes | No | Yes, as an audit annotation (only if API server audit logging is enabled) |
| warn | Yes | Yes, a warning returned to the client | No annotation |
In short, audit is the mode for building a record of violations for later review without affecting anyone, while warn is the mode for giving feedback to the person submitting the workload; neither blocks admission. Each mode takes its own level, so one namespace can combine them, for example enforce: baseline with warn: restricted and audit: restricted: Pods must meet Baseline today, while developers see which ones would fail Restricted. That is the usual migration pattern toward a stricter level. Enforce checks Pods themselves; warn and audit also flag workload objects such as Deployments whose Pod template would violate.
Hardening containers with securityContext
securityContext is where you declare a container's hardened settings; Pod Security Admission only decides whether a Pod with those settings is admitted. It appears at two levels of a Pod spec: Pod-level settings apply to all containers, and container-level settings override them for one container. The recurring fields are the ones the Restricted standard expects:
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault- runAsNonRoot: true: a check, not a choice. When the container starts, the kubelet verifies that it will not run as UID 0; if the image's user is root (or a non-numeric user it cannot verify) and no non-root
runAsUseris set, the container fails to start withCreateContainerConfigError. It never picks a user for you. - runAsUser: chooses the UID the process runs as, overriding the image's user.
- allowPrivilegeEscalation: false: sets the Linux no_new_privs flag, so the running process cannot later gain more privileges than its parent, for example through setuid binaries. It is always effectively true for privileged containers or those with CAP_SYS_ADMIN.
- readOnlyRootFilesystem: makes the container's root filesystem immutable, so an intruder cannot write tools or tamper with binaries; paths the app must write to, such as a cache or temp directory, get an explicit writable volume like emptyDir.
- capabilities: drop ALL: removes Linux kernel capabilities, granting back only the specific ones a workload truly needs;
privileged: trueis the opposite, giving the container almost full access to the host. - seccompProfile RuntimeDefault: filters which system calls the container may make, using the runtime's default profile.
| Field | When it acts | What it stops |
|---|---|---|
| runAsNonRoot | Once, at container start | A container whose process would start as root |
| allowPrivilegeEscalation: false | For the life of the process | A non-root process gaining privileges later, e.g. via setuid |
The two complement each other: a container can start as a non-root user and still escalate through a setuid binary unless escalation is also blocked. With the full set above, a compromised web container is not root, cannot escalate, cannot write to its own filesystem, holds no dangerous kernel capabilities, and is restricted in its system calls: defense in depth at the Container layer of the 4Cs.
NetworkPolicy as a security control
NetworkPolicy is the security control that limits which Pods can talk to which. Kubernetes networking is default-allow: with no policies in place, every Pod can open connections to every other Pod in the cluster. From a security standpoint that means one compromised workload can probe and attack everything else, a pattern called lateral movement. Policies are how you remove that freedom.
A NetworkPolicy is a namespaced object that selects Pods with a podSelector and lists allowed ingress sources and egress destinations by Pod labels, namespace labels, or IP ranges. The pivotal behavior: a Pod selected by no policy accepts all traffic, but once any policy selects it, traffic in the covered directions is denied unless explicitly allowed. Policies are additive allow-lists; like RBAC, there are no deny rules to write. A policy with an empty podSelector selects every Pod in its namespace, which is how teams apply a default-deny baseline and then add narrow allowances per workload.
Scenario: a payment namespace runs a frontend, an api, and a database. Under default-allow, a compromised frontend can connect straight to the database and attempt to dump it. After a default-deny policy plus rules allowing only frontend-to-api and api-to-database traffic, that path no longer exists: the frontend has no allowed route to the database, so the intrusion is contained at the first hop. Two supporting facts complete the picture. NetworkPolicies are enforced only when the cluster's CNI plugin supports them: the API server accepts the objects either way, but a plugin without policy support (such as plain Flannel) silently ignores them, so confirm support before relying on this control. And NetworkPolicy belongs to the Cluster layer of the 4Cs. Note what it does not do: RBAC limits API actions and Pod Security Admission limits Pod specs, but neither stops a compromised Pod opening network connections; only NetworkPolicy limits that lateral movement.
Admission control and supply chain security
Admission control is the API server's checkpoint between authorization and persistence: after a request is authenticated and authorized, admission controllers can still change it or reject it based on policy before it is stored in etcd. The order matters:
- Authentication, then authorization (RBAC). A request RBAC denies is rejected here and never reaches any admission controller or webhook.
- Mutating admission, including mutating webhooks, which can change the object, for example by injecting defaults or a sidecar.
- Validating admission, including validating webhooks and Pod Security Admission, which accept or reject the object as mutated. Validating controllers see the final object, so they can check what mutation produced.
- Persistence to etcd.
Clusters register webhooks to call external policy engines: OPA Gatekeeper and Kyverno use this to enforce custom rules, such as refusing images from unknown registries or requiring resource limits. Admission control is where cluster-wide policy is applied to objects before they are stored.
The supply chain is everything that happens before an image reaches the cluster, spanning the Code and Container layers of the 4Cs:
- Image scanning: tools such as Trivy inspect images for known vulnerabilities (CVEs) in OS packages and application dependencies, ideally in CI before push and continuously in the registry.
- Image signing: Sigstore's cosign attaches a cryptographic signature to an image, proving which pipeline or identity produced it and that it was not altered.
- Signature verification at admission: signing only helps if the cluster checks it. Kyverno image verification rules or the Sigstore policy-controller run as admission webhooks and refuse Pods whose images lack a valid signature from your CI, so an image someone built and pushed by hand cannot run.
- SBOM (Software Bill of Materials): a machine-readable inventory of every component inside an artifact, so when the next major vulnerability lands you can tell which images contain the affected library.
Two common controls do not establish provenance. An imagePullSecret only supplies credentials for pulling from a private registry; it does not restrict which registry or image a Pod references. Pinning an image by digest guarantees you get exactly those bytes every time (immutability), but says nothing about who built them. Round this out with minimal base images, which carry fewer packages and therefore fewer vulnerabilities, and trusted registries. Together these practices ensure that what runs in the cluster is known, verified, and as small as possible.
Tip. KCNA probes security with true-or-false facts, layer classification and short scenarios: expect stems about Secrets being base64-encoded and unencrypted in etcd by default, what RBAC versus encryption at rest protects against, the PodSecurityPolicy timeline (deprecated 1.21, removed 1.25) and its Pod Security Admission replacement, and which of the 4Cs a given control belongs to. Trigger words include least privilege, defense in depth, encryption at rest, Privileged, Baseline, Restricted, enforce, audit, warn, runAsNonRoot, admission webhook, cosign, and SBOM. Scenario questions describe a risky Pod spec, an over-broad RBAC grant, a compliance need such as recording violations without blocking, or an image that skipped CI, and ask which control or setting fits.
- The 4Cs are Cloud, Cluster, Container, Code, outermost to innermost: nested layers of defense in depth where a weakness in an outer layer undermines the inner ones.
- RBAC is additive and allow-only; a RoleBinding can reference a Role in its own namespace or a ClusterRole, granting its rules only in that namespace, while a ClusterRoleBinding grants cluster-wide.
- A ServiceAccount is a Pod's API identity; give workloads dedicated minimal ServiceAccounts and set automountServiceAccountToken: false when the API is not needed.
- Secrets are base64-encoded in the API (an encoding, not protection) and unencrypted in etcd by upstream default; RBAC guards API access, and an EncryptionConfiguration with a real provider before identity guards etcd data.
- PodSecurityPolicy was deprecated in 1.21 and removed in 1.25; Pod Security Admission applies Privileged, Baseline, or Restricted per namespace label in enforce (reject), audit (record only) or warn (message only) mode, and modes can use different levels.
- runAsNonRoot is a start-time check by the kubelet and does not choose a user (runAsUser does); allowPrivilegeEscalation: false blocks later escalation such as setuid.
- Admission runs after authorization: mutating admission first, then validating admission on the mutated object, with webhooks to engines like OPA Gatekeeper and Kyverno.
- Supply chain security means scanning for CVEs, signing images and verifying signatures at admission, keeping SBOMs and using minimal base images; pull secrets and digests do not prove provenance.
- NetworkPolicies turn default-allow networking into allow-lists that block lateral movement, but only when the CNI plugin enforces them.
Frequently asked questions
Are Kubernetes Secrets encrypted by default?
Not with upstream defaults. A Secret's values are base64-encoded in the API object, which is a reversible encoding rather than encryption, and the kube-apiserver stores Secrets in etcd unencrypted unless it is given an EncryptionConfiguration listing a real provider such as aescbc, aesgcm, secretbox or kms v2 ahead of identity. Many managed Kubernetes services enable etcd or envelope encryption for you. RBAC complements this by restricting who can read Secrets through the API, but only encryption at rest protects data read directly from etcd or its backups.
What replaced PodSecurityPolicy in Kubernetes?
Pod Security Admission (PSA) replaced PodSecurityPolicy, which was deprecated in Kubernetes 1.21 and removed in 1.25 (PSA was beta and on by default from 1.23). PSA is an admission controller built into the API server that evaluates Pods against the three Pod Security Standards: Privileged (unrestricted), Baseline (blocks known privilege escalations) and Restricted (requires hardening such as running as non-root). It is configured with namespace labels, each in enforce, audit or warn mode.
What are the 4Cs of cloud native security?
The 4Cs are Cloud, Cluster, Container, and Code: four nested layers running from the infrastructure outward around Kubernetes, the workload images and runtime settings, and finally the application code itself. Each inner layer depends on the security of the layers surrounding it, so weaknesses at an outer layer cannot be fully fixed further in. The model expresses defense in depth: apply appropriate controls at every layer rather than trusting any single one.
What is the difference between a Role and a ClusterRole?
Both grant permissions expressed as verbs on resources, but a Role is namespaced and only grants access within the namespace it lives in, while a ClusterRole is cluster-scoped and can grant access across all namespaces and to cluster-level resources such as nodes. A RoleBinding attaches either kind of role to subjects within one namespace, whereas a ClusterRoleBinding applies a ClusterRole across the whole cluster. Least privilege favors namespaced Roles wherever they suffice.
What is a ServiceAccount used for in Kubernetes?
A ServiceAccount is the identity that a Pod's processes use when calling the Kubernetes API. Every namespace has a default ServiceAccount, and every Pod runs as one, optionally with an API token mounted into its containers. From a security perspective, workloads that never call the API should not have a token mounted (automountServiceAccountToken: false), and workloads that do need API access should use a dedicated ServiceAccount bound to a minimal RBAC Role.
Why use a Secret instead of a ConfigMap?
ConfigMaps are for non-sensitive configuration and are stored as plain text; they are not normally encrypted at rest (an EncryptionConfiguration can cover them, but it is set up with Secrets in mind). Secrets are the designated object for credentials, tokens, and keys: they can be encrypted at rest via the API server's EncryptionConfiguration, their access is conventionally locked down more tightly with RBAC, and Kubernetes features and tooling treat them with extra care. Both deliver data to containers the same ways, as environment variables or mounted files, so the difference is protection, not delivery.
What is the difference between Pod Security Admission audit and warn modes?
Both admit a violating Pod, but they report it to different audiences. Audit mode records the violation as an annotation in the API server's audit log and shows the user nothing. Warn mode returns a warning message to the client, such as kubectl, but adds no audit annotation. Enforce mode is the only one that rejects the Pod. Because each mode takes its own level, a namespace can enforce Baseline while warning and auditing against Restricted.
Does runAsNonRoot make a container run as a non-root user?
No. runAsNonRoot: true is a check: when the container starts, the kubelet refuses to run it if its process would run as UID 0, and the container fails with CreateContainerConfigError. Choosing the user is done by runAsUser in the securityContext or by the USER set in the image. runAsNonRoot also does not stop a non-root process escalating later through setuid binaries; allowPrivilegeEscalation: false does that.
Source
This lesson covers the "Container Orchestration" domain of the official KCNA exam guide. Vendors revise their guides — check the source for the current version.
- KCNA certification page and curriculum — The Linux Foundation
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.