Scanning Images from Azure ACR
Give the rad-sbom plugin pull access to a private Azure Container Registry with AKS Workload Identity so RAD Security can scan its images
The rad-sbom plugin generates an SBOM (Software Bill of Materials) for every container image running in your cluster. It downloads images directly from registry APIs, so it needs credentials for private registries.
rad-sbom pulls images itself, independently of the kubelet. Attaching an ACR to your cluster with az aks update --attach-acr only grants pull access to the node's kubelet identity, not to rad-sbom. You must give rad-sbom its own identity with pull access to the registry.
The recommended approach is Microsoft Entra Workload ID: a user-assigned managed identity federated with the rad-sbom Kubernetes service account. No long-lived credentials are stored in the cluster.
How it works
When rad.azureWorkloadIdentityClientId is set, the rad-plugins Helm chart:
- annotates the
rad-sbomservice account withazure.workload.identity/client-id: <client-id> - labels the
rad-sbompod withazure.workload.identity/use: "true"
The AKS workload identity webhook then injects a federated token into the rad-sbom pod, which rad-sbom exchanges for an Entra ID token to authenticate to ACR.
Prerequisites
- An AKS cluster with the OIDC issuer and Workload Identity enabled
- Azure CLI, logged in with permissions to create managed identities and role assignments on the registry
- Helm CLI
- RAD Security API credentials
Configuration steps
Set these variables once. They are used by all the commands below:
export RG=<aks-resource-group>
export CLUSTER_NAME=<aks-cluster-name>
export LOCATION=<azure-region>
export ACR_NAME=<acr-name>
# Namespace where rad-plugins is (or will be) installed - the Helm release namespace
export NAMESPACE=rad
Enable OIDC issuer and Workload Identity on the cluster
Check whether both features are already enabled:
az aks show -g $RG -n $CLUSTER_NAME \
--query "{oidcIssuer: oidcIssuerProfile.enabled, workloadIdentity: securityProfile.workloadIdentity.enabled}" -o table
If either value is not True, enable them:
az aks update -g $RG -n $CLUSTER_NAME --enable-oidc-issuer --enable-workload-identity
Create a managed identity for rad-sbom
az identity create --name rad-sbom -g $RG --location $LOCATION
export RAD_SBOM_CLIENT_ID="$(az identity show -g $RG --name rad-sbom --query clientId -o tsv)"
export RAD_SBOM_PRINCIPAL_ID="$(az identity show -g $RG --name rad-sbom --query principalId -o tsv)"
echo $RAD_SBOM_CLIENT_ID
Note the client ID. You will need it in the Helm values.
Grant the identity pull access to the registry
export ACR_ID="$(az acr show --name $ACR_NAME --query id -o tsv)"
az role assignment create \
--assignee-object-id $RAD_SBOM_PRINCIPAL_ID \
--assignee-principal-type ServicePrincipal \
--role "AcrPull" \
--scope $ACR_ID
az acr show resolves the registry's resource ID, so this works even when the registry lives in a different resource group or subscription than the cluster.
Repeat this step for every registry your workloads pull from. A role assignment is scoped to a single registry.
If the registry uses the RBAC Registry + ABAC Repository Permissions role assignment mode, AcrPull has no effect. Assign the Container Registry Repository Reader role instead. Check the mode with az acr show --name $ACR_NAME --query roleAssignmentMode -o tsv.
Federate the identity with the rad-sbom service account
export AKS_OIDC_ISSUER="$(az aks show -g $RG -n $CLUSTER_NAME --query oidcIssuerProfile.issuerUrl -o tsv)"
az identity federated-credential create \
--name rad-sbom-to-${CLUSTER_NAME} \
--identity-name rad-sbom \
--resource-group $RG \
--issuer ${AKS_OIDC_ISSUER} \
--subject system:serviceaccount:${NAMESPACE}:rad-sbom \
--audience api://AzureADTokenExchange
The subject must match exactly: system:serviceaccount:<release-namespace>:rad-sbom. <release-namespace> is the namespace you install rad-plugins into. If it does not match, authentication fails and private images are not scanned.
Install or upgrade rad-plugins
Add the client ID to your values.yaml:
rad:
apiUrl: "https://api.rad.security"
base64AccessKeyId: "<rad-access-key-id>"
base64SecretKey: "<rad-secret-key>"
clusterName: "<cluster-name>"
azureWorkloadIdentityClientId: "<rad-sbom-client-id>"
Then install or upgrade the chart into $NAMESPACE:
helm repo add rad-security https://charts.rad.security/stable
helm repo update
helm upgrade --install rad rad-security/rad-plugins \
--namespace $NAMESPACE \
--create-namespace \
-f values.yaml
See ArtifactHub for all chart options.
If rad-plugins was already running when you created the federated credential or role assignment, restart rad-sbom so it picks up the new configuration: kubectl rollout restart deployment/rad-sbom -n $NAMESPACE. Role assignments can take a few minutes to propagate.
Verify the setup
Check that the workload identity webhook injected the identity into the rad-sbom pod:
kubectl get sa rad-sbom -n $NAMESPACE -o jsonpath='{.metadata.annotations.azure\.workload\.identity/client-id}{"
"}'
kubectl get pod -n $NAMESPACE -l app_name=rad-sbom \
-o jsonpath='{.items[0].spec.containers[0].env[?(@.name=="AZURE_CLIENT_ID")].value}{"
"}'
Both commands should print the rad-sbom client ID. If the second one prints nothing, the webhook did not mutate the pod. Confirm Workload Identity is enabled on the cluster, then restart the deployment.
Then deploy a workload that uses an image from your ACR and check the rad-sbom logs for errors:
kubectl logs -n $NAMESPACE deployment/rad-sbom --since=10m | grep -i -E "error|unauthorized|denied"
For more detail, set sbom.env.LOG_LEVEL: debug in your values and upgrade the release.