Azure Deployment
DeepIntShield Enterprise images for Azure customers are distributed through GCP Artifact Registry, using Azure Workload Identity Federation for secure, credential-less authentication.
Architecture
Section titled “Architecture”flowchart LR subgraph Azure[Azure Subscription] subgraph AKS[AKS Cluster] Pod[DeepIntShield Pod] KSA[K8s ServiceAccount] end MI[Managed Identity] end
subgraph GCP[GCP Project] WIF[Workload Identity<br/>Federation Pool] GSA[GCP Service Account] AR[Artifact Registry<br/>DeepIntShield Images] end
KSA -->|Federated| MI MI -->|OIDC Token| WIF WIF -->|Exchange| GSA GSA -->|Pull Permission| AR AR -->|Image| PodHow It Works
Section titled “How It Works”Azure Workload Identity Federation allows Azure Managed Identities to authenticate to GCP without exchanging credentials:
- AKS Pod requests a token using its Kubernetes ServiceAccount
- Azure AD issues an OIDC token for the Managed Identity
- GCP Workload Identity Federation validates the Azure token
- GCP STS exchanges it for a GCP access token
- Pod uses the GCP token to pull images from Artifact Registry
Prerequisites
Section titled “Prerequisites”- AKS cluster (v1.24+) with Workload Identity enabled
- Azure CLI configured with appropriate permissions
kubectlconfigured for your AKS cluster- Your Azure Tenant ID and Managed Identity Client ID provided to DeepIntShield team
Step 1: Enable Workload Identity on AKS
Section titled “Step 1: Enable Workload Identity on AKS”If not already enabled, enable Workload Identity on your AKS cluster:
# For existing clusteraz aks update \ --resource-group YOUR_RESOURCE_GROUP \ --name YOUR_CLUSTER_NAME \ --enable-oidc-issuer \ --enable-workload-identity
# Get the OIDC issuer URLaz aks show \ --resource-group YOUR_RESOURCE_GROUP \ --name YOUR_CLUSTER_NAME \ --query "oidcIssuerProfile.issuerUrl" -o tsvStep 2: Create Azure Managed Identity
Section titled “Step 2: Create Azure Managed Identity”# Create Managed Identityaz identity create \ --name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUP \ --location YOUR_LOCATION
# Get the Client IDCLIENT_ID=$(az identity show \ --name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUP \ --query clientId -o tsv)
echo "Client ID: $CLIENT_ID"Step 3: Create Federated Credential
Section titled “Step 3: Create Federated Credential”Link the Kubernetes ServiceAccount to the Azure Managed Identity:
# Get AKS OIDC issuerAKS_OIDC_ISSUER=$(az aks show \ --resource-group YOUR_RESOURCE_GROUP \ --name YOUR_CLUSTER_NAME \ --query "oidcIssuerProfile.issuerUrl" -o tsv)
# Create federated credentialaz identity federated-credential create \ --name deepintshield-federated-credential \ --identity-name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUP \ --issuer "$AKS_OIDC_ISSUER" \ --subject "system:serviceaccount:deepintshield:deepintshield-sa" \ --audience "api://AzureADTokenExchange"Step 4: Provide Details to DeepIntShield Team
Section titled “Step 4: Provide Details to DeepIntShield Team”Send the following information to the DeepIntShield team:
# Get Tenant IDaz account show --query tenantId -o tsv
# Get Client IDaz identity show \ --name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUP \ --query clientId -o tsvThe DeepIntShield team will configure GCP Workload Identity Federation to trust your Azure Managed Identity.
Step 5: Store GCP Credential Configuration
Section titled “Step 5: Store GCP Credential Configuration”After the DeepIntShield team configures access, they will provide a credential configuration. Store it as a ConfigMap:
apiVersion: v1kind: ConfigMapmetadata: name: gcp-credential-config namespace: deepintshielddata: credential-config.json: | { "type": "external_account", "audience": "//iam.googleapis.com/projects/DEEPINTSHIELD_PROJECT_NUMBER/locations/global/workloadIdentityPools/YOUR_HUB_SLUG-azure-pool/providers/YOUR_HUB_SLUG-azure-provider", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/DEEPINTSHIELD_SA@DEEPINTSHIELD_PROJECT.iam.gserviceaccount.com:generateAccessToken", "token_url": "https://sts.googleapis.com/v1/token", "credential_source": { "file": "/var/run/secrets/azure/tokens/azure-identity-token", "format": { "type": "text" } } }Step 6: Create Kubernetes ServiceAccount
Section titled “Step 6: Create Kubernetes ServiceAccount”apiVersion: v1kind: ServiceAccountmetadata: name: deepintshield-sa namespace: deepintshield annotations: azure.workload.identity/client-id: YOUR_MANAGED_IDENTITY_CLIENT_ID labels: azure.workload.identity/use: "true"Step 7: Create Image Pull Secret with Token Refresh
Section titled “Step 7: Create Image Pull Secret with Token Refresh”Create a CronJob to refresh the imagePullSecret using the federated identity:
apiVersion: batch/v1kind: CronJobmetadata: name: refresh-ar-secret namespace: deepintshieldspec: schedule: "*/30 * * * *" # Every 30 minutes successfulJobsHistoryLimit: 1 failedJobsHistoryLimit: 3 jobTemplate: spec: template: metadata: labels: azure.workload.identity/use: "true" spec: serviceAccountName: deepintshield-sa containers: - name: token-refresh image: google/cloud-sdk:slim command: ["/bin/bash", "-c"] args: - | set -e
# Set GCP credential config export GOOGLE_APPLICATION_CREDENTIALS=/etc/gcp/credential-config.json
# Get GCP access token via federation TOKEN=$(gcloud auth print-access-token)
# Delete existing secret if it exists kubectl delete secret ar-pull-secret --ignore-not-found -n deepintshield
# Create new imagePullSecret kubectl create secret docker-registry ar-pull-secret \ --docker-server=REGION-docker.pkg.dev \ --docker-username=oauth2accesstoken \ --docker-password="$TOKEN" \ -n deepintshield
echo "Secret refreshed at $(date)" volumeMounts: - name: gcp-credential-config mountPath: /etc/gcp readOnly: true - name: azure-identity-token mountPath: /var/run/secrets/azure/tokens readOnly: true volumes: - name: gcp-credential-config configMap: name: gcp-credential-config - name: azure-identity-token projected: sources: - serviceAccountToken: path: azure-identity-token expirationSeconds: 3600 audience: api://AzureADTokenExchange restartPolicy: OnFailure---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: secret-manager namespace: deepintshieldrules:- apiGroups: [""] resources: ["secrets"] verbs: ["get", "create", "delete"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: secret-manager-binding namespace: deepintshieldsubjects:- kind: ServiceAccount name: deepintshield-sa namespace: deepintshieldroleRef: kind: Role name: secret-manager apiGroup: rbac.authorization.k8s.ioStep 8: Deploy DeepIntShield
Section titled “Step 8: Deploy DeepIntShield”apiVersion: apps/v1kind: Deploymentmetadata: name: deepintshield namespace: deepintshieldspec: replicas: 2 selector: matchLabels: app: deepintshield template: metadata: labels: app: deepintshield azure.workload.identity/use: "true" spec: serviceAccountName: deepintshield-sa imagePullSecrets: - name: ar-pull-secret containers: - name: deepintshield image: REGION-docker.pkg.dev/DEEPINTSHIELD_PROJECT/YOUR_HUB_SLUG/deepintshield:latest ports: - containerPort: 8080 name: http resources: requests: cpu: "250m" memory: "512Mi" limits: cpu: "1000m" memory: "2Gi" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 volumeMounts: - name: config mountPath: /app/data/config.json subPath: config.json volumes: - name: config secret: secretName: deepintshield-config---apiVersion: v1kind: Servicemetadata: name: deepintshield namespace: deepintshieldspec: selector: app: deepintshield ports: - port: 80 targetPort: 8080 protocol: TCP type: ClusterIPBootstrap: Initial Secret Creation
Section titled “Bootstrap: Initial Secret Creation”Before the first deployment, manually trigger the CronJob or create the secret:
# Create namespacekubectl create namespace deepintshield
# Apply all configurationskubectl apply -f configmap.yamlkubectl apply -f serviceaccount.yamlkubectl apply -f cronjob.yaml
# Manually trigger the CronJobkubectl create job --from=cronjob/refresh-ar-secret initial-refresh -n deepintshield
# Wait for completionkubectl wait --for=condition=complete job/initial-refresh -n deepintshield --timeout=120s
# Verify secret was createdkubectl get secret ar-pull-secret -n deepintshieldVerifying Access
Section titled “Verifying Access”Check Workload Identity Configuration
Section titled “Check Workload Identity Configuration”# Verify AKS has Workload Identity enabledaz aks show \ --resource-group YOUR_RESOURCE_GROUP \ --name YOUR_CLUSTER_NAME \ --query "oidcIssuerProfile.enabled" -o tsv
# Check federated credentialaz identity federated-credential show \ --name deepintshield-federated-credential \ --identity-name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUPVerify Token Exchange
Section titled “Verify Token Exchange”# Check CronJob ran successfullykubectl get jobs -n deepintshield
# View CronJob logskubectl logs -l job-name=refresh-ar-secret -n deepintshield
# Verify imagePullSecret existskubectl get secret ar-pull-secret -n deepintshield -o yamlTroubleshooting
Section titled “Troubleshooting”ImagePullBackOff Errors
Section titled “ImagePullBackOff Errors”- Check imagePullSecret exists:
kubectl get secret ar-pull-secret -n deepintshield - Verify CronJob succeeded:
kubectl get jobs -n deepintshield - Check Azure Workload Identity: Ensure labels are set correctly
# Check pod eventskubectl describe pod -l app=deepintshield -n deepintshield
# Check ServiceAccount has correct annotationskubectl get sa deepintshield-sa -n deepintshield -o yamlToken Exchange Failures
Section titled “Token Exchange Failures”# Check CronJob logs for errorskubectl logs -l job-name=refresh-ar-secret -n deepintshield
# Common issues:# - "audience mismatch": Check credential-config.json audience field# - "subject mismatch": Verify federated credential subject matches SA# - "permission denied": Contact DeepIntShield team to verify WIF configurationAzure Workload Identity Issues
Section titled “Azure Workload Identity Issues”# Verify Managed Identity existsaz identity show \ --name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUP
# Check federated credentialsaz identity federated-credential list \ --identity-name deepintshield-pull-identity \ --resource-group YOUR_RESOURCE_GROUP
# Verify pod has identity token mountedkubectl exec -it deployment/deepintshield -n deepintshield -- \ ls -la /var/run/secrets/azure/tokens/Summary
Section titled “Summary”| Component | Value |
|---|---|
| Registry | GCP Artifact Registry |
| Authentication | Azure WIF -> GCP WIF -> GCP SA |
| Token Lifetime | 60 minutes (auto-refreshed every 30 min) |
| Secret Name | ar-pull-secret |
Next Steps
Section titled “Next Steps”- Configure DeepIntShield settings for your use case
- Set up observability for monitoring
- Enable clustering for high availability