| | |

LFCA 22 ๐Ÿง Viewing Users and Groups โ€” /etc/passwd, /etc/group

The previous chapter explained what users and groups are. This chapter is about seeing them. Every identity on a Linux system is recorded in plain text files, and reading those files directly is the fastest way to answer questions like “what UID does this user have?”, “who is in the sudo group?”, “what shell does the web server use?”, or “are there any accounts with UID 0 besides root?” The tools that display this information โ€” cat, grep, awk, getent, id, groups, who, w, last, lastlog โ€” are simple, and they are the ones you will reach for when a permission problem needs diagnosing or a security review needs doing. This chapter covers the format of /etc/passwd, /etc/group, /etc/shadow, and /etc/gshadow, the commands that display them, the query tools that read from the system’s configured identity source rather than the files directly, and the patterns for common inspection tasks. It does not cover creating or modifying users โ€” those are separate chapters.

Key point: /etc/passwd and /etc/group are the canonical files, but they are not always the only source of identity. On systems configured with LDAP, SSSD, or systemd-homed, users may exist in a directory service rather than in the local files. getent queries the system’s full identity source, while cat /etc/passwd reads only the local file. For reliable inspection on any system, getent is preferred. For raw structural inspection, the files themselves are the reference.


The format of /etc/passwd

Each line in /etc/passwd describes one user account and has seven colon-separated fields.

alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash

The fields are, in order: the username, the password placeholder (usually x), the UID, the primary GID, the comment field (GECOS, often the full name), the home directory, and the login shell. The colon is the separator, and no field may contain a colon โ€” that is why the comment field is limited and why paths with colons are unusual.

Why the password field is x. The password hash was moved out of /etc/passwd into /etc/shadow for security, because /etc/passwd must be world-readable and a readable hash is a cracking target. The x is a marker telling the system “the hash is in /etc/shadow, look it up by username.” On some very old or very minimal systems the field still contains the hash โ€” or is empty, meaning no password is required. On a modern Linux system it is x.

Why the fields are position-sensitive. The format is not self-describing. There is no header, no field names, no schema. The order is fixed and every tool that reads the file relies on that order. A malformed line โ€” one with the wrong number of fields or a stray colon โ€” can cause login failures or misidentify the account. This is one reason editing /etc/passwd by hand is discouraged and tools like useradd exist.

Why system accounts live in the same file. There is no separate file for system accounts. They are entries in /etc/passwd with UIDs below 1000 and, typically, a shell of /usr/sbin/nologin or /bin/false. www-data, sshd, systemd-network, and dozens of others appear alongside human accounts. Reading the file means reading both, and the UID range is the way to tell them apart.

Why the home directory and shell can be unusual. For system accounts, the home directory is often / or /nonexistent and the shell is a nologin variant. For human accounts, the home is under /home and the shell is a real shell. The home directory can technically be anywhere; the shell can be any program, including a custom one. The file records whatever the administrator configured.

Why /etc/passwd can be read by anyone. Every process that needs to translate a UID into a name consults this file. ls -l, ps aux, top, htop, and dozens of others display usernames, and they can be run by any user. If the file were restricted, these tools would show numbers instead of names for unprivileged users. Making it world-readable is a deliberate tradeoff: the names and UIDs are not considered secret, only the hashes are.


The format of /etc/group

Each line in /etc/group describes one group and has four colon-separated fields.

developers:x:1001:alice,bob,carol

The fields are: the group name, the password placeholder (usually x), the GID, and the member list โ€” a comma-separated list of usernames with no spaces.

Why the member list does not include everyone. A user’s primary group is recorded in /etc/passwd, not in /etc/group. So a user whose primary group is their own name (the standard on most distributions) does not appear in any member list for that group. The member list holds only the supplementary members. To find all members of a group, you need to check both the member list in /etc/group and the primary GID in /etc/passwd.

Why the group password field exists. Groups can technically have passwords โ€” a way to join a group temporarily with newgrp โ€” but the feature is almost never used and the field is nearly always x or empty. The password hashes, when present, are in /etc/gshadow, which serves the same role for groups that /etc/shadow serves for users.

