Command Palette

Search for a command to run…

ZarynZaryn
Private networks and proxy·25 min read·

DNS that works on any network (zev dns)

zev dns finds which DNS resolvers really work from your network and how (encrypted, another port, TCP, through your proxy), throws away a censor's answers, checks DNSSEC signatures itself, and falls back to the network's own DNS so names never stop.

Every website starts with a DNS question: "what is the address of github.com?". On a filtered network that question goes wrong in quiet ways: the answer is a lie (one address, such as `10.10.34.36`, for every blocked site), encrypted DNS is cut, the network's own DNS is slow or down. Programs just say "can't connect". `zev dns` measures every well-known resolver, every way it can be asked, from where you are; tells you what the network does to DNS; and, when you turn it on, answers this computer's names through the best few. It checks DNSSEC signatures itself, so a forged answer for a signed name is caught whichever way it came. If they all fail, it asks the network's own DNS: zev dns never leaves you without names. ```bash zev dns check # what this network does to DNS, and what gets around it sudo zev dns on # this computer's names through the best ways (Linux) zev dns status # what it uses now, and how it's doing sudo zev dns off # back exactly as it was ``` ```diagram type: flow title: How zev dns finds a way, for every resolver --- Encrypted | DNS over HTTPS and DNS over TLS, straight to the resolver's address (no name to look up first) | good | 🔒 ~ the handshake is cut Other names | The same resolver with every name its certificate has: a filter often knows only the main one | | 🔁 ~ still cut Split hello | The TLS hello in two records, in small pieces, so a filter can't read the resolver's name in it | | ✂️ ~ still blocked Another port | Plain DNS on ports some resolvers also answer on (OpenDNS 443, Quad9 9953), and over TCP | warn | 🚪 ~ nothing direct works Your proxy | DNS over HTTPS through zev proxy, when `zev proxy share` is on | accent | 🛰️ ~ everything fails The network's DNS | Asked last, as if zev dns weren't there: names keep working | dim | 🏠 ``` ## Start in one minute ```diagram type: cards title: Honest DNS in three commands --- 1️⃣ | Look | zev dns check: what this network does to DNS (lies, blocks) and which ways still tell the truth, in about 15 seconds 2️⃣ | On | sudo zev dns on: this computer's names through the best few, the network's DNS behind them, signatures checked 3️⃣ | See | zev dns status, and zev dns why <name> for any one name ``` ## Every command | Command | Example | What it does | |---|---|---| | `check` | `zev dns check` | what this network does to DNS, and what gets around it | | `bench` | `zev dns bench` · `zev dns bench --for private` | every resolver, every way, measured from here; the best for each use | | `why` | `zev dns why twitter.com` · `zev dns why example.com AAAA` | what each resolver says about one name, who lies, which are signed | | `verify` | `zev dns verify www.wikipedia.org` · `zev dns verify example.com --json` | which resolvers gave the real site, proven by each address's HTTPS certificate | | `on` | `sudo zev dns on` · `sudo zev dns on --for adblock` | this computer's names through zev dns (Linux), checked that the system really asks it | | `off` | `sudo zev dns off` | back exactly as it was | | `status` | `zev dns status` | what it asks now, how fast, the cache, lies dropped, forged answers | | `log` | `sudo zev dns log` · `sudo zev dns log -f` · `sudo zev dns log github` · `--json` | the names this computer asked, newest last, and how each was answered | | `top` | `sudo zev dns top` · `sudo zev dns top --json` | the most asked, how they were answered, which way answered, what failed, which devices asked | | `share` | `sudo zev dns share on` · `sudo zev dns share off` · `zev dns share` | phones, TVs and consoles on this network use it too | | `use` | `sudo zev dns use family` | another use while on: balanced, fast, private, secure, family, adblock, gaming | | `rule` | `sudo zev dns rule add corp.example system` · `sudo zev dns rule country de` | some names through the network's DNS, blocked, or one resolver | | `sanctions` | `sudo zev dns sanctions on` | sanctioned developer services through a local relay resolver | | `set` | `sudo zev dns set strict=on` · `set dnssec=off` · `set log=off` | strict, proxy, rebinding, dnssec, log, share, listen | | `ls` | `zev dns ls` | the resolvers it knows, and what each is good for | | `restore` | `sudo zev dns restore` | the system's DNS as before (what systemd runs if zev dns dies) | ## What does this network do to DNS? zev dns check ```bash zev dns check ``` It asks some 20 resolvers about 90 ways at once, takes a few seconds, and needs nothing installed. On a filtered network it looks like this: ```text This network's DNS ! 192.168.1.1 answers in 4 ms, but gives the censor's address for 4 of 4 blocked sites On the way out ✗ something on this network answers DNS questions meant for other servers (an address with no DNS server answered 10.10.34.36) ✗ plain DNS on port 53: 19 of 19 resolvers' answers were the censor's What gets through ✓ DNS over HTTPS (encrypted): 1 work dnssb DoH as dot.sb ✗ DNS over TLS (encrypted): blocked: the handshake is cut when the filter reads the resolver's name (9 of 13) ✓ plain DNS on another port: 4 work quad9 UDP :9953, quad9-unfiltered UDP :9953, opendns UDP :443, opendns-family UDP :443 ✓ plain DNS over TCP: 15 work ✗ plain DNS (UDP 53): blocked What zev dns would use (balanced) 1. dnssb DoH as dot.sb 110 ms 2. quad9 UDP :9953 125 ms 3. opendns UDP :443 140 ms then this network's own DNS, if all of those fail ``` What it found there, and how: - **Someone on the way answers.** zev asks an address where no DNS server runs (192.0.2.53, reserved for examples). Silence is normal; an answer means something between you and the internet answers DNS itself. Whatever addresses it hands out are this network's lies, and zev throws them away from then on, from any resolver. - **Lies on port 53.** Every resolver's plain answer for a blocked site was the censor's, because the filter answered first. - **What still gets through.** Here: DNS.SB's encrypted DNS with the second name on its certificate, plain DNS on ports other than 53, and TCP. `zev dns check --json` gives the same for scripts and agents. ## Turn it on: sudo zev dns on ```bash sudo zev dns on # balanced: fast, honest, encrypted when it costs little sudo zev dns on --for private # or another use, below ``` It starts a small resolver at `127.0.0.153` (a system service that starts with the computer) and points the system at it: - **With systemd-resolved** (Ubuntu, Fedora, most desktops): a setting in `/run/systemd/resolved.conf.d/zev-dns.conf`, and the network's links stop being the default route for names. Domains a link asks for itself, a work VPN's for example, still go to that link. - **Without it:** `/etc/resolv.conf` points at zev dns; the old file is kept and put back. Then it **checks the system really asks it**: it looks up a name only zev dns can answer, the way every program does. If that fails, everything goes back as it was and it tells you so. Nothing is left half-changed. The first measurement takes a few seconds; until then the network's own DNS answers, so names work from the first second. ## Names never stop ```diagram type: sequence title: One name, when things go wrong lanes: A program | zev dns | Best ways | Network's DNS alt: A program asks zev dns; it answers from its cache when it can; else it asks the best way, and if that is slow the next one too, and takes the first honest answer; if a censor's answer comes it is thrown away; if none of the best ways answer, the network's DNS is asked; if nothing answers, an expired copy is given --- A program -> zev dns : github.com? note zev dns : kept and still fresh? answered at once | good zev dns -> Best ways : the fastest first; the next joins if it's slow Best ways --> zev dns : 10.10.34.36 (a censor's answer, from the way) note zev dns : thrown away: not an answer | warn Best ways --> zev dns : 140.82.121.3 zev dns --> A program : 140.82.121.3 == If none of the best ways answer == zev dns -> Network's DNS : github.com? Network's DNS --> zev dns : whatever it says == If nothing answers at all == zev dns --> A program : the last answer it had, kept for up to 3 days ``` - **A race, not a queue.** The best way is asked first. If it hasn't answered by the time it usually would have, the second joins, then the third; the first honest answer wins. One slow resolver never makes you wait. - **Ways that fail rest.** After three failures in a row a way is skipped for a while (5 s, then longer, up to 2 minutes), then tried again. - **Answers too big for UDP.** A name with many addresses, or a signed zone's keys, doesn't fit in a UDP answer: the resolver sends it cut, and zev dns asks again over TCP. Where the network blocks TCP to that resolver, zev dns keeps two other ways for exactly this (the best TCP, DoT or DoH ways it measured that work here, such as AdGuard's TCP on port 5353) and asks them. `zev dns status` shows them with ◇ and how many answers came whole through them. - **The network's DNS behind them.** If none of the best ways answer within 2.5 seconds, the network's DNS is asked: exactly what would have happened without zev dns. If the best ways stop answering altogether (an outage, a new block), zev dns asks the network's DNS first for a while, measures again, and goes back to the best ways as soon as one answers. - **Expired copies.** When nothing answers at all, a name it answered before is answered from its old copy (for up to 3 days, with a 30-second TTL, as RFC 8767 suggests). Sites you already use keep opening during an outage. - **Kept answers.** Answers are kept as long as their TTL says. A name asked often is fetched again just before it expires, so it never waits. - **A new network, measured again.** zev dns notices when you change network (another Wi-Fi, a phone hotspot) and measures again, starting from what worked there last time. It measures every half hour anyway, and every two minutes while the best ways are failing. ## The best for what you need | Use | What it picks | |---|---| | `balanced` (the default) | Fast and honest; encrypted when it costs little more than plain. Can be the network's own DNS, if it told the truth and is fastest | | `fast` | The fastest honest answer, encrypted or not | | `private` | Encrypted only (DNS over HTTPS or TLS), to resolvers that keep no logs and don't pass on part of your address to websites (ECS) | | `secure` | Resolvers that block malware and phishing (Quad9, Cloudflare 1.1.1.2, AdGuard) | | `family` | Malware and adult sites blocked (Cloudflare 1.1.1.3, AdGuard Family, OpenDNS FamilyShield, CleanBrowsing) | | `adblock` | Ads and trackers blocked (AdGuard, Mullvad adblock) | | `gaming` | The steadiest: the least difference between its usual and its slow answers | ```bash zev dns bench # every way, measured from here, and the best for each use zev dns bench --for gaming # sorted for one use sudo zev dns use private # change while it's on ``` ```text ✓ quad9 UDP :9953 122 ms 168 ms 17/17 ✓ dnssb DoH as dot.sb 110 ms 265 ms 17/17 handshake 380 ms ✗ cloudflare UDP 31 ms 48 ms 17/17 lied 4× (censor's address) · google DoT - - 0/1 TLS handshake cut ``` A way only counts if it **never lied**: a censor's address, a private address for a public name, or an answer for a name that doesn't exist (some networks invent those to show ads) puts it out. Speed is the median and the slowest tenth of its answers, so a resolver that is fast on average but sometimes stalls doesn't win. Measurements are kept per network, so a network you return to starts with what worked there. `zev dns ls` lists the resolvers zev knows, what each speaks and what it's good for. ## Why this answer? zev dns why ```bash zev dns why twitter.com ``` ```text twitter.com A, asked 22 ways at once ✓ cloudflare TCP 162.159.140.229 273 ms ✓ quad9 UDP :9953 162.159.140.229 118 ms ✗ local-relay UDP 10.10.34.36 51 ms ← the censor's address, not the site's ✗ network DNS 192.168.1.1 10.10.34.36 4 ms ← the censor's address, not the site's zev dns answers: 162.159.140.229 2 ms ``` Every resolver, the way that works best for it from here, side by side: who tells the truth, who blocks the name on purpose (`⊘`), who lies, and what zev dns itself answers. Give a type for other records: `zev dns why example.com AAAA`. For a signed name, each answer is also checked against the signatures, with the keys asked from that same resolver the same way. `signed ✓` means its answer is proven; `forged` means it doesn't match, even when the address isn't one the censor is known for. Real lines from a filtered network, and the last one from zev's lab test, where a resolver forges cloudflare.com's address under its real signature: ```text cloudflare.com A, asked 25 ways at once ✓ cloudflare UDP 104.16.132.229 104.16.133.229 109 ms signed ✓ ✓ adguard-family UDP :5353 104.16.132.229 104.16.133.229 82 ms signed ✓ ✓ cleanbrowsing-family UDP 104.16.132.229 104.16.133.229 118 ms ✗ liar UDP 198.51.100.66 40 ms ← forged: the signature on cloudflare.com doesn't match cloudflare.com's keys cloudflare.com is signed (DNSSEC): an answer whose signatures check out from the root's key is the real one ``` A line with no mark gave the right address but no signatures to check (some resolvers, CleanBrowsing among them, drop them), or its keys didn't come back. ## Is this really the site? zev dns verify DNSSEC proves an answer only for names whose owner signed them, and most names aren't signed. `zev dns verify` proves answers another way, one that works for **every site that speaks HTTPS**: it asks every resolver, every way, then opens HTTPS to each address it was given and asks for that name's certificate. A forger can make up an address, but not the site's certificate. ```bash zev dns verify www.wikipedia.org ``` ```text www.wikipedia.org 37 ways asked; each address checked by opening HTTPS to it and asking for www.wikipedia.org's certificate The addresses ✓ 185.15.59.224 the site: a valid certificate for www.wikipedia.org (740 ms) ✓ 208.80.153.224 the site: a valid certificate for www.wikipedia.org (1066 ms) ✓ 198.35.26.224 the site: a valid certificate for www.wikipedia.org (1112 ms) The resolvers ✓ cloudflare UDP 185.15.59.224 ✓ quad9 UDP 185.15.59.224 ✓ dnssb UDP 198.35.26.224 · google DoH TLS handshake cut ✓ network DNS 192.168.1.1 185.15.59.224 Every address that could be checked is really www.wikipedia.org. ``` When an answer is forged, verify says which, and exits with code 1: ```text The addresses ✓ 104.16.123.96 the site: a valid certificate for www.cloudflare.com (3 ms) ✓ 104.16.124.96 the site: a valid certificate for www.cloudflare.com (2 ms) ✗ 198.51.100.66 not the site: its certificate isn't for www.cloudflare.com The resolvers ✓ honest UDP 104.16.124.96 104.16.123.96 ✗ liar UDP 198.51.100.66 ← not the site ✗ network DNS 192.168.1.1 198.51.100.66 ← not the site 2 answer(s) weren't www.cloudflare.com: forged by the resolver, or on the way to it (plain DNS can be answered by anything in between). zev dns check finds the ways that answer honestly from here, and sudo zev dns on uses only those. ``` ```diagram type: sequence title: Proving an answer with the site's own certificate lanes: zev dns verify | Resolvers | The address alt: zev dns verify asks every resolver for the name's addresses, then opens HTTPS to each address with the name and checks the certificate it shows; an address that shows a valid certificate for the name is the site, one that shows another name's is not, and a resolver is honest when its addresses are the site --- zev dns verify -> Resolvers : www.cloudflare.com? (every one, every way) Resolvers --> zev dns verify : 104.16.123.96 · 198.51.100.66 zev dns verify -> The address : HTTPS to 104.16.123.96, for www.cloudflare.com The address --> zev dns verify : a valid certificate for www.cloudflare.com | good zev dns verify -> The address : HTTPS to 198.51.100.66, for www.cloudflare.com The address --> zev dns verify : a certificate for another name | bad note zev dns verify : the resolver that gave 198.51.100.66 isn't honest | warn ``` ```diagram type: cards title: What each mark means --- ✓ | The site | it showed a valid certificate for the name, signed by an authority this computer trusts | good ✗ | Not the site | another name's certificate, or a private address for a public name | bad ? | Couldn't tell | it didn't answer HTTPS from here (blocked, cut, or not a web site). Not counted as a lie | · | No answer | that resolver or way gave no address | ``` `--json` gives each address's proof (`site`, `wrong_certificate`, `private`, `unknown`) and each resolver's verdict, for scripts and monitoring. ## Signed names: DNSSEC, checked here Many names are signed (DNSSEC): the zone's owner signs every answer, the zone above vouches for its key, and so on up to the root, whose key zev has built in. A censor can hand out any address it likes, but it can't make the signature fit. zev dns checks every answer itself, from the root's key down, so a forged answer for a signed name is caught **whichever way it came**: from an encrypted resolver, from plain DNS on another port, or from the network's own DNS when nothing else answered. ```diagram type: flow title: Why cloudflare.com's address can be trusted --- The root's key | Built into zev (IANA's KSK-2017 and KSK-2024): the one thing taken on trust | good | 🔑 ~ signs .com's DS | The root says which key .com uses | | 📜 ~ matches .com's keys | They sign everything in .com | | 🔐 ~ signs cloudflare.com's DS | .com says which key cloudflare.com uses | | 📜 ~ matches cloudflare.com's keys | They sign cloudflare.com's answers | | 🔐 ~ signs The address | 104.16.132.229, with its signature: proven | good | ✅ ``` Each answer ends up one of four things: ```diagram type: cards title: What zev dns makes of an answer --- ✅ | Proven | signed, and every signature checks out from the root's key. Answered, with the AD bit for programs that ask for it 📭 | Not signed | proven unsigned: a zone on the way has no key above it (most names, twitter.com among them). Answered as before ❔ | Couldn't check | the keys didn't come (too big for UDP where TCP is blocked, a resolver that drops signatures), or the clock is off. Answered as before: never worse than without DNSSEC ⛔ | Forged | it doesn't match the signatures. Thrown away; the next way is asked, up to three; if every one is forged, the program gets SERVFAIL with the reason (Extended DNS Error 6) ``` What it means in practice: - **"Forged" only when something is there and is wrong.** An answer whose signature doesn't fit, or an unsigned answer for a zone proven signed, from a resolver that speaks DNSSEC. Anything just missing (keys that don't arrive, a resolver that doesn't pass signatures on) is "couldn't check", and the name is answered as before. zev dns never stops a name only because DNSSEC couldn't be checked. - **The keys come from the best ways**, through the cache, and only answers that carry signatures count for them. Keys stay in the cache as long as their TTL says (usually a day or two), so checking adds a few round trips the first time a zone is seen, then nothing. A key lookup that fails isn't asked again for 5 minutes. - **Keys ahead of time.** Right after measuring a network, zev dns fetches the root's keys and those of the busiest top-level names (.com, .net, .org, .io, .dev and others), so the first signed name doesn't wait for them. `zev dns status` says so under *lately*: `dnssec keys ready: the root's, and 17 of 17 top-level names'`. - **Programs that check for themselves** (they set the CD bit) get the answer as it came, unchecked. Programs that ask with DO get the signatures too; the others get the answer without them. - **Office and home names** aren't checked: names under `.lan`, `.home.arpa`, `.local`, `.internal`, `.corp`, private address ranges' reverse names, and any name under a top-level name that doesn't exist in the public DNS. Rules that send names to `system` aren't checked either. - **The clock**: signatures are valid between two dates. With the computer's clock badly wrong, signed names are "couldn't check" (not forged), with an hour's slack. On a real filtered network (2026-10-26) every honest resolver's answer for cloudflare.com, ed25519.nl, nlnetlabs.nl and nic.cz was proven, with ECDSA, Ed25519, RSA, NSEC and NSEC3. The censor's injected answer for torproject.org (signed) carries no signature. `.org`'s keys are too big for UDP and TCP was blocked, so `.org` names were "couldn't check" through every resolver except AdGuard on port 5353, which answers over TCP. zev dns now keeps a way like that for answers too big for UDP, and fetches such keys through it (see [Names never stop](#names-never-stop)). `zev dns status` counts both: ```text dnssec 412 answers proven by their signatures, 3 forged ones thrown away ``` and lists each forged answer under *lately*, with the way it came from. It lists the ways it asks too, with the ones kept for big answers: ```text asking ● quad9 UDP :9953 41 ms ±6, 1290 answered, 2 failed ● cloudflare UDP 48 ms ±9, 980 answered, 0 failed ◇ adguard TCP :5353 (for answers too big for UDP; 37 so far) ○ network DNS 192.168.1.1 (the network's DNS, behind them) ``` ## What this computer asks: zev dns log and top With `zev dns on`, every name it answers goes into a short list: the time, the name, how it was answered, which way answered it, how long it took, and which device asked. It's kept in memory and in `/run` (root only, gone at reboot), and holds the last 2,000 names. ```bash sudo zev dns log # the last 30, newest last sudo zev dns log -f # as they come, like tail -f sudo zev dns log github # only names with github in them sudo zev dns log -n 200 --json ``` ```text Names this computer asked newest last 10:05:32 www.wikipedia.org A asked 4 ms quad9 TCP 10:05:33 github.com A asked 5 ms quad9 TCP 10:05:33 www.wikipedia.org A cache 0 ms 10:05:33 api.github.com A asked 4 ms quad9 TCP 10:05:38 github.com A cache 0 ms from 192.168.1.20 ``` ```diagram type: cards title: How a name was answered --- asked | Asked out | through the way named beside it | good cache | From the cache | no question went out: no time, and nothing for anyone to see | kept copy | An expired copy | nothing answered, so a recent answer kept the name working | warn blocked | Blocked | by one of your rules, or the use you chose (family, adblock) | warn forged / failed | Thrown away / no answer | a forged DNSSEC answer, or nothing answered at all | bad ``` `zev dns top` adds it up: how the names were answered (and how much the cache saved), the most asked sites, which way answered how many and how fast, what was blocked, forged or failed, and which devices asked. ```bash sudo zev dns top ``` ```text The last 6 names (over 1 min) how they were answered asked 4 66% cache 2 33% a name asked out took 3 ms on average; one from the cache takes none most asked github.com 3 wikipedia.org 2 answered by quad9 TCP 4 3 ms average asked from 127.0.0.1 5 (this computer) 192.168.1.20 1 ``` Don't want any list kept? `sudo zev dns set log=off`, and the file is removed. ## For the whole home: zev dns share Phones, TVs and consoles can't run zev, and they suffer the same lying or broken DNS. `sudo zev dns share on` lets them use this computer's zev dns: the honest resolvers, the cache, the fallback, your rules and DNSSEC checks, for every device on the network. ```bash sudo zev dns share on ``` ```text ● zev dns answers the local network too, on 192.168.1.34 On a phone, TV or console: in its network settings, set DNS to 192.168.1.34 (manual). For every device at once: put 192.168.1.34 as the DNS server in the router's DHCP settings. Only private addresses (192.168.x, 10.x, 172.16–31.x) are answered, so it's never open to the internet. sudo zev dns share off stops it; zev dns top shows which devices asked. ``` ```diagram type: flow title: The phone asks the laptop; the laptop asks the honest resolvers --- Phone | DNS set to 192.168.1.34 | | 📱 ~ a name This computer | zev dns: cache, rules, DNSSEC, the honest ways | accent | 💻 ~ only what isn't cached Resolvers | the ways that answer honestly from this network | good | 🌍 ``` It listens on every local network address this computer has (Wi-Fi, cable), not on Docker's or a VPN's, and follows them: join another Wi-Fi and it moves. Questions from anything but a private address get no answer at all, so it can't be used as an open resolver from the internet. `zev dns share` shows where it listens; `sudo zev dns share off` stops it. If a firewall runs here, allow port 53 from the local network. ## Rules: some names elsewhere ```bash sudo zev dns rule add corp.example system # a work domain through the network's DNS sudo zev dns rule add ads.example block # 0.0.0.0 for it and every name under it sudo zev dns rule add example.org quad9 # one name through one resolver sudo zev dns rule country de # a country's sites (.de) through the network's DNS zev dns rule ls sudo zev dns rule rm corp.example ``` A rule covers the name and everything under it; the longest matching rule wins. A country's own top-level name to `system` sends its sites to the network's own DNS: in that country they answer faster from inside, and some only answer inside. ### Sanctions resolvers Some services refuse visitors from some countries (Docker Hub, parts of Google's developer sites, JetBrains, HashiCorp…). In those countries, local resolvers answer those names with the address of their own relay, which passes you on. ```bash sudo zev dns sanctions on ``` sends only those services' names to the best of them; everything else stays on the best ways. Know what this means: the relay sees which of these services you use, and these resolvers are inside the country, so its filter's lies apply to them (`zev dns why` shows it). It is off until you turn it on. ## Settings ```bash zev dns set # see them sudo zev dns set strict=on # never the network's DNS: no answer rather than an unencrypted one sudo zev dns set proxy=off # don't use zev proxy share for DNS sudo zev dns set rebinding=on # allow private addresses for public names sudo zev dns set dnssec=off # don't check signatures here sudo zev dns set log=off # keep no list of the names asked sudo zev dns set share=on # the same as sudo zev dns share on sudo zev dns set listen=127.0.0.153:53 ``` - **strict**: for privacy. With it, when the best ways fail, a name gets no answer instead of going to the network's DNS. Off by default, because names that stop are worse for most people. - **dnssec** (on by default): signatures checked here, forged answers for signed names thrown away. See [Signed names](#signed-names-dnssec-checked-here). - **rebinding protection** (on by default): an answer with a private address (192.168.x, 10.x…) for a public name is thrown away. That is either a lie or an attack on the devices in your home (DNS rebinding). Rules to `system` aren't affected, so office names keep working. - **log** (on by default): the last 2,000 names asked, kept in memory and in `/run/zev-dns/queries.jsonl`, readable by root only and gone at reboot. Off, nothing is kept and the file is removed. - **share** (off by default): see [For the whole home](#for-the-whole-home-zev-dns-share). - Settings are picked up within seconds; nothing restarts. ## With zev proxy and zev vpn - When `zev proxy share` is on, zev dns also tries DNS over HTTPS **through your proxy**: when nothing direct works honestly, that still does. - When `sudo zev vpn on` is on, the VPN's own DNS goes through the tunnel; zev dns leaves the VPN's link alone and both answer honestly. ## Off, crashes and reboots ```bash sudo zev dns off # stop it, and put back exactly what it changed sudo zev dns restore # only the system's DNS settings back (what systemd runs if zev dns dies) zev dns serve --listen 127.0.0.1:5353 # the same server in this terminal, on any port, changing nothing else dig @127.0.0.1 -p 5353 example.com # ask it ``` stops the service and puts back exactly what it changed: the resolved setting, the links it changed (and only those) or the old `/etc/resolv.conf`. It is built so a failure can't leave the computer without DNS: - If zev dns stops for any reason, even killed, systemd puts the system's DNS back right away (`zev dns restore`), then starts it again. - The resolved setting lives in `/run`: a reboot always clears it, and zev dns sets it again when it starts. - `sudo zev dns on` checks the system really gets names through it, and undoes everything if not. ## How it tells a lie | Sign | What zev does | |---|---| | An address a censor hands out (such as `10.10.34.x`), or one an address with no DNS server answered with | The answer is thrown away; plain UDP keeps listening for the real one | | A private address for a public name | Thrown away (rebinding protection) | | An answer for a name that can't exist (a random name under example.com) | The resolver is not used | | 0.0.0.0 for an ordinary name | The resolver blocks names on purpose: counted, fine for `adblock` and `family` only | | The letters of the name in another case than asked (DNS 0x20) | A forged answer: thrown away, for resolvers known to keep the case | | For a signed name, an answer that doesn't match its DNSSEC signatures | Thrown away, the next way asked; SERVFAIL if every way's answer is forged | ## What each way shows - **DNS over HTTPS / TLS**: the network sees only that you talk to a resolver, not which names. zev pads questions to 128 bytes (RFC 8467) so their length doesn't give the name away. - **Plain DNS, any port, UDP or TCP**: the network can see every name. It's honest (zev checks), not private. - **The resolver** sees your names either way; the `private` use picks resolvers that keep no logs and don't pass on your address. ## Limits - `sudo zev dns on` is for Linux for now. On macOS, run `sudo zev dns serve --listen 127.0.0.1:53` and set the system's DNS to 127.0.0.1. `check`, `bench` and `why` work on Linux and macOS. - Resolvers are reached over IPv4; names with IPv6 addresses (AAAA) are answered normally. - DNSSEC is checked for the algorithms in use today (RSA/SHA-1, RSA/SHA-256, RSA/SHA-512, ECDSA P-256 and P-384, Ed25519); a zone signed only with others (Ed448, the old DSA and GOST) counts as unsigned. NSEC3 with more than 150 iterations counts as unsigned too (RFC 9276). - Plain DNS over TCP opens a connection per question, so TCP ways rank below the others.