Skip to content

SMTP Mail Guard (smtp) ​

The SMTP Mail Guard plugin inspects SMTP and ESMTP mail transfer streams (RFC 5321). It enforces recipient count limits per session, blocks disposable or blacklisted sender domains on MAIL FROM, intercepts authentication failures (535 Authentication credentials invalid), and provides transparent handover upon STARTTLS negotiation.


Capabilities & Threat Defense ​

Threat / Attack VectorDefense MechanismAction Taken
Credential Brute-Force & StuffingTracks 535 Authentication failed response codes; enforces connection close with 535 Too many failures on excess attempts (since v3.4.0)Abusive IP banned after max_auth_failures threshold; connection terminated immediately
Recipient Flooding / Directory HarvestCounts RCPT TO commands per sessionRejects excess recipients with 452 Too many recipients
Spam & Disposable SendersChecks MAIL FROM domain against blocked_sender_domainsRejects sender with 554 Sender domain rejected
STARTTLS HandoverDetects successful 220 Ready to start TLSTransparently transitions to bidirectional TLS pipe

Installation ​

Install the plugin via CLI or declaratively:

# Install using short nametcp-warden plugins install smtp# Or install via Git repository URLtcp-warden plugins install https://github.com/routewarden/plugins/smtp

Configuration Reference ​

Add an SMTP guard service to tcp-warden.yaml:

services:  smtp_guard:    listen: ":2525"    upstream: "127.0.0.1:25"    protocol: "smtp"    rate_limit:      connections_per_minute: 30      burst: 10    max_auth_failures: 3    ban_after_failures: 3    ban_duration: "2h"    plugin_config:      banner: "220 mail.example.com RouteWarden ESMTP Ready"      max_recipients: 10      blocked_sender_domains:        - "*.tempmail.com"        - "mailinator.com"        - "spam.org"      require_starttls: false

Configuration Options ​

FieldTypeDefaultDescription
listenstring":2525"Local proxy listen address and port.
upstreamstring"127.0.0.1:25"Target MTA (Postfix, Exim, Haraka) address and port.
protocolstring"smtp"Must be set to "smtp" or "mail".
max_auth_failuresint3Failed AUTH exchanges before the client IP is banned. Since v3.4.0 the connection is actively closed with a 535 Too many failures response when the cap is reached, preventing clients from retrying indefinitely.
ban_durationstring"2h"Duration of the automated IP ban ("30m", "2h", "24h").
plugin_config.bannerstring""Optional synthetic greeting banner returned to clients.
plugin_config.max_recipientsint10Max RCPT TO addresses allowed per single SMTP transaction.
plugin_config.blocked_sender_domains[]string[...]List of sender domains to reject with 554. Supports wildcards.

Network & Deployment Architecture ​

Option A: Kernel Firewall Redirection (nftables / iptables) ​

Keep Postfix listening on port 25 on loopback, and redirect incoming external connections from interface eth0:

# Redirect incoming external port 25 traffic to RouteWarden port 2525sudo nft add rule ip routewarden_nat prerouting iifname "eth0" tcp dport 25 redirect to :2525

Option B: Submission Port Protection (Port 587) ​

RouteWarden can also protect client submission ports (port 587):

# Optional: Protecting submission port 587services:  smtp_submission:    listen: ":5870"    upstream: "127.0.0.1:587"    protocol: "smtp"    max_auth_failures: 3    ban_after_failures: 3

Option C: Docker Compose Network Isolation ​

Run the mail transfer agent inside an isolated container network, exposing both SMTP (25) and Submission (587) services only through RouteWarden:

services:  routewarden:    image: ghcr.io/routewarden/tcp-warden:latest    ports:      - "25:2525"   # Map host SMTP port 25 to RouteWarden      - "587:5870"  # Map host Submission port 587 to RouteWarden      - "9091:9091" # Management API    volumes:      - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro    depends_on:      - mailserver  mailserver:    image: mailserver/docker-mailserver:latest    environment:      - OVERRIDE_HOSTNAME=mail.example.com    # Do NOT publish host ports (25/587); keep internal to bridge network

Testing & Verification ​

Verify session commands, test domain rejection, and test recipient throttling:

# Connect to SMTP service via netcat$ nc 127.0.0.1 2525220 mail.example.com ESMTP RouteWardenEHLO client.test250-mail.example.com250-8BITMIME250 OKMAIL FROM:<spammer@tempmail.com>554 5.7.1 Sender domain rejected by RouteWardenQUIT221 2.0.0 Bye

Released under the MIT License.