Why an empty member list is common. Many groups exist only to own files. A group created for a service โ€” docker, www-data, systemd-journal โ€” may have no supplementary members at all. Its purpose is to be the group owner of certain files or to be a primary group for a service account. An empty member list does not mean the group is unused.

Why the GID matters more than the name. Like UIDs, GIDs are the numbers the kernel uses. Two different systems can have groups with the same name but different GIDs, which matters when files are shared across systems. The ls -ln output shows the GID; getent group translates it back to a name.


The shadow files

/etc/shadow holds password hashes and password aging policy for users. /etc/gshadow holds the equivalent for groups. Both are readable only by root.

/etc/shadow format. Nine colon-separated fields: username, password hash, last password change (days since epoch), minimum days before change allowed, maximum days before change required, warning days before expiry, inactivity days after expiry before account is disabled, account expiration date (days since epoch), and a reserved field. The hash is the sensitive part; the aging fields are policy.

alice:$6$rounds=5000$xyz...:19000:0:99999:7:::

The $6$ prefix indicates SHA-512. Other prefixes include $1$ (MD5, obsolete), $5$ (SHA-256), and $y$ (yescrypt, modern default on some distributions). The hash format tells you which algorithm was used, which is useful for auditing โ€” a system still using $1$ hashes should be migrated.

Why the aging fields matter. The last-change, min, max, warn, and inactivity fields enforce password policy. A max of 99999 means the password never expires. A max of 90 means it must be changed every 90 days. The inactivity field disables the account after the password expires and the user fails to change it within the grace period. These fields are set by chage and consulted by the login program.

/etc/gshadow format. Four colon-separated fields: group name, password hash, group administrators, and group members. It is rarely inspected but can reveal which groups have administrators or unusual membership. On most systems it mirrors /etc/group with ! or empty in the password field.

Why shadow files are root-only. A password hash is not the password, but it is close enough. An attacker with a hash can attempt offline cracking โ€” trying billions of candidate passwords against the hash without touching the system. Restricting the file to root removes that opportunity. The check is enforced by file permissions: /etc/shadow is typically 640 with owner root and group shadow, so only root and members of the shadow group can read it.

Why chage -l is the safe way to read shadow data. The chage -l alice command prints the password aging information for a user in a readable format. It requires root for another user’s data but avoids direct parsing of /etc/shadow. chage is the tool designed for the job, and it handles the edge cases of missing or unusual entries.


Reading the files with text tools

The files are plain text, so cat, grep, awk, and cut all work. For quick inspection, they are often faster than a dedicated tool.

# Print the whole file
cat /etc/passwd

# Find a user
grep alice /etc/passwd

# Find a group
grep developers /etc/group

# List just usernames
cut -d: -f1 /etc/passwd

# List UIDs and usernames
awk -F: '{ print $3, $1 }' /etc/passwd

# List system accounts (UID < 1000)
awk -F: '$3 < 1000 { print $1, $3 }' /etc/passwd

# List regular users (UID >= 1000)
awk -F: '$3 >= 1000 && $3 < 65534 { print $1, $3 }' /etc/passwd

# Find accounts with a real shell
grep -v -E '/nologin|/false' /etc/passwd

# Find members of a group
grep sudo /etc/group

Why awk -F: is the standard tool. The -F: sets the field separator to colon, and then $1, $3, and so on refer to the fields. This is the cleanest way to extract specific columns from the identity files. The pattern '$3 < 1000 { print $1 }' reads as “when the third field is less than 1000, print the first field” โ€” a direct expression of the query.

Why cut -d: -f1 is sometimes enough. When you only need one field and no filtering, cut is simpler than awk. cut -d: -f1 /etc/passwd prints the first field of every line, which is the list of usernames. cut cannot filter by value, so for conditional queries awk is the tool.

Why grep -v is useful for finding real users. System accounts have nologin or false as their shell. Excluding those lines leaves the accounts that can actually log in. This is a fast way to answer “how many human users does this system have?” without needing to know the UID convention.

Why direct file reading has limits. On a system configured with LDAP, SSSD, or NIS, /etc/passwd contains only the local accounts. A user who exists in the directory service does not appear in the file, and grep alice /etc/passwd returns nothing even though id alice succeeds. This is the gap that getent fills.

