2010-04-23 20 views
40

Mi piacerebbe avere un programma Python che inizi ad ascoltare sulla porta 80, ma successivamente eseguirlo senza i permessi di root. C'è un modo per eliminare root o ottenere la porta 80 senza di essa?Eliminazione dei permessi di root 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

Su Linux moderno è necessaria solo la funzionalità CAP_NET_ BIND_SERVICE per collegarsi alla porta 80, NON è necessario essere root, nemmeno all'avvio dell'applicazione. Capabilities è uno standard POSIX, 1003.1e, ovvero un partizionamento del potente privilegio di root in un insieme di privilegi distinti. See: python-cap-ng E/sbin/setcap,/sbin/getcap (questi sono equivalenti a chmod setuid, e ls -l) –

+0

Per python2 e forse altri interpreti, guadagnando funzionalità è la parte che si vuoi stare attento con - libcap-ng può eliminare i cap ma non li concede. Questa risposta alla domanda Ian di riferimento è un modo relativamente sicuro di distribuire un limite alla volta per progetti specifici: http://stackoverflow.com/a/21895123/1724577 – duanev

risposta

50

Non sarà possibile aprire un server sulla porta 80 senza privilegi di root, questa è una restrizione a livello di sistema operativo. Quindi l'unica soluzione è quella di eliminare i privilegi di root dopo aver aperto la porta.

Ecco una possibile soluzione per eliminare i privilegi di root in Python: Dropping privileges in Python. Questa è una buona soluzione in generale, ma dovrai anche aggiungere os.setgroups([]) alla funzione per assicurarti che l'appartenenza al gruppo dell'utente root non venga mantenuta.

Ho copiato e ripulito il codice un po 'e rimosso la registrazione e i gestori di eccezioni in modo che sia lasciato a voi per gestire correttamente OSError (verrà generato quando il processo non è autorizzato a cambiare il suo UID effettivo o 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

Ricorda che la directory HOME sarà ancora '/ root' e non '/ home/uid_name', e che uid_name non sarà in grado di fare nulla con' ~/', che verrà quindi espanso in'/root/'. Questo può influire su moduli come matplotlib che memorizzano i dati di configurazione nella directory HOME. 'authbind' sembra essere il modo corretto per gestire questo problema. –

+1

E la variabile HOME non sarà l'unica a pensare che l'utente corrente sia root. –

+1

* "Non sarà possibile aprire un server sulla porta 80 senza privilegi di root ..." * - Questo non è necessariamente vero (forse più?). Vedi anche [Permetti al processo non-root di collegarsi alla porta 80 e 443?] (Https://superuser.com/q/710253/173513) su SuperUser e [C'è un modo per i processi non-root di collegarsi a "privilegiati "Porte su Linux?] (Https://stackoverflow.com/q/413807/608639) – jww

12

mi consiglia di utilizzare authbind per iniziare il vostro programma Python, quindi nessuno di esso deve essere eseguito come root.

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

+0

Un esempio di come usare' authbind' - http://goo.gl/fxFde6 - Sostituisci NodeJS con quello che vuoi (ex : python) – starlocke

3
  1. systemd può farlo per voi, se si avvia il programma attraverso systemd, systemd può mano fuori il socket di ascolto già aperto ad esso, e può anche attivare il programma sul primo collegamento . e non hai nemmeno bisogno di demonizzarlo.

  2. Se si intende adottare l'approccio standalone, è necessario disporre della funzionalità CAP_NET_BIND_SERVICE (consultare la pagina man di funzionalità). Questo può essere fatto programma per programma con lo strumento della riga di comando corretto, o facendo in modo che l'applicazione (1) sia suid root (2) start up (3) ascolta i privilegi/le capacità di rilascio della porta (4) immediatamente .

Ricordate che con privilegi di root programmi sono dotati di un sacco di considerazioni di sicurezza (ambiente pulito e sicuro, umask, privilegi, rlimits, tutte quelle cose sono cose che il programma sta andando ad avere per impostare correttamente). Se puoi usare qualcosa come systemd, tanto meglio allora.

2

La maggior parte di questo funziona, a meno che non sia necessario richiedere la presa dopo aver fatto altre cose che non si desidera essere superutente.

Ho creato un progetto chiamato tradesocket qualche tempo fa. Permette di passare avanti e indietro i socket su un sistema POSIX tra i processi. Quello che faccio è far partire un processo all'inizio che rimane superutente, e il resto del processo cade giù nelle autorizzazioni e quindi richiede il socket dall'altro.

7

Non è una buona idea chiedere all'utente di inserire il proprio nome utente e il gruppo ogni volta che ho bisogno di rilasciare i privilegi. Ecco una versione leggermente modificata del codice di Tamás che lascerà cadere i privilegi e passerà all'utente che ha avviato il comando sudo. Suppongo che tu stia usando sudo (se no, usa il codice di 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

Il seguente è un ulteriore adattamento di Tamás's answer, con le seguenti modifiche:

  • Utilizzare il python-prctl module a goccia le capacità di Linux a un elenco specifico di funzionalità per preservare.
  • L'utente può facoltativamente essere passato come parametro (per impostazione predefinita, cerca l'utente che ha eseguito sudo).
  • Imposta tutti i gruppi dell'utente e HOME.
  • Opzionalmente cambia directory.

(io sono relativamente nuovo per utilizzare questa funzionalità, tuttavia, così io possa aver perso qualcosa. Potrebbe non funzionare su vecchi kernel (< 3.8) o kernel con capacità di filesystem disabilitati.)

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) 

può essere utilizzato come segue:

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