Files
mail-autoconfig/README.md

125 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mail Autoconfig
A single HTTP service that provides **Thunderbird autoconfig** and **Outlook autodiscover** for multiple domains. Email clients discover IMAP/SMTP settings by requesting well-known URLs; this app serves the correct XML based on the request host (e.g. `autoconfig.example.com` or `autodiscover.example.com`).
## Features
- **Thunderbird (Mozilla)** — Serves `config-v1.1.xml` at `/mail/config-v1.1.xml` when the client requests `https://autoconfig.<domain>/mail/config-v1.1.xml`.
- **Outlook (Microsoft)** — Serves Autodiscover XML at `/Autodiscover/Autodiscover.xml` when the client requests `https://autodiscover.<domain>/Autodiscover/Autodiscover.xml` (GET or POST).
- **Multi-domain** — One deployment serves many domains; the `Host` header selects the domain. Each domain must be in the allowlist.
- **Security** — Host allowlist, domain sanitization, rate limiting, security headers, and a hardened Docker setup (see [Security](#security)).
## How it works
1. **Thunderbird** looks up the autoconfig URL for the users domain (e.g. `user@example.com``https://autoconfig.example.com/mail/config-v1.1.xml`). It requests that URL; the app returns XML with IMAP/SMTP host, ports, and auth.
2. **Outlook** does the same for autodiscover (e.g. `https://autodiscover.example.com/Autodiscover/Autodiscover.xml`). The app returns Microsofts Autodiscover XML with server settings.
The app derives the “domain” from the `Host` header (e.g. `autoconfig.example.com``example.com`). The domain is validated against an allowlist and sanitized; it is only used in the XML for display/identification. **IMAP and SMTP server hostnames and ports are currently fixed in the templates** (e.g. `mail.mifi.holdings`, 993/587). To use different mail servers per domain, you would extend the templates or config.
## Endpoints
| Endpoint | Methods | Description | Rate limit |
|----------|---------|-------------|------------|
| `/` | GET | Landing page with links to autoconfig/autodiscover URLs for the current host | 50/hour |
| `/ping` | GET | Health check (returns plain text) | 10/minute |
| `/mail/config-v1.1.xml` | GET | Thunderbird autoconfig XML | 20/hour |
| `/Autodiscover/Autodiscover.xml` | GET, POST | Outlook autodiscover XML | 20/hour |
All endpoints require a `Host` header that matches the allowlist (e.g. `autoconfig.example.com`, `autodiscover.example.com`). Unlisted hosts receive `403 Forbidden`.
## Supported domains
Domains are configured in the `ALLOWED_HOSTS` set in `app.py`. Each domain must have both forms:
- `autoconfig.<domain>`
- `autodiscover.<domain>`
To add a domain, add both hostnames to `ALLOWED_HOSTS` and, if you use Traefik, add matching router/service labels in `docker-compose.yml` (see [Deployment](#deployment)).
## Requirements
- **Python** 3.11+ (for local run)
- **Dependencies**: Flask, Jinja2, Gunicorn (see `requirements.txt`)
- **Production**: Docker; reverse proxy (e.g. Traefik) for TLS and routing
## Installation
### Docker (recommended)
Build and run with Docker Compose. The app listens on port 8080 inside the container and is intended to sit behind a reverse proxy (e.g. Traefik) that terminates TLS and routes by host.
```bash
docker build -t mail-autoconfig .
docker run --rm -p 8080:8080 mail-autoconfig
```
For production you typically use `docker-compose.yml`, which wires the service into a Traefik network and applies security and resource limits.
### Docker Compose (production)
1. Ensure the `marina-net` (or your Traefik network) exists:
`docker network create marina-net`
2. Update `docker-compose.yml` if your image name or network differs.
3. Run:
`docker compose up -d`
The Compose file configures Traefik routers so that each allowed host (e.g. `autoconfig.mifi.holdings`) is routed to this service on port 8080. TLS is handled by Traefik (e.g. Lets Encrypt).
### Local development
```bash
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
flask --app app run --port 5000
```
Then open e.g. `http://localhost:5000/` with a `Host` header set to an allowed host (e.g. `autoconfig.mifi.holdings`), or use a hosts file and `Host: autoconfig.mifi.holdings`. The app binds to all interfaces; behind a proxy it expects `X-Forwarded-For` and `X-Forwarded-Proto` from a trusted proxy (see [Security](#security)).
## Configuration
- **Allowed hosts** — Edit `ALLOWED_HOSTS` in `app.py` and add corresponding Traefik labels in `docker-compose.yml` for each host.
- **Mail server** — IMAP/SMTP hostnames and ports are in the Jinja2 templates under `templates/` (`config-v1.1.xml.j2`, `Autodiscover.xml.j2`). Change those to point to your mail server(s).
- **Rate limits** — Defaults are in `app.py` (e.g. 50 req/h for `/`, 20 req/h for config endpoints). Adjust the `@rate_limit` decorator arguments if needed.
- **Traefik** — Ensure Traefik sets the real client IP (e.g. overwrites or sets `X-Forwarded-For`) so rate limiting and logging use the correct IP.
## Security
The service is hardened for production: host validation, domain sanitization, rate limiting, request size limits, security headers (CSP, HSTS, etc.), and a read-only, non-root container with minimal capabilities. Details and audit history are in:
- [docs/SECURITY.md](docs/SECURITY.md) — Hardening summary and deployment notes
- [docs/SECURITY-AUDIT.md](docs/SECURITY-AUDIT.md) — Full audit and remediation log
**Important:** Only attach trusted containers to the same Docker network as this service. Ensure the reverse proxy overwrites `X-Forwarded-For` with the real client IP.
## CI/CD (Woodpecker)
Pipelines are in [.woodpecker/](.woodpecker/):
- **build** — On push to `main`: build and push Docker image to `git.mifi.dev/mifi-holdings/mail-autoconfig`, then notify.
- **production** — On deployment to `production`: trigger Portainer stack webhook, then notify.
See [docs/CI-CD.md](docs/CI-CD.md) for Woodpecker setup and migration from Drone.
## Project layout
```
.
├── app.py # Flask app, routes, security (host check, rate limit, sanitization)
├── templates/ # Jinja2 templates for Thunderbird and Outlook XML
│ ├── config-v1.1.xml.j2
│ └── Autodiscover.xml.j2
├── requirements.txt # Pinned Python dependencies
├── Dockerfile # Multi-stage not used; single stage, non-root user
├── docker-compose.yml # Service + Traefik labels and security options
├── .woodpecker/ # Woodpecker CI workflows
└── docs/
├── CI-CD.md # CI/CD setup and migration
├── SECURITY.md # Security summary
└── SECURITY-AUDIT.md
```
## License
Not specified in the repo; treat as proprietary unless stated otherwise.