Authorization Policy denies a request, but claim exists, request.auth.claims[realm_access_roles] with given value

Goal: Use keycloak to authenticate and (somehow)authorize for ingressgateway exposed services.

So I am using oauth2-proxy as ext_authz provider. In terms of authentication this is fine, but for authorization it doesnt have access control like for these hosts+paths allow users with these roles, etc.
So I still want to use istio’s claim based access control.

So I have got the pipeline setup this way.

  1. First an authpolicy with custom action “oauth2-proxy” that uses keycloak for token and login, and forwards all relaent headers to upstream.
  2. Another Deny(/allow) action authpolicy that dictates whether or not to allow based on the jwt token claim, that oauth2-proxy is putting.

I thought technically this should work because Custom action comes before deny/allow.
But it not working. Need HELP!!

The request(httpbin) gets denied by the second auth policy, even though i verify that, the token part of “Authorization: Bearer $JWT_TOKEN” this header contains the value in claim request.auth.claims[realm_access_roles], see below for jwt token and authpolicies.
For this I removed the second authpolicy and the setup works. The request is properly redirected to keycloak & authenticated and I can see the header also in httpbin.

questions:

  • Do i need a RequestAuthentication in this case? because a valid JWT token is coming as an Authorization: Bearer header anyway
  • Did I get something wrong in my setup?
  • Is there any other way I can have better access control with oauth2-proxy’s builtin settings which use the keycloak roles and match url, something like that (I know this is more an oauth2-proxy question)
  • Is there any other way with istio to achieve the feature like this in oauth2-proxy where; if there is no(valid) auth header redirect to keycloak login and get token put in header, and redirect back to previous url, then authorize based on claims/roles??.
  • Is there any other way of achieving what i want?

Thank a lot.
Any input is much appreciated.


(click) Here are my two authpolices:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: sample-httpbin-authn-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      istio: ingressgateway-internal
  action: CUSTOM
  provider:
    name: oauth2-proxy
  rules:
  - to:
    - operation:
        hosts: ["api-internal.v3box1.mosip.net","temp-gate.v3box1"]
        paths: ["/httpbin","/httpbin/*"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: sample-httpbin-authz-policy
  namespace: istio-system
spec:
  selector:
    matchLabels:
      istio: ingressgateway-internal
  action: DENY
  rules:
  - to:
    - operation:
        hosts: ["api-internal.v3box1.mosip.net","temp-gate.v3box1"]
        paths: ["/httpbin","/httpbin/*"]
    when:
    - key: request.auth.claims[realm_access_roles]
      notValues: ["kibana_access"]

(click) And when i remove the second AuthPolicy, here is the output that i get on httpbin (redacted):
{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "en-GB,en;q=0.9", 
    "Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2VS05d2w5NmZpLXpIRFNuUDNTVTUzd0lhVVRVVnljUTl2WnZVMmlmNDlVIn0.eyJleHAiOjE2Mzc3NzYzNTksImlhdCI6MTYzNzc3NjA1OSwiYXV0aF90aW1lIjoxNjM3Nzc2MDU5LCJqdGkiOiI4Njk4YzFkMS1iNjc5LTRmMDctYmIxOS1kOTY3MTllMDlmNWMiLCJpc3MiOiJodHRwczovL2lhbS52M2JveDEubW9zaXAubmV0L2F1dGgvcmVhbG1zL2lzdGlvIiwiYXVkIjoiaXN0aW8tYXV0aC1jbGllbnQiLCJzdWIiOiI1OTQ4ZGQyOS1hZjFiLTQxM2MtODQyYS03N2U4YWNhNGJjMDIiLCJ0eXAiOiJJRCIsImF6cCI6ImlzdGlvLWF1dGgtY2xpZW50Iiwibm9uY2UiOiJmakMwRUo4TkdjRUxGdk1LaTFkbWRjdXFpRll1YXlNSTc5OXdwQ1k4NzNJIiwic2Vzc2lvbl9zdGF0ZSI6IjVlMjMyZDdiLWNiOGMtNDQxYi1iNTA4LWE5ZjRjZjhjYzEwOSIsImF0X2hhc2giOiI3a3RZQmlsR1pHbDVfQjRDcG54LTR3IiwiYWNyIjoiMSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicmVhbG1fYWNjZXNzX3JvbGVzIjpbImtpYmFuYV9hY2Nlc3MiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiZGVmYXVsdC1yb2xlcy1pc3RpbyJdLCJuYW1lIjoiTGFsaXRoIEtvdGEiLCJncm91cHMiOltdLCJpc19raWJhbmFfYWNjZXNzIjoidHJ1ZSIsInByZWZlcnJlZF91c2VybmFtZSI6ImxhbGl0aCIsImdpdmVuX25hbWUiOiJMYWxpdGgiLCJmYW1pbHlfbmFtZSI6IktvdGEiLCJlbWFpbCI6ImxhbGl0aEBtb3NpcC5pbyJ9.dKyMO_ubVRh_Dcv1T0dJEG0WtxMSgjt3nfM67OCzgTTT0jILYZDKCXh-x7TyGWV6QJ0ibK-JOsUAsHEemLvlhDt_HvtXAIm4kC1DhAFwj8O6f2jfm9bpocmOdC4lKUFV3YDdUxcX8QMzyJTfHLYkPtVjwvomDB6DVDL5PhIqhJI5sVXPNo14bkKVLb-SBQoEI9QtfRQmuFqoOk3UZBOdfX-ROVyTn2mMKRUa4SVMhGbPvcPViScscVpDYTl6tjxwnto7VwHRa1-oOmGJhrmpZ9jzqZnMWKG9D68ikcn1oA1qd_pqOeIBZRTe6-NjhBaRr2kY-6brN9k7SYeYuZNarg", 
    "Cookie": <big cookie>
    <lots of headers>
    "X-Auth-Request-Access-Token": <bearer token>
    <lots of other headers>
  }
  <others>
}
(click) here is the decoded jwt token(redacted). Note the string "kibana_access" is present in the list in claim "realm_access_roles".
{
  <some_fields>
  "iss": "https://<domain>/auth/realms/istio",
  "aud": "istio-auth-client",
  "sub": "<key>",
  "typ": "ID",
  "realm_access_roles": [
    "kibana_access",
    "offline_access",
    "uma_authorization",
    "default-roles-istio"
  ],
  "name": "Lalith Kota",
  "groups": [],
  <some_other_metadata>
}

