GithubHelp home page GithubHelp logo

0x4447 / 0x4447_product_s3_email Goto Github PK

View Code? Open in Web Editor NEW
3.0K 46.0 161.0 535 KB

๐Ÿ“ซ A serverless email server on AWS using S3 and SES

Home Page: https://0x4447.com

License: MIT License

0x4447 s3 s3-bucket aws aws-lambda aws-s3 aws-ses email email-sender free

0x4447_product_s3_email's Introduction

How to support the project

This solution is free for anyone to use and learn from. If youโ€™d like to see more projects like this one, consider using some of our products from the AWS Marketplace or share them with someone you might think would be interested in using them.

๐Ÿ“ซ S3 Email

This stack was created out of frustration due to the fact that to this day there's no easy way to have a full email server without the overhead of installing and configuring all servers needed to handle incoming and outgoing messages. We wanted something simple, with no interface and no server management, so we came up with S3-Email. This included AWS SES as our email server (receive and send) and S3 as our database and interface. Then we tied everything together with a bit of code via AWS Lambda.

The result is an unmanaged email server with unlimited email addresses that also offers the benefit of easily organizing messages by adding the + character to the email names. The + is converted to a /, which correlates to an object path in S3.

Endless email addresses

Once you add and confirm your domain with SES, you can put any string you want in front of the @, as long as it conforms to the email address standard. This means that you'll have endless email addresses at your disposal, and you'll be able to organize your life in a way never possible before. For example, you can give each service you sign up for its own special email:

Organizing with a +

With that said, you can organize your emails with the + character in this way:

When dealing with clients we came up with this folder structure:

For all sorts of alerts we like to group them like this

This groups all emails in the corresponding folder by replacing the + with a / character which creates a folder structure in S3. The possibilities are endless.

Basically, receive and send email with some skills.

DISCLAIMER!

This stack is available to anyone at no cost, but on an as-is basis. 0x4447 LLC is not responsible for damages or costs of any kind that may occur when you use the stack. You take full responsibility when you use it.

How to deploy

All you need to do to deploy this stack is click the button to the left and follow the instructions that CloudFormation provides in your AWS Dashboard. Alternatively you can download the CF file from here.

What will deploy?

S3-Email Diagram

The stack takes advantage of AWS S3, AWS SES, AWS Lambda, and the AWS Trigger system to tie everything together. You'll get:

  • 1x SES Rule Sets
  • 2x S3 Bucket
    • 1x for CodePipeline to store artifacts
    • 1x for the emails
  • 3x CodePipelines
  • 3x CodeBuilds
  • 3x Lambdas
  • 1x IAM Group

All project resources can be found here.

Auto deploy

The stack is set up in a such a way that any time new code is pushed to a selected branch, the CodePipeline picks up the change and updates the Lambda for you. These are the available branches:

  • master: the latest stable code
  • development: unstable code that we test in our test environment - we don't recommend that you use this branch

Manual work

Keep in mind that when you deploy, everything may not work right out of the box.

Confirm to SES that you own the domain

You have to add your domain and confirm that you own it. Follow these steps to do so:

  1. Go to the SES Dashboard.
  2. Click Domains on the left side menu.
  3. Click the blue Verify a New Domain button.
  4. Type your domain in the modal and select Generate DKIM Settings.
  5. The next window displays all information needed to configure your domain.
  6. Once finisheds, you'll wait some time for the domain to switch from a pending verification status to a verified status.

Enable SES Rule Sets

Deployment creates SES rule sets. This should be enabled by default on fresh AWS accounts, but on accounts where you already had some rules, this won't work. This behavior is a known bug by AWS in CloudFormation. Taking the following steps will enable the rule:

  1. Go to the SES Dashboard.
  2. Click Email receiving on the left side menu.
  3. Check 0x4447_S3_Email on the All rule sets tab.
  4. Click Set as active to activate the rule.

Attach user to the IAM Group

After the stack is deployed you'll get a IAM Group with a policy attached that will give a user using it the bare minimum to access to the S3-Bucket to read and create emails.

