This repository provides artefacts to build and deploy an Amazon machine image (AMI) with SEV-SNP support. This image is meant to be deployed on a bare-metal host with SEV-SNP support (M6A or M7A). The AMI comprises a host OS with kernel support and patched KVM/OVMF (from the AMDSEV repository). The bare-metal EC2 host can be used standalone or attached to a Kubernetes cluster. With the standalone EC2, you can then launch a guest OS.
Standard attestation workflow is validated.
This repository provides sample codes for two build and deployment options. With Option 1, you start an EC2 bare-metal instance via the console and follow the build instructions in this README. With Options 2, you use the two CDK stacks. The first stack creates an EC2 Image Builder pipeline that builds all the software dependencies and creates an AMI. This first stack also builds a custom Ubuntu image for an EKS worker node with SEV-SNP support. The second stack launches an EC2 bare-metal instance, using the pre-build AMI, with a spot persistent request to keep cost to a minimum. You can also decide to launch the instance with any other AMI (useful to experiment).
Work in progress: two further steps are (in no specific order)
- Consider Linux distributions beyond Ubuntu
- Use upstream QEMU and OVMF rather the versions supplied via the AMDSEV repository
As SEV-SNP support trickles down in various distributions, getting it all setup will become simpler.
Just reach out for/to help ;-)
Warning: using bare-metal instances generate costs. Make sure to terminate or stop the instance when not in use.
m6a | m7a | OS | Custom Host Kernel | QEMU and OVMF | Standard attestation workflow | Rust version in Guest | Confidential containers |
---|---|---|---|---|---|---|---|
âś… | âś… | Ubuntu Jammy 1.31 for EKS | stable 6.11.5 | AMDSEV snp-latest d9404d5 | SEV-SNP Validated âś… / EKS workder node âś… | 1.82.0 | |
âś… | âś… | Ubuntu Jammy 1.31 for EKS | 6.8.0-rc5-next-20240221-snp-host-cc2568386 | Via Kata Containers 3.11.0 | SEV-SNP Validated âś… / EKS workder node with confidential containers 0.11.0 âś… | ||
âś… | âś… | Ubuntu 24.04.1 LTS | stable 6.11.5 | AMDSEV snp-latest d9404d5 | Validated âś… | 1.82.0 | |
âś… | âś… | Ubuntu 24.04.1 LTS | mainline 6.11 | AMDSEV snp-latest d9404d5 | Validated âś… | 1.82.0 |
The âś… under the m6a and m7a column means that the output of snphost
shows [ PASS ]
for all lines. Validated for Standard attestation workflow means that it could be replicated according to the instructions in the Section Standard Attestion below (and that the output of snpguest
shows shows [ PASS ]
).
The instructions in this repo will be updated as the software components (QEMU, etc) become readily available via distribution package managers.
You can run the get_metal_locations.sh
shell script located at the root of this repository. This script assumes that you have installed the AWS Command Line Inteface (CLI).
Using the console, deploy a bare-metal m6a or m7a instance (metal instance), with an Ubuntu 24.04 LTS AMI. You can use spot instances to mininize cost.
Get dependencies in place
sudo apt update
sudo apt upgrade -y
sudo apt install -y build-essential amazon-ec2-utils
sudo apt install -y tmux git rsync # For eks image
sudo apt install -y msr-tools libssl-dev pkg-config
sudo curl --proto '=https' --tlsv1.3 -sSf https://sh.rustup.rs | sh -s -- -y
. "$HOME/.cargo/env"
mkdir ~/src
I recommend you use a tmux
session.
Build sevctl
. It reports support for SEV-SNP is missing (with a kernel 6.8 on Ubuntu 24.04 at this time of writing)
cd ~/src
git clone https://github.com/virtee/sevctl
cd sevctl
cargo build -r
# cargo install --path .
sudo target/release/sevctl ok
Build snphost
. It reports issues with SEV-ES and SEV-SNP
cd ~/src
git clone https://github.com/virtee/snphost
cd snphost
cargo build -r
# cargo install --path .
sudo target/release/snphost ok
If your current kernel is not 6.11 or higher, you need to build a new kernel to get SEV-SNP support. First step is to install Dracut in order to defer the CCP module from loading and then to have the SEV metedata written to a file in the root directory. That's because the Nitro Security chip will prevent writing to firmware.
sudo apt install dracut -y
sudo tee -a /etc/dracut.conf.d/20-omit-ccp.conf <<EOF
omit_drivers+=" ccp "
EOF
sudo dracut --force
sudo tee -a /etc/modprobe.d/60-ccp.conf <<EOF
options ccp init_ex_path=/SEV_metadata
EOF
And update the kernel cmdline
# Update cmdline: add mem_encrypt=on kvm_amd.sev=1 iommu=nopt to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub.d/50-cloudimg-settings.cfg
sudo sed 's/\(GRUB_CMD.*\)"/\1 mem_encrypt=on kvm_amd.sev=1 iommu=nopt"/' -i /etc/default/grub.d/50-cloudimg-settings.cfg
grep GRUB_CMD /etc/default/grub.d/50-cloudimg-settings.cfg # To validate
sudo update-grub
sudo grep sev /boot/grub/grub.cfg # Check
Let's now build the kernel. Install prerequisites
sudo apt install -y libncurses-dev flex bison libelf-dev libudev-dev libpci-dev libiberty-dev libelf-dev debhelper-compat dwarves ccache
Choose the option that makes sense for you.
cd ~/src
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
cd linux
VERSION=6.11.5
git tag -l v${VERSION}*
git checkout tags/v${VERSION}
cd ~/src
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git #git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
VERSION=6.11
git tag -l v${VERSION}*
git checkout tags/v${VERSION} #git checkout tags/v6.11-rc5
cp -v /boot/config-$(uname -r) .config
scripts/config --disable CONFIG_BASE_SMALL
scripts/config --disable CONFIG_ANDROID_BINDER_IPC
scripts/config --disable CONFIG_ANDROID_BINDERFS
make olddefconfig
scripts/config --set-str SYSTEM_TRUSTED_KEYS ""
scripts/config --set-str SYSTEM_REVOCATION_KEYS ""
scripts/config --enable CONFIG_DEBUG_INFO_NONE
scripts/config --disable CONFIG_DEBUG_INFO
scripts/config --disable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
scripts/config --disable CONFIG_DEBUG_INFO_DWARF4
scripts/config --disable CONFIG_DEBUG_INFO_DWARF5
scripts/config --disable CONFIG_DEBUG_INF_BTF
make clean
# Faster, only necessary binaries.
make bindeb-pkg -j"$(nproc)" LOCALVERSION=-sev-snp-"$(dpkg --print-architecture)" KDEB_PKGVERSION="$(make kernelversion)-1"
(Useful references: ref, ref) And now install the packages
cd ~/src
sudo dpkg -i linux-*-${VERSION}*64_6* # Image and header
Reboot the instance to start with the new kernel. From the cdk
directory (not on the instance), you can use
bin/reboot-instance.sh $REGION
Note that rebooting a bare-metal instance takes some minutes. Go have a coffee. The status check in the console will temporarily fail. If the instance is not available after an hour, something failed. Else, once available, log back in and check you run the latest kernel
Alternatively, you can kexec
into the new kernel (look at the output over the EC2 serial console). You can get the command-line parameters from cat /proc/cmdline
sudo apt install -y kexec-tools
# Adapt accordingly
sudo kexec -l /boot/vmlinuz-${VERSION}-sev-snp-amd64 --append="root=PARTUUID=37df32be-d645-4334-8fae-e0cee60a7b2d ro console=tty1 console=ttyS0 nvme_core.io_timeout=4294967295 mem_encrypt=on kvm_amd.sev=1 iommu=nopt panic=-1" --initrd=/boot/initrd.img-${VERSION}-sev-snp-amd64
sudo kexec -e
Now that you have an up-and-running EC2 instance, you can validate SEV-SNP support.
uname -a # Should show the latest kernel, for example 6.11 in this case
cat /sys/module/kvm_amd/parameters/sev_snp # Should show y
sudo dmesg | grep -i -E "(ccp|sev)"
The last command shows SEV unusable, but SEV-ES and SEV-SNP enabled. And you can validate with the output of sevctl ok
and snphost ok
, e.g.
sudo src/sevctl/target/release/sevctl ok
should show
[ PASS ] - AMD CPU
[ PASS ] - Microcode support
[ PASS ] - Secure Memory Encryption (SME)
[ PASS ] - Secure Encrypted Virtualization (SEV)
[ PASS ] - Encrypted State (SEV-ES)
[ PASS ] - Secure Nested Paging (SEV-SNP)
[ PASS ] - VM Permission Levels
[ PASS ] - Number of VMPLs: 4
[ PASS ] - Physical address bit reduction: 5
[ PASS ] - C-bit location: 51
[ PASS ] - Number of encrypted guests supported simultaneously: 509
[ PASS ] - Minimum ASID value for SEV-enabled, SEV-ES disabled guest: 510
[ PASS ] - SEV enabled in KVM: enabled
[ PASS ] - SEV-ES enabled in KVM: enabled
[ PASS ] - Reading /dev/sev: /dev/sev readable
[ PASS ] - Writing /dev/sev: /dev/sev writable
[ PASS ] - Page flush MSR: ENABLED
[ PASS ] - KVM supported: API version: 12
[ PASS ] - Memlock resource limit: Soft: 101016338432 | Hard: 101016338432
and
sudo src/snphost/target/release/snphost ok
should show
[ PASS ] - AMD CPU
[ PASS ] - Microcode support
[ PASS ] - Secure Memory Encryption (SME)
[ PASS ] - SME: Enabled in MSR
[ PASS ] - Secure Encrypted Virtualization (SEV)
[ PASS ] - Encrypted State (SEV-ES)
[ PASS ] - SEV-ES INIT: Enabled
[ PASS ] - SEV INIT: SEV is INIT, but not currently running a guest
[ PASS ] - Secure Nested Paging (SEV-SNP)
[ PASS ] - VM Permission Levels
[ PASS ] - Number of VMPLs: 4
[ PASS ] - SNP: Enabled in MSR
[ PASS ] - SEV Firmware Version: Sev firmware version: 1.55
[ PASS ] - SNP INIT: SNP is INIT
[ PASS ] - Physical address bit reduction: 5
[ PASS ] - C-bit location: 51
[ PASS ] - Number of encrypted guests supported simultaneously: 509
[ PASS ] - Minimum ASID value for SEV-enabled, SEV-ES disabled guest: 510
[ PASS ] - Reading /dev/sev: /dev/sev readable
[ PASS ] - Writing /dev/sev: /dev/sev writable
[ PASS ] - Page flush MSR: ENABLED
[ PASS ] - KVM supported: API version: 12
[ PASS ] - SEV enabled in KVM: enabled
[ PASS ] - SEV-ES enabled in KVM: enabled
[ PASS ] - SEV-SNP enabled in KVM: enabled
[ PASS ] - Memlock resource limit: Soft: 101016338432 | Hard: 101016338432
[ PASS ] - RMP table addresses: Addresses: 412852682752 - 416083345407
[ PASS ] - RMP INIT: RMP is INIT
[ PASS ] - Comparing TCB values: TCB versions match
Platform TCB version:
TCB Version:
Microcode: 213
SNP: 20
TEE: 0
Boot Loader: 4
Reported TCB version:
TCB Version:
Microcode: 213
SNP: 20
TEE: 0
Boot Loader: 4
You might have to load the msr
module if you see error message related to MSR.
We use instructions from the snp-latest
branch of https://github.com/AMDESE/AMDSEV.git
sudo apt install -y nasm uuid-dev ninja-build acpica-tools libslirp-dev libbpf-dev libcap-ng-dev libtasn1-6-dev libnuma-dev libusb-1.0-0-dev libkeyutils-dev libglib2.0-dev python3-venv python-is-python3 virtinst valgrind
sudo apt install -y libbzip3-dev
sudo apt -y install ovmf qemu-system # To get the dependencies
cd ~/src
git clone https://github.com/AMDESE/AMDSEV.git
cd AMDSEV
git checkout snp-latest
#./build.sh -h # To get detail
./build.sh qemu
git clone https://github.com/AMDESE/ovmf.git # Fix for https://github.com/AMDESE/AMDSEV/issues/244
cd ovmf
git checkout snp-latest
sed 's/subhook.git/edk2-subhook.git/' -i .gitmodules && sed 's/Zeex/tianocore/' -i .gitmodules
cd ..
./build.sh ovmf
#./build.sh kernel
sudo cp kvm.conf /etc/modprobe.d/
You can now jump to the Guest Setup Section below.
The following deploys an Amazon EC2 bare-metal instance (m6a or m7a). The instance is deployed with a spot persistent request to keep cost to a minimum. The instance is deployed in the default VPC, in the public subnet. It can be accessed via AWS Systems Manager Session Manager.
If not done already, install the AWS Cloud Development Kit (CDK)
Before AWS CDK apps can be deployed in your AWS environment, you must provision preliminary resources. This process is called bootstrapping:
Clone the git repository, change directory to cdk
, and bootstrap
REGION=$(aws configure get region)
echo Default AWS Region: ${REGION}
ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text)
echo AWS Account number: ${ACCOUNT}
npm install # Install
cdk synth
APPLICATION=RuntimeAttestation
cdk bootstrap aws://${ACCOUNT}/${REGION} -t Application=${APPLICATION}
Session Manager is an AWS Systems Manager capability. We use Session Manager to securely access EC2 instances for development purpose and for interactive demo sessions. Session manager configuration is controlled by a session document. We deploy this session document before using Session manager
From the cdk
directory, run the following command to create a session document from the session_document.yaml
file:
REGION=$(aws configure get region)
echo Default AWS Region: ${REGION}
SESSION_DOCUMENT_NAME=SessionRegionalSettingsUbuntu
aws ssm create-document --content file://utils/ssm/session-document-ubuntu.yaml --document-type "Session" --name ${SESSION_DOCUMENT_NAME} --document-format YAML --region ${REGION}
This is a two steps process. First you deploy the EC2 Image Builder pipeline to build the custom AMI. Then you can deploy the EC2 instance. Hence, from the root of the cdk
directory, type
cdk deploy RuntimeAttestationImageBuilderStackEUC1 --require-approval never
To trigger the build of the custom AMI, open the AWS console for the EC2 Image Builder service. Select the checkbox for the pipeline named Image pipeline for Ubuntu SEV-SNP Host image on EC2, and in the Action drop-down menu, select Run pipeline. Building the AMI takes about 20 minutes. Similarly, you can launch the pipeline for the EKS worker node image with the Image pipeline for Ubuntu SEV-SNP Host image on EKS.
When complete, deploy the AWS EC2 bare-metal spot instance. From the root of the directory, this will deploy an M6A instance
cdk deploy RuntimeAttestationComputeStackEUC1 --require-approval never -c useCustomAmi=true -c subnetIndex=0
Use -c instanceClass=m7a
to deploy an M7A instance.
If you want to use the CDK application to deploy the AWS EC2 bare-metal instance with a default Ubuntu image, remove the -c useCustomAmi=true
parameter.
Use
AWS_REGION=REGION cdk ...
to deploy in another region.
From the root of the directory
cdk/bin/connect-to-intance-via-ssm-session.sh bare-metal-0
# Or cdk/bin/connect-to-intance-via-ssm-session.sh bare-metal-0 0 $REGION
You can follow the instructions above in the Validate the Host SEV-SNP Support Section.
Follow the instructions in DEPLOY.md. These instructions create an EKS cluster and a managed node-group with an M6A or M7A metal instance (using a spot deployment). You can then experiment with Kata and confidential containers.
Create a guest operating system image. We use again Ubuntu. From the ~/src/AMDSEV
directory
cd ~/src/AMDSEV
wget -c https://releases.ubuntu.com/noble/ubuntu-24.04.1-live-server-amd64.iso
usr/local/bin/qemu-img create -f qcow2 img-ubuntu-2404.qcow2 42G
# Install Ubuntu
sudo ./launch-qemu.sh -hda img-ubuntu-2404.qcow2 -cdrom ubuntu-24.04.1-live-server-amd64.iso
Edit the menu-entry for Try or Install Ubuntu Server
and make sure you have
set gfxpayload=text
linux /casper/vmlinuz console=tty0 console=ttyS0,115200n8 nosplash ---
initrd /casper/initrd
Now follow the installation process. Nothing special. Reboot at the end. Type C-c, or you might have to kill the QEMU process via SIGTERM (sudo kill -SIGTERM $PID
). If you know how to automate this section, let me know.
sudo ./launch-qemu.sh -hda img-ubuntu-2404.qcow2 -sev-snp
Then, from the guest
sudo dmesg | grep -i snp
sudo dmesg | grep -i sev
sudo apt install -y build-essential
sudo curl --proto '=https' --tlsv1.3 -sSf https://sh.rustup.rs | sh -s -- -y
. "$HOME/.cargo/env"
mkdir -p ~/src
cd ~/src
git clone https://github.com/virtee/snpguest
cd snpguest
cargo build -r
sudo target/release/snpguest ok
The outcome of snpguest
shows
[ PASS ] - SEV: ENABLED
[ PASS ] - SEV-ES: ENABLED
[ PASS ] - SNP: ENABLED
[ PASS ] - Optional Features statuses:
[ PASS ] - VTOM: DISABLED
[ PASS ] - ReflectVC: DISABLED
[ PASS ] - Restricted Injection: DISABLED
[ PASS ] - Alternate Injection: DISABLED
[ PASS ] - Debug Swap: DISABLED
[ PASS ] - Prevent Host IBS: DISABLED
[ PASS ] - SNP BTB Isolation: DISABLED
[ PASS ] - VMPL SSS: DISABLED
[ PASS ] - Secure TSE: DISABLED
[ PASS ] - VMG Exit Parameter: DISABLED
[ PASS ] - IBS Virtualization: DISABLED
[ PASS ] - VMSA Reg Prot: DISABLED
[ PASS ] - SMT Protection: DISABLED
If you want to reuse the kernel used for the host. On the host, in the directory hosting the kernel packages
python3 -m http.server
On the guest (change IP address of guest and kernel packages naming accordingly)
wget -c http://172.31.25.39:8000/linux-image-6.11.0-sev-snp-amd64_6.11.0-1_amd64.deb
wget -c http://172.31.25.39:8000/linux-headers-6.11.0-sev-snp-amd64_6.11.0-1_amd64.deb
# Install
sudo dpkg -i linux-*
cat /proc/cpuinfo | grep -i sev
sudo dmesg | grep -i -E "(ccp|sev)"
cat /sys/module/kvm_amd/parameters/sev
cat /sys/module/kvm_amd/parameters/sev_es
cat /sys/module/kvm_amd/parameters/sev_snp
echo "obase=2;$(sudo rdmsr -d 0xc0010010)" | bc
See AMDESE/AMDSEV#212 (comment) for an overview
On the guest (assuming Genoa
with M7A, use Milan
with M6A)
GEN=Milan
gen=$(echo $GEN | tr '[:upper:]' '[:lower:]')
sudo ./snpguest report report.bin request-file.txt --random
sudo ./snpguest fetch ca -e vcek pem ${gen} certs/ # From the KDS, ark and ask
sudo ./snpguest fetch vcek pem ${gen} certs/ report.bin
sudo curl --proto '=https' --tlsv1.2 \
-sSf https://kdsintf.amd.com/vcek/v1/${GEN}/cert_chain \
-o ./vcek_cert_chain.pem
sudo openssl verify --CAfile ./vcek_cert_chain.pem certs/vcek.pem
sudo ./snpguest verify attestation certs/ report.bin
Not supported
See "Create cert-chain for SNP attestation" in https://github.com/kata-containers/kata-containers/blob/main/docs/how-to/how-to-run-kata-containers-with-SNP-VMs.md
mkdir /tmp/certs
snphost fetch vcek der /tmp/certs
snphost import /tmp/certs /opt/snp/cert_chain.cert
This will clean-up in all regions
cdk destroy RuntimeAttestationComputeStack* --force true
bin/manage-metal-spot-requests.sh delete eu-central-1
cdk destroy RuntimeAttestationImageBuilderStack* --force true
bin/delete-image-builder-images.sh