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.
Process Overview
Section titled “Process Overview”Step-by-Step
Section titled “Step-by-Step”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.
| Property | Value |
|---|---|
| Auth method | jwt |
| Role | github-actions-jwt |
| Audience | https://github.com/roche-private |
| Bound repo | roche-private/model-dp-rdt |
| Policies | ci-dev-reader, ci-test-reader, ci-prod-reader |
| Token type | Batch (non-renewable, lightweight) |
| TTL | 5 minutes |
Secrets loaded:
| Path | Keys | Used for |
|---|---|---|
secret/common/artifactory | ARTIFACTORY_USER, ARTIFACTORY_TOKEN | Docker push to registry |
secret/common/caas | RANCHER_URL, RANCHER_BEARER_TOKEN, CAAS_PROJECT | Kubernetes API access |
secret/{env}/caas | CAAS_NAMESPACE, CAAS_CLUSTER | Target 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:65534HEALTHCHECK --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):
| Tool | Purpose | Flag |
|---|---|---|
| Trivy | CVE scanning (fails on CRITICAL/HIGH) | --scan |
| Dockle | CIS container benchmark (non-root, no shell, HEALTHCHECK) | --harden |
| Syft | CycloneDX SBOM for audit trail | --sbom |
| Cosign | Supply chain signing | --sign |
Build triggers:
- Push to
mainchangingk8s/Dockerfile,k8s/OPA_VERSION, orscripts/build-opa-image.sh - Manual
workflow_dispatch
Version pinning: k8s/OPA_VERSION contains both version and SHA256 digest:
OPA_VERSION=1.16.1OPA_DIGEST=sha256:6aeacdb587324ab38c83a572db8dca73e6f76c43a8ca25caa9d2fec83530c3a3To bump OPA version:
scripts/build-opa-image.sh --bump 1.17.03. 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:
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.
| Property | Value |
|---|---|
| Service account | RXPMODE1 |
| Token name | token-pwj8f |
| Token expires | 2026-08-04 (rotate before this date) |
| CIDM group | CAAS_MODEL_ADMIN |
| ServiceNow request | RITM6292237 |
4. CaaS — Deploy OPA Pods
Section titled “4. CaaS — Deploy OPA Pods”Namespaces:
| Environment | Namespace | Resource Quota |
|---|---|---|
| dev | rdt-model-dev | 300m CPU, 340Mi mem |
| test | rdt-model-test | 300m CPU, 340Mi mem |
| prod | rdt-model-prod | 300m CPU, 340Mi mem |
Deployment steps (per entity):
-
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 - -
Apply all manifests:
Terminal window kubectl apply -f k8s/$ENTITY/This deploys: Deployment (2 replicas), Service (ClusterIP:8181), NetworkPolicy, Bundle ConfigMap, Bundle Refresh CronJob.
-
Verify rollout:
Terminal window kubectl rollout status deployment/opa-$ENTITY --timeout=120s
Generated manifests per entity (k8s/{entity}/):
| File | Purpose |
|---|---|
opa-deployment.yaml | OPA server — 2 replicas, health checks, security context |
opa-service.yaml | ClusterIP service on port 8181 |
opa-networkpolicy.yaml | Ingress allow from same namespace only |
bundle-configmap.yaml | Pre-materialized lookup data for policy evaluation |
bundle-refresh-cronjob.yaml | Hourly Snowflake → bundle export |
validation.rego | Entity-specific validation rules |
deploy.rego | Deployment 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)
5. Policy Endpoints (after deployment)
Section titled “5. Policy Endpoints (after deployment)”Each OPA instance exposes six policy domains (ADR 0005b):
| Path | Domain | Question |
|---|---|---|
/v1/data/roche/rules/* | Validation | Is this data valid? |
/v1/data/roche/policies/*/access | Access | Can this user see this? |
/v1/data/roche/policies/*/workflow | Workflow | Is this transition OK? |
/v1/data/roche/policies/*/api | API | Is this operation OK? |
/v1/data/roche/policies/*/audit | Audit | Must this be logged? |
/v1/data/roche/policies/*/deployment | Deployment | Is this deploy OK? |
CI/CD Workflow
Section titled “CI/CD Workflow”Token and Credential Rotation
Section titled “Token and Credential Rotation”| Credential | Expires | Rotation procedure |
|---|---|---|
| Rancher bearer token | 2026-08-04 | CaaS self-service portal → new token → vault kv patch secret/common/caas RANCHER_BEARER_TOKEN=<new> |
| Artifactory token | 2027-05-05 | Artifactory UI → regenerate → vault kv patch secret/common/artifactory ARTIFACTORY_TOKEN=<new> |
| Vault JWT role | Never | Bound to GitHub OIDC — no rotation needed |
| OPA image digest | On version bump | scripts/build-opa-image.sh --bump <version> |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| Vault 404 from CI | Running on ubuntu-latest (public runner) | Switch to group: ubuntu-runner-1x-std-cpu-arc-ent |
| ImagePullBackOff | Image not pushed or wrong tag | Run Build OPA Image workflow manually |
| 401 from Rancher API | Using /v3/ path | Use /k8s/clusters/c-2dhbr/ path |
| Rollout timeout | OPA can’t start (missing ConfigMap) | Check ConfigMap creation step succeeded |
| Rego syntax error in ConfigMap | Template bug | Run rdt-model-policy generate locally, fix template |