Home/Infrastructure/Hardening Container Images: Build, Scan, Sign, and Runtime Checks
InfrastructureDockerTrivy

Hardening Container Images: Build, Scan, Sign, and Runtime Checks

Container image security must be managed systematically from build through runtime. This practical guide covers Docker image vulnerability scanning, SBOM generation, and runtime security policies.

Hardening Container Images: Build, Scan, Sign, and Runtime Checks
Contents
  1. Why Container Image Security Matters
  2. Hardening the Build Stage
  3. Vulnerability Scanning: Practical Trivy Usage
  4. Image Signing: Cosign + Sigstore
  5. Runtime Security: Falco
  6. Registry Security Checklist
  7. Hardening Roadmap
  8. Vulnerability Scan Results Decision Table
  9. Operations Checklist
  10. Frequently Asked Questions (FAQ)

Why Container Image Security Matters

As container-based infrastructure has become the norm, image security is a core challenge for modern DevSecOps. Vulnerabilities that come from the base image disappear when you switch to a patched base image, so base-image management is an efficient place to start.

The attack surface falls into three main areas:

  • At build time: Vulnerable base images, unnecessary packages, hardcoded secrets
  • In the registry: Unsigned images, inadequate access control
  • At runtime: Excessive privileges, sensitive mounts, abnormal process execution

Hardening the Build Stage

1. Use Minimal Base Images

Dockerfile
# —
FROM ubuntu:22.04

# — distroless
FROM gcr.io/distroless/nodejs20-debian12

# or Alpine
FROM node:20-alpine3.19

Alpine cuts the attack surface dramatically at around 5MB, and distroless does not even include a shell, which minimizes what an attacker can do if they compromise the container.

2. Multi-Stage Builds

Dockerfile
#
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# — tool
FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER nonroot
EXPOSE 3000
CMD ["dist/server.js"]

3. Never Run as Root

Dockerfile
# dedicated User
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

In Kubernetes, you can enforce this with PodSecurityContext:

YAML
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false

Vulnerability Scanning: Practical Trivy Usage

Trivy is currently the most widely used open-source container scanner.

Basic Scanning

Bash
#
trivy image nginx:1.25

# (HIGH, CRITICAL)
trivy image --severity HIGH,CRITICAL nginx:1.25

# JSON (CI )
trivy image --format json --output results.json myapp:latest

# CVE when (false positive )
trivy image --ignorefile .trivyignore myapp:latest

CI/CD Integration (GitHub Actions)

YAML
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.IMAGE_TAG }}
    format: sarif
    output: trivy-results.sarif
    severity: CRITICAL,HIGH
 exit-code: 1 # when failed

- name: Upload Trivy scan results
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif

Generating an SBOM

An SBOM (Software Bill of Materials) inventories every component included in the image:

Bash
# CycloneDX SBOM
trivy image --format cyclonedx --output sbom.json myapp:latest

# SPDX
trivy image --format spdx-json --output sbom.spdx.json myapp:latest

Image Signing: Cosign + Sigstore

Bash
# Cosign
cosign generate-key-pair

#
cosign sign --key cosign.key registry.example.com/myapp:latest

#
cosign verify --key cosign.pub registry.example.com/myapp:latest

# keyless (OIDC , GitHub Actions)
cosign sign --yes registry.example.com/myapp:${{ github.sha }}

To allow only signed images in Kubernetes, use a Kyverno or OPA Gatekeeper policy:

YAML
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  rules:
  - name: check-image
    match:
      any:
      - resources:
          kinds: [Pod]
    verifyImages:
    - imageReferences: ["registry.example.com/*"]
      attestors:
      - count: 1
        entries:
        - keys:
            publicKeys: |- 
              -----BEGIN PUBLIC KEY-----
              ...

Runtime Security: Falco

Falco detects anomalous behavior in the container runtime in real time.

YAML
# falco-rules.yaml — example
- rule: Terminal shell in container
 desc: run
  condition: >
    spawned_process and container and
    proc.name in (shell_binaries)
  output: >
    Shell spawned in container (user=%user.name container=%container.name
    image=%container.image.repository)
  priority: WARNING

- rule: Write below etc
 desc: /etc when
  condition: >
    open_write and container and fd.directory=/etc
  output: File opened for writing below /etc (user=%user.name file=%fd.name)
  priority: ERROR

Registry Security Checklist

ItemRecommended setting
Access controlRBAC + least privilege
Image scanningEnable automatic scanning on push
Signing policyAllow pull of signed images only
Vulnerable imagesAuto-quarantine on CRITICAL findings
Audit loggingRecord all pull/push events
Retention policyAutomatically delete old images

Hardening Roadmap

  1. Apply immediately (1 week): Integrate Trivy into the CI pipeline; fail builds on CRITICAL vulnerabilities
  2. Short term (1 month): Multi-stage builds + migrate to distroless/Alpine; enforce non-root users
  3. Medium term (3 months): Introduce Cosign image signing; apply Kyverno signature-verification policies
  4. Long term (6 months): Falco runtime detection; complete automated SBOM generation and vulnerability tracking

Image security is not a one-time setup. New CVEs are published every day, so the key is building automation that periodically rescans deployed images and keeps base images up to date.

Vulnerability Scan Results Decision Table

When Trivy dumps hundreds of findings, trying to fix everything and ending up fixing nothing is the worst outcome.

SituationAssessmentAction
Critical + a fix is availableRespond immediatelyBump the base image/package version → rebuild and redeploy
Critical but no patch releasedAssess exposureCheck whether the library is actually on the execution path — if unused, register an exception (expiration date required); if in use, document mitigations (e.g., network isolation)
Most vulnerabilities originate from the base imageImprove the structureReplacing with a minimal image (distroless/Alpine) is more effective than patching individually
The same CVE reappears every weekProcess problemNo periodic base-image rebuild pipeline (weekly) — automate it
Judged a false positiveRecord the rationaleRegister it in .trivyignore with comments for reason, reviewer, and expiration date

Operations Checklist

  • Document CI gate criteria — e.g., Critical = block, High = warn then remediate within 7 days
  • Expiration dates on the exception (ignore) list — no permanent exceptions
  • Verify that runtime image tags match scanned image tags (scanning latest is meaningless)
  • Enforce signature verification at admission control — otherwise signing is meaningless

Frequently Asked Questions (FAQ)

Q. How do you verify container image security? A. (1) Vulnerability scanning — inspect OS and library CVEs with Trivy or Grype. (2) Image signature verification — sign and verify with Cosign to prevent tampering. (3) SBOM generation — inventory components. (4) Minimize the base image (distroless/slim) to shrink the attack surface. The key is gating scans in the CI pipeline so that a failed scan blocks deployment.

Q. When should you scan images? A. At all three stages: at build time (CI), after registry registration, and at runtime (periodic rescan). CVEs continue to be disclosed after deployment, so images that are already running must be revalidated periodically.

✦ ✦ ✦
Sources and verification

Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us

This article has no recorded human check (date and scope) yet.

Published · ·Updated ·
Topic-related official docs (auto-linked · not per-claim sources)Docker documentation ↗OWASP documentation ↗CISA SBOM ↗

Comments

Be the first to comment.