Authorization for an application in the cloud

I started writing my pet project and got to the point where I needed to authorize the user in my application. Before this, I had experience using spring with keylock, but since I had now deployed the application in the cloud, I decided to find out how to use keylock in the cloud. Maybe you can use keylock as an authorization proxy?

To begin with, I had an application deployed in cuber and an ingress service. To begin with, I decided to use kubectl, not helm, in order to figure out what is responsible for what, and then switch to helm if necessary. Actually, I configured the ingress with this command.

kubectl --namespace default apply -f - <<< '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
spec:
  rules:
    - host: somehost.ru
      http:
        paths:
          - path: /app
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80
  ingressClassName: nginx
'

After digging a little on the Internet, I found that ingress can delegate authorization using annotations, and the service to which you can delegate this way is called oauth2-proxy.

oauth2-proxy

oauth2-proxy itself does not know how to authorize users. It is essentially an adapter between ingress and services that can be used for authorization. IN documentation they have instructions on how to use Google, GitHub, GitLab for authorization… To begin with, I decided not to set up the checklock service, but to experiment with something ready-made, for example, authorize through GitHub.

In fact, following the instructions, I configured oauth2-proxy and added only two settings:
1) –set-xauthrequest – adds X-Auth-Request-User, X-Auth-Request-Groups, X-Auth-Request-Email and X-Auth-Request-Preferred-Username headers to the request.
2) –set-authorization-header – adds Authorization: Bearer…

kubectl --namespace default apply -f - <<< '
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: oauth2-proxy
  name: oauth2-proxy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: oauth2-proxy
  template:
    metadata:
      labels:
        app: oauth2-proxy
    spec:
      containers:
        - name: oauth2-proxy
          image: quay.io/oauth2-proxy/oauth2-proxy:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 4180
              protocol: TCP
          args:
            - --provider=github
            - --email-domain=*
            - --upstream=file:///dev/null
            - --http-address=0.0.0.0:4180
            - --scope=user:email
            - --set-xauthrequest=true
            - --set-authorization-header=true
          env:
            - name: OAUTH2_PROXY_CLIENT_ID
              value: acb0636a63a40efab6d
            - name: OAUTH2_PROXY_CLIENT_SECRET
              value: 4076962096c9b3d1774e9cb6850b67eef23ad88e
            - name: OAUTH2_PROXY_COOKIE_SECRET
              value: 6Pf1nzU4nV2syd8e0JAkmw==
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: oauth2-proxy
  name: oauth2-proxy
spec:
  ports:
    - name: http
      port: 4180
      protocol: TCP
      targetPort: 4180
  selector:
    app: oauth2-proxy
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-oauth2-proxy
  annotations:
    nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
spec:
  rules:
    - host: somehost.ru
      http:
        paths:
          - path: /oauth2
            pathType: Prefix
            backend:
              service:
                name: oauth2-proxy
                port:
                  number: 4180
  ingressClassName: nginx
'

Next you need to set the environment variables (in general, you can also pass them in arguments):
OAUTH2_PROXY_CLIENT_ID – taken from applications in git-hub
OAUTH2_PROXY_CLIENT_SECRET – also taken from the application, but shown only once, after which you need to create a new one
OAUTH2_PROXY_COOKIE_SECRET – can be generated like this python ‑c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'

Application settings in github

Application settings in github

All that remains is to add annotations to the ingress service and authorization will work.

1) nginx.ingress.kubernetes.io/auth-url – judging by the instructions on the Internet, you can usually specify it via https://$host… but it didn’t work for me. There was a problem with the ssl certificate; perhaps it was not configured quite correctly. Therefore, I register this URL internally.
2 nginx.ingress.kubernetes.io/auth-signin: – this url will be returned to the user for unsuccessful authorization.
3) nginx.ingress.kubernetes.io/auth-response-headers: – here ingress adds the specified headers from the oauth2-proxy response to the request
4) nginx.ingress.kubernetes.io/proxy-buffer-size: – cache size, needed so that you don’t have to ask for authorization for every request.

kubectl --namespace default apply -f - <<< '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  annotations:
    nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.default.svc.cluster.local:4180/oauth2/auth"
    nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri"
    nginx.ingress.kubernetes.io/auth-response-headers: "x-auth-request-user, x-auth-request-email, authorization"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
spec:
  rules:
    - host: somehost.ru
      http:
        paths:
          - path: /app
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80
  ingressClassName: nginx
'

We now go to https://somehost.ru/app and we are redirected to the authorization page in git-hub.

Keycloak

So, now I have some kind of authorization, but I want to control it myself. It’s time to switch oauth2-proxy to keylock. And at first I was a little mistaken. A lot of instructions on the Internet tell you how to set up keylock up to version 16. I set it up, looked at how to connect Russian providers like VK Yandex, etc. and realized that I needed to configure the 19th version. In general, pay attention to this, version 19 is better, lighter, faster and all that. At least that’s what the documentation says.

