Wie kann am besten überprüft werden, ob der Benutzer eines Skripts über Root-ähnliche Berechtigungen verfügt?

73

Ich habe ein Python-Skript, das viele Dinge erledigt, für die Berechtigungen auf Stammebene erforderlich sind, z. B. das Verschieben von Dateien in / etc, das Installieren mit apt-get usw. Ich habe derzeit:

if os.geteuid() != 0:
    exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")

Ist dies der beste Weg, um die Prüfung durchzuführen? Gibt es andere Best Practices?

Paul Hoffman
quelle

Antworten:

31

Nach dem Prinzip "Einfacher um Vergebung als um Erlaubnis bitten":

try:
    os.rename('/etc/foo', '/etc/bar')
except IOError as e:
    if (e[0] == errno.EPERM):
       sys.exit("You need root permissions to do this, laterz!")

Wenn Sie über die Nicht-Portabilität von os.geteuid()Ihnen besorgt sind, sollten Sie sich wahrscheinlich /etcsowieso nicht damit herumschlagen .

msw
quelle
Dies ist die portabelste Methode und funktioniert sogar unter etwas exotischeren Umständen, z. B. wenn SELinux die Root-Rechte einschränkt.
Florian Diesch
4
Dies kann je nach Situation tatsächlich schlimmer sein. Hmm aber Florian hat Recht.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
@Longpoke - Beziehen Sie sich auf die Situation, in der ein Benutzer möglicherweise Umbenennungsberechtigungen in / etc hat, aber - sagen Sie, dass es nicht möglich ist, eine Datei aus diesem Pfad zu entfernen? Wenn Sie das meinen, ist der Versuch, im Voraus festzustellen, über welche Fähigkeiten der Benutzer verfügt, ein Kinderspiel. Besser, Sie strukturieren Ihren Code für die "Transaktions" -Semantik, bei der A und B zurückgesetzt werden können, wenn C fehlschlägt.
msw
@msw: Ich dachte anfangs: "Sie sollten nur prüfen, ob Sie root sind oder nicht und beenden", um Transaktionsfolgen zu vermeiden, aber SELinux, ACLs und Funktionen brechen dies definitiv, also denke ich, der einzige Weg, dies wirklich zu tun Richtig ist, was Sie vorgeschlagen haben.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
1
Manchmal müssen Sie wissen, ob alle Teile der Aufgabe erfolgreich sein werden, bevor Sie Änderungen vornehmen. Ich möchte die Schritte 1, 2 und 3 nicht ausführen, nur um herauszufinden, dass ich Schritt 4 nicht ausführen kann, und hoffe, dass ich zurücktreten kann, was getan wurde.
Keithpjolley
75

os.geteuiderhält die effektive Benutzer-ID, genau das, was Sie wollen, daher kann ich mir keinen besseren Weg vorstellen, um eine solche Prüfung durchzuführen. Das eine, was ungewiss ist, ist das "root-like" im Titel: Ihr Code prüft genau root , kein "like", und in der Tat würde ich nicht wissen, was "root-like, aber nicht root" bedeuten würde - also , wenn du etwas anderes meinst als "genau root", kannst du vielleicht klarstellen, danke!

Alex Martelli
quelle
2
Ich gehe davon aus, dass Paul sich Sorgen über verschiedene Systeme macht, die unterschiedliche Benutzeroberflächen für Administratoren verwenden. Normalerweise ist id (root) == 0, aber es ist kein Muss und auf einigen Systemen ist es wirklich nicht gleich.
Stan
@ Stan: dann wäre EAFP in der Tat besser
msw
8
Die Annahme, dass effektive UID == 0 "root" bedeutet, ist ziemlich tief in UNIX-Code verwurzelt (einschließlich in den meisten UNIX-ähnlichen Kernelquellen). Technisch gesehen ist diese Annahme unter Linux NICHT unbedingt richtig. Das Linux-Modell "Funktionen" ermöglicht die Ausführung des Systems mit detaillierteren Steuerelementen, die über die Prozessvererbung (lcap2-Wrapper) oder möglicherweise über erweiterte Dateisystemattribute delegiert werden. Auch SELinux-Funktionen könnten mit solchen Annahmen Chaos anrichten. Für 99% der Systeme da draußen ist geteuid () == 0 ausreichend; für den Rest versuchen Sie: ... außer: ist Ihr Freund.
Jim Dennis
15

