Skip to content

OPA Deployment Chain

This page documents the complete OPA deployment chain: how policy containers are built, scanned, pushed to Artifactory, and deployed to CaaS via Rancher. This is the process that runs every time an entity’s policies change.

OPA deployment chain — Vault, Artifactory, Rancher, CaaS with artifacts

1. Vault — Authenticate and Load Secrets

Section titled “1. Vault — Authenticate and Load Secrets”

Method: JWT/OIDC (zero stored secrets)

GitHub Actions issues a short-lived OIDC token per workflow run. Vault verifies it against token.actions.githubusercontent.com and returns a batch token (5m TTL) scoped to the pipeline’s read policies.

PropertyValue
Auth methodjwt
Rolegithub-actions-jwt
Audiencehttps://github.com/roche-private
Bound reporoche-private/model-dp-rdt
Policiesci-dev-reader, ci-test-reader, ci-prod-reader
Token typeBatch (non-renewable, lightweight)
TTL5 minutes

Secrets loaded:

PathKeysUsed for
secret/common/artifactoryARTIFACTORY_USER, ARTIFACTORY_TOKENDocker push to registry
secret/common/caasRANCHER_URL, RANCHER_BEARER_TOKEN, CAAS_PROJECTKubernetes API access
secret/{env}/caasCAAS_NAMESPACE, CAAS_CLUSTERTarget namespace per environment

Runner: ubuntu-runner-1x-std-cpu-arc-ent (Roche enterprise runner group with network access to vault.service.roche.com).

2. Artifactory — Build and Push OPA Image

Section titled “2. Artifactory — Build and Push OPA Image”

Registry: rdtmodel-general-docker-all-l.eu.repository.roche.com Repository: rdtmodel-general-docker-all-l Image: rdtmodel-general-docker-all-l.eu.repository.roche.com/opa:<version>-static

The OPA image is a hardened, CIS-aligned container built from k8s/Dockerfile:

FROM openpolicyagent/opa:${OPA_VERSION}-static@${OPA_DIGEST}
USER 65534:65534
HEALTHCHECK --interval=10s --timeout=3s CMD ["/opa", "eval", "true"]
ENTRYPOINT ["/opa"]
CMD ["run", "--server", "--addr=:8181", "--log-level=info", "--skip-version-check", "/policies", "/bundles"]

Security toolchain (all run by scripts/build-opa-image.sh):

ToolPurposeFlag
TrivyCVE scanning (fails on CRITICAL/HIGH)--scan
DockleCIS container benchmark (non-root, no shell, HEALTHCHECK)--harden
SyftCycloneDX SBOM for audit trail--sbom
CosignSupply chain signing--sign

Build triggers:

  • Push to main changing k8s/Dockerfile, k8s/OPA_VERSION, or scripts/build-opa-image.sh
  • Manual workflow_dispatch

Version pinning: k8s/OPA_VERSION contains both version and SHA256 digest:

OPA_VERSION=1.16.1
OPA_DIGEST=sha256:6aeacdb587324ab38c83a572db8dca73e6f76c43a8ca25caa9d2fec83530c3a3

To bump OPA version:

Terminal window
scripts/build-opa-image.sh --bump 1.17.0

3. Rancher — Configure Kubernetes Access

Section titled “3. Rancher — Configure Kubernetes Access”

Endpoint: https://rancher.emea.roche.com Cluster: caasawsprod (ID: c-2dhbr, Cloud Prod eu-central-1) Project: rdt_model (ID: c-2dhbr:p-r4z25)

The deploy workflow configures kubectl using Rancher’s cluster-scoped API:

Terminal window
kubectl config set-cluster caas --server="$RANCHER_URL/k8s/clusters/$CAAS_CLUSTER"
kubectl config set-credentials caas --token="$RANCHER_BEARER_TOKEN"
kubectl config set-context caas --cluster=caas --user=caas --namespace="$CAAS_NAMESPACE"

Critical: The API path MUST use /k8s/clusters/c-2dhbr/ — NOT /v3/. Cluster-scoped tokens return 401 on the /v3/ path.

PropertyValue
Service accountRXPMODE1
Token nametoken-pwj8f
Token expires2026-08-04 (rotate before this date)
CIDM groupCAAS_MODEL_ADMIN
ServiceNow requestRITM6292237

Namespaces:

EnvironmentNamespaceResource Quota
devrdt-model-dev300m CPU, 340Mi mem
testrdt-model-test300m CPU, 340Mi mem
prodrdt-model-prod300m CPU, 340Mi mem

Deployment steps (per entity):

  1. Create ConfigMap from Rego files:

    Terminal window
    kubectl create configmap opa-policies-$ENTITY --from-file=validation.rego --from-file=deploy.rego \
    --dry-run=client -o yaml | kubectl apply -f -
  2. Apply all manifests:

    Terminal window
    kubectl apply -f k8s/$ENTITY/

    This deploys: Deployment (2 replicas), Service (ClusterIP:8181), NetworkPolicy, Bundle ConfigMap, Bundle Refresh CronJob.

  3. Verify rollout:

    Terminal window
    kubectl rollout status deployment/opa-$ENTITY --timeout=120s

Generated manifests per entity (k8s/{entity}/):

FilePurpose
opa-deployment.yamlOPA server — 2 replicas, health checks, security context
opa-service.yamlClusterIP service on port 8181
opa-networkpolicy.yamlIngress allow from same namespace only
bundle-configmap.yamlPre-materialized lookup data for policy evaluation
bundle-refresh-cronjob.yamlHourly Snowflake → bundle export
validation.regoEntity-specific validation rules
deploy.regoDeployment authorization policy

Deployment SecurityContext (CIS-hardened):

  • Pod: runAsNonRoot: true, seccompProfile: RuntimeDefault
  • Container: allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities.drop: ALL
  • Writable temp: emptyDir (memory-backed, 10Mi limit)

imagePullSecret: artifactory-docker (created in all 3 namespaces, authenticated to eu.repository.roche.com)

Each OPA instance exposes six policy domains (ADR 0005b):

PathDomainQuestion
/v1/data/roche/rules/*ValidationIs this data valid?
/v1/data/roche/policies/*/accessAccessCan this user see this?
/v1/data/roche/policies/*/workflowWorkflowIs this transition OK?
/v1/data/roche/policies/*/apiAPIIs this operation OK?
/v1/data/roche/policies/*/auditAuditMust this be logged?
/v1/data/roche/policies/*/deploymentDeploymentIs this deploy OK?

CI/CD workflow — Build OPA Image and Deploy pipelines with environment gates

CredentialExpiresRotation procedure
Rancher bearer token2026-08-04CaaS self-service portal → new token → vault kv patch secret/common/caas RANCHER_BEARER_TOKEN=<new>
Artifactory token2027-05-05Artifactory UI → regenerate → vault kv patch secret/common/artifactory ARTIFACTORY_TOKEN=<new>
Vault JWT roleNeverBound to GitHub OIDC — no rotation needed
OPA image digestOn version bumpscripts/build-opa-image.sh --bump <version>
SymptomCauseFix
Vault 404 from CIRunning on ubuntu-latest (public runner)Switch to group: ubuntu-runner-1x-std-cpu-arc-ent
ImagePullBackOffImage not pushed or wrong tagRun Build OPA Image workflow manually
401 from Rancher APIUsing /v3/ pathUse /k8s/clusters/c-2dhbr/ path
Rollout timeoutOPA can’t start (missing ConfigMap)Check ConfigMap creation step succeeded
Rego syntax error in ConfigMapTemplate bugRun rdt-model-policy generate locally, fix template