AWS SES with Meteor - amazon-web-services

I'm trying to out a Meteor package to interface with AWS SES called tarang:email-ses built by #Akshat.
I'm on Meteor #1.* running on a AWS EC2 instance. When I a test run with the code below, no email was sent out.
Meteor Code
I've set up the AWS access key ID and secret access key and use it here:
Meteor.startup(function () {
Email.configSES({
AWSAccessKeyID: 'access-key',
AWSSecretKey: 'secret-key'
});
});
I've also verified my emails and domain. Here I make sure I'm sending from my verified sender SES address:
Accounts.emailTemplates.from = 'Domain Name <support#domain-name.com>';
Then in a Meteor method, I create a new user and send and enrollment email like so (this works if I deploy to meteor.com, without the Accounts.emailTemplates.from of course):
if (Meteor.user() && adminUser(this.userId)) {
var accountId = Accounts.createUser({
'username': doc.name,
'email': doc.email
});
Accounts.sendEnrollmentEmail(accountId);
}
Questions
Is the code to set things up for the email-ses package correct?
I thought this package abstracted out the Amazon SES API to Send Email (and allowed for native Meteor email calls). Is there a requirement to set up SMTP on AWS?

To send email in Meteor the easiest thing to do is to set the SMTP server and username information using process.env.MAIL_URL variable. The Accounts and Email packages will automatically use it to access the SMTP server whenever you send a message.
To enable SMTP access with AWS SES:
Log into the AWS Management Console
Select the appropriate region where you set up SES.
Select SES from the list of Application Services
Click the SMTP Settings menu item in the left menu
Click the Create My SMTP Credentials button
Use the default IAM username that's provided. This is a special IAM user that will only have access to the SMTP server. You shouldn't use your main IAM username/password when accessing the SMTP server.
Make sure to note the SMTP server name. You'll need it later.
After the account is created download the credentials.
Now that you have an account set up, simply copy and paste the following line to the top of your Meteor startup file and replace the username and password from the values in the credential file that you just downloaded and the server name that was provided.
process.env.MAIL_URL = 'smtp://username:password#smtp-server-name:465';
Please note that both the sending and receiving email addresses must be verified in the AWS SES management console if you haven't received production access yet. If you don't do this Meteor will throw an error in the console and the message will not be sent.

#brian-shamblen does not answer the question directly, but rather proposes an alternative solution to send emails.
You do not have to set up SMTP on AWS, nor you need to set environment variable inside your project process.env.MAIL_URL.
Log into the AWS Management Console
Go to IAM Service (!!not SES)
Create new IAM User
Save credentials (AWSAccessKeyID and AWSSecretKey). These are the credentials you'll use in Email.configSES({...})
Create inline Policy for this user:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": "*"
}
]
}
Make sure to use verified domail in Accounts.emailTemplates.from. In order to verify domain in AWS, go to SES services -> Domains and follow instructions.
This should let you send emails.

Related

Is it possible to send emails via AWS SES on AWS LightSail instance?

