Use the Conformity Knowledge Base AI to help improve your Cloud Posture

Enable Image Cleaner for AKS Clusters

Trend Cloud One™ – Conformity is a continuous assurance tool that provides peace of mind for your cloud infrastructure, delivering over 1000 automated best practice checks.

Risk Level: Medium (should be achieved)

Ensure that your Azure Kubernetes Service (AKS) clusters are configured to use Image Cleaner to automatically detect and remove unused container images from nodes, preventing storage issues and potential security risks. Enabling it improves overall security and reduces manual maintenance efforts.

Security

Pipelines are commonly used to build and deploy container images on Azure Kubernetes Service (AKS) clusters. However, while they efficiently create images, they often overlook stale images left behind, leading to image bloat on cluster nodes. These outdated images may also pose security risks due to potential vulnerabilities. To enhance cluster security, it's important to remove these unreferenced images. Manually cleaning them can be time-consuming, but Image Cleaner automates the process by identifying and removing unused images, reducing both security risks and the time needed for cleanup.


Audit

To determine if Image Cleaner is enabled for your Azure Kubernetes Service (AKS) clusters, perform the following operations:

Checking the Image Cleaner feature status for AKS clusters using Microsoft Azure Portal (Azure Console) is not currently supported.

Using Azure CLI

01 Run account list command (Windows/macOS/Linux) with custom output filters to list the IDs of the cloud subscriptions available in your Azure cloud account:

az account list
  --query '[*].id'

02 The command output should return the requested subscription identifiers (IDs):

[
	"abcdabcd-1234-abcd-1234-abcdabcdabcd",
	"abcd1234-abcd-1234-abcd-abcd1234abcd"
]

03 Run account set command (Windows/macOS/Linux) with the ID of the Azure cloud subscription that you want to examine as the identifier parameter to set the selected subscription to be the current active subscription (the command does not produce an output):

az account set
  --subscription abcdabcd-1234-abcd-1234-abcdabcdabcd

04 Run aks list command (Windows/macOS/Linux) with custom query filters to list the name and the associated resource group for each Azure Kubernetes Service (AKS) cluster available in the selected Azure subscription:

az aks list
  --output table
  --query '[*].{name:name, resourceGroup:resourceGroup}'

05 The command output should return the requested AKS cluster names:

Name                    ResourceGroup
----------------------  ------------------------------
cc-data-mining-cluster  cloud-shell-storage-westeurope
cc-project5-cluster     cloud-shell-storage-westeurope

06 Run aks show command (Windows/macOS/Linux) with the name of the AKS cluster that you want to examine (and the associated resource group) as identifier parameters, to determine if the Image Cleaner feature is enabled for the selected AKS cluster:

az aks show
  --name cc-data-mining-cluster
  --resource-group cloud-shell-storage-westeurope
  --query '{imageCleaner:securityProfile.imageCleaner.enabled}'

07 The command output should return the requested feature status (true for enabled, null or false for disabled):

{
	"imageCleaner": false
}

If the aks show command output returns null or false, as shown in the example above, the Image Cleaner feature is not enabled for the selected Azure Kubernetes Service (AKS) cluster.

08 Repeat steps no. 6 and 7 for each AKS cluster provisioned within the selected Azure subscription.

09 Repeat steps no. 3 – 8 for each subscription created in your Microsoft Azure cloud account.

Remediation / Resolution

To enable the Image Cleaner feature in order to clean up vulnerable stale images on your Azure Kubernetes Service (AKS) clusters, perform the following operations:

Enabling Image Cleaner for AKS clusters using Microsoft Azure Portal (Azure Console) is not currently supported.

Using Azure CLI

01 Run account list command (Windows/macOS/Linux) with custom output filters to list the IDs of the cloud subscriptions available in your Azure cloud account:

az account list
  --query '[*].id'

02 The command output should return the requested subscription identifiers (IDs):

[
	"abcdabcd-1234-abcd-1234-abcdabcdabcd",
	"abcd1234-abcd-1234-abcd-abcd1234abcd"
]

03 Run account set command (Windows/macOS/Linux) with the ID of the Azure cloud subscription that you want to examine as the identifier parameter to set the selected subscription to be the current active subscription (the command does not produce an output):

az account set
  --subscription abcdabcd-1234-abcd-1234-abcdabcdabcd

04 Run aks update command (OSX/Linux/UNIX) with the name of the cluster that you want to configure as the identifier parameter, to enable the Image Cleaner feature for the selected Azure Kubernetes Service (AKS) cluster:

