fix(docker): mail setup 받는 메일 크기 한도 50MiB · 전환 준비 문서 (mailcow 확인 · 서버 B 배포 · 백업 · 큰 메일) #12

Merged
ttipo merged 14 commits from feat/mail-migration-password into main 2026-11-04 07:59:25 +09:00
3 changed files with 9 additions and 4 deletions
Showing only changes of commit 9b530da6e1 - Show all commits
+1 -1
View File
@@ -17,7 +17,7 @@
12단계부터 운영은 `MAIL_WEBHOOK_SECRET`(엔진 웹훅 서명 키 — 영문 · 숫자 32자 이상, `openssl rand -hex 24`)도 필수다 — setup 과 backend 가 같은 값을 읽는다
13단계부터 운영은 `DB_IMAGE` 를 예시의 고정값(판 · digest)으로 두고, 첫 setup 은 복구 관리자와 함께(`--recovery` — 관리자 허용 IP 를 처음 걸 때 되돌릴 길) 돌린다.
이미 운영 중인 서버는 mail/README "13단계 전부터 운영 중인 서버" 차례를 따른다
- [ ] 운영 백업 · 감시(13단계 — 호스트 systemd, mail/README "백업 › 설치" · "감시"): Object Storage 비공개 버킷 + 그 버킷만 쓰는 하위 계정 키, restic 비밀번호(**서버 밖에도 보관** — 잃으면 백업을 풀 수 없다),
- [ ] 운영 백업 · 감시(13단계 — 호스트 systemd, mail/README "백업 › 설치" · "감시"): Object Storage 비공개 버킷 + Object Storage 만 쓰는 하위 계정 키(버킷에는 따로 키가 없고 NCP 는 키를 버킷 하나로 좁히지 못한다 — 같은 계정에 다른 버킷을 두지 않는다), restic 비밀번호(**서버 밖에도 보관** — 잃으면 백업을 풀 수 없다),
`/etc/arasystem/ops.env`, 타이머 셋(백업 03:30 · 점검 15분 · 부팅 되살리기) 켜기, 첫 백업 뒤 복원 연습(`drill`) 한 번. Healthchecks 체크 둘 · UptimeRobot 감시 셋 — 알림은 우리 메일 서버가 아닌 주소로
- [ ] `backend/.env.{env}.example` → `backend/.env.{env}` 복사 후 `JWT_SECRET` 등 고유 시크릿 작성 (compose `env_file` 로 로드 — 파일이 없으면 기동 실패)
- [ ] `flutter/.env.example` → `flutter/.env` 복사 후 `API_URL` 확인 (Android 에뮬레이터: `10.0.2.2`)
+6 -2
View File
@@ -288,7 +288,7 @@ node --env-file=.env.development mail/scripts/setup.mjs
- **계정 보안(12단계)** — `.env.production` 의 `MAIL_WEBHOOK_SECRET`(엔진 웹훅의 서명 키 — `openssl rand -hex 24` 로 만든 값, 사람이 지은 값은 쓰지 않는다).
운영 필수다 — 아래 2)의 `up` **전에** 넣는다(없으면 백엔드가 env 검사에서 멈춰 웹메일 전체가 서고, setup 도 멈춘다).
직원에게 미리 알린다 — 2단계 인증을 켜면 아웃룩 · 휴대폰 메일 앱은 앱 비밀번호로 다시 연결해야 한다 ([계정 보안](#계정-보안-12단계--인프라))
- **전환 준비(13단계)** — 이미 떠 있는 서버는 [13단계 전부터 운영 중인 서버](#13단계-전부터-운영-중인-서버)의 차례대로(첫 setup 은 `--recovery`). 첫 직원을 옮기기 **전에** 백업과 15분 점검을 켠다: NCP Object Storage 버킷 · 그 버킷만 쓰는 하위 계정 키 · restic 비밀번호(비밀번호 관리자에 따로 보관),
- **전환 준비(13단계)** — 이미 떠 있는 서버는 [13단계 전부터 운영 중인 서버](#13단계-전부터-운영-중인-서버)의 차례대로(첫 setup 은 `--recovery`). 첫 직원을 옮기기 **전에** 백업과 15분 점검을 켠다: NCP Object Storage 비공개 버킷 · Object Storage 만 쓰는 하위 계정 키 · restic 비밀번호(비밀번호 관리자에 따로 보관),
Healthchecks.io 점검 둘 · UptimeRobot 감시 셋 ([백업](#백업) · [감시](#감시-13단계)).
`.env.production` 의 `DB_IMAGE` 를 예시와 같은 값(판 · digest 고정 — 옛 파일은 `postgres:15-alpine`)으로 바꾼다 — 다음 `up -d` 에서 db 가 다시 만들어진다(몇 초 · 데이터는 볼륨 그대로, 같은 메이저 15 라 덤프 · 복원은 필요 없다).
frontend 는 `MAIL_DOMAIN` · `MAIL_HOSTNAME` 으로 [메일 프로그램 자동 설정](#메일-프로그램-자동-설정) 응답을 만든다 — 이미 있는 값이라 새로 넣을 것은 없다
@@ -1769,7 +1769,11 @@ sudo systemctl enable arasystem-mail-recover.service
systemctl list-timers 'arasystem-*'
```
- **NCP Object Storage** — 콘솔에서 한국 리전에 버킷을 만든다(공개 안 함 · 수명 주기 삭제 규칙 없음 — 지우기는 restic 이 한다). API 키는 이 버킷만 쓰는 서브 계정으로.
- **NCP Object Storage** — 콘솔에서 한국 리전에 버킷을 만든다(공개 안 함 · 수명 주기 삭제 규칙 없음 — 지우기는 restic 이 한다).
**버킷에는 따로 키가 없다** — API 인증키(Access Key ID · Secret Key)는 Object Storage 만 쓰는 **서브 계정**의 것을 쓴다(메인 계정 키는 서버 · 결제까지 연다):
콘솔 › Sub Account › Sub Accounts › 서브 계정 생성(접근 유형은 **API Gateway 접근만** — 콘솔 로그인 없음) → 그 계정의 **정책** 탭에 관리형 `NCP_OBJECT_STORAGE_MANAGER` 하나
(`…_VIEWER` 는 읽기만이라 백업이 안 된다) → **Access Key** 탭에서 추가 — **Secret Key 는 만들 때만 보인다**, 그 자리에서 비밀번호 관리자(restic 비밀번호와 같은 항목)에 둔다 → ops.env 의 `AWS_ACCESS_KEY_ID` · `AWS_SECRET_ACCESS_KEY`.
NCP 문서에는 키를 버킷 하나로 좁히는 방법이 없다(2026-11-03 — 사용자 정의 정책도 버킷을 고르는 칸이 문서에 없다) — 이 키로 같은 계정의 버킷이 모두 열리므로 다른 버킷을 두지 않는다(시험 버킷은 쓸모가 끝나면 지운다).
만들 때의 다른 칸은 기본값 그대로 — **잠금 해제**(보존 기간을 걸면 restic 이 지난 백업을 지우지 못해 prune 이 실패한다) · **암호화(KMS) 해제**(restic 이 이미 암호화한다. 한 번 켜면 끌 수 없다) · 타 계정 권한 없음.
"공개"는 버킷의 폴더 · 파일 목록을 누구에게나 연다 — restic 은 키로 서명해 들어가므로 필요 없다(NCP 가이드 "버킷 관리", 2026-11-03 확인).
`RESTIC_REPOSITORY=s3:https://kr.object.ncloudstorage.com/<버킷>/arasystem`, `RESTIC_OPTIONS='-o s3.region=kr-standard -o s3.bucket-lookup=path'` —
+2 -1
View File
@@ -31,7 +31,8 @@ RESTIC_REPOSITORY=s3:https://kr.object.ncloudstorage.com/change-me-bucket/arasys
# NCP 의 S3 옵션 — 지역 kr-standard(서명 V4 에 쓴다) · 경로 방식(https://<엔드포인트>/<버킷> — NCP 는 경로 · 가상 호스트 방식 둘 다 받는다)
# (근거: NCP API 가이드 Object Storage — https://api.ncloud-docs.com/docs/storage-objectstorage · …-listobjects)
RESTIC_OPTIONS='-o s3.region=kr-standard -o s3.bucket-lookup=path'
# NCP 서브 계정의 API 인증키 — 이 버킷만 쓰게 좁힌 계정으로 (콘솔 › Sub Account)
# NCP 서브 계정의 API 인증키 — Object Storage 만 쓰는 서브 계정으로 (콘솔 › Sub Account — 접근 유형 API Gateway 만 · 정책 NCP_OBJECT_STORAGE_MANAGER 하나 · Access Key 탭).
# 버킷에는 따로 키가 없고, NCP 는 키를 버킷 하나로 좁히지 못한다 — 같은 계정에 다른 버킷을 두지 않는다. Secret Key 는 만들 때만 보인다(바로 비밀번호 관리자에)
AWS_ACCESS_KEY_ID=change-me
AWS_SECRET_ACCESS_KEY=change-me
# 보존 — 매일 7 · 매주 4 · 매달 6 (그보다 오래된 백업은 지운다 — 지운 메일도 이 기간까지만 백업에 남는다)