SES Limitations

There are two major limitations with SES:

  1. For security reasons, AWS defaults to 200 emails sent per 24 hour period at a rate of 1 email/second. If you need to send more than that, you'll need to ask AWS to increase your limit.
  2. By default, you can't send emails to unverified addresses. If you'd like to be able to send (as opposed to just receiving), you'll need to reach out to AWS to remove this limitation from your account.

How the stack works

Receiving email:

  1. An email comes to SES and and it gets stored in TMP S3 folder.
  2. S3 will trigger the Inbound Lambda Function which will organize the email based on the to, from and date fields. In addition to that, the Lambda will read the domain(s) added to SES, and will use that data to determine if the email should land in the Inbox or Sent folder. If the to fields contains the domain from SES, it goes to the Inbox, if not, it is assumed the email was sent out.
  3. The Inbox or Sent folder triggers another Lambda function that loads the raw email, converts it to a .html and .txt file, and stores it alongside the original message, while storing any attachments in the attachments.

In addition to this flow, when a new email comes in, a copy of it will be saved in the Today folder to show you which emails are new. The S3 bucket has a Life Cycle Policy and will delete any email older than one day from the Today folder. This way you always know what's new.

Sending email:

  1. Create a properly formatted JSON file (see the following section).
  2. Save the file to the path TMP/email_out/json. The file name and extension are irrelevant as long as the content is text and JSON formatted.
  3. This action triggers a Lambda that generates a raw email, sends it out using SES, and saves the raw message to the Sent folder.
  4. The Sent folder triggers another Lambda function that loads the raw email, converts it to a .html and .txt file, and stores it alongside the original message.

This flow was designed to take advantage of the S3 trigger system and break each action into a small Lambda.

How to create an email message

