Skip to content
Open
4 changes: 2 additions & 2 deletions .pipelines/.vsts-vhd-builder-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ parameters:
- name: build2204fipscontainerd
displayName: Build 2204 FIPS containerd
type: boolean
default: false
default: true
- name: build2204fipsgen2containerd
displayName: Build 2204 FIPS Gen2 containerd
type: boolean
default: false
default: true
- name: build2204arm64gen2containerd
displayName: Build 2204 ARM64 Gen2 containerd
type: boolean
Expand Down
4 changes: 2 additions & 2 deletions pkg/agent/datamodel/sig_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,14 +429,14 @@ var (
ResourceGroup: AKSUbuntuResourceGroup,
Gallery: AKSUbuntuGalleryName,
Definition: "2204fipscontainerd",
Version: "202404.09.0", // TODO(artunduman): Update version when the image is ready
Version: LinuxSIGImageVersion,
}

SIGUbuntuFipsContainerd2204Gen2ImageConfigTemplate = SigImageConfigTemplate{
ResourceGroup: AKSUbuntuResourceGroup,
Gallery: AKSUbuntuGalleryName,
Definition: "2204gen2fipscontainerd",
Version: "202404.09.0", // TODO(artunduman): Update version when the image is ready
Version: LinuxSIGImageVersion,
}