Sie können den Benutzer zum Sudo-Zugriff auffordern:

import os, subprocess

def prompt_sudo():
    ret = 0
    if os.geteuid() != 0:
        msg = "[sudo] password for %u:"
        ret = subprocess.check_call("sudo -v -p '%s'" % msg, shell=True)
    return ret

if prompt_sudo() != 0:
    # the user wasn't authenticated as a sudoer, exit?

Der sudo -vSwitch aktualisiert die zwischengespeicherten Anmeldeinformationen des Benutzers (siehe man sudo).

jcarballo
quelle
1
Zur Verdeutlichung können Sie auf diese Weise überprüfen, ob der Benutzer im wahrsten Sinne des Wortes über Root-Rechte verfügt. Die Instanz des Programms, für das Sie den Root-Zugriff ausführen, wird jedoch nicht angegeben, wenn er beim Start nicht über diese verfügt. Sie könnten so etwas dafür versuchen .
Mateo de Mayo
7

Wenn Sie wirklich möchten, dass Ihr Code für eine Vielzahl von Linux-Konfigurationen robust ist, sollten Sie die Eckfälle berücksichtigen, in denen möglicherweise jemand SELinux oder Dateisystem-ACLs verwendet, oder die Funktionen des Linux-Kernels seit v. 2.2 oder so. Ihr Prozess wird möglicherweise unter einem Wrapper ausgeführt, der SELinux verwendet hat, oder unter einer Linux-Funktionsbibliothek wie libcap2 libcap-ng oder fscaps oder elfcap über etwas Exotischeres wie das wunderbare und leider unterschätzte Systrace- System von Niels Provos .

Auf all diese Arten wird Ihr Code möglicherweise als Nicht-Root ausgeführt, und Ihrem Prozess wurde möglicherweise der erforderliche Zugriff übertragen, um seine Arbeit ohne EUID == 0 auszuführen.

Daher würde ich vorschlagen, dass Sie erwägen, Ihren Code pythischer zu schreiben, indem Sie Vorgänge einschließen, die aufgrund von Berechtigungen oder anderen Problemen mit Ausnahmebehandlungscode fehlschlagen können. Wenn Sie verschiedene Vorgänge ausführen möchten (z. B. mithilfe des subprocessModuls), können Sie allen solchen Aufrufen sudoein Präfix voranstellen (z. B. als Befehlszeilen-, Umgebungs- oder RC-Dateioption). Wenn es interaktiv ausgeführt wird, können Sie anbieten, alle Befehle, die berechtigungsbezogene Ausnahmen auslösen , erneut auszuführen sudo(optional nur, wenn Sie sie sudoin der Datei os.environ ['PATH'] finden).

Insgesamt ist es richtig, dass die meisten Linux- und UNIX-Systeme den größten Teil ihrer Verwaltung noch von einem Benutzer mit Root-Berechtigung durchführen lassen. Es ist jedoch alte Schule und wir als Programmierer sollten versuchen, neuere Modelle zu unterstützen. Wenn Sie versuchen, Ihre Vorgänge auszuführen und die Ausnahmebehandlung ihre Arbeit erledigen zu lassen, kann Ihr Code unter jedem System ausgeführt werden, das die von Ihnen benötigten Vorgänge transparent zulässt. Es sudoist eine nette Geste , sich dessen bewusst und einsatzbereit zu sein (da dies bei weitem am weitesten verbreitet ist) Tool zur kontrollierten Delegierung von Systemberechtigungen).

Jim Dennis
quelle
5

Ich suche gerne in den Umgebungsvariablen nach Sudo:

wenn nicht 'SUDO_UID' in os.environ.keys ():
  print "Dieses Programm erfordert Super User Priv."
  sys.exit (1)
