178 lines
6.9 KiB
Markdown
178 lines
6.9 KiB
Markdown
# GoMail
|
|||
|
|
|
||
|
|
A complete self-hosted email server in a single Go binary — replacing
|
||
|
|
postfix + dovecot + roundcube + spamassassin + radicale with one process,
|
||
|
|
hand-rolled almost entirely on the Go standard library (SMTP, IMAP, POP3,
|
||
|
|
JMAP, CalDAV/CardDAV, ManageSieve, DKIM, SPF/DMARC verification, OAuth2,
|
||
|
|
ACME, JWT — all implemented from scratch, not wrapped around third-party
|
||
|
|
protocol libraries).
|
||
|
|
|
||
|
|
## What's included
|
||
|
|
|
||
|
|
- **Mail transport**: SMTP (inbound MTA + authenticated submission), full
|
||
|
|
security pipeline (SPF, DKIM, DMARC, header/URL heuristics, optional
|
||
|
|
ClamAV/Rspamd/LLM stages), outbound queue with retry and bounce handling
|
||
|
|
- **Mail access**: IMAP4rev1, POP3 (off by default), JMAP (RFC 8620/8621
|
||
|
|
subset)
|
||
|
|
- **Filtering**: Sieve scripts (RFC 5228 subset) managed via ManageSieve
|
||
|
|
(RFC 5804)
|
||
|
|
- **Calendar & contacts**: CalDAV/CardDAV, per-user and per-tenant
|
||
|
|
- **Webmail**: built-in dark-themed web client — folders, compose, search,
|
||
|
|
quarantine review, MFA/app-password/recovery settings
|
||
|
|
- **Admin portal**: tenants, domains (with DKIM key generation/rotation),
|
||
|
|
users, list rules, outbound queue management, global quarantine
|
||
|
|
- **Multi-account**: link external Gmail/Microsoft 365/generic IMAP
|
||
|
|
accounts via OAuth2
|
||
|
|
- **Security**: TOTP MFA with backup codes, app passwords, TLS via real
|
||
|
|
ACME (Let's Encrypt-compatible) certificate issuance and auto-renewal,
|
||
|
|
per-IP rate limiting on every listener
|
||
|
|
- **Everything encrypted at rest**: messages, contacts, calendar events,
|
||
|
|
DKIM/TLS/OAuth2 credentials — AES-256-GCM with a per-record key derived
|
||
|
|
via HKDF from a single master key
|
||
|
|
|
||
|
|
See `gomail-action-plan-v4.md` (if included alongside this README) for a
|
||
|
|
detailed phase-by-phase account of what's built, what's tested, and what's
|
||
|
|
intentionally deferred.
|
||
|
|
|
||
|
|
## Quick start
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 1. Generate a master key (required — GoMail refuses to start without one)
|
||
|
|
gomail -gen-master-key
|
||
|
|
# prints: GOMAIL_MASTER_KEY=<64 hex chars>
|
||
|
|
|
||
|
|
# 2. Set required environment variables
|
||
|
|
export GOMAIL_MASTER_KEY=<paste from step 1>
|
||
|
|
export GOMAIL_JWT_SECRET=$(openssl rand -hex 32)
|
||
|
|
export GOMAIL_ADMIN_INIT_PASSWORD='choose-a-strong-password'
|
||
|
|
|
||
|
|
# 3. First run auto-generates a default config at the path you specify
|
||
|
|
gomail -config /etc/gomail/gomail.yaml
|
||
|
|
|
||
|
|
# 4. Edit /etc/gomail/gomail.yaml before running for real:
|
||
|
|
# - server.hostname: your actual mail server hostname
|
||
|
|
# - tls.mode: "acme" with tls.acme_domains set (see below), or "file"
|
||
|
|
# - tls.acme_email: an address you actually monitor (Let's Encrypt
|
||
|
|
# account contact, used for expiry warnings)
|
||
|
|
```
|
||
|
|
|
||
|
|
For a full system install (dedicated user, systemd unit, directory
|
||
|
|
permissions), use `install.sh` instead of running the binary directly —
|
||
|
|
see that script's header comment for exactly what it does and doesn't do.
|
||
|
|
|
||
|
|
## TLS / ACME
|
||
|
|
|
||
|
|
GoMail obtains real Let's Encrypt-compatible certificates automatically
|
||
|
|
when `tls.mode: acme` and `tls.acme_domains` are set:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
tls:
|
||
|
|
mode: acme
|
||
|
|
acme_email: postmaster@yourdomain.com
|
||
|
|
acme_domains: ["mail.yourdomain.com"]
|
||
|
|
# acme_directory_url defaults to real Let's Encrypt production —
|
||
|
|
# override to Let's Encrypt staging while testing (see below)
|
||
|
|
```
|
||
|
|
|
||
|
|
This requires port 80 to be reachable from the internet for HTTP-01
|
||
|
|
challenge validation — GoMail runs its own minimal HTTP server on `:80`
|
||
|
|
for this, it doesn't need a separate web server in front of it for the
|
||
|
|
challenge itself.
|
||
|
|
|
||
|
|
**Before pointing this at Let's Encrypt production, test against
|
||
|
|
staging** — set `tls.acme_directory_url` to
|
||
|
|
`https://acme-staging-v02.api.letsencrypt.org/directory` — to avoid
|
||
|
|
hitting production rate limits while testing. Staging certificates aren't
|
||
|
|
trusted by real browsers/clients, so switch back to the production
|
||
|
|
directory (or just remove the override) once you've confirmed issuance
|
||
|
|
works.
|
||
|
|
|
||
|
|
Without `tls.acme_domains` set, GoMail falls back to a self-signed
|
||
|
|
certificate — fine for local testing, **not suitable for production**.
|
||
|
|
|
||
|
|
## DNS setup
|
||
|
|
|
||
|
|
Once GoMail is running, publish these DNS records for `yourdomain.com`
|
||
|
|
(adjust names/values to match your actual domain and what GoMail logs at
|
||
|
|
bootstrap — the exact DKIM public key is generated per-installation and
|
||
|
|
logged once at first startup, and also viewable via the admin portal's
|
||
|
|
Domains page).
|
||
|
|
|
||
|
|
### MX record
|
||
|
|
Point mail delivery at your GoMail server:
|
||
|
|
|
||
|
|
| Type | Name | Value | Priority |
|
||
|
|
|---|---|---|---|
|
||
|
|
| MX | `yourdomain.com` | `mail.yourdomain.com` | 10 |
|
||
|
|
|
||
|
|
Plus an A/AAAA record for `mail.yourdomain.com` pointing at the server's
|
||
|
|
IP.
|
||
|
|
|
||
|
|
### SPF record
|
||
|
|
Authorizes your server to send mail for the domain:
|
||
|
|
|
||
|
|
| Type | Name | Value |
|
||
|
|
|---|---|---|
|
||
|
|
| TXT | `yourdomain.com` | `v=spf1 mx ~all` |
|
||
|
|
|
||
|
|
`mx` covers "any host listed in this domain's MX records may send mail" —
|
||
|
|
sufficient for a single GoMail instance handling its own outbound
|
||
|
|
delivery. `~all` (soft fail) is a reasonable starting point; tighten to
|
||
|
|
`-all` (hard fail) once you're confident no other source legitimately
|
||
|
|
sends mail as this domain.
|
||
|
|
|
||
|
|
### DKIM record
|
||
|
|
GoMail generates a DKIM key pair automatically for each domain at
|
||
|
|
bootstrap (or when created via the admin portal) and logs the exact TXT
|
||
|
|
record to publish — it looks like this:
|
||
|
|
|
||
|
|
| Type | Name | Value |
|
||
|
|
|---|---|---|
|
||
|
|
| TXT | `mail._domainkey.yourdomain.com` | `v=DKIM1; k=rsa; p=<base64 public key>` |
|
||
|
|
|
||
|
|
The selector (`mail` by default) and the actual key value are
|
||
|
|
domain-specific — copy them from the startup log or the admin portal
|
||
|
|
rather than this example. Rotating the key (also available in the admin
|
||
|
|
portal) requires updating this record to match the new key.
|
||
|
|
|
||
|
|
### DMARC record
|
||
|
|
Tells receiving servers what to do with mail that fails SPF/DKIM
|
||
|
|
alignment, and where to send aggregate reports:
|
||
|
|
|
||
|
|
| Type | Name | Value |
|
||
|
|
|---|---|---|
|
||
|
|
| TXT | `_dmarc.yourdomain.com` | `v=DMARC1; p=quarantine; rua=mailto:postmaster@yourdomain.com` |
|
||
|
|
|
||
|
|
Start with `p=quarantine` (suspicious mail gets flagged, not silently
|
||
|
|
dropped) rather than `p=reject` until you've confirmed legitimate mail
|
||
|
|
flows aren't being caught by DMARC alignment failures.
|
||
|
|
|
||
|
|
### MTA-STS and TLS-RPT
|
||
|
|
**Not yet implemented** by GoMail (see the action plan's Phase 13
|
||
|
|
deferred-items list) — no records to publish for these yet. This section
|
||
|
|
will be filled in once that work lands.
|
||
|
|
|
||
|
|
## Configuration reference
|
||
|
|
|
||
|
|
Full config lives in `gomail.yaml` (auto-generated with defaults on first
|
||
|
|
run) plus a small number of required environment variables for secrets
|
||
|
|
(`GOMAIL_MASTER_KEY`, `GOMAIL_JWT_SECRET`, optionally
|
||
|
|
`GOMAIL_ADMIN_INIT_PASSWORD`) — secrets are deliberately never stored in
|
||
|
|
the YAML file itself. See `internal/config/config.go` for the full set of
|
||
|
|
available fields and their defaults; every field has a sensible default
|
||
|
|
except the two required secrets above.
|
||
|
|
|
||
|
|
## Building from source
|
||
|
|
|
||
|
|
```bash
|
||
|
|
go build -o gomail ./cmd/gomail
|
||
|
|
```
|
||
|
|
|
||
|
|
Requires CGO (for `mattn/go-sqlite3`) — a working C compiler must be
|
||
|
|
available. No other build-time dependencies beyond what `go mod
|
||
|
|
download` fetches.
|
||
|
|
|
||
|
|
## License
|
||
|
|
|
||
|
|
(Not yet specified — add your chosen license here before public release.)
|