aRustyDev / aRustyDev/mdbook-htmx
docs(examples): Kubernetes Ingress Authentication
- Dominant language
- Rust
- Stars
- 0
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
# Kubernetes Ingress Authentication
Add authentication to mdbook-htmx using Kubernetes Ingress annotations and OAuth2 Proxy.
## Overview
Options for Ingress authentication:
1. **Basic Auth**: Simple username/password
2. **OAuth2 Proxy**: SSO with GitHub, Google, etc.
3. **External Auth Service**: Custom authentication service
## Option 1: Basic Authentication
### Create Secret
```bash
# Create htpasswd file
htpasswd -c auth admin
# Create Kubernetes secret
kubectl create secret generic basic-auth \
--from-file=auth \
-n docs
```
### Ingress with Basic Auth
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs
namespace: docs
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
# Basic auth annotations
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Documentation - Login Required"
spec:
tls:
- hosts:
- docs.example.com
secretName: docs-tls
rules:
- host: docs.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: docs
port:
number: 80
```
### Selective Protection
Protect only specific paths:
```yaml
# Public docs
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs-public
namespace: docs
spec:
rules:
- host: docs.example.com
http:
paths:
- path: /public
pathType: Prefix
backend:
service:
name: docs
port:
number: 80
---
# Protected internal docs
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs-internal
namespace: docs
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
spec:
rules:
- host: docs.example.com
http:
paths:
- path: /internal
pathType: Prefix
backend:
service:
name: docs
port:
number: 80
```
## Option 2: OAuth2 Proxy
### Deploy OAuth2 Proxy
```yaml
# oauth2-proxy-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: oauth2-proxy
namespace: docs
spec:
replicas: 2
selector:
matchLabels:
app: oauth2-proxy
template:
metadata:
labels:
app: oauth2-proxy
spec:
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.5.1
args:
- --provider=github
- --email-domain=*
- --upstream=file:///dev/null
- --http-address=0.0.0.0:4180
- --cookie-secure=true
- --cookie-samesite=lax
- --cookie-refresh=1h
- --cookie-expire=4h
- --set-xauthrequest=true
- --pass-access-token=true
- --pass-user-headers=true
env:
- name: OAUTH2_PROXY_CLIENT_ID
valueFrom:
secretKeyRef:
name: oauth2-proxy-secrets
key: client-id
- name: OAUTH2_PROXY_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: oauth2-proxy-secrets
key: client-secret
- name: OAUTH2_PROXY_COOKIE_SECRET
valueFrom:
secretKeyRef:
name: oauth2-proxy-secrets
key: cookie-secret
ports:
- containerPort: 4180
name: http
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
livenessProbe:
httpGet:
path: /ping
port: 4180
initialDelaySeconds: 3
readinessProbe:
httpGet:
path: /ping
port: 4180
---
apiVersion: v1
kind: Service
metadata:
name: oauth2-proxy
namespace: docs
spec:
ports:
- port: 4180
targetPort: 4180
selector:
app: oauth2-proxy
```
### OAuth2 Proxy Secrets
```bash
# Generate cookie secret
cookie_secret=$(openssl rand -base64 32 | head -c 32)
# Create secret
kubectl create secret generic oauth2-proxy-secrets \
--from-literal=client-id=YOUR_GITHUB_CLIENT_ID \
--from-literal=client-secret=YOUR_GITHUB_CLIENT_SECRET \
--from-literal=cookie-secret=$cookie_secret \
-n docs
```
### Ingress with OAuth2 Proxy
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs
namespace: docs
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
# OAuth2 Proxy annotations
nginx.ingress.kubernetes.io/auth-url: "https://$host/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-Request-User,X-Auth-Request-Email,X-Auth-Request-Groups"
spec:
tls:
- hosts:
- docs.example.com
secretName: docs-tls
rules:
- host: docs.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: docs
port:
number: 80
---
# OAuth2 Proxy endpoints
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oauth2-proxy
namespace: docs
spec:
tls:
- hosts:
- docs.example.com
secretName: docs-tls
rules:
- host: docs.example.com
http:
paths:
- path: /oauth2
pathType: Prefix
backend:
service:
name: oauth2-proxy
port:
number: 4180
```
### GitHub OAuth App Setup
1. Go to GitHub → Settings → Developer settings → OAuth Apps
2. Create new OAuth App:
- Application name: `Docs Auth`
- Homepage URL: `https://docs.example.com`
- Authorization callback URL: `https://docs.example.com/oauth2/callback`
3. Copy Client ID and Client Secret
### Restrict to Organization
```yaml
args:
- --provider=github
- --github-org=your-org # Only allow org members
- --github-team=your-team # Optional: restrict to team
```
### User Info in Templates
Pass authenticated user info to mdbook-htmx:
```yaml
# NGINX config snippet
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
```
## Option 3: External Auth Service
### Custom Auth Service
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs
namespace: docs
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://auth-service.auth.svc.cluster.local:8080/verify"
nginx.ingress.kubernetes.io/auth-method: GET
nginx.ingress.kubernetes.io/auth-response-headers: "X-User-Id,X-User-Roles"
nginx.ingress.kubernetes.io/auth-cache-key: "$cookie_session"
nginx.ingress.kubernetes.io/auth-cache-duration: "200 202 5m, 401 1m"
spec:
# ... rest of ingress
```
### Auth Service Implementation
```python
# auth_service.py
from flask import Flask, request, Response
app = Flask(__name__)
@app.route('/verify')
def verify():
# Get session cookie
session_token = request.cookies.get('session')
if not session_token:
return Response('Unauthorized', status=401)
# Validate session (lookup in database, verify JWT, etc.)
user = validate_session(session_token)
if not user:
return Response('Unauthorized', status=401)
# Return user info in headers
return Response('OK', headers={
'X-User-Id': user['id'],
'X-User-Roles': ','.join(user['roles']),
'X-User-Email': user['email'],
})
```
## Role-Based Access Control
### Pass roles to documentation
```yaml
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $roles $upstream_http_x_user_roles;
proxy_set_header X-User-Roles $roles;
```
### Filter content by role (in mdbook-htmx)
```toml
# book.toml
[output.htmx.authz]
header = "X-User-Roles"
[[output.htmx.authz.rules]]
paths = ["/internal/*"]
required_roles = ["internal", "admin"]
[[output.htmx.authz.rules]]
paths = ["/admin/*"]
required_roles = ["admin"]
```
## IP Allowlisting
For internal docs, combine with IP restrictions:
```yaml
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
```
## Rate Limiting
Protect against brute force:
```yaml
annotations:
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-connections: "5"
```
## Monitoring Auth
### Log authentication events
```yaml
nginx.ingress.kubernetes.io/configuration-snippet: |
access_log /var/log/nginx/auth.log auth_log if=$auth_log_enabled;
```
### Metrics
```yaml
# ServiceMonitor for OAuth2 Proxy
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: oauth2-proxy
spec:
selector:
matchLabels:
app: oauth2-proxy
endpoints:
- port: http
path: /metrics
```
## Troubleshooting
### Debug auth headers
```bash
# Check what headers reach the pod
kubectl exec -it deploy/docs -- curl -v http://localhost/debug-headers
```
### OAuth2 Proxy logs
```bash
kubectl logs -f deploy/oauth2-proxy -n docs
```
### Common issues
1. **Cookie not set**: Check `cookie-secure` matches your TLS setup
2. **Redirect loop**: Verify callback URL matches ingress host
3. **401 on static assets**: Exclude static paths from auth
## Next Steps
- Deploy with [Helm chart](./helm-chart.md)
- Set up [GitHub Pages](./github-pages.md) for public docs
- Use [Docker Compose](./docker-compose.md) for local testing
Contributor guide
Assessment
This issue has not been assessed yet.