
numa v0.22.0
Rust로 작성된 휴대용 DNS 리졸버 — .numa 로컬 도메인, 광고 차단, 개발자 오버라이드
Numa
당신이 소유하는 DNS. 어디를 가든. — numa.rs
단일 바이너리로 동작하는 휴대용 DNS 리졸버. 어떤 네트워크에서든 광고를 차단하고, 로컬 서비스에 이름을 붙이고(frontend.numa), 자동 복원 기능으로 어떤 호스트명이든 재정의하며, 모든 아웃바운드 쿼리를 ODoH (RFC 9230) 로 봉인하여 누가 당신인지와 무엇을 물었는지를 단일 주체가 동시에 볼 수 없게 합니다 — 모두 노트북에서, 클라우드 계정이나 라즈베리 파이 없이 가능합니다.
Rust로 처음부터 직접 구현했습니다. DNS 라이브러리는 전혀 사용하지 않았습니다. 캐싱, 광고 차단, 로컬 서비스 도메인을 기본 제공합니다. 선택적으로 루트 네임서버로부터의 재귀 해석과 완전한 DNSSEC 신뢰 체인 검증, 그리고 암호화된 클라이언트 연결(iOS Private DNS, systemd-resolved 등)을 위한 DNS-over-TLS 리스너를 제공합니다. numa relay를 실행하면 동일한 바이너리가 공용 ODoH 엔드포인트가 됩니다 — 현재 큐레이션된 DNSCrypt 목록에는 살아남은 릴레이가 하나뿐이므로, 모든 Numa 배포는 생태계를 실질적으로 확장합니다. 약 8MB 바이너리 하나에 모든 것이 내장되어 있습니다. 와이어 프로토콜 파서는 학습 프로젝트로 손으로 작성했으며, 이후 기능(재귀 리졸버, DNSSEC, 대시보드)은 AI의 도움을 받아 구축했습니다.

