generated from ttipo/project-base
docs(rules): 10단계 — 파일럿 키(setup 전용 · 셸 예외)와 docker 스킬의 파일럿 공존
CI / Infra (compose · Dockerfile · nginx) (pull_request) Successful in 18s
CI / Backend (lint · type-check · test · e2e · build) (push) Successful in 13m30s
CI / Infra (compose · Dockerfile · nginx) (push) Successful in 21s
CI (Flutter) / Flutter (analyze · test) (pull_request) Successful in 22m38s
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Successful in 11m56s
CI / Frontend (lint · type-check · test · build) (pull_request) Successful in 11m55s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 10m20s
CI / Frontend (lint · type-check · test · build) (push) Successful in 11m58s
CI / 에러 코드 SSOT 동기화 검사 (push) Successful in 10m18s
CI / Infra (compose · Dockerfile · nginx) (pull_request) Successful in 18s
CI / Backend (lint · type-check · test · e2e · build) (push) Successful in 13m30s
CI / Infra (compose · Dockerfile · nginx) (push) Successful in 21s
CI (Flutter) / Flutter (analyze · test) (pull_request) Successful in 22m38s
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Successful in 11m56s
CI / Frontend (lint · type-check · test · build) (pull_request) Successful in 11m55s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 10m20s
CI / Frontend (lint · type-check · test · build) (push) Successful in 11m58s
CI / 에러 코드 SSOT 동기화 검사 (push) Successful in 10m18s
파일럿 키 둘은 setup 만 읽는다(compose · backend 에 넘기지 않는다). 셸 값과 env 파일이 다르면 멈추는 규칙의 예외를 바로 적었다 — 복구 관리자(원래 셸 일회성)와 파일럿 키: 개발은 한 번 시험하려고 셸로 줄 수 있고, 운영은 파일에 없는 시범 값이 셸에 있으면 멈춘다 (엔진에 남는 설정이라 파일만 보고도 알 수 있게). docker 스킬에는 켜고 끄는 차례 · 가드 없는 relay 를 만들지 않는 규칙 · "끄지 못했습니다"일 때 할 일 · 운영 확인 항목을 더했다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit was merged in pull request #5.
This commit is contained in:
@@ -30,6 +30,7 @@ description: 환경변수 단일 진실 공급원(SSOT) 구조 — 루트 .env.{
|
||||
│ MAIL_DIRECTORY_LOCAL_PART(사내 목록 계정, 기본 mail-directory)도 함께 읽는다 — 계정은 setup 이 복구 관리자로만 만든다(운영 --recovery)
|
||||
└─▶ mail/scripts/setup.mjs : `node --env-file=.env.{APP_ENV}` 로 같은 파일을 직접 읽는다 (mail/README.md)
|
||||
MAIL_ATTACHMENT_*(첨부 한도)도 읽는다 — 엔진의 임시 업로드 한도를 그 20배로 맞추므로 첨부 한도를 바꾸면 setup 도 다시 돌린다
|
||||
MAIL_PILOT_LEGACY_HOST · MAIL_PILOT_LEGACY_IPS(10단계 파일럿 공존 — 옛 서버 주소 · 그 서버가 보내는 IP)는 setup 만 읽는다(compose · backend 에 넘기지 않는다)
|
||||
```
|
||||
|
||||
규칙:
|
||||
@@ -53,6 +54,8 @@ description: 환경변수 단일 진실 공급원(SSOT) 구조 — 루트 .env.{
|
||||
- **루트 env 를 Node 스크립트가 직접 읽을 때**(`node --env-file=…`) 두 가지를 주의한다.
|
||||
- Node 는 **셸에 이미 있는 변수를 덮어쓰지 않는다** — 셸에 `APP_ENV=development` 가 남은 채 `.env.production` 을 주면 개발 값으로 돈다.
|
||||
스크립트는 env 파일 값과 셸 값이 다르면 중단해야 한다 (`mail/scripts/setup.mjs` 의 `findShellOverrides()`).
|
||||
예외는 복구 관리자 `MAIL_RECOVERY_ADMIN`(원래 셸에서 일회성으로 주는 값이라 검사하지 않는다)과 파일럿 키 둘이다 — 파일럿 키는 개발은 한 번 시험하려고 셸로 줄 수 있고(`[참고]` 를 낸다),
|
||||
운영은 반대로 파일에 없는 파일럿 값이 셸에 있으면 중단한다 (파일럿은 엔진에 남는 설정이라 파일만 보고도 알 수 있어야 한다). 운영에서 파일럿을 끄는 것은 값을 비우고 `--end-pilot` 을 줄 때뿐이다 — 값이 빠졌다고 저절로 끄지 않는다.
|
||||
- Node 는 따옴표 없는 값의 `#` 부터를 **공백이 없어도** 주석으로 자르고, compose 는 그대로 읽는다.
|
||||
비밀번호 등은 영문·숫자로만 만든다 (`#`·따옴표·`$`·공백 금지).
|
||||
- 모든 env 파일은 커밋 금지, `*.example` 만 커밋한다. (`.gitignore` 의 `!*.example` 재포함 규칙)
|
||||
@@ -74,6 +77,7 @@ description: 환경변수 단일 진실 공급원(SSOT) 구조 — 루트 .env.{
|
||||
> 메일 `MAIL_IMAGE`(패치까지 고정)·`MAIL_DOMAIN`·`MAIL_HOSTNAME`·`MAIL_PUBLIC_URL`·`MAIL_ADMIN_LOCAL_PART`(관리자 아이디, 기본 `admin` — setup·backend 공용, bootstrap 전에만)·
|
||||
> `MAIL_ADMIN_PASSWORD`·`MAIL_RECOVERY_ADMIN`(운영은 일회성 override)·`MAIL_GATEWAY_USER`/`MAIL_GATEWAY_PASSWORD`·`MAIL_DIRECTORY_LOCAL_PART`(사내 목록 계정 아이디, 기본 `mail-directory` — setup·backend 공용)·
|
||||
> `MAIL_JMAP_URL`·`MAIL_ATTACHMENT_MAX_SIZE_MB`/`MAIL_ATTACHMENT_TOTAL_MAX_SIZE_MB`·`MAIL_TRUSTED_NETWORKS`(비우면 `APP_NETWORK_SUBNET`, 그보다 넓으면 setup 실패)·
|
||||
> `MAIL_BLOCKED_ACCOUNTS`(선택 — 관리자 외에 웹메일 로그인·대리 접근을 막을 주소, 콤마. 비어 있어도 관리자는 항상 막힌다)
|
||||
> `MAIL_BLOCKED_ACCOUNTS`(선택 — 관리자 외에 웹메일 로그인·대리 접근을 막을 주소, 콤마. 비어 있어도 관리자는 항상 막힌다)·
|
||||
> `MAIL_PILOT_LEGACY_HOST`/`MAIL_PILOT_LEGACY_IPS`(선택 — 파일럿 공존 동안만, setup 전용. mail/README.md "파일럿 공존")
|
||||
> (개발 전용 `MAIL_ADMIN_PORT`·`MAIL_DEV_USER_PASSWORD`·`MAILPIT_IMAGE`/`MAILPIT_UI_PORT`). 실제 목록은 `.env.development.example` 참조.
|
||||
> 실행/배포 명령은 [[docker]], 보안 규칙은 [[security]] 참조.
|
||||
|
||||
@@ -317,6 +317,23 @@ node --env-file=.env.development mail/scripts/setup.mjs
|
||||
- 영구 관리자 아이디는 `MAIL_ADMIN_LOCAL_PART`(기본 `admin`) — setup 과 백엔드가 함께 읽는다 (compose 가 backend 에 넘긴다).
|
||||
**bootstrap 전에만 정할 수 있다** — 바꾼 값이 엔진의 관리자와 다르면 setup 이 멈춘다. 다른 관리자 계정은 `MAIL_BLOCKED_ACCOUNTS` 에 넣는다.
|
||||
- 사내 목록 계정 `<MAIL_DIRECTORY_LOCAL_PART>@도메인`(기본 `mail-directory`, compose 가 backend 에 넘긴다) — 백엔드가 엔진의 계정 목록에서 이름·주소만 읽는 비밀번호 없는 계정(게이트웨이 대리 접근으로만). 엔진 관리자는 그 권한을 내줄 수 없어 setup 이 **복구 관리자로 돌 때만** 만든다(개발은 늘, 운영은 `--recovery` — 이미 운영 중이면 한 번 더). 없으면 `[주의]` 만 내고 백엔드는 로그인한 직원 기록으로 대신한다 (mail/README.md "사내 목록 계정").
|
||||
- **파일럿 공존**(10단계 — mail/README.md "파일럿 공존"): 루트 env `MAIL_PILOT_LEGACY_HOST`(옛 서버 mailcow 주소)·`MAIL_PILOT_LEGACY_IPS`(그 서버가 보내는 IP)를 넣으면
|
||||
setup 이 새 서버에 없는 사내 주소를 옛 서버로 중계하게 한다. 켜는 차례는 막는 것부터 — 시범 표시(옛 서버 IP 의 자동 차단 예외 — 아래 식들의 주인 표시) →
|
||||
받는 쪽 가드(인증 없는 모르는 사내 주소 550)·위조 막기·옛 서버 IP 의 검사 · 한도 예외 → 리로드 → **켜기 전 가드 확인** → 경로(리로드) →
|
||||
도메인의 `allowRelaying`(저장 즉시 먹어 가드보다 먼저 켜면 열린 중계가 된다) → 켠 뒤 확인. 끌 때는 거꾸로(`allowRelaying` 먼저) — 운영은 값을 비우고 `--end-pilot` 일 때만.
|
||||
**가드 없는 relay 를 setup 이 만들지 않는다** — 가드 확인은 "2초 안에 550"이다(가드 거절은 곧바로 · 엔진 자체 거절은 `waitOnFail` 5초 뒤라 응답 시간으로 가른다 — 그래서 `waitOnFail` 은 4초 이상).
|
||||
켜기 전에 아니면 켜지 않고, 켠 뒤에 아니면 relay 를 바로 다시 끈다(둘 다 종료 코드 1). 끄지 못하면 "끄지 못했습니다"로 멈춘다 — 이미 켜진 relay 를 처음 확인할 때(0단계)는
|
||||
멈추기 전에 RCPT 가드만 다시 걸고 그 결과를 싣는다. "대신 RCPT 가드를 다시 걸어 지금은 가드가 답합니다"면 열린 relay 는 아니고(relay 는 켜진 채),
|
||||
"열려 있습니다 · 열려 있을 수 있습니다"면 관리 화면에서 그 도메인의 relay 허용을 바로 끈다.
|
||||
가드는 `allowRelaying` 이 꺼진 것을 엔진에서 다시 읽어 확인한 뒤에만 뺀다 — 켜져 있는데 setup 의 `legacy` 경로가 없으면
|
||||
끄지 않고 가드도 남긴 채 종료 코드 1(요약 `끝내지 못함`, 시범 설정이 하나도 없으면 `시범 설정 없음 · allowRelaying 켜짐`).
|
||||
개발은 셸로 한 번 시험한다(`MAIL_PILOT_LEGACY_HOST=mailpit:1025 node --env-file=.env.development mail/scripts/setup.mjs` — 다음 평소 실행이 끈다.
|
||||
엔진 로그에서 개발 외부 경로 `mailpit` 과 가르려면 컨테이너 이름 `<PROJECT_NAME>-mailpit:1025` 를 준다).
|
||||
운영 확인 항목: 새 호스트 → mailcow **25** 가 열려 있을 것(운영은 25 만 받는다), 파일럿 동안 mx1 에 AAAA 를 두지 않을 것(SPF 는 ip4 만), mailcow 가 보내는 IP 가 목록과 같을 것(빗나가면 동료 메일이 `550 5.7.1`),
|
||||
그 IP 로 사람이 만든 자동 차단 예외(`x:AllowedIp`) · 검사 예외(SPF · DMARC 등) · IP 한도 예외가 없을 것(setup 이 시범 표시를 붙여 만든다 — 있으면 멈춘다),
|
||||
새 DKIM TXT 를 **켜기 전에** DNS 에 넣을 것(운영은 처음 켤 때 TXT 가 없거나 다르면 켜지 않는다),
|
||||
끄기 · 되돌리기 전에 새 서버 대기열에 동료 앞 메일이 남지 않았을 것(끈 뒤에는 로컬 실패 → 반송).
|
||||
개발의 인증 발송 확인(465)은 백엔드 컨테이너의 node 로 돈다(메일 이미지에는 node 가 없다 — 비밀번호는 표준 입력으로 넘기고 출력하지 않는다).
|
||||
- ⚠️ **게이트웨이 비밀번호 = 관리자급 비밀.** Stalwart 0.16 은 대리 접근 대상을 제한하지 못해, 신뢰 대역 안에서는 게이트웨이로 관리자 계정도
|
||||
대리 접근된다 (setup 이 매번 `[주의]` 로 보여 준다). 막는 일은 백엔드가 한다 — mail/README.md "보안".
|
||||
|
||||
|
||||
Reference in New Issue
Block a user