Skip to content

Commit 6ace0dd

Browse files
authored
Merge pull request #18422 from sethmanheim/validsign7-11
Add how to validate signed container images article
2 parents 4dbb2ba + d287e6a commit 6ace0dd

File tree

2 files changed

+303
-3
lines changed

2 files changed

+303
-3
lines changed

AKS-Arc/TOC.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@
8080
href: deploy-load-balancer-cli.md
8181
- name: Azure portal
8282
href: deploy-load-balancer-portal.md
83-
# - name: Troubleshoot issues
84-
# href: load-balancer-troubleshoot.md
8583
- name: Security
8684
items:
8785
- name: Encrypt etcd secrets
8886
href: encrypt-etcd-secrets.md
87+
- name: Validate signed container images
88+
href: validate-signed-container-images.md
8989
- name: AI and Machine Learning
9090
items:
9191
- name: Deploy an AI model with the AI toolchain operator
@@ -107,7 +107,7 @@
107107
- name: Restrict SSH access
108108
href: restrict-ssh-access.md
109109
- name: Deploy and configure Workload Identity
110-
href: workload-identity.md
110+
href: workload-identity.md
111111
- name: Storage
112112
href: concepts-storage.md
113113
items:
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
---
2+
title: Validate Signed Container Images for AKS on Azure Local
3+
description: Learn how to validate signed container images for AKS on Azure Local deployments to ensure image integrity and prevent supply chain attacks.
4+
author: sethmanheim
5+
ms.topic: how-to
6+
ms.date: 07/25/2025
7+
ms.author: sethm
8+
ms.reviewer: leslielin
9+
ms.lastreviewed: 07/25/2025
10+
11+
---
12+
13+
# Validate signed container images for AKS on Azure Local deployments
14+
15+
[!INCLUDE [hci-applies-to-23h2](includes/hci-applies-to-23h2.md)]
16+
17+
Container image signing verifies the integrity and authenticity of images before deployment, ensuring a trusted infrastructure. This verification is vital in production environments, regulated industries, and automated CI/CD pipelines, where validating image sources helps to both prevent supply chain attacks and maintain compliance.
18+
19+
This article describes how to validate signed container images for AKS on Azure Local deployments.
20+
21+
## Prerequisites
22+
23+
Before you begin, ensure you have the following prerequisites:
24+
25+
1. **Cluster configuration:** Deploy an AKS Arc cluster with these requirements
26+
27+
- One control plane node.
28+
- At least one Linux node.
29+
- (Optional) Add a Windows node pool if you need to validate Windows container images.
30+
31+
1. **A Windows machine jump box** is required to run the image validation script. The machine must meet these prerequisites:
32+
33+
- **Network access**: The jump box must be able to communicate with the Azure Local physical boxes where the AKS Arc cluster is deployed and route traffic to the IP addresses assigned to the AKS Arc cluster, including all control plane and worker nodes.
34+
- **Required tools installed**: Install these tools on the jump box: **curl**, **ssh**, and **scp**. These tools enable secure remote access, file transfers, and HTTP-based interactions with your cluster or management endpoints.
35+
- **SSH access**: The private SSH key used during cluster creation must be present on the jump box.
36+
- This key enables connections to individual nodes (Linux or Windows).
37+
- For details on generating or retrieving the key, see [Configure SSH keys for a cluster](/azure/aks/aksarc/configure-ssh-keys).
38+
39+
1. **Install the Kubernetes CLI**
40+
41+
You can use the Kubernetes CLI, [kubectl](https://kubernetes.io/docs/reference/kubectl/), to connect to your Kubernetes cluster. Use the following Azure CLI or Azure PowerShell commands to install **kubectl**:
42+
43+
# [Azure CLI](#tab/cli)
44+
45+
Install **kubectl** locally using the [az aks install-cli](/cli/azure/aks?view=azure-cli-latest#az-aks-install-cli&preserve-view=true) command.
46+
47+
# [PowerShell](#tab/powershell)
48+
49+
Install **kubectl** locally using the [Install-AzAksCliTool](/powershell/module/az.aks/install-azaksclitool?view=azps-14.2.0&preserve-view=true) cmdlet.
50+
51+
---
52+
53+
## Step 1: download the image validation script
54+
55+
1. Open a PowerShell window as an administrator and create a temporary folder at **c:\imagesign**:
56+
57+
```powershell
58+
mkdir c:\imagesign
59+
cd c:\imagesign
60+
```
61+
62+
1. Run the following commands to download the [prerequisite.ps1](https://raw.githubusercontent.com/Azure/aksArc/refs/heads/main/scripts/ImageSignValidation/prerequisite.ps1) script to the jump box and save it in **c:\imagesign**:
63+
64+
```powershell
65+
$giturl = "https://raw.githubusercontent.com/Azure/aksArc/refs/heads/main/scripts/ImageSignValidation/prerequisite.ps1"
66+
Invoke-WebRequest -Uri $giturl -OutFile C:\imagesign\prerequisite.ps1 -UseBasicParsing
67+
Unblock-File .\prerequisite.ps1
68+
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
69+
```
70+
71+
1. Execute the script
72+
73+
```powershell
74+
.\prerequisite.ps1
75+
```
76+
77+
Sample output:
78+
79+
```output
80+
2025-05-10 00:39:27 [INFO] The current folder is C:\imagesign
81+
2025-05-10 00:39:27 [INFO] Linux folder location is C:\imagesign\linux
82+
2025-05-10 00:39:28 [INFO] Executable not found at C:\imagesign\linux\notation. Proceeding with download and extraction.
83+
2025-05-10 00:39:28 [INFO] Downloading file from https://github.com/notaryproject/notation/releases/download/v1.3.1/notation_1.3.1_linux_amd64.tar.gz
84+
2025-05-10 00:39:36 [INFO] Extracting file to C:\imagesign\linux
85+
2025-05-10 00:39:36 [INFO] Downloading certificate from https://www.microsoft.com/pkiops/certs/Microsoft%20Supply%20Chain%20RSA%20Root%20CA%202022.crt
86+
2025-05-10 00:39:36 [INFO] Certificate downloaded to C:\imagesign\linux\ca.crt
87+
2025-05-10 00:39:36 [INFO] Downloading certificate from http://www.microsoft.com/pki/certs/MicRooCerAut_2010-06-23.crt
88+
2025-05-10 00:39:36 [INFO] Certificate downloaded to C:\imagesign\linux\tsa.crt
89+
2025-05-10 00:39:36 [INFO] Windows folder location is C:\imagesign\win
90+
2025-05-10 00:39:36 [INFO] Executable not found at C:\imagesign\win\notation.exe. Proceeding with download and extraction.
91+
2025-05-10 00:39:36 [INFO] Downloading file from https://github.com/notaryproject/notation/releases/download/v1.3.1/notation_1.3.1_windows_amd64.zip
92+
2025-05-10 00:39:44 [INFO] Extracting file to C:\imagesign\win
93+
2025-05-10 00:39:44 [INFO] Downloading certificate from https://www.microsoft.com/pkiops/certs/Microsoft%20Supply%20Chain%20RSA%20Root%20CA%202022.crt
94+
2025-05-10 00:39:44 [INFO] Certificate downloaded to C:\imagesign\win\ca.crt
95+
2025-05-10 00:39:44 [INFO] Downloading certificate from http://www.microsoft.com/pki/certs/MicRooCerAut_2010-06-23.crt
96+
2025-05-10 00:39:44 [INFO] Certificate downloaded to C:\imagesign\win\tsa.crt
97+
```
98+
99+
1. Two folders will be created: **linux** for Linux files and **win** for Windows files.
100+
101+
### [Linux](#tab/linux)
102+
103+
Copy **LinuxImageValidate.py** to the **c:\imagesign\linux** folder:
104+
105+
```powershell
106+
$giturllinux = "https://raw.githubusercontent.com/Azure/aksArc/refs/heads/main/scripts/ImageSignValidation/LinuxImageValidate.py"
107+
Invoke-WebRequest -Uri $giturllinux -OutFile C:\imagesign\linux\LinuxImageValidate.py -UseBasicParsing
108+
Unblock-File C:\imagesign\linux\LinuxImageValidate.py
109+
```
110+
111+
### [Windows](#tab/windows)
112+
113+
Copy **WindowsImageValidate.ps1** to the **c:\imagesign\win** folder:
114+
115+
```powershell
116+
$giturlwin = "https://raw.githubusercontent.com/Azure/aksArc/refs/heads/main/scripts/ImageSignValidation/WindowsImageValidate.ps1"
117+
Invoke-WebRequest -Uri $giturlwin -OutFile C:\imagesign\win\WindowsImageValidate.ps1 -UseBasicParsing
118+
Unblock-File C:\imagesign\win\WindowsImageValidate.ps1
119+
Invoke-WebRequest -Uri $giturlwin -OutFile C:\imagesign\win\WindowsImageValidate.py -UseBasicParsing
120+
Unblock-File C:\imagesign\win\WindowsImageValidate.py
121+
```
122+
123+
---
124+
125+
## Step 2: get the control plane and worker node IP addresses
126+
127+
Follow steps 1 and 2 in the [Use SSH to connect to worker nodes](ssh-connect-to-windows-and-linux-worker-nodes.md#use-ssh-to-connect-to-worker-nodes) section to obtain IP addresses.
128+
129+
### Sample command
130+
131+
```powershell
132+
kubectl --kubeconfig /path/to/aks-cluster-kubeconfig get nodes -o wide
133+
```
134+
135+
Sample output:
136+
137+
| NAME | STATUS | ROLES | AGE | VERSION | INTERNAL-IP | EXTERNAL-IP | OS-IMAGE | KERNEL-VERSION | CONTAINER-RUNTIME |
138+
|-------------------|--------|--------------|-------|---------|-----------------|-------------|---------------------------|------------------------|-----------------------------|
139+
| moc-lsbe393il9d | Ready | control-plane| 3h14m | 1.30.4 | 100.0.12.133 | None | CBL-Mariner/Linux | 5.15.173.1-2.cm2 | containerd://1.6.26 |
140+
| moc-lzwagtkjah5 | Ready | None | 3h12m | 1.30.4 | 100.0.12.134 | None | CBL-Mariner/Linux | 5.15.173.1-2.cm2 | containerd://1.6.26 |
141+
| moc-wlcjnwn5n02 | Ready | None | 14m | 1.30.4 | 100.0.12.135 | None | Windows Server 2022 Datacenter | 10.0.20348.2700 | containerd://1.6.21+Azure |
142+
143+
From this sample output:
144+
145+
- Control plane IP is 100.0.12.133 (where the ROLES value is `control-plane` and OS image is `CBL-Mariner/Linux`).
146+
- Linux node IP is 100.0.12.134 (where the ROLES value is `none` and OS image is `CBL-Mariner/Linux`).
147+
- Windows node IP is 100.0.12.135 (where the OS image is `Windows Server 2022`).
148+
149+
## Step 3: run the image validation script on the control plane and worker nodes
150+
151+
### [Linux](#tab/linux)
152+
153+
These steps work for both the control plane node and Linux worker node since they use the **CBL-Mariner/Linux** OS image.
154+
155+
1. Check if the commands can be run on the Linux VM (assuming the private key is in **C:\Users\Administrator\.ssh**):
156+
157+
```bash
158+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" -o StrictHostKeyChecking=no [email protected] "sudo crictl images --no-trunc"
159+
```
160+
161+
1. Copy the Linux-specific files to the Linux VM:
162+
163+
```bash
164+
scp -i "C:\Users\Administrator\.ssh\id_rsa" C:\imagesign\linux\* [email protected]:.
165+
```
166+
167+
1. Mark the notation binary file as executable:
168+
169+
```bash
170+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" -o StrictHostKeyChecking=no [email protected] "sudo chmod +x notation"
171+
```
172+
173+
1. Execute commands to validate image signature verification. This step can take around 2-5 minutes:
174+
175+
```bash
176+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" -o StrictHostKeyChecking=no [email protected] "sudo python3 LinuxImageValidate.py"
177+
```
178+
179+
1. Copy the output file to the local directory:
180+
181+
```bash
182+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" -o StrictHostKeyChecking=no [email protected] "sudo cat imagevalidation_results_linux.json" > imagevalidation_results_controlplane.json
183+
```
184+
185+
1. Clean up all files copied to the VM:
186+
187+
```bash
188+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" -o StrictHostKeyChecking=no [email protected] rm LinuxImageValidate.py notation tsa.crt ca.crt LICENSE imagevalidation_results_linux.json results.yaml
189+
```
190+
191+
1. Clean up the IP reference from the SSH cache:
192+
193+
```bash
194+
ssh-keygen -R 100.0.12.133
195+
```
196+
197+
Sample output:
198+
199+
```output
200+
{
201+
"failed_signed_images": [
202+
"mcr.microsoft.com/mssql/server:2017-latest",
203+
"mcr.microsoft.com/oss/kubernetes/coredns:v1.9.3",
204+
"mcr.microsoft.com/oss/kubernetes/pause:3.9"
205+
],
206+
"passed_signed_images": [
207+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.28.3",
208+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.28.5",
209+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.28.9",
210+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.29.2",
211+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.29.4",
212+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.29.7",
213+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.29.9",
214+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.30.3",
215+
"mcr.microsoft.com/oss/kubernetes/kube-proxy:v1.30.4",
216+
"mcr.microsoft.com/oss/kubernetes/kube-rbac-proxy:v0.18.1",
217+
"mcr.microsoft.com/oss/kubernetes/kube-scheduler:v1.28.12-hotfix.20240719",
218+
"mcr.microsoft.com/oss/kubernetes/kube-scheduler:v1.28.14-hotfix.20240918",
219+
"mcr.microsoft.com/oss/kubernetes/kube-scheduler:v1.29.7",
220+
"mcr.microsoft.com/oss/kubernetes/kube-scheduler:v1.29.9",
221+
"mcr.microsoft.com/oss/kubernetes/kube-scheduler:v1.30.3",
222+
"mcr.microsoft.com/oss/kubernetes/kube-scheduler:v1.30.4",
223+
"mcr.microsoft.com/oss/kubernetes/pause:3.9-hotfix-20230808",
224+
"mcr.microsoft.com/oss/v2/kubernetes-csi/csi-driver-nfs:v4.9.0-1"
225+
]
226+
}
227+
```
228+
229+
> [!NOTE]
230+
> If the **failed_signed_images** list is empty, all images are signed correctly. Otherwise, the list shows images that failed signature verification.
231+
232+
### [Windows](#tab/windows)
233+
234+
These steps work on all supported Windows OS worker nodes.
235+
236+
1. Check if the commands can be run on the Windows VM (assuming the private key is in folder **C:\Users\Administrator\.ssh**):
237+
238+
```bash
239+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" -o StrictHostKeyChecking=no [email protected] "crictl images --no-trunc"
240+
```
241+
242+
1. Copy the Windows-specific files inside the Windows VM:
243+
244+
```bash
245+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" [email protected] "powershell -ExecutionPolicy Bypass mkdir c:\win"
246+
scp -i "C:\Users\Administrator\.ssh\id_rsa" C:\imagesign\win\ca.crt [email protected]:c:\win\ca.crt
247+
scp -i "C:\Users\Administrator\.ssh\id_rsa" C:\imagesign\win\notation.exe [email protected]:c:\win\notation.exe
248+
scp -i "C:\Users\Administrator\.ssh\id_rsa" C:\imagesign\win\tsa.crt [email protected]:c:\win\tsa.crt
249+
scp -i "C:\Users\Administrator\.ssh\id_rsa" C:\imagesign\win\WindowsImageValidate.ps1 [email protected]:c:\win\WindowsImageValidate.ps1
250+
```
251+
252+
1. Execute commands to validate image signature verification. This step can take around 2-5 minutes:
253+
254+
```bash
255+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" [email protected] "powershell -ExecutionPolicy Bypass -File c:\win\WindowsImageValidate.ps1"
256+
```
257+
258+
1. Copy the output file to the local directory:
259+
260+
```bash
261+
scp -i "C:\Users\Administrator\.ssh\id_rsa" [email protected]:c:\win\imagevalidation_results_windows.json C:\imagesign\imagevalidation_results_windows.json
262+
```
263+
264+
1. Clean up all files copied to the VM:
265+
266+
```bash
267+
ssh -i "C:\Users\Administrator\.ssh\id_rsa" [email protected] "powershell -ExecutionPolicy Bypass remove-item -force c:\win"
268+
```
269+
270+
1. Clean up IP reference from the SSH cache:
271+
272+
```bash
273+
ssh-keygen -R 100.0.12.135
274+
```
275+
276+
Sample output:
277+
278+
```output
279+
{
280+
"failed_signed_images": [
281+
],
282+
"passed_signed_images": [
283+
"mcr.microsoft.com/oss/kubernetes-csi/livenessprobe@sha256:20fd8754d36efc52ff0a837e646909102be5d47600a8656804aecd4eff52b7c7",
284+
"mcr.microsoft.com/oss/kubernetes/pause@sha256:2b5d81a9d7f04299f45ed1b6822de018188f463914fcbf5592f39087c9adead1",
285+
"mcr.microsoft.com/windows/nanoserver@sha256:f82cb05e20c4bfa93a007c9f073f83febd8bc6d16f98a3208f3baa486aafcdf4"
286+
],
287+
"failed_signed_images": [
288+
]
289+
}
290+
```
291+
292+
> [!NOTE]
293+
> If the **failed_signed_images** list is empty, all images are signed correctly. Otherwise, the list shows images that failed signature verification.
294+
295+
---
296+
297+
## Next steps
298+
299+
- [AKS on Azure Local overview](overview.md)
300+
- [Use Azure role-based access control (RBAC) for Kubernetes authorization](azure-rbac-local.md)

0 commit comments

Comments
 (0)