And for the oauth2-proxy setup;

(click) Here is my istio configmap (redacted)
data:
  mesh: |-
    defaultConfig:
      proxyMetadata:
        ISTIO_META_IDLE_TIMEOUT: 0s
      holdApplicationUntilProxyStarts: true
      discoveryAddress: istiod.istio-system.svc:15012
      gatewayTopology:
        numTrustedProxies: 2
      proxyMetadata: {}
      tracing:
        zipkin:
          address: zipkin.istio-system:9411
    enablePrometheusMerge: true
    pathNormalization:
      normalization: MERGE_SLASHES
    rootNamespace: istio-system
    trustDomain: cluster.local
    extensionProviders:
    - name: oauth2-proxy
      envoyExtAuthzHttp:
        service: oauth2-proxy.oauth2-proxy.svc.cluster.local
        port: 80
        includeRequestHeadersInCheck: ["authorization", "cookie"]
        includeAdditionalHeadersInCheck:
          X-Auth-Request-Redirect: "https://%REQ(:authority)%%REQ(:path)%"
        headersToUpstreamOnAllow: ["x-forwarded-access-token", "authorization", "path", "x-auth-request-user", "x-auth-request-email", "x-auth-request-access-token"]
        headersToDownstreamOnDeny: ["content-type", "set-cookie"]
(click) and here is my oauth2-proxy configuration (redacted):
provider = "keycloak-oidc"
oidc_issuer_url = "https://<domain>/auth/realms/istio"
email_domains = ["*"]
upstreams = ["static://200"]
redirect_url = "https://<domaint>/oauth2/callback"
insecure_oidc_allow_unverified_email = true
reverse_proxy = true
pass_access_token = true
pass_authorization_header = true
silence_ping_logging = true
set_authorization_header = true
set_xauthrequest = true
skip_provider_button = true
skip_auth_strip_headers = true
ssl_insecure_skip_verify = true
whitelist_domains = ["<base domain>"]
cookie_domains = ["<base domain>"]