How to implement istio authorization using oauth2 and keycloak

I have been trying to implement istio authorization using Oauth2 and keycloak. I have followed few articles related to this API Authentication: Configure Istio IngressGateway, OAuth2-Proxy and Keycloak, Authorization Policy

Expected output: My idea is to implement keycloak authentication where oauth2 used as an external Auth provider in the istio ingress gateway. when a user try to access my app in <ingress host>/app , it should automatically redirect to keycloak login page.

How do i properly redirect the page to keycloak login screen for authentication ?

problem: When i try to access <ingress host>/app, the page will take 10 seconds to load and it gives status 403 access denied. if i remove the authorization policy (kubectl delete -f authorization-policy.yaml) within that 10 seconds, it will redirect to the login screen (keycloak)

oauth2.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: oauth-proxy
  name: oauth-proxy
spec:
  type: NodePort
  selector:
    app: oauth-proxy
  ports:
  - name: http-oauthproxy
    port: 4180
    nodePort: 31023
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: oauth-proxy
  name: oauth-proxy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: "oauth-proxy"
  template:
    metadata:
      labels:
        app: oauth-proxy
    spec:
      containers:
      - name: oauth-proxy
        image: "quay.io/oauth2-proxy/oauth2-proxy:v7.2.0"
        ports:
        - containerPort: 4180
        args:
          - --http-address=0.0.0.0:4180
          - --upstream=http://test-web-app:3000
          - --set-xauthrequest=true
          - --pass-host-header=true
          - --pass-access-token=true
        env:
          # OIDC Config
          - name: "OAUTH2_PROXY_PROVIDER"
            value: "keycloak-oidc"
          - name: "OAUTH2_PROXY_OIDC_ISSUER_URL"
            value: "http://192.168.1.2:31020/realms/my_login_realm"
          - name: "OAUTH2_PROXY_CLIENT_ID"
            value: "my_nodejs_client"
          - name: "OAUTH2_PROXY_CLIENT_SECRET"
            value: "JGEQtkrdIc6kRSkrs89BydnfsEv3VoWO"
          # Cookie Config
          - name: "OAUTH2_PROXY_COOKIE_SECURE"
            value: "false"
          - name: "OAUTH2_PROXY_COOKIE_SECRET"
            value: "ZzBkN000Wm0pQkVkKUhzMk5YPntQRUw_ME1oMTZZTy0="
          - name: "OAUTH2_PROXY_COOKIE_DOMAINS"
            value: "*"
          # Proxy config
          - name: "OAUTH2_PROXY_EMAIL_DOMAINS"
            value: "*"
          - name: "OAUTH2_PROXY_WHITELIST_DOMAINS"
            value: "*"
          - name: "OAUTH2_PROXY_HTTP_ADDRESS"
            value: "0.0.0.0:4180"
          - name: "OAUTH2_PROXY_SET_XAUTHREQUEST"
            value: "true"
          - name: OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER
            value: "true"
          - name: OAUTH2_PROXY_SSL_UPSTREAM_INSECURE_SKIP_VERIFY
            value: "true"
          - name: OAUTH2_PROXY_SKIP_PROVIDER_BUTTON
            value: "true"
          - name: OAUTH2_PROXY_SET_AUTHORIZATION_HEADER
            value: "true"

keycloak.yaml

apiVersion: v1
kind: Service
metadata:
  name: keycloak
spec:
  type: NodePort
  selector:
    app: keycloak
  ports:
  - name: http-keycloak
    port: 8080
    nodePort: 31020
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
spec:
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
      - name: keycloak
        image: quay.io/keycloak/keycloak:17.0.0
        ports:
        - containerPort: 8080
        args: ["start-dev"]
        env:
        - name: KEYCLOAK_ADMIN
          value: "admin"
        - name: KEYCLOAK_ADMIN_PASSWORD
          value: "admin"

