Skip to content
DevOps

Container Security: Protecting Docker and Kubernetes in Production

Team ZT21 March 20269 min read

Containers changed how we build and deploy software. They also changed the attack surface. A container is not a security boundary -- it is a process isolation mechanism built on shared kernel features. When that distinction is unclear, organizations deploy containers with the same blind trust they once gave to virtual machines, and attackers take advantage.

At Zindagi Technologies, we secure containerized environments for organizations running everything from small Docker Compose deployments to large-scale Kubernetes clusters handling production workloads. This guide covers the security controls that matter at each stage of the container lifecycle.

The Container Threat Model

Understanding what can go wrong is the first step to preventing it.

Image-Level Threats

  • Vulnerable base images with known CVEs
  • Malicious images from untrusted registries
  • Embedded secrets (API keys, passwords) in image layers
  • Bloated images with unnecessary packages expanding the attack surface

Build-Level Threats

  • Compromised CI/CD pipelines injecting malicious code
  • Dependency confusion attacks pulling malicious packages
  • Build systems with excessive privileges

Runtime Threats

  • Container escape to the host via kernel exploits
  • Privilege escalation within the container
  • Cryptomining and resource abuse
  • Lateral movement between containers via unrestricted networking

Orchestration Threats (Kubernetes-specific)

  • Exposed Kubernetes API server
  • Overprivileged service accounts
  • etcd data exposure
  • Misconfigured RBAC allowing unauthorized operations

Securing the Image: Your Foundation

Security starts before a container runs. It starts when the image is built.

Use Minimal Base Images

Every package in your base image is a potential vulnerability. The difference is dramatic:

  • Ubuntu 22.04 base image: ~77MB, hundreds of packages, regular CVE disclosures
  • Alpine Linux: ~5MB, minimal package set, smaller attack surface
  • Distroless (Google): ~2MB, no shell, no package manager, no debugging tools
  • Scratch: 0MB, literally empty -- for statically compiled binaries only

For production, we recommend distroless images for most workloads. The lack of a shell means that even if an attacker gains code execution, they cannot easily interact with the container.

Multi-Stage Builds

Separate your build environment from your runtime environment. Build dependencies (compilers, testing tools, development packages) should never exist in the production image:

# Build stage
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /server

# Production stage
FROM gcr.io/distroless/static-debian12
COPY --from=builder /server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]

Scan Images in CI/CD

Integrate image scanning into every build pipeline. Block builds that contain critical vulnerabilities:

  • Trivy: Open-source, fast, scans images/filesystems/Git repos, excellent coverage
  • Grype: Open-source from Anchore, good vulnerability matching
  • Snyk Container: Commercial with free tier, integrates with development workflows

Configure scanning to fail builds on HIGH and CRITICAL severity CVEs. MEDIUM severabilities should be reported but not necessarily block deployment -- use risk-based judgment.

Never Use :latest in Production

The latest tag is mutable. It points to whatever was most recently pushed. In production, always use immutable references:

  • Image digests: myapp@sha256:abc123... (immutable, guaranteed)
  • Semantic version tags: myapp:v2.1.3 (conventional, but technically mutable)

Pin to digests in production deployments. Use tags for human readability in documentation.

Sign Your Images

Image signing proves that the image was built by your CI/CD pipeline and has not been tampered with. Cosign (from Sigstore) is the standard tool:

# Sign during CI/CD build
cosign sign --key cosign.key myregistry/myapp@sha256:abc123

# Verify before deployment (via admission controller)
cosign verify --key cosign.pub myregistry/myapp@sha256:abc123

Deploy an admission controller (Kyverno, OPA Gatekeeper) to reject unsigned images at deployment time.

Securing the Runtime

Even with perfect images, runtime security matters. Containers share the host kernel, and misconfigurations create exploitable conditions.

Run as Non-Root

Running containers as root is the single most common and most dangerous misconfiguration. A container running as root has capabilities that facilitate container escape:

# In your Dockerfile
RUN adduser --disabled-password --gecos "" appuser
USER appuser

In Kubernetes, enforce this via security context:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 1000

Drop All Capabilities

Linux capabilities grant fine-grained privileges. Containers get a default set that includes capabilities most applications never need:

securityContext:
  capabilities:
    drop:
      - ALL
    add:
      - NET_BIND_SERVICE  # Only if your app needs to bind to ports below 1024

Read-Only Root Filesystem

Prevent runtime filesystem modifications. If an attacker gains code execution, they cannot write malicious binaries or modify configuration:

securityContext:
  readOnlyRootFilesystem: true

If your application needs to write temporary files, mount a specific writable volume for that purpose. Do not make the entire filesystem writable.

