Eliminando permisos de root en Python

Me gustaría que un progtwig de Python comience a escuchar en el puerto 80, pero después de eso, ejecute sin permisos de root. ¿Hay alguna forma de eliminar la raíz o de obtener el puerto 80 sin ella?

No podrá abrir un servidor en el puerto 80 sin privilegios de root, esto es una restricción en el nivel del sistema operativo. Por lo tanto, la única solución es eliminar los privilegios de raíz después de haber abierto el puerto.

Aquí hay una posible solución para eliminar los privilegios de root en Python: Eliminar privilegios en Python . Esta es una buena solución en general, pero también tendrá que agregar os.setgroups([]) a la función para asegurarse de que no se conserve la pertenencia al grupo del usuario raíz.

Copié y limpié el código un poco, y eliminé el registro y los controladores de excepciones, por lo que depende de usted manejar OSError correctamente (se lanzará cuando no se permita que el proceso cambie su UID o GID efectivo):

 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) 

Recomiendo usar authbind para iniciar su progtwig Python, por lo que ninguno de ellos tiene que ejecutarse como root.

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

No es una buena idea pedirle al usuario que ingrese su nombre de usuario y grupo cuando necesito quitar los privilegios. Aquí hay una versión ligeramente modificada del código de Tamás que eliminará los privilegios y cambiará al usuario que inició el comando sudo. Supongo que está utilizando sudo (si no, use el código de 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. systemd puede hacerlo por usted, si inicia su progtwig a través de systemd, systemd puede entregarle el conector de escucha ya abierto, y también puede activar su progtwig en la primera conexión. y ni siquiera necesitas demonizarlo.

  2. Si va a seguir el enfoque independiente, necesita la capacidad CAP_NET_BIND_SERVICE (página de control de capacidades). Esto se puede hacer progtwig por progtwig con la herramienta de línea de comando correcta, o haciendo que su aplicación (1) sea suid root (2) inicie (3) escuche el puerto (4) suelte los privilegios / capacidades inmediatamente .

Recuerde que los progtwigs de suid root vienen con muchas consideraciones de seguridad (entorno limpio y seguro, umask, privilegios, límites, todas esas cosas son cosas que su progtwig tendrá que configurar correctamente). Si puedes usar algo como systemd, mejor entonces.

La mayoría de esto funciona a menos que necesite solicitar el socket después de hacer otras cosas que no desea que sean superusuarios.

Hice un proyecto llamado tradesocket hace un tiempo. Le permite pasar sockets de ida y vuelta en un sistema posix entre procesos. Lo que hago es desconectar un proceso al principio que se mantiene como superusuario, y el rest del proceso cae en los permisos y luego solicita el socket al otro.

La siguiente es una adaptación adicional de la respuesta de Tamás , con los siguientes cambios:

  • Use el módulo python-prctl para eliminar las capacidades de Linux a una lista específica de capacidades para preservar.
  • Opcionalmente, se puede pasar al usuario como parámetro (de manera predeterminada se busca al usuario que ejecutó sudo ).
  • Establece todos los grupos de usuarios y HOME .
  • Opcionalmente cambia de directorio.

(Sin embargo, soy relativamente nuevo en el uso de esta funcionalidad, por lo que es posible que me haya perdido algo. Es posible que no funcione en los kernels anteriores (<3.8) o en los kernels con las capacidades del sistema de archivos deshabilitadas).

 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) 

Se puede utilizar de la siguiente manera:

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