{"id":20256217,"url":"https://github.com/devcybiko/ses-catchall","last_synced_at":"2025-04-11T00:22:16.356Z","repository":{"id":99128361,"uuid":"429994942","full_name":"devcybiko/ses-catchall","owner":"devcybiko","description":null,"archived":false,"fork":false,"pushed_at":"2024-05-10T16:02:07.000Z","size":18,"stargazers_count":19,"open_issues_count":3,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-04T16:44:13.595Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devcybiko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-20T03:06:56.000Z","updated_at":"2025-04-04T14:57:57.000Z","dependencies_parsed_at":"2023-06-03T23:30:29.177Z","dependency_job_id":null,"html_url":"https://github.com/devcybiko/ses-catchall","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fses-catchall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fses-catchall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fses-catchall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devcybiko%2Fses-catchall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devcybiko","download_url":"https://codeload.github.com/devcybiko/ses-catchall/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248318929,"owners_count":21083751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T10:45:53.534Z","updated_at":"2025-04-11T00:22:16.338Z","avatar_url":"https://github.com/devcybiko.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ses-catchall\n\nThis is a AWS Lambda that implements SES/Workmail Catchalls.\n\nWorkmail has no capability for sending emails to non-registered users to a default inbox. This Lambda-based solution makes it possible.\n\n## THEORY\n\nThe '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.\n\nA 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. \n\nThe 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.\n\nIf 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.\n\nThe 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.\n\n## LIMITATIONS\n* There is a 10MB limit on attachments in SES. Despite Workmail's 25MB limit, SES will bounce any incoming emails with more that 10MB\n* There may also be some issues with multiple attachments.\n* **UPDATE** The SNS message queue has a limit of 256KB which severely limits the size of attachments\n\n## WARNINGS\n* Be careful to specify the correct `defaultEmail` and `adminEmail`.\n* If you mistype one of them, you could create a Lambda which infinitely replicates and forwards the same email.\n* If this happens - uncomment line 68 and DEPLOY the Lambda immediately. It will take some time, but the email storm will eventually subside\n\n```js\nasync function handler(SNSEvent, context, callback) {\n    // return callback(null, { 'disposition': 'STOP_RULE' }); // uncomment this and DEPLOY to curtail an email storm\n    let sesMsg = JSON.parse(SNSEvent.Records[0].Sns.Message); log(JSON.stringify(sesMsg, null, 2));\n    let originalDestination = sesMsg.mail.destination[0]; log({ originalDestination });\n```\n\n## WORKMAIL SETUP\n* It is expected that you've set up Workmail as described: https://docs.aws.amazon.com/workmail/latest/adminguide/howto-start.html\n* Set up a domain and a awsapps.com domain\n  * example: `agilefrontiers.com` and `agilefrontiers.awsapps.com`\n* Set up a default user in the main domain and an admin user on the awsapps.com domain\n  * example: `greg@agilefrontiers.com` and `admin@agilefrontiers.awsapps.com`\n\n## LAMBDA SETUP\n1. In the LAMBDA CONSOLE...\n1. Click: CREATE FUNCTION\n2. \"Author From Scratch\"\n3. Function Name: `ses-default-inbox`\n4. Runtime: Node.js 14.x\n5. Architecture: x86_64\n6. Click: CREATE FUNCTION\n7. In the code, replace the index.js with the `index.js` from the repo\n   1. Update the `config` object with your emails\n   2. set `defaultEmail` to the default email user (eg: `greg@agilefrontiers.com`)\n   3. set `adminEmail` to the admin user (eg: `admin@agilefrontiers.awsapps.com`)\n   4. set `verifiedEmails` to the list of users you've already created in Workmail. Email to these users will pass unchanged to Workmail\n   5. Optionally add a list of `FROM` email addresses you'd like filtered out - this is a bit of a spam filter\n\n```\nconst config = {\n    defaultEmail: \"greg@agilefrontiers.com\",\n    adminEmail: \"admin@agilefrontiers.awsapps.com\",\n    verifiedEmails: [\n        \"greg@agilefrontiers.com\"\n    ],\n    ignoreEmails: [\n        \"devcybiko@gmail.com\"\n    ]\n}\n```\n8. In the Configuration -\u003e Permissions, click on the role name\n   1. Expand the AWSLambdaBasicExecutionRole...\n   2. Click Edit Policy\n   3. Click JSON\n   4. Add the following policy to the JSON\n   5. Click REVIEW POLICY\n   6. Click SAVE CHANGES\n\n```\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": \"ses:SendRawEmail\",\n            \"Resource\": \"*\"\n        },\n```\n\n\n## SES SETUP\n* in the SES HOME console...\n* Click Email receiving...\n* Click VIEW ACTIVE RULE SET\n* Click on the Rule name for your Workmail\n  * example: m-4dfb6326f56e4a06a6e...\n* Under actions, create a new action\n  * Select \"Add Action -\u003e Publish to Amazon SNS topic\"\n  * Select SNS -\u003e Encoding UTF-8\n  * Select SNS topic -\u003e Create a SNS Topic\n    * Topic Name: `\u003cdomain.name\u003e`-sns (example: `agilefrontiers-sns`)\n    * Display Name: `\u003cdomain.name\u003e`-sns (example: `agilefrontiers-sns`)\n    * Click CREATE TOPIC\n    * Click the round \"UP ARROW\" and move the rule above the \"WORKMAIL\" rule\n  * Click SAVE RULE\n\n## SNS SETUP\n* in the SNS console...\n* Click TOPICS\n* Click your new SNS topic (eg: `agilefrontiers-sns`)\n* Click CREATE SUBSCRIPTION\n  * Select Protocol -\u003e AWS LAMBDA\n  * Select Endpoint -\u003e ....ses-default-inbox\n  * Click CREATE SUBSCRIPTION\n* Click on the EDIT button\n  * Under Access Policy...\n  * add `\"Service\": \"ses.amazonaws.com\",` to the Statement.Principal JSON object\n  * Click SAVE CHANGES\n\n```\n{\n  \"Version\": \"2008-10-17\",\n  \"Id\": \"__default_policy_ID\",\n  \"Statement\": [\n    {\n      \"Sid\": \"__default_statement_ID\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"*\",\n        \"Service\": \"ses.amazonaws.com\"\n      },\n      \"Action\": [\n```\n\n## TEST IT\n\n9. Back in the LAMBDA console... \n   1.  Click the TEST Down-arrow and select CONFIGURE TEST EVENT\n   2.  replace the JSON with the contents of `test.json` from the repo\n   3.  Click SAVE\n   4.  Click DEPLOY (see the green \"Changes Deployed\" indicator)\n   5.  Click TEST and see the results:\n```\nTest Event Name\ntest\n\nResponse\n{\n  \"disposition\": \"CONTINUE\"\n}\n\nFunction Logs\nSTART RequestId: aec50a9c-8080-4e30-9217-a8a7c5ea8c99 Version: $LATEST\n2021-11-20T02:13:00.866Z\taec50a9c-8080-4e30-9217-a8a7c5ea8c99\tINFO\t{\n  \"mail\": {\n    \"headers\": [\n      {\n        \"name\": \"From\",\n        \"value\": \"test@example.com \u003cadmin@agilefrontiers.awsapps.com\u003e\"\n      }\n    ],\n    \"destination\": [\n      \"test@example.com\"\n    ]\n  },\n  \"content\": \"email content\"\n}\n2021-11-20T02:13:00.872Z\taec50a9c-8080-4e30-9217-a8a7c5ea8c99\tINFO\t{ originalDestination: 'test@example.com' }\n2021-11-20T02:13:00.910Z\taec50a9c-8080-4e30-9217-a8a7c5ea8c99\tINFO\t{\n  originalLabel: 'test@example.com',\n  originalFrom: 'admin@agilefrontiers.awsapps.com'\n}\n2021-11-20T02:13:00.910Z\taec50a9c-8080-4e30-9217-a8a7c5ea8c99\tINFO\tPreviously forwarded to default greg@agilefrontiers.com from test@example.com by way of: admin@agilefrontiers.awsapps.com\nEND RequestId: aec50a9c-8080-4e30-9217-a8a7c5ea8c99\nREPORT RequestId: aec50a9c-8080-4e30-9217-a8a7c5ea8c99\tDuration: 65.32 ms\tBilled Duration: 66 ms\tMemory Size: 128 MB\tMax Memory Used: 74 MB\tInit Duration: 420.40 ms\n\nRequest ID\naec50a9c-8080-4e30-9217-a8a7c5ea8c99\n```\n\n## DEBUG / CONSOLE LOG\n* You can mute the debugging info by comment out line 26 and uncommenting line 27. \n\n```js\nfunction dummy() { }\nconst log = console.log;\n// const log = dummy;\n```\n\n## FINAL THOUGHTS\n\n* This is a bit of a hack. \n* It seems like a simple thing to implement and I'm surprised the AWS Workmail team hasn't implemented such a feature before now. \n* There may be some financial reasons since AWS charges $4.00 per month for each \"user\". \n* While you *can* create aliases, it is a bit of a headache to try and configure EVERY possible special-purpose email address as an alias.\n* Although, it may be an anti-spam feature to require all aliases to be pre-defined. \n* Best wishes, and Continued Success!\n\nGreg Smith\n- greg@agilefrontiers.com\n- 11/19/2021\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevcybiko%2Fses-catchall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevcybiko%2Fses-catchall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevcybiko%2Fses-catchall/lists"}