Seccomp and AppArmor Profiles

Seccomp restricts which system calls a container can make. The default Docker seccomp profile blocks approximately 44 of the 300+ available syscalls. For high-security workloads, create custom profiles that allow only the specific syscalls your application needs.

AppArmor (on Ubuntu/Debian) provides mandatory access control. Use it to restrict file access, network access, and capability usage beyond what Linux capabilities control.

Resource Limits

Without resource limits, a compromised container can consume all host resources (CPU, memory, disk I/O), affecting other containers and the host itself:

resources:
  limits:
    cpu: "500m"
    memory: "256Mi"
    ephemeral-storage: "1Gi"
  requests:
    cpu: "100m"
    memory: "128Mi"

This also prevents cryptomining -- a common post-exploitation activity in cloud and container environments.

Network Security

By default, all containers on the same Docker network or Kubernetes cluster can communicate freely. This enables lateral movement.

Network Policies in Kubernetes

Implement default-deny network policies and explicitly allow only required communication paths. This is micro-segmentation for containers.

Service Mesh for mTLS

Deploy a service mesh (Istio, Linkerd) to encrypt all inter-service communication with mutual TLS. This prevents eavesdropping and ensures that only authenticated services can communicate.

Egress Filtering

Restrict outbound traffic from containers. A compromised container should not be able to reach the internet to download additional tools or exfiltrate data. Use network policies or a service mesh to whitelist allowed egress destinations.

Registry Security

Your container registry is part of your supply chain.

  • Use a private registry: Harbor (open-source), AWS ECR, Azure ACR, or Google Artifact Registry. Never pull production images from Docker Hub.
  • Enable vulnerability scanning in the registry: Most registries support automatic scanning of pushed images.
  • Implement access controls: Restrict who can push images. Only CI/CD pipelines should push to production repositories.
  • Enable content trust: Docker Content Trust or Notary ensures image integrity.
  • Garbage collection: Regularly clean up unused images to reduce storage costs and attack surface.

Secrets Management

Containers need credentials -- database passwords, API keys, certificates. How you provide them matters.

Never Embed Secrets in Images

Secrets baked into images are visible to anyone who pulls the image. They persist in image layer history even if deleted in a later layer.

Use Dedicated Secrets Management

  • HashiCorp Vault with CSI driver for Kubernetes
  • AWS Secrets Manager with External Secrets Operator
  • Azure Key Vault with CSI driver
  • Sealed Secrets for GitOps workflows

Rotate Secrets Regularly

Automate secret rotation. Short-lived, dynamically generated credentials (Vault dynamic secrets) are ideal -- they expire automatically, limiting the window of exploitation if compromised.

Runtime Threat Detection

Prevention controls reduce risk. Detection controls catch what prevention misses.

Deploy Runtime Security Monitoring

Tools like Falco, Sysdig Secure, or Aqua Runtime Protection monitor container behavior at the system call level. They detect anomalies such as:

  • Shell execution in a container that should never spawn shells
  • Network connections to unexpected destinations
  • File system modifications in read-only containers
  • Privilege escalation attempts
  • Suspicious process execution patterns

Falco is open-source and provides a strong foundation. Commercial tools add response automation and centralized management.

Log Everything

Centralize container logs in a log aggregation platform:

  • Application logs (stdout/stderr from containers)
  • Container runtime logs (Docker/containerd)
  • Kubernetes audit logs (API server activity)
  • Network flow logs (from CNI or service mesh)

Correlate these with your SIEM for comprehensive visibility.

The Container Security Checklist

Prioritized for maximum impact:

  • Use minimal base images (distroless or Alpine)
  • Scan images in CI/CD and block vulnerable deployments
  • Run containers as non-root with dropped capabilities
  • Implement network policies with default deny
  • Use a private registry with access controls
  • Set resource limits on all containers
  • Enable read-only root filesystem
  • Manage secrets through dedicated tools (never in images)
  • Sign images and enforce signature verification
  • Deploy runtime security monitoring
  • Encrypt inter-service traffic (mTLS via service mesh)
  • Centralize and monitor container logs

Moving Forward

Container security is not a one-time setup. It requires integration into your development workflows, CI/CD pipelines, and operational processes. The organizations that get it right treat container security as a shared responsibility between development, operations, and security teams -- not as a gate that slows down deployment.

At Zindagi Technologies, our DevSecOps team helps organizations secure their container infrastructure from build through production. Whether you are containerizing your first application or hardening an existing Kubernetes platform, we bring the depth of experience to protect your workloads without slowing your delivery velocity.

Ready to build your cyber resilience?

Contact our team to discuss your cybersecurity requirements.