Files
doh-forwarder/docs/plan.md

135 lines
4.5 KiB
Markdown

# 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, …) │
└─────────────┘ └──────────────────┘ └─────────────────┘
│ ▲
│ UDP/TCP :53 │
└─────────────────────┘
System DNS (resolv.conf)
```
## 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
- [x] Project scaffold (Cargo workspace, CI)
- [x] HTTP server with `/dns-query` endpoint (GET + POST)
- [x] Parse incoming DNS wire-format queries
- [x] Forward to a single upstream DoH resolver
- [x] Return DNS wire-format response
- [x] Basic integration test
### M2 — Multiple Upstreams & Config
- [x] TOML config file support
- [x] Multiple upstream resolver support
- [x] Upstream selection strategy (round-robin / failover)
- [x] Environment variable overrides
- [x] Graceful shutdown
### M3 — Local DNS Listener
- [ ] UDP listener on configurable port (default 53)
- [ ] TCP listener on configurable port (default 53)
- [ ] Receive standard DNS wire-format queries
- [ ] Forward through DoH pipeline (shared upstream client)
- [ ] Return DNS wire-format response over UDP/TCP
- [ ] Config: `[dns]` section with `listen` address
- [ ] Integration test with UDP/TCP clients
### M4 — 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`)
### M5 — Observability
- [ ] Structured logging (tracing)
- [ ] Request logging middleware (query name, type, latency, upstream used)
- [ ] Health endpoint (`/health`)
- [ ] Metrics endpoint (optional, Prometheus format)
### M6 — Blocking & Filtering
- [ ] Domain blocklist (file-based, one domain per line)
- [ ] Wildcard / suffix matching
- [ ] Return NXDOMAIN for blocked queries
- [ ] Hot-reload blocklist on SIGHUP
### M7 — 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
│ ├── lib.rs # public API re-exports
│ ├── config.rs # config parsing
│ ├── dns/
│ │ ├── mod.rs
│ │ ├── query.rs # parse incoming DNS queries
│ │ ├── response.rs # build/parse DNS responses
│ │ └── listener.rs # UDP/TCP DNS listener
│ ├── 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