vossman77
quelle
Das ist einfach und ordentlich. Vielen Dank!
Blueren
5

Für Linux:

def is_root():
    return os.geteuid() == 0
dimon4eg
quelle
3

Antwort auf den zweiten Teil der Frage

(Entschuldigung, das Kommentarfeld war zu klein)

Paul Hoffman, Sie haben Recht, ich habe nur einen Teil Ihrer Frage behandelt, der sich mit Intrinsics befasst, aber es wäre keine würdige Skriptsprache, wenn sie nicht behandelt werden könnte apt-get. Die bevorzugte Bibliothek ist ein bisschen ausführlich, aber sie macht den Job:

>>> apt_get = ['/usr/bin/apt-get', 'install', 'python']
>>> p = subprocess.Popen(apt_get, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> p.wait()
100                 # Houston, we have a problem.
>>> p.stderr.read()
'E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)'
'E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n'

Ist Popenaber ein verallgemeinertes Werkzeug und kann der Einfachheit halber verpackt werden:

$ cat apt.py
import errno
import subprocess

def get_install(package):
    cmd = '/usr/bin/apt-get install'.split()
    cmd.append(package)
    output_kw = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE}
    p = subprocess.Popen(cmd, **output_kw)
    status = p.wait()
    error = p.stderr.read().lower()
    if status and 'permission denied' in error:
        raise OSError(errno.EACCES, 'Permission denied running apt-get')
    # other conditions here as you require
$ python
>>> import apt
>>> apt.get_install('python')
Traceback ...
OSError: [Errno 13] Permission denied running apt-get

Und jetzt kehren wir zur Ausnahmebehandlung zurück. Ich werde es ablehnen, die Java-ähnliche Überallgemeinheit des Unterprozessmoduls zu kommentieren.

msw
quelle
1
Wäre es nicht sinnvoller, dies in Ihrer anderen Antwort zu bearbeiten?
jpmc26
2

Meine App funktioniert mit diesem Code:

import os
user = os.getenv("SUDO_USER")
if user is None:
    print "This program need 'sudo'"
    exit()
Guillaume
quelle
Schlechter Code. Der Fehler geworfen hier ist TypeError, denn wenn Sie nicht als root ausgeführt wird , os.getenv("SUDO_USER")Rückkehr , Nonedie vom Typ ist NoneType, und Sie können nicht verketten strund NoneType. Wenn Sie verwenden möchten, os.getenv("SUDO_USER")um das Problem zu lösen, sollten Sie es wie if os.getenv("SUDO_USER") == None: print "This program need 'sudo'"; exit()
Oder B
1
Vielen Dank für das Update. Dieser Code testet nur sudo, nicht den Root-Zugriff. Da es nicht nach root sucht, wenn Sie sich als root anmelden (oder einen Cronjob wie mich geschrieben haben), müssen Sie das Skript immer noch mit sudo aufrufen, da sudo kein Passwort für root fragt, konnte ich meinen Cronjob reparieren mit nur meinem Python-Skript mit sudo aufrufen.
Cediddi
Ein bisschen spät zur Party, aber das ist in der Tat sehr nützlich! In Kombination mit so etwas os.getenv('HOME')zeigt Ihnen, ob der aktuelle Benutzer ein Benutzer ist rootoder sudowas Ihr Skript im Allgemeinen wissen muss.
Prahlad Yeri
0

Es hängt alles davon ab, wie portabel Ihre App sein soll. Wenn Sie geschäftlich meinen, müssen wir davon ausgehen, dass das Administratorkonto nicht immer gleich 0 ist. Dies bedeutet, dass die Überprüfung auf euid 0 nicht ausreicht. Das Problem ist, dass es Situationen gibt, in denen sich ein Befehl so verhält, als wären Sie root, und der nächste mit fehlgeschlagener Berechtigung fehlschlägt (denken Sie an SELinux & Co.). Daher ist es wirklich besser, ordnungsgemäß zu versagen und nach EPERM-Fehlern zu suchen, wann immer dies angemessen ist.

Stan
quelle