Lesson  in  Managing Kubernetes with Rancher

Security and Access Control

Enforce a Pod Security Standard on a namespace and watch Kubernetes reject an unsafe workload.

Enforcing Pod Security

A cluster is only as safe as the workloads it lets run. By default, Kubernetes will happily schedule a container that runs as root, mounts the host filesystem, or asks for privileged access - exactly the things an attacker wants. This lesson closes that door with Pod Security Admission, a control built into the Kubernetes API server that rejects unsafe pods before they ever start. You drive it from the dev-machine workstation.

A pod creation request passing through the API server's Pod Security Admission controller, which checks the target namespace's enforce level and admits a compliant pod while rejecting a privileged one

Pod Security Admission checks every pod against its namespace's standard and rejects the ones that violate it.

The Three Pod Security Standards

Kubernetes defines three built-in Pod Security Standards, from most to least permissive:

  • privileged - no restrictions; anything goes. This is the default when you set nothing.
  • baseline - blocks the most dangerous settings (privileged containers, host namespaces) while staying easy to adopt.
  • restricted - the hardened profile: containers must run as non-root, drop all Linux capabilities, disallow privilege escalation, and use a seccomp profile.

You apply a standard to a namespace with a label, and the API server enforces it on every pod created there. This is namespace-scoped by design: you can run system components under a loose policy while holding your application namespaces to restricted.

Pod Security Admission vs the old PodSecurityPolicy

If you have seen PodSecurityPolicy (PSP) in older material, note that it was removed in Kubernetes 1.25. Pod Security Admission is its built-in replacement: simpler (three fixed standards instead of hand-written policies), namespace-labelled, and always compiled into the API server so there is nothing to install. For rules beyond the three standards - say, "every image must come from our registry" - you reach for a policy engine like OPA Gatekeeper or Kubewarden, which the wider-security unit covers.

Step 1: Create a Namespace for Your Apps

Create a namespace to hold application workloads. From the dev-machine terminal:

kubectl create namespace secure-apps

At this point the namespace has no policy - it is effectively privileged, so an unsafe pod would run without complaint.

Step 2: Enforce the Restricted Standard

Label the namespace so the API server enforces the restricted standard on every pod created in it:

kubectl label namespace secure-apps \
  pod-security.kubernetes.io/enforce=restricted

That single label is the whole control. Confirm it:

kubectl get namespace secure-apps --show-labels
enforce, audit, and warn - three ways to apply a standard

Each standard can be attached at three levels, and you can mix them:

  • pod-security.kubernetes.io/enforce - rejects violating pods outright. This is the one with teeth.
  • pod-security.kubernetes.io/warn - allows the pod but returns a warning to the user, useful for flagging issues without breaking anything.
  • pod-security.kubernetes.io/audit - allows the pod and records a violation in the audit log, for after-the-fact review.

A common rollout pattern is to set warn and audit to restricted first, watch what would break, fix it, and only then switch enforce to restricted. Here we go straight to enforce because the point is to see the rejection.

Step 3: Watch an Unsafe Pod Get Rejected

Now try to run a privileged container in that namespace - the kind of workload the restricted standard exists to stop:

kubectl -n secure-apps run rogue --image=nginx:1.27 \
  --overrides='{"spec":{"containers":[{"name":"rogue","image":"nginx:1.27","securityContext":{"privileged":true}}]}}'

Instead of scheduling, the API server rejects the request with a Pod Security violation, listing exactly which rules the pod broke (privileged, allowPrivilegeEscalation, missing runAsNonRoot, capabilities, and seccomp). Nothing is created - the unsafe workload never runs.

Contrast that with a compliant pod, which the same namespace accepts:

kubectl -n secure-apps run safe --image=nginx:1.27 \
  --overrides='{"spec":{"containers":[{"name":"safe","image":"nginx:1.27","securityContext":{"runAsNonRoot":true,"runAsUser":1000,"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"seccompProfile":{"type":"RuntimeDefault"}}}]}}'
Setting Pod Security levels from the Rancher UI

Everything you just did with kubectl is also available in the Rancher UI. When you create or edit a namespace (or a Project), Rancher exposes the Pod Security Admission level as a dropdown, so you can set enforce, warn, and audit to privileged, baseline, or restricted without touching labels by hand. Rancher also ships built-in Pod Security Admission Configuration Templates that you can apply cluster-wide, so new namespaces inherit a secure default. The underlying mechanism is identical - Rancher is writing the same namespace labels the API server reads.

You're Done

You enforced the restricted Pod Security Standard on a namespace and watched the API server reject a privileged pod before it could run - a real, built-in security control with no extra components. That is the foundation of workload security in Kubernetes: decide what "safe" means per namespace, and let the API server enforce it on every pod.

Pod security is one layer. The next unit surveys the rest of the security surface on a Rancher-managed cluster - authentication, compliance scanning, network policy, and secrets - and where each fits.

The challenge below asks you to harden a namespace yourself and prove an unsafe pod is turned away. Solving it records your progress.

