In today’s fast-paced software development environment, safeguarding your development and QA environments from public access is essential. At Aptovet, we’ve optimized our security process by integrating OAuth2 Proxy with Azure AD, combined with Kubernetes NGINX Ingress. This setup ensures that only authenticated users — developers, stakeholders, managers, and clients — can access these environments, effectively protecting them from unauthorized public access.

Additionally, this solution offers a significant advantage: it requires zero code changes within the application itself. Instead, all necessary adjustments are made at the gateway level through configuration changes, making it a more efficient and streamlined approach.

Implementation

Prerequisites

  • Assuming we already have configured aks cluster and it has deployed nginx-ingress setup with a public IP load balancer.
  • For SSL certificate management we have installed cert-manager deployed in kubernetes cluster
  • azure subscription with a resource group created
  • Fully qualified domain

Azure Active Directory

  • Register an application
az ad app create --display-name $APP_NAME --sign-in-audience AzureADMYOrg --web-home-page-url $DOMAIN/oauth2/callback --query appId

output:

"00000000-0000-0000-0000-000000000000"

Will need this appId for generate secrets.

  • Create secret to application
az ad app credential reset --id "00000000-0000-0000-0000-000000000000" --display-name $DISPLAY_NAME --end-date "2017-06-31"

output:

{
"appId": "00000000-0000-0000-0000-000000000000",
"password": "9vr8Q~SdsfsdfdsFC21kKM.sdkfjiewfhiow",
"tenant": "00000000-0000-0000-0000-000000000000"
}
  • Generate cookie secret
openssl rand -hex 16

output:

2bd1adbe11e6e8e32f559869d3000000

Add kubernetes secrets

kubectl create secret generic oauth2-proxy-client \
--from-literal "oauth2_proxy_client_id=$APP_ID" \
--from-literal "oauth2_proxy_client_secret=$PASSWORD" \
--from-literal "oauth2_proxy_cookie_secret=$COOKIE_SECRET" \
--namespace $NAMESPACE

output:

secret/oauth2-proxy-client created

Write kubernetes deployment file

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
application: <service_name>-service-oauth2-proxy
name: <service_name>-service-oauth2-proxy-deployment
namespace: <namespace>
spec:
replicas: 1
selector:
matchLabels:
application: <service_name>-service-oauth2-proxy
template:
metadata:
labels:
application: <service_name>-service-oauth2-proxy
spec:
containers:
- args:
- --provider=oidc
- --azure-tenant=<azure_tenant_id> # Azure AD OAuth2 Proxy application Tenant ID
- --pass-access-token=true
- --cookie-name=_proxycookie
- --upstream=<service_public_domain>
- --cookie-csrf-per-request=true
- --cookie-csrf-expire=5m # Avoid unauthorized csrf cookie errors.
- --email-domain=* # Email domains allowed to use the proxy
- --http-address=0.0.0.0:4180
- --oidc-issuer-url=https://login.microsoftonline.com/<azure_tenant_id>/v2.0
- --user-id-claim=oid

name: <service_name>-service-oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0
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-client
key: oauth2_proxy_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-client
key: oauth2_proxy_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-client
key: oauth2_proxy_cookie_secret
ports:
- containerPort: 4180
protocol: TCP
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi

---
apiVersion: v1
kind: Service
metadata:
labels:
application: <service_name>-service-oauth2-proxy
name: <service_name>-service-oauth2-proxy-svc
namespace: <namespace>
spec:
ports:
- name: http
port: 4180
protocol: TCP
targetPort: 4180
selector:
application: <service_name>-service-oauth2-proxy
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "2000m"
nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
name: <service_name>-service-oauth2-proxy-ingress
namespace: <namespace>
spec:
ingressClassName: nginx
tls:
- hosts:
- <domain_name>
secretName: <secret_name>
rules:
- http:
paths:
- path: /oauth2
pathType: Prefix
backend:
service:
name: <service_name>-service-oauth2-proxy-svc
port:
number: 4180
host: <domain_name>

Important

Change the domain_namesecret_nameservice_namenamespaceazure_tenant_id From the manifest before deployment

  • Deploy with kubectl
kubectl apply -f oauth-proxy-deployment.yaml

Adjust targeted service ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: <namespace>-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/use-regex: 'true'
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "360"
nginx.ingress.kubernetes.io/proxy-send-timeout: "360"
nginx.ingress.kubernetes.io/proxy-read-timeout: "360"
nginx.ingress.kubernetes.io/proxy-body-size: "2000m"
nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
nginx.ingress.kubernetes.io/auth-url: https://<domain_name>/oauth2/auth
nginx.ingress.kubernetes.io/auth-signin: "https://<domain_name>/oauth2/start?rd=https://<domain_name>/oauth2/callback"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Auth-Request-Email, X-Auth-Request-User, X-Forwarded-Access-Token"
spec:
ingressClassName: nginx
tls:
- hosts:
- <domain_name>
secretName: <secret_name>
rules:
- host: <domain_name>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: <svc_name>
port:
number: 5000

Important

Change the domain_namesecret_namesvc_namenamespace From the manifest before deployment

  • Deploy with kubectl
kubectl apply -f ingress.yaml

Now try to access the application and it will redirect you to a microsoft login page.