ory / oathkeeper

A cloud native Identity & Access Proxy / API (IAP) and Access Control Decision API that authenticates, authorizes, and mutates incoming HTTP(s) requests. Inspired by the BeyondCorp / Zero Trust white paper. Written in Go.
https://www.ory.sh/?utm_source=github&utm_medium=banner&utm_campaign=hydra
Apache License 2.0
3.2k stars 349 forks source link

Allow/deny `remote(_json)` authorizers depending response content #1125

Open David-Wobrock opened 11 months ago

David-Wobrock commented 11 months ago

Preflight checklist

Describe your problem

We've set up Oathkeeper to query OPA for authorization through a remote_json handler.

To allow or deny a request, Oathkeeper requires the remote to return the correct HTTP code.

The remote authorizer is expected to return either "200 OK" or "403 Forbidden" to allow/deny access.

Source: https://www.ory.sh/docs/oathkeeper/pipeline/authz#remote_json-configuration

However, evaluating rules in OPA will always return 200 (if the rule evaluation itself worked), but the content of the response will contain the result of the evaluation. Therefore it's in the response body that we have a JSON containing a key with a boolean.

Describe your ideal solution

One solution would be to specify in the authorization handler configuration the JSON path to follow in the response body that should contain a boolean value. If it's set, then we can ignore the HTTP response code for allow/deny, but rather use the value of this boolean.

The handler could raise an error if the response is not JSON, the path does not exist, or the value found is not a boolean 🙂

I'm not sure how to configure the JSON path, it could use the jq syntax.

For instance:

        remote_json:
          enabled: true
          config:
            remote: http://127.0.0.1:4242/opa-proxy
            payload: |
              {
                "input": {
                  "sub": "{{ print .Subject }}",
                  "http": {
                    "url": "{{ print .MatchContext.URL }}",
                    "method": "{{ print .MatchContext.Method }}"
                  }
                }
              }
            json_response_path: ".result"

Which would extract the boolean from a JSON response body:

{
  "result": true
}

Workarounds or alternatives

Our current workaround is to have a tiny proxy service between Oathkeeper and OPA. It forwards requests from Oathkeeper to OPA, and returns the expected HTTP code to Oathkeeper depending on the OPA response body.

It works, but it would be great if Oathkeeper could talk directly to OPA.

Version

v0.40.6

Additional Context

Once the design is validated, we would be able to give some time to implement the feature if necessary :)

On a side note, it was also discussed on OPA side if this couldn't be handled by OPA directly: https://github.com/open-policy-agent/opa/issues/3539. But the discussions point to an OPA plugin/contrib which was never implemented I reckon.