Python grp: Query Unix Groups

Published on: August 26, 2026
Reading time: 5 minutes
Close-up view of a computer screen displaying code in a software development environment.

The grp module queries the Unix group database. It can find a group by numeric GID or name, list explicitly registered members, and translate file ownership into readable group names.

Unix groups participate in access control for files, devices, services, and system resources. However, the group database is not a complete authorization model. Effective permissions also depend on the user’s primary group, supplementary groups, mode bits, ACLs, capabilities, namespaces, SELinux, AppArmor, mount options, and other policies.

Availability

grp is available on Unix but not WASI, Android, or iOS. Windows uses a different group model.

try:
    import grp
except ImportError:
    grp = None

Cross-platform applications should isolate this code and retain a numeric fallback when a group name is not essential.

Group entry fields

Functions return a tuple-like object with these attributes:

gr_name, gr_passwd, gr_gid, gr_mem

gr_name is the name, gr_gid the numeric ID, and gr_mem a list of explicitly listed login names. gr_passwd is historical and normally empty or unusable.

Query by GID

import grp

try:
    group = grp.getgrgid(1000)
except KeyError:
    print("No group entry for this GID")
else:
    print(group.gr_name, group.gr_mem)

Since Python 3.10, a non-integer argument such as a string or float raises TypeError. Validate CLI and API input before calling the function.

Query by name

import grp

try:
    group = grp.getgrnam("developers")
except KeyError:
    print("Unknown group")
else:
    print(group.gr_gid)

Do not use an untrusted group name to make a privileged decision without resolving it and checking the process’s actual credentials.

Primary and supplementary groups

A user’s primary group is stored as pw_gid in Python pwd. The gr_mem list contains users explicitly associated with the group entry.

The official documentation warns that users are often not listed in gr_mem for their primary group. Looking only at that list therefore gives an incomplete result.

Find every group for a user

import grp
import pwd


def groups_for_user(name):
    account = pwd.getpwnam(name)
    gids = {account.pw_gid}

    for group in grp.getgrall():
        if name in group.gr_mem:
            gids.add(group.gr_gid)

    return [grp.getgrgid(gid) for gid in sorted(gids)]

On LDAP-connected systems or machines with many groups, getgrall() can be expensive. When available, prefer os.getgrouplist(name, primary_gid).

Groups of the current process

os.getgid() returns the real GID, os.getegid() the effective GID, and os.getgroups() supplementary GIDs.

import os

print("Real:", os.getgid())
print("Effective:", os.getegid())
print("Supplementary:", os.getgroups())

The kernel normally checks the effective GID and supplementary group list rather than reconstructing membership from static files.

Translate file ownership

import grp
import os

info = os.stat("file.txt")
try:
    group_name = grp.getgrgid(info.st_gid).gr_name
except KeyError:
    group_name = str(info.st_gid)

print(group_name)

Keep the numeric GID as a fallback. Files can outlive a group or come from another namespace or host.

gr_passwd is not authentication

Group passwords are a historical feature and are rarely used. gr_passwd is normally empty or contains a marker.

Do not validate credentials with it. Authentication and authorization should use PAM, service policy, ACLs, or another supported mechanism.

List all groups

import grp

for group in grp.getgrall():
    print(group.gr_gid, group.gr_name, group.gr_mem)

The order is arbitrary. NSS may consult LDAP, SSSD, NIS, or another remote source, making this operation slow and potentially large.

NSS and remote identity providers

Like pwd, grp follows the Name Service Switch configuration. Entries may come from /etc/group, LDAP, SSSD, NIS, container files, or plugins.

A seemingly local call can block on network I/O. Avoid getgrall() inside every web request or file operation.

NIS references

Group names beginning with + or - may be YP/NIS references and may not be individually accessible through getgrnam() or getgrgid().

Do not treat them as ordinary local groups without understanding host configuration.

Cache with expiration

Frequent lookups may use a short-lived cache.

import time

_cache = {}

def group_by_gid(gid, ttl=60):
    now = time.monotonic()
    item = _cache.get(gid)
    if item and now - item[0] < ttl:
        return item[1]

    value = grp.getgrgid(gid)
    _cache[gid] = (now, value)
    return value

Use a monotonic clock for TTL and do not cache failures forever.

Containers and namespaces

