feat(packaging): ODoH client Docker deploy
Single-container docker-compose recipe for running numa in ODoH client mode. Ships with a starter numa.toml pointing at odoh-relay.numa.rs paired with Cloudflare's ODoH target — two independent operators with distinct eTLD+1s, so the default passes numa's same-operator check. Exposes :53 UDP+TCP for LAN clients and :5380 for the dashboard + REST API. README covers prerequisites, deploy, verification, and the ODoH privacy boundary (relay sees IP, target sees query, neither sees both). Advertised alongside packaging/relay/ in the main README Docker section.
This commit is contained in:
@@ -125,6 +125,10 @@ docker run -d --name numa --network host \
|
||||
|
||||
Multi-arch: `linux/amd64` and `linux/arm64`.
|
||||
|
||||
Turnkey compose recipes:
|
||||
- [`packaging/client/`](packaging/client/) — ODoH client mode (anonymous DNS), Numa + starter `numa.toml`.
|
||||
- [`packaging/relay/`](packaging/relay/) — public ODoH relay, Numa + Caddy + ACME.
|
||||
|
||||
## How It Compares
|
||||
|
||||
| | Pi-hole | AdGuard Home | Unbound | Numa |
|
||||
|
||||
72
packaging/client/README.md
Normal file
72
packaging/client/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Numa ODoH Client — Docker deploy
|
||||
|
||||
Single-container deploy that runs Numa as an ODoH (RFC 9230) client: every
|
||||
DNS query routes through an independent relay + target so neither operator
|
||||
sees both your IP and your question. See the [ODoH integration doc][odoh]
|
||||
for the full protocol and privacy trade-offs.
|
||||
|
||||
[odoh]: ../../docs/implementation/odoh-integration.md
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker + Docker Compose v2.
|
||||
- Port 53 (UDP+TCP) free on the host — Numa listens there for DNS
|
||||
clients on your LAN.
|
||||
|
||||
## Configure
|
||||
|
||||
The shipped `numa.toml` points at Numa's own public relay
|
||||
(`odoh-relay.numa.rs`) paired with Cloudflare's ODoH target
|
||||
(`odoh.cloudflare-dns.com`). That's two independent operators with
|
||||
distinct eTLD+1s — the default configuration passes Numa's same-operator
|
||||
check and works out of the box.
|
||||
|
||||
To use a different relay or target, edit `numa.toml` and adjust the URLs.
|
||||
The `relay` and `target` must resolve to distinct operators or Numa
|
||||
refuses to start.
|
||||
|
||||
## Deploy
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
docker compose logs -f numa # watch startup
|
||||
```
|
||||
|
||||
The first query fires the bootstrap resolver + ODoH config fetch;
|
||||
subsequent queries reuse the warm HTTP/2 connection.
|
||||
|
||||
## Point your devices at it
|
||||
|
||||
Set each device's DNS server to the IP of the Docker host. For a LAN-wide
|
||||
rollout, set the DNS server in your router's DHCP config so every device
|
||||
picks it up automatically.
|
||||
|
||||
Verify a query landed on the ODoH path:
|
||||
|
||||
```sh
|
||||
dig @<host-ip> example.com
|
||||
curl http://<host-ip>:5380/stats | jq '.upstream_transport.odoh'
|
||||
```
|
||||
|
||||
`upstream_transport.odoh` should increment on each query.
|
||||
|
||||
## What this does NOT buy you
|
||||
|
||||
ODoH protects the *path*, not the content:
|
||||
|
||||
- **The target (Cloudflare here) still sees the question.** It just
|
||||
doesn't know it's you asking. If Cloudflare logs every ODoH query, the
|
||||
query is still visible — it's simply unattributed.
|
||||
- **The relay is a trusted party for availability.** A malicious relay
|
||||
can drop or delay queries; it just can't read them.
|
||||
- **Traffic analysis defeats small relays.** If you're the only client
|
||||
talking to a relay, timing alone re-identifies you. Shared, busy relays
|
||||
give better anonymity sets.
|
||||
|
||||
See the [ODoH integration doc][odoh] for more.
|
||||
|
||||
## Relay operator?
|
||||
|
||||
If you'd rather run your own relay (same binary, different mode), see
|
||||
[`../relay/`](../relay/) — that package spins up a public-facing relay
|
||||
with Caddy + ACME in front of it.
|
||||
15
packaging/client/docker-compose.yml
Normal file
15
packaging/client/docker-compose.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
numa:
|
||||
image: ghcr.io/razvandimescu/numa:latest
|
||||
command: ["/etc/numa/numa.toml"]
|
||||
ports:
|
||||
- "53:53/udp"
|
||||
- "53:53/tcp"
|
||||
- "5380:5380/tcp" # dashboard + REST API
|
||||
volumes:
|
||||
- ./numa.toml:/etc/numa/numa.toml:ro
|
||||
- numa_data:/var/lib/numa
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
numa_data:
|
||||
23
packaging/client/numa.toml
Normal file
23
packaging/client/numa.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
# Numa — ODoH client mode (docker-compose starter).
|
||||
# Sends every DNS query through an independent relay + target pair so
|
||||
# neither operator sees both your IP and your question. See
|
||||
# docs/implementation/odoh-integration.md for the protocol details and
|
||||
# packaging/client/README.md for deploy notes.
|
||||
|
||||
[server]
|
||||
bind_addr = "0.0.0.0:53"
|
||||
api_bind_addr = "0.0.0.0"
|
||||
data_dir = "/var/lib/numa"
|
||||
|
||||
[upstream]
|
||||
mode = "odoh"
|
||||
# Numa's own relay (Hetzner, systemd + Caddy). Swap to any other public
|
||||
# ODoH relay if you'd rather not depend on a single operator; the protocol
|
||||
# tolerates it, and Numa refuses same-operator relay+target by default.
|
||||
relay = "https://odoh-relay.numa.rs/relay"
|
||||
target = "https://odoh.cloudflare-dns.com/dns-query"
|
||||
# strict = true (default). Relay failure → SERVFAIL, never silent downgrade.
|
||||
|
||||
[blocking]
|
||||
enabled = true
|
||||
# Default blocklist (Hagezi Pro). Edit the `lists` array to taste.
|
||||
Reference in New Issue
Block a user