Skip to content

The clock query and the update fetcher give up every capability before they read from the network - #300

Merged
DevomB merged 3 commits into
mainfrom
net-clients-no-caps
Oct 9, 2026
Merged

DevomB merged 3 commits into
mainfrom
net-clients-no-caps

Conversation

@DevomB

@DevomB DevomB commented Oct 9, 2026

Copy link
Copy Markdown
Owner

What was wrong. sntp-offset.py and update-fetch.py run as the net zone's root. That root keeps CAP_NET_ADMIN and CAP_NET_RAW, plus CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT for dhcpcd's and dnsmasq's privilege drops. Neither script needs any of these, yet both parse what far hosts send: an NTP reply, and TLS and HTTP from the update channel. A flaw in that parsing would have had the firewall, the routes and raw sockets of the zone that carries every other zone's traffic.

What changed.

  • Both scripts begin with drop_capabilities(). It calls prctl(PR_SET_NO_NEW_PRIVS) and then capset with zeroed effective, permitted and inheritable sets, through ctypes; ambient goes with them. The zone's filter already allows both calls: capset through seccomp::CAP_CALLS for the kept CAP_SETUID, and prctl in the base list. kryptikd already sets no-new-privs on every zone process, and that is what keeps an emptied set empty across an exec by root. Setting it here makes the scripts hold the same guarantee wherever they run.
  • If the drop fails, the script refuses to run. The fetcher prints its usual one update-fetch: ... line; the SNTP client exits 1 with nothing on stdout, which the init already reads as no answer.
  • build/guest-tests/zones-check.sh: the new check fetch-and-sntp-no-caps runs both scripts through nsenter -t <net init> -U. In the net zone's user namespace, the zone's root holds every capability, so old code keeps them all. The fetcher waits on a broker socket that accepts but never answers, and the SNTP client waits on 127.0.0.1:9, which never replies. The check then reads /proc/PID/status and passes only when each process shows CapInh, CapPrm, CapEff and CapAmb all zero and NoNewPrivs: 1.
  • tools/image/zones-test.sh: lists the verdict. It goes on the list's last line, so it merges with Every claim in docs/architecture.md about what zones cannot share or reach has a guest check #280's and The net zone runs on one CPU's worth of time, and the zones suite reads its limit back #295's edits to that list.
  • docs/design/net-zone.md: what the two scripts hold, and the test.

How the run proves it. Under the Distro run's zones suite, the guest passes fetch-and-sntp-no-caps only with the drop in place. On main's scripts the same processes read CapEff: 000001ffffffffff and NoNewPrivs: 0. Under the real filter, the update suite's network step runs the real fetcher in the real net zone and the release still has to arrive, so a filter that refused prctl or capset would fail that step too. CI's offline suites (update-fetch.sh, netzone-time.sh) run both scripts unprivileged, where the drop also has to succeed.

Merges clean with every open PR except #180 and #207, which already conflict with main in run-ovmf.sh.

…eading the network

sntp-offset.py and update-fetch.py run as the net zone's root, which keeps
CAP_NET_ADMIN, CAP_NET_RAW and the privilege-separation capabilities for
dhcpcd and dnsmasq. Neither script needs any of them, yet a flaw in what
parses a time server's reply, TLS or HTTP would have had them all. Each
now sets no new privileges and empties its capability sets with capset
before it reads a byte, and refuses to run if it cannot.

The zones suite runs both in the net zone's user namespace, where its
root holds every capability, holds each waiting on a broker socket or a
time server that never answers, and reads that it holds none.
DevomB added 2 commits October 9, 2026 05:41
… whose loopback is up, and says what a send meets if it ends
@DevomB
DevomB marked this pull request as ready for review October 9, 2026 14:37
@DevomB
DevomB merged commit 8fd7613 into main Oct 9, 2026
21 checks passed
@DevomB
DevomB deleted the net-clients-no-caps branch October 9, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant