Permissions and sudo
What needs root
Section titled “What needs root”| Runs as you | Needs root |
|---|---|
list, search, diff, status, check, config-path |
apply |
add, rm, enable, disable (without --apply) |
off |
group *, zone * (without --apply) |
backup restore, backup prune |
source add/update/list/rm |
any command with --apply |
init, import, edit, completions, backup list, man |
migrate, once it gets to dropping the old block |
The split follows one rule: writing to /etc/hosts or to the backup directory needs root,
and nothing else does. backup prune is on the root side for the second reason — it
deletes files in a root-owned directory, so it checks up front instead of reporting that
it deleted nothing.
When permission is missing, hostsctl does not try to work around it. It prints the exact
command to re-run and exits with code 4:
error: apply: no write access to /etc/hosts run: sudo hostsctl applyWhat sudo does not break
Section titled “What sudo does not break”The config and the cache live in your home directory, not root’s. Under sudo, $HOME
points at the invoking user only some of the time and $XDG_CONFIG_HOME points at root’s
environment, so hostsctl does not trust either: the real home directory is resolved from
SUDO_USER through passwd.
Files and directories created while running as root are handed back to the invoking user,
walking up the chain from the created directory to the home directory. The result is that
sudo hostsctl apply never leaves root-owned files in ~/.config or ~/.cache — the
classic way a tool makes its own config unreadable for the person who owns it.
Dry runs need nothing
Section titled “Dry runs need nothing”hostsctl apply -n # renders, diffs, writes nothinghostsctl diff # the same picture, without the apply ceremony--dry-run is global, so it works on every command. On a mutating command it reports what
would have been written to the config as well.
The target file’s own permissions
Section titled “The target file’s own permissions”The write is atomic: a temporary file next to the target, fsync, then rename. The mode
and owner of the existing target are read first and applied to the temporary file, so
/etc/hosts stays root:wheel 0644 regardless of your umask, and the file it is replaced
by is a regular file with the same identity.
If the resulting content would not contain 127.0.0.1 localhost, the write is refused
before anything is renamed. That guard catches both a bad config and a bug in hostsctl
itself.
Using a different target
Section titled “Using a different target”hostsctl --target /tmp/hosts apply -y--target (or settings.target, or $HOSTSCTL_TARGET) points hostsctl at another file.
The DNS cache flush is skipped unless the target really is /etc/hosts — editing a copy in
/tmp is of no interest to the resolver. This is how the test suite exercises the whole
write path without touching the system file.