MAQPNADocs

Cloud identity & keys#

MAQPNA components get cloud credentials the way the cloud intends: from the pod's workload identity, never from a long-lived key in a Secret. One credential layer (pkg/cloudcreds) serves every component that calls a cloud API: KMS signing, keyless WORM shipping and the identity broker's token exchange. On top of it, signing keys can stay non-exportable in AWS KMS, Google Cloud KMS, Azure Key Vault or HashiCorp Vault / OpenBao Transit.

flowchart LR
  SA["ServiceAccount<br/>annotations / pod labels"] --> WI["Workload identity<br/>Pod Identity · IRSA · GKE WIF · Entra WI · OKE WI"]
  WI --> CC["pkg/cloudcreds<br/>cached, refreshed, single-flight"]
  CC --> KMS["kms:// signers<br/>checkpoints, evidence, tokens"]
  CC --> WORM["WORM sink<br/>S3 Object Lock, keyless"]
  CC --> EX["identity broker<br/>aws-sts · gcp-sts · azure-entra"]

How a component finds its credentials#

Each component reads the standard environment that the cloud's admission webhook or agent injects. Nothing is configured in MAQPNA beyond the ServiceAccount annotation or pod label the cloud asks for.

Cloud Mechanism What the pod gets Order
AWS Static keys (development only) AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN) 1
AWS IRSA AWS_ROLE_ARN + AWS_WEB_IDENTITY_TOKEN_FILE (from the eks.amazonaws.com/role-arn annotation) → STS AssumeRoleWithWebIdentity 2
AWS EKS Pod Identity (preferred) AWS_CONTAINER_CREDENTIALS_FULL_URI + AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (Pod Identity agent) 3
AWS IMDSv2 (node instance role) Off by default; MAQPNA_AWS_IMDS=true or cloudCredentials.aws.imds 4
Google Cloud Workload Identity Federation for GKE Metadata server token (GCE_METADATA_HOST overrides the host) —
Google Cloud Non-GKE clusters GOOGLE_APPLICATION_CREDENTIALS → an external_account JSON (file-sourced subject token, optional service account impersonation) —
Azure Entra Workload ID AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE, AZURE_AUTHORITY_HOST → client-credentials token with a client assertion 1
Azure AKS identity bindings Opt in with MAQPNA_AZURE_IDENTITY_BINDING=true; uses AZURE_KUBERNETES_TOKEN_PROXY, AZURE_KUBERNETES_SNI_NAME, AZURE_KUBERNETES_CA_FILE 1
Azure Managed identity (IMDS) Off by default (it is the node's identity); MAQPNA_AZURE_MANAGED_IDENTITY=true 2
OCI OKE workload identity MAQPNA_OCI_AUTH=oke-workload-identity (the pod's ServiceAccount token, exchanged in-cluster) —
OCI Resource principal v2.2 MAQPNA_OCI_AUTH=resource-principal with OCI_RESOURCE_PRINCIPAL_VERSION=2.2, OCI_RESOURCE_PRINCIPAL_RPST, OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM, OCI_RESOURCE_PRINCIPAL_REGION —
Vault / OpenBao Kubernetes auth or a token VAULT_ADDR, VAULT_NAMESPACE, VAULT_CACERT; MAQPNA_VAULT_K8S_ROLE (+ MAQPNA_VAULT_K8S_MOUNT, default kubernetes) or VAULT_TOKEN_FILE / VAULT_TOKEN —

The AWS order matches the AWS SDKs: when a pod has both IRSA and Pod Identity, IRSA wins, so configure one per component. Credentials are cached and refreshed before they expire, concurrent callers share one refresh, every call has a strict timeout, credential endpoints are fixed or validated (no request-controlled URLs), and no secret value is logged.

The gateway's gateway.json key cloudCredentials overrides the environment; the chart renders it from gateway.cloudCredentials, and only the values you set:

gateway:
  cloudCredentials:
    aws:
      region: eu-central-1
      imds: false                 # IMDSv2 fallback, off by default
      partitionDNSSuffix: ""      # e.g. amazonaws.eu
      stsEndpoint: ""             # like AWS_ENDPOINT_URL_STS
    azure:
      managedIdentity: false
      identityBinding: false
    gcp:
      metadataHost: ""

Chart settings#

Every component (gateway, identity, attestation, operator) has serviceAccount.annotations, serviceAccount.labels and podLabels. The identity broker gets its own ServiceAccount when you set either map (it already has one with identity.bootstrap.enabled); the attestation service gets one only when you set either map.

Value Purpose
<component>.serviceAccount.annotations e.g. eks.amazonaws.com/role-arn, iam.gke.io/gcp-service-account, azure.workload.identity/client-id
<component>.serviceAccount.labels Extra ServiceAccount labels
<component>.podLabels e.g. azure.workload.identity/use: "true"
gateway.secretsStoreCSI, identity.secretsStoreCSI Mount a SecretProviderClass volume (see Secrets)
gateway.cloudCredentials See above
identity.federationKey, identity.federationKeySecret, identity.federationIssuer Broker federation signing key and https issuer URL (see Token exchange)
audit.worm.credentials static (Secret credentialsSecret) or aws (keyless)

AWS (EKS)#

Pod Identity is AWS's recommendation and needs no annotation:

aws eks create-pod-identity-association --cluster-name prod \
  --namespace maqpna-system --service-account maqpna-gateway \
  --role-arn arn:aws:iam::111122223333:role/maqpna-gateway

IRSA is needed where Pod Identity is not supported (Fargate, hybrid nodes) and for AWS Marketplace metering (RegisterUsage must be signed with IRSA):

gateway:
  serviceAccount:
    annotations:
      eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/maqpna-gateway

Partitions. ARNs, STS and KMS endpoints follow the partition of the region or role ARN: aws (amazonaws.com), aws-cn (amazonaws.com.cn), aws-us-gov (amazonaws.com, us-gov-* regions) and the AWS European Sovereign Cloud aws-eusc (amazonaws.eu, region eusc-de-east-1). The European Sovereign Cloud identifiers are not fully verified against AWS's published endpoint list, so they are configurable: set MAQPNA_AWS_PARTITION_DNS_SUFFIX (or cloudCredentials.aws.partitionDNSSuffix) and, if needed, AWS_ENDPOINT_URL_STS / AWS_ENDPOINT_URL_KMS (HTTPS only). Credentials never cross partitions.

Keyless WORM. With audit.worm.credentials: aws the gateway signs S3 Object Lock writes with the temporary credentials of its own identity (session token included) and no credentialsSecret is needed:

audit:
  sink: worm
  worm:
    endpoint: https://s3.eu-central-1.amazonaws.com
    bucket: maqpna-audit
    region: eu-central-1
    credentials: aws

The role needs s3:PutObject, s3:PutObjectRetention and s3:GetObject on the bucket, and s3:GetBucketObjectLockConfiguration: the sink checks at startup that Object Lock is enabled and refuses to start otherwise.

Google Cloud (GKE)#

With Workload Identity Federation for GKE, grant the Kubernetes ServiceAccount directly (no annotation):

gcloud kms keys add-iam-policy-binding maqpna-audit --keyring maqpna --location europe-west3 \
  --role roles/cloudkms.signerVerifier \
  --member "principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/maqpna-system/sa/maqpna-gateway"

or impersonate a Google service account with the iam.gke.io/gcp-service-account annotation (and roles/iam.workloadIdentityUser on that service account):

gateway:
  serviceAccount:
    annotations:
      iam.gke.io/gcp-service-account: maqpna-gateway@PROJECT_ID.iam.gserviceaccount.com

Outside GKE (EKS, AKS, on premises), create a workload identity pool provider that trusts your cluster's OIDC issuer, generate the credential configuration with gcloud iam workload-identity-pools create-cred-config ... --credential-source-file=/var/run/secrets/tokens/gcp-token, mount it and a projected token with that audience, and set GOOGLE_APPLICATION_CREDENTIALS through extraEnv. Only file-sourced external_account configurations are accepted; URL- and executable-sourced ones and service account key files are refused.

Azure (AKS)#

Enable the OIDC issuer and workload identity on the cluster, create a user-assigned managed identity with a federated credential for the ServiceAccount, then:

gateway:
  podLabels:
    azure.workload.identity/use: "true"
  serviceAccount:
    annotations:
      azure.workload.identity/client-id: 00000000-0000-0000-0000-000000000000
az identity federated-credential create --name maqpna-gateway \
  --identity-name maqpna-gateway --resource-group rg-maqpna \
  --issuer "$(az aks show -g rg-maqpna -n prod --query oidcIssuerProfile.issuerUrl -o tsv)" \
  --subject system:serviceaccount:maqpna-system:maqpna-gateway \
  --audiences api://AzureADTokenExchange

Identity bindings (one federated credential per identity across many clusters) route the token request through an AKS-operated proxy; opt in with cloudCredentials.azure.identityBinding: true (or MAQPNA_AZURE_IDENTITY_BINDING=true). Managed identity through IMDS is the node's identity and stays off unless you set managedIdentity: true. AZURE_AUTHORITY_HOST selects sovereign clouds (Azure Government, Azure operated by 21Vianet); AZURE_CLIENT_SECRET (or AZURE_CLIENT_SECRET_FILE) is still accepted for clusters without workload identity.

Oracle Cloud (OKE)#

OKE workload identity scopes OCI IAM policies to a pod's ServiceAccount. Set MAQPNA_OCI_AUTH=oke-workload-identity (and OCI_RESOURCE_PRINCIPAL_REGION) through extraEnv, and write a policy such as:

Allow any-user to use keys in compartment maqpna where all {
  request.principal.type = 'workload',
  request.principal.namespace = 'maqpna-system',
  request.principal.service_account = 'maqpna-gateway',
  request.principal.cluster_id = 'ocid1.cluster.oc1..…' }

MAQPNA_OCI_AUTH=resource-principal reads a resource principal v2.2 session (OCI_RESOURCE_PRINCIPAL_RPST, OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM, each a value or a file path). OCI requests are signed with the OCI HTTP signature scheme. OCI Vault is not yet a kms:// signing backend (Planned).

The gateway signs model requests with this identity only for hosts under oraclecloud.com; for another realm (for example a government or dedicated region), list its domains in MAQPNA_OCI_REALM_DOMAINS (comma-separated, e.g. oraclegovcloud.com). Likewise the gateway's AWS identity signs model requests only for the partition DNS suffixes and api.aws (see the model providers guide).

Signing keys in a KMS#

Release images resolve these key URIs; the private key never leaves the KMS or HSM, and every signature is checked against the key's public key before it is used.

URI Key types → JWS alg Permissions
kms://aws/<key-id \| key ARN \| alias/name> ECC_NIST_P256 → ES256; ECC_NIST_EDWARDS25519 → EdDSA; RSA_2048+ → RS256 kms:Sign, kms:GetPublicKey
kms://gcp/projects/P/locations/L/keyRings/R/cryptoKeys/K/cryptoKeyVersions/V EC_SIGN_P256_SHA256 → ES256; EC_SIGN_ED25519 → EdDSA; RSA_SIGN_PKCS1_*_SHA256 → RS256 roles/cloudkms.signerVerifier and roles/cloudkms.publicKeyViewer
kms://azure/<vault>/<key>[/<version>], azurekv://<vault>/<key>[/<version>] EC P-256 (EC-HSM) → ES256; RSA → RS256 (Key Vault has no Ed25519) Key Vault Crypto User
kms://vault/<mount>/keys/<name>, kms://openbao/<mount>/keys/<name> Transit ed25519 → EdDSA; ecdsa-p256 → ES256; rsa-2048 → RS256 policy below
file:///abs/path Ed25519, ECDSA P-256 or RSA (2048+) PKCS#8 PEM —
pkcs11:… (RFC 7512) EC P-256 → ES256 (CKM_ECDSA), Ed25519 → EdDSA (CKM_EDDSA), RSA → RS256 (CKM_SHA256_RSA_PKCS) Only in binaries built with CGO_ENABLED=1 go build -tags pkcs11 (tested against SoftHSM v2); release images are static and refuse it

An alias (kms://aws/alias/…) is resolved once at start-up and the key ARN is pinned; a Cloud KMS or Transit key is pinned to its version. Rotating the key therefore takes a restart, and old signatures keep verifying against the retired public key you keep in the JWKS.

Vault / OpenBao Transit policy:

path "transit/sign/maqpna-audit"    { capabilities = ["update"] }
path "transit/sign/maqpna-audit/*"  { capabilities = ["update"] }
path "transit/keys/maqpna-audit"    { capabilities = ["read"] }

Use a key URI anywhere MAQPNA signs:

audit:
  checkpointSigning:
    enabled: true
    keyURI: kms://aws/alias/maqpna-audit

Audit checkpoints, evidence packs, usage reports, memory erasure certificates and licences take the algorithm from the key (EdDSA, ES256 or RS256 in the JWS header), and the published JWKS carries the matching key (kty: OKP/crv: Ed25519, kty: EC/crv: P-256, or kty: RSA), so maqpna audit verify, maqpna evidence verify, maqpna usage verify and third parties verify any of them. Verifiers bind the header alg to the key type, so a signature cannot be relabelled. Identity tokens, Txn-Tokens, A2A cards and backups stay EdDSA: for those, a KMS key must be Ed25519 (AWS ECC_NIST_EDWARDS25519, Cloud KMS EC_SIGN_ED25519, Transit ed25519; Azure Key Vault has none). The chart accepts built-in KMS URIs without customKeyBackends; pkcs11: signing URIs, other kms:// providers (ovh, stackit) and any kms:///pkcs11: sealing URI (gateway.captureArgs.keyURI) still need it.

Identity broker: token exchange to cloud credentials#

The broker's RFC 8693 exchange (POST /v1/exchange) can turn an agent's MAQPNA token into cloud credentials scoped to that agent. Add the provider to an allowlisted audience in identity.exchange.audiences:

Provider Mode What happens
aws-sts webIdentity (default) The broker signs a federation JWT (sub = agent SPIFFE ID) and calls AssumeRoleWithWebIdentity
aws-sts assumeRole The broker's own AWS identity calls AssumeRole with SourceIdentity and session tags maqpna-namespace, maqpna-agent, maqpna-session (ABAC per agent)
gcp-sts federated (default) Federation JWT → STS token exchange against a workload identity pool provider, optionally impersonating serviceAccount
gcp-sts impersonate The broker's own Google identity → generateAccessToken for serviceAccount
azure-entra — Federation JWT as the client assertion of an Entra app or managed identity
identity:
  federationKey: kms://aws/alias/maqpna-federation   # ES256 or RS256
  exchange:
    audiences:
      aws:bedrock:
        provider: aws-sts
        maxTTLSeconds: 900
        params:
          roleArn: arn:aws:iam::111122223333:role/maqpna-agents
          mode: webIdentity              # or assumeRole
          durationSeconds: "900"          # STS minimum
          tokenAudience: sts.amazonaws.com
          subject: session                # session | agent | namespace
      gcp:vertex:
        provider: gcp-sts
        params:
          audience: //iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/maqpna/providers/broker
          serviceAccount: maqpna-agents@PROJECT_ID.iam.gserviceaccount.com
          scope: https://www.googleapis.com/auth/cloud-platform
      azure:openai:
        provider: azure-entra
        params:
          tenantId: 00000000-0000-0000-0000-000000000000
          clientId: 11111111-1111-1111-1111-111111111111
          scope: https://cognitiveservices.azure.com/.default
          # subject defaults to "agent" here: Entra matches sub exactly

Unknown params, malformed role ARNs or WIF audiences, and federated modes without a federation key fail the broker at start-up, not at the first exchange.

The federation key. Clouds do not accept EdDSA tokens, so the federated modes need identity.federationKey (MAQPNA_IDENTITY_FEDERATION_KEY, broker flag -federation-key): an ES256 or RS256 key URI, or identity.federationKeySecret (Secret with federation.pem). AWS and Google accept RS256 and ES256; Entra officially supports RS256 only, so use an RSA key for azure-entra. The public key is published in the broker's /.well-known/jwks.json next to its Ed25519 keys (MAQPNA's own verifiers ignore it).

The issuer. Federation tokens carry iss = identity.federationIssuer (MAQPNA_IDENTITY_FEDERATION_ISSUER, flag -federation-issuer), an https URL. With a federation key the broker serves GET /.well-known/openid-configuration (whose jwks_uri is <issuer>/.well-known/federation/jwks.json) and that federation-only JWKS. Expose exactly those two paths at the issuer URL (an Ingress or HTTPRoute to the broker), or copy both documents to a static HTTPS location. The token's sub is the agent's SPIFFE ID at the subject granularity: session (default, spiffe://<td>/ns/<ns>/agent/<agent>/session/<id>), agent or namespace; claims maqpna_ns, maqpna_agent, maqpna_session, scope and the on-behalf-of act are included for conditions and attribute mappings.

Trust. Each cloud must trust the broker's issuer:

  • AWS: aws iam create-open-id-connect-provider --url <issuer> --client-id-list sts.amazonaws.com; the role trust policy conditions on <issuer>:sub (the agent SPIFFE ID, wildcards allowed with StringLike) and <issuer>:aud.
  • Google: gcloud iam workload-identity-pools providers create-oidc broker --issuer-uri=<issuer> --jwk-json-path=jwks.json --attribute-mapping=google.subject=assertion.sub,attribute.namespace=assertion.maqpna_ns (an uploaded JWKS means the issuer need not be reachable from Google).
  • Entra: a federated identity credential on the app or managed identity with --issuer <issuer>, --subject spiffe://<td>/ns/<ns>/agent/<agent> (one credential per agent) and audience api://AzureADTokenExchange.

For AWS and Entra the issuer's /.well-known/openid-configuration and JWKS must be reachable over HTTPS from the cloud.

Responses. gcp-sts and azure-entra return a bearer access token. aws-sts returns issued_token_type urn:maqpna:params:oauth:token-type:aws-credentials and an access_token holding JSON {"AccessKeyId","SecretAccessKey","SessionToken","Expiration"} for SigV4 clients. AWS session credentials last at least 900 seconds, longer than a short MAQPNA token. The gateway refuses to forward such a response as a bearer token (an MCP upstream with auth.tokenExchange needs a bearer provider).

Secrets from a cloud secret manager#

Every secret MAQPNA reads can come from a file, so the Secrets Store CSI driver and External Secrets Operator both work:

  • Gateway config already takes files: apiKeyFile (model routes), clientSecretFile, headersFile (SIEM, OTel), dsnFile (state backend), bearerTokenFile, hmacSecretFile, signingSecretFile, accessTokenFile.
  • Every secret environment variable has a _FILE twin (when both are set, the file wins, so a CSI mount can override the chart's Secret): MAQPNA_ADMIN_TOKEN_FILE, MAQPNA_AUDIT_HMAC_KEY_FILE, MAQPNA_BROKER_TOKEN_FILE, MAQPNA_AUDIT_WORM_ACCESS_KEY_FILE, MAQPNA_AUDIT_WORM_SECRET_KEY_FILE, AZURE_CLIENT_SECRET_FILE, VAULT_TOKEN_FILE.

External Secrets Operator#

ESO writes ordinary Kubernetes Secrets, so the chart's Secret references work unchanged. One ExternalSecret per Secret MAQPNA reads, e.g. the WORM keys (audit.worm.credentialsSecret) and the tokens Secret (secrets.existingSecret):

# AWS Secrets Manager (SecretStore with auth.jwt.serviceAccountRef or Pod Identity)
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: {name: maqpna-audit-s3, namespace: maqpna-system}
spec:
  refreshInterval: 1h
  secretStoreRef: {kind: ClusterSecretStore, name: aws-secretsmanager}
  target: {name: maqpna-audit-s3}
  data:
    - secretKey: access-key
      remoteRef: {key: maqpna/audit-s3, property: access-key}
    - secretKey: secret-key
      remoteRef: {key: maqpna/audit-s3, property: secret-key}
---
# Google Secret Manager
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: {name: maqpna-tokens, namespace: maqpna-system}
spec:
  secretStoreRef: {kind: ClusterSecretStore, name: gcp-secretmanager}
  target: {name: maqpna-tokens}
  data:
    - {secretKey: admin-token,    remoteRef: {key: maqpna-admin-token}}
    - {secretKey: broker-token,   remoteRef: {key: maqpna-broker-token}}
    - {secretKey: audit-hmac-key, remoteRef: {key: maqpna-audit-hmac-key}}
---
# Azure Key Vault (SecretStore with authType: WorkloadIdentity)
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: {name: maqpna-siem-headers, namespace: maqpna-system}
spec:
  secretStoreRef: {kind: SecretStore, name: azure-kv}
  target: {name: maqpna-siem-headers}
  data:
    - {secretKey: headers, remoteRef: {key: maqpna-siem-headers}}
---
# HashiCorp Vault KV v2 (SecretStore with auth.kubernetes)
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: {name: maqpna-state, namespace: maqpna-system}
spec:
  secretStoreRef: {kind: SecretStore, name: vault}
  target: {name: maqpna-state}
  data:
    - {secretKey: dsn, remoteRef: {key: maqpna/state, property: dsn}}

Use external-secrets.io/v1beta1 on ESO releases older than 0.17.

Secrets Store CSI driver#

The CSI driver mounts secrets as files without creating Kubernetes Secrets. Create a SecretProviderClass and enable the volume; files appear under mountPath (default /mnt/secrets-store):

gateway:
  secretsStoreCSI:
    enabled: true
    secretProviderClass: maqpna-gateway
  extraEnv:
    - name: MAQPNA_ADMIN_TOKEN_FILE
      value: /mnt/secrets-store/admin-token

Point file settings at the same directory, e.g. a model route's apiKeyFile: /mnt/secrets-store/openai-key. The gateway re-reads apiKeyFile per call, so a rotation the CSI driver syncs takes effect without a restart.

# AWS Secrets Manager (ASCP, with EKS Pod Identity)
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata: {name: maqpna-gateway, namespace: maqpna-system}
spec:
  provider: aws
  parameters:
    usePodIdentity: "true"
    objects: |
      - objectName: maqpna/admin-token
        objectType: secretsmanager
        objectAlias: admin-token
---
# Azure Key Vault (workload identity)
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata: {name: maqpna-gateway, namespace: maqpna-system}
spec:
  provider: azure
  parameters:
    clientID: 00000000-0000-0000-0000-000000000000
    keyvaultName: kv-maqpna
    tenantId: 11111111-1111-1111-1111-111111111111
    objects: |
      array:
        - |
          objectName: admin-token
          objectType: secret
---
# Google Secret Manager
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata: {name: maqpna-gateway, namespace: maqpna-system}
spec:
  provider: gcp
  parameters:
    secrets: |
      - resourceName: projects/PROJECT_ID/secrets/maqpna-admin-token/versions/latest
        path: admin-token
---
# HashiCorp Vault
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata: {name: maqpna-gateway, namespace: maqpna-system}
spec:
  provider: vault
  parameters:
    vaultAddress: https://vault.vault.svc:8200
    roleName: maqpna-gateway
    objects: |
      - objectName: admin-token
        secretPath: secret/data/maqpna
        secretKey: admin-token

The serviceAccount annotations and podLabels above are what the AWS and Azure providers use to authenticate the mount. The chart does not render a SecretProviderClass; it is yours to create in the release namespace.

Real-cloud smoke test#

An opt-in test, built with the cloud tag, signs and verifies with real keys. Each case is skipped unless its variable names a key URI. Run it from a pod (or a shell) that has the workload identity under test:

# EKS (Pod Identity or IRSA), GKE (WIF), AKS (Entra Workload ID), Vault
export MAQPNA_CLOUD_TEST_AWS_KMS_KEY=kms://aws/alias/maqpna-smoke
export MAQPNA_CLOUD_TEST_GCP_KMS_KEY=kms://gcp/projects/P/locations/L/keyRings/R/cryptoKeys/K/cryptoKeyVersions/1
export MAQPNA_CLOUD_TEST_AZURE_KV_KEY=kms://azure/kv-maqpna/maqpna-smoke
export MAQPNA_CLOUD_TEST_VAULT_KEY=kms://vault/transit/keys/maqpna-smoke
# Credential sources only (STS GetCallerIdentity, Google tokeninfo, an ARM token, a signed OCI call):
export MAQPNA_CLOUD_TEST_AWS=1 MAQPNA_CLOUD_TEST_GCP=1 MAQPNA_CLOUD_TEST_AZURE=1 MAQPNA_CLOUD_TEST_OCI=1
go test -tags cloud ./pkg/cloudcreds/ ./pkg/keys/ -run Cloud -v

PKCS#11 has its own opt-in test against SoftHSM v2 and OpenSC's pkcs11-tool: CGO_ENABLED=1 go test -tags pkcs11 ./pkg/keys/ -run PKCS11 -v.

A quick way on a cluster: run a golang pod with the gateway's ServiceAccount (and pod labels), copy the repository in, and run the command above. Record the result as a dated test/cloud/<provider>/RESULTS.md.

What is not validated yet#

  • No EKS, GKE, AKS or OKE run is recorded: Pod Identity, IRSA, GKE WIF, Entra Workload ID, identity bindings, OKE workload identity and resource principal flows are tested only against fakes built from the providers' API docs.
  • The AWS European Sovereign Cloud partition (aws-eusc, amazonaws.eu) and the AKS identity-binding proxy variables are taken from public documentation that was not fully verified; both are configurable. The OKE proxymux exchange follows the OCI Go SDK's wire format and has not been run against OKE.
  • The broker's cloud federation (AWS IAM OIDC provider, Google WIF provider, Entra federated credential trusting the broker issuer) has not been set up against a real account.
  • Entra acceptance of ES256 federation tokens is undocumented (RS256 is the supported algorithm).
  • PKCS#11 is tested against SoftHSM only, and only in -tags pkcs11 builds.
  • OCI Vault signing, Alibaba RRSA and IBM Trusted Profiles are Planned.