Securing Internal Kubernetes Apps with Google Login Using OAuth2 Proxy
Internal tools are handy, but they can be dangerous when exposed. Here’s how to guard them with Google login and Kubernetes.
Why Secure Internal Apps?
Maybe it’s your admin dashboard. Maybe it’s a tool only your dev team uses. Either way, internal apps often start wide open, behind a firewall or IP block. That works, until it doesn’t. Firewalls shift. Ingress controllers expose things. One misconfigured route and boom, your tool is public.
OAuth2 Proxy + Google Login is a simple, modern way to secure your apps using accounts your team already has.
In this guide, you’ll learn how to:
- Deploy an ingress controller that requires a Google login
- Use
oauth2-proxyto handle authentication - Protect a sample app (
whoami) with TLS and OAuth2 - Use
cert-managerandexternal-dnsto make it all automatic
🧱 What You’ll Need
- A Kubernetes cluster with:
external-dnssee Automate Kubernetes DNS with ExternalDNS and Cloudflare- A verified domain (we’ll use
markcallen.com) - Two subdomains:
login.markcallen.com→ hostsoauth2-proxyexample.markcallen.com→ your protected app
- A Google account and OAuth credentials (we’ll create those next)
🛠️ Step 1: Create Google OAuth Credentials
- Go to Google Cloud Console
- Create a new project or use an existing one
- Open OAuth consent screen → Choose "External" → Fill out app info
- Add
markcallen.comas an Authorized Domain - Go to Credentials → Create Credentials → OAuth Client ID
- Application type: Web App
- Javascript Origins: https://login.markcallen.com
- Redirect URI: https://login.markcallen.com/oauth2/callback
- Press Create
- Copy your Client ID and Client Secret
🔐 Step 2: Deploy oauth2-proxy
This service handles Google login and issues a secure cookie to authenticated users.
Create the oauth2 namespace
kubectl create ns oauth2Create a certificate for login.markcallen.com
# login-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: oauth2-proxy-cert
namespace: oauth2
spec:
secretName: login-markcallen-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- login.markcallen.comWait for the cert to be issued
kubectl describe order -n oauth2
Normal Complete 29s cert-manager-orders Order completed successfullySetup helm
helm repo add oauth2-proxy https://oauth2-proxy.github.io/manifests
helm repo updateCreate a cookie secret
openssl rand -base64 32 | tr -d '\n' | tr '+/' '-_'Create a values.yaml using the client id and secret and cookie secret created above. This will allow for multiple subdomains to use oauth2 as the authentication provider.
config:
clientID: "your-google-client-id"
clientSecret: "your-google-client-secret"
cookieSecret: "your-generated-cookie-secret"
redirectUrl: "https://login.markcallen.com/oauth2/callback"
configFile: |
email_domains = [ "markcallen.com" ]
upstreams = [ "file:///dev/null" ]
cookie_secure = "true"
cookie_domains = [ ".markcallen.com" ]
whitelist_domains = [ ".markcallen.com" ]
provider = "google"
scope = "openid email profile"
ingress:
enabled: true
className: nginx
hosts:
- login.markcallen.com
annotations:
cert-manager.io/cluster-issuer: letsencrypt-dns
external-dns.alpha.kubernetes.io/hostname: login.markcallen.com
tls:
- hosts:
- login.markcallen.com
secretName: login-markcallen-tlsInstall oauth2-proxy using helm
helm install oauth2-proxy oauth2-proxy/oauth2-proxy -f values.yaml \
--namespace oauth2Check that the DNS is set up correctly
dig +short login.markcallen.comand that you can get to the login URL over https without a certificate error
curl https://login.markcallen.com📦 Step 4: Deploy Your Internal App (Example: whoami)
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
spec:
replicas: 1
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app: whoami
ports:
- port: 80
targetPort: 80🔐 Step 5: Protect Your App with OAuth2 Proxy
Create a certificate for whoami.markcallen.com
# whoami-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-cert
namespace: default
spec:
secretName: whoami-markcallen-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- whoami.markcallen.comNow create an ingress for it.
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-dns
nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.oauth2.svc.cluster.local/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://login.markcallen.com/oauth2/start?rd=https://$host$escaped_request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Forwarded-User"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
external-dns.alpha.kubernetes.io/hostname: whoami.markcallen.com
spec:
ingressClassName: nginx
rules:
- host: whoami.markcallen.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: whoami
port:
number: 80
tls:
- hosts:
- whoami.markcallen.com
secretName: whoami-markcallen-tlsNote that the nginx.ingress.kubernetes.io/auth-url is the internal DNS for the oauth2-proxy service using http.
✅ The Result
- Users visit
whoami.markcallen.com - If not logged in, they’re redirected to
login.markcallen.com→ Google - On success, they get a secure cookie and access to your internal app
It’s simple, stateless, and backed by Google’s login infrastructure.
I've created the following Github repo: https://github.com/markcallen/internal-login-google-example
🧠 Final Thoughts
This method works great for dashboards, job runners, internal UIs, or staging environments. You get:
- Zero user management
- Passwordless auth with Google
- A secure, reusable OAuth2 proxy
Once you’ve done it once, it’s copy-paste for every internal app that needs a lock on the door.