Files
malias-web/PROXY_SETUP.md
2026-01-23 13:41:39 +01:00

433 lines
10 KiB
Markdown

# Reverse Proxy Setup Guide
This guide helps you configure reverse proxies (Nginx, Traefik, Zoraxy, Authelia, Caddy, etc.) to work with Mailcow Alias Manager.
---
## 🔧 ENABLE_PROXY Configuration
The application supports **two access modes** controlled by the `ENABLE_PROXY` environment variable in `docker-compose.yml`:
### **Mode 1: Direct Access (ENABLE_PROXY=false)** - DEFAULT
Use this when accessing the application directly via IP:port **without a reverse proxy**.
**docker-compose.yml:**
```yaml
environment:
- ENABLE_PROXY=false # Default setting
```
**Access:** `http://192.168.1.100:5172` (replace with your server IP)
**Features:**
- ✅ Works over HTTP (no HTTPS required)
- ✅ Standard cookie behavior (SameSite=Lax)
- ✅ No proxy configuration needed
- ✅ Simple login flow
- ✅ Perfect for internal/LAN access
**When to use:**
- Accessing from internal network only
- No reverse proxy in place
- Testing or development
- Simple single-server setup
---
### **Mode 2: Proxy Access (ENABLE_PROXY=true)**
Use this when running **behind a reverse proxy** (Authelia, Zoraxy, Nginx, Traefik, Caddy).
**docker-compose.yml:**
```yaml
environment:
- ENABLE_PROXY=true
```
**Access:** `https://alias.yourdomain.com` (through your reverse proxy)
**Features:**
- ✅ ProxyFix middleware handles X-Forwarded-* headers
- ✅ HTTPS redirect support
- ✅ Secure cookies (HTTPS only)
- ✅ Works with authentication proxies (Authelia)
- ✅ Multi-proxy chain support
**When to use:**
- Accessing from internet via domain name
- Behind Nginx, Traefik, Caddy, HAProxy
- Behind authentication proxy (Authelia, Authentik)
- SSL/TLS termination at proxy
- Production deployments with HTTPS
**Special Feature - Authelia Auto-Login:**
When `ENABLE_PROXY=true` and you're using Authelia, the app automatically logs you in using Authelia's authentication headers. **No app password needed!** Simply authenticate through Authelia, and you'll be logged into the Mailcow Alias Manager automatically.
---
### **Switching Between Modes**
To switch from one mode to another:
1. **Edit `docker-compose.yml`**
```yaml
# Change this line:
- ENABLE_PROXY=false # or true
```
2. **Restart the container**
```bash
docker compose down
docker compose up -d
```
3. **Verify mode in logs**
```bash
docker compose logs mailcow-alias-manager | grep "ACCESS MODE"
```
You should see either:
- `ACCESS MODE: Direct IP:Port (ENABLE_PROXY=false)`
- `ACCESS MODE: Reverse Proxy (ENABLE_PROXY=true)`
4. **Clear browser cookies** (IMPORTANT!)
- Press F12 → Application → Cookies
- Delete all cookies for your domain
- Close and reopen browser
5. **Login again**
---
### **Quick Reference Table**
| Access Method | ENABLE_PROXY | Access URL | Cookie Mode |
|--------------|--------------|------------|-------------|
| Direct IP:port | `false` (default) | `http://192.168.1.100:5172` | HTTP, SameSite=Lax |
| Nginx/Traefik | `true` | `https://alias.example.com` | HTTPS, SameSite=None |
| Authelia + Zoraxy | `true` | `https://alias.example.com` | HTTPS, SameSite=None |
| Caddy | `true` | `https://alias.example.com` | HTTPS, SameSite=None |
| Local dev (`python3 app.py`) | N/A (not set) | `http://localhost:5172` | HTTP, SameSite=Lax |
---
## ✅ Built-in Proxy Support (When ENABLE_PROXY=true)
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`
**Works with 2 proxies in chain** (e.g., Zoraxy → Authelia → App)
---
## 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: <client-ip>
```
#### **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! 🚀