Create a custom JSON file, then upload it to the TMP/email_out/json folder (if you don't have the folder structure yet, set it up). The JSON structure should look like this:

{
	"from": "[email protected]",
	"to": "[email protected]",
	"subject": "From SES",
	"html": "Write a nice message to whoever you are sending this message to.",
	"text": "Write a nice message to whoever you are sending this message to."
}

Remember that the from field must contain the domain you added to SES. You won't be able to send emails from unverified domains.

Backup old emails

A bonus feature of this stack is the possibility to process manually uploaded emails. This is possible thanks to the event driven nature of the stack. Just upload the raw email in the TMP/email_in folder, and your emails will be processed automatically.

The only prerequisite is that the email file needs to be the raw representation of the email itself. For example: files ending in .eml are nothing more than a txt file with the raw content of the email.

Which means you can just upload those files straight up to the S3 bucket.

Pricing

All resources deployed via this stack will potentially cost you money. But you'd have to do the following for this to happen:

  • Invoke Lambdas over 1,000,000 times a month
  • Send and receive over 1000 emails a month
  • Perform over 10,000 Get and Put operations and over 2000 Delete operations in your S3 Bucket
  • Exceed 100 build minutes on CodeBuild
  • $1 per active CodePipeline (must run at least once a month to be considered active)

The only payment you'll encounter from Day One is an S3 storage fee for emails and CodePipeline artifacts.

How to work with this project

When you want to deploy the stack, the only file you should be interested in is the CloudFormation.json file. If you'd like to modify the stack, we recommend that you use the Grapes framework, which was designed to make it easier to work with the CloudFormation file. If you'd like to keep your sanity, never edit the main CF file ๐Ÿคช.

The End

If you enjoyed this project, please consider giving it a ๐ŸŒŸ. And check out our 0x4447 GitHub account, where you'll find additional resources you might find useful or interesting.

Sponsor ๐ŸŽŠ

This project is brought to you by 0x4447 LLC, a software company specializing in building custom solutions on top of AWS. Follow this link to learn more: https://0x4447.com. Alternatively, send an email to [email protected].

0x4447_product_s3_email's People

Contributors

davidgatti avatar lynnepatterson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

0x4447_product_s3_email's Issues

Emails in which one is CCed/BCCed get treated as 'Sent'.

Error Symptoms

Emails in which one is CCed/BCCed get treated as 'Sent'.

Emails in which 'To' contains other addresses before a recognised address gets treated as 'Sent'

Notes

I decided to spin this up and have a play. It's wonderful!

I actually found it via a thread on reddit. One comment asked about how bcc and cc were handled, and you specified that you weren't sure. I thought I'd test that. The poster's concerns were astute: items that you are cced or bcced in get treated as 'Sent' mail.

I wish I could have a crack at fixing this and submit a pull request, but I have only the vaguest idea of how this all works (the plan is to use your brilliant product to learn). If you felt like it was worth your time to point me to the most relevant documentation, I would be more than willing to have a go. I think the thing I'm having most difficulty with understanding is how the cloudformation file would change if I were to play with the forks of your repos. Is your 'grapes' consolidation program included in the codepipeline autodeploy feature?

Otherwise, do let me know if there's any outputs I can provide that would be useful for you to troubleshoot the issue.

New feature: attachment for sent email

My Problem

Ability to send attachments to the email_out/json messages

Solution

the json for an outbound email could be arranged as such

{
  "to": "[email protected]"
  "from": "[email protected]"
  ...
  "attachments" : [
     "attachment/baz.pdf"
  ]
}

and the baz.pdf file being located in email_out/json/attachment/baz.pdf

this may already be possible but I'm not aware.

New feature: Programmatic addresses

My Problem

On the same domain I want to have email address (e.g. forum-*@example.com) what is send directly to our system (e.g. Discourse forum). Emails send to this address should be immediatelly send by HTTP to an app endpoint (by a Lambda what will transform it to the right format).

Solution

At least document how to configure custom Lambda processing specific address prefix to not break anything for 0x4447_product_s3_email.

Ideally have support for it. E.g. config what will turn off processing for some prefix but runs specific lambda.

New feature: IMAP like WorkMail?

My Problem

Is it / will it ever be possible to connect an IMAP Client (like Thunderbird) to handle S3 email?

Solution

According to the documentation, there is a way to connect AWS SES to an email client, but I suppose there would be more to it, hence the question.

Not to mention how to go about authenticating users...

Regards, DAVI

New feature: Rate limiting/Error handling

My Problem

This is more a question that could turn into a feature request.
I'm wondering how you're handling errors, especially those from send rate limits.
Let's say I can send 100 emails/s with a daily limit of 100k right now and I am sending emails from a couple of services with peaks of 5k emails within a few seconds.

Solution

Ideally the system would react to the rate limit errors that will come from the ses.sendEmail command and have an exponential delay (or actually keep track of the quota and adjust as needed).

Template format error

Template format error: Unrecognized resource types: [AWS::SES::ReceiptRule, AWS::SES::ReceiptRuleSet]

Webhook could not be registered with GitHub.

Error Symptoms

Running stage master, getting the following error when trying to deploy the CloudFormation template.

Error Message

Webhook could not be registered with GitHub. Error cause: Not found [StatusCode: 404, Body: {"message":"Not Found","documentation_url":"https://developer.github.com/v3/repos/hooks/#create-a-hook"}] (Service: AWSCodePipeline; Status Code: 400; Error Code: ValidationException;

The stack won't deploy some times

There is a known bug that AWS is aware of in CF, which makes it that the stack won't deploy when you deal with S3 Events. To mitigate this problem the stack could be split in two.

Work around

Right now a work around that works 100% of the time is to remove from the S3Email resource this NotificationConfiguration.LambdaConfigurations, build the stack, deploy it, and then re-add the removed section, and perform and update on the stack.

This solution works, since we make sure that the Lambdas are in place before we add the events. For some this will work out of the box, it all depends how fast a Lambda will be deployed, and no, the DependsOn directive dose nothing - hence the bug.

nodejs 12 is no longer supported and ses does not have permissions to write to bucket

I updated the lambda node version to 14 and that part deploys successfully (Not sure if the code actually works or not on node14) then the next error i get is SES does not have permissions to write to the s3 bucket. It looks like the bucket policy is there so kind of stuck on that one. I'll try to troubleshoot some more but wanted to report it in case anything immediate stands out.

.

.

Increase memory for all Lambdas

My Problem

Actually after #16, I realized that all Lambdas will have to be 256MB to be able, to process and send email over 25MB.

Solution

Update the CF with a new memory limit.

nodejs8.10 is no longer supported

Error Symptoms

The stack can't be created because the lambda functions use a not supported version.

Error Message

The runtime parameter of nodejs8.10 is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs12.x) while creating or updating functions. (Service: AWSLambdaInternal; Status Code: 400; Error Code: InvalidParameterValueException; 

New feature: Switch from GitHub pulling to GitHub Webhooks

My Problem

Right now CodePipeline will check for changes on GitHub, and this some times exceed the limit that GitHub imposes.

Solution

Add a new resource: AWS::CodePipeline::Webhook, to configure CodePipeline to receive notifications when something changes on GitHub.

Update Grapes

Update the build spec to use the latest version of Grapes.

Question regarding spam

Hey David, I like the idea & project so thank you ๐Ÿ˜ƒ
Even tho I didn't have a chance to give it a try I can't to the assumption that spam could be an issue. Not only does it cost, it could potentially even be exploited to harm someone directly, right?
I mean spam is always an issue and someone has to pay for the processing etc yet many probably never think about it. At least I didn't until your project came along ๐Ÿ˜…

New feature: aws cdk

My Problem

The repo would be easier to maintain and to understand if it used code to specify the infra imo

Solution

  • drop some links to the cdk project just in case someone isn't familiar with it
  • collect some opinions from maintainers about it, have a conversation

links

getting started
docs
examples

examples are amazing

Lambda can't process big emails.

Error Symptoms

Big emails over 25MB will stop at the parse_the_email step with no error.

Error Message

No error message, the lambda just exits after this step normally.

New feature: Don't make the artifact bucket

My Problem

Right now the stack will make the CodePipeline Artifact bucket. This is not a good approach, people should be able to point to a preexisting bucket that they already use for CodePipline artifacts

Solution

Remove the S3 resource and use the bucket name provided in the deployment as.

Webhook don't work as expected

Error Symptoms

Make the CodePipeline conditional based on this issue #22. Since to have a webhook set you need to really own the repo, for AWS to edit its configuration, and to do so you have to fork them if you want to have auto deployment, if you don't want to fork them and don't need auto-deployment, then the webhook should be skipped.

[BUG] Deploy Failed (Bucket already exists)

Error Symptoms

AWS Cloud Formation deployment is failed and rollbacked
The error message is clear. but the problem is that I don't have the incoming bucket.
I tried a couple of times with other names, and It's the same always.

Error Message

//Incoming is the bucket name for EamilRestingPlace 
S3Email | CREATE_FAILED | incoming already exists

Param

AutoDeployParam No -
CodePipelineArtifactsParam configure-s3-mail -
EmailRestingPlace incoming -
GitHubAccountOwnerParam 0x4447 -
GitHubTokenParam **** -
StageParam master -

New feature: add support for Intelligent-Tiering

My Problem

Over time there will be lots of files that are accessed very rarely. It be a nice thing to add support for Intelligent-Tiering, to makes sure we keep the price of hosting the files as low as possible.

Solution

Update the Stack with the right life time policy.

New feature: Deny list

My Problem

Great project, love having the catch-all option for emails, works perfect.

Thinking about a deny list of some sort, so I can sign up [email protected] and then after using it and not needing it again, add that specific email to a deny list, so when the lambda executes for incoming mail, checks the address against the deny, and drops it instead of formatting into the S3 buckey

Solution

Seems like a simple DynamoDB table lookup would suffice, but I haven't thought too much into latency lookups, or costs of looking up each time

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.