istio-operator.yaml

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    accessLogFile: /dev/stdout
    extensionProviders:
    - name: "oauth2-proxy"
      envoyExtAuthzHttp:
        service: "oauth-proxy.default.svc.cluster.local"
        port: "4180" # The default port used by oauth2-proxy.
        includeHeadersInCheck: ["authorization", "cookie","x-forwarded-access-token","x-forwarded-user","x-forwarded-email","x-forwarded-proto","proxy-authorization","user-agent","x-forwarded-host","from","x-forwarded-for","accept","x-auth-request-redirect"] # headers sent to the oauth2-proxy in the check request.
        headersToUpstreamOnAllow: ["authorization", "path", "x-auth-request-user", "x-auth-request-email", "x-auth-request-access-token","x-forwarded-access-token"] # headers sent to backend application when request is allowed.
        headersToDownstreamOnDeny: ["content-type", "set-cookie"] # headers sent back to the client when request is denied.
        

gateway.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: test-gateway
  namespace : istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - '*'

virtual-service.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: gateway-vs
spec:
  hosts:
    - '*'
  gateways: 
    - istio-system/test-gateway
  http:
    - match:
      - uri:
          prefix: /oauth2
      route:
      - destination:
          host: oauth-proxy.default.svc.cluster.local
          port:
            number: 4180
    - match:
      - uri:
          prefix: /app
      route:
      - destination:
          host: test-web-app.default.svc.cluster.local
          port:
            number: 3000

authorization-policy.yaml

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: example-auth-policy
spec:
  action: CUSTOM
  provider:
    name: "oauth2-proxy"
  rules:
  - to:
    - operation:
        paths: ["/app"]
        notPaths: ["/oauth2/*"]

The redirection issue solved by updating authorization policy

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: example-auth-policy
  namespace: istio-system
spec:
  action: CUSTOM
  provider:
    name: "oauth2-proxy"
  rules:
  - to:
    - operation:       
        paths: ["/app"]
        notPaths: ["/oauth2/*"]
  selector:
    matchLabels:
      app: istio-ingressgateway
  • Added istio-system namespace instead of workload namespace (it was default in my case)
  • Forgot to add matchLabels.

Hello @Krishnan, I am trying to implement this too.

Do you mind explaining steps in Keycloak, etc?

Thanks a lot!

@Sergio_Sanchez_Vega
For keycloak part,
it would be easy to setup. Create Realm and client.
Inside client setup, you need to select the access type confidential .
Additionally you need to add 2 mappers (Audiences, Group membership). Additional informations can be found here OAuth Provider Configuration | OAuth2 Proxy.
Apart from that, you can follow the above yaml files.

If you need to add user role based accessibility on istio, follow How to implement istio authorization based on keycloak user role

@Krishnan
/app is redirecting…but to the internal K8s service. E.g: https://keycloak.keycloak.svc.cluster.local/auth/realms/ssp/protocol/openid-connect/auth?approval_prompt=force&client_id=oauth2-proxy-client&nonce=hWVx9K5e1utA85QYNeeict5QzON-J5hIP0Vmua3Vsmw&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code&scope=openid+email+profile&state=iT7Vs04vFL1uUyDqjYiqO9dRTOUrA90dH6ecM5n4tho%3A%2Fapp

Do you know how to handle this?

@Sergio_Sanchez_Vega
Do you have kiali to see the istio config ?
what kind of action you are having inside your authorization policy ? (ALLOW, DENY or CUSTOM).
How did you configure it ? It would be great if you can post this as a question in this website.

@Sergio_Sanchez_Vega What did you give for OIDC issuer url in Oauth2 yaml ?

@Krishnan

