Ubuntu Server 24.04 on AWS User Guide
Overview
This image is Ubuntu Server 24.04 LTS (Noble Numbat), one software version of the cloudimg Hardened Ubuntu 22.04 & 24.04 LTS AMIs: Minimal, LVM, STIG listing on AWS Marketplace. Every release and variant in the listing is described at /guides/ubuntu-server-aws/.
Host firewall enabled (ufw, inbound SSH only): open app ports in both the security group and ufw. Opening a port in the security group alone is not enough.
What cloudimg does to the image:
- Remediated with the OpenSCAP CIS Level 1 Server profile. The build applies the SCAP Security Guide 0.1.82 profile
cis_level1_serverwithoscap --remediate, reboots, then runs a scored evaluation. The build fails if fewer than 95% of the scored rules pass; this version scored 345 of 352 rules (98.0%). The report ships in the image at/usr/share/doc/cloudimg/cis-l1-report.html. This is a measurement made by open-source tooling, not a certification or accreditation by the benchmark's publisher or any other body, and these images are not CIS Hardened Images. - 6 rules waived, each with its reason (listed below and in
/usr/share/doc/cloudimg/cis-l1-waivers.txt): rules that would lock you out of an EC2 instance or need a password baked into every copy of the image. - Fully patched at build time. The build fails if any update is outstanding or a reboot is pending.
- Automatic security updates stay on (unattended-upgrades).
- Key-only SSH as
ubuntu. Password login is off, root login is refused, the root account is locked and no account has a password. - IMDSv2 only. The AMI is marked IMDSv2-only, so instances launched from it require session tokens for instance metadata.
- Host firewall on (ufw): inbound SSH only, outbound open so the SSM agent, updates, DNS and NTP work.
- AppArmor enforcing, chrony on the Amazon Time Sync Service (169.254.169.123, no internet access needed), and the AWS Systems Manager (SSM) agent installed from its Debian package (no snaps).
- Nothing shared between instances. No SSH keys, host keys, machine ID or credentials are baked in; cloud-init creates them on each instance's first boot.
- No swap, and SSH is the only service listening on the network (the DNS stub resolver, chrony and the local mail transfer agent listen on loopback only).
Connecting to your instance
Connect over SSH as the default login user for the operating system variant you launched:
| Operating system variant | SSH login user |
|---|---|
| Ubuntu Server 24.04 LTS | ubuntu |
Launch from AWS Marketplace
- Open the listing on AWS Marketplace, choose Continue to Subscribe, then Continue to Configuration.
- In Software version, choose Ubuntu Server 24.04 LTS - YYYY-MM-DD (the date is when the version was added).
- Choose your Region, Continue to Launch, then Launch through EC2.
- Instance type:
m5.largeis recommended; any current x86_64 type works. The software charge is $0.04 per vCPU per hour. - Key pair: choose your EC2 key pair. It is the only way in. RSA and ED25519 keys both work.
- Security group: allow inbound TCP 22 from your own IP address or network only.
- Metadata: leave the defaults. The image already requires IMDSv2.
From the AWS CLI (use the AMI ID the Marketplace launch page shows for your Region):
aws ec2 run-instances --image-id ami-xxxxxxxxxxxxxxxxx --instance-type m5.large \
--key-name my-key --security-group-ids sg-xxxxxxxx --subnet-id subnet-xxxxxxxx
Connect over SSH
ssh -i ~/.ssh/my-key.pem ubuntu@<public-ip>
ubuntu has passwordless sudo. There is no password on any account. AWS Systems Manager Session Manager also works: the SSM agent is installed and running (attach an instance profile with AmazonSSMManagedInstanceCore).
Confirm the release and kernel:
grep -E '^(PRETTY_NAME|VERSION_ID|VERSION_CODENAME)=' /etc/os-release
uname -r
PRETTY_NAME="Ubuntu 24.04.5 LTS"
VERSION_ID="24.04"
VERSION_CODENAME=noble
7.0.0-1014-aws
The kernel version moves forward as Ubuntu publishes updates; the image ships the kernel current on its build date.

