앱 연동/메일
메일
승인된 모든 사용자에게(앱 전용 계정 포함) 사용자이름@jimmy-home.com 메일함이 자동으로 생깁니다. 콘솔의 메일 탭(웹메일)에서 읽고, 쓰고, 답장합니다.
사용 방법
- 상단 메일 탭 → 받은편지함 / 스팸함. 메일을 누르면 본문, 첨부파일, 삭제
- 콘솔 홈의 "메일" 카드에 안 읽은 메일 수와 최근 메일 5통
- 새 메일이 오면 폰으로 푸시 알림을 받을 수 있음 (보낸 사람·제목으로 거르기 가능) — 알림 참고
- 한도: 한 사람당 1GiB, 메일 1통 25MB
- 주소는 사용자 이름 그대로라서, 사용자 이름이 메일 주소로 쓸 수 없는 형식(대문자, 공백, 특수문자 등)이면 메일함이 만들어지지 않음.
postmaster/abuse같은 역할 주소는 관리자 메일함으로 연결됨
구조
인터넷 → 공유기 :25 → jimmy-master :25 (hostPort)
→ docker-mailserver (mail 네임스페이스)
Postfix (수신) → Rspamd (스팸, SPF/DKIM/DMARC 검사) → Dovecot (메일함, 스팸은 Junk)
포털 웹메일 → 클러스터 내부 IMAP(143) → Dovecot
- DNS:
MX jimmy-home.com → mail.jimmy-home.com,A mail → 집 공인 IP (DNS only).*.jimmy-home.com은 Cloudflare 프록시로 가지만, 명시적mailA 레코드가 와일드카드보다 우선함 (Cloudflare 프록시는 SMTP를 통과시키지 않음) - 서버: docker-mailserver 16 (Postfix + Dovecot 2.4 + Rspamd), jimmy-master에서 실행. master는 시스템 예약 메모리가 없어서 파드 메모리 상한 1Gi, 무거운 백신(ClamAV)은 끔. 메일은 작은 파일이 많은 워크로드라 CephFS가 아니라 RBD(
ceph-block) 20Gi에 저장 - 발신 IP 보존: 25번을 hostPort로 받아서 주소 변환만 거치므로 발신 서버의 실제 IP가 그대로 보임 → 스팸/SPF 판정이 제대로 동작
- 계정: 포털이 원본. 승인된 사용자 목록으로 메일함 목록·쿼터·별칭·마스터 사용자를
mail-accountsSecret에 쓰고(승인/거절 직후 + 2분마다), 사이드카가 5초마다 서버 설정 폴더로 복사 → 서버가 변경을 감지해 반영 - 읽기: 사용자에게는 메일 비밀번호가 없음. 포털에 로그인한 사용자의 메일함을, 포털이 Dovecot 마스터 사용자(
주소*yws-portal)로 대신 열어서 보여줌. 외부 메일 앱(IMAP)은 아직 지원 안 함
보안
- 없는 주소는 SMTP 단계에서 거절 (550) — 받은 뒤 반송하지 않아서 스팸 역산란(backscatter)이 없음
- 외부로 중계 안 함 (554 Relay access denied) — 오픈 릴레이 아님. 발신자 도메인이 메일을 받지 않는 도메인(null MX)이거나 HELO가 정식 호스트명이 아니면 거절
- IMAP은 포털 네임스페이스에서만 접근 가능 (NetworkPolicy), 평문 로그인도 클러스터 내부 대역에서만 허용
- HTML 메일은 격리 iframe으로 표시: 스크립트 실행 불가(sandbox), 포털과 다른 오리진, 외부 리소스 로딩 전부 차단(CSP) — 추적 픽셀도 안 불림. 첨부파일은 항상 다운로드로만 제공(브라우저에서 열지 않음)
검증
- 외부(Gmail)에서 보낸 메일 수신 확인 — ISP가 인바운드 25번을 막지 않음
- QA 테스트: 실제 SMTP 전달 → 웹메일 목록/읽기, HTML 격리, 첨부 바이트 일치, 삭제, 없는 주소/중계 거절
보내기
메일 탭의 + 새 메일 또는 메일 읽기 화면의 답장. 보낸 메일은 보낸편지함에 저장됩니다.
- 발송 경로: 포털 → Amazon SES(서울 리전, SMTP 587 + STARTTLS) → 받는 쪽. 집 회선에서 직접 보내지 않는 이유: 아웃바운드 25번은 열려 있지만 집 IP에 역방향 DNS(PTR)가 없고 가정용 IP라서 Gmail 등이 거부하거나 스팸 처리함 (PTR은 ISP만 설정 가능)
- 도메인 인증: SES에
jimmy-home.com을 도메인 identity로 등록 — DKIM(d=jimmy-home.com, Easy DKIM CNAME 3개), 사용자 지정 MAIL FROMbounce.jimmy-home.com(MX → SES 피드백 서버, SPF TXT), DMARC_dmarcTXT(p=none) - 반송/스팸 신고: SES가 MAIL FROM(
bounce.)으로 받아 처리하고, 보낸 사람 주소로 알림을 보냄 → 우리 수신 서버를 거쳐 본인 받은편지함으로 들어옴 - 왜 메일 서버(Postfix)가 아니라 포털이 직접 SES로 보내나: 메일 서버에서 인증된 발송을 받으려면 TLS 인증서(→ Cloudflare 토큰)가 필요하고, 인증 없이 파드 IP로 믿으면 같은 대역의 사용자 EC2도 우리 SES로 메일을 보낼 수 있게 됨. 포털은 로그인한 사용자 본인으로만 보내므로 이 문제가 없음. SES SMTP 자격 증명은
mail네임스페이스의ses-smtpSecret에 있고 포털은 읽기만 함 - 남용 방지: 보내는 주소는 항상 본인 주소, 한 통에 받는 사람 20명, 사용자당 최근 24시간 100명, 첨부 합계 10MB. 계정이 털려도 SES 계정 정지(→ 전체 발송 중단)로 번지지 않게 하는 장치
- SES 샌드박스: 2026-09-27 production access 승인 완료 — 외부 주소로도 발송 가능 (Gmail 수신 확인)
- 검증: 포털 → SES → Gmail 외부 발송 수신 확인, 포털 → SES → 인터넷 → 우리 MX 왕복 확인. 발신 IP
smtp-out.ap-northeast-2.amazonses.com,d=jimmy-home.comDKIM 서명, MAIL FROMbounce.jimmy-home.com, Rspamd 정상 판정
Lambda 코드에서도 send_mail(to, cc, subject, body)로 보낼 수 있습니다 (Lambda의 내장 함수 참고).
메일 트리거 (메일 → Lambda)
Lambda 상세 화면의 트리거 → 메일에서 태그를 정하면 사용자이름+태그@jimmy-home.com 주소가 생기고, 그 주소로 오는 메일마다 Lambda가 실행됩니다 (AWS의 SES 수신 규칙 → Lambda와 같은 개념).
- 주소는 항상 내 이름 아래(
내이름+태그@)라서 남의 메일을 가로챌 수 없음.+태그메일은 원래 내 받은편지함으로 배달되는 주소(plus addressing) - 처리 방식: 메일은 먼저 받은편지함에 저장되고, 포털이 15초마다 트리거가 있는 메일함을 확인해서 해당 주소로 온(
Delivered-To헤더 기준) 아직 처리 안 된 메일로 Lambda를 실행. 처리된 메일에는 IMAP 키워드YwsTriggered를 붙임- "메일함에 보관"을 끄면 실행 성공 후 메일을 지움
- 트리거를 만들기 전에 온 메일은 실행하지 않음
- Lambda가 5xx로 실패하면 다음 주기에 다시 시도, 5번 실패하면
YwsTriggerFailed를 붙이고 포기 - 트리거마다 분당 30회까지 (넘치는 메일은 다음 주기로 밀림)
- 스팸은 스팸함으로 가서 실행되지 않음
- 최소 한 번(at-least-once): Lambda 실행 직후 처리됨 표시를 남기기 전에 포털이 재시작되면 같은 메일로 한 번 더 실행될 수 있음 → 중복에 민감한 처리는
message_id로 걸러낼 것
이벤트 형식:
{
"trigger": "mail",
"mail": {
"recipient": "[email protected]",
"tag": "orders",
"from": {"name": "홍길동", "address": "[email protected]"},
"to": [{"name": "", "address": "[email protected]"}],
"cc": [], "reply_to": [],
"subject": "주문 접수",
"date": "Sat, 26 Sep 2026 21:30:59 +0900",
"received_at": "2026-09-26T12:31:09+00:00",
"message_id": "<[email protected]>",
"text": "본문(텍스트)", "html": "<p>본문(HTML)</p>",
"attachments": [{"filename": "a.pdf", "content_type": "application/pdf", "size": 1234,
"content_base64": "...", "omitted": false}],
"authentication_results": ["...SPF/DKIM/DMARC 판정..."],
"headers": [["From", "..."], ["Subject", "..."]]
}
}
- 첨부파일은 합계 5MB까지
content_base64로 들어가고, 넘는 파일은content_base64: null, omitted: true - 보낸 사람 주소는 위조될 수 있음 — 중요한 처리라면
authentication_results(SPF/DKIM/DMARC)를 확인할 것
하지 않기로 한 것 (2026-09-29)
- 외부 메일 앱(IMAP 993) 연결: 메일은 콘솔 웹메일로만 읽음
- 집 IP 변경 시
mailA 레코드 자동 갱신: IP가 바뀌면 관리자가 Cloudflare에서mail.jimmy-home.comA 레코드를 직접 수정 (안 고치면 그동안 외부 메일 수신이 끊김)