My actual keylock setup looks like this:

kubectl --namespace default apply -f - <<< '
apiVersion: v1
kind: Service
metadata:
  name: keycloak
  labels:
    app: keycloak
spec:
  type: LoadBalancer
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: keycloak
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  namespace: default
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
        - name: keycloak
          image: playaru/keycloak-russian
          args: [ "start" ]
          env:
            - name: KC_HOSTNAME_URL
              value: "https://somehost.ru"
            - name: KC_HOSTNAME_ADMIN_URL
              value: "https://somehost.ru"
            - name: KC_HOSTNAME_STRICT
              value: "false"
            - name: KC_HTTP_ENABLED
              value: "true"
            - name: KEYCLOAK_ADMIN
              value: "keycloak"
            - name: KEYCLOAK_ADMIN_PASSWORD
              value: "keycloak"
            - name: KC_PROXY
              value: "edge"
            - name: KC_HEALTH_ENABLED
              value: "true"
            - name: KC_DB
              value: "postgres"
            - name: KC_DB_URL
              value: ""
            - name: KC_DB_URL_DATABASE
              value: "keycloak"
            - name: KC_DB_USERNAME
              value: ""
            - name: KC_DB_PASSWORD
              value: ""
          ports:
            - name: http
              containerPort: 8080
          readinessProbe:
            httpGet:
              path: /realms/master
              port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-test
  namespace: default
spec:
  tls:
    - hosts:
        - somehost.ru
      secretName: k8s-secret
  ingressClassName: nginx
  rules:
    - host: somehost.ru
      http:
        paths:
          - path: /admin
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 80
          - path: /realms/
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 80
          - path: /resources/
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 80
          - path: /robots.txt
            pathType: ImplementationSpecific
            backend:
              service:
                name: keycloak
                port:
                  number: 80
          - path: /js/
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 80
  '

If you do not specify database settings, the keylock will create the database itself, but I needed to connect to my own. And the image I took playaru/keycloak-russianso that I would immediately have Russian providers and not have to worry about connecting them.

In addition to the service settings, you need to pass the path to keycloak through the ingress, since now it will return the authorization page. It’s better not to forward the /admin path; for example, you can add a vpn to connect to services inside the cuber, but I haven’t learned how to do that yet.

Now you need to change the oauth2-proxy settings.

kubectl --namespace default apply -f - <<< '
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: oauth2-proxy
  name: oauth2-proxy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: oauth2-proxy
  template:
    metadata:
      labels:
        app: oauth2-proxy
    spec:
      containers:
        - name: oauth2-proxy
          image: quay.io/oauth2-proxy/oauth2-proxy:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 4180
              protocol: TCP
          args:
            - --provider=keycloak-oidc
            - --upstream=file:///dev/null
            - --http-address=0.0.0.0:4180
            - --whitelist-domain=somehost.ru
            - --cookie-domain=somehost.ru
            - --set-xauthrequest=true
            - --reverse-proxy=true
            - --set-authorization-header=true
            - --oidc-issuer-url=https://somehost.ru/realms/master
            - --ssl-insecure-skip-verify=true
            - --silence-ping-logging=true
            - --email-domain=*
          env:
            - name: OAUTH2_PROXY_CLIENT_ID
              value: ingress
            - name: OAUTH2_PROXY_CLIENT_SECRET
              value: <client-secret>
            - name: OAUTH2_PROXY_COOKIE_SECRET
              value: zTBum01F+fZmVntQutehXw==
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: oauth2-proxy
  name: oauth2-proxy
spec:
  ports:
    - name: http
      port: 4180
      protocol: TCP
      targetPort: 4180
  selector:
    app: oauth2-proxy
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-oauth2-proxy
  annotations:
    nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
spec:
  rules:
    - host: somehost.ru
      http:
        paths:
          - path: /oauth2
            pathType: Prefix
            backend:
              service:
                name: oauth2-proxy
                port:
                  number: 4180
  ingressClassName: nginx
'

Here the following settings have been added:
1) –oidc-issuer-url – url by which the keylock will return settings for authorization, registration, etc.
2) –ssl-insecure-skip-verify – this is where I turn off certificate verification, because it was not possible to set up a successful trip inside the cuber. There is clearly something wrong with my certificate.
3) –silence-ping-logging – literally “do not write to the ping cuber logs”

How to set up a keylock and where to get client-secret is well described here.

Now, when we enter the page https://somehost.ru/app, authorization in the keylock will open.

conclusions

In general, I managed to do what I wanted, authorization works. But you still need to figure out the certificate and probably a lot more.

Well, as always cartalthough last time there was no point in subscribing to it, but I’m trying)

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *