2010-05-10 18 views
45

Tengo un script de Python que hará muchas cosas que requerirían privilegios de nivel de raíz, como mover archivos en/etc, instalar con apt-get, y más. Actualmente tengo:¿Cuál es la mejor manera de verificar si el usuario de un script tiene privilegios de root?

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

¿Es esta la mejor manera de hacer el cheque? ¿Hay otras mejores prácticas?

Respuesta

26

Bajo el principio "más fácil pedir perdón que pedir permiso":

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

Si usted está preocupado! la no portabilidad de os.geteuid() es probable que no se deba denegar con /etc de todos modos.

+0

Esta es la forma más portátil e incluso funciona en circunstancias un poco más exóticas, como con privilegios de raíz de limitación de SELinux. –

+3

Esto en realidad puede ser peor dependiendo de la situación. Hmm, pero Florian tiene un punto. –

+0

@Longpoke: ¿se refiere a la situación en la que un usuario puede haber cambiado el nombre de los permisos en/etc pero, por ejemplo, no tiene la capacidad de eliminar un archivo de esa ruta? Si eso es lo que quieres decir, entonces tratar de determinar qué capacidades tiene el usuario de antemano es una misión tonta. Es mejor estructurar el código para la semántica "transaccional", donde A y B pueden retrotraerse si C falla. – msw

48

os.geteuid obtiene la identificación de usuario efectiva, que es exactamente lo que quiere, así que no puedo pensar en ninguna otra forma mejor de realizar dicha comprobación. El único bit que es incierto es ese "root-like" en el título: su código comprueba exactamenteroot, no hay "me gusta" al respecto, y de hecho no sabría lo que significaría "raíz pero no raíz" - Por lo tanto, si usted quiere decir algo diferente a "exactamente la raíz", tal vez se puede aclarar, gracias

+2

Estoy asumiendo Pablo está preocupado por los diferentes sistemas que utilizan diferentes fluidos para los administradores. Normalmente id (root) == 0, pero no es obligatorio y en algunos sistemas no es realmente igual. – Stan

+0

@Stan: entonces EAFP sería mejor – msw

+8

La suposición de que el UID efectivo == 0 significa "raíz" está profundamente arraigado en el código UNIX (incluso en la mayoría de las fuentes de kernel similares a UNIX). Técnicamente en Linux esa suposición NO es necesariamente correcta. El modelo de "capacidades" de Linux permite que el sistema se ejecute con controles más detallados delegados a través de la herencia del proceso (envoltorios lcap2) o potencialmente a través de atributos extendidos del sistema de archivos. También las características de SELinux podrían causar estragos en tales suposiciones. Para el 99% de los sistemas disponibles, geteuid() == 0 es suficiente; para el resto, intenta: ... excepto: es tu amigo. –

0

Todo depende de qué tan portátil quieras que sea tu aplicación. Si se refiere a negocios, tenemos que suponer que la cuenta de administrador no siempre es igual a 0. Esto significa que la verificación de euid 0 no es suficiente. El problema es que hay situaciones en las que un comando se comportará como si fuera root y el siguiente fallará con el permiso denegado (piense en SELinux & co.). Por lo tanto, es mejor fracasar con elegancia y verifique si hay EPERM errno cuando sea apropiado.

7

Si realmente quiere que su código sea robusto en una amplia variedad de configuraciones de Linux, le sugiero que considere los casos de esquina donde alguien puede estar utilizando SELinux, o ACL del sistema de archivos, o las características de "capacidades" que han sido en el núcleo de Linux desde la v. 2.2 más o menos. Su proceso podría estar ejecutándose bajo algún envoltorio que haya usado SELinux, o alguna biblioteca de capacidades Linux, como libcap2libcap-ng, o fscaps o elfcap a través de algo más exótico como el maravilloso y lamentablemente poco apreciado sistema systrace de Niels Provos.

Todas estas son formas en que el código podría estar ejecutándose como no raíz y, sin embargo, su proceso podría haber sido delegado el acceso necesario para realizar su trabajo sin EUID == 0.

Así que le sugiero que considere escribir su código más Pythonically, envolviendo las operaciones que pueden fallar debido a permisos u otros problemas con el código de manejo de excepciones. Si realiza bombardeos para realizar diversas operaciones (por ejemplo, utilizando el módulo subprocess), puede ofrecer prefijar todas las llamadas con sudo (como línea de comando, entorno o opción de archivo .rc, por ejemplo). Si se ejecuta de forma interactiva, puede ofrecer volver a ejecutar los comandos que generan excepciones relacionadas con permisos usando sudo (opcionalmente solo si encuentra sudo en os.environ ['RUTA']).

En general, es cierto que la mayoría de los sistemas Linux y UNIX aún tienen la mayor parte de su administración realizada por un usuario con privilegios 'raíz'. Sin embargo, es de la vieja escuela y nosotros, como programadores, deberíamos intentar apoyar modelos más nuevos. Probar sus operaciones y dejar que el manejo de excepciones haga su trabajo permite que su código funcione bajo cualquier sistema que permita de manera transparente las operaciones que necesita, y estar al tanto y listo para usar sudo es un buen toque (ya que es, de lejos, el más herramienta generalizada para la delegación controlada de privilegios del sistema).

3

respuesta a la segunda parte de la pregunta

(lo siento el cuadro de comentarios era demasiado pequeña)

Paul Hoffman, estás en lo correcto, lo único que se dirigió a una parte de su pregunta se trata de los intrínsecos, pero no sería un lenguaje de scripting digno si no pudiera manejar apt-get. La biblioteca preferido es un poco prolijo, pero hace el trabajo:

>>> 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' 

Pero Popen es una herramienta generalizada y se puede envolver por conveniencia:

$ 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 

Y ahora estamos de vuelta a la gestión de excepciones. Rechazaré comentar sobre la generalidad similar a Java del módulo de subproceso.

+1

¿No tendría más sentido editar esto en su otra respuesta? – jpmc26

1

me gusta para comprobar si sudo en las variables ambientales:

 
if not 'SUDO_UID' in os.environ.keys(): 
    print "this program requires super user priv." 
    sys.exit(1) 
5

Usted puede solicitar al usuario el acceso sudo:

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? 

actualizar el interruptor sudo -v credenciales almacenadas en caché del usuario (ver man sudo) .

2

Mi aplicación funciona con este código:

import os 
user = os.getenv("SUDO_USER") 
if user is None: 
    print "This program need 'sudo'" 
    exit() 
+0

Código incorrecto. El error arrojado aquí es 'TypeError', ya que si no está ejecutando como root,' os.getenv ("SUDO_USER") 'devuelve' None' que es de tipo 'NoneType', y no puede concatenar' str' y ' NoneType'. Si desea utilizar 'os.getenv (" SUDO_USER ")' para resolver el problema, debe hacerlo así: 'if os.getenv (" SUDO_USER ") == Ninguno: imprimir" Este programa necesita " sudo '"; exit() ' –

+2

He editado mi respuesta – Guillaume

+1

Gracias por la actualización, este código solo probará el sudo, no el acceso raíz. Como no verifica la raíz, si inicias sesión como root (o escribes un cronjob como yo) aún necesitas llamar al script con sudo, porque sudo no pide contraseña en la raíz, pude arreglar mi cronjob simplemente llamando a mi script python con sudo. – Cediddi

Cuestiones relacionadas