Users & Permissions
Users & Groups
Accounts, UIDs, groups, and how identity is stored in /etc/passwd and /etc/shadow.
Identity is the anchor of every permission decision, so the files that define identity are among the most security-sensitive on the system. A stray account with a shell, a UID accidentally set to 0, or a password that never expires each turns into a foothold. Knowing exactly how accounts are stored, how to tell a service account from a human one, and how to age and lock credentials is the groundwork for everything else in this group. Work through this on the lab VM; snapshot first.
passwd, shadow, And group
Three world-readable-or-not files hold the account model. Their formats are colon-separated and worth memorizing.
/etc/passwd lists accounts. It is world-readable and holds no secrets.
grep -E 'root|ubuntu|www-data' /etc/passwd
Expected output:
root:x:0:0:root:/root:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
The seven fields are: name, password placeholder (x means “see shadow”), UID, primary GID, comment/GECOS, home directory, login shell. The two fields that matter most for security are the UID (is it 0?) and the shell (can this account log in?).
/etc/shadow holds the hashed passwords and aging data. It is readable only by root, which is the whole point of splitting it out from passwd.
sudo grep ubuntu /etc/shadow
Expected output:
ubuntu:$y$j9T$Xq...long-hash...:19910:0:99999:7:::
Its fields are: name, hashed password, last-change day, minimum age, maximum age, warning period, inactivity, expiration. A ! or * in the hash field means no valid password, so the account cannot authenticate with one. /etc/group maps group names to GIDs and lists supplementary members:
grep -E 'sudo|www-data' /etc/group
Expected output:
www-data:x:33:
sudo:x:27:ubuntu
Membership in sudo here is what lets ubuntu escalate, tying directly into Sudo.
System Accounts vs Human Accounts
Not every account is a person. Ubuntu reserves low UIDs for system and service accounts (traditionally below 1000) and gives human logins UIDs from 1000 up. Service accounts like www-data exist so a daemon runs as an unprivileged identity rather than root; if the web server is compromised, the attacker gets www-data, not the system.
| Trait | System account | Human account |
|---|---|---|
| UID range | Below 1000 | 1000 and up |
| Shell | Usually /usr/sbin/nologin | A real shell (/bin/bash) |
| Password | Locked (! in shadow) | Set |
| Purpose | Run a daemon in isolation | Interactive login |
The security marker is the shell. A service account should have nologin, which prints a message and exits, so a stolen service credential cannot open an interactive session.
awk -F: '$7 !~ /nologin|false/ {print $1, $3, $7}' /etc/passwd
Expected output:
root 0 /bin/bash
ubuntu 1000 /bin/bash
Anything in that list other than root and your real users is a red flag: a service account with a login shell is an escalation waiting to happen.
Password Policy, Aging, And Locking
Even human accounts need discipline. chage shows and sets password aging; passwd -l locks an account without deleting it. Create a throwaway user in the lab and inspect it:
sudo useradd -m -s /bin/bash alice
sudo passwd alice
sudo chage -l alice
Expected output:
Last password change : Jul 06, 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
“Password expires: never” is the default and is fine for a lab, but on a real host you would set a maximum age. Force a 90-day maximum and a warning window:
sudo chage -M 90 -W 7 alice
To disable an account fast, without removing it or its files, lock the password. Locking prepends a ! to the shadow hash so no password matches; the account can still be used by key-based SSH unless you also expire it.
sudo passwd -l alice
sudo grep alice /etc/shadow | cut -d: -f1,2
Expected output:
alice:!$y$j9T$Xq...long-hash...
To fully bar login (password and any expiry), also set an expiry date in the past with chage -E. Reverse a lock with passwd -u. When you are done experimenting, sudo userdel -r alice removes the account and its home directory, and rolling back the snapshot resets everything.
Once you understand who the accounts are, the next question is what each can touch, which is File Permissions. Group membership determined here also drives Sudo and the group column in every permission listing.
Practical Guidance
- Audit login shells regularly; every account with a real shell should map to a person or a documented need.
- Confirm no account other than root has UID 0; a second UID-0 account is a hidden root by another name.
- Give service accounts
nologinand a locked password so a stolen daemon credential cannot open a session. - Set a maximum password age with
chage -Mon human accounts rather than leaving the 99999-day default. - Prefer
passwd -lpluschage -Eto disable a departing user’s account before deleting it, so you keep an audit trail. - Never edit
/etc/shadowby hand; usepasswd,chage, andusermod, which keep the format and fields consistent.