Why the files are the reference but not always the truth. The files are the local identity database. They are authoritative for local accounts. But the system’s view of “who exists” may include remote identities. When inspecting a system, it is worth knowing whether it is standalone or directory-connected. On a standalone system, the files and getent agree. On a connected system, they do not, and getent is the one that shows what the system actually sees.


getent โ€” the query tool

getent reads from the system’s configured name service switch (NSS) sources, not just the files. On a standalone system, it reads /etc/passwd, /etc/group, and the other files. On a connected system, it also queries LDAP, SSSD, or NIS. For any query about identity, getent is the tool that gives the answer the system will act on.

# Look up a user by name
getent passwd alice
# alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash

# Look up a user by UID
getent passwd 1000
# alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash

# List all users the system knows
getent passwd

# Look up a group by name
getent group developers
# developers:x:1001:alice,bob,carol

# Look up a group by GID
getent group 1001
# developers:x:1001:alice,bob,carol

# Look up shadow data (requires root)
sudo getent shadow alice

Why getent is preferred over cat. The two produce the same output on a standalone system. On a directory-connected system, getent includes remote identities and cat does not. When debugging “the user should exist but the system says no,” getent tells you what the system actually resolves. When debugging “the file says the user exists but login fails,” getent reveals whether the name service is returning the entry.

Why the database name is the first argument. getent passwd, getent group, getent shadow, getent hosts, getent services โ€” each queries a different NSS database. The name matches the file name in /etc for the local source, but the database can be backed by anything the name service switch is configured to consult.

Why getent passwd with no argument lists everything. Unlike grep, which reads only the file, getent passwd with no lookup key enumerates every entry the system knows about, across all configured sources. This is the complete list of identities, and it is what a script should iterate over when it needs “all users” rather than “all local users.”

Why getent can be slower than cat. On a system connected to a directory service, getent passwd may issue network requests. On a large directory, the full enumeration can take seconds. For a single lookup by name or UID, getent is fast because the directory is indexed. For a full enumeration, cat /etc/passwd is faster but incomplete.


Viewing the current session

The identity files describe who exists. The session tools describe who is currently logged in and what they are doing.

# Who is logged in
who
# alice    pts/0        2026-03-15 10:22 (192.168.1.10)
# bob      pts/1        2026-03-15 10:45 (192.168.1.11)

# More detail, including idle time and current command
w
# 10:50:12 up 3 days,  2 users,  load average: 0.15, 0.10, 0.05
# USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
# alice    pts/0    192.168.1.10     10:22    0.00s  0.15s  0.02s w
# bob      pts/1    192.168.1.11     10:45    5:00   0.10s  0.05s vim notes.txt

# The current user's identity
id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),1001(developers)

# The current user's name only
whoami
# alice

Why who and w differ. who prints the login sessions โ€” one line per session, with the user, terminal, login time, and source address. w adds the system uptime, load average, and per-user idle time and current command. w is the richer view; who is the minimal one.

Why the FROM column matters. For remote logins, FROM shows the originating IP address or hostname. This is the first thing to check in a security review โ€” an unexpected source address means an unexpected login. For local logins, the column is empty or shows :0 (the local display).

Why id and whoami are still needed. who and w list all sessions. id and whoami answer “who am I right now?” โ€” which is essential when you have used sudo, su, or ssh and need to confirm which identity the current shell carries.


Viewing login history

The files /var/log/wtmp, /var/log/btmp, and /var/log/lastlog record login and logout events. The last, lastb, and lastlog commands read them.

# Recent successful logins
last
# alice    pts/0        192.168.1.10     Sat Mar 15 10:22   still logged in
# bob      pts/1        192.168.1.11     Sat Mar 15 10:45   still logged in
# alice    pts/0        192.168.1.10     Fri Mar 14 09:15 - 17:30  (08:15)

# Recent failed logins (requires root on some systems)
sudo lastb
# root     ssh:notty    203.0.113.5      Sat Mar 15 03:12 - 03:12  (00:00)
# admin    ssh:notty    203.0.113.5      Sat Mar 15 03:13 - 03:13  (00:00)

# Last login for each user
lastlog
# Username         Port     From             Latest
# root                       **Never logged in**
# daemon                     **Never logged in**
# alice            pts/0    192.168.1.10     Sat Mar 15 10:22:14 +0000 2026
# bob              pts/1    192.168.1.11     Sat Mar 15 10:45:01 +0000 2026

