Roger Wilco wrote:Ich finde das polling durch einen Cronjob, der jede Minute läuft nicht sonderlich elegant. Wieso sollte die Aktion nicht direkt durch die Benutzereingabe (natürlich erst nach einer Plausibilitätsprüfung) getriggert werden?
Polling ist auch nicht elegant. Es ist hier aber auch nicht der Punkt. Es geht hier um zwei Dinge.
Das Erste ist Rechteminimierung - wir wollen so viel als möglich ohne Privilegien tun, und wir wollen insbesondere die Prüfung aller Parameter und alle UI-Sachen ohne Rechte tun.
Das Zweite ist ist, den privilegierten Bereich sauber zu halten, also dort nix zu verarbeiten, was eine direkte Benutzereingabe ist. Das gezeigte Beispiel war schon ganz gut. Dort wurde der Name einer Tätigkeit ("Teamspeak restarten") transportiert und nicht ein Pfadname, der so direkt in eine system()-Anweisung einfliessen kann. Auf der privilegierten Seite existiert dann eine statische Zuordnung von Namen und Kommandos
Code: Select all
$jobs = array("Teamspeak restarten" => "/usr/local/bin/restart-teamspeak");
und auf diese Zuordnung und den Inhalt der ausgeführten Kommandos hat man von der unprivilegierten Seite erst einmal keinen Einfluss.
Die MySQL-Queue hat den Vorteil, daß man so auch gleich einen Paßwortprüfungsmechanismus hat. Man speichert in der unprivilegierten Seite einfach gar keine Paßworte, sondern recht die Benutzer-Usernamen und Paßworte einfach direkt ins mysql_connect() weiter. Die Rechte im MySQL setzt man nun so auf, daß diese User gar nichts machen können, außer vielleicht eine Stored Procedure enqueue() aufzurufen, die mit SQL SECURITY DEFINER läuft (also mit den Rechten des Autors von enqueue(), nicht mit den Rechten des Aufrufers von enqueue()). In enqueue() schreibt man das übergebene Kommando in die Queue-Tabelle und hinterlegt dabei zwangsweise auch session_user() (das ist der Username, mit dem man sich angemeldet hat, current_user() ist in einer SQL SECURITY DEFINER Prozedur der User, der die Prozedur geschrieben hat).
Auf dem privilegierten Ende der Queue kann man so den Request und wer ihn aufgegeben hat entnehmen, in ein Kommando umwandeln und das Kommando mit Privilegien ausführen, das alles in einer zweiten Result-Tabelle loggen und an die GUI zurückgeben.
Die Tabellen haben dabei eine ganze Reihe von Vorteilen - Datenbanken lösen einem hier das Locking-Problem, man kann atomar einen Job als "eingereiht, wartend", "in Bearbeitung", "gelöscht", "abgearbeitet" und "manuell pausiert" markieren ohne sich den Wolf zu programmieren. Man hat außerdem den ganzen Kram sofort asynchron (die GUI hakt nie!) und persistent (die Resultate und das Log stehen für einen Audit bequem zur Verfügung). Und es skaliert halt schön...
Das einzig Unschöne ist, da hast Du Recht, das Polling. Das kann man vermeiden, indem man in irgendeiner Weise vom unprivilegierten Teil in den privilegierten Teil signalisiert, und dafür gibt es eine Reihe von Mechanismen - leider sind die meisten rein mit PHP ein wenig hakelig zu bedienen.
Ob ich jetzt die Kommandos erst in eine Queue schreibe und durch ein Skript, das jede Minute läuft, wieder auslesen und umsetzen lasse, oder ob ich die Kommandos direkt nach der Aktion im richtigen Kontext ausführe (z. B. durch einen Systemaufruf im PHP-Skript und sudo), macht im Endeffekt keinen Unterschied.
Synchron, kein Locking, kein Logging und eine Shell auf dem Transportweg, also möglicherweise EscapeShellCmd()-Probleme. Und die Frage, wie Du den Aufrufer authentisierst - die Möglichkeit mit den MySQL-Usern macht es extrem leicht, komplett ohne Paßworte in den Scripten auszukommen.