Skip to content

anondotli/mx

Repository files navigation

anon.li Email Server

A production-ready, secure, and privacy-focused Haraka email server for handling anonymous email forwarding.

Features

  • Anonymous Forwarding: Accepts emails for @anon.li aliases and custom domains, forwarding them to real recipients
  • Reply System: Handles replies via @reply.anon.li tokenized addresses to hide user identities
  • PGP Encryption: Automatically encrypts forwarded emails if the recipient has uploaded a public key
  • Privacy Protection: Removes tracking pixels, cleans HTML, and sanitizes dangerous content
  • SRS Support: Implements Sender Rewriting Scheme to maintain SPF compliance when forwarding
  • DKIM Signing: Signs all outbound mail to ensure deliverability
  • Rate Limiting: Redis-backed rate limiting to prevent abuse
  • Custom Domains: Supports user-verified custom domains with per-domain DKIM keys

Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  External       │     │    Haraka       │     │   Next.js API   │
│  Sender         │────▶│    MTA          │────▶│   (Prisma)      │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                               │                        │
                               ▼                        ▼
                        ┌─────────────────┐     ┌─────────────────┐
                        │    Redis        │     │   PostgreSQL    │
                        │  (Rate Limit)   │     │   (Aliases)     │
                        └─────────────────┘     └─────────────────┘

Installation

Prerequisites

  • Docker and Docker Compose
  • Node.js 20+ (for local development)
  • Redis
  • Access to the Next.js frontend API

Quick Start

  1. Clone and configure:

    cp .env.example .env
    # Edit .env with your MAIL_API_SECRET
  2. Generate DKIM keys:

    ./scripts/generate-dkim.sh anon.li
    ./scripts/generate-dkim.sh reply.anon.li

    The generated keys stay on the host under config/dkim/ and are mounted into the container at runtime. On startup, the entrypoint stages them into a private runtime directory owned by the haraka user, so host-side 0600 permissions remain safe and ARC/DKIM can still read them. They are intentionally not baked into the Docker image.

  3. Add DNS records (output from script):

    default._domainkey.anon.li. IN TXT "v=DKIM1; k=rsa; p=..."
    default._domainkey.reply.anon.li. IN TXT "v=DKIM1; k=rsa; p=..."
    
  4. Place TLS certificates:

    # Place your Let's Encrypt or other TLS certs
    cat privkey.pem cert.pem chain.pem > config/tls/anon.li.pem
  5. Start the server:

    docker compose up -d --build

    The container now validates MAIL_API_SECRET and the TLS bundle at startup, and warns when local DKIM keys are missing so deliverability issues are visible immediately.

Configuration

Environment Variables

Variable Description Required
MAIL_API_SECRET Shared with the website; used to derive the reply-token AES-256-GCM key Yes
DATABASE_URL Postgres connection string for the same database the website uses Yes
UPSTASH_REDIS_REST_URL Upstash Redis REST endpoint (rate limiting) No
UPSTASH_REDIS_REST_TOKEN Upstash Redis auth token No
DKIM_REQUIRED_DOMAINS Space-separated local DKIM domains to warn about at startup No
LOCAL_DKIM_DIR Internal runtime directory for staged DKIM keys No

Plugins

Plugin Purpose
tls STARTTLS enforcement
limit-upstash IP-based rate limiting (Redis + in-memory fallback)
mailauth Inbound SPF / DKIM / DMARC validation
rcpt_to.bounce Validates SRS bounce return-paths
rcpt_to.anonli Validates aliases (direct Postgres) + per-alias rate limiting
rcpt_to.reply Decodes reply-token addresses (in-process AES-GCM)
data.loop_detect Rejects mail loops
data.tracking_remove Strips tracking pixels / UTM params
data.spam_check Spam scoring
arc.sign ARC sealing for forwarded mail
dkim.custom Per-domain DKIM signing (local keys, falls back to Postgres)
queue.forward Main forwarding (SRS, PGP, reply rewriting, Postgres stats)

DNS Configuration

For anon.li

; MX Record
anon.li.                IN MX   10 mx.anon.li.

; SPF
anon.li.                IN TXT  "v=spf1 mx a:mx.anon.li -all"

; DKIM (generated by script)
default._domainkey.anon.li. IN TXT "v=DKIM1; k=rsa; p=..."

; DMARC
_dmarc.anon.li.         IN TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@anon.li"

For Custom Domains

Users must add:

  1. MX record pointing to mx.anon.li
  2. SPF record including include:_spf.anon.li
  3. DKIM record (provided in dashboard)
  4. Verification TXT record

Security Features

  • TLS 1.2+ required for all connections
  • Timing-safe API secret comparison
  • Input validation on all email addresses
  • Rate limiting per IP and globally
  • DKIM/SPF/DMARC for email authentication
  • PGP encryption for enhanced privacy
  • Tracking removal for privacy protection

Monitoring

Logs are output in JSON format for easy parsing:

docker compose logs -f haraka

Production notes:

  • DKIM private keys and TLS certificates are mounted at runtime; keep them out of the image build context.
  • DKIM host mounts are staged into LOCAL_DKIM_DIR on startup so the unprivileged haraka process can read them without loosening host file permissions.
  • ARC signing reuses the same key lookup path as DKIM signing: staged local key first, API fallback second.
  • Docker Compose requests an IPv6-enabled user-defined bridge network for Haraka.
  • Outbound delivery uses Haraka's default dual-stack behavior, which prefers IPv6 when the container has working IPv6 egress.
  • Docker daemon IPv6 settings in /etc/docker/daemon.json are optional for this setup. They are only needed if you want to customize Docker's IPv6 address pools or enable IPv6 on the default bridge network.

Health check endpoint:

nc -z localhost 25 && echo "OK"

Development

# Install dependencies
npm install

# Run locally (requires Haraka global install)
npm install -g Haraka
npm start

# Run tests
npm test

License

GNU Affero General Public License v3.0 (AGPL-3.0-only).

Because anon.li-mx is network-facing server software, the AGPL requires that anyone running a modified version on a publicly accessible server make the corresponding source code available to its users.

About

The source code for our aliasing mail server, powered by Haraka.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors