ajmyyra / ambassador-auth-oidc

OpenID Connect AuthService for Ambassador API Gateway
MIT License
88 stars 35 forks source link

Redirect fails after successful login #21

Closed vakrat1 closed 4 years ago

vakrat1 commented 4 years ago

Hi I'm trying to connect Ambassador GW to Keycloak using this auth-oidc middleware. The only change I made in the auth-deployment.yaml and auth-service.yaml was to change from port 8080 to 9090.

I'm using MacOS Here is the K8S deployments:

NAME                                  READY   STATUS    RESTARTS   AGE
pod/ambassador-558b7b6cb7-8gzq9       1/1     Running   8          28h
pod/ambassador-558b7b6cb7-c8vd8       1/1     Running   9          28h
pod/ambassador-558b7b6cb7-m9jdp       1/1     Running   8          28h
pod/flinckr-events-7b97db9d64-pc2tz   1/1     Running   2          20h
pod/keycloak-0                        1/1     Running   2          17d
pod/oidc-auth-6cb698d467-ljncs        2/2     Running   5          20h
pod/postgres-kc-postgresql-0          1/1     Running   2          18d

NAME                                      TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
service/ambassador                        LoadBalancer   10.99.201.29     localhost     80:32393/TCP,443:30066/TCP      4d3h
service/ambassador-admin                  ClusterIP      10.99.238.140    <none>        8877/TCP                        4d3h
service/ambassador-oidc-auth              ClusterIP      10.105.50.239    <none>        9090/TCP                        20h
service/flinckr-events                    ClusterIP      10.101.31.137    <none>        9000/TCP                        20h
service/keycloak-headless                 ClusterIP      None             <none>        8080/TCP,8443/TCP               17d
service/keycloak-http                     LoadBalancer   10.107.104.233   localhost     8080:32156/TCP,8443:32712/TCP   5d15h
service/kubernetes                        ClusterIP      10.96.0.1        <none>        443/TCP                         18d
service/oidc-auth                         ClusterIP      10.102.175.104   <none>        9090/TCP                        20h
service/postgres-kc-postgresql            ClusterIP      10.103.204.109   <none>        5432/TCP                        18d
service/postgres-kc-postgresql-headless   ClusterIP      None             <none>        5432/TCP                        18d

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ambassador       3/3     3            3           4d3h
deployment.apps/flinckr-events   1/1     1            1           20h
deployment.apps/oidc-auth        1/1     1            1           20h

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/ambassador-558b7b6cb7       3         3         3       4d3h
replicaset.apps/flinckr-events-7b97db9d64   1         1         1       20h
replicaset.apps/oidc-auth-6cb698d467        1         1         1       20h

NAME                                      READY   AGE
statefulset.apps/keycloak                 1/1     17d
statefulset.apps/postgres-kc-postgresql   1/1     18d

The task is to make Ambassador to redirect to Keycloak in case the user is not authenticated, or in case the session is expired.

Here are the secrets I configured:

kubectl create secret generic ambassador-auth-jwt-key --from-literal=jwt-key=$(openssl rand -base64 64|tr -d '\n ')

kubectl create secret generic ambassador-auth-redis-password --from-literal=redis-password=$(openssl rand -base64 20)

kubectl create secret generic ambassador-auth-oidc-provider --from-literal=oidc-provider="http://keycloak-http:8080/auth/realms/flinckr_realm"

kubectl create secret generic ambassador-auth-self-url --from-literal=self-url="http://localhost"

kubectl create secret generic ambassador-auth-client-id --from-literal=client-id="flinckr_backend"

kubectl create secret generic ambassador-auth-client-secret --from-literal=client-secret= <MY_SECRET>

I'm able to successfully go thru the OIDC process, as was nicely explained in the OIDC-flow.png

The problem I'm facing is that the last redirect is wrong. I'll explain.... Here what I see in the browser: 1- going to http://localhost/events

oidc-phase-1

Call is being directed to keycloak as was expected

2- Keycloak shows login screen

oidc-phase-2

3- Code is being sent to the browser and from there back to oidc-auth service for generating JWT (if I get it correctly)

oidc-phase-3 oidc-phase-5

4- And now that user is authenticated, I expect the browser to be redirected back to my original request which was http://localhost/events. But here is the problem, the browser is being redirected to http://localhost (which of course returns 404 Error), instead of going to http://localhost/events.

