Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 179 additions & 6 deletions aci-samples/visual-attestation-demo-v2/Deploy-VisualAttestationV2.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,156 @@ function Wait-ForContainer {
return $false
}

function New-ComparePage {
param(
[string]$ConfidentialUrl,
[string]$StandardUrl,
[string]$OutputPath
)

$html = @"
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ACI Attestation Compare</title>
<style>
:root {
--bg: #0b1020;
--panel: #131a2f;
--text: #f3f6ff;
--muted: #9fb0d3;
--ok: #2ac769;
--warn: #ffb020;
--border: #2a3558;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Segoe UI, Helvetica, Arial, sans-serif;
background: radial-gradient(1200px 500px at 20% -10%, #1f2a4a 0%, var(--bg) 60%);
color: var(--text);
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
background: rgba(11, 16, 32, 0.8);
backdrop-filter: blur(6px);
}
h1 {
margin: 0 0 6px 0;
font-size: 18px;
font-weight: 700;
}
.meta {
color: var(--muted);
font-size: 13px;
display: flex;
gap: 14px;
flex-wrap: wrap;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
padding: 12px;
flex: 1;
min-height: 0;
}
.card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
}
.bar {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
font-size: 13px;
}
.bar .left {
font-weight: 700;
}
.pill {
display: inline-block;
border-radius: 999px;
padding: 2px 8px;
font-size: 12px;
font-weight: 700;
}
.pill.ok {
background: rgba(42, 199, 105, 0.18);
color: var(--ok);
border: 1px solid rgba(42, 199, 105, 0.35);
}
.pill.warn {
background: rgba(255, 176, 32, 0.18);
color: var(--warn);
border: 1px solid rgba(255, 176, 32, 0.35);
}
iframe {
border: 0;
width: 100%;
flex: 1;
min-height: 0;
background: #fff;
}
@media (max-width: 1100px) {
.grid { grid-template-columns: 1fr; }
.card { min-height: 55vh; }
}
</style>
</head>
<body>
<header>
<h1>ACI Runtime Attestation Compare</h1>
<div class="meta">
<span>Left: Confidential SKU (expected attestation success)</span>
<span>Right: Standard SKU (expected attestation failure)</span>
</div>
</header>

<main class="grid">
<section class="card">
<div class="bar">
<div class="left">Confidential</div>
<div>
<span class="pill ok">SEV-SNP expected</span>
<span>$ConfidentialUrl</span>
</div>
</div>
<iframe src="$ConfidentialUrl" title="Confidential ACI"></iframe>
</section>

<section class="card">
<div class="bar">
<div class="left">Standard</div>
<div>
<span class="pill warn">Expected /dev/sev-guest missing</span>
<span>$StandardUrl</span>
</div>
</div>
<iframe src="$StandardUrl" title="Standard ACI"></iframe>
</section>
</main>
</body>
</html>
"@

Set-Content -Path $OutputPath -Value $html -Encoding UTF8
}

