2010-04-23 16 views
40

Ich möchte, dass ein Python-Programm auf Port 80 hört, aber danach ohne root-Berechtigungen ausgeführt wird. Gibt es eine Möglichkeit, Root zu löschen oder ohne Port 80 zu bekommen?Löschen von Stammberechtigungen in Python

+3

http://stackoverflow.com/questions/413807/is-there-a-way-for-non-root-processes-to-bind-to-privileged -ports-1024-on-li –

+4

Auf modernen Linux brauchen Sie nur die Fähigkeit CAP_NET_ BIND_SERVICE, um an Port 80 zu binden, müssen Sie nicht root sein, nicht einmal beim Start der Anwendung. Capabilities ist ein POSIX-Standard, 1003.1e, der eine Partitionierung des allmächtigen Root-Privilegs in eine Reihe unterschiedlicher Privilegien darstellt. Siehe auch: Python-cap-ng und/sbin/setcap,/sbin/getcap (diese sind äquivalent zu chmod setuid und ls -l) –

+0

Für Python2 und vielleicht auch andere Interpreten, Fähigkeiten gewinnt, ist der Teil Sie will vorsichtig sein mit - libcap-ng kann Caps löschen, aber es gewährt ihnen nicht. Diese Antwort auf die Frage, auf die Ian Bezug nimmt, ist eine relativ sichere Methode, um für bestimmte Projekte jeweils eine Kappe zu verteilen: http://stackoverflow.com/a/21895123/1724577 – duanev

Antwort

50

Sie können keinen Server auf Port 80 ohne Root-Rechte öffnen, dies ist eine Einschränkung auf Betriebssystemebene. Die einzige Lösung besteht also darin, root-Berechtigungen nach dem Öffnen des Ports zu löschen.

Hier ist eine mögliche Lösung zum Löschen von Root-Berechtigungen in Python: Dropping privileges in Python. Dies ist im Allgemeinen eine gute Lösung, aber Sie müssen der Funktion auch os.setgroups([]) hinzufügen, um sicherzustellen, dass die Gruppenmitgliedschaft des Root-Benutzers nicht beibehalten wird.

Ich habe den Code ein wenig kopiert und aufgeräumt und die Protokollierung und die Ausnahmehandler entfernt, so dass es Ihnen überlassen ist, OSError richtig zu behandeln (es wird ausgelöst, wenn der Prozess nicht seine effektive UID oder wechseln darf) GID):

import os, pwd, grp 

def drop_privileges(uid_name='nobody', gid_name='nogroup'): 
    if os.getuid() != 0: 
     # We're not root so, like, whatever dude 
     return 

    # Get the uid/gid from the name 
    running_uid = pwd.getpwnam(uid_name).pw_uid 
    running_gid = grp.getgrnam(gid_name).gr_gid 

    # Remove group privileges 
    os.setgroups([]) 

    # Try setting the new uid/gid 
    os.setgid(running_gid) 
    os.setuid(running_uid) 

    # Ensure a very conservative umask 
    old_umask = os.umask(077) 
+1

Beachten Sie, dass das HOME-Verzeichnis immer noch '/ root 'und nicht'/home/uid_name', und dass uid_name nichts mit '~ /' machen kann, was dann zu '/ root /' expandiert. Dies kann sich auf Module wie matplotlib auswirken, die Konfigurationsdaten im HOME-Verzeichnis speichern. 'authbind' scheint der richtige Weg zu sein, um dieses Problem zu lösen. –

+1

Und die HOME-Variable wird nicht die einzige sein, die immer noch denkt, dass der aktuelle Benutzer root ist. –

+1