IMDSv2
The AMI carries ImdsSupport=v2.0, so every instance launched from it gets HttpTokens=required. Check it on the instance:
imds_token=$(curl -s -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 300' http://169.254.169.254/latest/api/token)
curl -s -H "X-aws-ec2-metadata-token: $imds_token" http://169.254.169.254/latest/meta-data/instance-id; echo
curl -s -o /dev/null -w '%{http_code}\n' http://169.254.169.254/latest/meta-data/instance-id
The first request (with a token) returns your instance ID; the second (IMDSv1, no token) is refused with 401:
i-0123456789abcdef0
401
Software that still uses IMDSv1 must be updated to request a token (current AWS SDKs and CLIs already do). If you must allow IMDSv1 temporarily, change it per instance with aws ec2 modify-instance-metadata-options --http-tokens optional; we do not recommend it.
View the compliance report
The evaluation the build ran is in /usr/share/doc/cloudimg/cis-l1-report.html (HTML), /usr/share/doc/cloudimg/cis-l1-arf.xml (machine-readable ARF) and /usr/share/doc/cloudimg/cis-l1-summary.json (counts, pass rate, threshold, SSG version and checksums). The build host's name and addresses are replaced in the shipped copy.
ls /usr/share/doc/cloudimg/
python3 -c "import json; d=json.load(open('/usr/share/doc/cloudimg/cis-l1-summary.json')); print(d['profileLabel'], d['ssgVersion'], '-', d['pass'], 'of', d['scored'], 'scored rules pass', '(' + str(d['ratePct']) + '%)', '- waived', d['waived'])"
cis-l1-arf.xml cis-l1-report.html cis-l1-summary.json cis-l1-tailoring.xml cis-l1-waivers.txt scap-security-guide-LICENSE.txt
CIS Level 1 Server 0.1.82 - 345 of 352 scored rules pass (98.0%) - waived 6

Copy the HTML report to your workstation and open it in a browser:
scp -i ~/.ssh/my-key.pem ubuntu@<public-ip>:/usr/share/doc/cloudimg/cis-l1-report.html .
The 7 scored rules that do not pass are listed in the summary's failingRules: all_apparmor_profiles_in_enforce_complain_mode, permissions_local_var_log, root_path_all_dirs, set_nftables_loopback_traffic, set_nftables_table, set_ufw_default_rule and ufw_rules_for_open_ports. The two nftables rules belong to the profile's nftables firewall option, which this image does not use (ufw is the host firewall), and set_ufw_default_rule fails because outbound traffic is allowed on purpose (see below).
Re-run the same evaluation on your instance at any time (about 5 to 10 minutes; nothing is changed):
sudo oscap xccdf eval --tailoring-file /usr/share/cloudimg/openscap/tailoring.xml \
--profile xccdf_co.cloudimg_profile_cis-l1_ec2_single \
--report ~/cis-l1-rescan.html /usr/share/xml/scap/ssg/content/ssg-ubuntu2404-ds.xml
oscap exits with status 2 when any rule fails; that is expected and the HTML report is still written.
Waived rules
These rules of the profile are deselected through the tailoring file /usr/share/cloudimg/openscap/tailoring.xml and are not scored. Everything else the profile selects is remediated and scored. The tailoring also selects ufw as the profile's firewall option and chrony as its time service.
| Rule | Category | Why it is waived on EC2 |
|---|---|---|
partition_for_tmp |
single-volume-partitions | The standard and Minimal versions boot from one EBS root volume, so /tmp, /var, /var/log, /var/log/audit, /var/tmp and /home live on the root filesystem. The LVM versions provide every one of these as a separate volume with the benchmark's mount options. |
grub2_password |
serial-console-and-boot | A boot-loader or root password would have to be baked into the image, giving every instance the same credential. Root stays locked and no password exists. EC2 Serial Console and single-user recovery need a password you set yourself on your own instance (see Emergency access). |
grub2_uefi_password |
serial-console-and-boot | As above, for the UEFI boot loader. |
accounts_maximum_age_login_defs |
key-only-account-aging | Password-age and inactivity defaults apply to every account cloud-init creates at launch. Those accounts are key-only and have no password, so an age limit expires them and locks the SSH key out. Accounts that do have a password are still aged. |
account_disable_post_pw_expiration |
key-only-account-aging | As above, for the inactivity default. |
sshd_limit_user_access |
ssh-access-list | An AllowUsers/AllowGroups list fixed at build time would refuse SSH to accounts you create with cloud-init user-data. SSH is key-only, root login is refused, and your security group restricts the source addresses. |
Deliberate deviation (scored as a failure): ufw denies all inbound traffic except SSH (22/tcp) but allows outbound traffic, so set_ufw_default_rule reports fail and counts against the score. A default-deny outbound policy would cut the SSM agent off from its endpoints, cloud-init and the instance from the metadata service (169.254.169.254), and the instance from DNS, NTP and package updates.
Host firewall and SSH
ufw is active and allows inbound SSH only; SSH accepts keys only:
sudo ufw status verbose
sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|pubkeyauthentication) '
sudo passwd -S root
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
Anywhere on lo ALLOW IN Anywhere
22/tcp ALLOW IN Anywhere
Anywhere DENY IN 127.0.0.0/8
Anywhere (v6) on lo ALLOW IN Anywhere (v6)
22/tcp (v6) ALLOW IN Anywhere (v6)
Anywhere (v6) DENY IN ::1
Anywhere ALLOW OUT Anywhere on lo
Anywhere (v6) ALLOW OUT Anywhere (v6) on lo
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
root L 2024-04-23 0 99999 7 -1
root L means the root password is locked.

To serve anything else, open the port in both your security group and the host firewall, for example HTTPS:
sudo ufw allow 443/tcp
sudo ufw status verbose
Services
systemctl is-active ssh ufw apparmor amazon-ssm-agent chrony
systemctl is-enabled unattended-upgrades
chronyc -n sources | head -3
active
active
active
active
active
enabled
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* 169.254.169.123 3 4 377 11 -35us[ -45us] +/- 520us
^* marks the source chrony is synchronised to: the Amazon Time Sync Service at 169.254.169.123, which every EC2 instance reaches without internet access. The Ubuntu NTP pool servers in /etc/chrony/chrony.conf are kept as fallbacks.
Patching
unattended-upgrades installs security updates automatically. To see how many updates are waiting, and to install everything now:
apt list --upgradable 2>/dev/null | grep -c upgradable || true
sudo apt-get update && sudo apt-get -y upgrade
If /var/run/reboot-required exists afterwards, a new kernel or core library was installed: reboot when convenient with sudo reboot.
Emergency access
There is no password on any account, by design. EC2 Serial Console and single-user recovery need a local password: set one for root on your own instance before you need it, and keep it in your secrets store:
sudo passwd root
Session Manager (SSM) is the other way in if SSH is unavailable.
Support
cloudimg provides 24/7 technical support for this image by email and live chat: support@cloudimg.co.uk. Ubuntu is a registered trademark of Canonical Ltd. CIS and CIS Benchmarks are trademarks of the Center for Internet Security, Inc. cloudimg is not affiliated with or endorsed by Canonical or CIS.