Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/validate-components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: '1.24'
go-version: '1.25'
- name: Install cue
run: |
go version
Expand Down
152 changes: 141 additions & 11 deletions parts/linux/cloud-init/artifacts/init-aks-custom-cloud.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,159 @@ fi
echo "distribution is $distribution"
echo "Running on $NAME"

# The purpose of RCV 1P is to reliably distribute root and intermediate certificates at scale to
# only Microsoft 1st party (1P) virtual machines (VM) and virtual machine scale sets (VMSS).
# This is critical for initiatives such as Microsoft PKI. RCV 1P ensures that these certificates
# are installed on the node at creation time. This eliminates the need for your VM to be connected
# to the internet and ping an endpoint to receive certificate packages. The feature also eliminates
# the dependency on updates to AzSecPack to receive the latest root and intermediate certs.
# RCV 1P is designed to work completely autonomously from the user perspective on all Azure 1st
# party VMs.

# Below code calls the wireserver to get the list of CA certs for this cloud and saves them to /root/AzureCACertificates. Then it copies them to the appropriate location based on distro and updates the CA bundle.

# http://168.63.129.16 is a constant for the host's wireserver endpoint
certs=$(curl "http://168.63.129.16/machine?comp=acmspackage&type=cacertificates&ext=json")
WIRESERVER_ENDPOINT="http://168.63.129.16"

# Function to make HTTP request with retry logic for rate limiting
make_request_with_retry() {
local url="$1"
local max_retries=10
local retry_delay=3
local attempt=1

local response
while [ $attempt -le $max_retries ]; do
response=$(curl -f --no-progress-meter "$url")
local request_status=$?

if echo "$response" | grep -q "RequestRateLimitExceeded"; then
sleep $retry_delay
retry_delay=$((retry_delay * 2))
attempt=$((attempt + 1))
elif [ $request_status -ne 0 ]; then
sleep $retry_delay
attempt=$((attempt + 1))
else
echo "$response"
return 0
fi
done

echo "exhausted all retries, last response: $response"
return 1
}

