Bouncy Sink Part 2: Features, Configuration and Installation
In the first article, we covered how to set up a simple traffic generator app to send messages through your SparkPost account to the Bouncy Sink.
The Bouncy Sink is a place where you can send your test email to. Unlike the standard SparkPost sink, it simulates aspects of real-world email user and ISP behaviour, such as in-band and out-of-band bounces, FBLs, opens and clicks.
These are unofficial tools, rather than officially supported SparkPost features, but we’ve tried to make them solid, easy to use, and you can change them.
Why do you need this? Sending test traffic to real ISP domains (such as gmail.com, hotmail.com and so on) to fictional addresses that bounce, can quickly damage your email reputation.
** Note that all sent messages count towards your account billing **
The Bouncy Sink does some other useful things, which we’ll cover in this article. Then we’ll go through how you can deploy and customise one of your own.
Here are the design goals we set for the Bouncy Sink:
- Simulate real-world ISPs and users, with a range of responses determined by a statistical model
- Work with the traffic generator (described in the first article) and also work with any other source of SparkPost-originated traffic
- Avoid being a possible vector of backscatter spam, by enforcing checks on the message authenticity and source (particularly for FBL and OOB reponses)
- Provide access to message responses in your tests, particularly the ones that are tricky to cause (FBLs and OOBs)
Let’s recap the high-level view of message flow..The sink’s job is to receive and process mail, triggering events that will show up in your SparkPost account reporting.
The sink provides a statistical mix of responses on its main inbound domain, but wait, there’s more! Different response behaviours are available, through choice of recipient subdomain.
These domains are open for you to send to, but please see the opening remark that this is an unofficial, unsupported service. If you need to receive huge test volumes or have business-critical requirements, you should spin up your own sink (covered in a later section).
|Use Recipient Address|
|Accept quietly, without opens or firstname.lastname@example.org|
|Spam Complaint (ARF format FBL)||email@example.com|
|Accepted and opened at least firstname.lastname@example.org|
|Statistical mix of email@example.com|
* if that’s all you need, use the industrial-strength SparkPost sink instead.
The following sections go into more detail about how the sink actions work, and provide advice on setting up your own sink.
Port 25 PowerMTA is a high performance Message Transfer Agent that can be used for inbound as well as outbound mailing applications. The sink uses PowerMTA’s built-in facility to generate in-band bounces with both 4xx (tempfail) and 5xx (permfail) codes on a portion of traffic.
The codes are chosen at random from a special file blacklist.dat included in the project, and are typical of what a real ISP might send back. The 4xx codes will show up on SparkPost reporting as “delayed” mails, and they will be retried.
Actions on the mail content
For these actions, messages must have a valid DKIM signature. Messages from legitimate SparkPost sources pass this check.
Opens and Clicks
If an HTML mail part is present, the sink opens (“renders”) the mail by fetching any <img .. src=”..”> tags present in the received mail that are served by the SparkPost engagement tracker endpoint.
The sink clicks links by fetching <a .. href=”..”> tags present in the received mail that are served by the SparkPost engagement tracker endpoint.
The sink uses an OPTIONS http request to check the server type is actually SparkPost prior to issuing a GET. It does not actually follow the link redirect, or fetch the whole object.
FBLs (aka Spam Complaints)
The sink responds to some mails with an FBL back to SparkPost in ARF format. The reply is constructed as follows:
- Additional checks below must pass
- The FBL From: header address and MAIL FROM is the received mail To: header value, which must be present
- The FBL To: header address and RCPT TO is derived by looking up the received mail Return-Path: MX, according to the below table
- The X-MSFBL header is populated from the received mail
- The ARF-format FBL mail is attempted directly over SMTP to the relevant MX (simply choosing the first MX, if there is more than one)
- SMTP error responses are logged internally in the sink
The FBLs show up as spam_complaint events in SparkPost.
OOB bounce replies are constructed as follows:
- Additional checks below must pass
- The OOB From: header address and MAIL FROM is taken from the received mail To: header value
- The OOB To: header address and RCPT TO is taken from the received mail Return-Path: header
- The OOB mail is attempted directly over SMTP to the relevant MX (choosing the first MX if there is more than one)
- Endpoint error responses are logged
The OOBs show up as out_of_band events in SparkPost.
Additional checks on OOB and FBL actions
To reduce the effect of bad actors trying to use the sink to mount a backscatter spam attack, the direct OOB and FBL actions also require SPF to pass (so we know the originating IP is valid for the domain). Messages from legitimate SparkPost sources pass this check.
All OOB and FBL actions require the Return-Path: MX to resolve back to a known SparkPost endpoint.
Install and configure a Bouncy Sink on your own domain
To plumb in your own sink on your own domain(s), you’ll need a host capable of running PMTA 4.x, ideally in AWS or another environment where you can front the server with a load-balancer.
Your host requires
- TCP port 25 open to inbound traffic (plus port 80 for optional web monitoring)
- PMTA install + 4.x license
- python3 + pip
The PMTA blackhole service configured in the dummy-smtp- settings requires its own host IP address (it can’t be set in a virtual MTA). This serves half of the inbound mail.
The other half of the mail is presented to the regular PMTA listener address, which relays to a directory. These distinct services coexist on one PMTA instance on one host, with two NICs and IP addresses.
To enable a single mail stream to experience the full range of sink responses, the main inbound domain is mapped to an AWS Elastic Load Balancer (ELB). ELB sharing percentage is non-configurable, so the .ini file is set to account for 50% / 50% between the listeners. Specific domains map to the regular PMTA listener, allowing direct access to those sink features.
Looking in the PMTA config file, you’ll see the usual PMTA listener serves eth0, and the special dummy-smtp listener serves eth1.
The specific action subdomains fbl, oob, openclick, and accept map to Elastic IP 1, which also handles all host outbound connections:
- FBL & OOB delivery
- http(s) opens & clicks.
Instructions on how to set up PMTA and the sink app are given in the Bouncy Sink Github repo, as well as information on the app log file format, script parameters, consume-mail.ini configuration (which sets the statistical model), and app internals.
Monitoring the sink
The sink uses redis to log some traffic counters (i.e. only anonymous data). If you run the webReporter script using Gunicorn , you see a basic stats reporting page. You can keep this private by serving from a non-public port number (e.g. 8888) and opening a tunnel via ssh – instructions in the README here.