chown() mit PHP und Apache als Superuser?

S

sevY

Hi zusammen,

ich habe mir ein kleines Adminpanel zum Anlegen von vhost, webalizer, temp_site gebaut.
SysCP & Co waren mir zu oversized.

Mein Problem:

Ich möchte als UID ftpuser und als GID apache nehmen.
PHP 5.0.4 (via APXS2 unter Apache2) führt das Kommando allerdings mit diesen Parametern nicht aus.

Laut manual muss der ausführende Superuser sein. Der apache läuft als apache:apache

Wie bekomme ich nun das ganze hin?



Hier das Script:

PHP:
<?php 
header('Content-Type: text/html; charset=utf-8'); 
require_once('config.inc.php');
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="default.inc.css" type="text/css">
<title>ROPEnetwork Setup Tool</title>
</head>
<body>

<?php

if(isset($_POST['setup']) && is_dir($webroot.'/'.$_POST['directory'])) {
    $base=$webroot.'/'.$_POST['directory'].'/'.$_POST['domain'];
    
    
    if(isset($_POST['create_docroot'])) {
    
 
    	mkdir($base,intval(0777,8)) or exit('Error while creating '.$base);
    	chmod($base,intval(0774,8)) or exit('Error while changing mode for '.$base.' to 0774');
    	chown($base,$uid) or exit('Error while changing owner of '.$base.' to '.$uid);
    	chgrp($base,$gid) or exit('Error while changing group of '.$base.' to '.$gid);
   
  
		$folderset=array(array('htdocs',0774),array('logs',0070),array('session',0070),array('tmp',0070),array('cgi',0740),array('webalizer',0770),array('webalizer/'.$_POST['domain'],0770));
    	foreach($folderset as $part) {
        	mkdir($base.'/'.$part[0],intval(0777,8)) or exit('Error while creating '.$base.'/'.$part[0]);
        	chmod($base.'/'.$part[0],intval($part[1],8)) or exit('Error while changing mode for '.$base.' to 0774');
        	chown($base.'/'.$part[0],$uid) or exit('Error while changing owner of '.$base.' to '.$uid);
        	chgrp($base.'/'.$part[0],$gid)  or exit('Error while changing group of '.$base.' to '.$gid);
		}
	}
    
    if(isset($_POST['create_html'])) {
    	copy($html_tpl,$base.'/htdocs/index.html') or exit('Error while copying '.$html_tpl.' to '.$base.'/htdocs/index.html');
    }
    
    
    if(isset($_POST['create_vhost'])) {
    	file_put_contents($vhostdir.'/'.$_POST['domain'].'.conf',ereg_replace('{LOG}',$logdir,ereg_replace('{DOMAIN}',$_POST['domain'],ereg_replace('{BASE}',$base,file_get_contents($vhost_tpl))))) or exit('Error while writing Apache2 vHost to '.$vhostdir.'/'.$_POST['domain'].'.conf');
    }
    
    if(isset($_POST['create_webalizer'])) {
    	file_put_contents($webalizerdir.'/'.$_POST['domain'].'.webalizer',ereg_replace('{LOG}',$logdir,ereg_replace('{DOMAIN}',$_POST['domain'],ereg_replace('{BASE}',$base,file_get_contents($webalizer_tpl))))) or exit('Error while writing Webalizer Config to '.$webalizerdir.'/'.$_POST['domain'].'.webalizer');
    }

}





?>
<div class="main">
    <div class="content">
        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
            <input type="hidden" name="setup" value="1">
            <select class="singleline" name="directory">
                <?php
                foreach(scandir($webroot) as $directory) {
                    echo '<option value="'.$directory.'">'.$directory.'</option>';
                }
                ?>
            </select><br>
            <input class="singleline" type="text" name="domain" value="domain.tld"><br>
            <input type="checkbox" name="create_docroot" checked="checked"> Create Docroot?<br>
            <input type="checkbox" name="create_vhost" checked="checked"> Create vHost?<br>
            <input type="checkbox" name="create_webalizer" checked="checked"> Create Webalizer?<br>
            <input type="checkbox" name="create_html" checked="checked"> Create HTML?<br>
            <input class="submit" type="submit" value="Setup"></form></div></div>
</body>
</html>


Hier die Config
PHP:
<?php
error_reporting(0); 
$webroot='/var/www';
$uid='ftpuser';
$gid='apache';
$html_tpl='default/default.tpl';
$vhost_tpl='default/default.conf';
$webalizer_tpl='default/default.webalizer';
$vhostdir='/etc/apache2/vhosts.d';
$webalizerdir='/etc/webalizer/vhosts';
$logdir='/var/log/apache2';
?>

Viele Grüße

Yves
 
Yves alter Haudegen,

du formatierst ja immer noch so grauenhaft wie früher ... stimmt nicht, ist
schon um einiges besser geworden.

Zu Deinem Problem: Idealerweise müsstest Du dem User apache die Gruppe
admin zuweisen, was natürlich nicht gerade dem Sicherheitsgedanken ent-
spricht. Warum willst Du eigentlich die Verzeichnisse den verschiedenen Be-
nutzern zuordnen - welcher Vorteil ergibt sich daraus?


