Web UI Authentication
The Headwind Web UI supports four authentication modes to meet different security requirements and deployment scenarios. All authentication modes include comprehensive audit logging to track who performed which actions.
Authentication Modes
Configure authentication using the HEADWIND_UI_AUTH_MODE environment variable in your deployment.
Mode 1: None (Default)
No authentication required. All actions are logged as "web-ui-user".
Use Case: Development environments, trusted internal networks, or when authentication is not required.
Configuration:
env:
- name: HEADWIND_UI_AUTH_MODE
value: "none"
Security Note: This mode provides no access control. Only use in trusted environments.
Mode 2: Simple Header Authentication
Reads username from HTTP header. The Web UI trusts the username provided in the X-User header.
Use Case: Behind an authenticating reverse proxy (e.g., nginx with auth_request, Apache with mod_auth).
Configuration:
env:
- name: HEADWIND_UI_AUTH_MODE
value: "simple"
Example Usage:
# With curl
curl -H "X-User: alice" http://headwind-ui:8082/
# Behind nginx (configured with auth_request)
# nginx sets X-User header after successful authentication
Nginx Example:
location / {
auth_request /auth;
proxy_pass http://headwind-ui:8082;
proxy_set_header X-User $remote_user;
}
Security Note: The proxy/reverse proxy must validate authentication before setting the X-User header. Headwind trusts whatever username is provided.
Mode 3: Kubernetes Token Authentication
Validates bearer tokens using Kubernetes TokenReview API and extracts the authenticated username.
Use Case:
- Service account authentication
- kubectl authentication
- Kubernetes-native authentication workflows
- Integration with Kubernetes RBAC
Configuration:
env:
- name: HEADWIND_UI_AUTH_MODE
value: "token"
Requirements:
- RBAC permission for
authentication.k8s.io/tokenreviews(already included indeploy/k8s/rbac.yaml)
Example Usage with Service Account:
- Create a service account:
kubectl create serviceaccount headwind-ui-user -n default
- Create a token:
TOKEN=$(kubectl create token headwind-ui-user -n default)
- Access the Web UI:
curl -H "Authorization: Bearer $TOKEN" http://headwind-ui:8082/
Example Usage with kubectl:
# Get your current user token
TOKEN=$(kubectl config view --raw -o jsonpath='{.users[0].user.token}')
# Access Web UI
curl -H "Authorization: Bearer $TOKEN" http://headwind-ui:8082/
How It Works:
- Client sends bearer token in
Authorizationheader - Headwind calls Kubernetes TokenReview API to validate token
- Kubernetes responds with authentication status and username
- Username extracted from response (e.g.,
system:serviceaccount:default:headwind-ui-user) - Actions audited with full username
Security Note: Token validation is performed on every request. Expired or invalid tokens are rejected with 401 Unauthorized.
Mode 4: Proxy/Ingress Authentication
Reads username from a configurable HTTP header set by an ingress controller or authentication proxy.
Use Case:
- Kubernetes Ingress with external authentication
- oauth2-proxy
- Authelia
- Keycloak Gatekeeper
- Any ingress controller with authentication
Configuration:
env:
- name: HEADWIND_UI_AUTH_MODE
value: "proxy"
- name: HEADWIND_UI_PROXY_HEADER # Optional, defaults to X-Forwarded-User
value: "X-Auth-Request-User"
Common Header Names:
X-Forwarded-User(default, used by many proxies)X-Auth-Request-User(oauth2-proxy)X-Forwarded-Email(some authentication proxies)Remote-User(traditional proxy auth)
Example with oauth2-proxy:
# oauth2-proxy configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: oauth2-proxy-config
data:
oauth2-proxy.cfg: |
email_domains = ["*"]
upstreams = ["http://headwind-ui:8082"]
pass_user_headers = true
---
# Headwind deployment
env:
- name: HEADWIND_UI_AUTH_MODE
value: "proxy"
- name: HEADWIND_UI_PROXY_HEADER
value: "X-Forwarded-Email"
Example with Ingress Annotation (nginx-ingress with oauth2-proxy):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: headwind-ui
annotations:
nginx.ingress.kubernetes.io/auth-url: "https://oauth2-proxy.example.com/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://oauth2-proxy.example.com/oauth2/start"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-Request-User,X-Auth-Request-Email"
spec:
rules:
- host: headwind.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: headwind-ui
port:
number: 8082
Security Note: The proxy/ingress must validate authentication before setting the username header. Headwind trusts the header value.
Audit Logging
All authentication modes produce detailed audit logs for approval and rejection actions.
Audit Log Format
{
"timestamp": "2025-11-08T23:00:00Z",
"username": "alice",
"action": "approve",
"resource_type": "Deployment",
"namespace": "default",
"name": "nginx-update-1-26-0",
"result": "success",
"reason": null
}
Fields:
timestamp: RFC3339 timestampusername: Authenticated username (varies by auth mode)action:approveorrejectresource_type: Deployment, StatefulSet, DaemonSet, or HelmReleasenamespace: Kubernetes namespacename: UpdateRequest nameresult:successorerrorreason: Rejection reason (only for rejections)
Viewing Audit Logs
Audit logs use the dedicated log target headwind::audit:
# Filter audit logs only
kubectl logs -n headwind-system deployment/headwind | grep "headwind::audit"
# Follow audit logs in real-time
kubectl logs -n headwind-system deployment/headwind -f | grep "headwind::audit"
# Export audit logs to file
kubectl logs -n headwind-system deployment/headwind | grep "headwind::audit" > audit.log
Username by Authentication Mode
| Mode | Example Username |
|---|---|
| None | web-ui-user |
| Simple | alice (from X-User header) |
| Token | system:serviceaccount:default:my-sa |
| Proxy | alice@example.com (from configured header) |
RBAC Requirements
Token Authentication Mode
Token authentication requires the authentication.k8s.io/tokenreviews permission:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: headwind
rules:
# Required for token authentication
- apiGroups: ["authentication.k8s.io"]
resources: ["tokenreviews"]
verbs: ["create"]
This permission is already included in deploy/k8s/rbac.yaml.
Other Modes
No additional RBAC permissions required for None, Simple, or Proxy modes.
Security Best Practices
-
Use Token or Proxy mode in production - Avoid "none" and "simple" modes unless behind a trusted authentication layer
-
Enable audit logging - Always monitor audit logs for suspicious activity
-
Use HTTPS/TLS - Always expose the Web UI over HTTPS in production via Ingress
-
Limit network access - Use NetworkPolicies to restrict access to the UI service
-
Rotate tokens regularly - For token mode, rotate service account tokens periodically
-
Validate proxy configuration - Ensure your authentication proxy cannot be bypassed
Example Deployments
Development (No Auth)
env:
- name: HEADWIND_UI_AUTH_MODE
value: "none"
Production with oauth2-proxy
env:
- name: HEADWIND_UI_AUTH_MODE
value: "proxy"
- name: HEADWIND_UI_PROXY_HEADER
value: "X-Forwarded-Email"
Production with Service Accounts
env:
- name: HEADWIND_UI_AUTH_MODE
value: "token"
Troubleshooting
"Missing Authorization header" (Token Mode)
Problem: 401 error with message about missing header
Solution: Include bearer token in Authorization header:
curl -H "Authorization: Bearer <token>" http://headwind-ui:8082/
"Token validation failed" (Token Mode)
Problem: Valid-looking token rejected
Possible Causes:
- Token expired (service account tokens can expire)
- Service account deleted
- RBAC permission missing for TokenReview
- Network connectivity to Kubernetes API
Solution:
# Check RBAC permissions
kubectl auth can-i create tokenreviews.authentication.k8s.io --as=system:serviceaccount:headwind-system:headwind
# Create fresh token
kubectl create token <service-account> -n <namespace>
"Missing X-Forwarded-User header" (Proxy Mode)
Problem: 401 error about missing header
Possible Causes:
- Proxy not configured to pass username header
- Wrong header name configured
Solution: Verify proxy configuration and ensure HEADWIND_UI_PROXY_HEADER matches your proxy's header name
Audit logs show "web-ui-user" instead of username
Problem: All audit entries show "web-ui-user"
Cause: Authentication mode is set to "none"
Solution: Configure proper authentication mode (simple, token, or proxy)
Next Steps
- Observability Dashboard - Monitor metrics
- Configuration Management - Customize Web UI settings