Create a Linux AMI with custom UEFI Secure Boot keys
This procedure shows you how to create a Linux AMI with UEFI Secure Boot and custom-made private keys. Amazon Linux supports UEFI Secure Boot starting with AL2023 release 2023.1. For more information, see UEFI Secure Boot in the AL2023 User Guide.
Important
The following procedure is intended for advanced users only. You must have sufficient knowledge of SSL and Linux distribution boot flow to use these procedures.
Prerequisites
-
The following tools will be used:
-
OpenSSL – https://www.openssl.org/
-
efivar – https://github.com/rhboot/efivar
-
efitools – https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git/
-
get-instance-uefi-data
command
-
-
Your Linux instance must have been launched with a Linux AMI that supports UEFI boot mode, and have non-volatile data present.
Newly created instances without UEFI Secure Boot keys are created in
SetupMode
, which allows you to enroll your own keys. Some AMIs come
preconfigured with UEFI Secure Boot and you cannot change the existing keys. If you
want to change the keys, you must create a new AMI based on the original AMI.
You have two ways to propagate the keys in the variable store, which are described in Option A and Option B that follow. Option A describes how to do this from within the instance, mimicking the flow of real hardware. Option B describes how to create a binary blob, which is then passed as a base64-encoded file when you create the AMI. For both options, you must first create the three key pairs, which are used for the chain of trust.
To create a Linux AMI that supports UEFI Secure Boot, first create the three key pairs, and then complete either Option A or Option B, but not both:
Step 1
UEFI Secure Boot is based on the following three key databases, which are used in a chain of trust: the platform key (PK), the key exchange key (KEK), and the signature database (db).¹
You create each key on the instance. To prepare the public keys in a format
that is valid for the UEFI Secure Boot standard, you create a certificate for
each key. DER
defines the SSL format (binary encoding of a format).
You then convert each certificate into a UEFI signature list, which is the
binary format that is understood by UEFI Secure Boot. And finally, you sign each
certificate with the relevant key.
Tasks
Prepare to create the key pairs
Before creating the key pairs, create a globally unique identifier (GUID) to be used in key generation.
-
Run the following command in a shell prompt.
uuidgen --random > GUID.txt
Key pair 1: Create the platform key (PK)
The PK is the root of trust for UEFI Secure Boot instances. The private PK is used to update the KEK, which in turn can be used to add authorized keys to the signature database (db).
The X.509 standard is used for creating the key pair. For information
about the standard, see X.509
To create the PK
-
Create the key. You must name the variable
PK
.openssl req -newkey rsa:4096 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=
Platform key
/" -out PK.crtThe following parameters are specified:
-
-keyout PK.key
– The private key file. -
-days 3650
– The number of days that the certificate is valid. -
-out PK.crt
– The certificate that is used to create the UEFI variable. -
CN=
– The common name (CN) for the key. You can enter the name of your own organization instead ofPlatform key
Platform key
.
-
-
Create the certificate.
openssl x509 -outform DER -in PK.crt -out PK.cer
-
Convert the certificate into a UEFI signature list.
cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
-
Sign the UEFI signature list with the private PK (self-signed).
sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth
Key pair 2: Create the key exchange key (KEK)
The private KEK is used to add keys to the db, which is the list of authorized signatures to boot on the system.
To create the KEK
-
Create the key.
openssl req -newkey rsa:4096 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=Key Exchange Key/" -out KEK.crt
-
Create the certificate.
openssl x509 -outform DER -in KEK.crt -out KEK.cer
-
Convert the certificate into a UEFI signature list.
cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
-
Sign the signature list with the private PK.
sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
Key pair 3: Create the signature database (db)
The db list contains authorized keys that are authorized to be booted on the system. To modify the list, the private KEK is necessary. Boot images will be signed with the private key that is created in this step.
To create the db
-
Create the key.
openssl req -newkey rsa:4096 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=Signature Database key/" -out db.crt
-
Create the certificate.
openssl x509 -outform DER -in db.crt -out db.cer
-
Convert the certificate into a UEFI signature list.
cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
-
Sign the signature list with the private KEK.
sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth
Sign the boot image (kernel) with the private key
For Ubuntu 22.04, the following images require signatures.
/boot/efi/EFI/ubuntu/shimx64.efi
/boot/efi/EFI/ubuntu/mmx64.efi
/boot/efi/EFI/ubuntu/grubx64.efi
/boot/vmlinuz
To sign an image
Use the following syntax to sign an image.
sbsign --key db.key --cert db.crt --output
/boot/vmlinuz
/boot/vmlinuz
Note
You must sign all new kernels.
will usually
symlink to the last installed kernel./boot/vmlinuz
Refer to the documentation for your distribution to find out about your boot chain and required images.
¹ Thanks to the ArchWiki community for all of the work they have done. The commands for creating the PK, creating the KEK,
creating the DB, and signing the image are from Creating keys
Step 2 (Option A): Add keys to the variable store from within the instance
After you have created the three key pairs, you can connect to your instance and add the keys to the variable store from within the instance by completing the following steps. Alternatively, complete the steps for Step 2 (Option B): Create a binary blob containing a pre-filled variable store.
Option A steps:
Step 1: Launch an instance that will support UEFI Secure Boot
When you launch an instance with the following prerequisites, the instance will then be ready to be configured to support UEFI Secure Boot. You can only enable support for UEFI Secure Boot on an instance at launch; you can't enable it later.
Prerequisites
-
AMI – The Linux AMI must support UEFI boot mode. To verify that the AMI supports UEFI boot mode, the AMI boot mode parameter must be uefi. For more information, see Determine the boot mode parameter of an Amazon EC2 AMI.
Note that AWS only provides Linux AMIs configured to support UEFI for Graviton-based instance types. AWS currently does not provide x86_64 Linux AMIs that support UEFI boot mode. You can configure your own AMI to support UEFI boot mode for all architectures. To configure your own AMI to support UEFI boot mode, you must perform a number of configuration steps on your own AMI. For more information, see Set the boot mode of an Amazon EC2 AMI.
-
Instance type – All virtualized instance types that support UEFI also support UEFI Secure Boot. Bare metal instance types do not support UEFI Secure Boot. For the instance types that support UEFI Secure Boot, see Requirements for UEFI boot mode.
-
Launch your instance after the release of UEFI Secure Boot. Only instances launched after May 10, 2022 (when UEFI Secure Boot was released) can support UEFI Secure Boot.
After you’ve launched your instance, you can verify that it is ready to be configured to support UEFI Secure Boot (in other words, you can proceed to Step 2) by checking whether UEFI data is present. The presence of UEFI data indicates that non-volatile data is persisted.
To verify whether your instance is ready for Step 2
Use the get-instance-uefi-data
aws ec2 get-instance-uefi-data --instance-id
i-0123456789example
The instance is ready for Step 2 if UEFI data is present in the output. If the output is empty, the instance cannot be configured to support UEFI Secure Boot. This can happen if your instance was launched before UEFI Secure Boot support became available. Launch a new instance and try again.
Step 2: Configure an instance to support UEFI Secure Boot
Enroll the key pairs in your UEFI variable store on the instance
Warning
You must sign your boot images after you enroll the keys, otherwise you won’t be able to boot your instance.
After you create the signed UEFI signature lists (PK
,
KEK
, and db
), they must be enrolled into
the UEFI firmware.
Writing to the PK
variable is possible only if:
-
No PK is enrolled yet, which is indicated if the
SetupMode
variable is1
. Check this by using the following command. The output is either1
or0
.efivar -d -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-SetupMode
-
The new PK is signed by the private key of the existing PK.
To enroll the keys in your UEFI variable store
The following commands must be run on the instance.
If SetupMode is enabled (the value is 1
), the keys can be
enrolled by running the following commands on the instance:
[ec2-user ~]$
efi-updatevar -f db.auth db
[ec2-user ~]$
efi-updatevar -f KEK.auth KEK
[ec2-user ~]$
efi-updatevar -f PK.auth PK
To verify that UEFI Secure Boot is enabled
To verify that UEFI Secure Boot is enabled, follow the steps in Verify whether an Amazon EC2 instance is enabled for UEFI Secure Boot.
You can now export your UEFI variable store with the get-instance-uefi-data
Step 3: Create an AMI from the instance
To create an AMI from the instance, you can use the console or the
CreateImage
API, CLI, or SDKs. For the console
instructions, see Create an Amazon EBS-backed AMI. For the API instructions, see
CreateImage.
Note
The CreateImage
API automatically copies the UEFI variable store of the
instance to the AMI. The console uses the CreateImage
API.
After you launch instances using this AMI, the instances will have the
same UEFI variable store.
Step 2 (Option B): Create a binary blob containing a pre-filled variable store
After you have created the three key pairs, you can create a binary blob containing a pre-filled variable store containing the UEFI Secure Boot keys. Alternatively, complete the steps for Step 2 (Option A): Add keys to the variable store from within the instance.
Warning
You must sign your boot images before you enroll the keys, otherwise you won’t be able to boot your instance.
Option B steps:
Step 1: Create a new variable store or update an existing one
You can create the variable store offline without a running instance by using the python-uefivars tool. The tool can create a new variable store from your keys. The script currently supports the EDK2 format, the AWS format, and a JSON representation that is easier to edit with higher-level tooling.
To create the variable store offline without a running instance
-
Download the tool at the following link.
https://github.com/awslabs/python-uefivars
-
Create a new variable store from your keys by running the following command. This will create a base64-encoded binary blob in
your_binary_blob
.bin. The tool also supports updating a binary blob via the-I
parameter../uefivars.py -i none -o aws -O
your_binary_blob
.bin -P PK.esl -K KEK.esl --db db.esl --dbx dbx.esl
Step 2: Upload the binary blob on AMI creation
Use register-image--uefi-data
parameter, specify
your binary blob, and for the --boot-mode
parameter,
specify uefi
.
aws ec2 register-image \ --name uefi_sb_tpm_register_image_test \ --uefi-data $(cat
your_binary_blob
.bin) \ --block-device-mappings "DeviceName=/dev/sda1,Ebs= {SnapshotId=snap-0123456789example
,DeleteOnTermination=true}" \ --architecture x86_64 \ --root-device-name /dev/sda1 \ --virtualization-type hvm \ --ena-support \ --boot-mode uefi