Files
runbooks/aws-ses-setup.md
kappa bafc79c81b Improve security documentation based on Context7 review
aws-ses-setup.md:
- Add SPF records for email authentication
- Add DMARC policy configuration
- Add bounce/complaint handling with SNS
- Add DNS verification commands

n8n-setup-guide.md:
- Use official Docker registry (docker.n8n.io)
- Add N8N_ENCRYPTION_KEY requirement
- Add N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS
- Add N8N_PUBLIC_API_DISABLED option
- Add security headers to nginx config
- Add healthcheck configuration

gitea-setup.md:
- Add password policy (MIN_PASSWORD_LENGTH, PASSWORD_COMPLEXITY)
- Add argon2 password hashing
- Add DISABLE_GIT_HOOKS for security
- Add Docker Secrets configuration
- Add file-based secret management (SECRET_KEY_URI)
- Add REVERSE_PROXY_TRUSTED_PROXIES setting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 00:45:40 +09:00

9.4 KiB

AWS SES 메일 발송 설정 가이드

작성일: 2026-01-12 리전: ap-northeast-2 (서울)

개요

AWS SES(Simple Email Service)를 사용하여 서버에서 메일을 발송하기 위한 설정 가이드입니다.


계정 상태

항목
리전 ap-northeast-2 (서울)
모드 프로덕션 (샌드박스 해제됨)
24시간 발송 한도 50,000통
초당 발송률 14통/초

인증된 도메인

도메인 도메인 인증 DKIM
anvil.it.com Success Success
ironclad.it.com Success Success

사용 가능한 발신자 주소

*@anvil.it.com      (예: noreply@anvil.it.com, support@anvil.it.com)
*@ironclad.it.com   (예: noreply@ironclad.it.com, admin@ironclad.it.com)

이메일 인증 (SPF, DKIM, DMARC)

DMARC 준수를 위해 SPF와 DKIM 모두 설정 필요. 두 가지 중 하나라도 통과하면 DMARC 통과.

인증 방식 비교

방식 역할 검증 대상
SPF 발신 서버 허용 목록 MAIL FROM 도메인
DKIM 메일 서명 검증 메일 헤더 서명
DMARC SPF/DKIM 정책 통합 From 도메인 정렬

DNS 설정 (Cloudflare)

anvil.it.com

TXT 레코드 (도메인 소유권 확인)

Type Name Content
TXT _amazonses.anvil.it.com 0cuw9v32N+aeFiNlTh2Poxglgzf3BlmFRjVOjeLEdy4=

TXT 레코드 (SPF) 신규

Type Name Content
TXT anvil.it.com v=spf1 include:amazonses.com ~all

TXT 레코드 (DMARC) 신규

Type Name Content
TXT _dmarc.anvil.it.com v=DMARC1; p=quarantine; rua=mailto:dmarc@anvil.it.com; pct=100

CNAME 레코드 (DKIM)

Type Name Target
CNAME dgcehnldehfmfgpvrrbc6drwasiibhnp._domainkey dgcehnldehfmfgpvrrbc6drwasiibhnp.dkim.amazonses.com
CNAME spopdscdt2sxngqzl5ir66k3ed6og7ut._domainkey spopdscdt2sxngqzl5ir66k3ed6og7ut.dkim.amazonses.com
CNAME 55l5wnmktvacgyfpt6sovcgb2rqexrpy._domainkey 55l5wnmktvacgyfpt6sovcgb2rqexrpy.dkim.amazonses.com

ironclad.it.com

TXT 레코드 (도메인 소유권 확인)

Type Name Content
TXT _amazonses.ironclad.it.com C+RMHyLd/U2H5WSCu8L2avRn8NmuwEll0xxYjTyvoEY=

TXT 레코드 (SPF) 신규

Type Name Content
TXT ironclad.it.com v=spf1 include:amazonses.com ~all

TXT 레코드 (DMARC) 신규

Type Name Content
TXT _dmarc.ironclad.it.com v=DMARC1; p=quarantine; rua=mailto:dmarc@ironclad.it.com; pct=100

CNAME 레코드 (DKIM)

Type Name Target
CNAME 5sycmzu364y2rgefnqaloptgodymasct._domainkey 5sycmzu364y2rgefnqaloptgodymasct.dkim.amazonses.com
CNAME qm7d7qgkdikpcbrbgo7bmgqfmuulrbah._domainkey qm7d7qgkdikpcbrbgo7bmgqfmuulrbah.dkim.amazonses.com
CNAME tu6oey5tktoqub753cxpayhhwrlzcskk._domainkey tu6oey5tktoqub753cxpayhhwrlzcskk.dkim.amazonses.com

DMARC 정책 옵션

정책 설명 권장 단계
p=none 모니터링만 (조치 없음) 초기 테스트
p=quarantine 스팸함으로 이동 중간 단계
p=reject 수신 거부 최종 단계

IAM 권한 설정

최소 권한 정책 (권장)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SESSendEmail",
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}

특정 발신자 제한 정책

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SESSendFromVerified",
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": [
                "arn:aws:ses:ap-northeast-2:*:identity/anvil.it.com",
                "arn:aws:ses:ap-northeast-2:*:identity/ironclad.it.com"
            ]
        }
    ]
}

