Install on Azure AKS
Install MAQPNA on Azure Kubernetes Service step by step, with Pod Sandboxing node pools, Azure CNI powered by Cilium, Entra Workload ID, immutable Blob audit storage and a passing doctor check.
This page installs MAQPNA on Azure Kubernetes Service (AKS). You create the cluster with Azure CNI powered by Cilium
and Entra Workload ID, add a Pod Sandboxing node pool for the sandboxes, federate an identity with the gateway, and
install the chart with the values-aks.yaml profile on top of the production profile.
The generic steps (Secrets, values, OIDC, the PostgreSQL state backend) are in Production install with Helm. This page adds what is specific to AKS.
What you get#
| Trust tier | Runtime on AKS | Node pool | RuntimeClass |
|---|---|---|---|
| tier-0 | Kata VM isolation (Pod Sandboxing, Cloud Hypervisor) | Azure Linux pool with --workload-runtime KataVmIsolation |
kata-vm-isolation (created by AKS) |
| tier-1 (microVM) | the same | the same | kata-vm-isolation |
| tier-2 (confidential VM) | not created | — | — |
gVisor is not a supported AKS runtime, so tier-0 also runs in a Kata VM: stronger isolation, a little more overhead.
The profile turns off all three of the chart's own RuntimeClasses because AKS creates kata-vm-isolation itself.
Plan for the Pod Sandboxing limits: each pod VM gets memory equal to the pod's memory limit and whole vCPUs, so set
limits in Agent.spec.resources. Arm64, FIPS and Trusted Launch pools cannot run Kata. Defender for Containers does
not assess Kata pods; the MAQPNA audit ledger still records every call they make.
flowchart LR
subgraph AKS["AKS cluster (one region)"]
subgraph SYS["System pool"]
OP[operator]
GW["gateway x3"]
ID[identity broker]
end
subgraph SB["Pod Sandboxing pool"]
S0["tier-0 / tier-1 sandboxes<br/>kata-vm-isolation"]
end
end
S0 -->|"Cilium default-deny"| GW
GW -->|"Entra Workload ID"| BLOB[("Blob immutable storage<br/>audit ledger")]
GW -->|"Entra Workload ID"| KV["Key Vault / Managed HSM<br/>checkpoint key"]
GW --> PG[("Azure Database<br/>for PostgreSQL")]
GW --> SEN["Microsoft Sentinel<br/>(optional SIEM)"]
ACR[("ACR mirror")] -.-> SYS
Before you begin#
- Azure CLI 2.60 or later (add
aks-previewwithaz extension add --name aks-previewif your region still has the features in preview),kubectl,helm3.14 or later. - The
maqpnaCLI and itsmaqpna-installplugin, version 2.1.0:curl -fsSL https://maqpna.com/install.sh | sh. REGISTRYset to the release registry listed on maqpna.com/download.
RG=maqpna-eu LOC=germanywestcentral CLUSTER=maqpna V=2.1.0
export REGISTRY=<release-registry-from-maqpna.com/download>
az group create -n "$RG" -l "$LOC"
Step 1: Create the cluster#
Azure CNI powered by Cilium enforces NetworkPolicy; Advanced Container Networking Services (ACNS) adds FQDN policies. The OIDC issuer and workload identity let the gateway get Entra tokens without a secret.
az aks create -g "$RG" -n "$CLUSTER" -l "$LOC" \
--os-sku AzureLinux \
--network-plugin azure --network-plugin-mode overlay --network-dataplane cilium \
--enable-acns \
--node-count 3 --node-vm-size Standard_D4ds_v5 \
--enable-oidc-issuer --enable-workload-identity \
--generate-ssh-keys
The system pool runs the MAQPNA control plane: operator, gateway, identity broker and, if enabled, the attestation service.
Step 2: Add the Pod Sandboxing node pool#
Use a Gen2 size with nested virtualization, such as Dsv5, Ddsv5 or Dadsv7:
az aks nodepool add -g "$RG" --cluster-name "$CLUSTER" -n sandbox \
--os-sku AzureLinux --workload-runtime KataVmIsolation \
--node-vm-size Standard_D8ds_v5 --node-count 2 \
--labels maqpna.com/pool=sandbox
az aks get-credentials -g "$RG" -n "$CLUSTER"
kubectl get runtimeclass # expect kata-vm-isolation
Step 3: Federate an identity with the gateway#
Create a user-assigned managed identity and a federated credential for the gateway's ServiceAccount:
az identity create -g "$RG" -n maqpna-gateway
az identity federated-credential create --name maqpna-gateway \
--identity-name maqpna-gateway --resource-group "$RG" \
--issuer "$(az aks show -g "$RG" -n "$CLUSTER" --query oidcIssuerProfile.issuerUrl -o tsv)" \
--subject system:serviceaccount:maqpna-system:maqpna-gateway \
--audiences api://AzureADTokenExchange
CLIENT_ID=$(az identity show -g "$RG" -n maqpna-gateway --query clientId -o tsv)
Grant it Key Vault Crypto User on the vault that holds the checkpoint key, and Storage Blob Data Contributor on the audit container. AKS identity bindings and managed identity through IMDS are covered in Cloud identity & keys.
Step 4: Create the immutable audit container#
The gateway requires version-level immutability support on the container and checks it at startup:
az storage container-rm create --storage-account acmeaudit -n maqpna-audit --enable-vlw
Step 5: Mirror the images into ACR (optional)#
For an air-gapped or sovereignty-restricted cluster, mirror everything into a Premium ACR in the same region and verify each image first:
ACR=maqpnaeu$RANDOM
az acr create -g "$RG" -n "$ACR" --sku Premium --public-network-enabled false
az aks update -g "$RG" -n "$CLUSTER" --attach-acr "$ACR"
for img in maqpna-operator maqpna-gateway maqpna-identity maqpna-attest maqpna-attest-agent \
maqpna-exec maqpna-egress-relay maqpna-browser maqpna-code-interpreter; do
maqpna verify image "$REGISTRY/$img:$V"
az acr import -n "$ACR" --source "$REGISTRY/$img:$V" --image "maqpna/$img:$V"
done
Fully disconnected: see Air-gapped install.
Step 6: Install the agent-sandbox controller#
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.6/sandbox-with-extensions.yaml
Step 7: Create the Secrets and your values file#
helm pull "oci://$REGISTRY/charts/maqpna" --version "$V" --untar
grep -n CHANGE-ME maqpna/values-production.yaml maqpna/values-aks.yaml
Create the Secrets as in
Production install, step 1. The
identity signing key is Ed25519, which Key Vault cannot hold, so it stays a Secret you sync from your key store.
Then write my-values.yaml. The client-id annotation and the pod label always go together:
gateway:
podLabels:
azure.workload.identity/use: "true"
serviceAccount:
annotations:
azure.workload.identity/client-id: "<CLIENT_ID>"
audit:
sink: worm
worm:
provider: azure
endpoint: https://acmeaudit.blob.core.windows.net
bucket: maqpna-audit # the container
credentials: azure # keyless, no Secret
checkpointSigning:
enabled: true
keyURI: kms://azure/<vault>/maqpna-audit # EC-HSM P-256 key, signs ES256
networkPolicy:
agentNamespaces:
names: [team-a]
# With an ACR mirror:
# image:
# registry: <ACR>.azurecr.io/maqpna
Key Vault and Managed HSM have no Ed25519, so checkpoints, evidence packs and usage reports are signed ES256; every
verifier accepts it. For a sovereign installation, use --profile sovereign-eu in step 8 and set
airgap.imageRegistry and sovereignty.allowedRegistries to the ACR.
Step 8: Validate, preflight and install#
maqpna values validate --chart ./maqpna --profile production \
-f maqpna/values-production.yaml -f maqpna/values-aks.yaml -f my-values.yaml
maqpna preflight --chart ./maqpna --profile prod -f maqpna/values-aks.yaml -f my-values.yaml --strict
maqpna install --chart ./maqpna --profile prod -f maqpna/values-aks.yaml -f my-values.yaml \
--license maqpna-license.jws --wait --timeout 15m
Preflight finds the cilium DaemonSet in kube-system, so cni-networkpolicy passes. Because the profile turns off
the chart's RuntimeClasses, preflight has no runtimeclass-* check here; maqpna doctor checks
runtimeclass-per-tier after the install.
Step 9: Expose the gateway#
The chart creates a ClusterIP Service and no Ingress. Keep it, and add your own Service for an internal Azure load
balancer (with gateway.service.type: LoadBalancer the chart drops the admin port from its Service):
apiVersion: v1
kind: Service
metadata:
name: maqpna-gateway-lb
namespace: maqpna-system
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
type: LoadBalancer
selector:
app.kubernetes.io/name: maqpna-gateway
ports:
- {name: http, port: 443, targetPort: 8080, protocol: TCP}
Add a NetworkPolicy that admits the load balancer's traffic on port 8080, and pick a TLS option from
Ingress and TLS. For FQDN egress filtering of the gateway with
ACNS, set networkPolicy.fqdnEgress.enabled: true (provider cilium) after listing your hosts in
sovereignty.allowedEgressHosts (FQDN egress policies).
Step 10: Verify#
kubectl -n maqpna-system get pods
kubectl get trusttiers
helm test maqpna -n maqpna-system
kubectl -n maqpna-system port-forward svc/maqpna-gateway 9090:9090 &
maqpna doctor -n maqpna-system --gateway http://127.0.0.1:9090 --oidc-token-file admin.tok --strict
Start a tier-0 session and confirm the pod landed on the sandbox pool with the Kata runtime:
kubectl get pods -A -o custom-columns=NS:.metadata.namespace,POD:.metadata.name,RC:.spec.runtimeClassName,NODE:.spec.nodeName
To prove keyless credentials, the immutability check and egress blocking end to end, run hack/cloud-smoke.sh aks
from the MAQPNA source.
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
kubectl get runtimeclass shows no kata-vm-isolation |
The sandbox pool was created without --workload-runtime KataVmIsolation, or on an unsupported size |
Recreate the pool on Azure Linux with a Gen2 size that supports nested virtualization. |
Agent process OOMKilled in the sandbox |
Pod VMs get exactly the pod's memory limit | Set realistic limits in Agent.spec.resources. |
tier-0 or tier-1 sessions Pending |
No node in the Kata pool, or the pool is full | kubectl get nodes -l maqpna.com/pool=sandbox; scale the pool. |
| Gateway fails at startup naming immutability | The container has no version-level immutability support | Create it with --enable-vlw (or the account with --enable-alw). |
Blob or Key Vault calls return 401 or 403 |
Pod label or annotation missing, or the federated subject does not match | Set both azure.workload.identity/use and client-id; the subject must be system:serviceaccount:maqpna-system:maqpna-gateway. |
doctor: runtimeclass-per-tier fails |
A TrustTier names a RuntimeClass that does not exist | Check kubectl get trusttiers against kubectl get runtimeclass. |
| Images fail to pull from ACR | ACR not attached or private endpoint not reachable | az aks check-acr -g "$RG" -n "$CLUSTER" --acr "$ACR.azurecr.io". |
More: Troubleshooting.
Next steps#
- Production checklist
- Cloud identity & keys: Key Vault keys, identity bindings, token exchange.
- Cloud profiles: the Sentinel SIEM sink and FQDN egress.
- Upgrade and rollback