az aks update
  --name cc-data-mining-cluster
  --resource-group cloud-shell-storage-westeurope
  --enable-image-cleaner
  --image-cleaner-interval-hours 48

05 The command output should return the configuration information available for the modified AKS cluster:

{
	"aadProfile": {
		"adminGroupObjectIDs": null,
		"adminUsers": null,
		"clientAppId": null,
		"enableAzureRbac": true,
		"managed": true,
		"serverAppId": null,
		"serverAppSecret": null,
		"tenantId": "abcdabcd-1234-abcd-1234-abcdabcdabcd"
	},
	"addonProfiles": {
		"azureKeyvaultSecretsProvider": {
			"config": null,
			"enabled": false,
			"identity": null
		},
		"azurepolicy": {
			"config": null,
			"enabled": true,
			"identity": {
				"clientId": "abcdabcd-1234-abcd-1234-abcdabcdabcd",
				"objectId": "abcdabcd-1234-abcd-1234-abcdabcdabcd",
				"resourceId": "/subscriptions/abcdabcd-1234-abcd-1234-abcdabcdabcd/resourcegroups/MC_cloud-shell-storage-westeurope_cc-data-mining-cluster_westeurope/providers/Microsoft.ManagedIdentity/userAssignedIdentities/azurepolicy-cc-data-mining-cluster"
			}
		}
	},
	"agentPoolProfiles": [
		{
			"availabilityZones": null,
			"capacityReservationGroupId": null,
			"count": 1,
			"creationData": null,
			"currentOrchestratorVersion": "1.29.8",
			"enableAutoScaling": false,
			"enableEncryptionAtHost": null,
			"enableFips": false,
			"enableNodePublicIp": false,
			"enableUltraSsd": null,
			"gpuInstanceProfile": null,
			"hostGroupId": null,
			"kubeletConfig": null,
			"kubeletDiskType": "OS",
			"linuxOsConfig": null,
			"maxCount": null,
			"maxPods": 30,
			"minCount": null,
			"mode": "System",
			"name": "datamining",
			"networkProfile": null,
			"nodeImageVersion": "AKSUbuntu-2204gen2containerd-202409.23.0",
			"nodeLabels": null,
			"nodePublicIpPrefixId": null,
			"nodeTaints": null,
			"orchestratorVersion": "1.29.8",
			"osDiskSizeGb": 128,
			"osDiskType": "Managed",
			"osSku": "Ubuntu",
			"osType": "Linux",
			"podSubnetId": null,
			"powerState": {
				"code": "Running"
			},
			"provisioningState": "Succeeded",
			"proximityPlacementGroupId": null,
			"scaleDownMode": null,
			"scaleSetEvictionPolicy": null,
			"scaleSetPriority": null,
			"securityProfile": null,
			"spotMaxPrice": null,
			"tags": null,
			"type": "VirtualMachineScaleSets",
			"upgradeSettings": {
				"drainTimeoutInMinutes": null,
				"maxSurge": "10%",
				"nodeSoakDurationInMinutes": null
			},
			"vmSize": "Standard_D2as_v4",
			"vnetSubnetId": null,
			"windowsProfile": null,
			"workloadRuntime": null
		}
	],
	"apiServerAccessProfile": null,
	"autoScalerProfile": null,
	"autoUpgradeProfile": {
		"nodeOsUpgradeChannel": "NodeImage",
		"upgradeChannel": "patch"
	},
	"azureMonitorProfile": null,
	"azurePortalFqdn": "cc-data-mining-cluster-dns-abcdabcd.portal.hcp.westeurope.azmk8s.io",
	"currentKubernetesVersion": "1.29.8",
	"disableLocalAccounts": true,
	"diskEncryptionSetId": null,
	"dnsPrefix": "cc-data-mining-cluster-dns",
	"enablePodSecurityPolicy": null,
	"enableRbac": true,
	"extendedLocation": null,
	"fqdn": "cc-data-mining-cluster-dns-abcdabcd.hcp.westeurope.azmk8s.io",
	"fqdnSubdomain": null,
	"httpProxyConfig": null,
	"id": "/subscriptions/abcdabcd-1234-abcd-1234-abcdabcdabcd/resourcegroups/cloud-shell-storage-westeurope/providers/Microsoft.ContainerService/managedClusters/cc-data-mining-cluster",
	"identity": {
		"delegatedResources": null,
		"principalId": "abcdabcd-1234-abcd-1234-abcdabcdabcd",
		"tenantId": "abcdabcd-1234-abcd-1234-abcdabcdabcd",
		"type": "SystemAssigned",
		"userAssignedIdentities": null
	},
	"identityProfile": {
		"kubeletidentity": {
			"clientId": "abcdabcd-1234-abcd-1234-abcdabcdabcd",
			"objectId": "abcdabcd-1234-abcd-1234-abcdabcdabcd",
			"resourceId": "/subscriptions/abcdabcd-1234-abcd-1234-abcdabcdabcd/resourcegroups/MC_cloud-shell-storage-westeurope_cc-data-mining-cluster_westeurope/providers/Microsoft.ManagedIdentity/userAssignedIdentities/cc-data-mining-cluster-agentpool"
		}
	},
	"ingressProfile": null,
	"kubernetesVersion": "1.29.8",
	"linuxProfile": null,
	"location": "westeurope",
	"maxAgentPools": 100,
	"metricsProfile": {
		"costAnalysis": {
			"enabled": false
		}
	},
	"name": "cc-data-mining-cluster",
	"networkProfile": {
		"dnsServiceIp": "10.0.0.10",
		"ipFamilies": [
			"IPv4"
		],
		"loadBalancerProfile": {
			"allocatedOutboundPorts": null,
			"backendPoolType": "nodeIPConfiguration",
			"effectiveOutboundIPs": [
				{
					"id": "/subscriptions/abcdabcd-1234-abcd-1234-abcdabcdabcd/resourceGroups/MC_cloud-shell-storage-westeurope_cc-data-mining-cluster_westeurope/providers/Microsoft.Network/publicIPAddresses/abcdabcd-1234-abcd-1234-abcdabcdabcd",
					"resourceGroup": "MC_cloud-shell-storage-westeurope_cc-data-mining-cluster_westeurope"
				}
			],
			"enableMultipleStandardLoadBalancers": null,
			"idleTimeoutInMinutes": null,
			"managedOutboundIPs": {
				"count": 1,
				"countIpv6": null
			},
			"outboundIPs": null,
			"outboundIpPrefixes": null
		},
		"loadBalancerSku": "Standard",
		"natGatewayProfile": null,
		"networkDataplane": "azure",
		"networkMode": null,
		"networkPlugin": "azure",
		"networkPluginMode": "overlay",
		"networkPolicy": "none",
		"outboundType": "loadBalancer",
		"podCidr": "10.244.0.0/16",
		"podCidrs": [
			"10.244.0.0/16"
		],
		"serviceCidr": "10.0.0.0/16",
		"serviceCidrs": [
			"10.0.0.0/16"
		]
	},
	"nodeResourceGroup": "MC_cloud-shell-storage-westeurope_cc-data-mining-cluster_westeurope",
	"oidcIssuerProfile": {
		"enabled": false,
		"issuerUrl": null
	},
	"podIdentityProfile": null,
	"powerState": {
		"code": "Running"
	},
	"privateFqdn": null,
	"privateLinkResources": null,
	"provisioningState": "Succeeded",
	"publicNetworkAccess": null,
	"resourceGroup": "cloud-shell-storage-westeurope",
	"securityProfile": {
		"azureKeyVaultKms": null,
		"defender": null,
		"imageCleaner": {
			"enabled": true,
			"intervalHours": 48
		},
		"workloadIdentity": null
	},
	"serviceMeshProfile": null,
	"servicePrincipalProfile": {
		"clientId": "msi",
		"secret": null
	},
	"sku": {
		"name": "Base",
		"tier": "Standard"
	},
	"storageProfile": {
		"blobCsiDriver": null,
		"diskCsiDriver": {
			"enabled": true
		},
		"fileCsiDriver": {
			"enabled": true
		},
		"snapshotController": {
			"enabled": true
		}
	},
	"supportPlan": "KubernetesOfficial",
	"systemData": null,
	"tags": null,
	"type": "Microsoft.ContainerService/ManagedClusters",
	"upgradeSettings": null,
	"windowsProfile": {
		"adminPassword": null,
		"adminUsername": "azureuser",
		"enableCsiProxy": true,
		"gmsaProfile": null,
		"licenseType": null
	},
	"workloadAutoScalerProfile": {
		"keda": null,
		"verticalPodAutoscaler": null
	}
}

06 Repeat steps no. 4 and 5 for each AKS cluster that you want to configure, available within the selected Azure subscription.

07 Repeat steps no. 3 – 6 for each subscription created in your Microsoft Azure cloud account.

References

Publication date Oct 21, 2024