oidc-phase-6

You could see from the attached images, that the authentication process is correct. The only thing is missing how to make sure the last redirect URL is set correctly

This is the Ambassador image:

image:
  repository: quay.io/datawire/ambassador
  tag: 0.86.0
  pullPolicy: IfNotPresent

This is the Keycloak image:

image:
    repository: jboss/keycloak
    tag: 7.0.0
    pullPolicy: IfNotPresent

Appreciate your help

ajmyyra commented 4 years ago

Hi there,

Thank you for reporting this! Could you post your Ambassador config for the AuthService? My initial guess is that it's set up so that /events gets dropped when requests are routed there so the OAuth client thinks the user is trying to access /. If the AuthService really gets the original URL, I'm interested in whats stored within Redis in the middle of the authentication redirect (when you haven't yet returned from Keycloak to the service).

This software only stores the requested URL and then redirects back to it when the authentication is done, so if it would return something else than it sees it really is a bug. The only time user is redirected to / is when we see an outdated session and user is forced to re-auth with Keycloak.

vakrat1 commented 4 years ago

Hi Appreciate so much your time and effort.

As for the Ambassador config for the AuthService, do you mean this configuration?

---
apiVersion: v1
kind: Service
metadata:
  name: ambassador-oidc-auth
  annotations:
    getambassador.io/config: |
      ---
      apiVersion: ambassador/v0
      kind:  AuthService
      name:  authentication
      auth_service: "oidc-auth:9090"
      proto: http
      allowed_headers:
      - "X-Auth-Userinfo"
      ---
      apiVersion: ambassador/v0
      kind:  Mapping
      name:  login_mapping
      prefix: /login
      rewrite: /login
      service: oidc-auth:9090
spec:
  type: ClusterIP
  selector:
    app: oidc-auth
  ports:
  - port: 9090
    name: http-oidc-auth
    targetPort: http-api
vakrat1 commented 4 years ago

Here is also the deployment.yaml file of the oidc-auth middleware:

---
apiVersion: v1
kind: Service
metadata:
  name: oidc-auth
spec:
  type: ClusterIP
  selector:
    app: oidc-auth
  ports:
  - port: 9090
    name: http-oidc-auth
    targetPort: http-api
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: oidc-auth
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"
      labels:
        app: oidc-auth
    spec:
      containers:
      - name: oidc-auth
        image: ajmyyra/ambassador-auth-oidc:1.3
        imagePullPolicy: Always
        ports:
        - name: http-api
          containerPort: 9090
        env:
          - name: PORT
            value: "9090"
          - name: REDIS_ADDRESS
            value: "localhost:6379"
          - name: OIDC_SCOPES
            value: "profile email"
          - name: JWT_HMAC_SECRET
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-jwt-key
                key: jwt-key
          - name: REDIS_PASSWORD
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-redis-password
                key: redis-password
          - name: OIDC_PROVIDER
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-oidc-provider
                key: oidc-provider
          - name: SELF_URL
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-self-url
                key: self-url
          - name: CLIENT_ID
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-client-id
                key: client-id
          - name: CLIENT_SECRET
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-client-secret
                key: client-secret

      - name: auth-redis
        image: bitnami/redis:latest
        imagePullPolicy: Always
        ports:
        - name: redis-port
          containerPort: 6379
        env:
          - name: REDIS_PASSWORD
            valueFrom:
              secretKeyRef:
                name: ambassador-auth-redis-password
                key: redis-password
ajmyyra commented 4 years ago

I see a small difference in login_mapping section of your config.

apiVersion: ambassador/v0
      kind:  Mapping
      name:  login_mapping
      prefix: /login
      rewrite: /login
      service: oidc-auth:9090

This is different from our example config that only has prefix: /login/ without the rewrite. What happens if you have just that?

vakrat1 commented 4 years ago

It seems like I had some leftover configuration in Ambassador, since I was trying several configurations while trying to fix the problem I had (turn on/off the Pro mode)

I got back to the basic configuration, as mentioned by you and it works like a magic.

Thank you so much for your time and support

ajmyyra commented 4 years ago

Great to hear! Also happy to notice that people are using the software. I'm doing some improvements for this soon, and adding support for #19. Code quality and test coverage isn't that good currently, so hoping to have this addressed.

vakrat1 commented 4 years ago

These are wonderful news. I think this is a great project!