Why last is the first tool in an incident review. It shows when each user logged in, from where, and for how long. A login from an unexpected IP address, or at an unexpected time, is a red flag. The “still logged in” marker shows active sessions.

Why lastb requires care. Failed logins are recorded in /var/log/btmp, which is readable only by root on most systems. Repeated failed logins from the same IP address suggest a brute-force attempt. The command to read it is lastb, and it is the mirror of last for failures.

Why lastlog is useful for account auditing. It shows the last login time for every account, including the ones that have never logged in. System accounts with **Never logged in** are expected. A human account that has never logged in, or has not logged in for months, is a candidate for review or removal.

Why these logs rotate. The wtmp, btmp, and lastlog files grow continuously and are rotated by logrotate. The rotation policy determines how far back the history goes. On a busy system, the history may cover only days; on a quiet one, months. The last command reports what is in the current file plus any archived rotations that are still present.

Why the login history is not the full picture. last records logins through the normal login mechanism โ€” login, sshd, su, and similar. It does not record every sudo invocation (that is in /var/log/auth.log or the journal) and it does not record commands run within a session. For a full audit trail, last is the starting point, and the auth log is the next place to look.


Complete Example Session

# ============================================
# PART 1: READ /etc/passwd
# ============================================

head -5 /etc/passwd
# root:x:0:0:root:/root:/bin/bash
# daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
# bin:x:2:2:bin:/bin:/usr/sbin/nologin
# sys:x:3:3:sys:/dev:/usr/sbin/nologin
# sync:x:4:65534:sync:/bin:/bin/sync

grep alice /etc/passwd
# alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash

# ============================================
# PART 2: EXTRACT FIELDS
# ============================================

cut -d: -f1 /etc/passwd | head -5
# root
# daemon
# bin
# sys
# sync

awk -F: '{ print $3, $1 }' /etc/passwd | sort -n | tail -3
# 1000 alice
# 1001 bob
# 1002 carol

# ============================================
# PART 3: FILTER BY UID RANGE
# ============================================

# System accounts
awk -F: '$3 < 1000 { print $1, $3 }' /etc/passwd | head -5
# root 0
# daemon 1
# bin 2
# sys 3
# sync 4

# Regular users
awk -F: '$3 >= 1000 && $3 < 65534 { print $1, $3 }' /etc/passwd
# alice 1000
# bob 1001
# carol 1002

# ============================================
# PART 4: READ /etc/group
# ============================================

head -5 /etc/group
# root:x:0:
# daemon:x:1:
# bin:x:2:
# sys:x:3:
# adm:x:4:syslog,alice

grep developers /etc/group
# developers:x:1001:alice,bob,carol

# ============================================
# PART 5: GROUP MEMBERSHIP
# ============================================

# Supplementary members from /etc/group
grep sudo /etc/group
# sudo:x:27:alice

# Primary group from /etc/passwd
grep alice /etc/passwd | awk -F: '{ print "GID:", $4 }'
# GID: 1000

# Complete view with id
id alice
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),1001(developers)

# ============================================
# PART 6: FIND ACCOUNTS WITH REAL SHELLS
# ============================================

grep -v -E '/nologin|/false' /etc/passwd
# root:x:0:0:root:/root:/bin/bash
# sync:x:4:65534:sync:/bin:/bin/sync
# alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash
# bob:x:1001:1001:Bob Jones:/home/bob:/bin/bash
# carol:x:1002:1002:Carol White:/home/carol:/bin/bash

# ============================================
# PART 7: GETENT VS CAT
# ============================================

getent passwd alice
# alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash

getent passwd 1000
# alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash

getent group developers
# developers:x:1001:alice,bob,carol

# Count entries
getent passwd | wc -l
# 42

# ============================================
# PART 8: SHADOW FILE (ROOT ONLY)
# ============================================

sudo head -1 /etc/shadow
# root:$6$rounds=5000$xyz...:19000:0:99999:7:::

# Check password aging
sudo chage -l alice
# Last password change                                    : Mar 15, 2026
# Password expires                                        : never
# Password inactive                                       : never
# Account expires                                         : never
# Minimum number of days between password change          : 0
# Maximum number of days between password change          : 99999
# Number of days of warning before password expires       : 7

