AWS Security ChangesHomeSearch

AWS imagebuilder high security documentation change

Service: imagebuilder · 2026-07-07 · Security-related high

File: imagebuilder/latest/userguide/security-best-practices.md

Summary

Updated security best practices documentation to add macOS-specific cleanup procedures, modified Linux cleanup scripts, added macOS skip flags, and restructured file removal sections.

Security assessment

Changes explicitly modify security-sensitive cleanup procedures including SSH key removal, log file deletion, and instance history resetting. Added macOS-specific security controls (ec2-macos-init clean) and documented skip flags for security-sensitive cleanup sections. Modifications to authorized_keys paths and user home directories indicate platform-specific security hardening.

Diff

diff --git a/imagebuilder/latest/userguide/security-best-practices.md b/imagebuilder/latest/userguide/security-best-practices.md
index e18178cf3..f8706f366 100644
--- a//imagebuilder/latest/userguide/security-best-practices.md
+++ b//imagebuilder/latest/userguide/security-best-practices.md
@@ -7 +7 @@
-Required post-build clean upOverride the Linux clean up script
+Required post-build clean upOverride the Linux clean up scriptOverride the macOS clean up script
@@ -302,9 +302,3 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-    # Clean up for cloud-init files
-    CLOUD_INIT_FILES=(
-      "/etc/sudoers.d/90-cloud-init-users"
-      "/etc/locale.conf"
-      "/var/log/cloud-init.log"
-      "/var/log/cloud-init-output.log"
-    )
-    if [[ -f {{workingDirectory}}/skip_cleanup_cloudinit_files ]]; then
-      echo "Skipping cleanup of cloud init files"
+    # Reset EC2 macOS Init instance history so the image behaves as a first boot
+    if [[ -f {{workingDirectory}}/skip_cleanup_ec2_macos_init_files ]]; then
+      echo "Skipping cleanup of ec2-macos-init instance history"
@@ -312,13 +306,5 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      echo "Cleaning up cloud init files"
-      cleanup "${CLOUD_INIT_FILES[@]}"
-      if [[ $( sudo find /var/lib/cloud -type f | sudo wc -l ) -gt 0 ]]; then
-          echo "Deleting files within /var/lib/cloud/*"
-          sudo find /var/lib/cloud -type f -exec rm -f {} \;
-      fi;
-    
-      if [[ $( sudo ls /var/lib/cloud | sudo wc -l ) -gt 0 ]]; then
-          echo "Deleting /var/lib/cloud/*"
-          sudo rm -rf /var/lib/cloud/* || true
-      fi;
-    fi;
-    
+      echo "Cleaning up ec2-macos-init instance history"
+      if [[ -x /usr/local/bin/ec2-macos-init ]]; then
+          sudo /usr/local/bin/ec2-macos-init clean -all
+      fi
+    fi
@@ -328,8 +314 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      "/etc/.updated"
-      "/etc/aliases.db"
-      "/etc/hostname"
-      "/var/lib/misc/postfix.aliasesdb-stamp"
-      "/var/lib/postfix/master.lock"
-      "/var/spool/postfix/pid/master.pid"
-      "/var/.updated"
-      "/var/cache/yum/x86_64/2/.gpgkeyschecked.yum"
+      "/Library/Preferences/SystemConfiguration/NetworkInterfaces.plist"
@@ -353 +332 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      "/root/.ssh/authorized_keys"
+      "/var/root/.ssh/authorized_keys"
@@ -360 +339 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      USERS=$(ls /home/)
+      USERS=$(ls /Users/)
@@ -362,2 +341,4 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-          echo Deleting /home/"$user"/.ssh/authorized_keys;
-          sudo find /home/"$user"/.ssh/authorized_keys -type f -exec rm -f {} \;
+          if [[ -f /Users/"$user"/.ssh/authorized_keys ]]; then
+              echo Deleting /Users/"$user"/.ssh/authorized_keys;
+              sudo rm -f /Users/"$user"/.ssh/authorized_keys;
+          fi;
@@ -366,2 +347,2 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-          if [[ -f /home/"$user"/.ssh/authorized_keys ]]; then
-              echo Failed to delete /home/"$user"/.ssh/authorized_keys;
+          if [[ -f /Users/"$user"/.ssh/authorized_keys ]]; then
+              echo Failed to delete /Users/"$user"/.ssh/authorized_keys;
@@ -376,4 +356,0 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      "/var/log/audit/audit.log"
-      "/var/log/boot.log"
-      "/var/log/dmesg"
-      "/var/log/cron"
@@ -396 +373,5 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      if [[ $( sudo find {{workingDirectory}}/TOE_* -type f | sudo wc -l) -gt 0 ]]; then
+      shopt -s nullglob
+      TOE_MATCHES=({{workingDirectory}}/TOE_*)
+      shopt -u nullglob
+      if [[ ${#TOE_MATCHES[@]} -gt 0 ]]; then
+          if [[ $( sudo find "${TOE_MATCHES[@]}" -type f | sudo wc -l) -gt 0 ]]; then
@@ -398 +379 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-          sudo find {{workingDirectory}}/TOE_* -type f -exec rm -f {} \;
+              sudo find "${TOE_MATCHES[@]}" -type f -exec rm -f {} \;
@@ -400 +381,5 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      if [[ $( sudo find {{workingDirectory}}/TOE_* -type f | sudo wc -l) -gt 0 ]]; then
+          shopt -s nullglob
+          TOE_REMAINING=({{workingDirectory}}/TOE_*)
+          shopt -u nullglob
+          if [[ ${#TOE_REMAINING[@]} -gt 0 ]]; then
+              if [[ $( sudo find "${TOE_REMAINING[@]}" -type f | sudo wc -l) -gt 0 ]]; then
@@ -404 +388,0 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      if [[ $( sudo find {{workingDirectory}}/TOE_* -type d | sudo wc -l) -gt 0 ]]; then
@@ -406 +390 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-          sudo rm -rf {{workingDirectory}}/TOE_*
+              sudo rm -rf "${TOE_REMAINING[@]}"
@@ -408 +392,4 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      if [[ $( sudo find {{workingDirectory}}/TOE_* -type d | sudo wc -l) -gt 0 ]]; then
+          shopt -s nullglob
+          TOE_FINAL=({{workingDirectory}}/TOE_*)
+          shopt -u nullglob
+          if [[ ${#TOE_FINAL[@]} -gt 0 ]]; then
@@ -412,0 +400 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
+    fi
@@ -418,0 +407 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
+      if [[ -d "/var/log/amazon/ssm" ]]; then
@@ -427 +415,0 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      if [[ -d "/var/log/amazon/ssm" ]]; then
@@ -430 +417,0 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-      fi
@@ -435,0 +423 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
+    fi
@@ -438,6 +426,12 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-    if [[ $( sudo find /var/log/sa/sa* -type f | sudo wc -l ) -gt 0 ]]; then
-      echo "Deleting /var/log/sa/sa*"
-      sudo rm -f /var/log/sa/sa*
-    fi
-    if [[ $( sudo find /var/log/sa/sa* -type f | sudo wc -l ) -gt 0 ]]; then
-      echo "Failed to delete /var/log/sa/sa*"
+    # Clean up for DHCP lease files
+    shopt -s nullglob
+    DHCP_LEASE_MATCHES=(/var/db/dhcpclient/leases/*)
+    shopt -u nullglob
+    if [[ ${#DHCP_LEASE_MATCHES[@]} -gt 0 ]]; then
+      echo "Deleting /var/db/dhcpclient/leases/*"
+      sudo rm -f "${DHCP_LEASE_MATCHES[@]}"
+      shopt -s nullglob
+      DHCP_LEASE_REMAINING=(/var/db/dhcpclient/leases/*)
+      shopt -u nullglob
+      if [[ ${#DHCP_LEASE_REMAINING[@]} -gt 0 ]]; then
+          echo "Failed to delete /var/db/dhcpclient/leases/*"
@@ -446,8 +439,0 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-    
-    if [[ $( sudo find /var/lib/dhclient/dhclient*.lease -type f | sudo wc -l ) -gt 0 ]]; then
-          echo "Deleting /var/lib/dhclient/dhclient*.lease"
-          sudo rm -f /var/lib/dhclient/dhclient*.lease
-    fi
-    if [[ $( sudo find /var/lib/dhclient/dhclient*.lease -type f | sudo wc -l ) -gt 0 ]]; then
-          echo "Failed to delete /var/lib/dhclient/dhclient*.lease"
-          exit 1
@@ -469,15 +455,2 @@ If you override **User data** in your recipe, the script doesn't run. In that ca
-    # Shredding is not guaranteed to work well on rolling logs
-    
-    if [[ -f "/var/lib/rsyslog/imjournal.state" ]]; then
-          echo "Deleting /var/lib/rsyslog/imjournal.state"
-          sudo rm -f /var/lib/rsyslog/imjournal.state
-          sudo rm -f /var/lib/rsyslog/imjournal.state
-    fi
-    
-    if [[ $( sudo ls /var/log/journal/ | sudo wc -l ) -gt 0 ]]; then
-          echo "Deleting /var/log/journal/*"
-          sudo find /var/log/journal/ -type f -exec rm -f {} \;
-          sudo rm -rf /var/log/journal/*
-    fi
-    
-    sudo touch /etc/machine-id
+    # Flush all pending writes to disk before instance shutdown and snapshot
+    sync
@@ -508,0 +482,23 @@ Input Clean up section | Files removed | Skip section file name
+## Override the macOS clean up script
+
+Image Builder creates images that are secure by default and follow our security best practices. However, some more advanced use-cases might require you to skip one or more sections of the built-in clean up script. If you do need to skip some of the clean up, we strongly recommend that you test your output AMI to ensure the security of your image.
+
+###### Important
+
+Skipping sections in the clean up script can result in sensitive information, such as owner account details or SSH keys being included in the final image, and in any instance launched from that image. You might also experience problems with launching in different Availability Zones, Regions, or accounts.
+
+The following table outlines the sections of the clean up script, the files that are deleted in that section, and the file names that you can use to flag a section that Image Builder should skip. To skip a specific section of the clean up script, you can use the [CreateFile](./toe-action-modules.html#action-modules-createfile) component action module or a command in your user data (if overriding) to create an empty file with the name specified in the **Skip section file name** column.
+
+###### Note
+
+The files that you create to skip a section of the clean up script should not include a file extension. For example, if you want to skip the `INSTANCE_FILES` section of the script, but you create a file named `skip_cleanup_instance_files.txt`, Image Builder will not recognize the skip file.
+
+Input Clean up section | Files removed | Skip section file name  
+---|---|---  
+`EC2_MACOS_INIT_FILES` | Runs `ec2-macos-init clean -all` to reset the EC2 macOS Init instance history so the image behaves as a first boot. | `skip_cleanup_ec2_macos_init_files`  
+`INSTANCE_FILES` | `/Library/Preferences/SystemConfiguration/NetworkInterfaces.plist` | `skip_cleanup_instance_files`  
+`SSH_FILES` | `/etc/ssh/ssh_host_rsa_key` `/etc/ssh/ssh_host_rsa_key.pub` `/etc/ssh/ssh_host_ecdsa_key` `/etc/ssh/ssh_host_ecdsa_key.pub` `/etc/ssh/ssh_host_ed25519_key` `/etc/ssh/ssh_host_ed25519_key.pub` `/var/root/.ssh/authorized_keys` `/Users/<all users>/.ssh/authorized_keys` | `skip_cleanup_ssh_files`  
+`INSTANCE_LOG_FILES` | `/var/log/amazon/ec2/ec2-macos-init.log` `/var/log/amazon/ec2/ena-ethernet.log` `/var/log/amazon/ec2/system-monitoring.log` | `skip_cleanup_instance_log_files`  
+`TOE_FILES` | `{{workingDirectory}}/TOE_*` | `skip_cleanup_toe_files`  
+`SSM_LOG_FILES` | `/var/log/amazon/ssm/*` | `skip_cleanup_ssm_log_files`  
+