diff --git a/PROXY_SETUP.md b/PROXY_SETUP.md new file mode 100644 index 0000000..d51426e --- /dev/null +++ b/PROXY_SETUP.md @@ -0,0 +1,322 @@ +# Reverse Proxy Setup Guide + +This guide helps you configure reverse proxies (Nginx, Traefik, Zoraxy, Authelia, Caddy, etc.) to work with Mailcow Alias Manager. + +## ✅ Built-in Proxy Support + +The application includes **ProxyFix middleware** that automatically handles: +- ✅ HTTPS detection via `X-Forwarded-Proto` +- ✅ Host header forwarding via `X-Forwarded-Host` +- ✅ Client IP forwarding via `X-Forwarded-For` +- ✅ Path prefix support via `X-Forwarded-Prefix` + +**No configuration changes needed in most cases!** + +--- + +## Common Proxy Configurations + +### **Zoraxy** (Your Current Setup) + +Zoraxy automatically sets the required headers. Just configure: + +1. **Upstream Target**: `http://localhost:5172` (or your Docker IP) +2. **Enable WebSocket**: Yes (optional, recommended) +3. **Enable Proxy Headers**: Yes (should be default) + +**Example Zoraxy Config:** +```json +{ + "name": "mailcow-alias-manager", + "upstream": "http://localhost:5172", + "domain": "aliases.yourdomain.com" +} +``` + +--- + +### **Authelia** (Authentication Proxy) + +If using Authelia in front of the app: + +1. **Authelia forwards the user after login** - this should work automatically +2. **Make sure Authelia passes these headers**: + - `X-Forwarded-Proto` + - `X-Forwarded-Host` + - `X-Forwarded-For` + +**Common Issue**: Authelia redirects → App login page +**Solution**: The app has its own authentication. You have two options: + - **Option A**: Disable app login (requires code modification) + - **Option B**: Use Authelia for network access, app login for API access + +--- + +### **Nginx** + +```nginx +server { + listen 443 ssl http2; + server_name aliases.yourdomain.com; + + ssl_certificate /path/to/cert.pem; + ssl_certificate_key /path/to/key.pem; + + location / { + proxy_pass http://localhost:5172; + + # Required headers for ProxyFix + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # Optional: WebSocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +--- + +### **Traefik** + +**docker-compose.yml:** +```yaml +services: + mailcow-alias-manager: + image: gitlab.pm/rune/malias-web:latest + container_name: mailcow-alias-manager + volumes: + - ./data:/app/data + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.malias.rule=Host(`aliases.yourdomain.com`)" + - "traefik.http.routers.malias.entrypoints=websecure" + - "traefik.http.routers.malias.tls=true" + - "traefik.http.routers.malias.tls.certresolver=letsencrypt" + - "traefik.http.services.malias.loadbalancer.server.port=5172" + networks: + - traefik + +networks: + traefik: + external: true +``` + +Traefik automatically sets `X-Forwarded-*` headers by default. + +--- + +### **Caddy** + +```caddy +aliases.yourdomain.com { + reverse_proxy localhost:5172 +} +``` + +Caddy automatically handles all proxy headers - no configuration needed! + +--- + +## 🔧 Troubleshooting + +### **Issue: "NetworkError when attempting to fetch resource"** + +**Causes:** +1. Mixed HTTP/HTTPS content +2. CORS blocking requests +3. Session cookie not being set +4. Proxy not forwarding headers + +**Solutions:** + +#### **1. Check Proxy Headers** +Your proxy MUST forward these headers: +``` +X-Forwarded-Proto: https +X-Forwarded-Host: aliases.yourdomain.com +X-Forwarded-For: +``` + +#### **2. Check Browser Console** +Open browser DevTools (F12) → Console tab → Look for errors: +- **CORS errors**: Proxy misconfiguration +- **Mixed content**: HTTP resources on HTTPS page +- **401 Unauthorized**: Session/cookie issue + +#### **3. Test Direct Access** +```bash +# Test without proxy +curl http://localhost:5172/ + +# Should return HTML (login page) +``` + +If this works but proxy doesn't, it's a proxy configuration issue. + +#### **4. Check Session Cookies** +In browser DevTools → Application tab → Cookies: +- **Domain**: Should match your proxy domain +- **Path**: `/` +- **HttpOnly**: Should be `true` +- **Secure**: Depends on your setup + +--- + +### **Issue: Login works but redirects to HTTP instead of HTTPS** + +**Solution**: Already fixed in latest version via `PREFERRED_URL_SCHEME='https'` in app config. + +If still happening: +1. Verify proxy sends `X-Forwarded-Proto: https` +2. Check `docker compose logs -f mailcow-alias-manager` +3. Rebuild image: `docker compose up -d --build` + +--- + +### **Issue: "Invalid password" but password is correct** + +This is NOT a proxy issue - this is an authentication issue: +```bash +# Reset password +docker exec -it mailcow-alias-manager python3 reset_password.py "newpass" +``` + +--- + +### **Issue: 502 Bad Gateway** + +**Causes:** +1. App not running +2. Wrong upstream port +3. Container network issue + +**Check:** +```bash +# Is container running? +docker ps | grep mailcow-alias-manager + +# Check logs +docker compose logs -f mailcow-alias-manager + +# Test internal connectivity +docker exec mailcow-alias-manager curl http://localhost:5172 +``` + +--- + +## 🔒 Security Best Practices + +### **1. HTTPS Only** +Always use HTTPS in production. HTTP is only for local testing. + +### **2. Restrict Access** +Use firewall or proxy rules to restrict access: +```nginx +# Nginx: Restrict to internal network only +allow 192.168.1.0/24; +deny all; +``` + +### **3. Use Authelia/Authentik** +Add SSO layer for additional security: +- Users authenticate via Authelia +- Then app login provides API access +- Two-factor authentication recommended + +### **4. Keep Session Cookies Secure** +The app sets: +- `HttpOnly=True` - Prevents JavaScript access +- `SameSite=Lax` - CSRF protection + +For HTTPS-only deployments, you can enforce secure cookies: +```python +# In app.py, change: +SESSION_COOKIE_SECURE=True # Only send cookie over HTTPS +``` + +--- + +## 📊 Testing Your Setup + +### **1. Basic Connectivity** +```bash +curl -v https://aliases.yourdomain.com/ +# Should return 200 OK with HTML +``` + +### **2. Login Test** +```bash +curl -v -X POST https://aliases.yourdomain.com/login \ + -H "Content-Type: application/json" \ + -d '{"password":"yourpassword"}' \ + -c cookies.txt + +# Check response - should be JSON with status: success +``` + +### **3. Session Test** +```bash +curl -v https://aliases.yourdomain.com/ \ + -b cookies.txt + +# Should return main page HTML (not login redirect) +``` + +--- + +## 🆘 Still Having Issues? + +### **Enable Debug Logging** + +**docker-compose.yml:** +```yaml +environment: + - FLASK_DEBUG=True # Enable debug mode +``` + +Restart: +```bash +docker compose down +docker compose up -d +docker compose logs -f +``` + +### **Check Application Logs** +```bash +# View logs +docker compose logs -f mailcow-alias-manager + +# Check for errors +docker compose logs mailcow-alias-manager | grep -i error +``` + +### **Test Without Proxy** +Temporarily bypass proxy to isolate the issue: +```bash +# Direct access test +curl http://localhost:5172/ +``` + +If this works, the issue is with proxy configuration, not the app. + +--- + +## ✅ Summary + +**The app is already configured for reverse proxies!** + +Key points: +- ✅ ProxyFix middleware is enabled +- ✅ Handles X-Forwarded-* headers automatically +- ✅ HTTPS redirect support built-in +- ✅ Session cookies work behind proxies +- ✅ No code changes needed + +Just make sure your proxy forwards the standard headers and you're good to go! 🚀 diff --git a/app.py b/app.py index 30a4cb1..8f8b13b 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ from flask import Flask, render_template, request, jsonify, redirect, url_for, session from functools import wraps +from werkzeug.middleware.proxy_fix import ProxyFix import malias_wrapper as malias_w import os import argparse @@ -8,9 +9,28 @@ import sys app = Flask(__name__) app.secret_key = os.urandom(24) # Secret key for session management +# Configure for reverse proxy (Authelia, Zoraxy, Nginx, etc.) +# This fixes HTTPS detection and redirects when behind a proxy +app.wsgi_app = ProxyFix( + app.wsgi_app, + x_for=1, # Trust X-Forwarded-For with 1 proxy + x_proto=1, # Trust X-Forwarded-Proto (http/https) + x_host=1, # Trust X-Forwarded-Host + x_prefix=1 # Trust X-Forwarded-Prefix +) + # Initialize database on startup malias_w.init_database() +# Session configuration for reverse proxy +# Allow session cookies to work properly behind HTTPS proxy +app.config.update( + SESSION_COOKIE_SECURE=False, # Set to True if using HTTPS only + SESSION_COOKIE_HTTPONLY=True, # Prevent JavaScript access to session cookie + SESSION_COOKIE_SAMESITE='Lax', # CSRF protection + PREFERRED_URL_SCHEME='https' # Generate HTTPS URLs when behind proxy +) + def login_required(f): """Decorator to require login for routes""" @wraps(f) diff --git a/docker-compose.yml b/docker-compose.yml index 5b457a7..dace85a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,12 @@ # Recommended Settings: # - Production: FLASK_ENV=production, FLASK_DEBUG=False (default) # - Development: FLASK_ENV=development, FLASK_DEBUG=True +# +# Reverse Proxy Support: +# ---------------------- +# This application is configured to work behind reverse proxies (Nginx, Traefik, Zoraxy, Authelia, etc.) +# The ProxyFix middleware automatically handles X-Forwarded-* headers for HTTPS detection +# No additional configuration needed for most standard proxy setups services: mailcow-alias-manager: