# 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