Tool reference

Open Policy Agent

The policy decision point — authorization as code.

Open source Source github.com/open-policy-agent/opa Licence Apache-2.0 Running here openpolicyagent/opa 1.10.0

What it does here

Open Policy Agent is the policy decision point. For every action a user attempts in the Golonex Portal, the Portal asks OPA one question — is this allowed? — and OPA answers from policy written as code, returning both a decision and a reason.

Separating the decision from the application is the point. The rules live in one reviewable file instead of being scattered through application logic, and they can be tested on their own.

How it is deployed

Container opa, running as a server on the internal network with decision logging to the console so every answer becomes an event. The policy file is mounted read-only — see Installation §4.5.

OPA ships no console — so the lab gives it one OPA is an API, not an application: upstream there is nothing to log into. Because that leaves the most interesting component of the stack invisible, the Golonex Portal carries a PDP console under IT Admin › Policy decision point. That console is part of the lab, not part of OPA.

The policy, in plain terms

ActionRequires
home.viewnothing — any signed-in user
hris.view / hris.edithr-staff
invoice.createfinance-ap-clerk
payment.approvefinance-ap-approver; above 50,000 also access-approvers
admin.viewit-admins
any finance actiondenied outright if the user holds both AP roles — segregation of duties

Two ideas are on display here at once: RBAC (does the user hold the right group?) and ABAC (is the amount over the threshold?). The exam separates these; the policy shows them working together.

Where you see it working

In the Portal

Every denial you meet in the Golonex Portal is an OPA answer. The page prints the reason OPA returned — missing entitlement, amount above 50,000 requires manager approval, or SoD violation. Read the reason: it is the policy explaining itself.

In the SIEM

Each decision is logged, and the Portal forwards its authorization events to Wazuh. A denied action is an auditable record, not just a message on screen.

Three ways to reach OPA

The same engine, three doors. Which one you use says a lot about what you are doing — demonstrating, operating, or debugging.

RouteWho uses itGood for
PDP console in the Portalinstructor, IT Opsshowing the policy and its decisions to a room
OPA's REST API from a containerengineersasking the engine directly, with no app involved
Files and scripts on the hostwhoever runs the labediting policy, watching decisions, running tests

1 · From the app — the PDP console

IT Admin › Policy decision point

Sign in to the Portal as a user holding it-admins and open IT Admin › Open PDP console. Two tabs:

  • Live decisions — recent authorization decisions with user, action, amount, ALLOW / DENY and the reason OPA gave. Click around the Portal in another tab, then refresh this one.
  • Policy — the Rego OPA is actually running, read live from the engine rather than from disk, so the page cannot show you something that is not in force. Read-only by default.

An Edit policy button turns that tab into an editor. Saving needs the policy.edit entitlement, applies to OPA immediately, and is written to disk so it survives a restart.

It is fail-safe. OPA compiles the policy before accepting it. A Rego error is rejected with a message and the running policy is untouched — a bad edit cannot take the lab down.

Worth saying out loud to candidates: editing authorization from a web form is a teaching device, not a production pattern. Real policy lives in version control, is reviewed, tested in CI and shipped as a bundle. This console exists so you can feel a policy change take effect — which is precisely why change control matters.

2 · From a container — OPA's own API

OPA listens on opa:8181 on the internal network. It is not published to the host and not exposed through Caddy: the API has no authentication, so it must never leave the private network. Ask it from any container on ciam-net.

Ask for a decision — no app, no login, no session:

docker exec -i portal python -c "import requests;print(requests.post('http://opa:8181/v1/data/golonex/portal/decision',json={'input':{'user':'x','groups':['app-users','finance-ap-approver'],'action':'payment.approve','amount':50001}}).json())"

See what policies are loaded:

docker exec portal python -c "import requests;print([p['id'] for p in requests.get('http://opa:8181/v1/policies').json()['result']])"

The endpoints that matter: GET /v1/policies (what is loaded), POST /v1/data/golonex/portal/decision (ask), PUT /v1/policies/<id> (replace — this is what the console uses), and GET /health.

3 · From the host — files, scripts and logs

The policy is a plain file on the server, bind-mounted into the container at /policies:

PathWhat it is
/opt/ciam-lab/opa/policies/portal.regothe live policy
/opt/ciam-lab/opa/policies/portal.rego.origpristine copy — the restore point for the live-edit demo
/opt/ciam-lab/opa/policies/portal_test.regothe policy's own unit tests
/opt/ciam-lab/opa-ask.shask OPA a question from the command line
/opt/ciam-lab/opa-watch.shlive feed of every decision — put it on a projector

Ask a question:

/opt/ciam-lab/opa-ask.sh payment.approve app-users,finance-ap-approver 50001

Watch decisions live (Ctrl-C to stop):

/opt/ciam-lab/opa-watch.sh

Run the policy's unit tests:

docker exec opa /opa test /policies -v

Raw decision log — every decision, with full input and result:

docker logs --tail 20 opa
Editing the file does not reload OPA OPA runs without --watch, so a change to portal.rego on disk takes effect only after docker restart opa — about four seconds, during which the Portal fails closed and denies everything. The PDP console sidesteps this: it pushes policy through the API, which applies instantly with no restart.

Every decision is audited

The Portal writes one JSON line per decision, and Wazuh turns the interesting ones into alerts:

RuleLevelFires on
1002014any denied action, naming the user and the reason
1002025a payment approved, with the amount
1002034an HR record changed — workforce data feeding IGA
1002044an invoice created
10020510the authorization policy itself was changed
10021010a segregation-of-duties violation blocked at the PDP

Rule 100205 is deliberately the loudest thing in the lab. Ask a candidate why that is right: changing the policy changes every future decision, so it outranks any single denied action.

Try this

  • Open the PDP console beside the Portal and click around the app. Watch decisions appear in real time — the application asking permission, over and over.
  • On the Policy tab, read the payment rule, then predict what happens at 50,000 and at 50,001 before you try it.
  • Approve a payment of exactly 50,000, then one of 50,001, as the same user. The first is allowed and the second is not — the clearest demonstration of attribute-based access control you will get.
  • Work through Part C of Practice: predict fifteen decisions, then check yourself against the validated answers.