YWS 문서
YWS 콘솔로 이동
앱 연동/메일

메일

승인된 모든 사용자에게(앱 전용 계정 포함) 사용자이름@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 프록시로 가지만, 명시적 mail A 레코드가 와일드카드보다 우선함 (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-accounts Secret에 쓰고(승인/거절 직후 + 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 FROM bounce.jimmy-home.com(MX → SES 피드백 서버, SPF TXT), DMARC _dmarc TXT(p=none)
  • 반송/스팸 신고: SES가 MAIL FROM(bounce.)으로 받아 처리하고, 보낸 사람 주소로 알림을 보냄 → 우리 수신 서버를 거쳐 본인 받은편지함으로 들어옴
  • 왜 메일 서버(Postfix)가 아니라 포털이 직접 SES로 보내나: 메일 서버에서 인증된 발송을 받으려면 TLS 인증서(→ Cloudflare 토큰)가 필요하고, 인증 없이 파드 IP로 믿으면 같은 대역의 사용자 EC2도 우리 SES로 메일을 보낼 수 있게 됨. 포털은 로그인한 사용자 본인으로만 보내므로 이 문제가 없음. SES SMTP 자격 증명은 mail 네임스페이스의 ses-smtp Secret에 있고 포털은 읽기만 함
  • 남용 방지: 보내는 주소는 항상 본인 주소, 한 통에 받는 사람 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.com DKIM 서명, MAIL FROM bounce.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 변경 시 mail A 레코드 자동 갱신: IP가 바뀌면 관리자가 Cloudflare에서 mail.jimmy-home.com A 레코드를 직접 수정 (안 고치면 그동안 외부 메일 수신이 끊김)