suexec my php, baby^wapache2
Von einem der auszog, seine PHP-Scripts nicht mehr als www-data laufen zu sehen.
Auf der Suche nach einer Lösung für ein Standardproblem
Während meiner ISP-Zeit habe ich es nur in zwei Bereichen geschafft, den Rat eines Kollegen ("Du kannst Dich nicht mit allem auskennen") zu beherzigen: Webserver und RADIUS. Der bewusste Verzicht auf RADIUS-Kenntnisse wäre heute im ISP-Bereich ein Hindernis, und Webserver-Knowhow versuche ich derzeit durch kleinere private Projektchen aufzubauen.
Eins dieser Projektchen war der Bau eines Webservers, bei dem PHP-Scripts nicht mit den Rechten des Webservers laufen, sondern mit den Rechten des Benutzers. Auf Servern, bei denen die Betreiber der virtuellen Server sich nicht gegenseitig vertrauen, ist das eine Grundanforderung, die gar nicht sooo leicht zu befriedigen ist. Apache bringt zwar schon seit geraumer Zeit suexec mit, das eigentlich genau diese Aufgabe erledigen soll. Allerdings arbeiten die großen Hoster teilweise aus historischen Gründen mit grotesk gepatschtem suexec, so dass man die Berichte, die man aus diesen Ecken bekommt, für ein privates Feldwaldundwiesenprojekt nicht umsetzbar sind. Die im Web ergooglebaren Dokumentationen sind allesamt durchwachsen, und nicht selten so voller Widersprüche, dass man am besten mit dem Lesen aufhört, ehe man völlig verwirrt ist.
Kurze Recherche zeigt, dass suexec sowieso ein Auslaufmodell ist. Irgendwann wird apache2 mit dem perchild threaded model so weit sein, dass für jeden virtuellen Host ein eigener Apache-Prozess mit den entsprechenden Rechten läuft, was alle heuten Probleme lösen dürfte. Aber, "This MPM is not currently expected to work correctly, if at all." Also lieber erstmal die Finger weg. Den Spezialfall PHP bekommt man heute mit suphp erschlagen, aber das ist ein Projekt für die nächsten Tage. Mein aktuelles Vorhaben war, die Geschichte von der Pike auf zu lernen, und das heißt definitiv klassisches suexec.
suexec ist ein Wrapper für CGI-Scripts, der vom Apache aufgerufen dank suid root erstmal seine Privilegien eskaliert, nach Prüfung einer ganzen Latte von Preconditions seine Rechte auf die des "target users" reduziert und schließlich endlich das CGI-Script aufruft. Nun, wenn ich hier von CGI spreche, meine ich auch CGI. Da perl und php normalerweise als Apache-Modul gerufen werden, kann man da nicht mit suexec eingreifen. Also muss man die Scripts als CGI aufrufen, was zu einem Performanceverlust führt. Das machen die großen Hoster auch nicht anders, also ist dieser Weg so verkehrt nicht.
Also: mod_php raus, php4-cgi installiert und die (einfache) Konfiguration für "PHP als CGI" durchgeführt.
Dabei fällt schon auf, dass die apache2-Packages von Debian ziemlich schlau gebaut sind: Jede Apache-Erweiterung bringt in ihrer Package entsprechende Konfigschnipsel mit, die der Administrator mit den Scripts a2{en|dis}{mod|site} ein- und ausschalten kann. Gute Arbeit, das fühlt sich viel besser an als die wilden Debconf-Orgien ("Pondering....... Doing Magic......") der apache-1.3-Packages. Ist halt wieder eine typische Debian-Geschichte mit vielen kleinen Dateien, die apache aber dank seines flexiblen Konfig-Parsers automatisch zusammenstellen kann; update-apache2.conf gibt es - glücklicherweise - nicht. Kris wird dieses Setup sicher nicht gefallen, ich mag es. Schön finde ich auch, dass suexec, das sehr stark zur compilezeit konfiguriert wird, in der Debian-Package sinnvoll parametriert daherkommt.
Die CGIs im Userdir ~/public_html laufen auf Anhieb unter meinem User. Allerdings ist's etwas komplexer, das auch ausserhalb des Userdirs hinzubekommen. Ursache dafür ist, dass ich mir selbst ein Bein gestellt hatte. Ich ging fälschlicherweise davon aus, dass auch ausserhalb des Userdirs suexec seine Rechte auf die des Users fallen lässt, dem die Datei gehört, in dem das CGI-Script steht; sozusagen ein auf den Webserver übertragenes suid-bit. Das stimmt aber nicht: Für suexec ausserhalb des Userdirs muss man innerhalb der Definition des virtual hosts explizit mit der Direktive SuexecUserGroup einen Target-User und eine Target-Group angeben, sonst fällt suexec transparent und kommentarlos auf den Fallbackuser zurück, der www-data heißt und man somit der Meinung ist, dass das suexec gar nicht aufgerufen würde. Nachdem dies endlich verstanden war, war der Weg zum ersten als mh laufenden CGI aus /var/www nicht mehr weit.
PHP als CGI unter suexec ist ein bisschen schwieriger. Bei einem über eine Action aufgerufenen php zählt nicht der Ablageort des PHP-Scripts, sondern der Ablageort des PHP-Binaries - und das liegt natürlich bei Debian weder innerhalb des Webspaces, noch gehört es dem richtigen User. Also wird man für jeden User, der PHP verwenden wird, ein eigenes PHP-Binary innerhalb des Webspaces brauchen, das obendrein auch noch dem richtigen User gehören muss. Ausserdem braucht's wohl noch eine passende Action-Direktive für jeden Virtuellen Host; das hab ich allerdings noch nicht ausprobiert, weil ich immer nur mit einem virtuellen Host gearbeitet habe. Kaum macht man's richtig, funktioniert's auch schon, und mein passthru("id") sagte ordentlich id=1001(mh). Erfolgserlebnis.
Was lernen wir daraus: Auch wenn man erwartet, zu wissen was in der Doku steht, kostet Querlesen gerne mal einen Tag Zeit, weil man die wichtigen Informationen mehrfach überliest. de.comm.software.webserver ist für Fragen dieser Gewichtsklasse überfordert, ausser Bastian Blank hat sich niemand zur Antwort auf meine Fragen motivieren können.
Dasselbe gilt für #apache in Freenode, dessen Niveau bedauernswert niedrig ist und wo die Leute größtenteils nicht einmal wussten, was suexec ist. Das beste Debugging-Tool ist nach wie vor strace, weil man damit dem Prozess so weit auf die Finger gucken kann, dass man irgendwann mal zu dem Schluß kommt "das ist aber nicht so wie ichs aus der Doku erinnere, lasst mal lieber genau nachlesen".
Nächste Projekte: suphp, und dann endlich die erste eigene s9y-Testinstallation.
Comments
Display comments as Linear | Threaded
kju on :
Ich empfehle nach wie vor lighttpd. Dort wird PHP über FastCGI ausgeführt, so daß sowohl chroot wie setuid kein Problem sind. Und deutlich performanter als Apache(2)+modphp ist es auch noch.
Marc 'Zugschlus' Haber on :
lighttpd ist ein Exot. Den kann und will ich mir erst angucken, wenn ich apache (das ist der Standard) gut genug kann um vergleichen zu können.
In der Praxis wird man in einer Unternehmensanwendung mit einem Kunden, der IIS, IIS und IIS kennt, höchstens mit der Standardlösung der "freien" Welt kommen können, und das ist nun mal der apache.
Ich kann es nicht oft genug sagen: Es geht hier zu 80 % ums Lernen und zu 20 % darum, eine funktionierende Lösung zu erreichen.
Florian Laws on :
Auf der ApacheCon war über mpm_perchild auch zu hören, dass es keine Planung gebe, wann dieses Modul einsatzfähig werde. Anscheinend ist das gerade völlig ungewartet.
Marc 'Zugschlus' Haber on :
Das ist natürlich bedauerlich, denn der Ansatz erscheint mir vielversprechend.
Django on :
Unter
http://www.debianhowto.de/de:howtos:sarge:apache2_php-fcgi
gibts ne Anleitung, wie man das noch mit Fastcgi macht. Das hat zudem noch den Vorteil, dass Suexec ein kleines Shell Script ausführt, man also nicht jedem User ein eigenes PHP Binary geben muss. Wenn PHP als CGI ausgeführt wird, dann werden nicht alle http Befehle übergeben, somit lässt sich kein WEBDAV machen. Interessant an dieser Lösung ist auch, dass man jedem User seine eigene Konfiguration und seine eigene PHP Version geben kann.