Files
arasystem/mail/resolver/unbound.conf
T
ttipo 9466a85daa feat(docker): 메일 엔진 전용 DNS 리졸버(unbound)와 Spamhaus 조회 키(DQS)
엔진의 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>
2026-10-30 19:01:12 +09:00

90 lines
5.1 KiB
Plaintext

# ==========================================
# 메일 엔진 전용 재귀 DNS 리졸버 (unbound) — docker-compose.yml 의 mail-resolver 가 읽는다
#
# 엔진(Stalwart)의 DNS 조회 — 스팸 차단 목록(DNSBL) · SPF · DKIM · DMARC · MX · PTR · TLSA(DANE) — 를
# 공용 · ISP DNS 가 아니라 이 서버에서 루트부터 직접 푼다 (mail/README.md "DNS 리졸버와 스팸 차단 목록").
# - Spamhaus 는 공용 · ISP DNS 를 거친 조회를 거부한다(127.255.255.254). 이 리졸버는 서버 자신의 공인 IP 로 묻는다
# - DNSSEC 을 검증한다 — 엔진의 DANE 이 켜진다 (기동 로그 "DNS resolver cannot validate DNSSEC" 가 사라진다)
#
# 이미지 klutchell/unbound(distroless — 셸 없음)의 기본 설정을 이 파일로 통째로 바꾼다.
# compose 가 처음부터 비-root(101:102)로 띄우므로 이 파일은 그 사용자가 읽을 수 있어야 한다(644 — 저장소를 umask 077 로 클론하지 않는다).
# 바꾼 뒤에는 docker compose … up -d --force-recreate mail-resolver (설정 파일만 바뀌면 compose 가 다시 만들지 않는다)
# ==========================================
server:
# 컨테이너 — 앞에서 돌고, 사용자를 바꾸지 않고(처음부터 비-root), chroot 하지 않는다
do-daemonize: no
username: ""
chroot: ""
directory: "/var/unbound"
pidfile: ""
logfile: ""
use-syslog: no
# 1 = 동작 정보와 오류만. 조회 이름은 남기지 않는다 — DNSBL 조회 이름에 Spamhaus DQS 키가 들어간다
# (log-queries · log-replies · log-servfail · val-log-level 은 기본값(끔) 그대로 둔다)
verbosity: 1
# 받는 곳 — 컨테이너의 53 (UDP · TCP). 엔진은 TCP 도 쓸 수 있어야 DNSSEC 을 믿는다 (Stalwart 의 기동 확인)
# 이 컨테이너는 메일 엔진과의 전용 네트워크(mail-dns — 바깥 길 없음)와 자기 바깥 조회 길(mail-dns-egress)에만 붙고
# 호스트 포트를 열지 않는다 — 닿는 것은 메일 엔진뿐이다. (비-root 가 53 에 붙는 것은 도커가 컨테이너마다
# net.ipv4.ip_unprivileged_port_start=0 으로 두기 때문이다 — 권한(capability)은 모두 뺀다)
interface: 0.0.0.0
port: 53
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
# 묻는 쪽 — 사설 대역(도커 네트워크)과 자기 자신(healthcheck)만 받는다. 1차 막은 네트워크 구성(메일 엔진만 붙는다)이고, 이것은 그 뒤의 막이다 —
# 누가 53 을 호스트에 공개해도 바깥(공인 주소)에서 쓰는 열린 리졸버가 되지 않게
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow
access-control: 172.16.0.0/12 allow
access-control: 192.168.0.0/16 allow
# DNSSEC 검증 — 루트 신뢰 앵커는 이미지에 든 root.key(KSK-2017 · KSK-2024)다. RFC 5011 로 스스로 고쳐 쓰므로
# /var/unbound 는 쓸 수 있어야 한다(컨테이너 쓰기 계층 — read_only 로 두지 않는다). 이미지를 올리면 새 파일로 시작한다
module-config: "validator iterator"
auto-trust-anchor-file: "/var/unbound/root.key"
root-hints: "/var/unbound/root.hints"
harden-dnssec-stripped: yes
harden-glue: yes
harden-below-nxdomain: yes
# 조회 이름은 필요한 만큼만 위로 보낸다(QNAME minimisation — 기본값) — 루트 · .net 서버는 DQS 키가 든 이름 전체를 보지 못한다
qname-minimisation: yes
hide-identity: yes
hide-version: yes
# 사설 · 내부 주소로 풀리는 바깥 이름은 버린다 — 바깥 도메인의 MX 가 이 서버의 사설망(VPC · 도커 네트워크)을 가리켜
# 엔진이 그쪽으로 메일을 보내는 일이 없게.
# ⚠️ 127.0.0.0/8 은 넣지 않는다 — 스팸 차단 목록(DNSBL)은 127.0.0.x 로 답한다
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 100.64.0.0/10
private-address: fc00::/7
private-address: fe80::/10
# 리졸버 자신도 사설 · 내부 주소의 DNS 서버에는 묻지 않는다(위 private-address 와 짝) — 바깥 도메인이 이름 서버(NS)의 주소(glue)를
# 사설 주소로 두어 이 리졸버가 VPC · 호스트 · 도커 네트워크(메일 엔진 포함)의 53 에 묻게 만드는 것을 막는다.
# 127.0.0.0/8 · ::1 은 기본값(do-not-query-localhost: yes)이 이미 막는다. 이 리졸버는 루트부터 직접 풀어 사설 주소에 물을 일이 없다
do-not-query-address: 10.0.0.0/8
do-not-query-address: 172.16.0.0/12
do-not-query-address: 192.168.0.0/16
do-not-query-address: 169.254.0.0/16
do-not-query-address: 100.64.0.0/10
do-not-query-address: fc00::/7
do-not-query-address: fe80::/10
# 크기 — 메일 서버 하나 몫 (compose 메모리 한도 128m 안)
num-threads: 1
msg-cache-size: 8m
rrset-cache-size: 16m
edns-buffer-size: 1232
so-rcvbuf: 0
so-sndbuf: 0
prefetch: yes
prefetch-key: yes