Skip to content

Technical — OPA Config

The image that runs RAGA's OPA (Open Policy Agent) server along with its Rego policy. Unlike opa-auth/opa-data, this is not a custom HTTP service — the image simply wraps the official opa binary, the Infisical CLI, and the policy files, then runs opa run --server as orchestrated by the rest of the OPA subsystem.

Repository

KeyValue
Git Remotehttps://git.tlab.co.id/tarantula/tarantula-v2/opa-bun/opa-config.git
Active Branchmain
bash
git clone https://git.tlab.co.id/tarantula/tarantula-v2/opa-bun/opa-config.git
cd opa-config

Tech Stack

LayerTechnology
Base ImageAlpine 3.20
Policy EngineOPA binary v1.5.1 (downloaded directly from the GitHub release during image build)
Policy LanguageRego
Secret ManagementInfisical CLI (not the SDK) — run via infisical run --watch

Environment Variables

There is no runtime .env file — running.sh reads bootstrap variables from the container environment, and the Infisical CLI injects the rest as environment variables into the opa run process.

VariableDescription
INFISICAL_CLIENT_IDUniversal Auth Client ID (required, checked at the start of running.sh)
INFISICAL_CLIENT_SECRETUniversal Auth Client Secret (required)
INFISICAL_PATHSecrets path in Infisical
INFISICAL_PROJECT_IDInfisical project ID
INFISICAL_ENVIRONMENTTarget environment (dev / staging / prod)
JWT_SECRETDeclared in .env.example ("keep in sync with user-service") for the Rego policy to verify JWT signatures via opa.runtime()["env"]["JWT_SECRET"]

File Structure

opa-config/
├── Dockerfile          # Alpine + opa binary + Infisical CLI
├── running.sh           # Entrypoint: Infisical login -> run `opa run --server`
├── config.yml            # OPA server config (decision log, etc — currently disabled)
├── policy.rego           # Active Rego policy (loaded by running.sh)
├── policy.old.rego       # Previous policy variant (not loaded)
├── policy.new.v0.rego    # Draft of the next policy variant (not loaded)
└── .env.example

Only policy.rego is loaded by running.sh (see the command below). policy.old.rego and policy.new.v0.rego exist in the repo as history/drafts and are not active at runtime.

Startup (running.sh)

bash
# 1. Log in to Infisical using Universal Auth
export INFISICAL_TOKEN=$(infisical login \
  --method=universal-auth \
  --client-id="$INFISICAL_CLIENT_ID" \
  --client-secret="$INFISICAL_CLIENT_SECRET" \
  --silent --plain)

# 2. Run the OPA server with secrets injected as env vars,
#    loading config, data, and policy from the /share volume
exec infisical run --watch --path="$INFISICAL_PATH" --projectId="$INFISICAL_PROJECT_ID" --env="$INFISICAL_ENVIRONMENT" -- \
  opa run --log-level=debug --server --addr 0.0.0.0:8181 -c /share/config.yml /share/data.json /share/policy.rego

--watch makes the Infisical CLI monitor secret changes and restart the opa run process on updates — so rotating a secret in Infisical doesn't require a manual redeploy, except for secrets hardcoded directly in a policy file (see the note below).

OPA Server Configuration (config.yml)

Currently the entire contents of config.yml are commented out — the OPA server runs with default settings (no decision log to an external service). If enabled, this configuration would send every authorization decision to a logger service (opa-logger-v2) for an audit trail:

yaml
# services:
#   logger:
#     url: http://opa-logger-v2:3000
# decision_logs:
#   console: true
#   service: logger
#   reporting:
#     min_delay_seconds: 0
#     max_delay_seconds: 1

Policy Decision Flow (policy.rego)

The verify_access policy receives input: { method, token, path } from opa-auth and evaluates it through the following rules:

RuleFunction
tokenVerifies the JWT signature (HS256) then decodes its payload. Verification failure → is_token_valid: false.
is_token_not_expTrue if token.payload.exp has not yet passed the current time.
user_rolesPulled from data.user_roles[token.payload.username] — data supplied by OPA Data.
user_is_grantedMerges every grant from data.role_grants[role] for all of the user's roles.
path_matchesMatches grant.path against input.path segment by segment, supporting a * wildcard on a single segment.
user_casesGrants whose method matches, status == true, and whose path matches.
allowtrue if: method is OPTIONS, or the path is /auth/login / /auth/refresh-token (whitelisted without a token), or (the token is not expired and at least one user_cases entry matches).

Technical note: the currently active policy.rego verifies the JWT signature with a secret that is hardcoded directly in the policy file, rather than read from the JWT_SECRET environment variable as policy.old.rego/policy.new.v0.rego do. As a result, rotating JWT_SECRET in Infisical/user-service will not automatically be picked up by the currently active policy — this needs to be reconciled so the mechanism is consistent with the other two file variants.

Build & Run

bash
docker build -t opa-config .

docker run -p 8181:8181 \
  -v /host/share:/share \
  -e INFISICAL_CLIENT_ID=... \
  -e INFISICAL_CLIENT_SECRET=... \
  -e INFISICAL_PATH=... \
  -e INFISICAL_PROJECT_ID=... \
  -e INFISICAL_ENVIRONMENT=dev \
  opa-config

The -v /host/share:/share volume is required and must match the volume used by OPA Dataconfig.yml, data.json, and policy.rego are all read from the /share path.