Install on Red Hat OpenShift
Install MAQPNA on OpenShift (OCP, ROSA, ARO) step by step, with OpenShift sandboxed containers, the default restricted-v2 SCC, OVN-Kubernetes network policy, a Route for the gateway and an optional attested tier with Trustee.
This page installs MAQPNA on Red Hat OpenShift: OpenShift Container Platform, ROSA, ARO or OpenShift on IBM Cloud.
You install OpenShift sandboxed containers for the Kata runtime, install the chart with the values-openshift.yaml
profile on top of the production profile, and expose the gateway with a Route. Every MAQPNA pod runs under the
default restricted-v2 security context constraint (SCC); you grant no extra SCC.
The generic steps (Secrets, values, OIDC, the PostgreSQL state backend) are in Production install with Helm. This page adds what is specific to OpenShift.
What you get#
| Trust tier | Runtime on OpenShift | RuntimeClass |
|---|---|---|
| tier-0 | Kata (Red Hat ships no gVisor) | kata (bare metal or nested-virt workers), or kata-remote (peer pods on AWS or Azure) |
| tier-1 (microVM) | Kata | kata or kata-remote |
| tier-2 (confidential VM) | Kata with confidential containers and the Red Hat build of Trustee | kata-remote, commented out in the profile until you deploy it |
The profile also:
- removes the pinned UID, GID and fsGroup 65532 (
podSecurityContext.runAsUser: null, and the same forrunAsGroupandfsGroup), so OpenShift assigns them from the namespace's range; - sets
operator.sandboxRunAsUID: -1, so the operator also leaves the UID of sandbox pods to OpenShift; - turns off the chart's three RuntimeClasses;
- puts the gateway's ledger mirror on the cluster's default storage class (
gateway.auditStorage.storageClassName: "").
The images run unchanged under an arbitrary UID: the binaries are world-readable and every writable path is a volume owned by the assigned fsGroup.
flowchart LR
RT["Router<br/>(openshift-ingress)"] -->|"Route, TLS"| GW
subgraph NS["maqpna-system (restricted-v2)"]
OP[operator]
GW["gateway x3"]
ID[identity broker]
AT["attestation service"]
end
subgraph WK["Kata workers (KataConfig)"]
S0["tier-0 / tier-1 sandboxes<br/>kata"]
S2["tier-2 sandboxes<br/>kata-remote + CoCo"]
end
S0 -->|"OVN NetworkPolicy"| GW
S2 --> GW
S2 -.->|evidence| AT
AT --> TR["Trustee<br/>(Red Hat build)"]
GW --> PG[("PostgreSQL")]
Before you begin#
oc,helm3.14 or later, and cluster-admin rights.- 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.- Workers that can run Kata: bare metal, or nested virtualization. For peer pods on AWS or Azure, follow Red Hat's peer-pods guide.
V=2.1.0
export REGISTRY=<release-registry-from-maqpna.com/download>
Step 1: Install OpenShift sandboxed containers#
- Install OpenShift sandboxed containers from OperatorHub.
- Create a
KataConfig. To limit Kata to some workers, setkataConfigPoolSelectorto a node label. - Wait until the nodes have rebooted and the RuntimeClass exists:
oc get runtimeclass # expect kata (and kata-remote for peer pods)
For peer pods (kata-remote), edit values-openshift.yaml or your values so tier-0 and tier-1 name kata-remote.
Step 2: Deploy confidential containers (tier-2, optional)#
Follow Red Hat's "Deploying confidential containers" guide for your platform (Azure DCasv5/ECasv5, or bare-metal AMD SEV-SNP or Intel TDX). It adds a Trustee operator, which the MAQPNA attestation service uses as its verifier. Skip this step if you do not need an attested tier.
Step 3: Create the project and install the agent-sandbox controller#
oc new-project maqpna-system
oc apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.6/sandbox-with-extensions.yaml
Step 4: Mirror the images (disconnected clusters)#
Connected clusters pull from the release registry. For a disconnected cluster, mirror the images into the internal
registry or Quay with oc mirror, or with maqpna airgap as described in
Air-gapped install, then set airgap.imageRegistry to the mirror.
Step 5: Create the Secrets and your values file#
helm pull "oci://$REGISTRY/charts/maqpna" --version "$V" --untar
grep -n CHANGE-ME maqpna/values-production.yaml
Create the Secrets as in
Production install, step 1. Then write
my-values.yaml. To turn on tier-2 once confidential containers and Trustee run, add it to the tier list. Helm
replaces lists as a whole, so repeat tier-0 and tier-1:
trustTiers:
create: true
items:
- name: tier-0
spec: {runtimeClassName: kata, isolation: microvm, egress: gateway-only}
- name: tier-1
spec: {runtimeClassName: kata, isolation: microvm, egress: gateway-only}
- name: tier-2
spec: {runtimeClassName: kata-remote, isolation: confidential, attestationRequired: true, egress: gateway-only}
attestation:
enabled: true
verifier: trustee
trusteeURL: https://kbs.trustee-operator-system.svc:8080 # your Trustee service
trusteeJWKS:
existingSecret: maqpna-trustee-jwks
referenceValues:
allowedMeasurements: ["<launch measurement of your guest image>"]
networkPolicy:
agentNamespaces:
names: [team-a]
An empty allowedMeasurements list rejects every report (fail closed).
Step 6: Validate, preflight and install#
maqpna values validate --chart ./maqpna --profile production \
-f maqpna/values-production.yaml -f maqpna/values-openshift.yaml -f my-values.yaml
maqpna preflight --chart ./maqpna --profile prod -f maqpna/values-openshift.yaml -f my-values.yaml
maqpna install --chart ./maqpna --profile prod -f maqpna/values-openshift.yaml -f my-values.yaml \
--license maqpna-license.jws --wait --timeout 15m
Check that every pod got restricted-v2:
oc get pods -n maqpna-system -o custom-columns=NAME:.metadata.name,SCC:.metadata.annotations.openshift\.io/scc
Step 7: Expose the gateway with a Route#
The chart creates a ClusterIP Service (maqpna-gateway, port 8080) and no Ingress or Route. Create a Route with
edge TLS (or passthrough when gateway.tls.enabled terminates TLS at the gateway):
oc -n maqpna-system create route edge maqpna-gateway --service maqpna-gateway --port http \
--hostname gateway.apps.example.com
The chart's NetworkPolicy admits only agent namespaces and the release namespace. Admit the OpenShift router too:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: maqpna-gateway-from-router
namespace: maqpna-system
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: maqpna-gateway
policyTypes: ["Ingress"]
ingress:
- from:
- namespaceSelector:
matchLabels:
policy-group.network.openshift.io/ingress: ""
ports:
- {protocol: TCP, port: 8080}
OVN EgressFirewall rules with dnsName apply to a whole namespace; MAQPNA does not render them. The gateway still
enforces its own egress allowlist (sovereignty.allowedEgressHosts).
Step 8: Verify#
oc -n maqpna-system get pods
oc get trusttiers
helm test maqpna -n maqpna-system
oc -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
oc get pods -A -o custom-columns=NS:.metadata.namespace,POD:.metadata.name,RC:.spec.runtimeClassName,NODE:.spec.nodeName
With tier-2 on, doctor also checks attest-verifier (must be trustee) and attest-response-signing.
Upgrading from an older guide#
Earlier versions of the OpenShift guide asked you to grant nonroot-v2 to every ServiceAccount. Remove those grants
after you move to the profile:
oc adm policy remove-scc-from-user nonroot-v2 -z <service-account> -n maqpna-system
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
Pods rejected: unable to validate against any security context constraint, runAsUser: Invalid value: 65532 |
The OpenShift profile is missing, so the UID pin is still set | Add -f maqpna/values-openshift.yaml. |
| Sandbox pods rejected for their UID | operator.sandboxRunAsUID is not -1 |
Keep the profile's operator.sandboxRunAsUID: -1. |
Sandbox pods Pending: no node matches |
The kata RuntimeClass schedules only on KataConfig nodes |
Check kataConfigPoolSelector and that the nodes finished rebooting. |
tier-2 sessions fail with reason AttestationFailed |
Trustee unreachable, wrong JWKS, or measurements not listed | Follow the attestation failure runbook. |
Route returns 503 |
The router is blocked by NetworkPolicy | Apply the router NetworkPolicy from step 7. |
pod has unbound immediate PersistentVolumeClaims |
No default StorageClass | Set gateway.auditStorage.storageClassName to an existing class (ODF, gp3-csi, managed-csi). |
More: Troubleshooting.