Skip to main content

max / alloy

4.2 KB · 80 lines History Blame Raw
1 /* Alloy: the settings a laptop owner changes about their own machine.
2 *
3 * `alloy settings` fronts timedatectl, hostnamectl and localectl. Every one of
4 * those actions ships as auth_admin_keep, so without this file the System tab
5 * would raise a password prompt to change a timezone. Granting them outright to
6 * an active local session in wheel is the honest reading: the person sitting at
7 * the machine, already logged into it, setting its clock is not performing an
8 * administrative act that a password would clarify. See wiki note
9 * `alloy-privilege` for the ladder this is the first rung of, and for what is
10 * deliberately left prompting.
11 *
12 * `active && local` is doing real work in the rule below: an ssh login is
13 * neither, so these five rows prompt over ssh exactly as they would without
14 * this file. That is not a gap. It is the same rule read honestly — nobody is
15 * sitting at the machine — and the console answers it at the second rung, by
16 * running the setter again under `pkttyagent`. Widening the grant to cover
17 * remote sessions would trade the whole justification for one less prompt.
18 *
19 * The list is short on purpose. A rules file is a security artifact and should
20 * be readable in one sitting; the failure mode to avoid is a grant that quietly
21 * covers more than anyone remembers asking for. Omarchy issue 5708 is the
22 * cautionary case, a first-run sudoers line of NOPASSWD on all of
23 * /usr/bin/systemctl written to enable one unit.
24 *
25 * Not granted, deliberately:
26 *
27 * timedate1.set-time the clock row is display-only; the console does
28 * not set the time by hand, and moving a system
29 * clock invalidates certificates and reorders logs
30 * timedate1.set-local-rtc nothing in Alloy offers it
31 * hostname1.set-machine-info pretty hostname, icon, chassis, deployment and
32 * location. Location in particular is not a thing
33 * to hand over for a screen that sets a hostname
34 * hostname1.get-product-uuid, .get-hardware-serial identifiers, not settings
35 * NetworkManager.settings.modify.system nothing to grant: it already reads
36 * allow_active=yes on this image, so saving a
37 * system connection is answered without asking
38 * rpmostree1.* changing the system image should be asked about;
39 * the prompt there is the point
40 *
41 * CALLER CONSTRAINT. `hostnamectl hostname NAME` with no scope flag sets the
42 * static, transient and pretty names, and the pretty one needs
43 * set-machine-info, which is not granted here. The console must call
44 * `hostnamectl --static hostname NAME`; hostnamed applies the static name to
45 * the running kernel hostname on its own. Granting set-machine-info to avoid
46 * one flag would trade a wider grant for less typing.
47 *
48 * Ordering: polkit reads /etc/polkit-1/rules.d and /usr/share/polkit-1/rules.d
49 * as one lexically sorted set, and the first rule to return a value wins. This
50 * file ships at 50- so anything a user drops in /etc with a lower number
51 * overrides it outright.
52 */
53
54 polkit.addRule(function (action, subject) {
55 var granted = [
56 "org.freedesktop.timedate1.set-timezone",
57 "org.freedesktop.timedate1.set-ntp",
58 "org.freedesktop.hostname1.set-static-hostname",
59 "org.freedesktop.locale1.set-locale",
60 "org.freedesktop.locale1.set-keyboard"
61 ];
62
63 /* active: the session in front of the screen, not one left open on another
64 * VT. local: not over SSH, where "the person sitting at the machine" is the
65 * whole justification and does not hold. wheel: the group Fedora's polkit
66 * already treats as administrators, and the one `alloy install` puts the
67 * first account in.
68 *
69 * Returning nothing rather than NOT for everything else, so this file
70 * grants and never denies: a rule that returned NOT would override a later
71 * one that had a better reason to allow something.
72 */
73 if (granted.indexOf(action.id) < 0) {
74 return;
75 }
76 if (subject.active && subject.local && subject.isInGroup("wheel")) {
77 return polkit.Result.YES;
78 }
79 });
80