feat(docker): 리버스 프록시가 다른 서버에 있을 때 — frontend 를 사설 IP 에만 게시하는 선택 파일 #6

Merged
ttipo merged 5 commits from feat/deploy-remote-proxy into main 2026-10-30 19:55:49 +09:00
Owner

무엇을 · 왜

확인 방법

영향 범위

  • backend
  • frontend
  • flutter
  • docker / 인프라
  • 규칙 문서(.claude/)

공유 계약(에러 코드 · 응답 포맷 · 페이지네이션 · API 규약)을 바꿨다면
세 스택에 미치는 영향을 아래에 구체적으로 적어 주세요.

체크리스트

  • 린트/분석 통과 (Warning 0) · 변경 영역 테스트 통과
  • 주석 한국어, 매직 넘버·하드코딩 문자열 없음
  • 사용자 노출 실패는 모두 에러 코드와 함께 처리 (BusinessException / error.code 분기)
  • 에러 코드를 바꿨다면 npm run sync:error-codes 실행 후 생성 파일까지 커밋
  • 민감정보 미커밋 (*.example 값은 placeholder)
  • 로그에 비밀번호·토큰·요청 본문 전체가 남지 않음
## 무엇을 · 왜 <!-- 무엇을 바꿨고 왜 필요한지 적습니다. "무엇을"은 diff 가 말해주므로 "왜"에 비중을 둡니다. --> ## 확인 방법 <!-- 리뷰어가 동작을 확인할 수 있는 절차나 명령을 적습니다. --> ## 영향 범위 - [ ] backend - [ ] frontend - [ ] flutter - [ ] docker / 인프라 - [ ] 규칙 문서(.claude/) > **공유 계약(에러 코드 · 응답 포맷 · 페이지네이션 · API 규약)을 바꿨다면** > 세 스택에 미치는 영향을 아래에 구체적으로 적어 주세요. ## 체크리스트 <!-- 자세한 항목은 .claude/rules/checklist.md 참조 --> - [ ] 린트/분석 통과 (Warning 0) · 변경 영역 테스트 통과 - [ ] 주석 한국어, 매직 넘버·하드코딩 문자열 없음 - [ ] 사용자 노출 실패는 모두 에러 코드와 함께 처리 (`BusinessException` / `error.code` 분기) - [ ] 에러 코드를 바꿨다면 `npm run sync:error-codes` 실행 후 생성 파일까지 커밋 - [ ] 민감정보 미커밋 (`*.example` 값은 placeholder) - [ ] 로그에 비밀번호·토큰·요청 본문 전체가 남지 않음
ttipo added 2 commits 2026-10-30 16:18:49 +09:00
운영 메일 서버를 NCP 에 따로 두고, 포털의 TLS 는 기존 서버의 Nginx Proxy Manager(다른 사이트들과 80 · 443 을 함께 쓴다)가
같은 VPC 사설망으로 넘기기로 했다. 메일 엔진이 그 서버의 443 을 직접 써야 해서(ACME · MTA-STS) 프록시를 같은 호스트에 둘 수 없다.

- docker-compose.remote-proxy.yml: frontend 를 proxy 네트워크에서 빼고 8080 을 FRONTEND_PRIVATE_BIND(사설 IPv4:포트)에만 게시한다.
  compose 의 ${…:?} 는 빈 값만 막아 포트뿐인 값이면 모든 주소에 열리므로, 한 번짜리 frontend-bind-check 가 값을 먼저 보고
  아니면 frontend 를 띄우지 않는다(네트워크 없음 · 비-root · DB 이미지의 셸).
- .env.production 의 COMPOSE_FILE 로 켠다 — 운영 명령(--env-file .env.production)을 그대로 쓰고 -f 를 빠뜨려 기본 구성으로 도는 일이 없게.
- TRUST_PROXY 는 <EDGE_NETWORK_SUBNET>,<프록시 서버 사설 IP> 주소 목록, NPM 은 v2.16.0 소스로 기본값을 확인해 고급 칸 세 줄
  (읽기 150s · 접근 로그 끔 · 사설 대역의 X-Real-IP 믿지 않기)과 Block Common Exploits 끄기(메일 검색이 403)를 적었다.
