kubernetes, oauth, proxy, authentication

Protect Kubernetes External Endpoints with OAuth2 Proxy

Last update:

Sometimes you just want to expose some services that don't have any authentication mechanism. Many users have this issue, especially with Kubernetes, because it is damn easy to expose any service over ingress and also to have HTTPS by default with Let's Encrypt. The missing piece could be authentication in the application you want to expose. In this case, we can always leverage external authentication from GitHub, Google, and many others via OAuth. This is where OAuth2 Proxy comes into place. It's a reverse proxy that provides external authentication and it's relatively easy to set up.

Related posts:

Installation

Check out the related posts above for ingress and automatic HTTPS setup. Those are the requirements. First, we need to register the new OAuth application. I will use GitHub, so let's create one at https://github.com/settings/applications/new:

Replace with your domain name. As you can see, callback URL has added /oauth2 at the end. Write down the auth details after you click on register application. Let's also generate a cookie secret. You probably have python installed, so you could just run a python command instead of running it in a Docker container:

⚡ docker run -ti --rm python:3-alpine \
    python -c 'import secrets,base64; print(base64.b64encode(base64.b64encode(secrets.token_bytes(16))));'

You can deploy OAuth2 Proxy now. Use Helm to make things easier:

⚡ helm install --name authproxy \
    --namespace=ingress \
    --set config.clientID=<YOUR_CLIENT_ID> \
    --set config.clientSecret=<YOUR_SECRET> \
    --set config.cookieSecret=<GENERATED_COOKIE_SECRET> \
    --set extraArgs.provider=github \
    stable/oauth2-proxy

⚡ kubectl get pods --selector=app=oauth2-proxy -n ingress
NAME                                     READY     STATUS    RESTARTS   AGE
authproxy-oauth2-proxy-cdb4f675b-wvdg5   1/1       Running   0          1m

You are done with the setup. Let's test it now.

Testing

For testing purposes I will deploy Kibana because it doesn't have authentication:

⚡ helm install --name kibana-test \
    --set service.externalPort=5601 \
    stable/kibana

⚡ kubectl get pods --selector=app=kibana
NAME                           READY     STATUS    RESTARTS   AGE
kibana-test-66b88f77bc-vcwl4   1/1       Running   0          1m

There is a small catch now. Not sure if this is a bug or by design. You need to create two ingress resources for both services (Kibana and OAuth2 Proxy), but to be available on the same FQDN. Otherwise you will end up with auth loop and GitHub will block you for some time. I didn't have time to dig further into it.

Kibana path will be on \ and OAuth2 on \oauth2, same domain. This part is what configures Nginx ingress to route requests to auth service first:

nginx.ingress.kubernetes.io/auth-url: "https://$host/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$request_uri"

And you will add above annotations to Kibana ingress only.

I could create ingress resources with Helm directly, but it will be easier for you to understand if I do it manually. Let's create both ingress resources with kubectl:

⚡ cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kibana-test
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/auth-url: "https://\$host/oauth2/auth"
    nginx.ingress.kubernetes.io/auth-signin: "https://\$host/oauth2/start?rd=\$request_uri"
spec:
  rules:
  - host: kibana.test.akomljen.com
    http:
      paths:
      - backend:
          serviceName: kibana-test
          servicePort: 5601
        path: /
  tls:
  - hosts:
    - kibana.test.akomljen.com
    secretName: kibana-tls
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: authproxy-oauth2-proxy
  namespace: ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
spec:
  rules:
  - host: kibana.test.akomljen.com
    http:
      paths:
      - backend:
          serviceName: authproxy-oauth2-proxy
          servicePort: 80
        path: /oauth2
  tls:
  - hosts:
    - kibana.test.akomljen.com
    secretName: kibana-tls
EOF

kibana.test.akomljen.com will be accessible through HTTPS. Open the URL you configured as ingress host for Kibana, and you should be prompted with GitHub login screen:

If you want to add more services to OAuth Proxy, you could edit its ingress and add another host.

Summary

Cloud Native tools again make things easier. People just love when you can quickly come with the things working and OAuth Proxy is no different. Some basic security is definitely easier. Stay tuned for the next one.