SESWatcher : how to prevent Amazon SES account from being suspended

Introdution

At this moment, AWS SES service is the best affordable price and high-quality service to sending (and receiving) programmatic emails. But, one day your account could be suspended without notice, and that takes a big effort of requesting support to get it back to work.

So it’s good to prevent that siatuation happens by watching your Bounces Rate and Complaints Rate. One way to resolve problem when your rates being too-high than allowance, is send a big amount of dummy emails to your blackhole email address.

Example

These is your send statistics :

  • Deliveries : 2000
  • Bounces : 180 ==> Bounces Rate : 180 / 2000 = 9%
  • Complaints : 30 ==> Complaints Rate : 30 / 2000 = 1.5%

Meaning :

  • Bounces : when your email didn’t send to existed email address (maybe your user type wrong or input fake one), we can pre-validate email before sending by using some email validator library or services like fast-email-validator
  • Complaints : when your receiver mark your email as SPAM :( Poor you !!!

Solution

AWS SES rules :

  • Bounces Rate : 5% => So you have to send more (180 / 4%) - 2000 = 2500 emails to reduce Bounces Rate to 4%
  • Complaints Rate : 0.1% => So you have to send more (30 / 0.07%) - 2000 = 40857 emails to reduce Complaints Rate to 0.07%

==> Number emails send = max(2500, 40857) = 40857 emails

SPAM YOURSELF 40857 EMAILS !!! 😂

SESWatcher - Your SES watchman

It’s open source software at https://github.com/khanhicetea/seswatcher (My first PyPi package :D)

Installation

1
$ pip install seswatcher

Usage

  1. Step 1 : Get AWS Credential User within AmazonSESFullAccess policy
  2. Step 2 : Verify sender email in AWS SES
  3. Step 3 : Get a blackhole email address which receives un-important emails.
  4. Step 4 : Create a hourly cronjob that runs seswatcher
1
$ seswatcher [OPTIONS] ACCESS_KEY SECRET_KEY FROM_EMAIL TO_EMAIL

Get help :

1
2
3
4
5
6
7
$ seswatcher --help
Usage: seswatcher [OPTIONS] ACCESS_KEY SECRET_KEY FROM_EMAIL TO_EMAIL

Options:
--region TEXT AWS SES Region (default is us-east-1)
--interval INTEGER Timer interval
--help Show this message and exit.

Getting AWS, GCP and Azure IP ranges

TL;DR

This is my gist of result

https://gist.github.com/khanhicetea/c6cc74b99ab336d58c2da7929c2de709

Introdution

These are 3 biggest cloud providers (from Amazon, Google and Microsoft). This is the way to get their IP ranges (IPv4).

Amazon Web Service

AWS update its IP ranges on this link : https://ip-ranges.amazonaws.com/ip-ranges.json

1
2
3
4
# download ip-ranges.json file
curl -o aws.json https://ip-ranges.amazonaws.com/ip-ranges.json
# parse ip range with grep tool
grep -o -E '\d+\.\d+\.\d+\.\d+/\d+' aws.json > aws.txt

The result are in aws.txt file.

Google Cloud Platform

This script from LinuxFreelancer article

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# array to hold list of IP blocks
ALL_IPS=()
NAME_SERVER='8.8.8.8'
txt_records=$(dig @${NAME_SERVER} _cloud-netblocks.googleusercontent.com txt +short)
txt_rr_only=$(echo $txt_records | grep -oP 'include:\S+' | sed 's/include://g')
[[ -z ${txt_rr_only} ]] && { echo 'No TXT dns record found.'; exit 1;}
## unpack txt records to get IPv4 ranges
for rr in ${txt_rr_only}; do
new_ips=$(dig @${NAME_SERVER} $rr txt +short | grep -o -P '(\d+\.){3}\d+/\d+')
for item in ${new_ips}; do
# add space separator between ip blocks
item=" ${item} "
ALL_IPS+=${item}
done
done

# sort IPs then output result file
echo ${ALL_IPS[@]} | sed 's/ /\n/g' | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 > gcp.txt

Microsoft Azure

This script download the file updated at Nov 06 2017, the latest file is here : https://www.microsoft.com/en-us/download/details.aspx?id=41653

1
2
3
4
# download ip-ranges.json file
curl -o azure.xml https://download.microsoft.com/download/0/1/8/018E208D-54F8-44CD-AA26-CD7BC9524A8C/PublicIPs_20171106.xml
# parse ip range with grep tool
grep -o -E '\d+\.\d+\.\d+\.\d+/\d+' azure.xml > azure.txt

Play it all !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash

# AWS
curl -o aws.json https://ip-ranges.amazonaws.com/ip-ranges.json
grep -oP '\d+\.\d+\.\d+\.\d+/\d+' aws.json > aws.txt
rm -f aws.json

# GCP
ALL_IPS=()
NAME_SERVER='8.8.8.8'
txt_records=$(dig @${NAME_SERVER} _cloud-netblocks.googleusercontent.com txt +short)
txt_rr_only=$(echo $txt_records | grep -oP 'include:\S+' | sed 's/include://g')
[[ -z ${txt_rr_only} ]] && { echo 'No TXT dns record found.'; exit 1;}
for rr in ${txt_rr_only}; do
new_ips=$(dig @${NAME_SERVER} $rr txt +short | grep -o -P '(\d+\.){3}\d+/\d+')
for item in ${new_ips}; do
item=" ${item} "
ALL_IPS+=${item}
done
done

# sort IPs then output result file
echo ${ALL_IPS[@]} | sed 's/ /\n/g' | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 > gcp.txt

# Azure
curl -o azure.xml https://download.microsoft.com/download/0/1/8/018E208D-54F8-44CD-AA26-CD7BC9524A8C/PublicIPs_20171106.xml
grep -oP '\d+\.\d+\.\d+\.\d+/\d+' azure.xml > azure.txt
rm -f azure.xml

MacOS issue

If you’re running this script on MacOS, try to replace grep -P to grep -E

Use Cases ???

This list is very useful for some usecases, I will post them later ;)