Skip to content

Commit

Permalink
Coconut-SVSM support to invoke AMD-ES/AMD-SEV for setting up the hos…
Browse files Browse the repository at this point in the history
…t, launching the guest (directly setting up snpguest in the guest VM), and performing attestation & measurement verification (using igvmmeasure vs snpguest)
  • Loading branch information
ramagali24 committed Sep 18, 2024
1 parent 4419036 commit 1ccfc77
Showing 1 changed file with 172 additions and 43 deletions.
215 changes: 172 additions & 43 deletions tools/snp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ GENERATED_INITRD_BIN="${SETUP_WORKING_DIR}/initrd.img"
AMDSEV_URL="https://github.com/ryansavino/AMDSEV.git"
AMDSEV_DEFAULT_BRANCH="snp-latest-fixes"
AMDSEV_NON_UPM_BRANCH="snp-non-upm"
AMDSEV_SVSM_URL="https://github.com/ramagali24/AMDSEV-SVSM.git"
AMDSEV_SVSM_BRANCH="svsm-latest-fixes"
SNPGUEST_URL="https://github.com/virtee/snpguest.git"
SNPGUEST_BRANCH="tags/v0.7.1"
NASM_SOURCE_TAR_URL="https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/nasm-2.16.01.tar.gz"
Expand All @@ -116,6 +118,7 @@ usage() {
>&2 echo " stop-guests Stop all SNP guests started by this script"
>&2 echo " where OPTIONS are:"
>&2 echo " -n|--non-upm Build AMDSEV non UPM kernel (sev-snp-devel)"
>&2 echo " -s|--svsm Build coconut-svsm components, launch guest and verify attestation & measurement"
>&2 echo " -i|--image Path to existing image file"
>&2 echo " -h|--help Usage information"

Expand Down Expand Up @@ -524,10 +527,14 @@ save_binary_paths() {
# Save binary paths in source file
cat > "${SETUP_WORKING_DIR}/source-bins" <<EOF
QEMU_BIN="${SETUP_WORKING_DIR}/AMDSEV/qemu/build/qemu-system-x86_64"
OVMF_BIN="${SETUP_WORKING_DIR}/AMDSEV/ovmf/Build/AmdSev/DEBUG_GCC5/FV/OVMF.fd"
INITRD_BIN="${GENERATED_INITRD_BIN}"
KERNEL_BIN="${guest_kernel}"
EOF
if "$SVSM"; then
echo "IGVM_FILE=\"${SETUP_WORKING_DIR}/AMDSEV/svsm/bin/coconut-qemu.igvm\"" >> "${SETUP_WORKING_DIR}/source-bins"
else
echo "OVMF_BIN=\"${SETUP_WORKING_DIR}/AMDSEV/ovmf/Build/AmdSev/DEBUG_GCC5/FV/OVMF.fd\"" >> "${SETUP_WORKING_DIR}/source-bins"
fi
}

copy_launch_binaries() {
Expand All @@ -545,16 +552,24 @@ copy_launch_binaries() {

# Copy the setup generated bins to the guest launch directory
# initrd is copied after the first guest boot and is scp-ed off
cp "${OVMF_BIN}" "${LAUNCH_WORKING_DIR}"
if "$SVSM"; then
cp "${IGVM_FILE}" "${LAUNCH_WORKING_DIR}"
else
cp "${OVMF_BIN}" "${LAUNCH_WORKING_DIR}"
fi
#cp "${INITRD_BIN}" "${LAUNCH_WORKING_DIR}"
cp "${KERNEL_BIN}" "${LAUNCH_WORKING_DIR}"

# Save binary paths in source file
cat > "${LAUNCH_WORKING_DIR}/source-bins" <<EOF
OVMF_BIN="${LAUNCH_WORKING_DIR}/$(basename "${OVMF_BIN}")"
INITRD_BIN="${LAUNCH_WORKING_DIR}/$(basename "${INITRD_BIN}")"
KERNEL_BIN="${LAUNCH_WORKING_DIR}/$(basename "${KERNEL_BIN}")"
EOF
if "$SVSM"; then
echo "IGVM_FILE=\"${LAUNCH_WORKING_DIR}/$(basename "${IGVM_FILE}")\"" >> "${LAUNCH_WORKING_DIR}/source-bins"
else
echo "OVMF_BIN=\"${LAUNCH_WORKING_DIR}/$(basename "${OVMF_BIN}")\"" >> "${LAUNCH_WORKING_DIR}/source-bins"
fi
}

add_qemu_cmdline_opts() {
Expand Down Expand Up @@ -646,9 +661,16 @@ set_acl_for_sev_device() {
echo "${setfacl_command}" | sudo tee -a "${rc_local_file}" >/dev/null
}

build_and_install_amdsev() {
build_and_install_amdsev() {

local amdsev_branch="${1:-${AMDSEV_DEFAULT_BRANCH}}"


if "$SVSM"; then
AMDSEV_URL=${AMDSEV_SVSM_URL}
amdsev_branch="${1:-${AMDSEV_SVSM_BRANCH}}"
else
local amdsev_branch="${1:-${AMDSEV_DEFAULT_BRANCH}}"
fi
# Create directory
mkdir -p "${SETUP_WORKING_DIR}"

Expand Down Expand Up @@ -786,10 +808,17 @@ setup_and_launch_guest() {
#add_qemu_cmdline_opts "-machine pc-q35-7.1"

# snp object and kernel-hashes on
add_qemu_cmdline_opts "-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,kernel-hashes=on"
if "$SVSM"; then
add_qemu_cmdline_opts "-object memory-backend-memfd,size=2G,id=mem0,share=true,prealloc=false,reserve=off"
add_qemu_cmdline_opts "-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,init-flags=5,igvm-file="$IGVM_FILE""
else
add_qemu_cmdline_opts "-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,kernel-hashes=on"
fi

# ovmf, initrd, kernel and append options
add_qemu_cmdline_opts "-bios ${OVMF_BIN}"
if "$SVSM"; then
add_qemu_cmdline_opts "-bios ${OVMF_BIN}"
fi
add_qemu_cmdline_opts "-initrd ${INITRD_BIN}"
add_qemu_cmdline_opts "-kernel ${KERNEL_BIN}"
add_qemu_cmdline_opts "-append \"${GUEST_KERNEL_APPEND}\""
Expand Down Expand Up @@ -1026,7 +1055,7 @@ get_cpu_code_name() {
esac
}

generate_snp_expected_measurement() {
generate_snp_expected_measurement() {
# Get ovmf, kernel, initrd paths
# Get vcpu type and kernel append command line
local ovmf_path=$(cat "${QEMU_CMDLINE_FILE}" \
Expand Down Expand Up @@ -1073,50 +1102,141 @@ generate_snp_expected_measurement() {
{ >&2 echo -e "sev-snp-measure return value is empty"; return 1; }
echo ${measurement}
}

attest_guest() {
local cpu_code_name=$(get_cpu_code_name)

# Install the sev-guest module
ssh_guest_command "sudo insmod /lib/modules/*/kernel/drivers/virt/coco/sev-guest/sev-guest.ko >/dev/null 2>&1 || true"

# Request and display the snp attestation report with random data
ssh_guest_command "sudo ./snpguest report attestation-report.bin request-data.txt --random"
ssh_guest_command "./snpguest display report attestation-report.bin"
local cpu_code_name=$(get_cpu_code_name)
vmpl_flag=""
if "$SVSM"; then
vmpl_flag="--vmpl 3"
fi
# Install the sev-guest module
ssh_guest_command "sudo insmod /lib/modules/*/kernel/drivers/virt/coco/sev-guest/sev-guest.ko >/dev/null 2>&1 || true"

# Request and display the snp attestation report with random data
ssh_guest_command "sudo ./snpguest report attestation-report.bin request-data.txt --random $vmpl_flag"
ssh_guest_command "./snpguest display report attestation-report.bin"

# Retrieve ark, ask, vcek (saved in ./certs)
ssh_guest_command "./snpguest fetch ca pem ${cpu_code_name} ."
ssh_guest_command "./snpguest fetch vcek pem ${cpu_code_name} . attestation-report.bin"

# Verifies that ARK, ASK and VCEK are all properly signed
ssh_guest_command "./snpguest verify certs ."

# Verifies the attestation-report trusted compute base matches vcek
ssh_guest_command "./snpguest verify attestation . attestation-report.bin"

# Use appropriate measurement function based on SVSM
if "$SVSM"; then
local expected_measurement=$(generate_svsm_expected_measurement)
echo -e "\nExpected Measurement (igvmmeasure): ${expected_measurement}"
else
local expected_measurement=$(generate_snp_expected_measurement)
echo -e "\nExpected Measurement (sev-snp-measure): ${expected_measurement}"
fi

# Retrieve ark, ask, vcek (saved in ./certs)
ssh_guest_command "./snpguest fetch ca pem ${cpu_code_name} . --endorser vcek"
ssh_guest_command "./snpguest fetch vcek pem ${cpu_code_name} . attestation-report.bin"
# Parse the measurement out of the snp report
local snpguest_report_measurement=$(ssh_guest_command \
"./snpguest display report attestation-report.bin | \
tr '\n' ' ' | \
sed 's|.*Measurement:\(.*\)Host Data.*|\1|g' | \
sed 's| ||g'")

# Remove any special characters and print the value
snpguest_report_measurement=$(echo "$snpguest_report_measurement" | sed 's/[^[:print:]\t]//g')
echo -e "Measurement from SNP Attestation Report: ${snpguest_report_measurement}\n"

# Compare the expected measurement to the guest report measurement
if [ "${expected_measurement}" = "${snpguest_report_measurement}" ]; then
echo -e "The expected measurement matches the snp guest report measurement!"
else
>&2 echo -e "FAIL: measurements do not match"
return 1
fi
}

# Verifies that ARK, ASK and VCEK are all properly signed
ssh_guest_command "./snpguest verify certs ."
setup_svsm_guest_attestation() {
# Define the guest command to be executed
local guest_command=$(cat <<'EOF'
# Update and install necessary packages
echo 'Updating package list...'
sudo apt-get update
echo 'Installing necessary packages...'
sudo apt-get install -y git build-essential libtss2-dev tpm2-tools
# Install Rust
echo 'Installing Rust...'
source "$HOME/.cargo/env" 2>/dev/null || true
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env" 2>/dev/null
# Create the 'tools' directory if it does not exist
if [ ! -d "tools" ]; then
echo 'Directory tools does not exist. Creating it...'
mkdir tools
else
echo 'Directory tools already exists.'
fi
# Change to 'tools' directory
cd tools || { echo 'Failed to change directory to tools'; exit 1; }
# Check if 'snpguest' directory exists and remove it if it does
if [ -d "snpguest" ]; then
echo 'Directory snpguest exists. Removing it...'
rm -rf snpguest
else
echo 'Directory snpguest does not exist.'
fi
# Clone the repository into the 'tools' directory
git clone https://github.com/virtee/snpguest.git
# Build the project inside the 'snpguest' directory
cd snpguest || { echo 'Failed to change directory to snpguest'; exit 1; }
source "$HOME/.cargo/env"
cargo build --release
# Copy the built binary to the home directory
cp target/release/snpguest "$HOME/"
EOF
)

# Verifies the attestation-report trusted compute base matches vcek
ssh_guest_command "./snpguest verify attestation . attestation-report.bin"
# Pass the guest command to ssh_guest_command
ssh_guest_command "$guest_command"
}

# Use sev-snp-measure utility to calculate the expected measurement
local expected_measurement=$(generate_snp_expected_measurement)
echo -e "\nExpected Measurement (sev-snp-measure): ${expected_measurement}"
generate_svsm_expected_measurement() {
# Change directory to where igvmmeasure is located
if ! cd "$SETUP_WORKING_DIR/AMDSEV/svsm/target/x86_64-unknown-linux-gnu/debug"; then
echo 'Failed to change directory'
exit 1
fi

# Parse the measurement out of the snp report
local snpguest_report_measurement=$(ssh_guest_command \
"./snpguest display report attestation-report.bin \
| tr '\n' ' ' \
| sed \"s|.*Measurement:\(.*\)Host Data.*|\1\n|g\" \
| sed \"s| ||g\"")
# Run the igvmmeasure command and capture its output
local output
if ! output=$(./igvmmeasure --check-kvm "$SETUP_WORKING_DIR/AMDSEV/svsm/bin/coconut-qemu.igvm" measure 2>&1); then
echo 'Failed to run igvmmeasure'
echo "Output: $output"
exit 1
fi

# Remove any special characters and print the value
snpguest_report_measurement=$(echo ${snpguest_report_measurement} | sed $'s/[^[:print:]\t]//g')
echo -e "Measurement from SNP Attestation Report: ${snpguest_report_measurement}\n"
# Extract the Launch Digest from the output
local svsm_measurement
svsm_measurement=$(echo "$output" | awk '/Launch Digest:/ {print $3}')

# Compare the expected measurement to the guest report measurement
[[ "${expected_measurement}" == "${snpguest_report_measurement}" ]] \
&& echo -e "The expected measurement matches the snp guest report measurement!" \
|| { >&2 echo -e "FAIL: measurements do not match"; return 1; }
# Check if measurement was extracted
if [ -z "$svsm_measurement" ]; then
echo 'Failed to extract measurement'
echo "Output: $output"
exit 1
fi
# Convert svsm_measurement to lowercase
svsm_measurement=$(echo "$svsm_measurement" | tr '[:upper:]' '[:lower:]')
echo ${svsm_measurement}
}



###############################################################################

# Main
Expand Down Expand Up @@ -1148,6 +1268,10 @@ main() {
SKIP_IMAGE_CREATE=true
shift; shift
;;
-s|--svsm)
SVSM=true
shift
;;

setup-host)
COMMAND="setup-host"
Expand Down Expand Up @@ -1182,7 +1306,7 @@ main() {
;;
esac
done

# Set SETUP_WORKING_DIR for non-upm
if ! $UPM; then
SETUP_WORKING_DIR="${SETUP_WORKING_DIR}/non-upm"
Expand Down Expand Up @@ -1235,12 +1359,17 @@ main() {
;;

attest-guest)
if "$SVSM"; then
wait_and_retry_command verify_snp_guest
setup_svsm_guest_attestation
else
install_rust
install_sev_snp_measure
install_dependencies
wait_and_retry_command verify_snp_guest
setup_guest_attestation
attest_guest
fi
attest_guest
;;

stop-guests)
Expand Down

0 comments on commit 1ccfc77

Please sign in to comment.