Files
mailgosend/README.md
T
2026-05-22 06:06:44 +00:00

280 lines
9.3 KiB
Markdown

# mailgosend
Self-hosted email server and webmail client in a single Go binary.
## What it does
- **SMTP** — receives inbound mail (port 25), accepts authenticated submission (587 STARTTLS, 465 TLS)
- **IMAP** — serves mail to desktop clients (143 STARTTLS, 993 TLS)
- **Webmail** — full browser client: read, compose, reply, forward, trash, search (port 8080)
- **Admin panel** — manage domains, users, queue, IP bans, security events (port 8081, localhost-only by default)
- **CalDAV** — calendar sync compatible with Apple Calendar, Thunderbird, DAVx5 (port 8080 at `/caldav/`)
- **CardDAV** — contacts sync compatible with Apple Contacts, Thunderbird, DAVx5 (port 8080 at `/carddav/`)
- **DKIM** — signs outbound mail, verifies inbound signatures
- **SPF / DMARC** — validates inbound mail policy
- **Spam filtering** — DNSBL checks + header heuristics with configurable score threshold
- **Encryption at rest** — all email bodies, attachments, contacts, and calendar events encrypted AES-256-GCM
- **TOTP / MFA** — per-user two-factor authentication with backup recovery codes
- **Auto TLS** — ACME (Let's Encrypt) via DNS-01 or HTTP-01; or bring your own certs
- **Multi-domain** — serve multiple mail domains from one instance
---
## Requirements
- Go 1.26.3+
- Linux / macOS (Windows: SMTP port 25 typically blocked)
- Ports 25, 587, 465 open inbound for mail reception
- A real FQDN with MX, A/AAAA, and SPF DNS records
---
## Build
```bash
git clone https://ghb.freebede.com/nahakubuilder/mailgosend
cd mailgosend
go build -o mailgosend ./cmd/mailgosend/
```
Produces a single ~35 MB binary with all web assets embedded.
---
## First run
```bash
./mailgosend
```
On first run with no `app_config.conf` present, the binary:
1. Generates `app_config.conf` with secure random `ENCRYPTION_KEY` and `SESSION_SECRET`
2. Creates `./data/mail.db` (SQLite) and applies the schema
3. Starts all configured services and prints listening addresses
**Back up `app_config.conf` immediately** — the encryption key is required to read stored mail. Losing it means losing access to all stored messages, contacts, and calendar data.
---
## Configuration
All settings live in `app_config.conf` (INI-style `KEY = VALUE`). The file is auto-generated with commented defaults on first run. The most important settings:
### Identity
| Key | Default | Description |
|---|---|---|
| `HOSTNAME` | `mail.example.com` | Server FQDN — used in SMTP HELO and TLS SNI |
| `DEFAULT_DOMAIN` | `example.com` | Primary mail domain |
### Ports
| Key | Default | Description |
|---|---|---|
| `SMTP_PORT` | `25` | Inbound SMTP |
| `SUBMIT_PORT` | `587` | SMTP submission (STARTTLS) |
| `SMTPS_PORT` | `465` | SMTP submission (implicit TLS) |
| `IMAP_PORT` | `143` | IMAP (STARTTLS) |
| `IMAPS_PORT` | `993` | IMAP (implicit TLS) |
| `WEBCLIENT_PORT` | `8080` | Webmail + CalDAV + CardDAV |
| `WEBADMIN_PORT` | `8081` | Admin panel (binds `127.0.0.1` by default) |
Disable any service by setting its `_ENABLED = false` or `_PORT = 0`.
### TLS
| Key | Default | Description |
|---|---|---|
| `TLS_MODE` | `dns01` | `dns01` \| `http01` \| `file` \| `off` |
| `ACME_EMAIL` | _(empty)_ | Required for ACME modes |
| `ACME_DNS_PROVIDER` | `cloudflare` | `cloudflare` \| `route53` \| `digitalocean` \| `hetzner` |
| `TLS_CERT` / `TLS_KEY` | `./certs/` | Certificate paths for `TLS_MODE=file` |
For `dns01` (recommended for wildcard certs), set the provider credentials:
```ini
TLS_MODE = dns01
ACME_EMAIL = admin@example.com
ACME_DNS_PROVIDER = cloudflare
CF_DNS_API_TOKEN = your-cloudflare-api-token
```
For manual / existing certs:
```ini
TLS_MODE = file
TLS_CERT = /etc/letsencrypt/live/mail.example.com/fullchain.pem
TLS_KEY = /etc/letsencrypt/live/mail.example.com/privkey.pem
```
### Storage
| Key | Default | Description |
|---|---|---|
| `DB_DRIVER` | `sqlite` | `sqlite` (embedded, no deps) |
| `DB_PATH` | `./data/mail.db` | SQLite database file |
| `STORAGE_BACKEND` | `db` | `db` (blobs in SQLite) \| `fs` (files on disk) |
| `STORAGE_FS_PATH` | `./data/messages` | Directory for `fs` backend |
| `MAX_MESSAGE_SIZE` | `52428800` | Max message size in bytes (50 MB) |
### Security
| Key | Default | Description |
|---|---|---|
| `BRUTE_MAX_TRIES` | `5` | Failed attempts before IP ban |
| `BRUTE_WINDOW_MIN` | `30` | Rolling window for attempt counting (minutes) |
| `BRUTE_BAN_HOURS` | `24` | How long a banned IP stays banned |
| `SECURE_COOKIE` | `false` | Set `true` when serving over HTTPS (marks cookies `Secure`) |
| `SESSION_MAX_AGE` | `604800` | Session lifetime in seconds (7 days) |
### Spam
| Key | Default | Description |
|---|---|---|
| `SPAM_THRESHOLD` | `10` | Score at which mail is marked spam |
| `SPAM_DNSBL` | `zen.spamhaus.org,...` | Comma-separated DNSBL hosts |
| `SPAM_CHECK_SPF` | `true` | Validate SPF on inbound |
| `SPAM_CHECK_DKIM` | `true` | Validate DKIM on inbound |
| `SPAM_CHECK_DMARC` | `true` | Validate DMARC on inbound |
### Queue / Delivery
| Key | Default | Description |
|---|---|---|
| `QUEUE_MAX_AGE_HOURS` | `72` | Hours before undeliverable mail bounces |
| `QUEUE_RETRY_MINS` | `5,15,60,240,480` | Retry backoff schedule (minutes) |
| `DNS_PRIMARY` | `1.1.1.1` | Primary resolver for MX lookups |
---
## DNS records (required)
Replace `example.com` and `203.0.113.1` with your domain and server IP.
```
; MX
example.com. MX 10 mail.example.com.
; A record for the mail host
mail.example.com. A 203.0.113.1
; SPF — only this server sends mail for example.com
example.com. TXT "v=spf1 mx ~all"
; DMARC
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com"
; PTR (set at your hosting provider — must match HOSTNAME)
1.113.0.203.in-addr.arpa. PTR mail.example.com.
```
DKIM keys are generated per-domain from the admin panel. After generating, copy the displayed TXT record into DNS.
---
## Initial setup workflow
1. Start the binary, open the admin panel at `http://127.0.0.1:8081/admin/`
2. On first run there is no admin account. Create one:
```bash
# The binary exposes no separate CLI; create the first admin via the
# setup endpoint that appears when zero users exist, or insert directly:
./mailgosend --create-admin admin@example.com
```
*(If no `--create-admin` flag exists yet, use the DB directly or the admin panel bootstrap page.)*
3. Add your domain: Admin → Domains → New Domain
4. Generate DKIM keys: Domain detail page → Generate DKIM
5. Copy the DKIM TXT record into your DNS provider
6. Create users: Admin → Users → New User
7. Point your mail client at the server (IMAP + SMTP credentials = email + password)
---
## Connecting a mail client
| Setting | Value |
|---|---|
| IMAP server | `mail.example.com` |
| IMAP port | `993` (TLS) or `143` (STARTTLS) |
| SMTP server | `mail.example.com` |
| SMTP port | `465` (TLS) or `587` (STARTTLS) |
| Username | Full email address (`user@example.com`) |
| Password | Account password |
| Authentication | Normal password |
---
## CalDAV / CardDAV
Clients discover the service via well-known URLs:
| URL | Service |
|---|---|
| `https://mail.example.com/.well-known/caldav` | Calendar discovery |
| `https://mail.example.com/.well-known/carddav` | Contacts discovery |
Authentication: HTTP Basic Auth (email + password). Compatible with Apple Calendar, Apple Contacts, Thunderbird (with TbSync), and DAVx5 on Android.
---
## Two-factor authentication (TOTP)
Users enable MFA from the webmail Settings page. The flow:
1. Settings → Set Up Two-Factor Auth → scan QR code with authenticator app
2. Enter the 6-digit code to confirm → MFA enabled
3. 10 single-use recovery codes are generated and encrypted; displayed once at enrollment time
On next login, users enter password then a 6-digit TOTP code (or 8-char recovery code).
Admin accounts support the same MFA flow via the admin panel login.
---
## Reloading TLS certificates
Send `SIGHUP` to reload certificates without downtime:
```bash
kill -HUP $(pidof mailgosend)
```
---
## Graceful shutdown
`SIGTERM` or `SIGINT` (Ctrl+C) triggers graceful shutdown: HTTP servers drain active connections (10-second deadline), queue worker stops, database closes.
---
## Security model
- **Sessions**: raw token in `HttpOnly` cookie; SHA-256 hash stored in DB. Cookie theft without DB access yields nothing.
- **Passwords**: bcrypt (cost 12).
- **Encryption**: AES-256-GCM with HKDF-derived per-user per-purpose keys. Master key in `app_config.conf`.
- **CSRF**: stateless HMAC-SHA256 token bound to session + clock hour. No DB storage.
- **Brute force**: failed attempts tracked per IP; configurable lockout threshold and duration.
- **Rate limiting**: token-bucket per IP — 60 req/min (webmail), 10 req/min (admin).
- **Security headers**: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy on all HTTP responses.
- **HTML emails**: rendered in `<iframe sandbox="allow-same-origin">` — scripts cannot execute.
- **Admin panel**: binds `127.0.0.1` by default; not exposed to the internet.
---
## Logging
Logs go to stdout by default. Set `LOG_FILE = ./logs/mail.log` to write to a file. Rotate with `logrotate` + `SIGHUP`.
---
## Upgrading
Replace the binary and restart. Schema migrations run automatically on startup; they are sequential and non-destructive (only `ADD COLUMN` and `CREATE TABLE IF NOT EXISTS`).
---
## License
See [LICENSE](LICENSE).