# Function to process certificate operations from a given endpoint
process_cert_operations() {
local endpoint_type="$1"
local operation_response

echo "Retrieving certificate operations for type: $endpoint_type"
operation_response=$(make_request_with_retry "${WIRESERVER_ENDPOINT}/machine?comp=acmspackage&type=$endpoint_type&ext=json")
local request_status=$?
if [ -z "$operation_response" ] || [ $request_status -ne 0 ]; then
echo "Warning: No response received or request failed for: ${WIRESERVER_ENDPOINT}/machine?comp=acmspackage&type=$endpoint_type&ext=json"
return
fi

# Extract ResourceFileName values from the JSON response
local cert_filenames
mapfile -t cert_filenames < <(echo "$operation_response" | grep -oP '(?<="ResouceFileName": ")[^"]*')
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

process_cert_operations only extracts filenames from the JSON key ResouceFileName (typo). The Windows implementation supports both ResouceFileName and ResourceFileName; if the WireServer payload uses the correct spelling, this will silently find 0 certs. Update the extraction to handle both keys (or parse JSON reliably) so cert pulls work across payload variations.

Suggested change
mapfile -t cert_filenames < <(echo "$operation_response" | grep -oP '(?<="ResouceFileName": ")[^"]*')
mapfile -t cert_filenames < <(echo "$operation_response" | grep -oP '(?<="ResouceFileName":\s*")[^"]*|(?<="ResourceFileName":\s*")[^"]*')

Copilot uses AI. Check for mistakes.

if [ ${#cert_filenames[@]} -eq 0 ]; then
echo "No certificate filenames found in response for $endpoint_type"
return
fi

# Process each certificate file
for cert_filename in "${cert_filenames[@]}"; do
echo "Processing certificate file: $cert_filename"

# Extract filename and extension
local filename="${cert_filename%.*}"
local extension="${cert_filename##*.}"

echo "Downloading certificate: filename=$filename, extension=$extension"

# Retrieve the actual certificate content with retry logic
local cert_content
cert_content=$(make_request_with_retry "${WIRESERVER_ENDPOINT}/machine?comp=acmspackage&type=$filename&ext=$extension")
local request_status=$?
if [ -z "$cert_content" ] || [ $request_status -ne 0 ]; then
echo "Warning: No response received or request failed for: ${WIRESERVER_ENDPOINT}/machine?comp=acmspackage&type=$filename&ext=$extension"
continue
fi

if [ -n "$cert_content" ]; then
# Save the certificate to the appropriate location
echo "$cert_content" > "/root/AzureCACertificates/$cert_filename"
echo "Successfully saved certificate: $cert_filename"
Comment on lines +112 to +114
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

When the RCV 1P opt-in path is taken, certificates are saved with their original filenames (including .crt extension) in line 81. However, the Flatcar cert installation logic at lines 143-147 attempts to copy files matching "*.crt" pattern. This creates an inconsistency: the fallback path (lines 121-124) saves files with .pem extension for Flatcar, but the RCV 1P path saves them as .crt regardless of distro. Either the cert saving logic should respect IS_FLATCAR and save as .pem, or the RCV 1P cert download should ensure filenames use appropriate extensions for each distro.

Suggested change
# Save the certificate to the appropriate location
echo "$cert_content" > "/root/AzureCACertificates/$cert_filename"
echo "Successfully saved certificate: $cert_filename"
# Determine target filename based on distro to match cert installation expectations
local target_cert_filename="$cert_filename"
if [ "$IS_FLATCAR" -eq 1 ]; then
# For Flatcar, use .pem extension to align with fallback cert handling
target_cert_filename="${filename}.pem"
fi
# Save the certificate to the appropriate location
echo "$cert_content" > "/root/AzureCACertificates/$target_cert_filename"
echo "Successfully saved certificate: $target_cert_filename"

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +114
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

In the opted-in (operation-requests) path, cert filenames are saved exactly as returned (e.g., .cer/.crt/.pem), but the install step later only copies /root/AzureCACertificates/*.crt. If WireServer returns .cer (as the legacy flow suggests), no files will match and no certs will be installed. Normalize the saved filenames/extensions (e.g., always write .crt on Ubuntu/Mariner and handle Flatcar separately) or broaden the install glob to include the actual extensions returned.

Suggested change
# Save the certificate to the appropriate location
echo "$cert_content" > "/root/AzureCACertificates/$cert_filename"
echo "Successfully saved certificate: $cert_filename"
# Determine the filename to use when saving the certificate.
# On non-Flatcar systems, normalize to a .crt extension so that later
# install steps that use /root/AzureCACertificates/*.crt will pick it up.
local save_filename
if [ "$IS_FLATCAR" -eq 1 ]; then
save_filename="$cert_filename"
else
save_filename="${filename}.crt"
fi
# Save the certificate to the appropriate location
echo "$cert_content" > "/root/AzureCACertificates/$save_filename"
echo "Successfully saved certificate: $save_filename (original: $cert_filename)"

Copilot uses AI. Check for mistakes.
else
echo "Warning: Failed to retrieve certificate content for $cert_filename"
fi
done
}

IFS_backup=$IFS
IFS=$'\r\n'
certNames=($(echo $certs | grep -oP '(?<=Name\": \")[^\"]*'))
certBodies=($(echo $certs | grep -oP '(?<=CertBody\": \")[^\"]*'))
ext=".crt"
if [ "$IS_FLATCAR" -eq 1 ]; then
ext=".pem"

# First check via curl "http://168.63.129.16/acms/isOptedInForRootCerts" and JSON response for
# {"IsOptedInForRootCerts":true}. The value captured in optInCheck indicates whether THIS VM
# is opted in for the RCV 1P PKI setup described above. If not opted in, skip the RCV 1P pull
# path and use the default cert retrieval flow instead. This check can be removed if you want to
# attempt to pull certs regardless of opt-in status, but it may result in errors in the logs if
# not opted in.
# https://eng.ms/docs/products/onecert-certificates-key-vault-and-dsms/onecert-customer-guide/autorotationandecr/rcv1ptsg

optInCurlStatus=0
optInCheck=$(curl -sS --fail "http://168.63.129.16/acms/isOptedInForRootCerts" 2>/dev/null) || optInCurlStatus=$?
if [ "$optInCurlStatus" -ne 0 ]; then
echo "Warning: failed to query root cert opt-in status (curl exit code $optInCurlStatus); defaulting to non-opt-in flow"
optInCheck=""
fi
for i in ${!certBodies[@]}; do
echo ${certBodies[$i]} | sed 's/\\r\\n/\n/g' | sed 's/\\//g' > "/root/AzureCACertificates/$(echo ${certNames[$i]} | sed "s/.cer/.${ext}/g")"
done

if echo "$optInCheck" | grep -Eq '"IsOptedInForRootCerts"[[:space:]]*:[[:space:]]*true'; then
echo "Opted in for root certs, proceeding with CA cert pull and install"
# Process root certificates
process_cert_operations "operationrequestsroot"

# Process intermediate certificates
process_cert_operations "operationrequestsintermediate"
echo "successfully pulled in root certs"
else
echo "Not opted in for root certs, skipping CA cert pull and install"
# http://168.63.129.16 is a constant for the host's wireserver endpoint
certs=$(curl "http://168.63.129.16/machine?comp=acmspackage&type=cacertificates&ext=json")
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

The fallback curl request to retrieve certificates lacks error handling. Unlike the RCV 1P path which uses make_request_with_retry with retry logic, this curl command has no --fail flag, no retry mechanism, and no validation of the response. If the wireserver is temporarily unavailable or rate-limits the request, the script will continue with potentially empty or malformed data, leading to provisioning failures. Consider adding retry logic and error checking similar to the RCV 1P path.

Copilot uses AI. Check for mistakes.
certNames=($(echo $certs | grep -oP '(?<=Name\": \")[^\"]*'))
certBodies=($(echo $certs | grep -oP '(?<=CertBody\": \")[^\"]*'))
Comment on lines +151 to +152
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

Variable expansions should be quoted to prevent word splitting and glob expansion. The expressions ${certBodies[@]} and ${certNames[@]} within command substitutions should have proper quoting. The use of unquoted variables in command substitution, especially with grep and sed, can lead to unexpected behavior if the data contains special characters or spaces.

Copilot uses AI. Check for mistakes.
ext=".crt"
if [ "$IS_FLATCAR" -eq 1 ]; then
ext=".pem"
fi
for i in ${!certBodies[@]}; do
echo ${certBodies[$i]} | sed 's/\\r\\n/\n/g' | sed 's/\\//g' > "/root/AzureCACertificates/$(echo ${certNames[$i]} | sed "s/.cer/.${ext}/g")"
done
echo "successfully pulled in default certs"
fi

IFS=$IFS_backup

if [ "$IS_FLATCAR" -eq 0 ]; then
if [ "${IS_FLATCAR}" -eq 0 ]; then
Comment on lines +154 to +165
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

The quote style change from "$IS_FLATCAR" to "${IS_FLATCAR}" is good for consistency, but the original comparison at line 122 still uses the old style "$IS_FLATCAR". For consistency, all IS_FLATCAR comparisons in the script should use the same quote style.

Copilot uses AI. Check for mistakes.
# Copy all certificate files to the system certificate directory
cp /root/AzureCACertificates/*.crt /usr/local/share/ca-certificates/

# Update the system certificate store
update-ca-certificates

# This copies the updated bundle to the location used by OpenSSL which is commonly used
cp /etc/ssl/certs/ca-certificates.crt /usr/lib/ssl/cert.pem
else
cp /root/AzureCACertificates/*.pem /etc/ssl/certs/
for cert in /root/AzureCACertificates/*.crt; do
destcert="${cert##*/}"
destcert="${destcert%.*}.pem"
cp "$cert" /etc/ssl/certs/"$destcert"
done
Comment on lines +165 to +179
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Flatcar branch now loops over /root/AzureCACertificates/*.crt, but in the non-opt-in (legacy) path this script writes certs with a .pem extension (see ext=".pem"). When not opted in, this glob won't match and the copy will fail, leaving Flatcar without installed custom cloud certs. Either keep copying .pem for the legacy flow or normalize the downloaded artifacts to .crt consistently before the copy step (and enable nullglob to avoid iterating on a literal pattern).

Copilot uses AI. Check for mistakes.
Comment on lines +175 to +179
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Flatcar install loop iterates over /root/AzureCACertificates/*.crt, but the non-opted-in flow writes .pem files (and the opted-in flow may write .cer). This loop will no-op (and then run update-ca-certificates) if no .crt files exist. Align the Flatcar copy/convert logic with the actual extensions produced by both flows (e.g., convert whatever was downloaded into .pem during the copy step, or standardize downloads to .crt).

Copilot uses AI. Check for mistakes.
update-ca-certificates
fi

Expand Down
Loading
Loading