Deployment Guide
This guide covers authentication configuration, security hardening, and production deployment best practices for the Helpdesk system.
Authentication Modes
Local Authentication (AUTH_MODE=local)
Local authentication uses HMAC-signed JWTs stored in HTTP-only cookies for simple deployments.
Configuration:
export AUTH_MODE=local
export AUTH_LOCAL_SECRET=your-secret-key-here # Use strong random key in production
export ADMIN_PASSWORD=secure-admin-password # Optional: creates initial admin user
Cookie Details:
- Cookie name:
hd_auth - Security flags:
HttpOnly,SameSite=Lax,Secure(in production) - Automatic admin user creation when
ADMIN_PASSWORDis set orENV=dev
Use Cases:
- Development environments
- Small deployments without existing identity providers
- Quick proof-of-concepts
OIDC Authentication (AUTH_MODE=oidc)
OIDC authentication integrates with external identity providers using JWT tokens validated via JWKS.
Configuration:
export AUTH_MODE=oidc
export OIDC_ISSUER=https://auth.example.com/realms/helpdesk
export OIDC_JWKS_URL=https://auth.example.com/realms/helpdesk/protocol/openid-connect/certs
export OIDC_GROUP_CLAIM=groups # JWT claim containing user roles
Supported Identity Providers:
- Keycloak
- Authentik
- Auth0
- Azure AD
- Any OIDC-compliant provider
JWT Claims:
groups: Array of role names (admin,manager,agent)sub: User identifieremail: User email addressname: Display name
Security Features:
- JWKS key rotation with automatic refresh
- Issuer validation
- Algorithm restrictions (RS256, ES256)
- Clock skew tolerance
- Audience validation (when configured)
Rate Limiting
The system uses Redis-backed rate limiting for consistency across multiple replicas.
Configuration
export RATE_LIMIT_LOGIN=10 # Login attempts per minute per IP
export RATE_LIMIT_TICKETS=20 # Ticket operations per minute per user
export RATE_LIMIT_ATTACHMENTS=30 # Attachment operations per minute per user
Protected Endpoints
POST /login,POST /logout- Login rate limitingPOST /tickets- Ticket creationPOST /tickets/:id/attachments/presign- Attachment upload initiationPOST /tickets/:id/attachments- Direct attachment uploadGET /tickets/:id/attachments/:attID- Attachment downloads (optional)
Monitoring
Rate limit rejections are tracked via Prometheus metric:
rate_limit_rejections_total{route="login|tickets_create|attachments_presign"}
Dependencies
- Redis required for distributed rate limiting
- Falls back to in-memory (single replica only) if Redis unavailable
- Rate limits are per-user for authenticated endpoints, per-IP for login
CORS Configuration
Control cross-origin requests to prevent unauthorized access.
Configuration
export ALLOWED_ORIGINS=https://helpdesk.example.com,https://portal.example.com
Security Considerations
- Never use wildcards (
*) in production - Avoid public domains to prevent data leaks
- Use HTTPS origins for secure communication
- Headers restricted to:
Authorization,Content-Type,X-Requested-With Vary: Originheader always set for proper caching
Effects of Misconfiguration
❌ Dangerous:
ALLOWED_ORIGINS=*
ALLOWED_ORIGINS=https://evil.com
✅ Safe:
ALLOWED_ORIGINS=https://helpdesk.internal.corp,https://portal.internal.corp
Production Deployment Hardening
Environment Security
- Use Kubernetes Secrets for sensitive configuration:
secrets:
enabled: true
data:
DATABASE_URL: "postgres://user:pass@db:5432/helpdesk?sslmode=require"
AUTH_LOCAL_SECRET: "32-byte-random-key"
ADMIN_PASSWORD: "secure-password"
MINIO_ACCESS_KEY: "access-key"
MINIO_SECRET_KEY: "secret-key"
SMTP_PASS: "smtp-password"
- Enable SSL/TLS everywhere:
# Database
DATABASE_URL=postgres://user:pass@db:5432/helpdesk?sslmode=require
# MinIO/S3
MINIO_USE_SSL=true
# SMTP
SMTP_PORT=587 # Use STARTTLS
- Set appropriate timeouts:
DB_TIMEOUT_MS=5000 # Database operations
REDIS_TIMEOUT_MS=2000 # Redis operations
OBJECTSTORE_TIMEOUT_MS=10000 # S3/MinIO operations
Network Security
- Ingress with TLS:
ingress:
enabled: true
className: nginx
hosts:
- host: helpdesk.example.com
tls:
- secretName: helpdesk-tls
hosts:
- helpdesk.example.com
- Network policies (if supported):
# Allow only necessary traffic between pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: helpdesk-network-policy
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: helpdesk
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: nginx-ingress
Resource Limits
Set appropriate resource limits to prevent resource exhaustion:
resources:
limits:
memory: "1Gi"
cpu: "1000m"
requests:
memory: "512Mi"
cpu: "500m"
workerResources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
Health Checks
The system provides multiple health check endpoints:
API Service:
GET /healthz- Basic liveness checkGET /readyz- Readiness check (includes DB and object store)
Worker Service:
GET /health- Basic liveness check (port 8081)GET /ready- Readiness check (includes DB and Redis)
Kubernetes Configuration:
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: http
initialDelaySeconds: 5
periodSeconds: 5
Monitoring and Observability
-
Prometheus Metrics:
- Request counters by endpoint
- Authentication failure rates
- Rate limit rejection counts
- JWKS refresh metrics
-
Structured Logging:
- JSON format in production (
ENV=prod) - Request tracing with correlation IDs
- Security event logging
- JSON format in production (
-
Grafana Dashboards: See
docs/grafana.mdfor dashboard examples
Object Storage (S3/Garage)
When using external S3 storage (like Garage or AWS), keep the following in mind:
- CORS Configuration: Browser-based uploads will fail unless you apply a CORS policy to your bucket that allows the Helpdesk frontend origin.
- Force Path Style: Many custom S3 providers (Garage, Ceph) do not support virtual-host style buckets by default. Enable "Force Path Style" in the UI Storage Settings if you encounter connection or upload issues.
Backup and Recovery
-
Database Backups:
- Regular PostgreSQL dumps
- Point-in-time recovery setup
- Backup encryption
-
Object Storage:
- S3 versioning enabled
- Cross-region replication for critical attachments
-
Configuration Backup:
- Kubernetes manifests in version control
- Secrets encrypted at rest
- Helm values backed up securely
Troubleshooting
Common Issues
-
CORS Errors:
- Check
ALLOWED_ORIGINSconfiguration - Verify origin matches exactly (including protocol)
- Check browser developer tools for specific errors
- Check
-
Authentication Failures:
- OIDC: Verify JWKS URL is accessible
- Local: Check
AUTH_LOCAL_SECRETis set - Check JWT expiration and clock skew
-
Rate Limiting:
- Verify Redis connectivity
- Check rate limit configuration values
- Monitor Prometheus metrics
-
Performance Issues:
- Check database connection pool settings
- Monitor resource usage and limits
- Review timeout configurations
Debugging Commands
# Check health endpoints
curl -i http://api:8080/healthz
curl -i http://api:8080/readyz
curl -i http://worker:8081/health
curl -i http://worker:8081/ready
# Test authentication
curl -i -X POST http://api:8080/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"password"}'
# Check JWKS (OIDC mode)
curl -i $OIDC_JWKS_URL
# Verify CORS
curl -i -X OPTIONS http://api:8080/tickets \
-H "Origin: https://helpdesk.example.com" \
-H "Access-Control-Request-Method: GET"
Security Checklist
- Strong secrets generated and stored securely
- TLS enabled for all external connections
- CORS configured with specific origins only
- Rate limiting enabled and monitored
- Resource limits set appropriately
- Health checks configured
- Monitoring and alerting in place
- Backup and recovery procedures tested
- Network policies implemented (if available)
- Security scanning integrated into CI/CD