docs: Add comprehensive AWS SES setup guides
- AWS_SES_SETUP.md: Complete setup with prod/dev env separation - AWS_SES_BROWSER_AGENT.md: Step-by-step for browser-based agent - Explicit security notes about IAM scoping and key handling
This commit is contained in:
parent
bb1cba5e20
commit
cc694ed5bb
2 changed files with 215 additions and 90 deletions
127
docs/AWS_SES_BROWSER_AGENT.md
Normal file
127
docs/AWS_SES_BROWSER_AGENT.md
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
# AWS SES Setup - Browser Agent Handoff
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Configure AWS SES for the Elmeg platform to enable transactional emails (verification, password reset).
|
||||||
|
|
||||||
|
**Domain:** `elmeg.xyz`
|
||||||
|
**Production URL:** `https://elmeg.xyz`
|
||||||
|
**Sender Email:** `noreply@elmeg.xyz`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Verify Domain in SES
|
||||||
|
|
||||||
|
1. Go to: <https://console.aws.amazon.com/ses>
|
||||||
|
2. Select region **US East (N. Virginia) us-east-1** from top-right dropdown
|
||||||
|
3. Left sidebar → **Verified identities** → Click **Create identity**
|
||||||
|
4. Select **Domain**
|
||||||
|
5. Enter: `elmeg.xyz`
|
||||||
|
6. Keep "Use a custom MAIL FROM domain" unchecked
|
||||||
|
7. Click **Create identity**
|
||||||
|
8. Copy the DNS records shown:
|
||||||
|
- 1 TXT record (for verification)
|
||||||
|
- 3 CNAME records (for DKIM)
|
||||||
|
9. **Save these records** - they need to be added to elmeg.xyz DNS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2: Add DNS Records
|
||||||
|
|
||||||
|
Go to the DNS provider for `elmeg.xyz` and add:
|
||||||
|
|
||||||
|
| Type | Name | Value |
|
||||||
|
|------|------|-------|
|
||||||
|
| TXT | `_amazonses.elmeg.xyz` | (from SES console) |
|
||||||
|
| CNAME | `xxxx._domainkey.elmeg.xyz` | (DKIM 1 from SES) |
|
||||||
|
| CNAME | `xxxx._domainkey.elmeg.xyz` | (DKIM 2 from SES) |
|
||||||
|
| CNAME | `xxxx._domainkey.elmeg.xyz` | (DKIM 3 from SES) |
|
||||||
|
|
||||||
|
Wait for verification (can take 5-72 hours).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3: Request Production Access
|
||||||
|
|
||||||
|
By default SES is in sandbox mode (can only send to verified emails).
|
||||||
|
|
||||||
|
1. In SES console → **Account dashboard** (left sidebar)
|
||||||
|
2. Find "Your account is in the Amazon SES sandbox" banner
|
||||||
|
3. Click **Request production access**
|
||||||
|
4. Fill form:
|
||||||
|
- **Mail type:** Transactional
|
||||||
|
- **Website URL:** <https://elmeg.xyz>
|
||||||
|
- **Use case description:**
|
||||||
|
> "Transactional emails for user account lifecycle on a live music rating platform. Emails include: account verification, password reset, and account notifications. Expected volume: under 1000 emails/month initially."
|
||||||
|
5. Submit and wait for approval (~24 hours)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4: Create IAM User for SES
|
||||||
|
|
||||||
|
1. Go to: <https://console.aws.amazon.com/iam>
|
||||||
|
2. Left sidebar → **Users** → **Create user**
|
||||||
|
3. User name: `elmeg-ses-sender`
|
||||||
|
4. Click **Next**
|
||||||
|
5. Select **Attach policies directly**
|
||||||
|
6. Click **Create policy** (opens new tab):
|
||||||
|
- Select **JSON** tab
|
||||||
|
- Paste:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": ["ses:SendEmail", "ses:SendRawEmail"],
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Click **Next**
|
||||||
|
- Policy name: `SES-SendEmail-Only`
|
||||||
|
- Click **Create policy**
|
||||||
|
7. Back on user creation, refresh policies, search `SES-SendEmail-Only`, select it
|
||||||
|
8. Click **Next** → **Create user**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Create Access Key
|
||||||
|
|
||||||
|
1. Click on user `elmeg-ses-sender`
|
||||||
|
2. Tab: **Security credentials**
|
||||||
|
3. Scroll to **Access keys** → **Create access key**
|
||||||
|
4. Select: **Application running outside AWS**
|
||||||
|
5. Click **Next** → **Create access key**
|
||||||
|
6. **SAVE BOTH VALUES:**
|
||||||
|
- Access key ID: `AKIA...`
|
||||||
|
- Secret access key: `...`
|
||||||
|
7. Click **Done**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6: Return Values
|
||||||
|
|
||||||
|
Return these values to the user:
|
||||||
|
|
||||||
|
```
|
||||||
|
AWS_ACCESS_KEY_ID=AKIA...
|
||||||
|
AWS_SECRET_ACCESS_KEY=...
|
||||||
|
AWS_SES_REGION=us-east-1
|
||||||
|
EMAIL_FROM=noreply@elmeg.xyz
|
||||||
|
FRONTEND_URL=https://elmeg.xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
Also return the DNS records that need to be added for domain verification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Never share or commit AWS keys**
|
||||||
|
- SES sandbox removal takes ~24hrs
|
||||||
|
- Domain verification DNS propagation can take up to 72hrs
|
||||||
|
- DKIM is required or emails will be marked as spam
|
||||||
|
|
@ -1,73 +1,105 @@
|
||||||
# AWS SES Email Setup
|
# AWS SES Email Setup
|
||||||
|
|
||||||
## Overview
|
## Environment Configuration
|
||||||
|
|
||||||
The Elmeg platform uses AWS SES (Simple Email Service) for sending transactional emails (verification, password reset). Cost: ~$0.10 per 1,000 emails.
|
### Production (`elmeg.xyz`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AWS_ACCESS_KEY_ID=AKIA... # IAM user with SES send-only perms
|
||||||
|
AWS_SECRET_ACCESS_KEY=...
|
||||||
|
AWS_SES_REGION=us-east-1 # Must match region where domain is verified
|
||||||
|
EMAIL_FROM=noreply@elmeg.xyz # Must be on SES-verified domain
|
||||||
|
FRONTEND_URL=https://elmeg.xyz
|
||||||
|
NODE_ENV=production
|
||||||
|
APP_ENV=production
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development (`elmeg.runfoo.run`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FRONTEND_URL=https://elmeg.runfoo.run
|
||||||
|
APP_ENV=development
|
||||||
|
# AWS keys optional in dev - emails log to console instead
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prerequisites
|
## SES Setup Checklist
|
||||||
|
|
||||||
- AWS Account
|
### 1. Verify Domain in SES
|
||||||
- Domain: `elmeg.xyz` (for verified sender)
|
|
||||||
- Production server: `tangible-aacorn` / `nexus-vector`
|
1. AWS Console → SES → Verified Identities → Create identity
|
||||||
|
2. Select "Domain" → enter `elmeg.xyz`
|
||||||
|
3. Add DNS records AWS provides:
|
||||||
|
- **TXT record** for domain verification
|
||||||
|
- **3 CNAME records** for DKIM signing
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Add DKIM records or mail will get flagged as spam
|
||||||
|
|
||||||
|
### 2. Move Out of Sandbox
|
||||||
|
|
||||||
|
By default SES is sandboxed (can only send to verified emails).
|
||||||
|
|
||||||
|
1. SES → Account dashboard → Request production access
|
||||||
|
2. Fill out:
|
||||||
|
- Mail type: **Transactional**
|
||||||
|
- Website URL: `https://elmeg.xyz`
|
||||||
|
- Use case: "User registration verification, password reset for live music rating platform"
|
||||||
|
3. Wait for approval (~24hrs)
|
||||||
|
|
||||||
|
### 3. Create IAM User
|
||||||
|
|
||||||
|
1. IAM → Users → Create user: `elmeg-ses-sender`
|
||||||
|
2. Attach inline policy (SES send only):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"ses:SendEmail",
|
||||||
|
"ses:SendRawEmail"
|
||||||
|
],
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Security credentials → Create access key
|
||||||
|
4. Save credentials securely
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> **Never commit AWS keys.** Use environment variables only. Never use root AWS credentials - always create a scoped IAM user.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Setup Steps
|
## DNS Records Required
|
||||||
|
|
||||||
### 1. Verify Email Domain in SES
|
Add these to `elmeg.xyz` DNS (exact values from SES console):
|
||||||
|
|
||||||
1. Go to AWS Console → SES → Verified Identities
|
| Type | Name | Value |
|
||||||
2. Click "Create identity" → Choose "Domain"
|
|------|------|-------|
|
||||||
3. Enter: `elmeg.xyz`
|
| TXT | `_amazonses.elmeg.xyz` | (provided by SES) |
|
||||||
4. Enable "Use a custom MAIL FROM domain" (optional)
|
| CNAME | `xxxx._domainkey.elmeg.xyz` | (DKIM 1) |
|
||||||
5. Add the provided DNS records to your domain
|
| CNAME | `xxxx._domainkey.elmeg.xyz` | (DKIM 2) |
|
||||||
|
| CNAME | `xxxx._domainkey.elmeg.xyz` | (DKIM 3) |
|
||||||
|
|
||||||
**Required DNS Records** (example):
|
---
|
||||||
|
|
||||||
```
|
## Deployment
|
||||||
Type: TXT
|
|
||||||
Name: _amazonses.elmeg.xyz
|
|
||||||
Value: [provided by AWS]
|
|
||||||
|
|
||||||
Type: CNAME
|
### Add to production server
|
||||||
Name: [dkim1]._domainkey.elmeg.xyz
|
|
||||||
Value: [provided by AWS]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Move Out of SES Sandbox
|
|
||||||
|
|
||||||
By default, SES is in sandbox mode (can only send to verified emails).
|
|
||||||
|
|
||||||
1. Go to SES → Account dashboard
|
|
||||||
2. Click "Request production access"
|
|
||||||
3. Fill out:
|
|
||||||
- Mail type: Transactional
|
|
||||||
- Website URL: <https://elmeg.xyz>
|
|
||||||
- Use case: User registration verification, password reset
|
|
||||||
4. Wait for approval (usually 24hrs)
|
|
||||||
|
|
||||||
### 3. Create IAM User for SES
|
|
||||||
|
|
||||||
1. Go to IAM → Users → Create user
|
|
||||||
2. Name: `elmeg-ses-sender`
|
|
||||||
3. Attach policy: `AmazonSESFullAccess` (or create custom with `ses:SendEmail` only)
|
|
||||||
4. Create access key for "Application running outside AWS"
|
|
||||||
5. Save:
|
|
||||||
- Access Key ID
|
|
||||||
- Secret Access Key
|
|
||||||
|
|
||||||
### 4. Configure Production Server
|
|
||||||
|
|
||||||
SSH into server and update environment:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh root@tangible-aacorn
|
ssh root@tangible-aacorn
|
||||||
cd /srv/containers/elmeg-demo
|
cd /srv/containers/elmeg-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit `docker-compose.yml`, add to `backend` service:
|
Edit `docker-compose.yml` backend environment:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
backend:
|
backend:
|
||||||
|
|
@ -78,56 +110,21 @@ backend:
|
||||||
- AWS_SES_REGION=us-east-1
|
- AWS_SES_REGION=us-east-1
|
||||||
- EMAIL_FROM=noreply@elmeg.xyz
|
- EMAIL_FROM=noreply@elmeg.xyz
|
||||||
- FRONTEND_URL=https://elmeg.xyz
|
- FRONTEND_URL=https://elmeg.xyz
|
||||||
|
- APP_ENV=production
|
||||||
```
|
```
|
||||||
|
|
||||||
Or create `.env` file:
|
Rebuild and restart:
|
||||||
|
|
||||||
```env
|
|
||||||
AWS_ACCESS_KEY_ID=AKIA...
|
|
||||||
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
||||||
AWS_SES_REGION=us-east-1
|
|
||||||
EMAIL_FROM=noreply@elmeg.xyz
|
|
||||||
FRONTEND_URL=https://elmeg.xyz
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Add boto3 to Requirements
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Add to backend/requirements.txt
|
|
||||||
boto3>=1.28.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Run Migration & Restart
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose build backend
|
docker compose build backend
|
||||||
docker compose exec backend python migrations/add_email_verification.py
|
|
||||||
docker compose restart backend
|
docker compose restart backend
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. Test
|
|
||||||
|
|
||||||
1. Register at <https://elmeg.runfoo.run/register>
|
|
||||||
2. Check for verification email
|
|
||||||
3. Test forgot password at <https://elmeg.runfoo.run/forgot-password>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
| Variable | Description | Example |
|
|
||||||
|----------|-------------|---------|
|
|
||||||
| `AWS_ACCESS_KEY_ID` | IAM user access key | `AKIA...` |
|
|
||||||
| `AWS_SECRET_ACCESS_KEY` | IAM user secret | `wJalr...` |
|
|
||||||
| `AWS_SES_REGION` | SES region | `us-east-1` |
|
|
||||||
| `EMAIL_FROM` | Verified sender address | `noreply@elmeg.xyz` |
|
|
||||||
| `FRONTEND_URL` | Base URL for links | `https://elmeg.runfoo.run` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Cost
|
## Cost
|
||||||
|
|
||||||
- $0.10 per 1,000 emails
|
- **$0.10 per 1,000 emails**
|
||||||
- No monthly minimum
|
- No monthly minimum
|
||||||
- First 62,000 emails/month free if sending from EC2
|
- First 62,000 emails/month free if sending from EC2
|
||||||
|
|
||||||
|
|
@ -137,7 +134,8 @@ docker compose restart backend
|
||||||
|
|
||||||
| Issue | Solution |
|
| Issue | Solution |
|
||||||
|-------|----------|
|
|-------|----------|
|
||||||
| "Email address is not verified" | Domain not verified in SES, or still in sandbox |
|
| "Email address is not verified" | Domain not verified, or still in sandbox |
|
||||||
| "Access Denied" | Check IAM permissions for ses:SendEmail |
|
| "Access Denied" | Check IAM policy has `ses:SendEmail` |
|
||||||
| Emails not sending | Check `docker compose logs backend` |
|
| Emails not sending | Check `docker compose logs backend` |
|
||||||
| Emails in spam | Complete DKIM setup, verify domain |
|
| Emails in spam | Verify DKIM records are set correctly |
|
||||||
|
| Wrong links in emails | Check `FRONTEND_URL` matches prod domain |
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue