El módulo grp consulta la base de grupos en sistemas Unix. Permite encontrar un grupo por GID numérico o nombre, listar miembros registrados explícitamente y convertir ownership de archivos en nombres legibles.
Los grupos Unix participan en el control de acceso a archivos, dispositivos, servicios y recursos. Sin embargo, la base de grupos no representa toda la autorización: también intervienen el grupo primario, los grupos suplementarios, mode bits, ACLs, capabilities, namespaces, SELinux, AppArmor y opciones de montaje.
Disponibilidad
grp está disponible en Unix, pero no en WASI, Android ni iOS. Windows usa otro modelo.
try:
import grp
except ImportError:
grp = None
En software multiplataforma, aísla este código y conserva el GID numérico cuando el nombre no sea esencial.
Campos de una entrada
Las funciones devuelven un objeto similar a una tupla:
gr_name, gr_passwd, gr_gid, gr_mem
gr_name es el nombre, gr_gid el ID numérico y gr_mem una lista de logins registrados explícitamente. gr_passwd es histórico y normalmente está vacío o no es útil.
Consultar por GID
import grp
try:
grupo = grp.getgrgid(1000)
except KeyError:
print("GID sin entrada")
else:
print(grupo.gr_name, grupo.gr_mem)
Desde Python 3.10, un argumento no entero, como string o float, genera TypeError. Valida entradas de CLI y API.
Consultar por nombre
import grp
try:
grupo = grp.getgrnam("developers")
except KeyError:
print("Grupo inexistente")
else:
print(grupo.gr_gid)
No utilices directamente un nombre no confiable para decisiones privilegiadas sin resolverlo y comprobar las credenciales efectivas del proceso.
Grupo primario y suplementarios
El grupo primario de una cuenta está en pw_gid del módulo pwd en Python. gr_mem contiene usuarios listados explícitamente en la entrada del grupo.
La documentación advierte que muchos usuarios no aparecen en gr_mem de su grupo primario. Por eso esa lista sola es incompleta.
Encontrar todos los grupos de un usuario
import grp
import pwd
def grupos_del_usuario(nombre):
cuenta = pwd.getpwnam(nombre)
gids = {cuenta.pw_gid}
for grupo in grp.getgrall():
if nombre in grupo.gr_mem:
gids.add(grupo.gr_gid)
return [grp.getgrgid(gid) for gid in sorted(gids)]
En sistemas con LDAP o muchos grupos, getgrall() puede ser costoso. Cuando exista, prefiere os.getgrouplist(nombre, gid_primario).
Grupos del proceso actual
os.getgid() devuelve el GID real, os.getegid() el efectivo y os.getgroups() los grupos suplementarios.
import os
print("Real:", os.getgid())
print("Efectivo:", os.getegid())
print("Suplementarios:", os.getgroups())
El kernel normalmente usa el GID efectivo y los grupos suplementarios para decidir permisos.
Convertir ownership de archivos
import grp
import os
info = os.stat("archivo.txt")
try:
nombre_grupo = grp.getgrgid(info.st_gid).gr_name
except KeyError:
nombre_grupo = str(info.st_gid)
print(nombre_grupo)
Mantén el GID numérico como fallback. Los archivos pueden sobrevivir a un grupo eliminado o venir de otro namespace.
gr_passwd no autentica
Las contraseñas de grupo son una característica histórica. gr_passwd suele estar vacío o contener un marcador.
No lo uses para validar credenciales. La autenticación y autorización deben depender de PAM, ACLs o políticas soportadas.
Listar todos los grupos
import grp
for grupo in grp.getgrall():
print(grupo.gr_gid, grupo.gr_name, grupo.gr_mem)
El orden es arbitrario. NSS puede consultar LDAP, SSSD, NIS u otra fuente remota, haciendo la operación lenta.
NSS y proveedores remotos
Al igual que pwd, grp sigue Name Service Switch. Los datos pueden venir de /etc/group, LDAP, SSSD, NIS, containers o plugins.
Una llamada aparentemente local puede bloquear por red. No ejecutes getgrall() en cada request web.
Referencias NIS
Nombres que comienzan con + o - pueden ser referencias YP/NIS y no siempre están disponibles mediante getgrnam() o getgrgid().
Cache con expiración
Consultas frecuentes pueden utilizar cache de corta duración.
import time
_cache = {}
def grupo_por_gid(gid, ttl=60):
ahora = time.monotonic()
item = _cache.get(gid)
if item and ahora - item[0] < ttl:
return item[1]
valor = grp.getgrgid(gid)
_cache[gid] = (ahora, valor)
return valor
Usa reloj monotónico y no caches errores para siempre.
Containers y namespaces
En un container, un GID puede no tener entrada en /etc/group. Kubernetes también puede añadir grupos suplementarios sin nombres.
No falles al mostrar ownership: conserva el número. Para autorización, utiliza las credenciales efectivas del kernel.
Volúmenes compartidos
El mismo GID puede tener nombres distintos en hosts diferentes. En NFS o volúmenes compartidos, la consistencia numérica es más importante que el nombre.
Comprobar membresía del proceso
import os
def proceso_en_grupo(gid):
return gid == os.getegid() or gid in os.getgroups()
Esta comprobación responde sobre el proceso actual y suele ser más útil que reconstruir membresía desde archivos.
Deja que el kernel decida
Aunque el proceso pertenezca al grupo, el acceso puede fallar por ACL, mode bits, SELinux, AppArmor, filesystem read-only o mount options.
Intenta la operación y maneja PermissionError. La consulta sirve para diagnóstico, no sustituye la syscall.
Cambiar el grupo de un archivo
import grp
import os
objetivo = grp.getgrnam("developers")
os.chown("archivo.txt", -1, objetivo.gr_gid)
La operación necesita permisos. Valida rutas y evita seguir symlinks no confiables.
Cambiar el grupo efectivo
Procesos privilegiados pueden usar os.setgid() u os.setegid(). Son operaciones sensibles y pueden ser irreversibles.
Es preferible que el supervisor inicie el servicio con el usuario y grupo correctos.
initgroups()
os.initgroups(usuario, gid_primario) inicializa grupos suplementarios según la configuración del sistema.
import os
import pwd
cuenta = pwd.getpwnam("appuser")
os.initgroups(cuenta.pw_name, cuenta.pw_gid)
os.setgid(cuenta.pw_gid)
os.setuid(cuenta.pw_uid)
Llama initgroups() antes de perder privilegios. Un orden incorrecto puede conservar grupos privilegiados o impedir la configuración.
Limpiar grupos heredados
Cambiar solo UID y GID primario puede dejar grupos suplementarios heredados. Usa initgroups() o os.setgroups([]) según la política.
Race conditions
La membresía puede cambiar entre consulta y uso. Para seguridad, confía en el resultado de la operación real y maneja el fallo.
Privacidad
Las listas de miembros revelan nombres y estructura organizacional. No expongas getgrall() en APIs o logs sin necesidad.
Logs
Al registrar errores con syslog en Python, usa GID numérico y nombre sanitizado. No incluyas todos los miembros.
Aislamiento
Usuarios y grupos sin privilegios complementan los límites de resource en Python, pero no forman una sandbox completa.
Pruebas
Prueba grupo existente y ausente, argumentos no enteros, usuario cuyo grupo primario no lo lista en gr_mem, grupos suplementarios, container sin nombres, LDAP lento, NIS, volumen compartido, permisos reales y reducción de privilegios.
Los tests unitarios no deben depender de la base real del host. Usa wrappers y registros fake.
Errores comunes
Los fallos frecuentes son tratar gr_mem como lista completa, usar gr_passwd para autenticar, asumir nombre para todo GID, llamar getgrall() en hot path, olvidar grupos suplementarios y creer que la membresía garantiza acceso.
Conclusión
grp resuelve grupos Unix por nombre o GID y ayuda a interpretar ownership y membresía. Combínalo con pwd y los grupos efectivos del proceso.
Conserva IDs numéricos, considera NSS y containers, no uses la base para autenticar y deja que el kernel decida el acceso final. Consulta la documentación oficial de grp y group(5).







