This is a AWS Lambda that implements SES/Workmail Catchalls.
Workmail has no capability for sending emails to non-registered users to a default inbox. This Lambda-based solution makes it possible.
The 'big idea' is to forward any emails that have an "unknown" email address to the selected "default" email address. This is accomplished using SES rules.
A rule is put just before the Workmail rule that creates an SNS message with the incoming email event and sends it to a custom Lambda.
The Lambda determines if the email is one of the already verified user emails. If it is already verified, it passes the event on to Workmail for delivery.
If the recipient is not a verified email, it will update the headers and forward it back to SES as a new email. First, it removes a number of headers (DKIM, etc...) which would interfere with forwarding the email, and sets the recipient to the defaultEmail
(specificed in a config object). Also, because SES will not allow unverified senders to deliver email, the TO
field is set to the adminEmail
(eg: admin@domain.awsapps.com). Finally, this new email is forwarded to SES using the SES.sendRawEmail()
. As a convenience, the Reply-To
field in the forwarded email is set to the original sender, so that when you click the "Reply" button in the email client, the To
field is set appropriately.
The trick here is that the Lambda will screen all incoming emails and look for any sent from the adminEmail
. It assumes that anything sent from the adminEmail
was previously forwarded and is destined for a defaultEmail
inbox. Since the To
field was previously reset to the defaultEmail
, Workmail dutifully deposits it there.
defaultEmail
and adminEmail
.async function handler(SNSEvent, context, callback) {
// return callback(null, { 'disposition': 'STOP_RULE' }); // uncomment this and DEPLOY to curtail an email storm
let sesMsg = JSON.parse(SNSEvent.Records[0].Sns.Message); log(JSON.stringify(sesMsg, null, 2));
let originalDestination = sesMsg.mail.destination[0]; log({ originalDestination });
agilefrontiers.com
and agilefrontiers.awsapps.com
greg@agilefrontiers.com
and admin@agilefrontiers.awsapps.com
ses-default-inbox
index.js
from the repo
config
object with your emailsdefaultEmail
to the default email user (eg: greg@agilefrontiers.com
)adminEmail
to the admin user (eg: admin@agilefrontiers.awsapps.com
)verifiedEmails
to the list of users you've already created in Workmail. Email to these users will pass unchanged to WorkmailFROM
email addresses you'd like filtered out - this is a bit of a spam filterconst config = {
defaultEmail: "greg@agilefrontiers.com",
adminEmail: "admin@agilefrontiers.awsapps.com",
verifiedEmails: [
"greg@agilefrontiers.com"
],
ignoreEmails: [
"devcybiko@gmail.com"
]
}
{
"Effect": "Allow",
"Action": "ses:SendRawEmail",
"Resource": "*"
},
<domain.name>
-sns (example: agilefrontiers-sns
)<domain.name>
-sns (example: agilefrontiers-sns
)agilefrontiers-sns
)"Service": "ses.amazonaws.com",
to the Statement.Principal JSON object{
"Version": "2008-10-17",
"Id": "__default_policy_ID",
"Statement": [
{
"Sid": "__default_statement_ID",
"Effect": "Allow",
"Principal": {
"AWS": "*",
"Service": "ses.amazonaws.com"
},
"Action": [
test.json
from the repo
Test Event Name
test
Response { "disposition": "CONTINUE" }
Function Logs START RequestId: aec50a9c-8080-4e30-9217-a8a7c5ea8c99 Version: $LATEST 2021-11-20T02:13:00.866Z aec50a9c-8080-4e30-9217-a8a7c5ea8c99 INFO { "mail": { "headers": [ { "name": "From", "value": "test@example.com admin@agilefrontiers.awsapps.com" } ], "destination": [ "test@example.com" ] }, "content": "email content" } 2021-11-20T02:13:00.872Z aec50a9c-8080-4e30-9217-a8a7c5ea8c99 INFO { originalDestination: 'test@example.com' } 2021-11-20T02:13:00.910Z aec50a9c-8080-4e30-9217-a8a7c5ea8c99 INFO { originalLabel: 'test@example.com', originalFrom: 'admin@agilefrontiers.awsapps.com' } 2021-11-20T02:13:00.910Z aec50a9c-8080-4e30-9217-a8a7c5ea8c99 INFO Previously forwarded to default greg@agilefrontiers.com from test@example.com by way of: admin@agilefrontiers.awsapps.com END RequestId: aec50a9c-8080-4e30-9217-a8a7c5ea8c99 REPORT RequestId: aec50a9c-8080-4e30-9217-a8a7c5ea8c99 Duration: 65.32 ms Billed Duration: 66 ms Memory Size: 128 MB Max Memory Used: 74 MB Init Duration: 420.40 ms
Request ID aec50a9c-8080-4e30-9217-a8a7c5ea8c99
## DEBUG / CONSOLE LOG
* You can mute the debugging info by comment out line 26 and uncommenting line 27.
```js
function dummy() { }
const log = console.log;
// const log = dummy;
Greg Smith