221 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/bin/bash
 | 
						|
 | 
						|
# SPDX-License-Identifier: Apache-2.0
 | 
						|
 | 
						|
set -e
 | 
						|
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"
 | 
						|
 | 
						|
echo_blue() {
 | 
						|
  echo -e "\033[34m$1\033[0m"
 | 
						|
}
 | 
						|
 | 
						|
echo_yellow() {
 | 
						|
  echo -e "\033[0;33m$1\033[0m"
 | 
						|
}
 | 
						|
 | 
						|
echo_red() {
 | 
						|
  echo -e "\033[0;31m$1\033[0m"
 | 
						|
}
 | 
						|
 | 
						|
if [[ -z "${SERVER_ADDR}" ]]; then
 | 
						|
  echo_red "The SERVER_ADDR environment variable is needed."
 | 
						|
  exit 1
 | 
						|
fi
 | 
						|
 | 
						|
if [[ -z "${MEASUREMENT}" ]]; then
 | 
						|
  echo_red "The MEASUREMENT environment variable is needed."
 | 
						|
  exit 1
 | 
						|
fi
 | 
						|
 | 
						|
if [[ -z "${VM_HOSTNAME}" ]]; then
 | 
						|
  VM_HOSTNAME="detee-vm"
 | 
						|
fi
 | 
						|
 | 
						|
server="$SERVER_ADDR"
 | 
						|
ssh_pubkey_dir="${HOME}/.detee/cli/vms/ssh"
 | 
						|
cert_dir="${HOME}/.detee/cli/vms/certs"
 | 
						|
snp_reports_dir="${HOME}/.detee/cli/vms/snpreports"
 | 
						|
keyfile_dir="${HOME}/.detee/cli/vms/secrets"
 | 
						|
keyfile="${keyfile_dir}/${server}"
 | 
						|
mkdir -p "$ssh_pubkey_dir"
 | 
						|
mkdir -p "$cert_dir"
 | 
						|
mkdir -p "$snp_reports_dir"
 | 
						|
mkdir -p "$keyfile_dir"
 | 
						|
server_crt="${cert_dir}/${server}.crt"
 | 
						|
server_report="${snp_reports_dir}/${server}-report.bin"
 | 
						|
 | 
						|
mkdir -p "$(dirname "$keyfile")"
 | 
						|
if [[ -f "$keyfile" ]]; then
 | 
						|
  echo_yellow "Found keyfile $keyfile"
 | 
						|
else
 | 
						|
  echo_yellow "Creating keyfile $keyfile"
 | 
						|
  dd if=/dev/urandom "of=$keyfile" bs=32 count=4
 | 
						|
fi
 | 
						|
 | 
						|
snpguest --help > /dev/null \
 | 
						|
  || {
 | 
						|
  echo_yellow "Please install https://github.com/virtee/snpguest"
 | 
						|
  exit 3
 | 
						|
}
 | 
						|
 | 
						|
try_countdown=20;
 | 
						|
echo -n Trying $server
 | 
						|
while [[ $try_countdown -gt 0 ]]; do
 | 
						|
  echo -n .
 | 
						|
  curl --max-time 1 -k "https://$server" > /dev/null 2>&1 && break
 | 
						|
  sleep 1
 | 
						|
  ((try_countdown--))
 | 
						|
done
 | 
						|
echo
 | 
						|
 | 
						|
openssl s_client -connect "$server" </dev/null \
 | 
						|
  | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > "$server_crt"
 | 
						|
openssl x509 -in "$server_crt" -noout
 | 
						|
crt_hash=$(openssl dgst -sha3-512 < "$server_crt" | awk '{ print $2}')
 | 
						|
 | 
						|
echo_blue "The certificate hash is $crt_hash"
 | 
						|
 | 
						|
echo_blue "Getting hash from attestation report..."
 | 
						|
ip=$(echo "$server" | cut -d ":" -f1)
 | 
						|
port=$(echo "$server" | cut -d ":" -f2)
 | 
						|
echo "$server" | grep -F '[' | grep -F ']' && {
 | 
						|
  ip=$(echo "$server" | grep -oE '\[[a-f0-9\:]*\]')
 | 
						|
  port=$(echo "$server" | grep -oE '\]:[0-9]+' | cut -d ':' -f2)
 | 
						|
}
 | 
						|
 | 
						|
curl --cacert "$server_crt" \
 | 
						|
  --resolve dtrfs-api:${port}:${ip} \
 | 
						|
  "https://dtrfs-api:${port}/report" > "${server_report}.base64"
 | 
						|
 | 
						|
cat "${server_report}.base64" | basenc --base64url -d > "$server_report"
 | 
						|
