Hardened AMIs and container images

Lesson 1 of 6 in Securing Compute Workloads.

There are two places to put a control: in the running instance, or in the image the instance boots from. Everything you install at boot time — the agent, the hardened sshd config, the CIS benchmark remediation, the log forwarder — is a thing that can fail, be skipped, or drift. Everything baked into the image is a property of every instance that ever launches from it, including the one Auto Scaling creates at 4 a.m. during an incident.

So the pattern the exam wants is embed the control in the image. A golden AMI is a versioned, tested, organization-approved image: patched to a known baseline, carrying the SSM Agent and the CloudWatch agent, hardened against a benchmark, stripped of what you do not run, with no credentials anywhere in it. Launch templates and Auto Scaling groups reference it by id, an AWS Config rule or SCP can require approved images, and "is this instance hardened?" becomes "which image version did it boot?" — a question with an answer.

EC2 Image Builder is the service that makes that repeatable rather than aspirational. Four objects: a recipe (a parent image plus an ordered list of components and a semantic version), components (AWSTOE YAML documents describing build and test steps — AWS supplies managed ones, including STIG hardening and agent installation, and you add your own), an infrastructure configuration (the instance type, subnet, instance profile and logging used for the ephemeral build), and a distribution configuration (which Regions and accounts receive the finished image, and which launch templates get updated). A pipeline ties them together and runs on a schedule or when a dependency updates. Image Builder drives the build instance through Systems Manager, which is why the build instance needs an instance profile with SSM permissions — and which is also how the same machinery validates the result before anyone boots it.

The same discipline, two artifact types
ConcernEC2 / AMIContainer image

Baseline

minimal OS, benchmark-hardened (CIS, STIG), agents present, unused services removed

minimal base image — distroless or Alpine — with a multi-stage build so compilers and package managers never reach the final layer

Built by

an Image Builder pipeline, on a schedule and on dependency updates

the same pipeline (Image Builder builds container images too) or your CI, pushing to Amazon ECR

Secrets

none in the image: no keys, no .pem files, no baked-in passwords — identity comes from the instance profile

none in any layer: layers are immutable and inspectable, so a secret added and later deleted is still there. Pull from Secrets Manager or Parameter Store at start-up

What you patch

the image, then redeploy — or Patch Manager for long-lived instances

always the image: rebuild on the new base and roll the service

Provenance

AMI ids are account-scoped; share deliberately and require approved ids in launch templates

immutable tags plus digests so a tag cannot be moved under you, and image signing (AWS Signer, Notation-compatible) verified before deployment

Scanning

Amazon Inspector continuously scans running instances

Inspector scans ECR repositories — on push and continuously as new CVEs appear

Enforced by

AWS Config rules on approved AMI ids, SCPs restricting ec2:RunInstances by image, launch templates teams cannot edit

ECR repository policies, admission control in the cluster, and a pipeline that refuses to deploy an unsigned or critically vulnerable image

Two container specifics are worth saying out loud because they are frequently got wrong.

Layers remember. COPY secrets.env . followed by RUN rm secrets.env leaves the file in the earlier layer, readable by anyone who pulls the image. The same is true of build arguments carrying tokens. The correct shapes are a multi-stage build where the secret only exists in a discarded stage, build-time secret mounts, or — best — no secret at build time at all: the task gets its credentials at runtime through its execution role, Secrets Manager, or Parameter Store.

Runtime posture is part of the image contract. Declare a non-root USER, run with a read-only root filesystem where the application allows it, drop Linux capabilities you do not need, and keep the image free of shells and package managers if nothing requires them — an attacker who achieves code execution in a distroless container has startlingly few tools to hand. On Fargate there is no host for you to harden at all, which moves the entire argument onto the image: AWS patches the platform, you own the layers.

Key terms: Golden AMI, EC2 Image Builder, Immutable infrastructure

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.