- NCP 는 공인 IP 를 사설 IP 로 NAT 하므로 게시 주소로는 바깥을 막지 못한다 — 8080 은 ACG 가 프록시 서버 사설 IP 에서만 받는다.
- CI infra 잡이 이 조합의 config 와 frontend-bind-check 의 받을 값 · 거절할 값을 본다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(rules): 다른 서버의 리버스 프록시 — frontend 게시 예외 · COMPOSE_FILE · TRUST_PROXY 주소 목록
CI / Frontend (lint · type-check · test · build) (pull_request) Successful in 12m6s
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Failing after 7m16s
CI / Infra (compose · Dockerfile · nginx) (pull_request) Successful in 28s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 10m26s
4047bd30e7
운영 frontend 는 호스트 포트를 열지 않는다는 규칙에 예외(remote-proxy — 사설 IP 에만)를 적고, compose 가 --env-file 의
COMPOSE_FILE 을 읽는다는 것(빈 값은 실패), 프록시가 다른 서버면 TRUST_PROXY 를 주소 목록으로 둔다는 것을 env · 체크리스트에 맞췄다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ttipo added 3 commits 2026-10-30 19:18:35 +09:00
엔진의 DNS 조회(DNSBL · SPF · DKIM · DMARC · MX · PTR · TLSA)를 공용 · ISP DNS 가 아니라
이 서버의 리졸버로 푼다. Spamhaus 는 공용 DNS 를 거친 조회를 거부하고, DNSSEC 을 검증해야
엔진의 DANE 이 켜지기 때문이다.

- mail-resolver: unbound 1.26.2(digest 고정 · 비-root · cap_drop ALL). mail 과만 닿는
  internal 네트워크 mail-dns 와 바깥 길 mail-dns-egress. 사설 주소의 답 · 질의를 막는다.
- setup: 바꾸기 전에 리졸버 네트워크 · 주소 · DNSSEC 을 확인한다(UDP 는 루트 SOA, TCP 는
  루트 DNSKEY — UDP DNSKEY 는 루트 키 교체 기간에 잘려 오탐이 난다). 엔진 DNS 는 운영 Custom,
  개발 System. Spamhaus 키가 있으면 시험 항목을 확인한 뒤 DQS 로 켜고 없으면 두 항목을 끈다.
  키는 모든 출력에서 가린다.
- mail 은 리졸버를 기다리되(required: false) 리졸버 오류 하나로 메일 전체가 서지 않게 한다.
- README: 리졸버와 스팸 차단 목록, 운영 배포 차례, 리졸버 장애 때(리로드 거절 · restart 비상 길),
  DNSSEC 이 깨진 도메인의 예외.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
실제 시범은 직원 메일함이 아니라 관리용 새 주소로 한다. mailcow 에 같은 주소의 메일함을
받기 전용으로 먼저 만들어야 Transport Map 과 reject_unlisted_sender 가 맞는다.
운영 mailcow 는 SSH 없이 관리 화면만 쓸 수 있어, 명령 절차마다 화면에서 하는 길을 표로 적었다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(rules): 메일 전용 리졸버 · Spamhaus 키 — docker 스킬 · env 규칙
CI / Backend (lint · type-check · test · e2e · build) (pull_request) Successful in 11m38s
CI / Frontend (lint · type-check · test · build) (pull_request) Successful in 12m22s
CI / Infra (compose · Dockerfile · nginx) (pull_request) Successful in 37s
CI / 에러 코드 SSOT 동기화 검사 (pull_request) Successful in 11m33s
CI / Backend (lint · type-check · test · e2e · build) (push) Successful in 12m50s
CI / Frontend (lint · type-check · test · build) (push) Successful in 17m42s
CI / Infra (compose · Dockerfile · nginx) (push) Successful in 41s
CI / 에러 코드 SSOT 동기화 검사 (push) Successful in 10m31s
c3dba24be0
docker 스킬에 리졸버 네트워크 두 개 · 이미지 고정(digest) · 비-root · CI 설정 검사 ·
장애와 재부팅 때 할 일을, env 규칙에 MAIL_RESOLVER_SUBNET/IP 와 setup 전용 비밀
MAIL_SPAMHAUS_DQS_KEY(운영은 파일에만)를 더했다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ttipo merged commit c3dba24be0 into main 2026-10-30 19:55:49 +09:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ttipo/arasystem#6