PS: Wohne jetzt in Hannover ... ist aber scheiße da, wie Du gesagt hast! :mad:
 
Mh, also falls du nur usern noch zugriff auf die verzeichnisse geben willst, wie waere es mit Access Controll Lists? Muss natuerlich vom jeweiligen betriebssystem unterstuetzt werden, auf dem deine daten liegen (z.b. 10.4 kann es ja nun gluecklicherweise)
 
Du kannst das eigentlich nicht in einer verantwortlich verwalteten PHP-Umgebung auf diese Weise machen.

Die einzige Möglichkeit ist ein Daemon oder ein externes Programm, das vom PHP-Script mit den nötigen Rechten gestartet werden kann, um die Dateirechte umzusetzen. Dafür kannst Du z.B. ein C-Programm schreiben, das die Aufrufe ausführt, die ausführbare Datei root gehören lassen, und das suid-Flag setzen. Eine andere Alternative wäre, dem Apache-User sudo zu erlauben, natürlich nur für den einen Befehl den Du tatsächlich privilegiert ausführen musst. Denk aber immer dran: Je mehr ein PHP-Script Dein System administrieren kann, desto mehr kann es auch jemand, der eine Lücke darin (oder sonstwo in Deinem Webserver!) ausnutzt!
 
Delmar schrieb:
...
Zu Deinem Problem: Idealerweise müsstest Du dem User apache die Gruppe
admin zuweisen, was natürlich nicht gerade dem Sicherheitsgedanken ent-
spricht.
...[/SIZE]
Auah!
Abgesehen davon glaube ich, dass das nicht genügt, weil
Gruppe "admin" != "Superuser"
dannycool schrieb:
...
Eine andere Alternative wäre, dem Apache-User sudo zu erlauben, natürlich nur für den einen Befehl den Du tatsächlich privilegiert ausführen musst.
...
Die Idee mit dem C oder c++ Wrapper ist gut und funktioniert vermutlich auch; bei dem sudo frage ich mich, wer das Passwort eingibt.
Oder willst Du sudo ohne Passwort einsetzen (wieder auah!)
 
Zuletzt bearbeitet:
Hi,

ich habe das Script gebaut, um "SysCP" like neue Kunden auf unserern Server einzurichten (das ganze ist seit gestern auf eine Art CRM ausgebaut…).
Ich war und bin kein Fan von Confixx, Virtuoso, SysCP, Webmin & Co, da mir diese System teilweise zu komplex und auch zu unsicher sind.

Nur ist es bei mittlerweile knapp mehr als 800 Domains m.E. nach höchste Zeit, mal eine Alternative zum manuelle einrichten per Shell zu finden.
Bisher habe ich das immer mit Bash Scripts und einer Checkliste manuell gemacht… vHosts erstellt, FTP Accounts angelegt usw. …dauerte pro Neukunden gut und gerne 30 Minuten.
Ich musste ja auch noch die ganzen Daten in das Formblatt für Neukunden einfügen… alá „Willkommen, hier sind Ihre Zugangsdaten, das sind die technischen Details Ihres Accounts“… und dann halt noch den ganzen Buchhaltungskram.

Naja… SysCP usw. wollte ich trotzdem nicht… ich vertraue in der Hinsicht nur dem, was ich selbst geschrieben habe.

Nur zur Technik… chown() und chgrp() Funktioniert ausschließlich als Superuser.
Und Apache als Superuser laufen zu lassen… ich weiss nicht. Eventuell könnte man mit mod_suexec das PHP Script über FastCGI aufrufen… und eben nur diesem einen vHost entsprechend einen Superuser als suexecuser zukommen lassen.

Warum die Sache mit den Gruppen? Ganz einfach… ich bin sehr pingelig was Rechtevergabe auf den Produktivsystemen angeht… ist es angedacht (auch aufgrund des chrooted laufenden Apache2), das jeder Neukunde auch einen eigenen User bekommt und entsprechend das vHost dann damit betrieben wird.
Ist mittlerweile auch ein muss, da wir TCL, Ruby, Python usw. anbieten… und es da keine OpenBaseDirRestrictions wie bei PHP gibt.

Nun… warum PHP? Ich habe in letzter Zeit viel mit PHP gemacht… es sind erstaunliche Dinge möglich, wenn man mal in die tiefen des ZendCore abtaucht… Semaphoren, Session Wrapper für Servlets usw. … und da dachte ich „nimm' doch PHP bevor Du die Java-Wemmse rausholst“.
In C bin ich (noch) nicht so fit, das ich sowas „sicher“ schreiben kann.

Aber vielleicht baue ich das ganze auch auf Commandline Ebene… das PHP Script bekommt dann eine Shebang-Line und ist unabhängig vom Apache.


Liebe Grüße
Yves
 
