Skip to main content

Hardening and Execution Restrictions in Containers

🌟 Objective​

Strengthen the security of container execution through reduction of the attack surface, removal of unnecessary components and explicit restriction of runtime permissions, ensuring that each container runs only what is necessary, with the least privilege possible.

In the modern SSDLC, hardening cannot be treated as a declared intention.
It is necessary to ensure that the restrictions defined are effectively applied and verifiable at execution time.

These measures complement the choice of secure images and the validation of their provenance, reducing the impact of exploitable vulnerabilities, configuration errors and runtime abuse.


🧬 What container hardening means​

Container hardening consists of the systematic application of measures that:

  • Remove unnecessary binaries and packages (e.g. curl, bash, ping);
  • Prevent execution as root;
  • Restrict kernel capabilities to what is strictly necessary;
  • Limit syscalls and interactions with the host (seccomp, AppArmor, SELinux);
  • Apply immutability to the filesystem;
  • Block dangerous interfaces such as /proc, /sys or the Docker socket.

🧱 A container can only be considered hardened if the effective runtime state matches what was defined in the build and in the manifests.


⚠️ Configured hardening ≠ effective hardening​

One of the most common errors in automated environments is to assume that:

  • a well-written Dockerfile,
  • a defined securityContext,
  • or an applied policy,

automatically guarantee a secure runtime.

In practice, there are three distinct states:

  1. Declared hardening
    What is defined in code or configuration.
  2. Applied hardening
    What the platform actually attempts to apply.
  3. Effective hardening
    What can be empirically observed in the running container.

This chapter explicitly treats hardening as a verifiable state, not as an assumed property.


📘 Examples of hardening practices​

PracticeTechnique / MechanismOperational observations
Non-root userUSER in the DockerfileMust be validated at runtime
Read-only FSreadOnlyRootFilesystem: true (K8s)Requires verification of mounts
Minimal capabilitiesdrop: ALL + explicit addAvoid permissive defaults
Seccompruntime/default or custom profileConfirm the active profile
AppArmor / SELinuxRestrictive profilesDepends on host support
Unprivileged executionNo --privileged, no Docker socketMust be blocked by policy

🛠️ How to apply and verify at build and runtime​

1️⃣ At build (image)​

  • Define a non-root user (USER);
  • Avoid installing interactive tools;
  • Minimise ENTRYPOINT and CMD;
  • Add security and context labels;
  • Validate the Dockerfile with linters (e.g. Hadolint).

The build defines intentions; it does not guarantee application.


2️⃣ At deploy (orchestration / CI/CD)​

  • Define an explicit securityContext:
    • runAsNonRoot: true
    • readOnlyRootFilesystem: true
    • allowPrivilegeEscalation: false
    • capabilities: drop: ["ALL"]
  • Apply Pod Security Standards / PSA;
  • Enforce through OPA or Kyverno (see 05-policies-runtime-opa.md).

Technical enforcement blocks insecure configurations, but does not validate the real state.


3️⃣ At runtime (effective verification)​

  • Verify the effective user (id, /proc/self/status);
  • Validate mounts and permissions;
  • Confirm that the seccomp/AppArmor profile is active;
  • Detect deviations (drift) from the expected configuration;
  • Record results for audit.

Without this verification, hardening is merely assumed, not demonstrated.


📂 Where to configure and control​

  • Dockerfile: minimal and reproducible baseline;
  • Manifests / Helm charts: declarative definition of restrictions;
  • CI/CD: automatic validation of insecure configurations;
  • Admission Controllers: centralised enforcement;
  • Runtime monitoring: empirical confirmation of the state.

Each layer reduces the likelihood of error, but none is sufficient on its own.


✅ Good practices​

  • Define hardening profiles by workload type;
  • Prohibit privileged containers by policy;
  • Validate hardening after deploy, not only before;
  • Treat exceptions as formal and temporary decisions;
  • Review hardening after platform or kernel changes;
  • Integrate continuous runtime verification in critical environments.

📎 Cross-references​

DocumentRelation to hardening
01-imagens-base.mdMinimisation of the surface in the image
02-runners-isolamento.mdIsolation of the execution environment
05-policies-runtime-opa.mdTechnical enforcement of restrictions
08-kubernetes-execucao.mdPractical application in clusters
09-riscos-processo-imagens.mdDifference between intention and effective state

🔐 Hardening only exists when it can be observed, reproduced and audited.
Configuration without verification is merely assumption.