# ============================================================================
# Deploy phase (single container group)
# ============================================================================
Expand Down Expand Up @@ -362,15 +512,38 @@ function Invoke-Compare {
Wait-ForContainer -Fqdn $fqdn_std | Out-Null

Write-Header "Both containers deployed"
Write-Host "Confidential (attestation succeeds): http://$fqdn_conf"
Write-Host "Standard (attestation FAILS) : http://$fqdn_std"

# Retrieve SKU info
$confSku = az container show -g $cfg.resourceGroup -n $name_conf --query "sku" -o tsv
$stdSku = az container show -g $cfg.resourceGroup -n $name_std --query "sku" -o tsv

# Output summary table
Write-Host ""
Write-Host "╔════════════════════════════════════════════════════════════════╗"
Write-Host "║ Compare Deployment Summary ║"
Write-Host "╠════════════════════════════════════════════════════════════════╣"
Write-Host "║ Confidential SKU (Attestation succeeds) ║"
Write-Host "║ SKU: $($confSku.PadRight(54)) ║"
Write-Host "║ URL: http://$($fqdn_conf.PadRight(49)) ║"
Write-Host "╠════════════════════════════════════════════════════════════════╣"
Write-Host "║ Standard SKU (Attestation FAILS) ║"
Write-Host "║ SKU: $($stdSku.PadRight(54)) ║"
Write-Host "║ URL: http://$($fqdn_std.PadRight(49)) ║"
Write-Host "╚════════════════════════════════════════════════════════════════╝"
Write-Host ""
Write-Host "Click 'Attest' on each pane to see the difference:"
Write-Host " • Confidential: Hardware-rooted SEV-SNP attestation succeeds"
Write-Host " • Standard: Attestation fails (no /dev/sev-guest)"
Write-Host ""
Write-Host "Open both side-by-side, click 'Run Attestation' on each, and compare."

if (-not $SkipBrowser) {
Start-Process "http://$fqdn_conf"
Start-Sleep -Seconds 1
Start-Process "http://$fqdn_std"
$confUrl = "http://$fqdn_conf"
$stdUrl = "http://$fqdn_std"
$comparePage = Join-Path $PSScriptRoot 'side-by-side-compare.html'
New-ComparePage -ConfidentialUrl $confUrl -StandardUrl $stdUrl -OutputPath $comparePage
Write-Success "Generated: $comparePage"
Write-Host "Opening in new browser window..."
Start-Process $comparePage
}
}