The Wider Security Surface

Pod security is one control among several. Hardening a Rancher-managed cluster in production means layering authentication, compliance scanning, network segmentation, and secrets management on top. This unit surveys those layers and where each fits - it is a map of the territory rather than a hands-on walk, because most of these controls depend on external systems (an identity provider, a policy-enforcing CNI, a secrets backend) that a single throwaway cluster does not have.

Authentication: Who Gets In

Out of the box Rancher uses a local admin account, which is fine for a lab but not for a team. In production, Rancher integrates with an external identity provider so people log in with credentials they already have, and Rancher maps their group membership to roles automatically:

  • LDAP / Active Directory - the classic enterprise directory.
  • SAML (Okta, ADFS, Ping Identity, Keycloak) - single sign-on for web access.
  • GitHub / GitLab - OAuth login, popular with engineering teams.
  • OpenID Connect - standards-based federation for anything OIDC-capable.
Why this is not hands-on here

Every one of these requires a second system to authenticate against - a running LDAP directory, a configured Okta tenant, a GitHub OAuth app. A disposable single-cluster playground has none of them, and standing one up would teach you about the identity provider, not about Rancher. The Rancher side is straightforward once an IdP exists: Users & Authentication > Auth Provider, pick the type, enter the endpoint and credentials, and map groups to Rancher roles. The skill that transfers is the concept - authenticate against an external directory, authorize by group - not the specific provider setup.

A user logging in through an external identity provider such as LDAP, SAML, GitHub, or OIDC, which Rancher trusts to verify the user, then Rancher maps the user's provider groups to Rancher roles that grant access to clusters and projects

Rancher delegates login to an external identity provider, then maps the user's groups to Rancher roles.

Compliance: CIS Benchmark Scanning

Rancher ships a CIS Benchmark scanning tool that audits a cluster against the Center for Internet Security's Kubernetes Benchmark - a published checklist of hardening controls. You install it as a Rancher app, run a scan, and get a report of which controls pass, fail, or need manual review. Scans can be scheduled so you track compliance drift over time. It is the fastest way to answer "how hardened is this cluster against a recognized standard?" without auditing by hand.

The Rancher CIS Benchmark scanner reading a cluster and the CIS Kubernetes Benchmark checklist, then producing a report that groups controls into pass, fail, and manual-review, with a scheduler that reruns the scan to track compliance drift

The CIS Benchmark scanner audits the cluster against a published checklist and reports which controls pass, fail, or need a manual check.

Network Policy: Segmenting Traffic

By default, every pod in a Kubernetes cluster can talk to every other pod. NetworkPolicy objects let you lock that down - default-deny all traffic, then allow only the connections each workload actually needs, isolating namespaces and restricting egress to known endpoints.

Important

There is an important catch: NetworkPolicy objects are only enforced if the cluster's CNI plugin supports them. This playground's K3s uses the default Flannel CNI, which does not enforce NetworkPolicy - you could create the objects, but nothing would honor them. Production clusters that rely on network policy run a policy-enforcing CNI such as Calico, Cilium, or Canal. That is why network segmentation is described here rather than demonstrated: teaching a control that silently does nothing would be worse than not teaching it.

Two states of pod-to-pod traffic in a namespace. On the left, with no policy, every pod can reach every other pod. On the right, a default-deny NetworkPolicy blocks all traffic and an allow rule permits only the frontend pod to reach the backend pod, while a note warns that a policy-enforcing CNI such as Calico or Cilium is required for the rules to take effect

NetworkPolicy turns an open namespace into default-deny plus explicit allow rules - but only a policy-enforcing CNI actually applies them.

Secrets Management

Kubernetes Secrets are only base64-encoded in etcd by default - encoded, not encrypted. Hardening secrets means one or more of:

  • Encryption at rest for etcd, so a stolen etcd snapshot does not leak secrets.
  • External secret stores - HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault - that hold the real secret and inject it at runtime, so it never lives in the cluster datastore.
  • Sealed Secrets - encrypt a secret so the ciphertext is safe to commit to Git, which fits the GitOps workflow from the Fleet lessons.
A contrast between the default state where a Kubernetes Secret is only base64-encoded in etcd and anyone with access can decode it back to plaintext, versus three hardening options - encryption at rest for etcd, an external secret store such as HashiCorp Vault that injects the secret at runtime, and Sealed Secrets that encrypt the value so the ciphertext is safe to store in Git

Base64 is encoding, not encryption - a Secret in etcd is trivially decoded unless you add encryption at rest, an external store, or Sealed Secrets.

Putting It Together

A hardened Rancher-managed cluster layers these controls: authenticate people against a real identity provider, authorize them by role, enforce Pod Security Standards on workloads (which you did hands-on in the previous unit), segment traffic with a policy-enforcing CNI, scan against the CIS Benchmark to catch drift, and keep secrets out of plain etcd. No single control is sufficient alone; security is the sum of the layers.

Next lesson
Day-2 Operations