Inside a container, a GID may have no entry in /etc/group. Kubernetes and other platforms may also inject supplementary groups without creating names.

Do not fail when displaying ownership. Show the numeric GID. For authorization, use the credentials actually applied by the kernel.

Shared volumes

The same GID can map to different names on different hosts. For NFS and shared volumes, consistent numeric IDs matter more than display names.

Plan UID and GID allocation across hosts, containers, and services before relying on POSIX permissions.

Check current-process membership

import os


def process_in_group(gid):
    return gid == os.getegid() or gid in os.getgroups()

This answers a question about the current process and is more relevant to an immediate operation than rebuilding membership from database entries.

Let the kernel make the final decision

Even a process in a group can be denied by mode bits, ACLs, mount options, SELinux, AppArmor, read-only filesystems, or capabilities.

Attempt the operation and handle PermissionError. Group queries help diagnosis but do not replace the system call.

Change a file’s group

import grp
import os

target = grp.getgrnam("developers")
os.chown("file.txt", -1, target.gr_gid)

This requires permission. Validate paths and avoid following untrusted symbolic links. Security-sensitive code should use directory-relative APIs and appropriate flags.

Change the effective group

Privileged processes can use os.setgid() or os.setegid(). These operations are security-sensitive and may be irreversible.

Prefer having the service manager start the application with the correct user and group.

initgroups()

os.initgroups(user, primary_gid) initializes supplementary groups according to system policy.

import os
import pwd

account = pwd.getpwnam("appuser")
os.initgroups(account.pw_name, account.pw_gid)
os.setgid(account.pw_gid)
os.setuid(account.pw_uid)

Call initgroups() before dropping privileges. The wrong order can prevent configuration or retain privileged groups.

Clear inherited supplementary groups

Changing only UID and primary GID may leave inherited supplementary groups that retain access.

Use initgroups() for the target account or os.setgroups([]) when the policy requires no additional groups.

Race conditions

Membership can change between a lookup and an operation. For security, rely on the system call’s authorization result and handle failure.

Do not use a check-then-use sequence as the only protection.

Privacy

Membership lists expose account names and organizational structure. Do not publish getgrall() through an API or dump it into logs without authorization and a clear need.

Logging

When reporting access failures through Python syslog, prefer numeric GID and a sanitized name. Avoid logging the full member list.

Resource isolation

Unprivileged users and groups complement the quotas described in Python resource, but neither measure forms a complete sandbox.

Testing

Test existing and missing groups, non-integer arguments, a user whose primary group omits them from gr_mem, supplementary groups, containers without names, slow LDAP, NIS, shared volumes, actual permission checks, and privilege dropping.

Unit tests should not depend on the host’s real group database. Wrap lookups and provide fake records.

Common mistakes

Common failures include treating gr_mem as complete, using gr_passwd for authentication, assuming every GID has a name, calling getgrall() in a hot path, forgetting supplementary groups while dropping privileges, and assuming membership guarantees access.

Conclusion

grp resolves Unix groups by name or GID and helps interpret ownership and membership. Combine it with pwd and the process’s effective group list for an accurate view.

Preserve numeric IDs, account for NSS and containers, never use the database for authentication, and let the kernel decide final access. Consult the official grp documentation and group(5).

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Server rack representing connection load balancing with Python SO_REUSEPORT_LB
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: Balance Connections Across Workers

    Learn Python SO_REUSEPORT_LB to distribute connections across multiple workers with safe testing, portability, and graceful shutdown.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    Python asynchronous code on a laptop for inspect.markcoroutinefunction
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: Detect Async Wrappers

    Learn inspect.markcoroutinefunction in Python to identify async wrappers, integrate frameworks, and avoid incorrect coroutine detection.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Python code for traversing folders and files with Path.walk
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: Traverse Directories Safely

    Learn Python Path.walk to traverse directories, filter files, handle errors, and control directory-tree processing safely.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Python process debugging in a terminal with source code
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: Debug Running Python Processes

    Learn how to attach pdb to a running Python process, inspect stacks, and diagnose hangs safely.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Python programming code for exact fractions
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: Convert Numbers to Fractions

    Learn Python fractions.from_number for exact rational conversion, float and Decimal handling, approximation, validation, and safe arithmetic.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026
    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026