Wir kennen alle sudo
oder doas
-- Programme, die es erlauben,
Befehle als ein anderer Benutzer auf einem System, meist als root,
auszuführen. Auf einem Einzelplatzsystem ist man
auch oft auch gerne geneigt, seinen Benutzer in die Gruppe "wheels" zu
packen und in der /etc/sudoers
der Gruppe das Attribut :NOPASSWD
zu
verpassen und man kann durch einfaches Hinzufügen von sudo alle Befehle
ausführen. "sudo make me a sandwich" halt.
Jetzt ist es vielleicht notwendig, bestimmte Befehle nur mit bestimmten Kommandozeilenargumenten zu für bestimmte Benutzer zu erlauben. Da kann man mit sudo und doas bei komplexeren Setups schon an seine Grenzen kommen.
Die Regeln ("rules") werden in /etc/polkit-1/rules.d
in
*.rules
-Dateien gespeichert. Die Regeln werden dort als
JavaScript-Programme -- ja, wirklich! -- abgespeichert.
Für den Benutzer "john" wollen wir nun erlauben, alle Befehle als
Benutzer "root" auszuführen. Wir editieren die Datei
/etc/pokit-1/rules.d/10-example.rules
und fügen ein:
polkit.addRule( function(action, subject) {
if ( action.id == "org.freedesktop.policykit.exec" ) {
if (subject.user == "john") {
return polkit.Result.YES;
}
}
});
Nun speichern wir und der PolicyKit-Dienst wird die Änderungen sofort detektieren und sollte sie sofort aktivieren.
In diesem Beispiel passiert nun Folgendes:
Es wird die Funktion addRule
des globalen polkit
-Objektes
aufgerufen. polkit
muss nicht erstellt oder initialisiert werden,
es existiert bereits in diesem Kontext.
Eine anonyme Funktion mit zwei Parametern action
und subject
wird übergeben.
subject
beinhaltet Informationen über den Prozess, der überprüft
werden soll. Darin gibt es z.B. das Attribut user
, das den
Benutzernamen hält. Dieser kann innerhalb der Funktion auf einen
bestimmten Wert überprüft werden.
action
beinhaltet nur ein Attribut id
, was das Zielprogramm
identifiziert, das ausgeführt werden soll, in diesem Fall
policykit.exec
was durch den Befehl pkexec
verkörpert wird.
Treffen also beide if-Anweisungen zu, wird der Wert polkit.Result.YES zurückgegeben, andernfalls nichts.
Probieren wir das Ganze einmal aus -- erst ohne die Einträge in der
rules-Datei. Wir versuchen, als User "john" den Inhalt des
Verzeichnisses /root
aufzulisten:
> pkexec ls /root
==== AUTHENTICATING FOR org.freedesktop.policykit.exec ====
Authentication is needed to run `/usr/bin/ls' as the super user
Authenticating as: John Connor (john)
Password:
Wir werden nach einem Passwort gefragt. Fügen wir die obigen Zeilen einer
rules
-Datei in /etc/polkit-1/rules.de
hinzu:
> pkexec ls /root
dir1 dir2
Das funktioniert schonmal.
Jetzt möchten wir gerne die Befehle für den Butzer john auf einige wenige
einschränken. Z.B. auf ls
, cat
und less
. Dazu kommt das action
-Object
mit einer Methode lookup
. Je nachdem was für eine action-Instanz wir hier
vorliegen haben, ist das interne Dictionary mit den entsprechenden Werten
befüllt. Hier, bei action.id
org.freedesktop.policykit.exec
hält der Key
command_line
die Zeichenkette des Befehls, die unmittelbar auf pkexec
folgt.
Somit können wir die erlaubten Befehle mit einem Zweizeiler eingrenzen:
polkit.addRule( function(action, subject) {
if ( action.id == "org.freedesktop.policykit.exec" ) {
let allowed_programs = ["/usr/bin/ls", "/usr/bin/cat", "/usr/bin/less"];
let program = action.lookup("command_line").split(" ")[0]
if (subject.user == "sxw" && allowed_programs.includes(program)) {
return polkit.Result.YES;
} else {
return polkit.Result.NO;
}
}
});
Hier sehen wir zudem noch, dass wir im else
-Zweig ein explizites
polkit.Result.NO
zurückgeben. Das verhindert, dass polkit den Passwort-Prompt
für Root-Authentifizierung anzeigt, so dass der Benutzer den Befehl bei Eingabe
des Passwortes dennoch ausführen könnte.
Jetzt können wir das ganze auch nur zwischen 12 und 18 Uhr zulassen. Dank JavaScript haben wir Zugriff auf die ganze Class-Library:
let hours = new Date().getHours();
if ( hours < 12 || hours >= 19 ) {
return polkit.Result.NO;
}
Das Ganze kann man dann auch je nach Programm nach Belieben anpassen.
Beim Entwickeln kann es ganz hilfreich sein, Meldungen in den Log zu schreiben.
Das kann man mit polkit.log("...")
tun. Die Meldungen landen dann im Journal
und können in einer anderen Shell dann mit journalctl -f -u polkit
beobachtet
werden.
Ich weiß, die meisten werden mich jetzt lynchen für einen Artikel im systemd-Umfeld und dann kommt auch noch JavaScript drin vor. Aber ich kann mir schon den ein oder anderen Fall vorstellen -- gerade auf Systemen, die von einer Vielzahl an Benutzern mit unterschiedlichen Admin-Aufgaben frequentiert werden.