In part 1, we had a quick tour of S/MIME, looking at signing and encryption of our message streams across a range of mail clients. Part 2 took us through a simple command-line tool to sign and encrypt emails, then send them through SparkPost.
In this part, we’ll look at how the tool can be adapted to inject mail streams into on-premises platforms such as Port25 PowerMTA and Momentum.
OK – let’s get started!
1. Getting Started
Installing the tool, getting your keys etc. is exactly the same as before. When you’re using an on-premises email system such as PowerMTA or Momentum, you’re already responsible for setting up sending domains, DKIM keys etc. What we need to do now, is to provide some way of injecting the fully-formed S/MIME messages into your servers.
2. SMTP injection towards Port25 PowerMTA
PowerMTA supports various means of message injection, including a file “pickup” directory, SMTP, and an API. SMTP is the method used here.
To illustrate the simplest possible setup, we’ll install the S/MIME tools on the same server as PowerMTA. We inject messages to the listener, which is open by default on TCP port 25, accepting local traffic only.
export SMTP_HOST=localhost
(If you forget that step, you’ll see: “Environment var SMTP_HOST not set – stopping” when you try to run.)
We have the sender’s private key (steve@thetucks.com.pem) and the recipient’s public key (steve.tuck@sparkpost.com.crt) already present. The first few lines of the message file are:
To: SteveT <steve.tuck@sparkpost.com> From: Steve <steve@thetucks.com> Subject: This is a message created using HEML MIME-Version: 1.0 Content-Type: text/html; charset=utf-8 Content-Language: en-GB Content-Transfer-Encoding: 7bit
We send the message with:
./sparkpostSMIME.py tests/fancy-HTML-to-smt.eml --sign --encrypt --send_smtp
We see:
Opened SMTP connection (plain) to localhost, port 25, user="", password="" Sending tests/fancy-HTML-to-smt.eml From: Steve <steve@thetucks.com> To: SteveT <steve.tuck@sparkpost.com> OK - in 0.028 seconds
The message arrives quickly in the inbox and reports in Mac Mail as signed and encrypted.
Bonus feature: DKIM with PowerMTA
DKIM is quite easy to configure and coexists happily with S/MIME. The steps are:
Use the PowerMTA DKIM Wizard site to create sending domain private key (in my case, mypmta.thetucks.com.pem) and public DNS TXT record contents.
Set up the DNS TXT record, with a chosen selector. For example, I used selector pmta201811. Valid selector characters are defined here.
Put mypmta.thetucks.com.pem file on the server in directory /etc/pmta .
Add the following to my /etc/pmta/config and restart the pmta service. (Here, these directives are written at global scope; on a production system, you might prefer to add them under a virtual-mta instead.)
host-name thetucks.com domain-key pmta201811,*,/etc/pmta/mypmta.thetucks.com.pem <domain *> dkim-sign yes </domain>
The DNS record checks out OK via MX Toolbox, and DKIM is now active.
3. SMTP Injection Towards Momentum
Momentum supports various means of message injection, including API and SMTP. SMTP is the method used here, towards a host already running Momentum. We’ll leave its configuration unchanged, as it already has a capability to accept incoming injections from other approved hosts.
This is a smaller version of a production setup, where “generation” nodes and MTA nodes are separate, yet closely coupled via a private VLAN and load-balancers, carrying internal SMTP injection traffic.
The S/MIME tools are installed as before, and we will inject messages to the address of the SMTP host (MTA):
export SMTP_HOST=xx.xx.xx.xx # set your own MTA / VIP address here
As before, we have the sender’s private key (steve@thetucks.com.pem) and the recipient’s public key (steve.tuck@sparkpost.com.crt) already present on the “generation” node. The first few lines of the message file match these addresses.
We send the message from the “generation” node with exactly the same command as before, and the message shows up in the inbox.
./sparkpostSMIME.py tests/fancy-HTML-to-smt.eml --sign --encrypt --send_smtp
As you’d expect, S/MIME also happily coexists with Momentum’s DKIM signing.
4. SMTP injection towards SparkPost
In part 2 we used the SparkPost transmissions REST API to inject messages. Of course, it’s also possible to inject messages into SparkPost using SMTP. We set the environment variables like this:
export SMTP_PASSWORD=<<YOUR API KEY HERE>> export SMTP_HOST=smtp.sparkpostmail.com export SMTP_USER=SMTP_Injection export SMTP_PORT=587
If you’re using SparkPost EU-hosted service then set SMTP_HOST as smtp.eu.sparkpostmail.com.
(See here for more options – for example you can inject on port 2525 rather than 587.)
The output below shows STARTTLS is used, along with the username and password.
./sparkpostSMIME.py tests/fancy-HTML-to-smt.eml --sign --encrypt --send_smtp
You’ll see:
Opened SMTP connection (STARTTLS) to smtp.sparkpostmail.com, port 587, user="SMTP_Injection", password="****************************************" Sending tests/fancy-HTML-to-smt.eml From: Steve <steve@thetucks.com> To: SteveT <steve.tuck@sparkpost.com> OK - in 0.057 seconds
The password is printed with substitute *** characters, so you’re not compromising the privacy of your key if someone’s looking over your shoulder.
Securing Your Credentials
Note that environment variables could be set up in a shell script file or similar, to save retyping. If you do, please look after your passwords/API keys by limiting access to that file to yourself only. For example, if your credentials setup file is named my_envs.sh, then run:
chmod 0700 my_envs.sh
SMTP-Related Warnings You May See
SparkPost’s SMTP injection is pretty strict, as you would expect from a public service. If you haven’t set the SMTP port number, you’ll see a warning:
{'bob.lumreeker@gmail.com': (550, b'5.7.1 relaying denied')}
If you haven’t set the SMTP username or haven’t set the password, you’ll see:
(530, b'5.7.1 Authorization required. Ref. https://developers.sparkpost.com/api/index#header-smtp-relay-endpoints', 'steve@thetucks.com')
These error messages are simply reported as-is from the Python SMTP library, hence the formatting.
Which one’s faster – SMTP or API?
Frankly, S/MIME is unlikely to be a high-volume use-case, but having the same tool with two output options was just asking for us to run a race!
The “Avocado” email test file used here is approx 19KB. Repeating the tests 10 times via a bash loop showed the average times to be similar for SMTP and API, around 60 milliseconds per message, which is pretty fast. In this case, we injected from a medium EC2 instance in the same hosting region as SparkPost.com, which is a good way to keep network round-trip times low.
Repeating this with a larger test file (577KB), the API took roughly 200 milliseconds, while SMTP took 280 milliseconds per message – still impressive for a file size 30x larger. Of course, your mileage may vary depending on location, internet congestion etc, but performance is unlikely to be an issue.
If you really need maximum performance, a good starting point would be to launch a set number of concurrent injection processes/sessions as per our transmission best practices recommendations – e.g. from a supervisor task.
Summing up …
We’ve seen how the SparkPost API-based tool used in Part 2 is updated to support SMTP injection to support on-premises MTAs such as Port25 PowerMTA and Momentum in a variety of configurations, as well as with SparkPost.
That’s it for now! Happy sending.