Andrea Cosentino created CAMEL-24634:
----------------------------------------

             Summary: camel-opa - new component to evaluate Open Policy Agent 
(Rego) policies against an Exchange
                 Key: CAMEL-24634
                 URL: https://issues.apache.org/jira/browse/CAMEL-24634
             Project: Camel
          Issue Type: New Feature
            Reporter: Andrea Cosentino
            Assignee: Andrea Cosentino


h2. Background

{{camel-spiffe}} (CAMEL-23305 / CAMEL-24571) gave Camel a way to *obtain and 
verify workload identity* - X.509-SVIDs, JWT-SVIDs and SPIFFE-backed mTLS. It 
answers "who is this caller?".

What Camel still lacks is the next step: "is this caller allowed to do this?". 
There is no policy decision component, so authorization logic ends up 
hand-coded in {{.process()}} / {{.bean()}} blocks - scattered across routes, 
hard to test in isolation, and impossible to change without redeploying the 
integration.

Camel does ship two authorization integrations - {{camel-shiro}} 
({{ShiroSecurityPolicy}}) and {{camel-keycloak}} ({{KeycloakSecurityPolicy}}) - 
but both are tied to a specific identity provider and to coarse role/permission 
checks. Neither supports externalised, declarative, attribute-based policy.

h2. Proposal

Add a {{camel-opa}} component that acts as a Policy Enforcement Point (PEP) 
delegating decisions to [Open Policy Agent|https://www.openpolicyagent.org/], 
the CNCF-graduated policy engine whose policies are written in Rego. Camel 
supplies the {{input}} document built from the Exchange; OPA evaluates the Rego 
policy and returns the decision.

Two complementary usage modes:

*1. Producer endpoint* - {{opa:<decisionPath>}}

Builds an OPA {{input}} document from the Exchange (headers, and optionally the 
body), POSTs it to the OPA Data API, and sets the decision back on the Exchange 
as headers:
* {{CamelOpaDecisionAllow}} (Boolean) - the allow/deny verdict
* {{CamelOpaDecision}} - the full raw decision document, for policies returning 
more than a boolean (obligations, row filters, deny reasons)

That makes it directly usable in a {{filter}} or {{choice}} predicate:

{code:java}
from("platform-http:/orders")
    .to("opa:authz/orders/allow")
    .filter(header(OpaConstants.DECISION_ALLOW).isEqualTo(true))
        .to("direct:handleOrder");
{code}

*2. {{OpaSecurityPolicy implements org.apache.camel.spi.AuthorizationPolicy}}* 
- the interceptor style, mirroring {{ShiroSecurityPolicy}} / 
{{KeycloakSecurityPolicy}}:

{code:java}
from("platform-http:/orders")
    .policy(opaPolicy)
        .to("direct:handleOrder");
{code}

A deny throws {{CamelAuthorizationException}}, so it composes with the existing 
{{onException}} machinery and never silently continues down the route.

h2. Design notes

* *Library*: {{com.styra:opa}} (the official OPA Java SDK, 
{{styrainc/opa-java}}) - Apache-2.0 and published on Maven Central, so it 
clears the new-component dependency gate. It wraps OPA's REST Data API, which 
is the standard OPA deployment model (OPA running as a sidecar/daemon next to 
the application).
* *Input shaping must be opt-in and bounded.* Sending the whole message body to 
the PDP is wrong as a default - bodies can be large, streaming, or carry data 
the PDP has no business seeing. Default to headers plus a configurable header 
allow-list; body inclusion behind an explicit {{includeBody}} option, with the 
stream-caching implications documented.
* *Fail-closed by default.* If OPA is unreachable, times out, or returns a 
malformed or absent decision, the default must be *deny*. A {{failOpen}} option 
may exist but must default to {{false}}. See CAMEL-24281 and the keycloak 
fail-open finding, where an authorization component that failed open was 
treated as a security defect.
* *Header hygiene.* The decision headers are Camel-internal. The component must 
not let an external sender pre-set {{CamelOpaDecisionAllow}} and have it 
survive into the route - overwrite unconditionally, and document 
{{removeHeaders("CamelOpa*")}} for untrusted ingress.
* *In-process evaluation* ({{com.styra.opa:opa-java-wasm}}, Rego compiled to 
WASM) is deliberately out of scope for this first increment - that artifact is 
still at 0.0.6, and the REST/sidecar model covers the mainstream deployment. It 
can follow later.

h2. Scope of this issue

First increment, {{main}} only (new component, so no backport):
* new module {{components/camel-opa}} - component, endpoint, producer, 
configuration, constants
* {{OpaSecurityPolicy}} + {{OpaSecurityProcessor}} implementing 
{{AuthorizationPolicy}}
* unit tests covering allow, deny, unreachable PDP (fail-closed), non-boolean 
decision documents, and the security-policy path
* component documentation ({{src/main/docs/opa-component.adoc}})




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to