The resolver
The resolver is the mediated DNS server. It answers only names its caller’s rule allows, from an
allowlist and a blocklist, and never sends a disallowed name upstream. ipd asks it when
a person’s session connects by name, and filters and pins what it answers. So a principal’s network
capability can name domains, not only prefixes and ports, and DNS itself is not a way out.
Purpose
People need to reach services by name, and addresses behind a name change. A name a client
resolved for itself could stand for any address, so a person connects by name at ipd, which
asks the resolver. The resolver is the one place names become addresses and it applies the name
rules; ipd filters what it answers and pins the connection. It also stops DNS being a channel: a
client that can send arbitrary queries to an arbitrary resolver can encode data in the names it
asks for.
Interface
Resolving a name
Status: planned · M5 (self-hosted development)
- Name rules. A caller’s badge names an allowlist of domain names and suffixes and a blocklist
that subtracts from it and always wins. A suffix matches only at a label boundary:
example.comcoversa.example.comand neverevilexample.com. The rules come from whoever granted the connection, and a grant only narrows them. - Callers. The steward mints one resolver connection per (account, label set) from its
resolver root and hands it to
ipdwith that principal’sipdroot;ipduses exactly that connection for everyconnect(name, port)under that root and never shares one across roots (ipd). A session’s own lookups (forgetaddrinfo) use its own steward-minted resolver connection, carrying the same rule; their answers are only informational, since the session connects by name. resolve(name)answers the IPv4 addresses fornameif the rules allow it, with their lifetime. A disallowed name is refused locally with its own error,refused, and never sent upstream, so the names queried cannot carry data out; a nonexistent allowed name is the upstream’s NXDOMAIN (R63 (only allowed names)).- Queries go upstream from the resolver alone, as DNS over TCP through its own
ipdconnection, to the upstream resolvers its arguments name. Sessions have no route to port 53, and no route to anything by address. - Names are checked strictly: lower-cased, at most 253 bytes, labels of letters, digits and hyphens, no trailing tricks; a name that is not a plain host name is refused.
- The resolver is a sink. Like
ipdit refuses every labelled caller, since a query leaves the box.
The attack test: a disallowed name never reaches upstream (the test upstream sees no query).
Open: the protocol table and which page includes it.
Connections by name, pinned
Status: planned · M5 (self-hosted development)
ipd resolves at connect time: it checks the name against the connecting connection’s rule, asks
the resolver, drops every always-forbidden address from the answer, connects to one of the rest,
and pins the connection to that address for its whole life
(ipd). A client never names an address, and an answer widens
nothing beyond the one connection it was asked for
(R64 (connections by name are pinned)). Name rules are for
people’s sessions only; an agent holds no socket and no name rule, and reaches outside the box only
through gatewayd (gatewayd).
Open: none.
Caching
Status: planned · M5 (self-hosted development)
The cache is keyed by the connection a query arrives on, its own badge, never by anything in the
query and never by the connection’s admission share: the account-0 fold of
R26 (admission fairness) governs admission only.
Since the steward mints one connection per (account, label set) for ipd and one for the session,
each principal’s lookups fill only its own caches, and no principal’s answers are served from
another’s: a shared cache would tell one principal, by how fast it answers, which names another
asked for. A cached answer keeps the lifetime the upstream gave it, capped by the resolver’s own
maximum.
The attack test: two principals resolve one name and the upstream sees two queries; a mutation that keys the cache by rule or name alone fails it.
Open: the cap on an answer’s lifetime and on each connection’s cache size.
Authority
Status: planned · M5 (self-hosted development)
- The resolver holds its endpoint, one
ipdconnection scoped to its upstream resolvers’ addresses and port. - A client’s authority is its name rules: which names it may have answered.
- It holds no keys and no device.
Open: none.
Security properties
R63 (only allowed names)
Status: planned · M5 (self-hosted development)
The resolver sends upstream, and answers, only names its caller’s rules allow and its blocklist does not; a refused name causes no query. So a client cannot use DNS queries to carry data to a resolver it chose, or to learn names outside its rules.
Open: none.
R64 (connections by name are pinned)
Status: planned · M5 (self-hosted development)
A connection made by name reaches exactly one address, one the resolver answered for that name at connect time and that is not always-forbidden, on a port its rule allows, for its whole life.
Open: none.
Failure and restart
Status: planned · M5 (self-hosted development)
- The resolver restarts: its cache is gone; connections by name made meanwhile fail and are
asked again. Connections already open are
ipd’s, pinned, and stay. - An upstream resolver fails or lies: the answer is an error, or another address, which
ipdstill filters; a lie cannot reach a forbidden address or a port the rule does not allow.
Open: none.
Residual risks
- Upstream answers are believed. DNS over TCP is unauthenticated: an upstream or on-path attacker can answer an allowed name with another address, and the person’s connection goes there on the allowed ports. Forbidden-address filtering still holds. DNS over TLS waits for a TLS implementation.
- The allowed names are a channel. A client may choose which allowed names it asks for and when; the resolver bounds the alphabet to its rules, not the timing.
- The upstream’s cache is shared. The upstream DNS server’s own cache serves everyone outside the box as well, so timing there is a side channel the resolver cannot close.
- A name’s addresses are shared. An allowed name hosted beside other services on one address reaches all of them on the allowed ports.
Why
- Names resolved for
ipd, not for the client.ipdchecks the name the session asked for and the resolver answers it; the resolver alone could not stop a connection it does not carry, andipdalone would not know what a name means. Pinning ties the two together. - No route to port 53 for sessions. A client that can query any resolver can encode anything in the names it asks for; one resolver that answers only allowed names closes that.
- Per-principal caches. A shared cache is a timing channel between principals.