Edge Services

The term edge is not necessarily common among other MTAs. It was adapted loosely by slimta from the Edge Transport Server Role in Microsoft Exchange, but the name is about where the similarities end.

In slimta, an edge service are those that are listening for new messages entering the system. The protocol does not matter, an edge service produces an Envelope object and hands it off to the next stage of delivery.

Edge services (usually) send their requests to and receive their responses from a queue service. That means the response delivered to the client does not signify whether the message was successfully delivered, but rather that the queue service has taken responsibility for its delivery.

SMTP Edge Services

Traditionally, email MTAs receive messages from other MTAs or user email clients using the SMTP protocol (RFC 5321, RFC 2821, RFC 821). An SMTP session delivering a message from a client to a server might look like this, with server (edge) replies back to the client bolded:

220 server.example.com ESMTP Mail Gateway
EHLO client.example.com
250-Hello client.example.com
250-8BITMIME
250-PIPELINING
250-STARTTLS
250 ENHANCEDSTATUSCODES
MAIL FROM:<sender@client.example.com>
250 2.1.0 <sender@client.example.com> Ok
RCPT TO:<recipient@server.example.com>
250 2.1.5 <recipient@server.example.com> Ok
DATA
354 Start mail input; end with <CRLF>.<CRLF>
... email data ...
.
250 2.6.0 Message queued as e64820855322425486542d1bd59ba6cd
QUIT
221 2.0.0 Bye

SMTP edge services are somewhat unique in that they control not just the receipt of and response to the message, but also a lot of interim requests. A server may respond negatively to any command sent to it by the client, and often, crucial policies are implemented that way. For example, if you are sure you don’t want to accept messages from a certain IP (e.g. known spammers) you would want to limit the amount of memory and CPU cycles they consume by rejecting before the before DATA.

Creating SMTP Edge Objects

from slimta.edge.smtp import SmtpEdge

smtp = SmtpEdge(('', 25), queue)
smtp.start()

Authentication with SMTP Edge

There are a few steps involved with adding authentication to the edge. The first is exposing the AUTH SMTP extensions to clients by passing auth=True to the SmtpEdge constructor.

By default, any credentials will successfully authenticate, so the next step is adding a SmtpValidators class that implements the handle_auth method:

from slimta.edge.smtp import SmtpValidators
from slimta.smtp.reply import invalid_credentials

class MyValidators(SmtpValidators):
    def handle_auth(self, reply, creds):
        try:
            secret = valid_creds[creds.authcid]
            assert creds.check_secret(secret)
        except (KeyError, AssertionError):
            reply.copy(invalid_credentials)

Finally, we’ll want to disallow MAIL FROM commands until there has been a successful authentication:

def handle_mail(self, reply, sender, params):
    if not self.session.auth:
        reply.code = '550'
        reply.message = '5.7.1 Sender not allowed'

If you have not already, you’ll need to attach your validators to your SmtpEdge by passing in validator_class=MyValidators to its constructor.

Sender Policy Framework (SPF)

SPF is a tool that, at its most basic, allows domains to explicitly list the outbound hosts/IPs from which they are legitimately sending mail. Domains may set DNS records of special formats that email receivers query and compare against the information they know about the sending client.

To set it up, you need to create rules for the different types of results. You do this by creating a EnforceSpf object and calling set_enforcement() for each different results you want to act upon. These results are:

So we create our rules:

spf = EnforceSpf()
spf.set_enforcement('fail', match_message='5.7.1 Access denied: {reason}')
spf.set_enforcement('softfail', match_code='250', match_message='2.0.0 Ok; {reason}')

And then in our SmtpValidators class, use the check() decorator:

@spf.check
def handle_mail(self, reply, sender, params):
    pass

Proxy Protocol with SMTP Edge

New in version 3.0.0.

When using a TCP proxy in front of slimta, it is common to lose information about the original source address of the connection. The PROXY protocol adds a header to each connection where the client posts information about the request’s original connection information. To use the proxy protocol:

from slimta.edge.smtp import SmtpEdge
from slimta.util.proxyproto import ProxyProtocolV1

class MyEdge(ProxyProtocolV1, SmtpEdge):
    pass

smtp = MyEdge(('', 25), queue)
smtp.start()

Please note, this will cause strange behavior if you are not running slimta behind a proxy that is configured to use PROXY protocol version 1.

HTTP Edge Services

A very common desire these days is to be able to submit emails to an MTA using HTTP or HTTPS protocols, due to the simplicity and ubiquity of these protocols. Unfortunately, no standards have been settled on, so HTTP mail reception and delivery can only really be used within controlled environments.

An HTTP session delivering a message from a client to a server might look like this, with server (edge) replies back to the client bolded:

POST / HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost:8080
Accept: */*
Content-Type: message/rfc822
X-Envelope-Sender: c2VuZGVyQGV4YW1wbGUuY29t
X-Envelope-Recipient: cmVjaXBpZW50QGV4YW1wbGUuY29t
Content-Length: 101

From: sender@example.com
To: recipient@example.com
Subject: HTTP mail delivery

Test message!

HTTP/1.1 200 OK
X-Smtp-Reply: 250; message="2.6.0 Message accepted for delivery"
Date: Mon, 29 Jul 2013 20:11:55 GMT
Content-Length: 0

Creating HTTP Edge Objects

from slimta.edge.wsgi import WsgiEdge

wsgi = WsgiEdge(queue)
http = wsgi.build_server(('', 8025))
http.start()