* "Sie können keinen Server auf Port 80 ohne Root-Rechte öffnen ..." * - Das stimmt nicht unbedingt (vielleicht nicht mehr?). Siehe auch [Nicht-root-Prozess an Port 80 und 443 binden?] (Https://superuser.com/q/710253/173513) auf SuperUser und [Gibt es eine Möglichkeit für Nicht-Root-Prozesse, an "privilegiert" zu binden "Ports on Linux?" (Https://stackoverflow.com/q/413807/608639) – jww

12

ich empfehle authbind mit Ihrem Python-Programm zu starten, so dass keiner davon hat als root ausführen.

https://en.wikipedia.org/wiki/Authbind

+0

Ein Beispiel für die Verwendung von 'authbind' - http://goo.gl/fxFde6 - Ersetzen Sie NodeJS mit allem, was Sie wollen (z. B. : Python) – starlocke

3
  1. systemd kann es für Sie tun, wenn Sie Ihr Programm durch systemd starten, systemd können die bereits geöffneten Socket ihm die Hand ab, und es kann Ihr Programm bei der ersten Verbindung auch aktivieren . und du musst es nicht einmal dämonisieren.

  2. Wenn Sie mit dem Standalone-Ansatz gehen, benötigen Sie die Fähigkeit CAP_NET_BIND_SERVICE (überprüfen Sie die Fähigkeiten-Manpage). Dies kann auf einer Programm-für-Programm-Basis mit dem richtigen Befehlszeilentool durchgeführt werden, oder indem Ihre Anwendung (1) suid root (2) gestartet wird (3) sofort die Port- (4) drop-Rechte/Fähigkeiten abhören .

dass suid root Denken Sie daran, Programme kommen mit vielen Sicherheitsüberlegungen (saubere und sichere Umgebung, umask, Privilegien, rlimits, all diese Dinge sind Dinge, die Ihr Programm haben wird richtig einzurichten). Wenn Sie etwas wie Systemd verwenden können, umso besser.

2

Das meiste funktioniert, es sei denn, Sie müssen den Socket anfordern, nachdem Sie etwas anderes getan haben, das Sie nicht Superuser sein wollen.

Ich habe vor einer Weile ein Projekt namens tradesocket gemacht. Es ermöglicht Ihnen, Sockets auf einem Posix-System zwischen Prozessen hin und her zu leiten. Was ich tue, spinnt einen Prozess am Anfang ab, der Superuser bleibt, und der Rest des Prozesses fällt in Berechtigungen und fordert dann den Sockel vom anderen an.

7

Es ist keine gute Idee, den Benutzer zu bitten, seinen Benutzernamen und seine Gruppe einzugeben, wenn ich Berechtigungen löschen muss. Hier ist eine leicht modifizierte Version von Tamás 'Code, der Privilegien löscht und zu dem Benutzer wechselt, der den sudo-Befehl initiiert hat. Ich gehe davon aus, dass Sie sudo verwenden (wenn nicht, verwenden Sie den Code von Tamás).

#!/usr/bin/env python3 

import os, pwd, grp 

#Throws OSError exception (it will be thrown when the process is not allowed 
#to switch its effective UID or GID): 
def drop_privileges(): 
    if os.getuid() != 0: 
     # We're not root so, like, whatever dude 
     return 

    # Get the uid/gid from the name 
    user_name = os.getenv("SUDO_USER") 
    pwnam = pwd.getpwnam(user_name) 

    # Remove group privileges 
    os.setgroups([]) 

    # Try setting the new uid/gid 
    os.setgid(pwnam.pw_gid) 
    os.setuid(pwnam.pw_uid) 

    #Ensure a reasonable umask 
    old_umask = os.umask(0o22) 


#Test by running... 
#./drop_privileges 
#sudo ./drop_privileges 
if __name__ == '__main__': 
    print(os.getresuid()) 
    drop_privileges() 
    print(os.getresuid()) 
1

Das folgende ist eine weitere Anpassung von Tamás's answer, mit folgenden Änderungen:

  • Verwenden Sie die python-prctl module Linux-Funktionen auf eine bestimmte Liste von Fähigkeiten fallen zu bewahren.
  • Der Benutzer kann optional als Parameter übergeben werden (standardmäßig sucht er nach dem Benutzer, der sudo ausgeführt hat).
  • Es setzt alle Benutzergruppen und HOME.
  • Es ändert optional Verzeichnis.

(ich bin relativ neu um diese Funktionalität zu verwenden, aber so etwas habe ich verpasst haben könnte. Es ist nicht älteren Kernel arbeiten könnte (< 3.8) oder Kernel mit Dateisystem-Funktionen deaktiviert.)

def drop_privileges(user=None, rundir=None, caps=None): 
    import os 
    import pwd 

    if caps: 
     import prctl 

    if os.getuid() != 0: 
     # We're not root 
     raise PermissionError('Run with sudo or as root user') 

    if user is None: 
     user = os.getenv('SUDO_USER') 
     if user is None: 
      raise ValueError('Username not specified') 
    if rundir is None: 
     rundir = os.getcwd() 

    # Get the uid/gid from the name 
    pwnam = pwd.getpwnam(user) 

    if caps: 
     prctl.securebits.keep_caps=True 
     prctl.securebits.no_setuid_fixup=True 

    # Set user's group privileges 
    os.setgroups(os.getgrouplist(pwnam.pw_name, pwnam.pw_gid)) 

    # Try setting the new uid/gid 
    os.setgid(pwnam.pw_gid) 
    os.setuid(pwnam.pw_uid) 

    os.environ['HOME'] = pwnam.pw_dir 

    os.chdir(os.path.expanduser(rundir)) 

    if caps: 
     prctl.capbset.limit(*caps) 
     try: 
      prctl.cap_permitted.limit(*caps) 
     except PermissionError: 
      pass 
     prctl.cap_effective.limit(*caps) 

    #Ensure a reasonable umask 
    old_umask = os.umask(0o22) 

Es kann wie folgt verwendet werden:

drop_privileges(user='www', rundir='~', caps=[prctl.CAP_NET_BIND_SERVICE]) 
Verwandte Themen