KB
Linux Administration

Linux User and Group Management

6 min read1237 words17 code blocks

At a glance#

  • Purpose: Create, modify, disable and remove Linux user accounts and groups, and grant administrative access correctly.
  • Applies to: RHEL-family and Debian/Ubuntu systems.
  • Risk: Medium — removing the wrong account or sudo entry can lock administrators out.
  • Time: 10 minutes per account; 30 minutes for an offboarding.

Overview#

Account management is routine until someone leaves, at which point doing it badly leaves live credentials on production servers. This article covers the full lifecycle: create, grant, audit, disable, remove.

Two principles worth stating:

  • Never share accounts. Every person gets their own login. Shared accounts destroy accountability in the audit log.
  • Disable before deleting. Deleting an account immediately orphans its files and breaks anything running as it. Disable first, confirm nothing broke, delete later.

Understanding the files#

FileContents
/etc/passwdUsername, UID, GID, home directory, shell. World-readable.
/etc/shadowPassword hashes and ageing. Root-only.
/etc/groupGroup names and members.
/etc/sudoers and /etc/sudoers.d/Who may run what as root.

UID conventions matter when auditing:

UID rangeMeaning
0root
1–999System and service accounts
1000+Real people

Creating accounts#

1. Create the user#

bash
# Debian / Ubuntu - interactive, creates home and group
sudo adduser jsmith

# RHEL family, or non-interactive anywhere
sudo useradd -m -s /bin/bash -c "Jane Smith" jsmith
sudo passwd jsmith
FlagMeaning
-mCreate the home directory
-sLogin shell
-cComment field, used for the full name
-GSupplementary groups
-eAccount expiry date
Note: useradd without -m creates an account with no home directory. The user can log in but lands in / with no dotfiles, and SSH key authentication fails because there is nowhere for ~/.ssh to live.

2. Force a password change at first login#

bash
sudo chage -d 0 jsmith

3. Set an expiry date for temporary access#

Contractors and vendors should never have open-ended accounts:

bash
sudo usermod -e 2026-12-31 jsmith
sudo chage -l jsmith

Groups#

bash
# create
sudo groupadd developers

# add a user - -a is essential
sudo usermod -aG developers jsmith

# remove from a group
sudo gpasswd -d jsmith developers

# list a user's groups
groups jsmith
id jsmith

# list members of a group
getent group developers
Warning: usermod -G without -a replaces every supplementary group the user has. Running usermod -G developers jsmith on an admin removes them from sudo/wheel. Always use -aG.

Granting administrative access#

Add the user to the administrative group rather than writing individual sudoers entries:

bash
# RHEL family
sudo usermod -aG wheel jsmith

# Debian / Ubuntu
sudo usermod -aG sudo jsmith

For a specific, limited grant, use a file in /etc/sudoers.d/ — never edit /etc/sudoers directly:

bash
sudo visudo -f /etc/sudoers.d/webteam
text
# Allow the web team to manage nginx only
%webteam ALL=(root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/nginx -t
bash
sudo chmod 440 /etc/sudoers.d/webteam
Warning: Always use visudo, including for files in sudoers.d. It validates syntax before saving. A malformed sudoers file breaks sudo for everyone, and fixing it then requires the root password or single-user mode.

Verify what a user can do:

bash
sudo -l -U jsmith

Auditing accounts#

Run these periodically, and always during an offboarding review.

bash
# real user accounts
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6, $7}' /etc/passwd

# accounts with a login shell
grep -E "/bin/(ba)?sh$" /etc/passwd

# accounts with no password set - should return nothing
sudo awk -F: '$2 == "" {print $1}' /etc/shadow

# UID 0 accounts other than root - should return nothing
awk -F: '$3 == 0 {print $1}' /etc/passwd

# who holds administrative rights
getent group wheel sudo

# password ageing for one account
sudo chage -l jsmith

# accounts that have never logged in
lastlog | grep "Never logged in"

# recent logins
last -20
Warning: Any account other than root with UID 0 is a full root equivalent and is a classic backdoor. Investigate immediately if one appears.

Disabling an account#

Do this the moment someone leaves. It is reversible and takes seconds.

bash
# lock the password
sudo usermod -L jsmith

# also prevent SSH key login by changing the shell
sudo usermod -s /sbin/nologin jsmith

# expire the account entirely
sudo usermod -e 1 jsmith
Warning: usermod -L locks the password only. If the user has an SSH key in ~/.ssh/authorized_keys, they can still log in. Locking without changing the shell — or removing the key — is the most common offboarding mistake.

Remove their SSH access explicitly:

bash
sudo mv /home/jsmith/.ssh/authorized_keys /home/jsmith/.ssh/authorized_keys.disabled

Terminate any live sessions:

bash
who | grep jsmith
sudo pkill -KILL -u jsmith

Removing an account#

Only after the account has been disabled for an agreed period and nothing has broken.

bash
# find files owned by the user first
sudo find / -user jsmith -not -path "/proc/*" 2>/dev/null

# check for scheduled jobs
sudo crontab -l -u jsmith

Archive the home directory before deletion:

bash
sudo tar -czf /root/leavers/jsmith-$(date +%F).tar.gz /home/jsmith

Then remove:

bash
# keep the home directory
sudo userdel jsmith

# remove home and mail spool as well
sudo userdel -r jsmith
Warning: Files elsewhere on the system remain owned by the now-free UID. If a new account later reuses that UID, it silently inherits ownership of those files. Always run the find above and reassign or delete what it reports.

Offboarding checklist#

  • [ ] Lock the password (usermod -L)
  • [ ] Change shell to /sbin/nologin
  • [ ] Disable ~/.ssh/authorized_keys
  • [ ] Remove from wheel / sudo and any application groups
  • [ ] Kill active sessions
  • [ ] Check for cron jobs and running processes
  • [ ] Archive the home directory
  • [ ] Repeat on every server, not just the obvious ones
  • [ ] Rotate any shared credentials the person knew

Verification#

bash
# account exists with expected properties
id jsmith
getent passwd jsmith

# group membership
groups jsmith

# sudo rights
sudo -l -U jsmith

# locked state - ! or * prefix on the hash means locked
sudo passwd -S jsmith

For a new account, have the user log in and run sudo -v before closing the ticket. For a disabled account, confirm login is refused by both password and key.

Troubleshooting#

SymptomCause and fix
User cannot sudo after being added to the groupGroup membership applies at login. Have them log out and back in, or run newgrp wheel.
sudo: /etc/sudoers.d/x is mode 0644, should be 0440Wrong permissions. sudo chmod 440 the file.
sudo broken for everyoneMalformed sudoers file. Recover via console: pkexec visudo, or boot single-user.
User locked out despite usermod -L not being runAccount expiry set. Check chage -l.
Disabled user still logging inSSH key still present. Remove authorized_keys.
Home directory missing after useradd-m was omitted. Create it and chown to the user.
userdel: user is currently used by processLive session or running service. Kill with pkill -u, then retry.
Files show a numeric owner instead of a nameThe owning account was deleted. Reassign with chown.