배경

SES 관련 요청이 들어왔다. 프로젝트 중 SES 설정은 보통 잘 들어오지 않아서 내용 정리를 해보려고 한다.

SMTP Credential

보통 Amazon SES에서 SMTP로 메일을 보낼 때는 IAM User의 일반 AWS Access Key와 Secret Key를 그대로 사용하는 것이 아니라 SES SMTP Credential을 사용한다.

여기서 가장 헷갈렸던 부분은 SES SMTP Credential도 IAM User의 Access Key를 기반으로 생성되지만, 일반 AWS Credential과 그대로 호환되는 것은 아니라는 점이었다.

  • AWS SDK나 AWS CLI로 SES API를 호출할 때는 일반 AWS Access Key와 Secret Key를 사용한다.
  • SES SMTP Interface를 사용할 때는 SMTP Username과 SMTP Password를 사용한다.
  • SMTP Username은 Access Key ID와 같은 값이지만 SMTP Password는 AWS Secret Access Key와 다른 값이다.
  • SMTP Credential은 Region별로 생성되므로 SMTP Endpoint의 Region과 맞아야 한다.

SES Console에서 Create SMTP credentials를 실행하면 SMTP 전용 IAM User와 Credential을 생성할 수 있다. IAM Console에서 Access Key를 직접 추가한 뒤 AWS Secret Access Key를 SMTP Password로 넣으면 정상적으로 인증되지 않는다.

기존 IAM User의 AWS Secret Access Key를 AWS에서 제공하는 변환 방식으로 SMTP Password로 만들 수도 있다. 다만 임시 AWS Credential은 SMTP Credential로 변환해서 사용할 수 없다.

테스트

사실, 위의 개념은 실제로 SES를 운영해본 사람들이라면 바로 확인할 수 있는 특징이다. 하지만 SES 관련 테스트 관련해서 찾기가 조금 쉽지 않았다.

테스트 전에는 발신 주소나 도메인이 SES에 인증되어 있는지 확인해야 한다. SES Sandbox 환경이라면 수신 주소도 인증되어 있어야 한다.

01 시나리오: PowerShell

PowerShell을 이용한 테스트다.

$smtpCredential = [System.Management.Automation.PSCredential]::new(
    '[SES_SMTP_USERNAME]',
    (ConvertTo-SecureString '[SES_SMTP_PASSWORD]' -AsPlainText -Force)
)
 
Send-MailMessage `
    -From 'sender@domain.com' `
    -To 'recipient@example.com' `
    -Subject 'SES SMTP test' `
    -Body 'test mail ses' `
    -SmtpServer 'email-smtp.ap-northeast-2.amazonaws.com' `
    -Credential $smtpCredential `
    -UseSsl `
    -Port 587
  • -From, -To: 발신자와 수신자를 지정한다.
  • -SmtpServer: 사용하는 Region의 SES SMTP Endpoint를 지정한다.
  • -Credential: SES SMTP Username과 SMTP Password를 전달한다.
  • -UseSsl, -Port 587: STARTTLS 방식으로 암호화된 연결을 사용한다.

24년도에 해당 명령어를 이용했을 때는 정상적으로 메일이 오는 것을 확인했다. 지금은 Send-MailMessage가 Obsolete 상태라 아래와 같은 경고가 발생한다.

WARNING: The command "Send-MailMessage" is obsolete. This cmdlet does not guarantee secure connections to SMTP servers. While there is no immediate replacement available in PowerShell, we recommend you do not use Send-MailMessage at this time.

기존 환경을 확인하는 단발성 테스트에는 사용할 수 있지만, 신규 자동화나 운영 코드에서는 MailKit처럼 TLS를 명확하게 지원하는 Client를 사용하는 것이 좋다.

02 시나리오: CloudShell

보통 테스트를 할 때는 CloudShell을 많이 이용한다. 특히 클라우드 환경에서 서버가 없을 때 간단한 통신이나 네트워크 테스트를 진행할 때 좋다.

# SMTP 접속을 위한 Username과 Password Encoding
printf '%s' '[SES_SMTP_USERNAME]' | openssl enc -base64 -A
printf '%s' '[SES_SMTP_PASSWORD]' | openssl enc -base64 -A
 
# SMTP 접속
openssl s_client -crlf -quiet -starttls smtp -connect email-smtp.ap-northeast-2.amazonaws.com:587
 
# SMTP 접속 후 Command
EHLO example.com
AUTH LOGIN
[BASE64_SMTP_USERNAME]
[BASE64_SMTP_PASSWORD]
MAIL FROM:<sender@domain.com>
RCPT TO:<recipient@example.com>
DATA
From: sender@domain.com
To: recipient@example.com
Subject: SES SMTP test
 
test mail ses
.
QUIT

Base64는 암호화가 아니라 SMTP AUTH에 값을 전달하기 위한 Encoding이다. 실제 Credential 값이나 Encoding 결과를 Shell History, 문서, Git에 남기지 않도록 주의해야 한다.

MAIL FROM에는 SES에서 인증된 발신 주소를 사용한다. Sandbox 환경이라면 RCPT TO의 수신 주소도 인증되어 있어야 한다. SMTP Endpoint와 Credential의 Region이 다르면 인증에 실패하므로 두 값도 같이 확인한다.

openssl s_client로 직접 입력할 때는 일정 시간 입력이 없으면 연결이 종료될 수 있다. 먼저 Command와 Encoding 결과를 준비한 뒤 연결하는 편이 좋다.

참고 자료