# ============================================
# PART 9: CURRENT SESSION
# ============================================

who
# alice    pts/0        2026-03-15 10:22 (192.168.1.10)

w
# 10:50:12 up 3 days,  1 user,  load average: 0.15, 0.10, 0.05
# USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
# alice    pts/0    192.168.1.10     10:22    0.00s  0.15s  0.02s w

id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),1001(developers)

# ============================================
# PART 10: LOGIN HISTORY
# ============================================

last | head -5
# alice    pts/0        192.168.1.10     Sat Mar 15 10:22   still logged in
# bob      pts/1        192.168.1.11     Sat Mar 15 10:45   still logged in
# alice    pts/0        192.168.1.10     Fri Mar 14 09:15 - 17:30  (08:15)

lastlog | head -5
# Username         Port     From             Latest
# root                       **Never logged in**
# daemon                     **Never logged in**
# bin                        **Never logged in**
# sys                        **Never logged in**
# alice            pts/0    192.168.1.10     Sat Mar 15 10:22:14 +0000 2026

# ============================================
# PART 11: AUDIT โ€” USERS WITH UID 0
# ============================================

awk -F: '$3 == 0 { print $1 }' /etc/passwd
# root
# (only root should appear)

# ============================================
# PART 12: AUDIT โ€” PASSWORDLESS ACCOUNTS
# ============================================

sudo awk -F: '$2 == "" { print $1 }' /etc/shadow
# (empty output is good โ€” no passwordless accounts)

Each part covers one inspection task. Parts 1 through 6 read the files directly. Part 7 shows getent. Part 8 reads shadow data safely. Parts 9 and 10 show session and history. Parts 11 and 12 are security audits.


Quick Reference

The Identity Files

FilePurposePermissions
/etc/passwdUser accounts644 (world-readable)
/etc/groupGroup definitions644 (world-readable)
/etc/shadowPassword hashes640 (root only)
/etc/gshadowGroup passwords640 (root only)

Field Extraction

TaskCommand
Usernames onlycut -d: -f1 /etc/passwd
UID + nameawk -F: '{ print $3, $1 }' /etc/passwd
System accountsawk -F: '$3 < 1000' /etc/passwd
Regular usersawk -F: '$3 >= 1000 && $3 < 65534' /etc/passwd
Real shellsgrep -v -E '/nologin|/false' /etc/passwd
Group membersgrep groupname /etc/group

getent Databases

DatabaseQueries
passwdUsers
groupGroups
shadowPassword hashes
hostsHostnames
servicesNetwork services

Session Commands

CommandOutput
whoLogged-in sessions
wSessions + activity
idCurrent identity
whoamiCurrent username
lastLogin history
lastbFailed login history
lastlogLast login per user

Security Audits

QueryCommand
UID 0 accountsawk -F: '$3 == 0' /etc/passwd
Passwordlesssudo awk -F: '$2 == ""' /etc/shadow
Real shellsgrep -v -E '/nologin|/false' /etc/passwd
Never logged inlastlog | grep 'Never'
Failed loginssudo lastb

Best Practices

โœ… Do This:

# Use getent for reliable lookups
getent passwd alice                                            # โœ…

# Use awk -F: for field extraction
awk -F: '{ print $1, $3 }' /etc/passwd                         # โœ…

# Check UID ranges to distinguish account types
awk -F: '$3 < 1000' /etc/passwd                                # โœ…

# Use chage -l for password policy
sudo chage -l alice                                            # โœ…

# Verify session identity before debugging
id                                                             # โœ…

# Review login history
last | head -20                                                # โœ…

โŒ Don’t Do This:

# Don't read /etc/shadow without root
cat /etc/shadow  # Permission denied                           # โš ๏ธ

# Don't assume /etc/passwd has all users
# Directory services may add more                              # โš ๏ธ

# Don't edit identity files by hand
vim /etc/passwd  # use useradd/usermod                         # โš ๏ธ

# Don't parse shadow directly when chage exists
# chage handles the edge cases                                  # โš ๏ธ

# Don't forget primary group is in /etc/passwd
# Member lists in /etc/group are supplementary only             # โš ๏ธ

