Sometimes, we may want to add authentication features to our service running on Kubernetes, but the application itself does not include authentication capabilities.
Fortunately, there is a third-party application that we can use to address this issue, called oauth2-proxy.
Oauth2-proxy is a third-party application that we can use as centralized authentication for other services in Kubernetes.
Oauth2-proxy is a reverse proxy and static file server that provides authentication using Providers (Google, Keycloak, GitHub and others) to validate accounts by email, domain or group.
Requirements
- Kubernetes Cluster v1.18+
- Public Domain for oauth2-proxy ingress
- Nginx Ingress Controller
- Helm Installed
- Cert Manager for TLS Ingress
Diagram
Preparation
The public domains I've set up as example are oauth.rjhaikal.my.id for oauth2-proxy and code.rjhaikal.my.id for the app.
You can also see how I deploy the applications used in this example on the following blog.
Setup Azure AD
1. Register an application
Update the redirect URI to your suit your domain and protocol.
https://<yourdomain>/oauth2/callback
Once your application is created take a note of the Application (client) ID on the overview page. We will need this later.
Within our application registration we will also need to create a client secret to be used to identify our application. We could also use a certificate for higher assurance however for this example a secret will suffice.
Save this secret, and denote the following information under the 'Overview' Tab for later
- Secret Key
- App (Client) ID
- Directory (Tenant) ID
Setup Redis
1. Create namespace
kubectl create ns rj-oauth2
2. Add Redis repo
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
3. edit values
vim values.yml
---
global:
storageClass: <your storageclass>
redis:
password: "YourSecurePassword!"
helm install redis bitnami/redis --values=values.yml -n rj-oauth2
export REDIS_PASSWORD=$(kubectl get secret --namespace rj-oauth2 redis -o jsonpath="{.data.redis-password}" | base64 --decode)
Oauth2 Proxy Installation
1. Collate and Deploy Secrets
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo
SGEOwUnlydJJGWG3YYcrxLUscUdv00Cg4Jb_ewcvAx4=
export COOKIE_SECRET="SGEOwUnlydJJGWG3YYcrxLUscUdv00Cg4Jb_ewcvAx4="
export AZURE_TENANT_ID="yyyyyyyyyyyyyyy"
export AZURE_CLIENT_ID="xxxxxxxxxxxxxxx"
export AZURE_CLIENT_SECRET="zzzzzzzzzzzzzz"
kubectl create secret generic oauth2-proxy-creds -n rj-oauth2 \
--from-literal=cookie-secret="$COOKIE_SECRET" \
--from-literal=azure-tenant="$AZURE_TENANT_ID" \
--from-literal=client-id="$AZURE_CLIENT_ID" \
--from-literal=client-secret="$AZURE_CLIENT_SECRET"
2. Install Oauth2 Proxy
vim oauth2-proxy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
application: oauth2-proxy
name: oauth2-proxy
namespace: rj-oauth2
spec:
replicas: 1
selector:
matchLabels:
application: oauth2-proxy
template:
metadata:
labels:
application: oauth2-proxy
spec:
containers:
- args:
- --provider=azure
- --azure-tenant=<tenant-id> # Microsoft Entra ID OAuth2 Proxy application Tenant ID
- --pass-access-token=true
- --cookie-name=_proxycookie # this can be any name of your choice which you would like OAuth2 Proxy to use for the session cookie
- --email-domain=*
- --upstream=file:///dev/null
- --http-address=0.0.0.0:4180
- --oidc-issuer-url=https://login.microsoftonline.com/<tenant-id>/v2.0
- --whitelist-domain=.rjhaikal.my.id
- --cookie-domain=.rjhaikal.my.id
- --cookie-secure=false
- --cookie-csrf-per-request=true
- --cookie-csrf-expire=5m
- --redirect-url=https://oauth.rjhaikal.my.id/oauth2/callback
- --session-store-type=redis
- --reverse-proxy=true
- --redis-connection-url=redis://redis-master.rj-oauth2.svc.cluster.local:6379
- --redis-password=<redis-password>
name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.5.1
imagePullPolicy: Always
env:
- name: OAUTH2_PROXY_CLIENT_ID # keep this name - it's required to be defined like this by OAuth2 Proxy
valueFrom:
secretKeyRef:
name: oauth2-proxy-creds
key: client-id
- name: OAUTH2_PROXY_CLIENT_SECRET # keep this name - it's required to be defined like this by OAuth2 Proxy
valueFrom:
secretKeyRef:
name: oauth2-proxy-creds
key: client-secret
- name: OAUTH2_PROXY_COOKIE_SECRET # keep this name - it's required to be defined like this by OAuth2 Proxy
valueFrom:
secretKeyRef:
name: oauth2-proxy-creds
key: cookie-secret
ports:
- containerPort: 4180
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
labels:
application: oauth2-proxy
name: oauth2-proxy-svc
namespace: rj-oauth2
spec:
ports:
- name: http
port: 4180
protocol: TCP
targetPort: 4180
selector:
application: oauth2-proxy
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/proxy-body-size: "2000m"
nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
name: oauth2-proxy-ingress
namespace: rj-oauth2
spec:
ingressClassName: nginx
tls:
- hosts:
- 'oauth.rjhaikal.my.id'
secretName: rjhaikal.my.id
rules:
- host: oauth.rjhaikal.my.id
http:
paths:
- path: /oauth2
pathType: Prefix
backend:
service:
name: oauth2-proxy-svc
port:
number: 4180
Reconfigure Code Server (Application)
Changing the authentication to "none" because I will be using External Authentication.
sudo vim /home/code-user/.config/code-server/config.yaml
---
bind-addr: 127.0.0.1:8080
auth: none # change to this
password: [REDACTED]
cert: false
sudo systemctl restart [email protected]
Add OAuth2 Proxy annotations for Code Server Ingress
This service will depend on oauth2-proxy. If a user accesses the ingress of this service, the service will redirect the user's request to oauth2-proxy to check whether the user has been authenticated or not.
1. Add proper OAuth2 Proxy annotations to Code Server Ingress
cat<<EOF | kubectl replace -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/auth-url: "https://oauth.rjhaikal.my.id/oauth2/auth" # <- Add this
nginx.ingress.kubernetes.io/auth-signin: "https://oauth.rjhaikal.my.id/oauth2/start" # <- Add this
name: rj-codeserver-ingress
spec:
ingressClassName: nginx
rules:
- host: code.rjhaikal.my.id
http:
paths:
- backend:
service:
name: rj-codeserver-service
port:
number: 8080
path: /
pathType: Prefix
tls:
- hosts:
- 'code.rjhaikal.my.id'
secretName: code-rjhaikal-secret
EOF
Try accessing code.rjhaikal.my.id and ensure that the request is redirected to oauth.rjhaikal.my.id, then further redirected to Azure AD. After login, it should be redirected back to code.rjhaikal.my.id.