# Authentication Service with Active Directory Integration This authentication service provides JWT-based authentication with Active Directory integration and Traefik ForwardAuth support for Kubernetes environments. ## Features - πŸ” **Active Directory Authentication**: Validates credentials against your AD server - 🎫 **JWT Tokens**: Secure token-based authentication with configurable expiration - πŸͺ **Cookie & Local Storage**: Tokens stored securely in HTTP-only cookies and locally - πŸš€ **Traefik Integration**: ForwardAuth middleware for seamless Kubernetes access control - πŸ“± **Responsive UI**: Clean, modern login interface - πŸ”’ **Security Headers**: Proper CORS, security headers, and token validation ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ User Browser │───▢│ Auth Service │───▢│ Active Directoryβ”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ 1. Login Form β”‚ β”‚ 2. Validate AD β”‚ β”‚ 3. LDAP Auth β”‚ β”‚ 4. Store Token │◀───│ Create JWT β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Protected API │◀───│ Traefik Forward β”‚ β”‚ β”‚ β”‚ Auth Middleware β”‚ β”‚ 5. Access with β”‚ β”‚ 6. Validate JWT β”‚ β”‚ JWT Token β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Quick Start ### 1. Build and Deploy ```bash # Build the authentication service cd auth-service docker build -t your-registry/auth-service:1.0.0 . docker push your-registry/auth-service:1.0.0 # Update values in values-example.yaml cp values-example.yaml values.yaml # Edit values.yaml with your AD configuration # Deploy to Kubernetes kubectl apply -f kubernetes-auth.yaml ``` ### 2. Configure Active Directory Update the `values.yaml` file with your AD configuration: ```yaml authService: activeDirectory: server: "ldap://your-ad-server.yourdomain.com" baseDN: "DC=yourdomain,DC=com" userSearchBase: "CN=Users,DC=yourdomain,DC=com" bindUser: "CN=ServiceAccount,CN=Users,DC=yourdomain,DC=com" bindPassword: "your-service-account-password" ``` ### 3. Configure Traefik ForwardAuth The service automatically creates a ForwardAuth middleware that: ```yaml apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: auth-forward spec: forwardAuth: address: http://auth-service:8080/auth/verify authResponseHeaders: - "X-Auth-User" - "X-Auth-Email" - "X-Auth-Groups" - "X-Auth-Display-Name" ``` ### 4. Protect Your Services Add the ForwardAuth middleware to any IngressRoute: ```yaml apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: protected-service spec: routes: - match: Host(`api.yourdomain.com`) kind: Rule services: - name: your-api-service port: 8000 middlewares: - name: auth-forward # This protects the entire service ``` ## How It Works ### Authentication Flow 1. **User visits protected resource** β†’ Traefik ForwardAuth redirects to login 2. **User enters AD credentials** β†’ Service validates against Active Directory 3. **JWT token created** β†’ Stored in HTTP-only cookie + localStorage 4. **Subsequent requests** β†’ Traefik validates JWT via ForwardAuth 5. **Access granted** β†’ User headers passed to backend service ### Token Storage The system uses a dual-storage approach: - **HTTP-only Cookie**: Secure, automatic transmission, protected from XSS - **localStorage**: Available to JavaScript for SPA applications ### Security Features - βœ… **LDAP over TLS** support for secure AD communication - βœ… **JWT token expiration** with configurable timeouts - βœ… **HTTP-only cookies** prevent XSS token theft - βœ… **Secure headers** for production deployment - βœ… **CORS protection** with configurable origins ## API Endpoints ### Authentication Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/` | GET | Login page (HTML) | | `/dashboard` | GET | Dashboard page (HTML) | | `/auth/login` | POST | Authenticate user | | `/auth/verify` | POST | Verify JWT token (ForwardAuth) | | `/auth/logout` | GET | Logout user | | `/auth/user` | GET | Get current user info | | `/health` | GET | Health check | ### ForwardAuth Integration When Traefik calls `/auth/verify`, the service: 1. **Checks for token** in Authorization header or cookies 2. **Validates JWT** signature and expiration 3. **Returns user headers** for backend services: - `X-Auth-User`: Username - `X-Auth-Email`: User email - `X-Auth-Groups`: AD group memberships - `X-Auth-Display-Name`: User's display name ## Configuration ### Environment Variables | Variable | Description | Default | |----------|-------------|---------| | `JWT_SECRET` | Secret key for JWT signing | (required) | | `TOKEN_EXPIRE_HOURS` | Token expiration in hours | 8 | | `AD_SERVER` | LDAP server URL | (required) | | `AD_BASE_DN` | Base DN for AD | (required) | | `AD_USER_SEARCH_BASE` | User search base | (required) | | `AD_BIND_USER` | Service account for LDAP | (optional) | | `AD_BIND_PASSWORD` | Service account password | (optional) | ### Kubernetes Secrets Create the required secrets: ```bash kubectl create secret generic auth-secrets \ --from-literal=jwt-secret="your-super-secret-key" \ --from-literal=ad-bind-user="CN=ServiceAccount,CN=Users,DC=yourdomain,DC=com" \ --from-literal=ad-bind-password="your-service-password" ``` ## Advanced Usage ### Custom Group-Based Access The service passes AD group memberships in the `X-Auth-Groups` header. You can use this in your backend services: ```python # In your FastAPI backend from fastapi import Header def check_admin_access(x_auth_groups: str = Header(None)): groups = x_auth_groups.split(',') if x_auth_groups else [] if 'CN=Admins,CN=Groups,DC=yourdomain,DC=com' not in groups: raise HTTPException(status_code=403, detail="Admin access required") ``` ### Multiple Protection Levels You can create different ForwardAuth middlewares for different access levels: ```yaml # Admin-only middleware apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: admin-auth spec: forwardAuth: address: http://auth-service:8080/auth/verify-admin authResponseHeaders: - "X-Auth-User" - "X-Auth-Groups" ``` ### Token Refresh The service automatically handles token refresh. Configure shorter expiration times and implement refresh logic in your frontend: ```javascript // Check token expiration const token = localStorage.getItem('auth_token'); const payload = JSON.parse(atob(token.split('.')[1])); const expiry = new Date(payload.exp * 1000); if (expiry < new Date()) { // Redirect to login for refresh window.location.href = '/auth/login'; } ``` ## Monitoring ### Health Checks The service includes health check endpoints: ```bash curl http://auth-service:8080/health ``` ### Logging The service logs authentication attempts and failures: ``` INFO: Successfully authenticated user: john.doe ERROR: Authentication failed for user: invalid.user ERROR: LDAP connection failed: timeout ``` ## Security Considerations 1. **Use HTTPS**: Always deploy with TLS certificates 2. **Secure JWT Secret**: Use a strong, unique JWT secret 3. **Network Security**: Restrict access to AD servers 4. **Token Expiration**: Use reasonable token expiration times 5. **Service Account**: Use a dedicated AD service account with minimal permissions 6. **Audit Logs**: Monitor authentication logs for suspicious activity ## Troubleshooting ### Common Issues 1. **LDAP Connection Failed** - Check AD server connectivity - Verify LDAP URL format - Check firewall rules 2. **Authentication Failed** - Verify AD credentials - Check user search base DN - Confirm user exists in specified OU 3. **ForwardAuth Not Working** - Verify Traefik middleware configuration - Check service connectivity - Review Traefik logs 4. **Token Issues** - Verify JWT secret consistency - Check token expiration - Validate cookie settings ### Debug Mode Enable debug logging by setting log level: ```yaml env: - name: LOG_LEVEL value: "DEBUG" ``` This will provide detailed authentication flow logs. # πŸ”§ Drone Configuration Factorization Options ## 1. Jsonnet Configuration (Recommended) Drone supports Jsonnet for dynamic configuration generation, which allows you to include files and create reusable components. ## 2. YAML Anchors & References (Limited) YAML supports anchors and references for basic factorization: ## 3. External Configuration Files (Starlark) Drone also supports Starlark (Python-like) for advanced configuration: 🎯 Recommended: Convert to Jsonnet Let me create a script to help you convert your current .drone.yml to the modular Jsonnet approach: convert-to-jsonnet.sh ```bash #!/bin/bash # Convert existing .drone.yml to modular Jsonnet structure # This script helps migrate from YAML to factorized Jsonnet configuration echo "πŸ”„ Converting Drone configuration to modular Jsonnet..." # Generate the final configuration from Jsonnet echo "πŸ“ Generating .drone.yml from Jsonnet..." if command -v jsonnet >/dev/null 2>&1; then jsonnet .drone.jsonnet > .drone.yml.generated echo "βœ… Generated .drone.yml.generated from Jsonnet" echo "" echo "πŸ“‹ To use the new configuration:" echo "1. Review: cat .drone.yml.generated" echo "2. Test: python3 -c \"import yaml; yaml.safe_load(open('.drone.yml.generated'))\"" echo "3. Replace: mv .drone.yml.generated .drone.yml" echo "4. Commit: git add .drone.jsonnet common.libsonnet build-steps.libsonnet .drone.yml" else echo "⚠️ jsonnet not installed. Installing..." # Try to install jsonnet if command -v apt-get >/dev/null 2>&1; then sudo apt-get update && sudo apt-get install -y jsonnet elif command -v apk >/dev/null 2>&1; then apk add --no-cache jsonnet elif command -v yum >/dev/null 2>&1; then sudo yum install -y jsonnet else echo "❌ Cannot install jsonnet automatically" echo "πŸ“– Please install jsonnet manually:" echo " - Ubuntu/Debian: sudo apt-get install jsonnet" echo " - Alpine: apk add jsonnet" echo " - CentOS/RHEL: sudo yum install jsonnet" echo " - Or download from: https://github.com/google/jsonnet" exit 1 fi # Try generating again if command -v jsonnet >/dev/null 2>&1; then jsonnet .drone.jsonnet > .drone.yml.generated echo "βœ… Generated .drone.yml.generated from Jsonnet" else echo "❌ Failed to install jsonnet" exit 1 fi fi echo "" echo "🎯 Benefits of Jsonnet configuration:" echo " βœ… Reusable components (common.libsonnet, build-steps.libsonnet)" echo " βœ… Variables and functions" echo " βœ… Conditional logic" echo " βœ… Better maintainability" echo " βœ… DRY principle" echo "" echo "πŸ“š Files created:" echo " - .drone.jsonnet (main configuration)" echo " - common.libsonnet (shared steps and config)" echo " - build-steps.libsonnet (build-specific logic)" echo " - .drone.yml.generated (generated YAML)" # Validate the generated YAML if [ -f ".drone.yml.generated" ]; then echo "" echo "πŸ” Validating generated YAML..." if python3 -c "import yaml; yaml.safe_load(open('.drone.yml.generated'))" 2>/dev/null; then echo "βœ… Generated YAML is valid" else echo "❌ Generated YAML has syntax errors" exit 1 fi fi ``` # πŸ“‹ Summary of Factorization Options | Method | Pros | Cons | Best For | | ------ | ---- | ---- | -------- | | Jsonnet | βœ… Full programming language
βœ… Variables, functions, imports
βœ… Conditional logic | ❌ Learning curve
❌ Requires jsonnet tool | Complex configurations | | YAML Anchors | βœ… Native YAML
βœ… Simple syntax
βœ… No extra tools | ❌ Limited functionality
❌ No conditionals | Simple repetition | | Starlark | βœ… Python-like syntax
βœ… Advanced logic
βœ… Built into Drone | ❌ Drone-specific
❌ Less common | Advanced pipelines | # 🎯 Recommendation for Your Project For your external Buildah build system, I recommend Jsonnet because: - **Modularity**: Separate build logic from common steps - **Reusability**: Common kubectl setup, environment config - **Maintainability**: Easier to update build steps independently - **Flexibility**: Can create variants (dev, staging, prod) easily ## πŸš€ Quick Start with Jsonnet ```console # Install jsonnet (if needed) sudo apt-get install jsonnet # Convert to modular structure ./convert-to-jsonnet.sh # Review generated configuration cat .drone.yml.generated # Test and deploy mv .drone.yml.generated .drone.yml git add .drone.jsonnet common.libsonnet build-steps.libsonnet .drone.yml git commit -m "Convert to modular Jsonnet configuration" git push ``` The modular approach will make it much easier to: - πŸ”§ Update build steps without touching common logic - 🎯 Create environment-specific configurations - πŸ§ͺ Test individual components - πŸ“¦ Share configuration across projects