Common Pitfalls

PitfallProblemSolution
grep misses remote usersFile has only local accountsUse getent
Group member list incompletePrimary group in /etc/passwdCheck both files
/etc/shadow unreadableExpected โ€” root onlyUse sudo
UID shown without nameStale UIDCheck /etc/passwd
last history shortLog rotationCheck rotation policy
awk field wrongMiscounted colonsVerify field number
getent passwd slowDirectory serviceUse targeted lookup
Editing passwd breaks loginMalformed lineUse vipw or tools

Real-World Examples

1. List all usernames

cut -d: -f1 /etc/passwd

2. Find a user’s UID

getent passwd alice | cut -d: -f3

3. List system accounts

awk -F: '$3 < 1000 { print $1 }' /etc/passwd

4. List human users

awk -F: '$3 >= 1000 && $3 < 65534 { print $1 }' /etc/passwd

5. Find accounts with real shells

grep -v -E '/nologin|/false' /etc/passwd

6. List group members

getent group developers

7. Check password policy

sudo chage -l alice

8. Audit UID 0 accounts

awk -F: '$3 == 0 { print $1 }' /etc/passwd

9. Find never-logged-in accounts

lastlog | grep 'Never'

10. Review recent logins

last | head -20

Visual: /etc/passwd Structure

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash     โ”‚
โ”‚    โ”‚   โ”‚   โ”‚    โ”‚        โ”‚           โ”‚         โ”‚         โ”‚
โ”‚    โ”‚   โ”‚   โ”‚    โ”‚        โ”‚           โ”‚         โ””โ”€โ”€ shell โ”‚
โ”‚    โ”‚   โ”‚   โ”‚    โ”‚        โ”‚           โ””โ”€โ”€ home dir        โ”‚
โ”‚    โ”‚   โ”‚   โ”‚    โ”‚        โ””โ”€โ”€ comment (GECOS)             โ”‚
โ”‚    โ”‚   โ”‚   โ”‚    โ””โ”€โ”€ primary GID                          โ”‚
โ”‚    โ”‚   โ”‚   โ””โ”€โ”€ UID                                       โ”‚
โ”‚    โ”‚   โ””โ”€โ”€ password placeholder                          โ”‚
โ”‚    โ””โ”€โ”€ username                                          โ”‚
โ”‚                                                          โ”‚
โ”‚  Colon-separated, position-sensitive, no header.         โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Primary vs Supplementary Membership

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  /etc/passwd:                                            โ”‚
โ”‚    alice:x:1000:1000:...                                 โ”‚
โ”‚                    โ”‚                                     โ”‚
โ”‚                    โ””โ”€โ”€ primary GID = 1000 (alice)        โ”‚
โ”‚                                                          โ”‚
โ”‚  /etc/group:                                             โ”‚
โ”‚    alice:x:1000:                                         โ”‚
โ”‚      (empty member list โ€” alice is here by primary GID)  โ”‚
โ”‚                                                          โ”‚
โ”‚    sudo:x:27:alice                                       โ”‚
โ”‚      (supplementary membership)                          โ”‚
โ”‚                                                          โ”‚
โ”‚    developers:x:1001:alice,bob,carol                     โ”‚
โ”‚      (supplementary membership)                          โ”‚
โ”‚                                                          โ”‚
โ”‚  id alice:                                               โ”‚
โ”‚    groups=1000(alice),27(sudo),1001(developers)          โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: getent vs cat

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  STANDALONE SYSTEM                                       โ”‚
โ”‚                                                          โ”‚
โ”‚  cat /etc/passwd  โ”€โ”€โ–บ local accounts                     โ”‚
โ”‚  getent passwd    โ”€โ”€โ–บ local accounts                     โ”‚
โ”‚                                                          โ”‚
โ”‚  Same result.                                            โ”‚
โ”‚                                                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  DIRECTORY-CONNECTED SYSTEM (LDAP, SSSD)                 โ”‚
โ”‚                                                          โ”‚
โ”‚  cat /etc/passwd  โ”€โ”€โ–บ local accounts only                โ”‚
โ”‚  getent passwd    โ”€โ”€โ–บ local + directory accounts         โ”‚
โ”‚                                                          โ”‚
โ”‚  Different results. getent shows the full picture.       โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Inspection Decision Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  What do you need to know?                               โ”‚
โ”‚       โ”‚                                                  โ”‚
โ”‚       โ”œโ”€โ”€ A specific user's identity?                    โ”‚
โ”‚       โ”‚      โ””โ”€โ”€ id username / getent passwd username    โ”‚
โ”‚       โ”‚                                                  โ”‚
โ”‚       โ”œโ”€โ”€ All local accounts?                            โ”‚
โ”‚       โ”‚      โ””โ”€โ”€ cat /etc/passwd                         โ”‚
โ”‚       โ”‚                                                  โ”‚
โ”‚       โ”œโ”€โ”€ All accounts (incl. remote)?                   โ”‚
โ”‚       โ”‚      โ””โ”€โ”€ getent passwd                           โ”‚
โ”‚       โ”‚                                                  โ”‚
โ”‚       โ”œโ”€โ”€ Who is logged in now?                          โ”‚
โ”‚       โ”‚      โ””โ”€โ”€ who / w                                 โ”‚
โ”‚       โ”‚                                                  โ”‚
โ”‚       โ”œโ”€โ”€ Who logged in recently?                        โ”‚
โ”‚       โ”‚      โ””โ”€โ”€ last / lastlog                          โ”‚
โ”‚       โ”‚                                                  โ”‚
โ”‚       โ””โ”€โ”€ Password policy?                               โ”‚
โ”‚              โ””โ”€โ”€ sudo chage -l username                  โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Security Audit Checklist

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  1. UID 0 accounts (only root)                           โ”‚
โ”‚     awk -F: '$3 == 0' /etc/passwd                        โ”‚
โ”‚                                                          โ”‚
โ”‚  2. Passwordless accounts                                โ”‚
โ”‚     sudo awk -F: '$2 == ""' /etc/shadow                  โ”‚
โ”‚                                                          โ”‚
โ”‚  3. Accounts with real shells                            โ”‚
โ”‚     grep -v -E '/nologin|/false' /etc/passwd             โ”‚
โ”‚                                                          โ”‚
โ”‚  4. Never-logged-in accounts                             โ”‚
โ”‚     lastlog | grep 'Never'                               โ”‚
โ”‚                                                          โ”‚
โ”‚  5. Recent failed logins                                 โ”‚
โ”‚     sudo lastb                                           โ”‚
โ”‚                                                          โ”‚
โ”‚  6. Recent successful logins from unexpected IPs         โ”‚
โ”‚     last | head -50                                      โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