I have tried sending emails on my LightSail instance via AWS SES using the following Node.js JavaScript AWS SDK code, but it failed with the following error message. The email sending code works fine on my development computer. (The email sending code is from https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/ses-examples-sending-email.html.)
(code here)
import { readFile } from 'fs/promises';
import * as path from 'path';
import { SESClient } from "#aws-sdk/client-ses";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const awsConfigFileFullName = "aws_config.json";
let awsConfigFileFullPath = path.join(__dirname, awsConfigFileFullName);
const awsConfig = await readFile(awsConfigFileFullPath).then(json => JSON.parse(json)).catch(() => null);
aws_ses_client = new SESClient({ region: awsConfig.region, accessKeyId: awsConfig.accessKeyId, secretAccessKey: awsConfig.secretAccessKey });
const createSendEmailCommand = (toAddress, fromAddress, htmlContent, textContent, emailSubject) => {
return new SendEmailCommand({
Destination: {
/* required */
CcAddresses: [
/* more items */
],
ToAddresses: [
toAddress,
/* more To-email addresses */
],
},
Message: {
/* required */
Body: {
/* required */
Html: {
Charset: "UTF-8",
Data: htmlContent,
},
Text: {
Charset: "UTF-8",
Data: textContent,
},
},
Subject: {
Charset: "UTF-8",
Data: emailSubject,
},
},
Source: emailSenderName + '<' + fromAddress + '>',
ReplyToAddresses: [
/* more items */
],
});
};
const sendEmailCommand = createSendEmailCommand(
recipientEmailAddress,
senderEmailAddress,
htmlEmailContent,
textEmailContent,
emailSubject
);
try {
await aws_ses_client.send(sendEmailCommand);
} catch (e) {
console.error("Failed to send email.", e);
}
(error here)
AccessDenied: User arn:aws:sts::(some number):assumed-role/AmazonLightsailInstanceRole/i-(some alphanumeric number)is not authorized to perform ses:SendEmail' on resource
`arn:aws:ses:us-east-1:(some number):identity/(recipient email
address)'
After doing some search online on the issue, thinking that the error was caused by port 25 restriction on Lightsail instance restriction (https://aws.amazon.com/premiumsupport/knowledge-center/lightsail-port-25-throttle/), I sent a restriction removal request to AWS, but the request was declined, telling me to "consider looking into the Simple Email Service". I sent a reply asking if sending emails via AWS SES was possible on LightSail instance, but got a reply saying "we cannot grant your request"; I feel that the second reply was either automated or wasn't even thoroughly read by the person who reviewed the email.
I have a multisite WordPress installed on my LightSail webserver, which can send emails via AWS SES with WP Mail SMTP plugin, with TLS encryption using SMTP port 587. I think this is a proof that emails can be sent on a LightSail instance via AWS SES. Emails do get sent by my WordPress on my LightSail webserver, everyday, using my AWS SES SMTP credentials; so, maybe the AWS SES SMTP server must be directly contacted in the LightSail instance email sending code to send emails, instead of using the authenticated SES client object in the code?
I'm thinking that maybe the assumed role AmazonLightsailInstanceRole doesn't have AWS SES email sending allowed. I've checked my AWS IAM web console, and there was no role named AmazonLightsailInstanceRole; it doesn't look like I can modify the policy on the assumed role AmazonLightsailInstanceRole.
Can AWS SES email-sending permission be granted to the assumed role AmazonLightsailInstanceRole? If so, how?
Is it possible to send emails via AWS SES on an AWS LightSail instance? If so, how can it be done in Node.js JavaScript AWS SDK code? Any verifications, references and/or directions would be very helpful.
Sounds like an IAM issue - the instance role is the fallback option if you dont pass your own credentials. Lines 6..8 of the code is looking for a file (aws_config.json) which contains an API key and secret and a default region, then on line 9 its passing those values into SESClient. When you finally call aws_ses_client.send(sendEmailCommand) it will use the instance default credentails provided by lightsail which wont have access to resources in your aws account like SES. Check the following:
Does file aws_config.json exist?
Does it contain api key/secret/region for a valid user in your AWS account?
Does that user have a suitable IAM policy including ses:SendEmail and possibly other iam permissions?
FYI catch(() => null) could be hiding an error - you should use console.log in your catch() statements to write an error (at least while your debugging).
UPDATE
While testing my Node.js Express web app on an EC2 instance, to resolve the error I encountered, I came across the following: "Could not load credentials from any providers" while using dynamodb locally in Node.
Because I was using #aws-sdk/ses-client (version 3), not version 2, I should've used the following:
aws_s3_client = new S3Client({ region: awsConfig.region, credentials: {accessKeyId: awsConfig.accessKeyId, secretAccessKey: awsConfig.secretAccessKey }});
aws_ses_client = new SESClient({ region: awsConfig.region, credentials: {accessKeyId: awsConfig.accessKeyId, secretAccessKey: awsConfig.secretAccessKey }});
Using the above code enabled using both AWS SES and S3 from my web app running on a LightSail instance. So, the answer is, yes, it is possible to send emails via AWS SES on AWS LightSail instance.

Can I verify my domain once in AWS SES and allow my other accounts to use this?

I have successfully verified my domain in AWS SES on one account. I would like my other accounts to send from this domain, and as such I have created an Authorization policy with this in mind (via Terraform):
data "aws_iam_policy_document" "this" {
statement {
actions = ["ses:SendEmail", "ses:SendRawEmail"]
resources = ["${aws_ses_domain_identity.this.arn}"]
effect = "Allow"
principals {
identifiers = ["123456789100", "1234567890101", "1234567890102"]
type = "AWS"
}
}
}
and attached this to the verified domain.
However, when I try to send from another account (listed in the identifiers group above) I am unable to send, being given an error:
554 Access denied: User `arn:aws:iam::1234567890101:user/ses_user' is not authorized to perform `ses:SendRawEmail' on resource `arn:aws:ses:eu-west-1:1234567890101:identity/my.name#domain.com'
The ses_user on that account has the following IAM policy (again, in TF):
data "aws_iam_policy_document" "ses_user" {
statement {
actions = ["ses:SendEmail", "ses:SendRawEmail"]
resources = ["*"]
}
}
I was under the impression that cross-account email sending was possible here. Am I mistaken? Perhaps I am missing something.
Thanks
sources:
https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy
No responses, but I understand that it is possible to do this with delegated senders. The SMTP sender user needs to include X headers in the email when it is sent (these are stripped by SES before being sent). The verified domain then simply requires the SES Auth policy to allow the account ID of the delegated account to send emails. This can be bundled with other conditions (ie can only send to certain destinations) in the same policy, as per here:
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/sending-authorization-identity-owner-tasks-policy.html
See also:
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/sending-authorization-delegate-sender-tasks-email.html
I chose not to go down this road, but it is possible.
For an example of how to insert Headers, you might be using Postfix for example in which case use header_checks with the prepend directive:
http://www.postfix.org/header_checks.5.html

ERROR 1045 (28000): Access denied for user 'db_user'#'ip' (using password: YES) while connecting to a RDS DB instance using IAM DB Authentication

Following is a quick summary of the question. Read the full description section for the underlying details.
Condensed description:
Assume you have an IAM user already existing and the user is already able to access other AWS services, such as S3, CloudFront, ECS, EC2...
Let's say we need to provide the user with read-only access over the RDS cluster and set up IAM DB Authentication as well.
We perform all the steps mentioned as per the official guide, in OUR local system and it works perfectly and we are able to generate correct auth token for db_user.
However, here is where it gets interesting.. when the user tries to generate the token for the db_user account, from their local machine.. the user will be denied access.
Full description:
Setup:
My RDS cluster instance runs the Aurora MySQL engine.
Engine version: 5.6.10a
I've been following the AWS knowledge center guide on How do I allow users to connect to Amazon RDS with IAM credentials?
The guide doesn't explicitly mention but while generating the authentication token, AWS CLI uses IAM credentials stored locally, to sign the request.
I'd like to highlight that in the below-mentioned snippet, admin is the profile name stored by AWS CLI for my admin IAM user while the db_user is the IAM user (with rds-db:connect privileges).
TOKEN="$(aws --profile admin rds generate-db-auth-token -h.. .. .. -u db_user)
Using the above snippet I'm able to authenticate with the generated token and connect to the cluster.
If --profile attribute is not mentioned, it reads the default profile saved in the credentials file.
Issue:
Instead of using --profile admin I'm looking to use an already existing non-admin IAM profile for generating an authentication token.
For instance, assume IAM user named developer, with RDS read-only privileges and the credentials stored locally under the profile rds_read_only
TOKEN="$(aws --profile rds_read_only rds generate-db-auth-token -h.. .. .. -u db_user)
If I use the above token, I get the following error:
ERROR 1045 (28000): Access denied for user 'db_user'#'ip' (using password: YES)
After hours of troubleshooting, I was able to conclude that my rds_read_only profile is unable to generate valid authentication tokens probably because IAM user developer is missing some required policies.
I tried attaching all policies available under RDS and RDS Data API (individually as well as in combinations) to IAM user developer, without any luck. If I attach the AdministrativeAccess policy to IAM user developer, only then it is able to generate the token successfully.
Question:
What are the mandatory policies required for non-admin IAM users to generate an authentication token successfully?
i saw your question in AWS Blog.
You need to create an IAM policy to define access to your AWS RDS instances. Check this docs
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds-db:connect"
],
"Resource": [
"arn:aws:rds-db:us-east-2:1234567890:dbuser:/"
]
}
]
}
Create User inside RDS DB instance with the instructions to use an IAM Plugin. Check this docs
Create the token check this docs
I found this nice plugin that builds a JDBC jar to allow IAM authentication.
This answers the specific question of #Ronnie regarding the Token generation.
Ronnie i am back. I used the following policy in my AWS Account: Sandbox I am an AWS Federated user with AssumeRole priveleges so Admin
You have to be very careful because as you said the article doesn't make the distinction from:
AWS IAM USER using a generated token to access the DB and
AWS IAM User/Role with the right Admin policies that generates a VALID Token
I will give an Example of how to identify the correct generated Tokens. For some reason AWS generates a value but it doesn't tell you whether is a useful token or not :-\
Token without admin special access= WILL NOT WORK
sandboxdb.asdasdffw.ap-southeast-2.rds.amazonaws.com:3306/?Action=connect&DBUser=human_user&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA5XZIHS3GYVMRPRZF%2F20200424%2Fap-southeast-2%2Frds-db%2Faws4_request&X-Amz-Date=20200424T035250Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=3efd467d548ea05a8bdf097c132b03661680908f723861e45323723c870ef646
Token with Access= Will Work! Look carefully and it contains X-Amz-Security-Token=
sandboxdb.ras21th1z8.ap-southeast-2.rds.amazonaws.com:3306/?Action=connect&DBUser=human_user&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIA5XZIHS3GSAQI6XHO%2F20200424%2Fap-southeast-2%2Frds-db%2Faws4_request&X-Amz-Date=20200424T040756Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEQaDmFwLXNvdXRoZWFzdC0yIkgwRgIhAOzVIondlMxYkJG5nWNeQlxS0M6B1pphgD1ewFwx2VfKAiEAkcp2jNHHmNMgwqUholnW545MwjzoEjS1uh4BHI4R4GAqvgMIbRABGgw5NDQ0NDAzMTc2NDUiDEvFkyEy833kd%2By4nyqbAybqK5dcP0nTlqZ19I2OVZxzwzz%2BUv9RVdVLMPHE5b%2FqXQGVG1CRtw90r9Lt4QkzTBeIVzdtIkXbpwFtqFh24Djb%2BiZHfvElj%2Fhz29ExzStU0fPYMewEB1u%2F2Osi72Fw6KbZ6TDy5EjuWcrrS08PZQ9CHc%2Fc8iDAIKs28vJ70KKcmow0SInVZGHGpD2JAgIL7jvnadVlcAW7lN2OAnxS72Kb4neqNuHcWzfPLfbXaOP1OaOs7vCR7zDlTTxX2aHoVflC69K9K67BqzdnDnnju%2F4XWQWU3r%2ByXylExwOsiG3y4Qq6wv002l%2BpQmF5%2BMXdTrFR5ewpfrcHf8TZLI5eq8HLA2gG1%2B255L%2Bqt%2BD80T%2FCzEdKSJPjppdYSq9FdeCMRSsqp5PpXP%2BDbQZwmhxiE2RmrbOKwNsFPJqUUnemQHXYLB8lily56nnswT2PYmQOGHqnZWRrv%2FTlGOAGlThuiR%2BLhQLBC08nBEGbBqK%2FjU4JwFMY4JfhgUHr8BA9CuGwAu0qIAFzG71M3HzCNX6o56k1gYJB%2F3%2FJaKlp7TCIxIn1BTrqASqywcfKrWhIaNX3t%2BV%2FZoYYO%2FtGVBZLyr3sSmByA%2Fwq538LiPHA0wDE3utOg%2FwNP%2BQGTcXhk1F%2BI0HOHztAQ2afnKW8r1oRbXxYAzb2j2b8MNEwrsaBju2gHFRgZHkM8YI%2FP5cvYr%2F8FQXWcE9eqjdme0hOo3rPETzxZfRwNQTHEntBbVVD1ec0d7DblfSEDZhLk%2By1%2BFMAYf7NeBIfU6GNsAN2hTdSkPPuto2fQKzRybRAwxQz5P3cO5CClUNIxu4J3bM1MUUTux%2BtMjqRvjGxDhB4yLIJmIPOOYLDSOXl3aWO2y4v89wu5A%3D%3D&X-Amz-Signature=1c6fcc472bb2af09055117075ca21d4a5f715910443115116c9230905721e79d
AWS IAM policy For DB User to Connect to AWS RDS DB Instance
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "rds-db:connect",
"Resource": "*"
}
]
}
To avoid messing with local configs i used the following testing process:
Token generation from an AWS EC2 instance running in the same VPC as the DB. Generation is successful
Token generation from local machine by using a Docker container with AWS CLI Token generation is successful.
Of course my user was created in the MySQL DB with the following command
mysql -h $HOSTNAME -u$ADMIN_USER -p$ADMIN_PASS <<EOF
CREATE USER db_human_user IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
GRANT SELECT ON $SPECIFIC_GIVEN_DB.* TO 'db_human_user';
EOF

Using Firebase OpenID Connect provider as AWS IAM Identity Provider

I get the following error while setting up Firebase as an AWS IAM Identity Provider using OpenID Connect.
We encountered the following errors while processing your request:
Please check .well-known/openid-configuration of provider:
https://securetoken.google.com/<Project ID> is valid.
The AWS IAM Identity Provider setup requires two input parameters, to which I plugged in the following:
Provider URL: https://securetoken.google.com/<Firebase Project ID>
Audience: <Firebase Client ID>
To troubleshoot the error, I opened http://<Provider URL>/.well-known/openid-configuration in a browser and noted the JSON response has the Issuer and jwks_uri fields. I believe these JSON fields indicate the Firebase OpenID Connect Provider URL is valid.
Any idea how I could avoid the above error and successfully set up the AWS IAM Identity Provider?
I contacted AWS support and they helped resolve the problem. Thanks to Shaun H # AWS!
The solution to the problem is to use AWS CLI instead of AWS console to set up an OIDC provider.
I'm pasting relevant parts of Shaun's response below:
1.) Manually obtain and verify the thumbprint using the procedure described here[1].
"ThumbprintList" = "6040DB92306CC8BCEB31CACAC88D107430B16AFF"
2.) Create the OIDC identity provider using the AWS Cli [2].
For example: $ aws iam create-open-id-connect-provider --cli-input-json file://oidc.json Note - the format would be:
aud Audience Must be your Firebase project ID, the unique identifier for your Firebase project, which can be found in the URL of that project's console.
iss Issuer Must be https://securetoken.google.com/<projectId>, where is the same project ID used for aud above.
Content for file://oidc.json: (replace with your Project ID)
{
"Url": "https://securetoken.google.com/<Firebase Client ID>",
"ClientIDList": [ "<Firebase Client ID>" ],
"ThumbprintList": [ "6040DB92306CC8BCEB31CACAC88D107430B16AFF" ]
}
[1] http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
[2] http://docs.aws.amazon.com/cli/latest/reference/iam/create-open-id-connect-provider.html

Lambda SES forwarding function AWS

I'm using this email AWS SES forwarding script on a registered domain in AWS, along with releavant MX and TXT records. Personal emails im testing this with are also authorised as im in sandbox mode. I've set a rule in SES, pushing emails to an s3 bucket. After i call the aformentioned lambda function to redirect emails to anther gmail account.
var defaultConfig = {
fromEmail: "info#domainInRoute53.co.uk",
subjectPrefix: "",
emailBucket: "s3.xxxxx.xxxxx",
emailKeyPrefix: "emails/",
forwardMapping: {
"info#domainInRoute53.co.uk": [
"personal#gmail.com",
"personal#hotmail.co.uk"
]
}
}; ..etc..
It writes to the s3 bucket but i cant seem to get it to forward to the personal email addresses.
I've tried numerous edits on the inline and managed policies in the IAM roles for the lamdba function to select, as suggested in the link I'm following above on step 2. Any ideas why this is failing?