rm "${server_report}.base64" 
 | 
						|
 | 
						|
report_crt_hash=$( snpguest display report "$server_report" \
 | 
						|
  | grep "Report Data" -A 4 | tail -4 | tr '\n' ' ' | sed 's/\s//g')
 | 
						|
 | 
						|
echo_blue "The hash in the report is $report_crt_hash"
 | 
						|
 | 
						|
if [[ "$crt_hash" != "$report_crt_hash" ]]; then
 | 
						|
  echo The hash of the certificate does not match the hash from the report. Exiting.
 | 
						|
  exit 2
 | 
						|
fi
 | 
						|
 | 
						|
echo_blue "The hash matches!"
 | 
						|
echo_blue "Verifying AMD signature in attestation report..."
 | 
						|
 | 
						|
chip_id_hash=$( snpguest display report "$server_report" \
 | 
						|
  | grep "Chip ID:" -A 4 | tail -3 | tr '\n' ' ' | sed 's/\s//g' \
 | 
						|
  | md5sum | awk '{ print $1 }')
 | 
						|
tcb_hash=$(grep -e "Committed TCB" -e "Reported TCB" -A 10 "$server_report" |
 | 
						|
  md5sum | awk '{ print $1 }')
 | 
						|
vcek_path="${cert_dir}/${chip_id_hash}-${tcb_hash}.vcek.pem"
 | 
						|
amd_certs_dir="${cert_dir}/amd_certs_${server}"
 | 
						|
mkdir -p "$amd_certs_dir"
 | 
						|
 | 
						|
# TODO: add support for Genoa, Bergamo, Siena and Turin
 | 
						|
# Or at least for Genoa...
 | 
						|
[[ -f "${cert_dir}/ask-milan.pem" ]] || {
 | 
						|
  snpguest fetch ca pem milan "$amd_certs_dir" --endorser vcek
 | 
						|
  mv "${amd_certs_dir}/ask.pem" "${cert_dir}/ask-milan.pem" 
 | 
						|
  mv "${amd_certs_dir}/ark.pem" "${cert_dir}/ark-milan.pem" 
 | 
						|
}
 | 
						|
ln -fs "${cert_dir}/ask-milan.pem" "${amd_certs_dir}/ask.pem" 
 | 
						|
ln -fs "${cert_dir}/ark-milan.pem" "${amd_certs_dir}/ark.pem" 
 | 
						|
[[ -f "${vcek_path}" ]] || {
 | 
						|
  snpguest fetch vcek pem milan "$amd_certs_dir" "$server_report" || {
 | 
						|
    # You are probably wondering what this weird shit is doing here.
 | 
						|
    # The AMD API for VCEK has throttling, and this scripts needs to run in parallel.
 | 
						|
    sleep 10
 | 
						|
    [[ -f "${vcek_path}" ]] || {
 | 
						|
      snpguest fetch vcek pem milan "$amd_certs_dir" "$server_report"
 | 
						|
    }
 | 
						|
  }
 | 
						|
  mv "${amd_certs_dir}/vcek.pem" "${vcek_path}" 
 | 
						|
}
 | 
						|
ln -fs "${vcek_path}" "${amd_certs_dir}/vcek.pem" 
 | 
						|
snpguest verify certs "$amd_certs_dir"
 | 
						|
snpguest verify attestation "$amd_certs_dir" "$server_report"
 | 
						|
 | 
						|
echo_yellow "The attestation got verified based on the CA from AMD for the Milan generation!"
 | 
						|
echo_blue "Verifying if measurement is $MEASUREMENT..."
 | 
						|
 | 
						|
guest_measurement=$( snpguest display report "$server_report" \
 | 
						|
  | grep Measurement -A 3 | tail -3 | tr '\n' ' ' | sed 's/\s//g' )
 | 
						|
 | 
						|
echo_blue "The guests's measurement is $guest_measurement"
 | 
						|
 | 
						|
if [[ "$guest_measurement" != "$MEASUREMENT" ]]; then
 | 
						|
  echo_red "The measurement of the server does not match."
 | 
						|
  echo_yellow "Please use this project to get your measurement: https://github.com/virtee/sev-snp-measure"
 | 
						|
  echo_yellow "After that, please sepcify the measurement ast the MEASUREMENT environment variable."
 | 
						|
  exit 2
 | 
						|
fi
 | 
						|
 | 
						|
echo_yellow "The measurement matched!"
 | 
						|
 | 
						|
echo_blue "Creating ed25519 signature..."
 | 
						|
ed25519_signature=$(detee-cli account sign "$server_crt" || exit 1)
 | 
						|
 | 
						|
if [[ -n "$DETEE_INSTALL_URL" ]] && [[ -n "$DETEE_INSTALL_URL" ]]; then 
 | 
						|
  echo_blue "Injecting installation url: ${DETEE_INSTALL_URL}"
 | 
						|
  curl --fail-with-body \
 | 
						|
    -H "ed25519-signature: ${ed25519_signature}" \
 | 
						|
    --cacert "$server_crt" \
 | 
						|
    --resolve "dtrfs-api:${port}:${ip}" \
 | 
						|
    --data-urlencode "url=${DETEE_INSTALL_URL}" \
 | 
						|
    -d "sha=${DETEE_INSTALL_SHA}" \
 | 
						|
    -d "keyfile=$(cat "$keyfile" | basenc --base64url -w 0)" \
 | 
						|
    -d "hostname=${VM_HOSTNAME}" \
 | 
						|
    "https://dtrfs-api:${port}/install" || exit 1
 | 
						|
else 
 | 
						|
  echo
 | 
						|
  echo_blue "Injecting keyfile..."
 | 
						|
  curl --fail-with-body \
 | 
						|
    -H "ed25519-signature: ${ed25519_signature}" \
 | 
						|
    --cacert "$server_crt" \
 | 
						|
    --resolve "dtrfs-api:${port}:${ip}" \
 | 
						|
    -d "keyfile=$(cat "$keyfile" | basenc --base64url -w 0)" \
 | 
						|
    "https://dtrfs-api:${port}/decrypt" || exit 1
 | 
						|
fi
 | 
						|
 | 
						|
[[ -z "$SSH_KEY_FILE" ]] && SSH_KEY_FILE="$HOME/.ssh/id_ed25519.pub"
 | 
						|
echo
 | 
						|
echo_blue "Injecting SSH key from file ${SSH_KEY_FILE}"
 | 
						|
curl --fail-with-body \
 | 
						|
  -H "ed25519-signature: ${ed25519_signature}" \
 | 
						|
  --cacert "$server_crt" \
 | 
						|
  --resolve "dtrfs-api:${port}:${ip}" \
 | 
						|
  --data-urlencode "ssh_key=$(cat ${SSH_KEY_FILE})" \
 | 
						|
  "https://dtrfs-api:${port}/authorized_keys" || exit 1
 | 
						|
 | 
						|
echo
 | 
						|
echo_blue "Downloading SSH keys from server"
 | 
						|
curl --fail-with-body \
 | 
						|
  -H "ed25519-signature: ${ed25519_signature}" \
 | 
						|
  --cacert "$server_crt" \
 | 
						|
  --resolve "dtrfs-api:${port}:${ip}" \
 | 
						|
  "https://dtrfs-api:${port}/server_ssh_pubkeys" > "${ssh_pubkey_dir}/${server}" || {
 | 
						|
    echo_red "Could not grab SSH pubkeys from the server. This could be a MITM attack. Error:"
 | 
						|
    cat "${ssh_pubkey_dir}/${server}"
 | 
						|
    echo
 | 
						|
    echo_yellow "SSH at your own risk. IP: ${ip} Port: ${port} User: root"
 | 
						|
    curl -X POST --cacert "$server_crt" \
 | 
						|
      -H "ed25519-signature: ${ed25519_signature}" \
 | 
						|
      --resolve dtrfs-api:${port}:${ip} \
 | 
						|
      "https://dtrfs-api:${port}/switch_root" 2>/dev/null
 | 
						|
    exit 1
 | 
						|
  }
 | 
						|
[[ "$port" != "22" ]] && ssh_keygen_ip="[$ip]:$port" || ssh_keygen_ip="$ip"
 | 
						|
ssh-keygen -R "$ssh_keygen_ip" -f "${HOME}/.ssh/known_hosts2" || true
 | 
						|
cat "${ssh_pubkey_dir}/${server}" |
 | 
						|
  awk '{ print $1 " " $2 }' |
 | 
						|
  xargs -I {} echo "$ssh_keygen_ip" {} >> "${HOME}/.ssh/known_hosts2"
 | 
						|
 | 
						|
echo
 | 
						|
echo_blue "Starting guest OS. Give it a few seconds and try to SSH into the guest."
 | 
						|
# this command is supposed to fail; it kills the API in the initrd
 | 
						|
curl -X POST --cacert "$server_crt" \
 | 
						|
  -H "ed25519-signature: ${ed25519_signature}" \
 | 
						|
  --resolve dtrfs-api:${port}:${ip} \
 | 
						|
  "https://dtrfs-api:${port}/switch_root" 2>/dev/null || echo Success.
 |