Expand Down
13 changes: 11 additions & 2 deletions aci-samples/visual-attestation-demo-v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ prints the last 25 lines of container logs for inspection.
./Deploy-VisualAttestationV2.ps1 -Compare
```

Deploys both flavors in the same resource group and opens both URLs. Same
image, same code path, opposite result - the cleanest way to demo why
Deploys both flavors in the same resource group and opens a generated
`side-by-side-compare.html` page that embeds both live endpoints in one view.
Same image, same code path, opposite result - the cleanest way to demo why
attestation matters.

### 5. Cleanup
Expand Down Expand Up @@ -172,6 +173,14 @@ contrast - it proves the success case really did need confidential hardware.

![Standard SKU - attestation fails - /dev/sev-guest absent on non-CC SKU](images/screenshot-standard.png)

### Side-by-side compare view

The compare page below is generated automatically by
`Deploy-VisualAttestationV2.ps1 -Compare` and opens both live deployments in
one browser window.

![Confidential and Standard side-by-side compare view](images/screenshot-side-by-side.png)

## Why two SKUs?

The point of this sample is **falsifiability**. A demo that only ever shows
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
137 changes: 137 additions & 0 deletions aci-samples/visual-attestation-demo-v2/side-by-side-compare.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ACI Attestation Compare</title>
<style>
:root {
--bg: #0b1020;
--panel: #131a2f;
--text: #f3f6ff;
--muted: #9fb0d3;
--ok: #2ac769;
--warn: #ffb020;
--border: #2a3558;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Segoe UI, Helvetica, Arial, sans-serif;
background: radial-gradient(1200px 500px at 20% -10%, #1f2a4a 0%, var(--bg) 60%);
color: var(--text);
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
background: rgba(11, 16, 32, 0.8);
backdrop-filter: blur(6px);
}
h1 {
margin: 0 0 6px 0;
font-size: 18px;
font-weight: 700;
}
.meta {
color: var(--muted);
font-size: 13px;
display: flex;
gap: 14px;
flex-wrap: wrap;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
padding: 12px;
flex: 1;
min-height: 0;
}
.card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
}
.bar {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
font-size: 13px;
}
.bar .left {
font-weight: 700;
}
.pill {
display: inline-block;
border-radius: 999px;
padding: 2px 8px;
font-size: 12px;
font-weight: 700;
}
.pill.ok {
background: rgba(42, 199, 105, 0.18);
color: var(--ok);
border: 1px solid rgba(42, 199, 105, 0.35);
}
.pill.warn {
background: rgba(255, 176, 32, 0.18);
color: var(--warn);
border: 1px solid rgba(255, 176, 32, 0.35);
}
iframe {
border: 0;
width: 100%;
flex: 1;
min-height: 0;
background: #fff;
}
@media (max-width: 1100px) {
.grid { grid-template-columns: 1fr; }
.card { min-height: 55vh; }
}
</style>
</head>
<body>
<header>
<h1>ACI Runtime Attestation Compare</h1>
<div class="meta">
<span>Left: Confidential SKU (expected attestation success)</span>
<span>Right: Standard SKU (expected attestation failure)</span>
</div>
</header>

<main class="grid">
<section class="card">
<div class="bar">
<div class="left">Confidential</div>
<div>
<span class="pill ok">SEV-SNP expected</span>
<span>http://cc-attest-conf-07131653.eastus.azurecontainer.io</span>
</div>
</div>
<iframe src="http://cc-attest-conf-07131653.eastus.azurecontainer.io" title="Confidential ACI"></iframe>
</section>

<section class="card">
<div class="bar">
<div class="left">Standard</div>
<div>
<span class="pill warn">Expected /dev/sev-guest missing</span>
<span>http://cc-attest-std-07131655vspm.eastus.azurecontainer.io</span>
</div>
</div>
<iframe src="http://cc-attest-std-07131655vspm.eastus.azurecontainer.io" title="Standard ACI"></iframe>
</section>
</main>
</body>
</html>
7 changes: 7 additions & 0 deletions aks-samples/virtual nodes/Deploy-VirtualNodesAKS.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ Invoke-Az "az network vnet subnet create --resource-group $resourceGroupName --v

$aksSubnetId = (Invoke-Az "az network vnet subnet show --resource-group $resourceGroupName --vnet-name $vnetName --name $aksSubnetName --query id --output tsv").Trim()
$aciSubnetId = (Invoke-Az "az network vnet subnet show --resource-group $resourceGroupName --vnet-name $vnetName --name $aciSubnetName --query id --output tsv").Trim()
$aciSubnetNsgId = (Invoke-Az "az network vnet subnet show --resource-group $resourceGroupName --vnet-name $vnetName --name $aciSubnetName --query networkSecurityGroup.id --output tsv").Trim()
$vnetId = (Invoke-Az "az network vnet show --resource-group $resourceGroupName --name $vnetName --query id --output tsv").Trim()

Write-Header "Creating AKS cluster"
Expand All @@ -874,6 +875,9 @@ Invoke-Az @"
az aks nodepool add --resource-group $resourceGroupName --cluster-name $aksName --name $confidentialPoolName --node-count $ConfidentialNodeCount --node-vm-size $ConfidentialVmSize --os-sku Ubuntu --mode User --labels workload=confidential sku=amd-sev-snp --tags owner=$ownerUpn BuiltBy=$scriptName Workload=virtual-nodes CCType=AMD-SEV-SNP --only-show-errors
"@

Write-Header "Waiting for AKS operation to complete"
Invoke-Az "az aks wait --resource-group $resourceGroupName --name $aksName --updated --interval 15 --timeout 1800"

Write-Header "Enabling virtual nodes"
Invoke-Az "az aks enable-addons --resource-group $resourceGroupName --name $aksName --addons virtual-node --subnet-name $aciSubnetName --only-show-errors"

Expand All @@ -884,6 +888,9 @@ $aciConnectorPrincipalId = Wait-ForManagedIdentity -ResourceGroup $nodeResourceG
Write-Header "Assigning subnet rights to the virtual nodes identity"
Ensure-RoleAssignment -AssigneeObjectId $aciConnectorPrincipalId -RoleName "Network Contributor" -Scope $aciSubnetId
Ensure-RoleAssignment -AssigneeObjectId $aciConnectorPrincipalId -RoleName "Network Contributor" -Scope $vnetId
if ($aciSubnetNsgId) {
Ensure-RoleAssignment -AssigneeObjectId $aciConnectorPrincipalId -RoleName "Network Contributor" -Scope $aciSubnetNsgId
}

Write-Header "Connecting kubectl"
Invoke-Az "az aks get-credentials --resource-group $resourceGroupName --name $aksName --overwrite-existing --only-show-errors"
Expand Down
Loading
Loading