Create a Linux AMI with custom UEFI Secure Boot keys - Amazon Elastic Compute Cloud

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

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:

    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.

    Prepare to create the key pairs

    Before creating the key pairs, create a globally unique identifier (GUID) to be used in key generation.

    1. Connect to the instance.

    2. 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 on Wikipedia.

    To create the PK
    1. 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.crt

      The 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=Platform key – The common name (CN) for the key. You can enter the name of your own organization instead of Platform key.

    2. Create the certificate.

      openssl x509 -outform DER -in PK.crt -out PK.cer
    3. Convert the certificate into a UEFI signature list.

      cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
    4. 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
    1. 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
    2. Create the certificate.

      openssl x509 -outform DER -in KEK.crt -out KEK.cer
    3. Convert the certificate into a UEFI signature list.

      cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
    4. 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
    1. 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
    2. Create the certificate.

      openssl x509 -outform DER -in db.crt -out db.cer
    3. Convert the certificate into a UEFI signature list.

      cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
    4. 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. /boot/vmlinuz will usually symlink to the last installed kernel.

    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, authored by the ArchWiki Maintenance Team and/or the ArchWiki contributors.

    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.

    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 command and specify the instance ID.

    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 is 1. Check this by using the following command. The output is either 1 or 0.

      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 CLI command, or you continue to the next step and sign your boot images to reboot into a UEFI Secure Boot-enabled instance.

    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.

    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.

    Warning

    You must sign your boot images before you enroll the keys, otherwise you won’t be able to boot your instance.

    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
    1. Download the tool at the following link.

      https://github.com/awslabs/python-uefivars
    2. 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 to pass your UEFI variable store data. For the --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