通过Nodemailer使用AWS SES SMTP时,程序化创建SMTP凭证
解决方案:避免Lambda硬编码SES SMTP凭证
一、通过CloudFormation自动化创建SMTP凭证并传递给Lambda
1. CloudFormation模板核心资源配置
创建IAM用户、生成Access Key、转换为SMTP凭证并存储到Secrets Manager:
AWSTemplateFormatVersion: '2010-09-09' Resources: # 创建用于SES SMTP的IAM用户 SESSMTPUser: Type: AWS::IAM::User Properties: Policies: - PolicyName: SESSendAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - ses:SendEmail - ses:SendRawEmail Resource: "*" # 为IAM用户生成长期Access Key SESSMTPAccessKey: Type: AWS::IAM::AccessKey Properties: UserName: !Ref SESSMTPUser # 自定义资源:将IAM Access Key转换为SES SMTP凭证 SESSMTPSecret: Type: Custom::SESSMTPSecret Properties: ServiceToken: !GetAtt SESSMTPConverterFunction.Arn AccessKeyId: !Ref SESSMTPAccessKey SecretAccessKey: !GetAtt SESSMTPAccessKey.SecretAccessKey Region: !Ref AWS::Region # 存储SMTP凭证到Secrets Manager SESSMTPSecretStore: Type: AWS::SecretsManager::Secret Properties: SecretString: !Sub '{"smtp_user": "${SESSMTPSecret.SMTPUsername}", "smtp_pass": "${SESSMTPSecret.SMTPPassword}"}' # Lambda执行角色:允许读取Secrets Manager和SES访问 LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole Policies: - PolicyName: SESAndSecretsAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue Resource: !Ref SESSMTPSecretStore - Effect: Allow Action: - ses:SendEmail - ses:SendRawEmail Resource: "*" # 自定义资源用的Lambda:转换Access Key为SMTP凭证 SESSMTPConverterFunction: Type: AWS::Lambda::Function Properties: Runtime: nodejs16.x Handler: index.handler Role: !GetAtt SESSMTPConverterRole.Arn Code: ZipFile: | const crypto = require('crypto'); exports.handler = async (event) => { const { AccessKeyId, SecretAccessKey } = event.ResourceProperties; const smtpUsername = AccessKeyId; const message = `SendRawEmail`; const version = 0x02; const signature = crypto.createHmac('sha256', SecretAccessKey) .update(message) .digest(); const smtpPassword = Buffer.concat([Buffer.from([version]), signature]).toString('base64'); return { PhysicalResourceId: AccessKeyId, Data: { SMTPUsername: smtpUsername, SMTPPassword: smtpPassword } }; }; # 自定义资源Lambda的执行角色 SESSMTPConverterRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
2. Lambda代码修改:从Secrets Manager获取凭证
更新Node.js代码,通过AWS SDK读取Secrets Manager中的SMTP凭证,无需硬编码:
const nodemailer = require('nodemailer'); const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager'); const secretsManager = new SecretsManagerClient({ region: process.env.AWS_REGION }); const SECRET_ARN = process.env.SES_SMTP_SECRET_ARN; // 通过CloudFormation传递到Lambda环境变量 async function getSmtpCredentials() { const command = new GetSecretValueCommand({ SecretId: SECRET_ARN }); const response = await secretsManager.send(command); const secret = JSON.parse(response.SecretString); return { user: secret.smtp_user, pass: secret.smtp_pass }; } exports.handler = async (event) => { const sourceEmailAddress = 'your-verified-email@example.com'; const mailOptions = { from: sourceEmailAddress, to: 'myemail@something.com', text: 'This is some text', html: '<b>This is some HTML</b>', subject: 'Nodemailer SMTP transporter', }; const region = process.env.AWS_REGION; const smtpCreds = await getSmtpCredentials(); const smtpTransporter = nodemailer.createTransport({ port: 587, host: `email-smtp.${region}.amazonaws.com`, auth: smtpCreds, secure: false, tls: { rejectUnauthorized: true } }); await smtpTransporter.sendMail(mailOptions); return { statusCode: 200, body: 'Email sent successfully' }; };
3. 给Lambda配置环境变量
在CloudFormation的Lambda资源中添加环境变量,传递Secrets Manager的ARN:
YourLambdaFunction: Type: AWS::Lambda::Function Properties: # 其他配置(Runtime、Handler、Code、Role等) Environment: Variables: AWS_REGION: !Ref AWS::Region SES_SMTP_SECRET_ARN: !Ref SESSMTPSecretStore VpcConfig: # 你的VPC配置(子网、安全组等)
二、关键说明
- SMTP凭证生成逻辑:AWS SES的SMTP密码是通过IAM用户的Secret Access Key对固定字符串
SendRawEmail进行HMAC-SHA256加密,再加上版本号0x02后转base64得到的,自定义Lambda实现了这个转换。 - 权限最小化:建议把IAM用户的SES权限限制到特定的验证邮箱或发送源,不要用
*。 - VPC终端节点:确保你的VPC终端节点涵盖Secrets Manager和SES的终端节点,Lambda才能在VPC内访问这些服务。
内容的提问来源于stack exchange,提问作者tteguayco
相关产品推荐
相关产品推荐

