Building a NGINX-Based Identity-Aware Proxy for Securing Multi-Service Kubernetes Deployments
A reverse proxy that enforces authentication BEFORE forwarding requests to backend services.
Summary of this article - Zero-trust perimeter with centralized authentication gateway
Internet → Cloudflare Tunnel → NGINX Ingress → Centralized Auth OR Apps
↓
Auth0Components:
Cloudflare Tunnel: Secure ingress, forwards ALL traffic to NGINX only. When we set up our tunnel, we configure it to forward traffic to a single destination: the NGINX Ingress Controller service.
NGINX Ingress Controller: Routes by path, queries auth before forwarding
Centralized Auth Service: Handles OAuth2 flow + validates sessions for NGINX
Auth0: Identity provider (Google/LinkedIn/Facebook login)
Protected Apps: Zero auth code, read
X-User-Emailheader, ClusterIP only
Security Model:
Network isolation: Apps use ClusterIP (cluster-internal only)
Single entry point: Only NGINX receives external traffic
Trust boundary: Apps trust headers because network topology guarantees they originate from NGINX
Optional: NetworkPolicies enforce pod-level access control
Key Benefit: Authentication logic centralized in one service. Apps receive pre-authenticated requests with user context. No auth code duplication across microservices.
Our different apps (/restaurant or /marketing) do NOT check authentication at all. No decorators. No middleware. No duplicated logic.
NGINX Ingress + OAuth/IAP proxy does it before traffic reaches our services. If a request reaches our app → it is already authenticated.
The Situation
We’re running multiple independent applications on a Kubernetes cluster:
/restaurant- ordering system/marketing- campaign manager/company- internal tools
Current problem: Each app handles its own authentication. This means:
Duplicated auth code across every service
Users log in separately for each app
Hard to maintain - update auth logic in 5 places
Security risk - one weak implementation compromises everything
Problem: No centralized access control. Every new app requires reimplementing authentication.
What we want: Single sign-on. User authenticates once, accesses everything. Zero auth code in individual apps.
The Goal
Centralized authentication gateway that:
Centralize authentication for all apps using a single OAuth proxy layer
Ensure all public traffic is authenticated before reaching any app
Keep downstream apps simple (no auth logic, no JWT parsing)
Maintain support for multiple social logins via Auth0
Leverage NGINX Ingress Controller for authentication enforcement
Key requirement: Apps should be completely stateless regarding authentication. They just read “who is this user” and do their job.
Core Concepts
Identity-Aware Proxy (IAP)
Reverse proxy that enforces authentication before forwarding requests. App never sees unauthenticated traffic.
Reverse Proxy
Server between clients and backends. Routes requests, can inspect/modify before forwarding. NGINX Ingress Controller in our case.
NGINX Ingress Controller (Kubernetes)
Acts as a traffic gateway. Intercepts requests using auth_request. Forwards authenticated traffic to apps. Returns 401 and redirects to login for unauthenticated users. Single Entry Point: All external traffic flows through Ingress Controller, then routes internally
OAuth2 Flow
User → clicks “Login with Google” → Google login → redirect back with code → exchange for user info → create session. Auth0 handles this complexity.
Custom OAuth Proxy
Handles login (/auth/login) and OAuth callback (/auth/callback). Maintains session via secure HttpOnly cookies. Exposes /auth/verify endpoint called by NGINX to validate sessions. Injects user information into headers (X-Auth-User, X-Auth-Email). This is our existing app.
/auth/login→ redirect to Auth0/auth/callback→ exchange code, set secure cookie, redirect to original path/auth/verify→ check session cookie, validate JWT, return200+ user headers or401
Session Management
After login: generate session ID, store user info server-side, set cookie in browser. Subsequent requests include cookie = system knows user identity.
Network Isolation
Apps use ClusterIP (internal Kubernetes only). External requests cannot reach apps directly - only through NGINX.
Backend services are not exposed to the internet - only accessible within Kubernetes cluster. Kubernetes Service Types:
ClusterIP: Internal only (DNS works inside cluster, not from internet)
LoadBalancer: Exposed to internet (DON’T use for apps)
NodePort: Exposed on node IPs (DON’T use for apps)
Trust Boundary
Apps trust identity headers (X-User-Email) because network topology guarantees they originate from NGINX only.
Security Model - Why header injection is secure?
Layer 1 - Single Entry Point
Cloudflare Tunnel only points to NGINX service. No other ingress paths.
Layer 2 - Network Isolation
Apps use ClusterIP. DNS like restaurant-backend.default.svc.cluster.local only resolves inside Kubernetes. Internet can’t reach it.
Layer 3 - Header Control
NGINX strips external X-User-* headers, only adds them after auth validation. Apps never see untrusted headers.
Result: Requests can ONLY arrive via NGINX. NGINX only adds headers after verifying auth. Apps trust headers.
Optional: NetworkPolicy to enforce only NGINX pods can reach app pods.
Request Flow
First-Time User - Unauthenticated access to /restaurant:
Browser →
yourdomain.com/restaurantCloudflare Tunnel → NGINX
NGINX queries:
http://centralized-auth:8000/auth(internal)Centralized Auth checks session cookie → NOT FOUND → returns 401
NGINX redirects browser to:
/login?redirect=/restaurantUser clicks “Login with Google”
OAuth2 flow: Centralized Auth ↔ Auth0 ↔ Google
Auth0 callback: exchange code for user info
Centralized Auth creates session, sets cookie, redirects to
/restaurantBrowser requests
/restaurantagain (with cookie)NGINX queries auth → session valid → returns
X-User-Email: user@google.com200 OK+X-Auth-*headers → forward to app401→ redirect to/auth/login
NGINX forwards to restaurant with header
Restaurant app reads email, returns content - Apps only receive authenticated requests.
Subsequent access to /marketing:
Session cookie already exists
NGINX validates → forwards with identity header
No login required
Conclusion
Pattern: IAP with reverse proxy + centralized auth service + network isolation
Flow: NGINX intercepts all traffic → queries auth service → unauthenticated = redirect to login → authenticated = inject identity headers → forward to app
Security: Single entry point (Cloudflare→NGINX), apps isolated (ClusterIP), headers trusted (network topology enforces origin)
Result: Enterprise-grade SSO for Kubernetes with zero auth code duplication.
How to make our cluster even safer?
Role-based routing
Per-path policies
Multi-tenant isolation
mTLS inside the cluste





