Keep the momentum going. Explore more insights to move your business forward.
Containers have changed how software is shipped. But with better portability and faster releases, the required security model has also changed completely.
If you treat containers as lightweight VMs, you’re going to have a hard time. The attack surface in containers is simply different. To build successful container and Kubernetes (K8s) security, you need to develop defenses across different layers.
This post will walk you through safeguarding containers and K8s, from threat detection and image provenance to least-privilege enforcement and policy as code (PaC). Plus, we’ll cover the importance of shift left.
Implementing runtime monitoring
The extended Berkeley Packet Filter (eBPF ) shows you what’s happening inside your containers. With it, you can easily view kernel events, system calls, file access, network connections, and even process execution.
Falco is the most widely adopted tool that leverages eBPF for container runtime security. The way it works is as follows:
- A kernel module captures system calls
- A userspace engine evaluates the system call against set rules
- Alerts fire when something looks suspicious
Out of the box, Falco understands container context, knowing the difference between a shell spawning in a web server container (which, let’s face it, is most likely malicious) versus opening a shell in a debug sidecar (which is probably intentional).
You can also easily write rules like “Alert when a container writes to /etc/passwd” or “Alert when there are privilege changes to a specific directory.”
Detection, however, is just half of the battle. Integrating with SIEM systems like Datadog, Splunk, or Elastic, or even cloud native solutions like AWS Security Hub, gives you the upper hand when dealing with runtime security issues.
By also sending alerts to Slack or PagerDuty, you can trigger automated responses or at least notify the engineers accountable to start resolving a breach.
Image provenance and supply chain security
Most container compromises begin in the supply chain, well before runtime. In 2025, supply chain attacks caused more than $60 billion in damages, underlining the reality that everyone is a target.
There are three primary vectors teams face in supply chains: compromised base images, malicious dependencies, and registry poisoning.
1. Compromised base images
If the upstream image you’re pulling has been hijacked, all images you build will be compromised. As a response, Docker has open-sourced their Docker Hardened Images (DHI) catalog, with over 1,000 images and Helm charts available.
Now, you can actually leverage hardened images from the start.
2. Malicious dependencies
Dependencies can introduce malicious code into your application if they’re compromised. So make sure to use various tools like Snyk or Socket.dev to detect suspicious packages and pin dependency versions.
3. Registry poisoning
Someone might push a malicious image using a legitimate tag. To overcome this, consistently enforce image signing and configure your admission controller to reject any image without a valid signature.
For image provenance and verification, the Sigstore ecosystem enables identity and cryptographic verification via three key components:
- Cosign signs and verifies container images; you sign images after building them, and image consumers will verify their signatures before running them
- Fulcio acts as a certificate authority for keyless signing, issuing short-lived certificates based on an OpenID Connect (OIDC) identity
- Rekor provides an immutable transparency log, helping you identify image signing issues; if an attacker somehow manages to forge a signature, the discrepancy will be visible in the logs
Sigstore should be used in deployment pipelines, with a typical workflow looking like this:
- Your CI builds the image.
- You then generate a software bill of materials (SBOM) to list every component in your image (very helpful for compliance and faster incident response when there’s a new CVE).
- Cosign signs it using keyless signing tied to your identity.
- Both the signature and certificate get recorded in Rekor.
- Your admission controller verifies the signature before allowing the actual deployment of this image.
Least privilege pod security
In any talk, article, or video about security, you always hear about “least privilege.” It’s used absolutely everywhere, and for good reason. For containers, it restricts what system calls a container can make, what kernel capabilities it has, and what files it can actually access.
Kubernetes has three pod security profiles you can implement on this front:
- Privileged: Unrestricted; should be used for system workloads
- Baseline: Minimally restrictive; blocks known privilege escalations
- Restriced: Heavily restricted per security best practices
Additional mechanisms to enforce least privilege for pods include the following.
Seccomp profiles
These limit which syscalls a container can make by filtering them at the kernel level.
Kubernetes supports custom seccomp profiles that allow only the syscalls your application needs, while the default Docker profile blocks around 44 dangerous syscalls. This does, however, make it easy to go further with application-specific profiles.
AppArmor and SELinux
These Linux-specific security modules provide mandatory access control, restricting file access, process interaction, and resource usage. They require some tuning, but the payoff is worth it.
Rootless containers
When you run your container as an unprivileged user, you’re actually reducing the chances of container escapes.
Many organizations simply don’t enforce this because it’s way more convenient to run everything as root. The downside? If an attacker manages to breach an application’s boundaries, they will have permissions to do anything they want in your system.
Dropping dangerous capabilities by default
In Linux, root privileges are split into smaller permissions like CAP_SYS_ADMIN for system changes and CAP_NET_ADMIN for network changes.
Containers receive several of these capabilities by default, which can be dangerous. To be safe, drop all of them, and then add back only those your application needs to run.
You should never grant CAP_SYS_ADMIN or CAP_NET_ADMIN permissions unless they are absolutely necessary.
Policy as code
You can define all the security measures you want, but if you don’t enforce them automatically, they won’t always be respected.
This is where policy as code helps. It replaces your wiki procedures with something that automatically takes action when a policy is violated. There are no exceptions to this, unless you make changes to the policy in your version control system and deploy them.
For containers and K8s, two tools dominate the policy as code space: Kyverno and OPA Gatekeeper.
Kyverno
Kyverno is Kubernetes-native, and its policies are written in YAML, making the learning curve easy for people who are already using K8s. Kyverno policies can block the creation of your non-compliant resources or even ask you to add missing fields.
Here’s an example of a policy that blocks pods trying to create containers with images that are using the latest image tag in Kyverno:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: no-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: no-latest-tag
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Using 'latest' tag is not allowed"
pattern:
spec:
containers:
- image: "!*:latest"
OPA Gatekeeper
OPA Gatekeeper, on the other hand, uses Rego as its policy language. It feels more powerful and flexible, but the Rego language is much harder to learn.
If you’re already using OPA for other kinds of policies (e.g., in conjunction with your IaC, like Terraform or OpenTofu), Gatekeeper might make more sense for consistency.
In OPA Gatekeeper, you need to create a ConstraintTemplate and then a Constraint based on it. Here’s the same example for restricting the latest image tag, but in OPA:
# Constraint Template
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8snoimagelatest
spec:
crd:
spec:
names:
kind: K8sNoImageLatest
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8snoimagelatest
violation[{"msg": msg, "details": {"container": container_name}}] {
container := input.review.object.spec.containers[_]
endswith(container.image, ":latest")
container_name := container.name
msg := sprintf("container %s is using the latest tag", [container_name])
}
violation[{"msg": msg, "details": {"container": container_name}}] {
container := input.review.object.spec.template.spec.containers[_]
endswith(container.image, ":latest")
container_name := container.name
msg := sprintf("container %s is using the latest tag", [container_name])
}
—--
# Constraint[Text Wrapping Break]apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sNoImageLatest
metadata:
name: no-latest-image-tag
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
Shifting Security Left in Your CI/CD Pipelines
The earlier you catch issues, the cheaper they are to fix, both from a cost and an engineering perspective. Best practices for shifting left include the following:
- Use automated image scanning in every pipeline. Tools like Trivy or Snyk scan images for known vulnerabilities before they reach your private registries.
- Leverage Dependabot and Renovate to automatically create pull requests when dependencies have known vulnerabilities. Configure them to check base images, catch issues early, and avoid creating application images that are vulnerable from the start.
- Include PaC in your pipelines before you merge code into the main branch to enforce security from the get-go. This prevents the deployment of compromised resources and ensures compliance.
- Use GitOps tools like ArgoCD or Flux to make your Git repository the only source of truth for what runs inside your Kubernetes clusters. This gives you Git’s built-in audit trail, while also letting you enforce approval workflows through pull requests for every change.
Secure containers from build to runtime
No single control is sufficient. That means implementing defenses everywhere possible. Even if someone manages to push a signed but malicious image and also passes the admission control, runtime monitoring with Falco can still detect anomalies.
If you’re looking to accelerate your container security posture, a managed security platform can help. RapidScale’s partnership with Orca Security is especially beneficial.
Orca Security offers comprehensive cloud-native security coverage, while RapidScale's managed Kubernetes services offload operational burden while keeping security standards tight.
Want to learn more? Send a message to a RapidScale expert today.