mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 05:23:14 +00:00
fix(infra): send proposals from seahaven.com domain identity (primary SES domain) (#230)
Switch the API Lambda's SES sender from the email identity proposals@seahavenind.com to proposals@seahaven.com under the seahaven.com DOMAIN identity — the org's primary sending domain for all workload stacks. The domain identity is account-shared and managed out-of-band in seahaven-prod (Easy-DKIM verified in the seahaven.com Route53 zone; production-access request submitted), so this stack references its ARN instead of creating an EmailIdentity (avoids per-stack duplication + cross-account DNS at deploy). Send scoped to identity/seahaven.com with a required ses:FromAddress condition. GPT-4.1 cross-review APPROVE. Drops the unused aws-ses import. Follow-up: codify the seahaven.com identity in the seahaven-prod account baseline.
This commit is contained in:
parent
590f0c075c
commit
c19c7fda72
1 changed files with 14 additions and 19 deletions
|
|
@ -5,7 +5,6 @@ import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
|
|||
import * as apigatewayv2Authorizers from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
|
||||
import * as apigatewayv2Integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
||||
import * as iam from 'aws-cdk-lib/aws-iam';
|
||||
import * as ses from 'aws-cdk-lib/aws-ses';
|
||||
import * as s3 from 'aws-cdk-lib/aws-s3';
|
||||
import * as sqs from 'aws-cdk-lib/aws-sqs';
|
||||
import * as sns from 'aws-cdk-lib/aws-sns';
|
||||
|
|
@ -216,7 +215,7 @@ export class ComputeStack extends cdk.Stack {
|
|||
Auth__CognitoDomain: `${config.cognitoDomainPrefix}.auth.${this.region}.amazoncognito.com`,
|
||||
COGNITO_WEB_CLIENT_ID: props.webClientId,
|
||||
COGNITO_MOBILE_CLIENT_ID: props.mobileClientId,
|
||||
SES_FROM_ADDRESS: 'proposals@seahavenind.com',
|
||||
SES_FROM_ADDRESS: 'proposals@seahaven.com',
|
||||
},
|
||||
tracing: lambda.Tracing.ACTIVE,
|
||||
logRetention: logs.RetentionDays.TWO_MONTHS,
|
||||
|
|
@ -234,31 +233,27 @@ export class ComputeStack extends cdk.Stack {
|
|||
resources: [props.userPool.userPoolArn],
|
||||
}));
|
||||
|
||||
// PR4: SES email identity for proposal delivery.
|
||||
// NOTE: New SES identities start in the **sandbox**. While in sandbox mode the
|
||||
// account can only send email to *verified* recipient addresses. To send to
|
||||
// arbitrary recipients, submit a production-access request via the AWS SES console
|
||||
// (Support Center → "SES Sending Limits Increase"). The email identity itself
|
||||
// must also be verified — AWS sends a click-link email to the address below after
|
||||
// deployment; someone with access to the mailbox must click it.
|
||||
const sesFromAddress = 'proposals@seahavenind.com';
|
||||
const sesSenderIdentity = new ses.EmailIdentity(this, 'SesSenderIdentity', {
|
||||
identity: ses.Identity.email(sesFromAddress),
|
||||
});
|
||||
// SES sender: proposals@seahaven.com. seahaven.com is the org's PRIMARY sending
|
||||
// domain for all workload stacks. The domain identity is an ACCOUNT-SHARED resource
|
||||
// managed OUT OF BAND in seahaven-prod (verified via Easy DKIM in the seahaven.com
|
||||
// Route53 zone; production-access request submitted 2026-07-15) — this stack
|
||||
// references it rather than owning it, so it is not re-created per stack and does not
|
||||
// depend on cross-account DNS at deploy time. Do NOT create an EmailIdentity here
|
||||
// (CloudFormation would fail "already exists" against the out-of-band identity).
|
||||
// TODO: codify the seahaven.com identity in the seahaven-prod account baseline.
|
||||
const sesFromAddress = 'proposals@seahaven.com';
|
||||
|
||||
// Grant the API Lambda least-privilege SES send permission, scoped to the
|
||||
// sender identity ARN only (not ses:* / resource:*).
|
||||
const senderIdentityArn = `arn:aws:ses:${this.region}:${this.account}:identity/${sesFromAddress}`;
|
||||
// Least-privilege SES send: scope to the seahaven.com domain identity ARN. A domain
|
||||
// identity authorizes sending as ANY user@seahaven.com, so the ses:FromAddress
|
||||
// condition is REQUIRED (not optional) to pin the Lambda to proposals@seahaven.com.
|
||||
const senderIdentityArn = `arn:aws:ses:${this.region}:${this.account}:identity/seahaven.com`;
|
||||
apiFunction.addToRolePolicy(new iam.PolicyStatement({
|
||||
actions: ['ses:SendEmail', 'ses:SendRawEmail'],
|
||||
resources: [senderIdentityArn],
|
||||
// Defense-in-depth (cross-review): only send AS this address, even if the
|
||||
// identity scope is later widened.
|
||||
conditions: {
|
||||
StringEquals: { 'ses:FromAddress': sesFromAddress },
|
||||
},
|
||||
}));
|
||||
void sesSenderIdentity;
|
||||
|
||||
// Fix: LAM-C1/INF-H1 — require IAM auth on Function URL (was authType NONE).
|
||||
// NOTE: Lambda HTTP clients (suggestions, pdf-extract, pdf-generate, library-ingest)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue