O módulo grp consulta a base de grupos em sistemas Unix. Ele permite encontrar um grupo pelo GID numérico ou nome, listar membros explicitamente registrados e converter ownership de arquivos para nomes legíveis.
Grupos Unix participam do controle de acesso a arquivos, dispositivos, serviços e recursos do sistema. Porém, a base de grupos não é uma autorização completa: permissões efetivas também dependem do usuário, grupo primário, grupos suplementares, ACLs, capabilities, namespaces, SELinux, AppArmor e outras políticas.
Disponibilidade
grp está disponível em Unix, mas não em WASI, Android nem iOS. Windows usa outro modelo de grupos.
try:
import grp
except ImportError:
grp = None
Aplicações multiplataforma devem isolar esse código e oferecer fallback quando o nome do grupo não for essencial.
Campos de uma entrada
As funções retornam um objeto parecido com tupla:
gr_name, gr_passwd, gr_gid, gr_mem
gr_name é o nome, gr_gid o ID numérico e gr_mem uma lista de logins explicitamente associados. gr_passwd é histórico e normalmente vazio ou inútil.
Consultar pelo GID
import grp
try:
grupo = grp.getgrgid(1000)
except KeyError:
print("GID sem entrada")
else:
print(grupo.gr_name, grupo.gr_mem)
Desde Python 3.10, argumentos não inteiros, como string ou float, geram TypeError. Valide e converta entradas de CLI ou API antes da chamada.
Consultar pelo nome
import grp
try:
grupo = grp.getgrnam("developers")
except KeyError:
print("Grupo inexistente")
else:
print(grupo.gr_gid)
Não use um nome fornecido pelo usuário para tomar decisões privilegiadas sem consultar a base e verificar a identidade efetiva do processo.
Grupo primário e grupos suplementares
O grupo primário de uma conta está em pw_gid no módulo pwd no Python. Já gr_mem contém membros explicitamente listados na base de grupos.
A documentação alerta que muitos usuários não aparecem em gr_mem do próprio grupo primário. Portanto, pesquisar somente essa lista produz uma visão incompleta.
Encontrar todos os grupos de um usuário
import grp
import pwd
def grupos_do_usuario(nome):
conta = pwd.getpwnam(nome)
gids = {conta.pw_gid}
for grupo in grp.getgrall():
if nome in grupo.gr_mem:
gids.add(grupo.gr_gid)
return [grp.getgrgid(gid) for gid in sorted(gids)]
Em sistemas com LDAP ou muitos grupos, getgrall() pode ser caro. Quando disponível, prefira os.getgrouplist(nome, gid_primario), que consulta o sistema de forma mais direta.
Grupos do processo atual
os.getgid() retorna o GID real, os.getegid() o efetivo e os.getgroups() a lista suplementar.
import os
print("Real:", os.getgid())
print("Efetivo:", os.getegid())
print("Suplementares:", os.getgroups())
As permissões de uma operação normalmente consideram o GID efetivo e os grupos suplementares do processo, não apenas a base estática.
Converter ownership de arquivos
import grp
import os
info = os.stat("arquivo.txt")
try:
nome_grupo = grp.getgrgid(info.st_gid).gr_name
except KeyError:
nome_grupo = str(info.st_gid)
print(nome_grupo)
Mantenha o GID numérico como fallback. Arquivos podem pertencer a grupos removidos, namespaces diferentes ou volumes externos.
gr_passwd não autentica
Senhas de grupo são um recurso histórico e raramente usado. gr_passwd costuma estar vazio, conter x ou outro marcador.
Não tente validar credenciais com esse campo. Autenticação e autorização devem usar PAM, políticas do serviço, ACLs ou mecanismos oficiais.
Listar grupos
import grp
for grupo in grp.getgrall():
print(grupo.gr_gid, grupo.gr_name, grupo.gr_mem)
A ordem é arbitrária. A operação pode consultar NSS, LDAP, SSSD, NIS ou outras fontes remotas e ser lenta.
NSS e provedores remotos
Assim como pwd, grp segue a configuração Name Service Switch. A entrada pode vir de /etc/group, LDAP, SSSD, NIS ou plugins.
Uma chamada aparentemente local pode bloquear em rede. Evite executar getgrall() em cada requisição web ou operação de arquivo.
Referências NIS
Nomes iniciados com + ou - podem representar referências YP/NIS e nem sempre são acessíveis por getgrnam() ou getgrgid().
Não trate essas entradas como grupos locais comuns sem conhecer a configuração da máquina.
Cache com expiração
Consultas frequentes podem usar cache, mas mudanças de grupo precisam aparecer dentro de um prazo razoável.
import time
_cache = {}
def grupo_por_gid(gid, ttl=60):
agora = time.monotonic()
item = _cache.get(gid)
if item and agora - item[0] < ttl:
return item[1]
valor = grp.getgrgid(gid)
_cache[gid] = (agora, valor)
return valor
Use relógio monotônico para TTL. Não armazene falhas indefinidamente.
Containers e namespaces
Em containers, um GID pode não ter entrada em /etc/group. Kubernetes e outras plataformas também podem adicionar grupos suplementares sem criar nomes correspondentes.
Não falhe ao exibir ownership: mostre o GID numérico. Para autorização, use os IDs efetivos fornecidos pelo kernel.
Grupos em volumes compartilhados
O mesmo GID pode representar nomes diferentes em hosts distintos. Em NFS ou volumes compartilhados, a consistência numérica entre sistemas é mais importante que o nome.
Planeje UID/GID entre hosts, containers e serviços antes de depender de permissões POSIX.
Verificar membresia
import os
def processo_esta_no_grupo(gid):
return gid == os.getegid() or gid in os.getgroups()
Essa função responde sobre o processo atual. Ela é mais relevante para uma operação imediata do que reconstruir a membresia a partir de arquivos.
Não antecipe a decisão do kernel
Mesmo estando em um grupo, o acesso pode falhar por mode bits, ACL, mount options, SELinux, AppArmor, read-only filesystem ou capabilities.
Tente a operação e trate PermissionError. A consulta de grupo ajuda no diagnóstico, não substitui a syscall.
Alterar grupo de arquivo
import grp
import os
alvo = grp.getgrnam("developers")
os.chown("arquivo.txt", -1, alvo.gr_gid)
A operação exige permissão. Valide o caminho e evite seguir symlinks não confiáveis. Em código sensível, use APIs com dir_fd e flags apropriadas.
Alterar o grupo efetivo
Processos privilegiados podem usar os.setgid() ou os.setegid(). Essas operações são sensíveis e podem ser irreversíveis.
Prefira que o supervisor inicie o serviço com o usuário e grupo corretos, em vez de fazer mudanças dentro da aplicação.
initgroups()
os.initgroups(usuario, gid_primario) inicializa a lista suplementar com base na configuração do sistema.
import os
import pwd
conta = pwd.getpwnam("appuser")
os.initgroups(conta.pw_name, conta.pw_gid)
os.setgid(conta.pw_gid)
os.setuid(conta.pw_uid)
Chame initgroups() antes de perder privilégios. A ordem errada pode impedir a configuração ou manter grupos privilegiados.
Limpar grupos suplementares
Ao reduzir privilégios, não basta trocar UID e GID primário. Grupos suplementares herdados podem manter acesso indevido.
Use initgroups() para a conta alvo ou os.setgroups([]) quando a política exigir ausência de grupos adicionais.
Race conditions
A membresia pode mudar entre consulta e operação. Para segurança, confie na autorização da syscall e trate o resultado.
Não implemente “check then use” como única proteção.
Privacidade
Listas de membros revelam estrutura organizacional e nomes de contas. Não exponha getgrall() em APIs ou logs sem necessidade.
Logs
Ao registrar falhas em syslog no Python, prefira GID numérico e nome sanitizado. Não despeje a lista inteira de membros.
Limites de recursos
Grupos e contas sem privilégio complementam as cotas de resource no Python, mas nenhuma dessas medidas forma uma sandbox completa.
Testes
Teste grupo existente e ausente, argumento não inteiro, usuário no grupo primário mas fora de gr_mem, grupos suplementares, container sem nomes, LDAP lento, NIS, volume compartilhado, permissões reais e drop de privilégios.
Testes unitários não devem depender da base real do host. Encapsule chamadas e use registros fake.
Erros comuns
Os erros mais frequentes são tratar gr_mem como lista completa, usar gr_passwd para autenticação, presumir que nome existe para todo GID, chamar getgrall() em hot path, esquecer grupos suplementares ao reduzir privilégios e considerar membresia suficiente para garantir acesso.
Conclusão
grp resolve grupos Unix por nome ou GID e ajuda a interpretar ownership e membresia. Combine-o com pwd e com os grupos efetivos do processo para obter uma visão correta.
Preserve IDs numéricos, trate NSS e containers, não use a base para autenticação e deixe o kernel decidir o acesso final. Consulte a documentação oficial de grp e o manual group(5).







