A golden image copies every setting, account, key and secret it contains onto every server built from it, so one mistake in the image becomes a mistake on thousands of hosts. Done well, a hardened image is the cheapest security control an infrastructure team has: every new server starts compliant, patched and free of build-time credentials. Done badly, it spreads shared SSH host keys, duplicate machine identities, default passwords and embedded cloud keys across the estate. This guide covers how to build server images through a pipeline, how to generalise Windows and Linux images so each host gets its own identity, how to keep secrets out, and how to test, distribute, retire and monitor images. It is for platform, infrastructure and cloud engineers who own image builds, and for security teams that set the baseline. Container images are covered separately in the Container Image Security Guide.
Key takeaways
- Build images in a pipeline, never by hand: a recipe in version control, automated hardening, automated tests and a record of what went into each version.
- Generalise every image so each host creates its own SID, machine ID and SSH host keys on first boot; skipping this step quietly gives many servers the same identity.
- No secrets in the image: credentials, tokens and private keys are injected at first boot from a secrets manager or the platform's workload identity, never baked in.
- Harden to a named baseline (CIS Benchmark or DISA STIG) during the build and scan the result, so compliance is a property of the image rather than a later project.
- Images expire: rebuild on a fixed cadence and on critical vulnerabilities, deprecate old versions, and block launches from images that are no longer approved.
Why golden images are a security control and a risk
An image is a template of an operating system disk plus agents and configuration, captured and reused to create servers. The same template feeds virtual machines, cloud instances (AMIs, Azure Compute Gallery images, Google Compute Engine images) and bare-metal builds. Three properties make it both valuable and dangerous:
- Multiplication: a setting in the image applies to every host built from it. A disabled audit policy or an open management port is replicated, and so is a fix.
- Longevity: instances launched from an image months ago still carry that image's patch level and agents unless they are updated or replaced.
- Trust: deployment tooling trusts whatever image ID it is given. An attacker who can modify or substitute an image gains persistence in every future build; MITRE ATT&CK tracks this as T1525 Implant Internal Image.
Two operating models exist. In a mutable model, servers are built from the image and then patched in place. In an immutable model, servers are never patched in place: a new image is built and instances are replaced. Immutable estates get the full benefit of image hardening, because drift has little time to accumulate. Mutable estates still benefit, but need configuration management to hold the baseline after build. See the Configuration Drift and Baseline Enforcement Guide.
The image pipeline
Whatever tool you use (HashiCorp Packer, RHEL image builder, EC2 Image Builder, Azure VM Image Builder or a hypervisor template workflow), the stages are the same. EC2 Image Builder, for example, separates build, validate, test and distribution stages, and only distributes an image if all test components pass.
| Stage | What happens | Security controls | Evidence to keep |
|---|---|---|---|
| Source | Pick a base image from the vendor or a trusted internal parent | Use vendor-published images by verified owner ID; verify checksums and signatures of ISOs | Base image ID, version and checksum |
| Recipe | Recipe or template in version control | Code review, branch protection, signed commits for the recipe repository | Commit hash per image version |
| Build | Temporary build instance applies packages, agents and hardening | Isolated build network; build role with least privilege; packages from signed repositories only | Package manifest and build log |
| Harden | Apply CIS or STIG baseline and organisation settings | Automated profile (for example OpenSCAP or vendor STIG components) rather than manual steps | Compliance report per image |
| Generalise | Remove machine-specific identity and build traces | Sysprep, cloud-init clean, machine-id reset, host key removal, log and history clean-up | Clean-up script version |
| Test | Launch a test instance from the new image | Vulnerability scan, secret scan of the disk, baseline scan, boot and agent health tests | Scan results attached to the version |
| Distribute | Copy to regions, accounts or clusters | Encrypt with a managed key, share only to named accounts, block public sharing | Distribution list and key ID |
| Retire | Deprecate and delete old versions | Expiry dates, launch restrictions on unapproved images | Image lifecycle record |
- The build role is a privileged identity: it can create images that everyone else trusts. Give it a dedicated role (on AWS, the IAM role on the instance profile Image Builder uses for build instances), restrict who can change recipes and pipelines, and log its activity. See the CI/CD Pipeline Identity Security Guide for pipeline identity patterns.
- Treat base images as supply chain inputs: pull from the operating system vendor or your own approved parent images, and record provenance so you can answer which hosts were built from a given base. The Linux Package Signing and Repository Trust Guide covers the package side.
Generalising: giving every host its own identity
A captured disk contains identifiers that must be unique per machine. If they are not reset, cloned hosts share identity, which breaks authentication assumptions, confuses logging and lets one host's keys impersonate another.
| Identifier | Platform | How to reset in the image | What goes wrong if you do not |
|---|---|---|---|
| Computer SID and device state | Windows | sysprep.exe /generalize /shutdown /oobe; use /mode:vm only for VHDs redeployed on the same hypervisor | Microsoft states generalisation is required even for identical hardware so the image can be reused safely |
/etc/machine-id | Linux with systemd | Leave the file empty or missing, or run cloud-init clean --machine-id, which writes uninitialized so the next boot is treated as a first boot | Duplicate IDs across hosts; systemd's documentation also says the ID should be treated as confidential |
| SSH host keys | Linux and Unix | Delete /etc/ssh/ssh_host_*key* before capture; cloud-init's ssh_deletekeys defaults to true and regenerates rsa, ecdsa and ed25519 keys (ed25519 is dropped in FIPS mode) | Every server presents the same host key, so one stolen key enables impersonation of all of them |
| cloud-init state | Linux cloud images | cloud-init clean --logs --seed, with --configs to remove generated SSH, network and fstab configuration | Instances skip first-boot set-up or inherit the build instance's datasource configuration |
| Azure agent provisioning data | Linux on Azure | waagent -deprovision+user, then deallocate and mark generalised | Build user account and provisioning data remain in the image |
| Agent registrations | All | Remove EDR, monitoring and configuration management agent IDs using each vendor's golden image procedure | Hosts report as one device, breaking detection and inventory |
Microsoft's Azure guidance warns that waagent -deprovision+user does not guarantee the image is free of sensitive information. Generalisation tools reset identity; they do not search for secrets. That needs a separate step.
Keeping secrets and credentials out of images
Every credential in an image is a credential shared by every host and readable by anyone who can obtain a copy of the image or a snapshot of a disk built from it. The Docker Hub secrets leak of 2025 showed how ordinary build habits (environment files, credentials in build scripts, whole project directories copied in) put more than 10,000 public images' worth of keys on the internet, and the same habits apply to VM images.
- Answer files: Windows unattend files often contain administrator, autologon and domain join passwords. Microsoft's "Hide Sensitive Data" option in Windows SIM only hides local account passwords, states that it does not provide encryption, and notes that domain passwords and product keys may remain in clear text. Keep answer files out of the captured image and treat them as secrets.
- Build credentials: repository tokens, artefact store keys and domain join accounts used during the build must be removed before capture, and should be short-lived so a missed copy expires.
- Cloud credentials: never place access keys in the image. Use the platform's workload identity (instance profiles, managed identities, service accounts) so credentials are issued at runtime. See the Workload Identity Federation Guide.
- Local administrator passwords: a shared local admin password in a Windows image is a lateral movement path across every server. Use Windows LAPS so each host gets a unique, rotated password (MITRE ATT&CK T1078.003 Local Accounts). See the Local Administrator Password Solution (LAPS) Guide.
- Default accounts: remove or lock vendor default accounts and default passwords on appliances and applications included in the image (T1078.001 Default Accounts).
- Shell history, logs and caches: clean
~/.bash_history, package manager caches, cloud-init logs (cloud-init clean --logs) and temporary build directories. cloud-init's log collection includes user-data, so treat build logs as sensitive. - Scan before release: run a secret scanner against the mounted image filesystem as a pipeline test, and fail the build on any finding. See the Secrets in Container Images and Build Artefacts Guide.
Hardening the image to a baseline
- Pick one named baseline per operating system: CIS Benchmarks (Level 1 for general servers, Level 2 where stricter controls are acceptable) or DISA STIGs for regulated environments. AWS publishes Amazon-managed STIG hardening components for Image Builder, and RHEL can apply SCAP profiles. See the CIS Benchmarks Guide and the OpenSCAP and Linux Compliance Scanning Guide.
- Record exceptions in the recipe: where a control breaks a workload, disable it in code with a comment and a ticket, so the exception is visible and reviewable.
- Minimise: install only the packages the server role needs. Separate images per role (web, database, domain controller) are easier to harden than one universal image with everything.
- Include security tooling, configured but unregistered: EDR, log forwarder, vulnerability agent and configuration management agent, with registration happening at first boot.
- Logging defaults: enable auditd or Windows advanced audit policy in the image so evidence exists from the first boot. See the Linux Logging and auditd for Defenders Guide.
- Disk encryption: enable platform encryption for cloud volumes at distribution (for example a KMS key on AMI copies). Note that Azure's Sysprep process requires fully decrypted drives, so encryption is applied to instances after generalisation rather than inside the source VM.
Distribution, access and lifecycle
| Decision | Option A | Option B | Choose A when |
|---|---|---|---|
| Patching model | Rebuild and replace (immutable) | Patch in place (mutable) | Workloads are stateless or behind load balancers; B for stateful legacy servers |
| Image scope | One image per role | One base image plus configuration management | Roles are few and stable; B for large, varied estates |
| Rebuild cadence | Monthly scheduled build | Build on every upstream patch | Change control is heavy; B where pipelines and tests are fully automated |
| Sharing | Share to named accounts or projects | Organisation-wide sharing | Almost always A; B only with central guardrails |
- Block public sharing: AWS's block public access setting for AMIs is per Region, is enabled for new accounts, and does not make already-public AMIs private. Check for existing public images, and manage the setting through an AWS Organizations declarative policy so accounts cannot switch it off.
- Restrict which images can launch: use organisation policies or admission rules so production can only launch images from the approved image account or gallery and within their validity period.
- Expire images: give every version an end date, deprecate superseded versions, and report on running instances built from deprecated images.
- Small organisations: a single monthly rebuild of two or three images with a scripted Sysprep or cloud-init clean step gets most of the value. Large organisations need a central image factory, signed provenance and enforcement of approved images in every account.
Implementing an image factory
- Inventory current images: list every template, AMI and gallery image, who owns it, when it was built and how many hosts use it. Many estates find images several years old still in use.
- Write the first recipe: start with the most-used server image, codify the build, and add generalisation and secret clean-up steps.
- Add hardening in audit mode: run the baseline scan against the image and fix findings in the recipe before enforcing.
- Add test gates: vulnerability scan, secret scan, baseline score threshold and boot health; the pipeline must fail closed.
- Pilot: deploy the new image to a non-production ring and one low-risk production service, then compare monitoring, agent registration and application behaviour with the old image.
- Roll out and keep rollback: keep the previous approved version available for a defined period so a faulty image can be withdrawn by switching launch templates back.
- Enforce: restrict launches to approved images and start retiring old ones.
Detection and measurement
- Image changes: alert on image creation, sharing changes and launch permission changes outside the pipeline's identity (cloud audit logs such as AWS CloudTrail or Azure Activity Log).
- Duplicate identities: look for identical SSH host key fingerprints, identical machine IDs or duplicate EDR device IDs across hosts; these indicate an image that was not generalised.
- Unapproved images: report instances launched from images outside the approved catalogue.
Metrics: median age of the image behind running production instances (target under 60 days in immutable estates), percentage of instances built from an approved, non-deprecated image, baseline compliance score of each released image, number of images released with secret scanner findings (target zero), and time from a critical vendor patch to a released, tested image.
Common failure patterns
- The hand-built template: an engineer built the image years ago, nobody knows exactly what is in it, and it cannot be rebuilt.
- Cloned without generalisation: hosts share SSH host keys and machine IDs; EDR shows one device for fifty servers.
- Build credentials left behind: a domain join account password in an answer file, or an artefact repository token in a build script, ships in every host.
- Shared local administrator password: one password extracted from one host unlocks every server built from the image.
- Images shared too widely: a public AMI or a snapshot shared to the wrong account exposes the disk contents, including anything the clean-up missed.
- Stale images in auto scaling groups: launch templates still point to an image from last year, so every scale-out event creates unpatched hosts.
Framework mapping
| Image control | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | ISO/IEC 27001:2022 Annex A | MITRE ATT&CK addressed |
|---|---|---|---|---|
| Recipe and baseline in code | CM-2, CM-6, CM-7 | PR.PS-01 | A.8.9 | T1525 |
| Change control on recipes and pipelines | CM-3, SA-10 | PR.PS-01 | A.8.32 | T1525 |
| Image provenance and integrity | SI-7, SR-4 | ID.AM-02 | A.8.19 | T1525 |
| No embedded secrets, unique local admin | IA-5 | PR.AA-01 | A.5.17 | T1552.001, T1552.004, T1078.003 |
| Patch cadence and expiry | SI-2 | PR.PS-02 | A.8.8 | T1068 |
| Inventory of images and instances | CM-8 | ID.AM-02 | A.5.9 | None |
CIS Controls v8.1 Safeguard 4.1 (establish and maintain a secure configuration process) applies directly to image recipes.
Practitioner checklist
- Build every server image from a version-controlled recipe through a pipeline with a least-privilege build identity.
- Apply a named CIS or STIG baseline during the build and attach the scan report to each image version.
- Generalise every image: Sysprep for Windows, empty machine-id, removed SSH host keys and cloud-init clean for Linux.
- Remove answer files, build tokens, history and logs, and fail the build on any secret scanner finding.
- Use workload identity for cloud access and Windows LAPS for local administrator passwords instead of baked-in credentials.
- Encrypt distributed images, share only to named accounts and enforce block public access for AMIs or the equivalent.
- Restrict production launches to approved, non-expired images.
- Rebuild at least monthly and on critical vulnerabilities, and track the age of the image behind each running instance.
Standards and references
- Microsoft Learn: Sysprep (Generalize) a Windows installation
- Microsoft Learn: Remove machine specific information by generalizing a VM
- Microsoft Learn: Hide Sensitive Data in an Answer File
- systemd: machine-id(5)
- cloud-init: CLI commands (clean)
- AWS: What is EC2 Image Builder?
- AWS: Block public access to your AMIs
- MITRE ATT&CK: Implant Internal Image
Related NHI Mgmt Group resources: Container Image Security Guide · CIS Benchmarks Guide · Configuration Drift and Baseline Enforcement Guide · Linux Server Hardening Guide