The OIDC issuer url in oauth2 yaml is the internal url (http://keycloak.keycloak.svc.cluster.local/auth/realms/ssp).

As you can see, this is an internal URL and the problem is when I try to login using my laptop, through the istio ingress using port-forward (kubectl port-forward -n istio-ingress svc/istio-ingress 8080:80).

When I try to access to the protected endpoint (/app), it redirects me (the authorization policy and the external auth provider are working properly) to the internal URL, which is the one keycloak has given to oauth2-proxy when getting the OIDC info.

I would like to get, in my laptop, redirected to https://localhost:8080/auth/realms/ssp/protocol/openid-connect/auth?approval_prompt=force&client_id=oauth2-proxy-client&nonce=hWVx9K5e1utA85QYNeeict5QzON-J5hIP0Vmua3Vsmw&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code&scope=openid+email+profile&state=iT7Vs04vFL1uUyDqjYiqO9dRTOUrA90dH6ecM5n4tho%3A%2Fapp

instead of https://keycloak.keycloak.svc.cluster.local/auth/realms/ssp/protocol/openid-connect/auth?approval_prompt=force&client_id=oauth2-proxy-client&nonce=hWVx9K5e1utA85QYNeeict5QzON-J5hIP0Vmua3Vsmw&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code&scope=openid+email+profile&state=iT7Vs04vFL1uUyDqjYiqO9dRTOUrA90dH6ecM5n4tho%3A%2Fapp

Apart from this…do you know, why is it redirecting me to https instead of http? OIDC answers http…

Thanks a lot for your help!

2 Likes

@Sergio_Sanchez_Vega I cannot say without looking into the files. If you need to be redirected to localhost:8080/auth.realms…, then you need to give the OIDC issuer url accordingly.
for getting https, may be you have TCP enabled in your keycloak.

I also have the same problem , oauth2-proxy redirects me to the internal URL. When i set OIDC issuer url is localhost:31020, oauth2-proxy can not connect.Did you have any solution for this ?

http://10.100.152.178:8080/realms/my_login_realm/protocol/openid-connect/auth?approval_prompt=force&client_id=my_nodejs_client&nonce=fFckahyO4BOX4ZFt-ZvJsHpKrFhnF0-VnoC8aLVmg1A&redirect_uri=http%3A%2F%2Flocalhost%2Foauth2%2Fauth&response_type=code&scope=openid+email+profile&state=2wCnuPrFNViMb4brWCkbnX3R9aIIHONiFD-vAtcqjqM%3A%2F

10.100.152.178 is cluster-ip

I am getting rbac access denied error to my nginx app after i deploy
authorization policy

keycloak is on different hostname on the same cluster

I have a similar error ( rbac access denied).And I have followed this tutorial (How to implement istio authorization based on keycloak user role) but it is not working . In addition to this I have not seen any virtual services in the tutorial. How are they configured?How I can test the connectivity ? How the cluster is created and configured ?

Hi together

I am working on the same use case and I am reading a lot.
But it is hard to find out a easy solution who works.

Expected output: My idea is to implement keycloak authentication used as an external Auth provider in the istio ingress gateway. When a user try to access my app in <ingress host>/app , it should automatically redirect to keycloak login page.

My first point where I am struggling.
Is it necessary to use “oauth2-proxy” for this?

Or is it possible to configure Keycloak directly as an external provider in “IstioOperator”?
I like to prefer the easiest way with less tools as possible.

Thanks for your advice
Manfred

Hi together

The following alternative solution would also be possible for my use case.

Step 1)
User is accessing directly a Keycloak Login URL. For example:
https://keycloak.dev.cluster.tech/realms/our-application/protocol/openid-connect/auth?client_id=alertmanager&redirect_uri=https%3A%2F%2Fprometheus-alertmanager.dev.cluster.tech&response_type=code&scope=openid
Insert Username and Password.

Step 2)
Keycloak is redirecting to the application and the user is authenticated.

My first question:
Is this scenario generally possible?

I was following this post and it was helping me a lot:
https://aytartana.wordpress.com/2023/03/02/adding-authentications-with-no-code-istio-and-keycloak/

The following commands are working for me like expected:

TOKEN=$(curl
-sk
–data “username=jane&password=janespw&grant_type=password&client_id=alertmanager”
“https://keycloak.dev.cluster.tech/realms/our-applications/protocol/openid-connect/token” | jq “.access_token”)

echo $TOKEN

curl -kv -H “Authorization: Bearer $TOKEN” “https://prometheus-alertmanager.dev.cluster.tech/#/alerts”

Here I am authenticated and I get the application content with curl. That is fine.
Which kind of configuration do I need for a “normal” Web-browser login?

Thanks for your advice
Manfred

I am wondering the same thing, what have you got so far.

I am trying to implement SPA + BFF pattern, to avoid store token in browser.

by far, I use the oauth2-proxy as the BFF, but I consider it as a complex solution, I am looking for a istio-native solution to reduce complexity and network hops.

something I found maybe useful was this envoy oauth2 filter