LinuxIV · Users, Groups and IdentityIdentity
Account lifecycle and offboarding - creating, changing and removing users
What you'll learn
- Create accounts with the defaults that login.defs and /etc/skel actually apply
- Predict what usermod changes and what it leaves behind
- Find files orphaned by a deleted account
- Explain the UID reuse hazard and prevent it
- Run an offboarding that leaves no residual access
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11
The earlier lessons in this part read the user database, set password policy and separated service accounts from human ones. This lesson is about the three moments when the database changes.
The theme running through all three is that the account is a name and the filesystem records a number. Every difficulty in this lesson comes from that gap.
Creating an account
Two tools exist and they are not the same thing. useradd is
the low-level utility present on every distribution. adduser
is a Debian-family interactive wrapper that applies policy from
/etc/adduser.conf and then calls useradd.
The defaults useradd will apply are printable, and are worth
looking at before the first account rather than after the
twentieth:
$ useradd -DGROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/sh
SKEL=/etc/skel
CREATE_MAIL_SPOOL=noThe rest of the policy is in /etc/login.defs:
$ grep -E '^(UID_MIN|UID_MAX|GID_MIN|HOME_MODE|USERGROUPS_ENAB)' /etc/login.defsHOME_MODE 0750
UID_MIN 1000
UID_MAX 60000
GID_MIN 1000
USERGROUPS_ENAB yesThree of those change behaviour in ways people trip over:
UID_MIN/UID_MAXbound whereuseraddallocates. It picks the lowest free value in that range, which is the mechanism behind the UID reuse problem below.HOME_MODEsets the permissions on a new home directory directly. Where it is absent, the mode is derived fromUMASKinstead. A distribution that ships0750and one that ships0755produce visibly different privacy properties from the identicaluseraddcommand.USERGROUPS_ENAB yescreates a group with the same name as the user and makes it the primary group. That is what makes aumaskof002safe: files are group-writable by a group containing only the owner.
# The full form, stating everything rather than relying on defaults
sudo useradd -m -s /bin/bash -c 'Jordan Martin, Platform' \
-G adm,sudo jmartin
# Verify what was actually created
getent passwd jmartin
getent group jmartin
sudo ls -la /home/jmartin
-m is required for a home directory on hosts where
CREATE_HOME is not enabled in login.defs. An account
created without it has a /etc/passwd entry pointing at a
directory that does not exist, and the symptom is a login that
succeeds and lands in / with a shell that cannot write
anything.
/etc/skel is a template, not a policy
Files in /etc/skel are copied into the home directory at
creation time only:
ls -A /etc/skel
# .bash_logout .bashrc .profile
Adding a file to /etc/skel today does nothing for any account
that already exists. If a shell profile, an SSH configuration
or a compliance banner has to reach everyone, that is a
configuration management job. Skel covers new accounts and
nothing else.
Changing an account
usermod is precise about what it touches, and the man page is
explicit about what it deliberately leaves alone.
Renaming changes the login name and nothing else:
sudo usermod -l jmartin-old jmartin
The home directory keeps its old name. So does the primary
group, the mail spool, and every reference in a script or a
sudoers file. Files remain correctly owned, because they were
never owned by the name - they are owned by the UID, which did
not change.
Moving the home directory needs -m alongside -d:
sudo usermod -d /srv/home/jmartin -m jmartin
Without -m, the passwd entry points at a new path and the
contents stay where they were.
Changing the UID is the one that causes damage:
sudo usermod -u 1500 jmartin
usermod also refuses several operations while the user has
running processes, reporting user X is currently used by process N. That is a feature: changing a UID underneath a
running process leaves it holding file descriptors and
credentials that no longer match the database.
Removing an account
userdel removes the entries from /etc/passwd, /etc/shadow
and the groups. It removes no files.
userdel -r additionally removes the home directory and the
mail spool. The man page is again explicit: files located
elsewhere have to be searched for and deleted manually.
sudo userdel -r jmartin
# What did that leave behind
sudo find / -xdev -nouser -printf '%p\n' 2>/dev/null | head -50
find -nouser matches files whose numeric owner has no entry
in the user database - exactly the residue of a deletion. It is
also the check to run on a schedule, because orphaned files
accumulate silently.
Debian offers a heavier option from the adduser package:
sudo deluser --remove-all-files jmartin
It removes every file on the system owned by the user. Read
that sentence twice before running it: a service account that
owns files under /var/lib will have them deleted, and there
is no undo. It also has to walk the whole filesystem, so it is
slow on a large host.
Offboarding: the order that matters
Deleting the account is the last step, not the first. Access survives account deletion in more places than people expect, and the account is what you use to find them.
1. Lock the account and expire it. Reversible, immediate, and it holds the UID.
sudo usermod -L jmartin # disable the password
sudo chage -E 0 jmartin # expire the account
sudo passwd -S jmartin # confirm: L means locked
Locking the password alone is not enough on any host that accepts SSH keys - key authentication does not consult the password field. The account expiry is what stops the login.
2. Terminate live access.
loginctl list-sessions
sudo loginctl terminate-user jmartin
sudo pkill -u jmartin # anything not in a session
3. Remove key-based access. Both the local file and any central source.
sudo rm -f /home/jmartin/.ssh/authorized_keys
sudo grep -rn 'jmartin' /etc/ssh/ 2>/dev/null
If sshd uses AuthorizedKeysCommand, the authoritative copy
is in that backend and removing the local file achieves
nothing.
4. Remove scheduled work. This is the step most commonly missed, and it is how a departed account keeps executing code.
sudo crontab -u jmartin -l # look before removing
sudo crontab -u jmartin -r
ls -la /var/spool/cron/atjobs 2>/dev/null
sudo loginctl disable-linger jmartin
ls -la /home/jmartin/.config/systemd/user/ 2>/dev/null
loginctl enable-linger allows a user manager - and therefore
the services it supervises - to keep running with nobody logged
in.
An account with linger enabled has processes on the host right
now regardless of whether anybody has connected for months.
5. Remove privilege.
sudo grep -rn 'jmartin' /etc/sudoers /etc/sudoers.d/
sudo getent group | awk -F: '$4 ~ /(^|,)jmartin(,|$)/ { print $1 }'
6. Deal with the files. Reassign to the successor, archive, or delete - but decide, per directory, and record the decision.
sudo find / -xdev -user jmartin -printf '%p\n' 2>/dev/null > /root/jmartin-files.txt
wc -l /root/jmartin-files.txt
7. Only now delete the account, and retire the UID.
Knowledge check
Knowledge check · 5 questions
Q1. You run `usermod -u 1500 jmartin`. What happens to files the user owned in /srv/data?
Q2. A new file is added to /etc/skel. Which accounts get it?
Q3. A user has been deleted with userdel -r. Which residual access or residue can still exist? Select all that apply.
Q4. Locking a user password with `usermod -L` prevents that user from logging in over SSH.
Q5. Why does an access review based on file ownership names fail to detect the UID reuse problem?
Passing score: 75%. Answers are checked in this browser.