FileRead WithContains
/etc/passwdcat, grep, awkUser accounts
/etc/groupcat, grep, awkGroup definitions
/etc/shadowsudo cat, chage -lPassword hashes
/etc/gshadowsudo catGroup passwords
QueryTool
User by name/UIDgetent passwd
Group by name/GIDgetent group
Current identityid, whoami
Current sessionswho, w
Login historylast, lastlog
Failed loginslastb
Password policychage -l

Key takeaways:

  • /etc/passwd and /etc/group are plain text with fixed field order โ€” seven fields for users, four for groups, colon-separated
  • /etc/shadow holds the password hashes and is readable only by root; chage -l is the safe way to read aging data
  • getent is preferred over cat because it queries the system’s full identity source, not just the local files
  • A user’s primary group is in /etc/passwd, not /etc/group โ€” the member list in /etc/group holds only supplementary members
  • awk -F: is the standard tool for extracting fields and filtering by UID range
  • id is the fastest way to see a user’s complete identity โ€” UID, primary GID, and all supplementary groups
  • who, w, last, and lastlog show current and historical sessions โ€” the starting point for any access review
  • Security audits follow predictable queries โ€” UID 0 accounts, passwordless accounts, real shells, never-logged-in accounts, failed logins
  • System accounts live in the same files as human accounts โ€” the UID range (below 1000) is how to tell them apart

Remember: The identity files are the reference for who exists on a Linux system, and getent is the tool that shows what the system actually resolves. Reading them is a matter of knowing the field order and using awk -F: to extract what you need. The session and history commands โ€” id, who, last, lastlog โ€” answer the questions about who is here now and who was here before. Together they cover the full range of user and group inspection, from a single lookup to a system-wide audit.


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!