빠른 시작
세 가지 방법이 있으며, 부담이 적은 순서대로입니다. 세 번째만이 시스템 DNS를 변경합니다.
1. Docker에서 사용해 보기 (호스트에 아무것도 설치하지 않음)
docker run -d --name numa -p 127.0.0.1:5553:53/udp -p 127.0.0.1:5553:53/tcp \
-p 127.0.0.1:5380:5380 ghcr.io/razvandimescu/numa
dig @127.0.0.1 -p 5553 example.com
docker exec numa numa token # dashboard password
http://localhost:5380을 열고 아무 사용자명과 해당 토큰으로 로그인하세요. 포트 5553은 호스트에서 이미 53을 점유하고 있는 무엇이든 피해갑니다. 호스트가 DNS로 Numa를 사용하지 않으므로 여기서는 numa.numa가 해석되지 않습니다. docker rm -f numa로 정리하세요.
2. 포그라운드에서 실행 (시스템 DNS는 그대로 유지)
바이너리를 설치하세요:
# macOS
brew install razvandimescu/tap/numa
# Linux
curl -fsSL https://raw.githubusercontent.com/razvandimescu/numa/main/install.sh | sh
# Arch Linux
pacman -S numa
# Windows — download from GitHub Releases
# All platforms
cargo install numa
# Nix
nix run github:razvandimescu/numa
sudo numa # Ctrl-C to stop (port 53 requires root/admin)
Numa는 포트 53에서 수신하지만, numa install을 실행하기 전까지 시스템은 기존 리졸버를 유지하므로 dig @127.0.0.1 example.com으로 테스트하세요. 대시보드는 http://localhost:5380에 있습니다. 포트 53이 사용 중이라면(Ubuntu/Mint의 systemd-resolved), numa.toml에서 bind_addr을 설정하거나 Docker를 사용하세요.
3. 시스템 DNS로 설정
| Platform | Install | Uninstall |
|---|---|---|
| macOS | sudo numa install | sudo numa uninstall |
| Linux | sudo numa install | sudo numa uninstall |
| Windows | numa install (admin) + reboot | numa uninstall (admin) + reboot |
install은 서비스를 등록하고, 시스템 DNS를 Numa로 지정하며, 로컬 CA를 신뢰하도록 설정합니다. uninstall은 이 세 가지를 모두 되돌립니다. 설치 후 대시보드는 **http://numa.numa**에서도 접근할 수 있습니다.
macOS와 Linux에서 numa는 시스템 서비스(launchd/systemd)로 실행됩니다. systemd 유닛은 비특권(DynamicUser=yes, CAP_NET_BIND_SERVICE만)으로 실행되며, launchd 데몬은 root로 실행됩니다. numa install은 numa uninstall이 제거하는 drop-in을 통해 systemd-resolved를 재구성합니다. 포트 53을 점유하는 다른 프로세스(dnsmasq, NetworkManager의 것 포함)는 수동으로 중지해야 합니다. Windows에서는 numa가 레지스트리를 통해 로그인 시 자동 시작됩니다. 또한 Windows는 127.0.0.2:53을 바인딩하고(내장 Dnscache가 127.0.0.1:53을 소유함) 쿼리를 이곳으로 라우팅하는 NRPT 규칙을 설치합니다 — 따라서 bind_addr/api_bind_addr은 127.0.0.1이 아니라 127.0.0.2를 기준으로 편집하세요.
로그인
루프백(localhost, 127.0.0.1, numa.numa)에서는 로그인이 필요하지 않습니다. Docker 포트 매핑이나 이 머신의 LAN 주소를 포함한 그 외의 모든 경우에는 API 토큰을 요구하며, Numa가 첫 시작 시 생성합니다. sudo numa token(Windows에서는 관리자 셸)으로 출력하고 아무 사용자명으로 로그인하세요. [server] api_token 또는 NUMA_API_TOKEN으로 직접 지정할 수 있습니다.
모든 흔적 제거
uninstall은 DNS를 복원하지만 데이터 디렉터리는 유지하므로, 재설치 시 동일한 토큰과 CA가 유지됩니다. 모든 것을 제거하려면 먼저 uninstall한 후 다음을 삭제하세요:
| Platform | Left behind |
|---|---|
| macOS | the binary, /usr/local/var/numa, /usr/local/var/log/numa.log |
| Linux | the binary (/usr/local/bin/numa from install.sh), /var/lib/numa, /etc/numa |
| Windows | the binary, %PROGRAMDATA%\numa |
패키지 관리자 설치의 경우 brew uninstall, pacman -R 또는 cargo uninstall로 바이너리를 제거합니다. Linux에서 numa install은 원래 위치(예: ~/.cargo/bin)에서 실행할 수 없는 바이너리를 /usr/local/bin/numa로 복사하며, uninstall은 그 복사본을 남겨둡니다.
사용자 설정을 만들었다면 ~/.config/numa도 삭제하고, [server] data_dir 경로를 설정했다면 그 경로도 삭제하세요.
로컬 서비스
포트 번호를 외우는 대신 개발 서비스에 이름을 붙이세요:
curl -X POST localhost:5380/services \
-d '{"name":"frontend","target_port":5173}'
이제 브라우저에서 https://frontend.numa가 작동합니다 — 녹색 자물쇠, 유효한 인증서, HMR을 위한 WebSocket 패스스루. mkcert도, nginx도, /etc/hosts도 필요 없습니다.
경로 기반 라우팅(app.numa/api → :5001)을 추가하고, LAN 검색을 통해 여러 머신 간에 서비스를 공유하거나, numa.toml에서 모든 것을 구성하세요.
광고 차단 및 개인정보 보호
Hagezi Pro를 통한 광고 및 트래커 차단, 매일 갱신됩니다. 카페, 호텔, 공항 등 어떤 네트워크에서든 작동합니다. 노트북과 함께 이동합니다.
세 가지 해석 모드:
forward(기본값) — 기존 시스템 DNS로의 투명 프록시. 모든 것이 이전과 동일하게 작동하며, 그 위에 캐싱과 광고 차단이 추가됩니다. 캡티브 포털, VPN, 기업 DNS — 모두 존중됩니다.recursive— 루트 네임서버로부터 직접 해석. 업스트림 의존성 없음, 어떤 단일 주체도 당신의 전체 쿼리 패턴을 볼 수 없습니다. 완전한 신뢰 체인 검증을 위해[dnssec] enabled = true를 추가하세요.auto— 시작 시 루트 서버를 탐색하여, 도달 가능하면 재귀, 그렇지 않으면 Quad9(https://9.9.9.9/dns-query)로 DoH를 통한 포워딩을 하며, 이 경우 Quad9이 당신의 쿼리를 보게 됩니다. 다른 제공자를 선택하려면 자체[upstream]과 함께forward를 사용하세요.
DNSSEC는 완전한 신뢰 체인을 검증합니다: RRSIG 서명, DNSKEY 검증, DS 위임, NSEC/NSEC3 부인 증명. 작동 방식 읽기 →
DNS-over-TLS 리스너 (RFC 7858) — iOS Private DNS, systemd-resolved, stubby 같은 엄격한 클라이언트로부터 포트 853에서 암호화된 쿼리를 수락합니다. 두 가지 모드:
- 자체 서명 (기본값) — numa가 로컬 CA를 자동으로 생성합니다.
numa install은 macOS, Linux(Debian/Ubuntu, Fedora/RHEL/SUSE, Arch), Windows의 시스템 신뢰 저장소에 이를 추가하고,numa uninstall은 이를 제거합니다. iOS에서는numa setup-phone의.mobileconfig를 설치하세요. Firefox는 자체 NSS 저장소를 유지하고 시스템 저장소를 무시합니다 — Firefox에서.numa서비스에 HTTPS가 필요하면 해당 CA를 수동으로 신뢰하세요. - 자체 인증서 사용 —
[dot] cert_path/key_path를 공개적으로 신뢰되는 인증서(예: numa 인스턴스를 가리키는 도메인에 대한 DNS-01 챌린지를 통한 Let's Encrypt)로 지정하세요. 클라이언트는 신뢰 저장소 설정 없이 연결됩니다 — AdGuard Home이나 Cloudflare1.1.1.1과 동일한 UX입니다.
ALPN "dot"은 두 모드 모두에서 광고되고 강제됩니다. ALPN이 일치하지 않는 핸드셰이크는 교차 프로토콜 혼동 방어로 거부됩니다.
Oblivious DoH (RFC 9230) — [upstream] mode = "odoh"(레시피)를 사용하면 각 쿼리가 대상에게 HPKE로 봉인되어 릴레이를 통해 전송됩니다. 릴레이는 당신의 IP와 암호문을 보고, 대상은 질문과 릴레이의 IP를 보며, 쿼리를 리디렉션하는 릴레이는 새 목적지에게 복호화할 수 없는 것을 넘겨줍니다. Numa는 동일한 등록 가능 도메인을 공유하는 릴레이와 대상을 거부합니다. ODoH는 이후에 당신이 여는 연결을 숨기지 않습니다: ISP는 여전히 목적지 IP를 보고, ECH가 없으면 TLS 핸드셰이크의 호스트명도 봅니다.
휴대폰 설정 — iPhone 또는 Android를 한 단계로 Numa에 연결하세요:
numa setup-phone
QR 코드를 출력합니다. 스캔하고, 프로필을 설치하고, 인증서 신뢰를 토글하세요 — 이제 휴대폰의 DNS가 TLS를 통해 Numa로 라우팅됩니다. numa.toml에서 [mobile] enabled = true가 필요합니다.
LAN 검색
여러 머신에서 Numa를 실행하세요. mDNS를 통해 자동으로 서로를 찾습니다:
Machine A (192.168.1.5) Machine B (192.168.1.20)
┌──────────────────────┐ ┌──────────────────────┐
│ Numa │ mDNS │ Numa │
│ - api (port 8000) │◄───────────►│ - grafana (3000) │
│ - frontend (5173) │ discovery │ │
└──────────────────────┘ └──────────────────────┘
Machine B에서: curl http://api.numa → Machine A의 포트 8000으로 프록시됩니다. numa lan on으로 활성화하세요.
허브 모드: bind_addr = "0.0.0.0:53"으로 인스턴스 하나를 실행하고 다른 기기의 DNS를 이곳으로 지정하세요 — 아무것도 설치하지 않고 광고 차단 + .numa 해석을 얻습니다. bind_addr은 특정 인터페이스 하위 집합을 바인딩하기 위해 목록도 허용합니다. 단계별 안내: 네트워크 전체 레시피.
Docker
# Recommended — host networking (Linux)
docker run -d --name numa --network host -v numa-data:/var/lib/numa ghcr.io/razvandimescu/numa
# Port mapping (macOS/Windows Docker Desktop)
docker run -d --name numa -p 53:53/udp -p 53:53/tcp -p 5380:5380 -v numa-data:/var/lib/numa ghcr.io/razvandimescu/numa