aRustyDev / aRustyDev/mdbook-htmx

docs(examples): Kubernetes Ingress Authentication

Open
#44 0 comments 0 reactions 1 assignee Claimed by @aRustyDev View on GitHub
documentation
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.