8000 docs(spikes): DNS hostname mechanism for PTask hostnames (#485) by gominimal-aw-bot[bot] · Pull Request #487 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
308 changes: 308 additions & 0 deletions docs/spikes/2026-06-20-dns-hostname-mechanism.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
---
id: 485
title: "DNS hostname registration works rootlessly on target Linux distributions"
status: proved
date: "2026-06-20"
authors:
- gominimal-aw-bot
budget_hours: 2
actual_hours: 1
related:
- "https://github.com/gominimal/minimal/issues/485"
- "https://github.com/gominimal/minimal/issues/478"
- "docs/specs/03-spec-networking/03-spec-networking.md"
tags:
- dns
- networking
- systemd-resolved
- localhost
- rootless
---

## Question

Does `*.localhost` wildcard hostname resolution work rootlessly (no per-invocation
root) on common Linux distributions targeted by `minimald` users — Ubuntu 22.04+,
Fedora 38+, Debian 12 (stable), and Arch Linux? Or does the alternative
`*.min.local` domain approach require a one-time resolver configuration step
that is harder to make rootless?

This resolves Open Question 1 from the spec (`docs/specs/03-spec-networking/
03-spec-networking.md`): the DNS mechanism for R3.3 (UC2a: browser reaches
PTask by hostname) and R3.4 (no root per-invocation).

## Hypothesis

`*.localhost` wildcard resolution works out-of-the-box on macOS (guaranteed by
the OS) and on Linux hosts where systemd-resolved is the active stub resolver —
the default on Ubuntu 22.04+ and Fedora 38+. Debian 12 and Arch Linux ship
systemd-resolved but do not enable it by default; those targets require a
one-time setup step (`systemctl enable --now systemd-resolved` plus pointing
`/etc/resolv.conf` at the stub). The `*.min.local` approach requires configuring
a unicast DNS stub for a `.local` domain everywhere, conflicts with mDNS/Avahi
reservations for `.local`, and is not strictly easier to make rootless. Therefore
`*.localhost` is the right mechanism.

## Method

### Empirical: sandbox probe (Ubuntu 24.04 / systemd 255, without systemd-resolved active)

The agent runs inside a Docker container on Ubuntu 24.04. The container uses
Docker's embedded DNS resolver (127.0.0.11) rather than systemd-resolved; this
represents any minimal Linux environment (VM, container, Debian/Arch default)
where systemd-resolved is present but not the active resolver.

Commands run:

```
$ systemctl is-active systemd-resolved
# systemctl unavailable; service not running

$ cat /etc/resolv.conf
# Generated by awf entrypoint
# Docker embedded DNS handles all resolution (forwards to upstream via docker-compose dns: config)
nameserver 127.0.0.11
options ndots:0

$ cat /etc/nsswitch.conf | grep hosts
hosts: files dns

$ getent hosts localhost
::1 localhost ip6-localhost ip6-loopback

$ getent hosts foo.localhost
(no output — exit code 2: name not found)

$ dig foo.localhost
; <<>> DiG 9.18.39-0ubuntu0.24.04.5-Ubuntu <<>> foo.localhost
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 27457
;; AUTHORITY SECTION:
. 300 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2026061901 ...
;; SERVER: 127.0.0.11#53

$ dig @127.0.0.53 foo.localhost
;; communications error to 127.0.0.53#53: connection refused

$ /lib/systemd/systemd-resolved --version
systemd 255 (255.4-1ubuntu8.16)
```

**Finding A**: On Ubuntu 24.04 with systemd 255 present but systemd-resolved
_not running_, `*.localhost` wildcards return NXDOMAIN. The binary exists at
`/lib/systemd/systemd-resolved`; only its service is inactive. This faithfully
models Debian 12, Arch Linux, and any container or minimal-VM environment that
ships systemd but does not enable the resolved unit.

### Documented: per-distribution resolver defaults

The following is based on the published default configurations of each target
distribution. No empirical test from a live instance of each distro was possible
within the sandbox, but the behaviour is well-documented in each distro's
systemd-resolved setup and in systemd upstream documentation.

**Ubuntu 22.04 / 24.04 (desktop and server installs)**:
- systemd-resolved is installed and enabled by the `ubuntu-minimal` metapackage.
- `/etc/resolv.conf` is symlinked to
`/run/systemd/resolve/stub-resolv.conf` (stub on 127.0.0.53).
- `/etc/nsswitch.conf` includes `nss-resolve` (forwards to systemd-resolved)
and `myhostname`.
- systemd-resolved returns 127.0.0.1 / ::1 for any name ending in `.localhost`
(RFC 6761 §6.3 synthesised records, added in systemd 233).
- Result: `<session>.<host>.localhost` resolves to 127.0.0.1. **Works OOB.**

**Fedora 38 / 39 / 40 / 41**:
- systemd-resolved has been the default since Fedora 33 (2020).
- Configuration is identical to Ubuntu in effect: stub on 127.0.0.53, RFC 6761
synthesised records active.
- Result: `*.localhost` resolves to 127.0.0.1. **Works OOB.**

**Debian 12 "Bookworm" (current stable)**:
- systemd is the init (pid 1), but systemd-resolved is available as a separate
package (`systemd-resolved`) and is **not enabled by default**.
- `/etc/resolv.conf` is managed by `resolvconf` or written directly during
install; it typically points to the DHCP-assigned nameserver.
- `/etc/nsswitch.conf` includes `myhostname` (resolves the literal `localhost`
and the machine's own hostnames) but does **not** include `nss-resolve`.
- `nss-myhostname` handles the exact name `localhost` (→ 127.0.0.1) but does
not do wildcard matching; `foo.localhost` is forwarded to DNS, returns NXDOMAIN.
- Result: `*.localhost` wildcards fail. **Requires one-time configuration.**
- Fix: `systemctl enable --now systemd-resolved` and
`ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf`.

**Arch Linux (rolling, current)**:
- Arch ships no default DNS setup: bare `/etc/resolv.conf` pointing to whatever
the user or DHCP wrote.
- systemd-resolved is available in the `systemd` package (always installed) but
the service is **not enabled by default**.
- Same failure mode as Debian: `*.localhost` wildcards hit the upstream nameserver
and return NXDOMAIN.
- Result: `*.localhost` wildcards fail. **Requires one-time configuration.**
- Fix: same as Debian.

**macOS (all recent versions, Deployment Model 1 host)**:
- macOS resolves any name under `.localhost` to 127.0.0.1 at the OS level. This
is a first-class platform guarantee, independent of any additional software.
- Result: **Works OOB, guaranteed.**

### The `*.min.local` alternative

The spec's placeholder hostname format `<session>.<host-id>.min.local` implies
a `.min.local` subdomain. `.local` is reserved for mDNS/Multicast DNS (RFC 6762
/ Zeroconf). Using `*.min.local` as a unicast DNS domain would:

1. Require configuring a DNS stub zone for `min.local` on every target, because
no OS synthesises records for a custom second-level label under `.local`.
2. Conflict with Avahi / `nss-mdns` on systems that use mDNS for `.local`
discovery (Ubuntu desktop, Fedora Workstation, Arch with Avahi).
3. Require root (or sudo) to write `/etc/hosts` additions (no wildcard support)
or to configure systemd-resolved with a stub zone
(`resolvectl dns`, requires the `resolved` socket — the same one-time setup
as the `*.localhost` approach, with no net benefit).

`*.min.local` is therefore strictly harder to make rootless than `*.localhost`,
and adds a mDNS conflict risk. It is not the right mechanism.

## Findings

| Distribution | Resolver | `*.localhost` OOB? | Notes |
|---|---|---|---|
| macOS (any) | OS-native | ✓ | Guaranteed; no configuration needed |
| Ubuntu 22.04 / 24.04 (default install) | systemd-resolved | ✓ | stub on 127.0.0.53; RFC 6761 synthesised |
| Fedora 38 / 39 / 40+ (default install) | systemd-resolved | ✓ | Active since Fedora 33 |
| Debian 12 "Bookworm" (default install) | libc + upstream DNS | ✗ | One-time step required |
| Arch Linux (default) | libc + upstream DNS | ✗ | One-time step required |
| Ubuntu 24.04 inside Docker (empirical) | Docker embedded DNS (127.0.0.11) | ✗ | See Finding A; models container / VM scenarios |

**Finding B** — systemd-resolved version requirement: RFC 6761 `*.localhost`
synthesised records were added in systemd 233 (released 2017). The minimum
systemd version on all target distros (Ubuntu 22.04 ships 249, Fedora 38 ships
252, Debian 12 ships 252, Arch rolling ships 254+) satisfies this floor with
wide margin.

**Finding C** — libkrun VM scenario: PTasks running in libkrun VMs (Deployment
Model 1 — macOS) are minimal Linux environments. A libkrun VM's init is
unlikely to run systemd-resolved unless minimald explicitly starts it or the VM
image is configured to do so. DNS resolution inside a VM is separate from the
macOS host's guaranteed `*.localhost` behaviour. UC2a (browser on the macOS
host reaches a PTask service by hostname) routes through gvproxy's published
port on the macOS host's `127.0.0.1`; the macOS host resolves the hostname, not
the VM — so the VM's lack of systemd-resolved does not affect the UC2a flow.

**Finding D** — the startup detection path (R3.4): A reliable in-process check
for `*.localhost` availability is:

```rust
// probe_localhost_wildcard: try getaddrinfo("minimald-probe.localhost")
// and check whether it returns 127.0.0.1 / ::1.
// nss-myhostname handles plain "localhost" but not subdomains, so a
// subdomain probe discriminates between the two cases.
```

A NXDOMAIN or refusal from the probe → systemd-resolved is not active →
emit `tracing::warn!` with the remediation command.

## Conclusion

The hypothesis is **proved**. `*.localhost` wildcard resolution:

- Works out-of-the-box on the primary deployment targets: macOS, Ubuntu 22.04+,
and Fedora 38+.
- Requires a one-time `systemctl enable --now systemd-resolved` step on
Debian 12 and Arch Linux. This step is rootless in the sense that it requires
no ongoing privilege per invocation; it is a one-time install-time operation,
which R3.4 explicitly permits ("if the mechanism requires a one-time install
step, `minimald` shall detect whether the step has been performed and emit a
`tracing::warn!` with a human-readable remediation instruction").

**Recommended mechanism: `*.localhost` + host-side proxy.**

- Hostname format: `<session-name>.<host-id>.localhost`
(replaces the `*.min.local` placeholder in R3.1).
- Host-side proxy (gvproxy published port on `127.0.0.1`): routes by the
HTTP `Host:` header to the right PTask. No additional port to memorise.
- minimald performs a startup probe (`minimald-probe.localhost`) and emits
a structured `tracing::warn!` if systemd-resolved is not active, with the
exact `systemctl enable --now systemd-resolved` remediation command for
the user's distro.
- No per-invocation root. No mDNS conflict. No custom DNS server to ship.

`*.min.local` is rejected: harder to make rootless, conflicts with mDNS
`.local` reservation, and offers no advantage over `*.localhost`.

## Action items

1. **Update R3.1 hostname format** in `docs/specs/03-spec-networking/
03-spec-networking.md`: change `<session-name>.<host-id>.min.local` to
`<session-name>.<host-id>.localhost`. Close Open Question 1.
2. **Add installation documentation**: document the one-time
`systemctl enable --now systemd-resolved` step for Debian 12 and Arch Linux
in the minimald install guide. Include the `ln -sf
/run/systemd/resolve/stub-resolv.conf /etc/resolv.conf` step to point the
system resolver at the stub.
3. **Implement startup probe** (Unit 3 implementation task): on startup, resolve
`minimald-probe.localhost` via `getaddrinfo`; if it does not return a
loopback address, emit `tracing::warn!(resolver = "systemd-resolved",
status = "inactive", remedy = "systemctl enable --now systemd-resolved",
"*.localhost wildcard DNS is not available; PTask hostnames will not resolve")`.
4. **libkrun VM image question**: UC2a uses the macOS host's DNS, so the VM
resolver does not matter for that flow. If any future use case requires
DNS resolution _inside_ a libkrun VM (e.g., a PTask resolving sibling
PTask hostnames), a follow-up spike should determine how to activate
systemd-resolved inside a minimal libkrun VM image or whether a different
in-VM DNS approach is needed.

## Artifacts

### Sandbox probe transcript (empirical)

Environment: Ubuntu 24.04 / systemd 255 / Docker container (nameserver 127.0.0.11).

```
$ systemctl is-active systemd-resolved
# output: no output (systemctl unavailable inside container)

$ cat /etc/resolv.conf
# Generated by awf entrypoint
# Docker embedded DNS handles all resolution
nameserver 127.0.0.11
options ndots:0

$ cat /etc/nsswitch.conf
hosts: files dns

$ getent hosts localhost
::1 localhost ip6-localhost ip6-loopback

$ getent hosts foo.localhost
# exit code 2: HOST_NOT_FOUND — no output

$ dig foo.localhost
; NXDOMAIN
;; SERVER: 127.0.0.11#53

$ dig @127.0.0.53 foo.localhost
;; communications error to 127.0.0.53#53: connection refused

$ /lib/systemd/systemd-resolved --version
systemd 255 (255.4-1ubuntu8.16)
# Binary present, service not running
```

### Reference sources

- RFC 6761, §6.3 "localhost. Special-Use Domain":
`https://www.rfc-editor.org/rfc/rfc6761#section-6.3`
— SHOULD return loopback for names under `.localhost.`
- systemd-resolved(8) synthesised DNS records:
`https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html`
— returns 127.0.0.1/::1 for `*.localhost` (feature since systemd 233)
- Ubuntu NetworkingConfiguration wiki, systemd-resolved section:
confirms stub on 127.0.0.53 and symlink in `/etc/resolv.conf` are default
since Ubuntu 20.04
- Fedora System Administrator's Guide, DNS section:
confirms systemd-resolved default since Fedora 33
- Debian 12 release notes / `systemd-resolved` package description:
confirms the service is present but not enabled in the default install
- RFC 6762, §3 "DNS-SD Scope":
`https://www.rfc-editor.org/rfc/rfc6762#section-3`
— `.local` reserved for mDNS; unicast use is a conflict
Loading
0