SIGUbuntuArm64Containerd2204Gen2ImageConfigTemplate = SigImageConfigTemplate{
Expand Down
2 changes: 2 additions & 0 deletions pkg/agent/datamodel/sig_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ var _ = Describe("GetMaintainedLinuxSIGImageConfigMap", func() {
expected := map[Distro]SigImageConfig{
AKSUbuntuFipsContainerd2004: SIGUbuntuFipsContainerd2004ImageConfigTemplate.WithOptions(),
AKSUbuntuFipsContainerd2004Gen2: SIGUbuntuFipsContainerd2004Gen2ImageConfigTemplate.WithOptions(),
AKSUbuntuFipsContainerd2204: SIGUbuntuFipsContainerd2204ImageConfigTemplate.WithOptions(),
AKSUbuntuFipsContainerd2204Gen2: SIGUbuntuFipsContainerd2204Gen2ImageConfigTemplate.WithOptions(),
AKSUbuntuArm64Containerd2204Gen2: SIGUbuntuArm64Containerd2204Gen2ImageConfigTemplate.WithOptions(),
AKSUbuntuArm64Containerd2404Gen2: SIGUbuntuArm64Containerd2404Gen2ImageConfigTemplate.WithOptions(),
AKSUbuntuArm64GB200Containerd2404Gen2: SIGUbuntuArm64GB200Containerd2404Gen2ImageConfigTemplate.WithOptions(),
Expand Down
125 changes: 125 additions & 0 deletions vhdbuilder/packer/fips-helper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/bash
# FIPS Helper Functions for VHD Scanning

# FIPS 140-3 encryption is not automatically supported in Linux VMs.
# Because not all extensions are onboarded to FIPS 140-3 yet, subscriptions must register the Microsoft.Compute/OptInToFips1403Compliance feature.
# After registering the feature, the VM must be created via Azure REST API calls to enable support for FIPS 140-3.
# There is currently no ETA for when FIPS 140-3 encryption is natively supported, but all information can be found here: https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux-fips

# This script contains functions related to FIPS 140-3 compliance for Ubuntu 22.04

# Function to ensure FIPS 140-3 compliance feature is registered
ensure_fips_feature_registered() {
echo "Detected Ubuntu 22.04 + FIPS scenario, enabling FIPS 140-3 compliance..."

# Enable FIPS 140-3 compliance feature if not already enabled
echo "Checking FIPS 140-3 compliance feature registration..."
FIPS_FEATURE_STATE=$(az feature show --namespace Microsoft.Compute --name OptInToFips1403Compliance --query 'properties.state' -o tsv 2>/dev/null || echo "NotRegistered")

if [ "$FIPS_FEATURE_STATE" != "Registered" ]; then
echo "Registering FIPS 140-3 compliance feature..."
az feature register --namespace Microsoft.Compute --name OptInToFips1403Compliance
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is we get that the feature is not available ? we will poll and retry ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it will try for 5 min, time out, and echo a warning before continuing.

This means it will attempt to create the vm without the feature, which will fail.


# Poll until registered (timeout after 5 minutes)
local TIMEOUT=300
local ELAPSED=0
while [ "$FIPS_FEATURE_STATE" != "Registered" ] && [ $ELAPSED -lt $TIMEOUT ]; do
sleep 10
ELAPSED=$((ELAPSED + 10))
FIPS_FEATURE_STATE=$(az feature show --namespace Microsoft.Compute --name OptInToFips1403Compliance --query 'properties.state' -o tsv)
echo "Feature state: $FIPS_FEATURE_STATE (waited ${ELAPSED}s)"
done

if [ "$FIPS_FEATURE_STATE" != "Registered" ]; then
echo "Warning: FIPS 140-3 feature registration timed out. Continuing anyway..."
else
echo "FIPS 140-3 feature registered successfully. Refreshing provider..."
az provider register -n Microsoft.Compute
fi
else
echo "FIPS 140-3 compliance feature already registered"
fi
}

# Function to build FIPS-enabled VM request body
build_fips_vm_body() {
local location="$1"
local vm_name="$2"
local admin_username="$3"
local admin_password="$4"
local image_id="$5"
local nic_id="$6"
local umsi_resource_id="$7"
local vm_size="$8"

cat <<EOF
{
"location": "$location",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"$umsi_resource_id": {}
}
},
"properties": {
"additionalCapabilities": {
"enableFips1403Encryption": true
},
"hardwareProfile": {
"vmSize": "$vm_size"
},
"osProfile": {
"computerName": "$vm_name",
"adminUsername": "$admin_username",
"adminPassword": "$admin_password"
},
"storageProfile": {
"imageReference": {
"id": "$image_id"
},
"osDisk": {
"createOption": "FromImage",
"diskSizeGB": 50,
"managedDisk": {
"storageAccountType": "Premium_LRS"
}
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "$nic_id"
}
]
}
}
}
EOF
}

# Function to create FIPS-enabled VM using REST API
create_fips_vm() {
local vm_size="$1"
echo "Creating VM with FIPS 140-3 encryption using REST API..."

# Build the VM request body for FIPS scenario
local VM_BODY=$(build_fips_vm_body \
"$PACKER_BUILD_LOCATION" \
"$SCAN_VM_NAME" \
"$SCAN_VM_ADMIN_USERNAME" \
"$SCAN_VM_ADMIN_PASSWORD" \
"$VHD_IMAGE" \
"$SCANNING_NIC_ID" \
"$UMSI_RESOURCE_ID" \
"$vm_size")

# Create the VM using REST API
az rest \
--method put \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.Compute/virtualMachines/${SCAN_VM_NAME}?api-version=2024-11-01" \
--body "$VM_BODY"
Comment on lines +106 to +120
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the FIPS creation path, build_fips_vm_body embeds SCAN_VM_ADMIN_PASSWORD as adminPassword in the JSON body and create_fips_vm passes that JSON to az rest via --body while the caller script uses set -x, causing the full JSON (including the plaintext password) and REST invocation to be printed into logs. Anyone with access to these logs can recover the admin password for the FIPS-enabled scanning VM and potentially access it while it is running. Ensure the VM body construction and az rest call execute with shell tracing disabled or use a secret-safe mechanism so the password value is never written to stdout/stderr or persisted in logs.

Copilot uses AI. Check for mistakes.

Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The az rest command at line 117-120 lacks error handling. If the REST API call fails, the script will continue to the az vm wait command which will likely fail or hang indefinitely. Consider adding error checking after the az rest call to verify the VM was created successfully, or add || exit 1 to ensure the script exits if the API call fails. This is especially important since this is using a REST API that might have different error behavior than the standard az vm create command.

Suggested change
local az_rest_exit_code=$?
if [ "$az_rest_exit_code" -ne 0 ]; then
echo "Error: Failed to create VM with FIPS 140-3 encryption via REST API (exit code: $az_rest_exit_code)" >&2
return "$az_rest_exit_code"
fi

Copilot uses AI. Check for mistakes.
# Wait for VM to be ready
echo "Waiting for VM to be ready..."
az vm wait --created --name $SCAN_VM_NAME --resource-group $RESOURCE_GROUP_NAME
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a timeout for this wait command ? could we make is explicit on the CLI so we know what it is form code inspection ?

Also, should we catch timeout cases, and return an error code and error message that that VM was never created ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. default timeout is 1h and can be explicitly set. Would a shorter time ~30min be better?
  2. the existing vm creation pattern in vhd-scanning.sh only attempts to create the vm and does not have any error handling. I can look into catching the timeout case for the fips vm though.

}
42 changes: 30 additions & 12 deletions vhdbuilder/packer/vhd-scanning.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,22 @@ function cleanup() {
trap cleanup EXIT
capture_benchmark "${SCRIPT_NAME}_set_variables_and_create_scan_resource_group"

VM_OPTIONS="--size Standard_D8ds_v5"
VM_SIZE="Standard_D8ds_v5"
VM_OPTIONS="--size $VM_SIZE"
# shellcheck disable=SC3010
if [[ "${ARCHITECTURE,,}" == "arm64" ]]; then
VM_OPTIONS="--size Standard_D8pds_v5"
VM_SIZE="Standard_D8pds_v5"
VM_OPTIONS="--size $VM_SIZE"
fi

if [ "${OS_TYPE}" = "Linux" ] && [ "${ENABLE_TRUSTED_LAUNCH}" = "True" ]; then
VM_OPTIONS+=" --security-type TrustedLaunch --enable-secure-boot true --enable-vtpm true"
fi

if [ "${OS_TYPE}" = "Linux" ] && grep -q "cvm" <<< "$FEATURE_FLAGS"; then
VM_SIZE="Standard_DC8ads_v5"
# We completely re-assign the VM_OPTIONS string here to ensure that no artifacts from earlier conditionals are included
VM_OPTIONS="--size Standard_DC8ads_v5 --security-type ConfidentialVM --enable-secure-boot true --enable-vtpm true --os-disk-security-encryption-type VMGuestStateOnly --specialized true"
VM_OPTIONS="--size $VM_SIZE --security-type ConfidentialVM --enable-secure-boot true --enable-vtpm true --os-disk-security-encryption-type VMGuestStateOnly --specialized true"
fi

# GB200 specific VM options for scanning (uses standard ARM64 VM for now)
Expand All @@ -101,15 +104,30 @@ if [ -z "$SCANNING_NIC_ID" ]; then
exit 1
fi

az vm create --resource-group $RESOURCE_GROUP_NAME \
--name $SCAN_VM_NAME \
--image $VHD_IMAGE \
--nics $SCANNING_NIC_ID \
--admin-username $SCAN_VM_ADMIN_USERNAME \
--admin-password $SCAN_VM_ADMIN_PASSWORD \
--os-disk-size-gb 50 \
${VM_OPTIONS} \
--assign-identity "${UMSI_RESOURCE_ID}"
# Create VM using appropriate method based on scenario
if [ "${OS_SKU}" = "Ubuntu" ] && [ "${OS_VERSION}" = "22.04" ] && [ "$(printf %s "${ENABLE_FIPS}" | tr '[:upper:]' '[:lower:]')" = "true" ]; then
# Source the FIPS helper functions
FULL_PATH=$(realpath $0)
CDIR=$(dirname $FULL_PATH)
source "$CDIR/fips-helper.sh"

# Register FIPS feature and create VM using REST API
ensure_fips_feature_registered
create_fips_vm "$VM_SIZE"
else
echo "Creating VM using standard az vm create command..."

# Use the standard VM creation approach for all other scenarios
az vm create --resource-group $RESOURCE_GROUP_NAME \
--name $SCAN_VM_NAME \
--image $VHD_IMAGE \
--nics $SCANNING_NIC_ID \
--admin-username $SCAN_VM_ADMIN_USERNAME \
--admin-password $SCAN_VM_ADMIN_PASSWORD \
--os-disk-size-gb 50 \
Comment on lines +121 to +127
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The az vm create call passes SCAN_VM_ADMIN_PASSWORD directly via --admin-password while the script is running under set -x, so the full command line including the plaintext password will be emitted to build logs and any attached consoles. An attacker or insider with access to CI logs could recover this password and log into the scanning VM while it is active. Disable shell tracing around this command or use a mechanism that avoids putting the password on the command line so the credential never appears in logs.

Copilot uses AI. Check for mistakes.
${VM_OPTIONS} \
--assign-identity "${UMSI_RESOURCE_ID}"
fi

capture_benchmark "${SCRIPT_NAME}_create_scan_vm"
set +x
Expand Down
Loading