Achso… das System ist Gentoo Linux 2005.0 Hardened, es laufen Apache2 und PHP5 (noch als APXS2 Modul… demnächst als Fast-CGI Executable)… PureFTP, Postfix, Cyrus (Aggretator Cluster), MySQL und PostgreSQL.
Aufgrund dieser „nicht-standard“ Ausstattung möchte ich gerne selbst ein Admintool entwickeln… eben um alles nach „eigenem Ermessen“ verwalten zu können… und Dinge, die mir zu heikel sind, nur in Form eines Befehls in einer DB ablegen und gesondert freischalten… usw.
 
maceis schrieb:
Auah!
Abgesehen davon glaube ich, dass das nicht genügt, weil
Gruppe "admin" != "Superuser"

Ja. Auf dem Mac gibt es diese Gruppe und Mitglieder sind Administratoren. Das heißt aber auch nur, dass man sudo benutzen darf um root zu werden. Man ist es nicht automatisch ständig.

Die Idee mit dem C oder c++ Wrapper ist gut und funktioniert vermutlich auch;

Das funktioniert unter Garantie, ist aber sehr aufwändig.

bei dem sudo frage ich mich, wer das Passwort eingibt.
Oder willst Du sudo ohne Passwort einsetzen (wieder auah!)

Bei sudo ohne Passwort kann man immernoch sagen, dass man nur die entsprechenden chown- und chgrp-Aufrufe machen darf. Jemanden in sudoers einzutragen, heißt nicht automatisch, dass der alles darf. Man kann das auch für ein Programm einzeln (z.B. eben chown) machen, und dann weiters noch die erlaubten Argumente für dieses Programm festlegen.

Optimal ist das trotzdem nicht, aber man muss kein C programmieren können. Scriptsprachen eignen sich für so einen Wrapper ja nicht so toll.
 
Noch eine kleine Ergänzung: Sämtliche solchen Interfaces, die ich bisher näher betrachtet habe (hauptsächlich in kommerziellen Produkten) haben das über einen Daemon gelöst, der über RPC die entsprechenden Aktionen (und nur die) ausgeführt hat. Das ist aber natürlich mit Abstand das aufwändigste.

Was ich selbst machen würde kann ich leider nicht sagen, da ich Linux am liebsten per bash bediene. ;)
 
dannycool schrieb:
...
Das funktioniert unter Garantie, ist aber sehr aufwändig.
Nein, eigentlich kann man das sehr flach halten.
Man kann ja die Kommandos in einer beliebigen Skriptsprache schreiben und in C/c++ nur einen Wrapper erstellen, der dann das SUID Bit erhält.
Der Wrapper ruft dann das Arbeitsskript auf und zwar mit entsprechenden Rechten.
Ist relativ harmlos.


dannycool schrieb:
...
Bei sudo ohne Passwort kann man immernoch sagen, dass man nur die entsprechenden chown- und chgrp-Aufrufe machen darf. Jemanden in sudoers einzutragen, heißt nicht automatisch, dass der alles darf. Man kann das auch für ein Programm einzeln (z.B. eben chown) machen, und dann weiters noch die erlaubten Argumente für dieses Programm festlegen.
...
Das ist mir bekannt, aber auch hier würde ich nicht chgrp etc. pauschal "freischalten", sondern mit einem Programm arbeiten, dass eben die erforderlichen Aufrufe enthält.
Bin aber trotzdem nicht so begeistert von diesem Ansatz, weil es eine zusätzliche Ebene einführt, ohne das Ausgangsproblem zu lösen.

dannycool schrieb:
Scriptsprachen eignen sich für so einen Wrapper ja nicht so toll.
Wenn man das in einer Scriptsprache umsetzen würde, könnte man auf einen Wrapper komplett verzichten.
Das Problem ist, dass man dann die Skriptausführung mit SUID Bit aktivieren muss (ist ne sysctl Variable, IIRC), dann allerdings könnte auch das "Arbeitsskript" direkt das SUID Bit erhalten.

Zusammenfassung:
Nach meiner Ansicht ist die beste und einfachste Lösung ein kompiliertes Programm (binary), welches nur die Aufgabe hat, das Skript in dem (ausschließlich) die erforderlichen Kommandos stehen, mit ausreichenden Rechten aufzurufen.
C oder c++ bieten sich da an, theoretisch würde das aber (wennn es denn unter Mac OS X wäre) sogar mit einem kompilierten AppleSkript funktionieren.
 
maceis schrieb:
Nein, eigentlich kann man das sehr flach halten.

Sorry, Missverständnis ist meine Schuld. Ich meinte, dass man dafür C lernen muss. Wir haben es hier ja mit einem PHPler zu tun. Wenn man C kann, ist es sehr einfach.

Das Problem ist, dass man dann die Skriptausführung mit SUID Bit aktivieren muss (ist ne sysctl Variable, IIRC), dann allerdings könnte auch das "Arbeitsskript" direkt das SUID Bit erhalten.

Es gibt auch andere Lösungen, aber eigentlich will man das ja nicht, weil es zusätzliche Lücken schafft. Speziell will man nicht, dass ein Scriptinterpreter in einem Webserver sowas kann.
 
Zurück
Oben Unten