Use UEFI Secure Boot and TPM on Ubuntu-based EC2 instances¶
UEFI Secure Boot is a security feature specified in UEFI, which verifies the state of the boot chain. With UEFI Secure Boot enabled, after firmware self-initialization only cryptographically verified UEFI binaries are allowed to be executed. This prevents any unauthorized modification of the instance boot flow.
Trusted Platform Module (TPM) is a virtual device provided by the AWS Nitro System. It securely stores artifacts (such as passwords, certificates, or encryption keys) that are used to authenticate the instance. Check the AWS NitroTPM documentation for more details.
Although UEFI Secure Boot is supported by both Ubuntu images and EC2, it is not enabled in the EC2 AMIs. So to use UEFI Secure Boot (along with TPM), a couple of configuration steps are needed that create and register a new AMI based on an existing Ubuntu AMI:
Download a prebuilt UEFI Secure Boot variable store
Get an Ubuntu AMI ID to be used as the base image
Register a new AMI configured with - UEFI boot mode, the downloaded UEFI Secure Boot variable store and support for a virtual TPM
To follow these steps, you’ll need aws-cli
and jq
:
sudo snap install aws-cli
sudo apt install jq
Download a prebuilt UEFI Secure Boot variable store¶
The variable store is prebuilt and can be downloaded using:
wget https://github.com/canonical/aws-secureboot-blob/releases/latest/download/blob.bin
For information on how this binary blob gets created, you can refer to its source code and read the relevant AWS documentation.
Get an Ubuntu AMI ID¶
The SSM parameter store can be used to get e.g. the latest Ubuntu 22.04 LTS AMI ID:
AMI=$(aws ssm get-parameters \
--names /aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id \
--query 'Parameters[0].Value' --output text)
If you want to use a different Ubuntu image, refer to Find Ubuntu images on AWS. Once you have the AMI ID, use it to get the image name and its snapshot ID. These will be needed during the registration of a new AMI.
AMI_NAME=$(aws ec2 describe-images \
--image-id "${AMI}" \
| jq -r '.Images[0].Name')
AMI_SNAPSHOT=$(aws ec2 describe-images \
--image-id "${AMI}" \
| jq -r '.Images[0].BlockDeviceMappings[0].Ebs.SnapshotId')
The ${AMI}
variable should now contain a valid AMI ID (e.g. ami-0373268fb2dac8b4b
),
${AMI_NAME}
should have something like ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20230711
and
${AMI_SNAPSHOT}
should have a valid Snapshot ID (e.g. snap-095ef5e2836b5a9e3
).
Register a new AMI¶
To register a new AMI, first create a copy of the chosen AMI’s snapshot:
REGION=$(aws configure get region)
AMI_SNAPSHOT_NEW=$(aws ec2 copy-snapshot \
--source-region "${REGION}" \
--source-snapshot-id "${AMI_SNAPSHOT}" \
| jq -r '.SnapshotId')
aws ec2 wait snapshot-completed --snapshot-ids "${AMI_SNAPSHOT_NEW}"
Now register a new AMI with the boot mode set to uefi
, TPM support enabled, and the downloaded UEFI variable store attached:
AMI_NEW=$(aws ec2 register-image \
--name "${AMI_NAME}-secureboot" \
--uefi-data "$(cat blob.bin)" \
--block-device-mappings "DeviceName=/dev/sda1,Ebs= {SnapshotId=""${AMI_SNAPSHOT_NEW}"",DeleteOnTermination=true}" \
--architecture x86_64 \
--root-device-name /dev/sda1 \
--virtualization-type hvm \
--ena-support \
--boot-mode uefi \
--tpm-support v2.0 \
| jq -r '.ImageId')
aws ec2 wait image-available --image-ids "${AMI_NEW}"
$AMI_NEW
now contains the new AMI ID. This new registered image can now be booted with UEFI Secure Boot.
Verify secure boot mode¶
Note
UEFI boot mode (and in turn Secure Boot) is only supported by certain instance types. Check the AWS boot mode documentation for details.
NitroTPM is only supported by certain instance types. Check the AWS NitroTPM prerequisites documentation for details.
Let’s start a new instance with the newly created image ($AMI_NEW
) and verify two things - (1) Secure Boot is enabled and (2) A TPM device is present.
To do this, first set the KEY_NAME variable to your keypair name. This allows you to log in to the instance over ssh.
KEY_NAME=my-uploaded-keypair-name
Next start an instance:
INSTANCE=$(aws ec2 run-instances --image-id "${AMI_NEW}" --instance-type t3.medium --key-name "${KEY_NAME}"|jq -r '.Instances[].InstanceId')
INSTANCE_IP=$(aws ec2 describe-instances --instance-ids "${INSTANCE}"|jq -r '.Reservations[].Instances[].PublicIpAddress')
Now login and check the Secure Boot status:
ssh ubuntu@${INSTANCE_IP} mokutil --sb-state
... which should output:
SecureBoot enabled
Finally check that the TPM device is available:
ssh ubuntu@${INSTANCE_IP} ls -al /dev/tpm*
... which should output something like:
crw-rw---- 1 tss root 10, 224 Jul 18 10:53 /dev/tpm0
crw-rw---- 1 tss tss 253, 65536 Jul 18 10:53 /dev/tpmrm0