2012-07-30 24 views
10

Ich schreibe einen Webserver auf Python-Basis, der "Plugins" ausführen kann, damit die Funktionalität einfach erweitert werden kann.Python: Sicherung nicht vertrauenswürdiger Skripts/Subprozesse mit Chroot und Chjail?

Dafür habe ich den Ansatz, eine Reihe von Ordnern (eine für jedes Plugin) und eine Reihe von Shell/Python-Skripte in dort benannt nach vordefinierten Namen für verschiedene Ereignisse, die auftreten können.

Ein Beispiel ist eine on_pdf_uploaded.py Datei, die ausgeführt wird, wenn ein PDF auf den Server hochgeladen wird. Um dies zu tun, würde ich Pythons Subprozess Werkzeuge verwenden.

Für Bequemlichkeit und Sicherheit, würde dies erlauben mir Unix-Umgebungsvariablen zu verwenden, um weitere Informationen zu geben und das Arbeitsverzeichnis (cwd) des Prozesses festzulegen, so dass es auf die richtigen Dateien zugreifen kann, ohne ihren Standort zu finden.

Da der Plugin-Code von einer nicht vertrauenswürdigen Quelle stammt, möchte ich es so sicher wie möglich machen. Meine Idee war, den Code in einem Subprozess auszuführen, ihn aber in ein chroot-Gefängnis mit einem anderen Benutzer zu legen, so dass er nicht auf andere Ressourcen auf dem Server zugreifen kann.

Leider konnte ich nichts darüber finden, und ich würde mich nicht auf das nicht vertrauenswürdige Skript verlassen wollen, um sich selbst ins Gefängnis zu bringen.

Desweiteren kann ich den main/calling-Prozess auch nicht in ein chroot-Gefängnis stellen, da Plugin-Code in mehreren Prozessen zur gleichen Zeit ausgeführt werden kann, während der Server andere Anfragen beantwortet.

Hier ist die Frage: Wie kann ich Subprozesse/Skripte in einem Chroot-Gefängnis mit minimalen Berechtigungen ausführen, um den Rest des Servers vor fehlerhaftem, nicht vertrauenswürdigem Code zu schützen?

Vielen Dank!

+0

Ist das wirklich dein Job? Sollten sie nicht wissen, welchen Code sie ausführen? Was auch immer ... Hilft das? [os.chroot()] (http://docs.python.org/library/os.html#os.chroot). Plus, "os" hat gute Sachen, um mit der uid usw. zu verwirren. Also, erstelle einen neuen Prozess, (os.fork()?) Dann os.setuid dann os.excle(). – Logan

Antwort

2

Nachdem Sie Ihr Gefängnis erstellt haben, würden Sie os.chroot von Ihrer Python-Quelle aufrufen, um darin zu gehen. Aber selbst dann wären alle gemeinsam genutzten Bibliotheken oder Moduldateien, die der Interpreter bereits geöffnet hat, noch geöffnet, und ich habe keine Ahnung, welche Konsequenzen das Schließen dieser Dateien über os.close hätte; Ich habe es nie ausprobiert.

Auch wenn dies funktioniert, ist die Einrichtung von Chroot eine große Sache, also seien Sie sicher, dass der Nutzen den Preis wert ist. Im schlimmsten Fall müssten Sie sicherstellen, dass die gesamte Python-Runtime mit allen Modulen, die Sie verwenden möchten, sowie alle abhängigen Programme und gemeinsam genutzten Bibliotheken und andere Dateien von /bin, /lib usw. in jedem inspizierten Dateisystem verfügbar sind. Und natürlich schützt dies nicht andere Arten von Ressourcen, d. H. Netzwerkziele, Datenbank.

Eine Alternative könnte darin bestehen, den nicht vertrauenswürdigen Code als Zeichenfolge einzulesen und dann exec code in mynamespace, wobei mynamespace ein Wörterbuch ist, das nur die Symbole definiert, die Sie dem nicht vertrauenswürdigen Code aussetzen möchten. Dies wäre eine Art "Gefängnis" innerhalb der Python-VM. Möglicherweise müssen Sie die Quelle zuerst nach Dingen wie import Anweisungen analysieren, es sei denn, die eingebaute __import__ Funktion würde das abfangen (ich bin nicht sicher).

+0

Ich ging mit dem Dynamic Code Loading-Ansatz, weil es die beste Flexibilität des Datenaustauschs und der Ausnahmebehandlung bietet. – BastiBen

4

Vielleicht so etwas?Dann

# main.py 
subprocess.call(["python", "pluginhandler.py", "plugin", env]) 

,

# pluginhandler.py 
os.chroot(chrootpath) 
os.setgid(gid) # Important! Set GID first! See comments for details. 
os.setuid(uid) 
os.execle(programpath, arg1, arg2, ..., env) 
# or another subprocess call 
subprocess.call["python", "plugin", env]) 

EDIT: Gesucht fork(), aber ich habe nicht wirklich verstehen, was es getan hat. Ich habe es nachgeschlagen. Neuer Code!

# main.py 
import os,sys 
somevar = someimportantdata 
pid = os.fork() 
if pid: 
    # this is the parent process... do whatever needs to be done as the parent 
else: 
    # we are the child process... lets do that plugin thing! 
    os.setgid(gid) # Important! Set GID first! See comments for details. 
    os.setuid(uid) 
    os.chroot(chrootpath) 
    import untrustworthyplugin 
    untrustworthyplugin.run(somevar) 
    sys.exit(0) 

This war nützlich, und ich schlich so ziemlich genau, dass Code, so ein dickes Lob an diesen Kerl für ein anständiges Beispiel.

+1

Das hat meine Probleme ziemlich gelöst. Die Sache war schwierig wegen der Installation von Psutil in einem virtualenv und Benutzerberechtigungen, aber nach einigem Versuch/Fehler habe ich es behoben. – coya

+0

@habnabit können Sie auf Ihrer Bearbeitung näher ausführen? Ich bin nicht vertraut mit der Frage, warum die Reihenfolge wichtig ist, und ich glaube, sie verdient es, in der Antwort expliziter genannt zu werden, wenn sie Auswirkungen auf die Sicherheit hat. – Logan

+1

@Logan https://Stackoverflow.com/a/11062896 – habnabit