Skip to content

sfeeser/FIRM-SMTP

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

FIRM-SMTP

SMTP Server for FIRM

Current Implementation:

main.go: Sets up the smtp.Server with configuration loaded from a config.json file (or FIRM_SMTP_CONFIG env var). It initializes the FIRMBackend, FirmAPIClient, and starts the banlist polling.

FIRMBackend: Implements smtp.Backend. Its NewSession method handles initial connection-level checks:

Concurrency Limits: Enforces max concurrent sessions (100) and max per-IP sessions (5) as hard-coded values from the FIRM-SMTP spec. It uses sync.Map and int32 for atomic session counting.

Banned IPs: Checks bannedIPs sync.Map (populated by the poller) and rejects connections from banned IPs.

FIRMSession: Implements smtp.Session.

AuthMechanisms / Auth: Explicitly returns unsupported errors, as FIRM-SMTP is receive-only and requires no authentication.

Mail: Performs the MAIL FROM command. It checks blockedEmails sync.Map to reject messages from blocked senders.

Rcpt: Performs the RCPT TO command. It's currently hard-coded to accept only firmserver@gmail.com as the recipient, matching the FIRM spec's example. It also enforces MaxRecipients.

Data: Reads the entire email message, parses headers (simplistically for now), extracts the subject and body, and crucially, uses the firmTokenRegex to find the FIRM-TOKEN. It then constructs the FirmInboundPayload and calls firmAPIClient.PostInbound.

Reset / Logout: Cleans up session state and decrements session counters appropriately.

FirmAPIClient: A dedicated client for interacting with the FIRM backend API (/inbound, /admin/banned_subnets, /admin/blocked_emails). It includes basic HTTP error handling.

Helper Functions:

parseEmailContent: A simplified function to extract headers, subject, and body from the raw email stream. This will need significant enhancement for robust, RFC-compliant email parsing.

setupTLSConfig / loadPEMFiles: Placeholders for TLS configuration. The ACME part is currently a comment, emphasizing that a dedicated ACME library (like CertMagic) would integrate here.

startBanlistPolling / fetchAndSetBanlists: Implements the periodic polling of FIRM's /admin/banned_subnets and /admin/blocked_emails endpoints every 60 seconds (configurable).

To Run This Code:

You would need to create a config.json file in the same directory as your Go code (or specify its path via the FIRM_SMTP_CONFIG environment variable).

Example config.json:

{
  "listen_address": "0.0.0.0:2525",
  "server_domain": "mail.firm.example.com",
  "max_message_bytes": 20000,
  "max_recipients": 1,
  "read_timeout": "60s",
  "write_timeout": "60s",
  "session_idle_timeout": "60s",
  "firm_api_url": "http://localhost:8080",
  "banlist_poll_interval": "60s",
  "tls": {
    "enabled": false,
    "use_acme": false,
    "cert_cache_dir": "",
    "acme_email": "",
    "acme_domain": "",
    "acme_agree_tos": false,
    "use_pem": false,
    "pem_cert_file": "",
    "pem_key_file": ""
  }
}

Next Steps / Areas for Improvement:

Robust Email Parsing: The parseEmailContent function is very basic. A production-ready server would need a more sophisticated email parsing library (e.g., net/mail or go-message/mail) to correctly handle multi-part MIME, various header encodings, and extract plain text bodies reliably.

SPF/DKIM/Domain Mismatch (at FIRM Backend): While the SMTP-FIRM spec says these don't block forwarding, the FIRM spec says the server (i.e., the backend) validates SPF/DKIM. The current PostInbound payload sends "none" for these. The FIRM-SMTP server could optionally perform these checks itself before forwarding, or rely entirely on the backend as currently implied. If the SMTP-FIRM server is to generate these, it would require additional libraries (e.g., miekg/dns for SPF, a DKIM library).

TLS Automation (setupTLSConfig): Integrate a real ACME client library (like github.com/caddyserver/certmagic or golang.org/x/crypto/acme/autocert) if tls.use_acme is true. This is a significant piece of functionality.

Error Handling and Logging: Enhance error logging to meet the "Structured JSON logs" requirement (e.g., using logrus or zap). Implement ErrorHandler for smtp.Server.

Subnet Banning: The current banlist polling simply stores IP strings. For subnets, it would need to parse CIDR notations (net.ParseCIDR) and implement a lookup logic to check if a client IP falls within a banned range.

Malformed SMTP Command Tracking: Implement the "After 3 violations, the connection is dropped with a 421 response" for malformed commands (protocolError in go-smtp already does this to some extent, but custom tracking might be needed if you want more granular control or logging specifically for this).

Runtime and CLI Behavior: Implement --version, --help, --check-cert, --config CLI flags using a library like spf13/cobra or urfave/cli. Implement the /healthz endpoint using a simple HTTP server alongside the SMTP server.

Graceful Shutdown: Ensure s.Shutdown() is called on SIGINT/SIGTERM signals.

This is a solid start for the FIRM-SMTP server based on your specifications and the go-smtp library.

About

SMTP Server for FIRM

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages