- Axum HTTP server with /dns-query endpoint (GET + POST per RFC 8484) - DNS wire-format parsing via hickory-proto - Upstream DoH client with round-robin selection - TOML config file with resolver definitions - SERVFAIL fallback when upstream is unreachable - /health endpoint - Unit tests (7) and integration tests (5) - Zero warnings, clippy clean, fmt clean
3.8 KiB
3.8 KiB
DoH Forwarder — Project Plan
Overview
A lightweight DNS-over-HTTPS (DoH) forwarder that accepts DNS queries over HTTPS (RFC 8484) and forwards them to upstream DoH resolvers, with caching, logging, and optional blocking.
Goals
- Accept DoH queries (GET and POST per RFC 8484)
- Forward to configurable upstream DoH resolvers (Cloudflare, Google, Quad9, custom)
- In-memory DNS response cache with configurable TTL
- Query logging (stdout/file)
- Optional domain blocklist (ad-blocking / parental control)
- Health-check endpoint
- Low resource footprint, single binary
Architecture
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ DNS Client │─────▶│ DoH Forwarder │─────▶│ Upstream DoH │
│ (browser, │ HTTPS│ (this project) │ HTTPS│ (1.1.1.1, │
│ OS, app) │◀─────│ │◀─────│ 8.8.8.8, …) │
└─────────────┘ └──────────────────┘ └─────────────────┘
Tech Stack
- Language: Rust
- HTTP server: Axum
- DNS wire-format: hickory-proto (formerly trust-dns-proto)
- Async runtime: Tokio
- Config: TOML (config file) + env vars
- Logging: tracing + tracing-subscriber
Milestones
M1 — Core Forwarding
- Project scaffold (Cargo workspace, CI)
- HTTP server with
/dns-queryendpoint (GET + POST) - Parse incoming DNS wire-format queries
- Forward to a single upstream DoH resolver
- Return DNS wire-format response
- Basic integration test
M2 — Multiple Upstreams & Config
- TOML config file support
- Multiple upstream resolver support
- Upstream selection strategy (round-robin / failover)
- Environment variable overrides
- Graceful shutdown
M3 — Caching
- In-memory DNS response cache
- Cache key = (query name, query type)
- Respect DNS response TTL (with configurable max)
- Cache eviction (TTL-based + LRU size cap)
- Cache stats endpoint (
/cache/stats)
M4 — Observability
- Structured logging (tracing)
- Request logging middleware (query name, type, latency, upstream used)
- Health endpoint (
/health) - Metrics endpoint (optional, Prometheus format)
M5 — Blocking & Filtering
- Domain blocklist (file-based, one domain per line)
- Wildcard / suffix matching
- Return NXDOMAIN for blocked queries
- Hot-reload blocklist on SIGHUP
M6 — Production Hardening
- Rate limiting (per-IP)
- Request timeout for upstream queries
- Dockerfile (multi-stage, distroless)
- Helm chart
- CI pipeline (lint, test, build, image push)
Directory Structure (target)
doh-forwarder/
├── Cargo.toml
├── src/
│ ├── main.rs # entrypoint, server setup
│ ├── config.rs # config parsing
│ ├── dns/
│ │ ├── mod.rs
│ │ ├── query.rs # parse incoming DNS queries
│ │ └── response.rs # build/parse DNS responses
│ ├── upstream/
│ │ ├── mod.rs
│ │ └── doh_client.rs # DoH forwarding client
│ ├── cache.rs # DNS cache
│ ├── blocklist.rs # domain blocking
│ ├── routes.rs # HTTP route handlers
│ └── middleware.rs # logging, rate-limit
├── config/
│ └── default.toml
├── docs/
│ └── plan.md
├── DEVELOP.md
└── tests/
└── integration.rs
Non-Goals (for now)
- DNS-over-TLS (DoT) listener
- DNSSEC validation
- Persistent cache (Redis / disk)
- Web UI / dashboard