사용 방법

AWS CLI

aws ses send-email \
  --from "noreply@anvil.it.com" \
  --to "recipient@example.com" \
  --subject "메일 제목" \
  --text "메일 본문" \
  --region ap-northeast-2

Python (boto3)

import boto3
from botocore.exceptions import ClientError

def send_email(to_email: str, subject: str, body: str, sender: str = "noreply@anvil.it.com"):
    client = boto3.client('ses', region_name='ap-northeast-2')

    try:
        response = client.send_email(
            Source=sender,
            Destination={'ToAddresses': [to_email]},
            Message={
                'Subject': {'Data': subject, 'Charset': 'UTF-8'},
                'Body': {
                    'Text': {'Data': body, 'Charset': 'UTF-8'},
                    # HTML 본문 사용 시:
                    # 'Html': {'Data': '<h1>Hello</h1>', 'Charset': 'UTF-8'}
                }
            }
        )
        print(f"발송 완료: {response['MessageId']}")
        return response
    except ClientError as e:
        print(f"발송 실패: {e.response['Error']['Message']}")
        raise

# 사용 예시
send_email(
    to_email='recipient@example.com',
    subject='테스트 메일',
    body='안녕하세요!'
)

Python (HTML 메일)

def send_html_email(to_email: str, subject: str, html_body: str, text_body: str = None):
    client = boto3.client('ses', region_name='ap-northeast-2')

    body = {'Html': {'Data': html_body, 'Charset': 'UTF-8'}}
    if text_body:
        body['Text'] = {'Data': text_body, 'Charset': 'UTF-8'}

    response = client.send_email(
        Source='noreply@anvil.it.com',
        Destination={'ToAddresses': [to_email]},
        Message={
            'Subject': {'Data': subject, 'Charset': 'UTF-8'},
            'Body': body
        }
    )
    return response

# 사용 예시
html = """
<html>
<body>
    <h1>안녕하세요!</h1>
    <p>AWS SES로 발송된 <strong>HTML 메일</strong>입니다.</p>
</body>
</html>
"""
send_html_email('recipient@example.com', '테스트', html)

환경변수 설정 (컨테이너용)

export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="ap-northeast-2"

환경변수 설정 시 코드에서 자격증명 생략 가능:

client = boto3.client('ses')  # 환경변수에서 자동 로드

상태 확인 명령어

# 발송 활성화 상태
aws ses get-account-sending-enabled

# 발송 한도 확인
aws ses get-send-quota

# 등록된 Identity 목록
aws ses list-identities

# 도메인 인증 상태
aws ses get-identity-verification-attributes --identities anvil.it.com ironclad.it.com

# DKIM 상태
aws ses get-identity-dkim-attributes --identities anvil.it.com ironclad.it.com

새 도메인 추가 방법

1. SES 도메인 등록

aws ses verify-domain-identity --domain newdomain.com
aws ses verify-domain-dkim --domain newdomain.com

2. Cloudflare DNS 레코드 추가

출력된 토큰으로 TXT, CNAME 레코드 추가 (Cloudflare API 또는 대시보드)

3. 인증 확인

aws ses get-identity-verification-attributes --identities newdomain.com
aws ses get-identity-dkim-attributes --identities newdomain.com

반송/불만 처리 (SNS 연동)

반송(Bounce)과 불만(Complaint) 처리는 SES 평판 유지에 필수

SNS 토픽 생성

# 반송 알림용 토픽
aws sns create-topic --name ses-bounces --region ap-northeast-2

# 불만 알림용 토픽
aws sns create-topic --name ses-complaints --region ap-northeast-2

SES 알림 설정

# 반송 알림 연결
aws ses set-identity-notification-topic \
  --identity anvil.it.com \
  --notification-type Bounce \
  --sns-topic arn:aws:sns:ap-northeast-2:ACCOUNT_ID:ses-bounces

# 불만 알림 연결
aws ses set-identity-notification-topic \
  --identity anvil.it.com \
  --notification-type Complaint \
  --sns-topic arn:aws:sns:ap-northeast-2:ACCOUNT_ID:ses-complaints

반송 유형

유형 설명 조치
Hard Bounce 영구적 실패 (주소 없음) 즉시 목록에서 제거
Soft Bounce 일시적 실패 (용량 초과) 재시도 후 제거
Complaint 스팸 신고 즉시 목록에서 제거

권장 임계값

  • 반송률: 5% 미만 유지 (10% 초과 시 계정 정지 위험)
  • 불만률: 0.1% 미만 유지

인증 검증 명령어

# SPF 레코드 확인
dig TXT anvil.it.com +short

# DMARC 레코드 확인
dig TXT _dmarc.anvil.it.com +short

# DKIM 레코드 확인
dig CNAME dgcehnldehfmfgpvrrbc6drwasiibhnp._domainkey.anvil.it.com +short

참고 사항

  • 프로덕션 모드: 수신자 제한 없음
  • DKIM: 메일 서명으로 위변조 방지
  • SPF: 허용된 발신 서버 목록 정의
  • DMARC: SPF/DKIM 정책 통합 및 리포트 수신
  • 발송 한도: 필요시 AWS에 증가 요청 가능
  • 비용: $0.10 / 1,000통

관련 링크