Skip to main content

Git as source of truth and bundle delivery

Objective

Make Git the source of truth for policy and deliver signed bundles to OPA and Enterprise OPA PDPs running as Kubernetes sidecars, Envoy ext_authz, or Terraform-provisioned services, using deploy-action with GitHub OIDC.

Concept

With the policy assessed and the entity mapping decided, the migration proper is a change of source of truth. In a source system like Styra DAS, the console is often where policy is edited and pushed. On EnforceAuth, Git is the source of truth: policy lives in a repository, changes go through pull requests and review, and Writ turns a reviewed commit into a signed bundle it promotes dev to staging to prod. Every promotion and every decision is logged, so an auditor can replay what was decided and why.

Bundle delivery, unchanged transport

OPA already knows how to pull policy as a bundle over HTTP. Migration re-points that pull at a Writ-served, signed bundle instead of the source system's bundle endpoint. The PDPs and how they run do not change:

  • Kubernetes. OPA (or Enterprise OPA) runs as a sidecar or DaemonSet next to the workload. Its bundle configuration points at the EnforceAuth-served bundle URL; it keeps polling and hot-swaps policy when a new signed bundle is published. No pod rewrite, just a config change to the bundle source.
  • Envoy. OPA fronts Envoy as an external authorization (ext_authz) service. Envoy calls OPA per request; OPA evaluates the same bundle. Only the bundle source moves.
  • Terraform-provisioned infrastructure. The PDP service, its network path, and the bundle-source configuration are declared in Terraform. Migration is a reviewed change to that infrastructure-as-code: update the bundle source, apply, and the PDP fleet picks up EnforceAuth-signed bundles.

For large or high-churn deployments, Enterprise OPA adds delta bundles (ship only what changed instead of the whole bundle), datasources, and decision_logs.mask for masking sensitive fields in the audit stream. These are eopa features you turn on where scale demands; the policy is still the same Rego.

deploy-action and GitHub OIDC

Delivery from CI uses deploy-action, EnforceAuth's GitHub Action. It authenticates with OIDC workload identity (RFC 8693): GitHub mints a short-lived, signed identity token for the workflow run, and a trust policy on the EnforceAuth side accepts that token. No long-lived API keys live in the repo. The workflow grants the token with permissions: id-token: write, the action exchanges it, and then deploys the OPA bundle through the EnforceAuth API. Rollback is by prior commit_sha: because every deploy is tied to a commit, reverting to a known-good policy is redeploying that commit's bundle.

An illustrative workflow. The load-bearing, groundable parts are the OIDC permission (id-token: write) and that the deploy is pinned to the commit. The action's exact with: input names are not reproduced here; the entries below are labeled placeholders, so check deploy-action's own published inputs for the real field names before you copy this:

name: deploy-policy
on:
push:
branches: [main]

permissions:
contents: read
id-token: write # required: lets GitHub mint the OIDC token deploy-action exchanges

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy signed bundle to EnforceAuth
uses: enforceauth/deploy-action@v1
with:
# PLACEHOLDERS — not real input names. Replace with deploy-action's
# documented inputs. No API key is set: identity comes from the OIDC
# exchange above, and the deploy is pinned to this commit (rollback =
# redeploy a prior commit).
# <bundle-source>: the signed policy bundle to publish for this commit
# <target-stage>: the promotion stage to deploy into (dev | staging | prod)

The chain end to end: a pull request changes Rego in Git, review and CI (including the parity suite from lesson 3) run, merge triggers deploy-action, which exchanges an OIDC token and asks the EnforceAuth API to publish the signed bundle. Writ promotes it through stages; OPA and Enterprise OPA PDPs on K8s, Envoy, or Terraform-provisioned hosts pull the new bundle. Nothing on the request path was rewritten.

Hands-on lab

The bundle content is the policy in the lab. In a real pipeline this exact opa test run is a required CI check before deploy-action ships the bundle; a red suite blocks the deploy. Run it against the starter to see that gate working:

git clone https://github.com/EnforceAuth/university-labs.git
cd university-labs
opa test courses/migrating-to-enforceauth -v

The starter reports PASS: 4/8, so CI would block this deploy: the migrated policy is incomplete. You complete policy.rego and drive the suite to PASS: 8/8 in lesson 3; only then would this bundle pass the check and ship. This lesson's deploy-action and Writ steps are described, not run here. The runnable, graded part is the policy and its tests: that is the bundle that would ship.

Check for understanding

  1. After migrating, what is the source of truth for policy, and what does Writ do with a reviewed commit?
  2. deploy-action uses GitHub OIDC instead of a stored API key. Which single workflow permission lets GitHub mint the token the action exchanges, and why is avoiding a long-lived key the point?
  3. You must roll back to yesterday's known-good policy. What does deploy-action roll back by, and why does that work?
  4. An OPA sidecar in Kubernetes currently pulls bundles from a source system. What is the minimal change to move it onto EnforceAuth, and does the pod itself get rewritten?