desktop: route wildcard zones to a local DNS stub
A hosts file cannot express a wildcard, so '*.youtube.com' was only ever the
base domain plus the guessed labels. The patterns the server now hands over
as unexpandable are answered by a stub on 127.0.0.1, and the helper owns one
systemd-resolved drop-in that points just those zones at it:
* the stub is listening before the drop-in is written, and the drop-in is
removed before the stub stops, so a zone's queries never land on a dead
port;
* stub and route live as long as the app, not the interval: between
intervals they forward everything, instead of restarting resolved (and
dropping its cache) on every pomodoro;
* a crashed run leaves a world-readable drop-in the next start adopts, and
the stub comes back before anything else;
* masks with nothing routable after their last '*' ('*gram*') are reported
as unenforceable, not silently dropped.
The Blocker page now reports what is really in place — the hosts region, the
routed zones, and the masks this machine cannot do — rather than the whole
expansion. 39 desktop tests, including the ordering invariants.
This commit is contained in:
12 files changed
+1695
-29
No files matched your search
+34
-5
@@ -49,11 +49,35 @@ the same build runs on KDE, GNOME or a bare window manager.
|
||||
## Blocking
|
||||
|
||||
The rules (which sites, which apps, when) live on the backend so the web app can
|
||||
edit them and a second machine obeys the same list. This app enforces them:
|
||||
edit them and a second machine obeys the same list. This app enforces them with
|
||||
two mechanisms, because no single one covers the masks people actually write:
|
||||
|
||||
* **Websites** — the managed region of `/etc/hosts` is kept in sync with the
|
||||
policy. Writing it needs root, which a desktop app must not have, so it calls
|
||||
`kadence-helper` through **pkexec**.
|
||||
* **Wildcards a hosts file cannot express** — a hosts file is a list of exact
|
||||
names, so `*.google.com` is expanded server-side into real hostnames. A bare
|
||||
`*` in the *subdomain expansion* list changes what that means: `*.google.com`
|
||||
then covers **every** subdomain, not just the configured labels, and those are
|
||||
answered by a small DNS stub on `127.0.0.1` (port 5353, falling back to
|
||||
5453/5553/5354) which the helper points `systemd-resolved` at with one drop-in
|
||||
for just the zones that need it (`~google.com`). Every other name flows to the
|
||||
machine's normal resolver untouched. The stub is unprivileged, speaks plain DNS
|
||||
over UDP and TCP, answers blocked A records with the redirect address and other
|
||||
record types with NODATA (so `HTTPS`/SVCB hints cannot smuggle an address past
|
||||
it), and returns SERVFAIL when no upstream can answer.
|
||||
* **What cannot be routed** — the zone is whatever follows the last `*`, and it
|
||||
needs at least two labels: `*gram.com` routes `gram.com`, while `*gram*` has
|
||||
nothing after its last `*` and `*.site` has a single label. Those stay honestly
|
||||
reported as "cannot be blocked on this computer" instead of pretending; the
|
||||
phone still matches them against the URLs it sees.
|
||||
* **Ordering** — the stub is listening *before* the drop-in is written, and the
|
||||
drop-in is removed *before* the stub stops, so resolved never has a moment with
|
||||
nowhere to send a zone's queries. The stub and the route belong to the app's
|
||||
lifetime, not to one interval: between intervals they stay up and forward
|
||||
everything, because repointing resolved costs a restart and a dropped DNS cache.
|
||||
If a crash left the drop-in behind, the next start sees it (the file is
|
||||
world-readable), brings its stub back first, and carries on.
|
||||
* **Applications** — `/proc` is scanned every few seconds for your own processes
|
||||
matching the list; they are terminated (SIGTERM, then SIGKILL) or merely
|
||||
reported, and every attempt is sent to the backend for the evening report.
|
||||
@@ -65,7 +89,7 @@ edit them and a second machine obeys the same list. This app enforces them:
|
||||
### The privileged helper
|
||||
|
||||
`helper/kadence-helper` is the entire privileged surface, and it is deliberately
|
||||
dull:
|
||||
dull. It has two jobs — the hosts region and the resolved drop-in:
|
||||
|
||||
* every hostname must match a strict pattern, the redirect must parse as an IP;
|
||||
* only the region between its own markers is rewritten — your own entries above
|
||||
@@ -73,12 +97,17 @@ dull:
|
||||
* the input list is opened `O_NOFOLLOW` and must be a regular file;
|
||||
* the write is atomic (temp file + `rename`), and the first run backs up
|
||||
`/etc/hosts` to `/etc/hosts.kadence-backup`;
|
||||
* `localhost` can never be blocked.
|
||||
* `localhost` can never be blocked;
|
||||
* `resolved-on` accepts only real zone names (two or more labels, no `*`, no
|
||||
ports or junk), deduplicates them, refuses privileged ports, and writes the
|
||||
drop-in `0644` atomically; `resolved-off` removes it. Both then restart
|
||||
systemd-resolved and flush its cache; on a machine that does not run resolved,
|
||||
they say so and the hosts half carries on alone.
|
||||
|
||||
```bash
|
||||
sudo ./helper/install.sh # polkit asks once per session
|
||||
sudo ./helper/install.sh --passwordless # or never, for your user only
|
||||
sudo ./helper/install.sh --uninstall # unblocks first, then removes itself
|
||||
sudo ./helper/install.sh --uninstall # unblocks, removes the drop-in, then removes itself
|
||||
```
|
||||
|
||||
---
|
||||
@@ -190,7 +219,7 @@ kadence_desktop/
|
||||
config.py # ~/.config/kadence/desktop.json
|
||||
tray.py # the painted countdown icon and its menu
|
||||
integrations/ # notifications, inhibit/DND, MPRIS, launcher, logind, D-Bus, autostart
|
||||
blocking/ # hosts file, /proc scanning, and the manager that syncs policy
|
||||
blocking/ # hosts region, DNS stub + resolved route, /proc scanning, the manager that syncs policy
|
||||
qml/ # Material UI: Timer, Tasks, Blocker, Report, Settings
|
||||
helper/ # the privileged helper + polkit action + installer
|
||||
packaging/ # .desktop entry, icon, user installer
|
||||
|
||||
Reference in new issue
Block a user