diff --git a/cc-eventlog/src/lib.rs b/cc-eventlog/src/lib.rs index 50f491cc..496f1e8c 100644 --- a/cc-eventlog/src/lib.rs +++ b/cc-eventlog/src/lib.rs @@ -120,11 +120,15 @@ impl TryFrom for TdxEventLog { .try_into() .ok() .context("invalid digest size")?; + // TCG event logs use 1-based IMR indices (1-4), while TDX RTMRs are 0-based (0-3). + // Standard conversion: TCG IMR 1 → RTMR 0, TCG IMR 2 → RTMR 1, etc. + // + // However, some cloud platforms (notably GCP) may include events with IMR index 0 + // in their CCEL tables. Rather than failing on these, we pass them through as RTMR 0. + // This maintains compatibility with both standard TCG format and GCP's implementation. + let imr = value.imr_index.saturating_sub(1); Ok(TdxEventLog { - imr: value - .imr_index - .checked_sub(1) - .context("invalid imr index")?, + imr, event_type: value.event_type, digest, event: Default::default(), diff --git a/deploy/gcp/README.md b/deploy/gcp/README.md new file mode 100644 index 00000000..86f2a9f6 --- /dev/null +++ b/deploy/gcp/README.md @@ -0,0 +1,87 @@ + + +# Dstack on Google Cloud Platform + +Deploy Dstack with Intel TDX Confidential Computing on GCP. + +## Prerequisites + +- Google Cloud account with billing enabled +- `gcloud` CLI authenticated +- Terraform >= 1.5.0 + +## Supported Machines + +| Type | TEE | Zones | +|------|-----|-------| +| `c3-standard-*` | Intel TDX | us-central1-a/b/c, us-east1-c/d, europe-west4-a/b/c | +| `a3-highgpu-1g` | Intel TDX + H100 | us-central1-a, us-east5-a, europe-west4-c | + +## Quick Start + +```bash +# Authenticate +gcloud auth login +gcloud auth application-default login +gcloud config set project YOUR_PROJECT_ID +gcloud services enable compute.googleapis.com + +# Deploy +cd deploy/gcp/terraform +cp terraform.tfvars.example terraform.tfvars +# Edit terraform.tfvars +terraform init && terraform apply + +# Verify +./test.sh [SSH_KEY] +``` + +## Verify TDX + +```bash +ssh ubuntu@ + +# TDX device +ls -la /dev/tdx_guest + +# Kernel detection +sudo dmesg | grep tdx + +# Memory encryption +sudo dmesg | grep "Memory Encryption" + +# TSM quote +dd if=/dev/zero bs=1 count=64 | sudo tee /sys/kernel/config/tsm/report/com.intel.dcap/inblob >/dev/null +sudo cat /sys/kernel/config/tsm/report/com.intel.dcap/outblob | xxd | head -5 +``` + +## Real vs Simulated + +| Check | Simulated | Real TDX | +|-------|-----------|----------| +| `/dev/tdx_guest` | Missing | Present | +| `dmesg "tdx: Guest"` | Missing | Present | +| TSM provider | N/A | `tdx_guest` | +| Quote TEE type | N/A | `0x81000000` | + +## Limitations + +- No Local SSD (use pd-balanced) +- No live migration +- H100 requires quota request + +## Cost + +| Resource | $/hr | +|----------|------| +| c3-standard-8 | ~$0.40 | +| a3-highgpu-1g | ~$3.50 | + +## Cleanup + +```bash +terraform destroy +``` diff --git a/deploy/gcp/deploy.sh b/deploy/gcp/deploy.sh new file mode 100755 index 00000000..21f143e3 --- /dev/null +++ b/deploy/gcp/deploy.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# SPDX-FileCopyrightText: © 2025 Phala Network +# SPDX-License-Identifier: Apache-2.0 +# Dstack GCP Deployment Script +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TERRAFORM_DIR="$SCRIPT_DIR/terraform" + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +check_prereqs() { + command -v gcloud &>/dev/null || { echo -e "${RED}gcloud CLI not found${NC}"; exit 1; } + command -v terraform &>/dev/null || { echo -e "${RED}Terraform not found${NC}"; exit 1; } + + ACCOUNT=$(gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null) + [ -z "$ACCOUNT" ] && { echo -e "${RED}Not authenticated. Run: gcloud auth login${NC}"; exit 1; } + echo -e "${GREEN}Authenticated as: $ACCOUNT${NC}" +} + +deploy() { + cd "$TERRAFORM_DIR" + + [ ! -f terraform.tfvars ] && { + cp terraform.tfvars.example terraform.tfvars + echo "Edit terraform.tfvars with your project ID, then re-run." + exit 1 + } + + terraform init + terraform plan -out=tfplan + + read -p "Apply? (y/N) " -n 1 -r; echo + [[ $REPLY =~ ^[Yy]$ ]] && terraform apply tfplan && terraform output +} + +destroy() { + cd "$TERRAFORM_DIR" + terraform destroy +} + +case "${1:-deploy}" in + deploy) check_prereqs; deploy ;; + destroy) destroy ;; + *) echo "Usage: $0 [deploy|destroy]"; exit 1 ;; +esac diff --git a/deploy/gcp/terraform/main.tf b/deploy/gcp/terraform/main.tf new file mode 100644 index 00000000..e7abcba2 --- /dev/null +++ b/deploy/gcp/terraform/main.tf @@ -0,0 +1,195 @@ +# SPDX-FileCopyrightText: © 2025 Phala Network +# SPDX-License-Identifier: Apache-2.0 +# Dstack on GCP with Intel TDX +# Supported zones: us-central1-a/b/c, us-east1-c/d, europe-west4-a/b/c, asia-southeast1-a/b/c + +terraform { + required_version = ">= 1.5.0" + required_providers { + google-beta = { + source = "hashicorp/google-beta" + version = "~> 5.0" + } + } +} + +variable "project_id" { + description = "GCP Project ID" + type = string +} + +variable "region" { + type = string + default = "us-central1" +} + +variable "zone" { + type = string + default = "us-central1-a" +} + +variable "machine_type" { + type = string + default = "c3-standard-8" +} + +variable "disk_size_gb" { + type = number + default = 200 +} + +variable "ssh_public_key" { + type = string + default = "" +} + +variable "dstack_version" { + type = string + default = "0.5.2" +} + +provider "google-beta" { + project = var.project_id + region = var.region +} + +resource "google_compute_network" "dstack" { + provider = google-beta + name = "dstack-network" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "dstack" { + provider = google-beta + name = "dstack-subnet" + ip_cidr_range = "10.0.0.0/24" + region = var.region + network = google_compute_network.dstack.id +} + +resource "google_compute_firewall" "dstack" { + provider = google-beta + name = "dstack-allow" + network = google_compute_network.dstack.name + + allow { + protocol = "tcp" + ports = ["22", "9080", "9201", "9204", "9300"] + } + + source_ranges = ["0.0.0.0/0"] + target_tags = ["dstack"] +} + +resource "google_compute_instance" "dstack_vmm" { + provider = google-beta + name = "dstack-vmm" + machine_type = var.machine_type + zone = var.zone + tags = ["dstack"] + + boot_disk { + initialize_params { + image = "ubuntu-os-cloud/ubuntu-2204-lts" + size = var.disk_size_gb + type = "pd-balanced" + } + } + + confidential_instance_config { + confidential_instance_type = "TDX" + enable_confidential_compute = true + } + + scheduling { + on_host_maintenance = "TERMINATE" + automatic_restart = true + } + + network_interface { + subnetwork = google_compute_subnetwork.dstack.id + access_config {} + } + + metadata = { + ssh-keys = var.ssh_public_key != "" ? "ubuntu:${var.ssh_public_key}" : null + } + + metadata_startup_script = <<-EOF + #!/bin/bash + set -e + exec > >(tee /var/log/dstack-startup.log) 2>&1 + + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential curl wget git docker.io jq + + systemctl enable docker && systemctl start docker + usermod -aG docker ubuntu + + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sudo -u ubuntu sh -s -- -y + + cd /home/ubuntu + sudo -u ubuntu git clone https://github.com/Dstack-TEE/dstack.git + mkdir -p vmm-data/{images,run} + chown -R ubuntu:ubuntu vmm-data + + cd vmm-data + wget -q "https://github.com/Dstack-TEE/meta-dstack/releases/download/v${var.dstack_version}/dstack-${var.dstack_version}.tar.gz" || true + [ -f "dstack-${var.dstack_version}.tar.gz" ] && tar -xzf "dstack-${var.dstack_version}.tar.gz" -C images/ && rm "dstack-${var.dstack_version}.tar.gz" + + cd /home/ubuntu/dstack + sudo -u ubuntu /home/ubuntu/.cargo/bin/cargo build --release -p dstack-vmm -p supervisor || true + [ -f target/release/dstack-vmm ] && cp target/release/{dstack-vmm,supervisor} /home/ubuntu/vmm-data/ + + cat > /home/ubuntu/vmm-data/vmm.toml << 'VMCFG' +address = "0.0.0.0:9080" +image_path = "./images" +run_path = "./run/vm" +[cvm] +kms_urls = ["http://127.0.0.1:9201"] +gateway_urls = ["http://127.0.0.1:9204"] +cid_start = 30000 +cid_pool_size = 1000 +[host_api] +port = 9300 +[supervisor] +exe = "./supervisor" +sock = "./run/supervisor.sock" +VMCFG + chown ubuntu:ubuntu /home/ubuntu/vmm-data/vmm.toml + + cat > /etc/systemd/system/dstack-vmm.service << 'SVC' +[Unit] +Description=Dstack VMM +After=network.target docker.service +[Service] +Type=simple +User=ubuntu +WorkingDirectory=/home/ubuntu/vmm-data +ExecStart=/home/ubuntu/vmm-data/dstack-vmm -c vmm.toml +Restart=always +[Install] +WantedBy=multi-user.target +SVC + + [ -f /home/ubuntu/vmm-data/dstack-vmm ] && systemctl daemon-reload && systemctl enable --now dstack-vmm + EOF + + service_account { + scopes = ["cloud-platform"] + } + + allow_stopping_for_update = true +} + +output "ip" { + value = google_compute_instance.dstack_vmm.network_interface[0].access_config[0].nat_ip +} + +output "ssh" { + value = "ssh ubuntu@${google_compute_instance.dstack_vmm.network_interface[0].access_config[0].nat_ip}" +} + +output "verify_tdx" { + value = "ssh ubuntu@${google_compute_instance.dstack_vmm.network_interface[0].access_config[0].nat_ip} 'ls -la /dev/tdx_guest'" +} diff --git a/deploy/gcp/terraform/terraform.tfvars.example b/deploy/gcp/terraform/terraform.tfvars.example new file mode 100644 index 00000000..29a09dc7 --- /dev/null +++ b/deploy/gcp/terraform/terraform.tfvars.example @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: © 2025 Phala Network +# SPDX-License-Identifier: Apache-2.0 +# Copy to terraform.tfvars and edit + +project_id = "your-project-id" +region = "us-central1" +zone = "us-central1-a" # Must support TDX + +machine_type = "c3-standard-8" # c3-* for TDX +disk_size_gb = 200 +dstack_version = "0.5.2" + +# ssh_public_key = "ssh-rsa AAAAB3... user@host" diff --git a/deploy/gcp/test.sh b/deploy/gcp/test.sh new file mode 100755 index 00000000..90caa9c0 --- /dev/null +++ b/deploy/gcp/test.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# SPDX-FileCopyrightText: © 2025 Phala Network +# SPDX-License-Identifier: Apache-2.0 +# Dstack GCP TDX Verification +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +VM_IP="${1:-$VM_IP}" +SSH_KEY="${2:-${SSH_KEY:-$HOME/.ssh/id_rsa}}" +SSH_USER="${SSH_USER:-ubuntu}" + +[ -z "$VM_IP" ] && { echo "Usage: $0 [SSH_KEY]"; exit 1; } + +SSH_CMD="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o ConnectTimeout=10 $SSH_USER@$VM_IP" + +PASSED=0 +FAILED=0 + +run_test() { + local name="$1" cmd="$2" expect="$3" show_output="${4:-false}" + echo -n "Testing: $name... " + result=$($SSH_CMD "$cmd" 2>&1) || true + if echo "$result" | grep -q "$expect"; then + echo -e "${GREEN}PASS${NC}" + [ "$show_output" = "true" ] && echo -e " ${CYAN}$result${NC}" + PASSED=$((PASSED + 1)) + else + echo -e "${RED}FAIL${NC}" + FAILED=$((FAILED + 1)) + fi +} + +echo "VM: $VM_IP | Key: $SSH_KEY" +echo "" + +# Connectivity +run_test "SSH" "echo ok" "ok" + +# TDX Hardware +echo "" +echo "=== TDX Hardware ===" +run_test "TDX device" "test -c /dev/tdx_guest && echo exists" "exists" +run_test "Kernel TDX" "sudo dmesg | grep -m1 'tdx: Guest'" "Guest detected" true +run_test "Memory encryption" "sudo dmesg | grep -m1 'Memory Encryption'" "Intel TDX" true +run_test "CPU flag" "grep -q tdx_guest /proc/cpuinfo && echo yes" "yes" +run_test "TSM provider" "cat /sys/kernel/config/tsm/report/com.intel.dcap/provider 2>/dev/null" "tdx_guest" + +# TSM Quote +echo "" +echo "=== TDX Attestation ===" +echo -n "Testing: TSM quote... " +QUOTE=$($SSH_CMD 'dd if=/dev/zero bs=1 count=64 2>/dev/null | sudo tee /sys/kernel/config/tsm/report/com.intel.dcap/inblob >/dev/null; sudo dd if=/sys/kernel/config/tsm/report/com.intel.dcap/outblob bs=8192 count=1 2>/dev/null | xxd -p | tr -d "\n"' 2>&1) +QLEN=$((${#QUOTE}/2)) +if [ "$QLEN" -gt 1000 ] && [ "${QUOTE:8:8}" = "81000000" ]; then + echo -e "${GREEN}PASS${NC} ($QLEN bytes, TEE=0x${QUOTE:8:8})" + PASSED=$((PASSED + 1)) +else + echo -e "${RED}FAIL${NC}" + FAILED=$((FAILED + 1)) +fi + +# Dstack (optional) +echo "" +echo "=== Dstack APIs ===" +run_test "Docker" "systemctl is-active docker 2>/dev/null" "active" + +echo -n "Testing: guest-agent... " +status=$($SSH_CMD 'pgrep -f dstack-guest-agent >/dev/null && echo running || echo stopped' 2>&1) +if [ "$status" = "running" ]; then + echo -e "${GREEN}PASS${NC}" + PASSED=$((PASSED + 1)) +else + echo -e "${YELLOW}SKIP${NC}" +fi + +echo -n "Testing: GetQuote API... " +quote=$($SSH_CMD '[ -S /var/run/dstack/dstack.sock ] && sudo curl -s --unix-socket /var/run/dstack/dstack.sock "http://localhost/GetQuote" || echo no-socket' 2>&1) +if echo "$quote" | grep -q "040002"; then + size=$(echo "$quote" | python3 -c "import sys,json; print(len(json.load(sys.stdin).get('quote',''))//2)" 2>/dev/null) + echo -e "${GREEN}PASS${NC} ($size bytes)" + PASSED=$((PASSED + 1)) +elif echo "$quote" | grep -q "no-socket"; then + echo -e "${YELLOW}SKIP${NC}" +else + echo -e "${RED}FAIL${NC}" + FAILED=$((FAILED + 1)) +fi + +echo -n "Testing: DeriveKey API... " +key=$($SSH_CMD '[ -S /var/run/dstack/tappd.sock ] && sudo curl -s --unix-socket /var/run/dstack/tappd.sock -X POST "http://localhost/prpc/Tappd.DeriveKey" -H "Content-Type: application/json" -d "{\"path\":\"/test\"}" || echo no-socket' 2>&1) +if echo "$key" | grep -q "PRIVATE KEY"; then + echo -e "${GREEN}PASS${NC}" + PASSED=$((PASSED + 1)) +elif echo "$key" | grep -q "no-socket"; then + echo -e "${YELLOW}SKIP${NC}" +else + echo -e "${RED}FAIL${NC}" + FAILED=$((FAILED + 1)) +fi + +# Summary +echo "" +echo "=== Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC} ===" +[ $FAILED -eq 0 ] && echo -e "${GREEN}Intel TDX verified${NC}" || exit 1 diff --git a/kms/auth-eth/hardhat.config.ts b/kms/auth-eth/hardhat.config.ts index dd7c660c..10203808 100644 --- a/kms/auth-eth/hardhat.config.ts +++ b/kms/auth-eth/hardhat.config.ts @@ -40,6 +40,11 @@ const config: HardhatUserConfig = { url: 'https://mainnet.base.org', accounts: [PRIVATE_KEY], }, + 'base-sepolia': { + url: 'https://base-sepolia-rpc.publicnode.com', + accounts: [PRIVATE_KEY], + chainId: 84532, + }, test: { url: process.env.RPC_URL || 'http://127.0.0.1:8545/', accounts: [PRIVATE_KEY],