User & Group Management
Managing users
# Create a user with home directory and default shell
useradd -m -s /bin/bash alice
# Create a user with a comment (full name) and specific UID
useradd -m -s /bin/bash -c "Alice Smith" -u 1500 alice
# Create a user and add to a primary group
useradd -m -s /bin/bash -g developers alice
# Modify an existing user
usermod -s /bin/zsh alice # change shell
usermod -c "Alice Smith" alice # change comment/full name
usermod -d /home/newdir -m alice # change home dir and move files
usermod -l newname alice # rename the login name
usermod -aG wheel alice # add to supplementary group (append, don't replace)
usermod -L alice # lock account (disable password login)
usermod -U alice # unlock account
# Delete a user (keeps home directory)
userdel alice
# Delete a user AND their home directory
userdel -r alice
-a with -G when adding a user to a group: usermod -aG groupname username. Without -a, -G replaces all supplementary groups, removing the user from groups they were already in.
Checking user info
# Show all groups the current user belongs to
id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),10(wheel),1001(developers)
# Show info for a specific user
id alice
# Show user's login name and shell
getent passwd alice
# List all users on the system
getent passwd | cut -d: -f1
Managing groups
# Create a group
groupadd developers
groupadd -g 2000 developers # with specific GID
# Delete a group
groupdel developers
# Rename a group
groupmod -n devs developers
# Add a user to a group (alternative to usermod -aG)
gpasswd -a alice developers
# Remove a user from a group
gpasswd -d alice developers
# List members of a group
getent group developers
# developers:x:2000:alice,bob,charlie
# Show all groups on the system
getent group | cut -d: -f1
Passwords
# Set password interactively
passwd alice
# Set password non-interactively (scripted) — use with care
echo "alice:newpassword" | chpasswd
# Force user to change password at next login
chage -d 0 alice
# Set password expiry (days)
chage -M 90 alice # password expires in 90 days
chage -m 1 alice # minimum 1 day between changes
chage -W 14 alice # warn 14 days before expiry
# View password aging info
chage -l alice
# Lock / unlock an account
passwd -l alice # lock (prepends ! to shadow hash)
passwd -u alice # unlock
/etc/passwd, /etc/shadow, /etc/group
# /etc/passwd — one record per user (world-readable)
# username:x:UID:GID:comment:home:shell
alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash
# The x in field 2 means the password hash is in /etc/shadow
# UID 0 = root; UIDs 1-999 are typically system accounts
# /etc/shadow — password hashes (root-readable only)
# username:hash:lastchange:min:max:warn:inactive:expire:reserved
alice:$6$rounds=...:19500:0:90:14:::
# /etc/group — one record per group
# groupname:x:GID:member-list
developers:x:2000:alice,bob
# /etc/gshadow — group passwords (rarely used)
# Look up entries with getent (works for local files AND SSSD/LDAP)
getent passwd alice
getent group developers
sudo and sudoers
sudo lets a regular user run commands as root (or another user). Configuration is in /etc/sudoers — always edit it with visudo, which validates syntax before saving.
# Edit sudoers safely — validates before writing
visudo
# Core sudoers syntax:
# WHO WHERE=(AS_WHOM) WHAT
# Allow alice to run any command as root
alice ALL=(ALL) ALL
# Allow alice to run any command without a password
alice ALL=(ALL) NOPASSWD: ALL
# Allow alice to run only specific commands
alice ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
# Allow the wheel group full sudo access (standard RHEL setup)
%wheel ALL=(ALL) ALL
# Allow the sudo group full access (standard Debian setup)
%sudo ALL=(ALL) ALL
Add users to the wheel group (RHEL) or sudo group (Debian) for standard admin access, rather than writing individual sudoers rules. These groups are already defined with full sudo access by default.
Add a user to wheel/sudo group
# RHEL — add to wheel group
usermod -aG wheel alice
# Debian — add to sudo group
usermod -aG sudo alice
# Verify (user must log out and back in for group change to take effect)
id alice
/etc/sudoers.d/
Rather than editing /etc/sudoers directly, drop files in /etc/sudoers.d/. This keeps custom rules separate from distribution defaults and makes them easier to manage with Ansible.
# Create a sudoers.d file for deploy user
visudo -f /etc/sudoers.d/deploy
# Contents of /etc/sudoers.d/deploy:
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp, \
/usr/bin/systemctl reload nginx
# Permissions must be exactly 0440
chmod 0440 /etc/sudoers.d/deploy
# Validate without saving (dry run)
visudo -c -f /etc/sudoers.d/deploy
visudo -c to validate a sudoers file before applying it. A syntax error in sudoers can lock everyone out of sudo, including root escalation via sudo.
Service accounts
Service accounts are users created for running services, not for human login. They have no password, no interactive shell, and often no home directory.
# Create a system account (low UID, no login)
useradd -r -s /sbin/nologin -d /var/lib/myapp -c "myapp service" myapp
# -r = system account (UID below 1000)
# -s /sbin/nologin = cannot log in interactively
# -d = home directory (can be /nonexistent if the service doesn't need one)
# Verify the account
id myapp
getent passwd myapp
# Create a directory owned by the service account
mkdir -p /var/lib/myapp
chown myapp:myapp /var/lib/myapp
chmod 750 /var/lib/myapp
# In a systemd unit, run as this account
# [Service]
# User=myapp
# Group=myapp
Ansible
---
# Create a user
- name: Create deploy user
ansible.builtin.user:
name: deploy
comment: Deployment service account
shell: /bin/bash
create_home: true
groups:
- deploy
append: true # don't remove from other groups
# Create a system service account
- name: Create myapp service account
ansible.builtin.user:
name: myapp
system: true
shell: /sbin/nologin
home: /var/lib/myapp
create_home: true
comment: myapp service account
# Create a group
- name: Create developers group
ansible.builtin.group:
name: developers
gid: 2000
state: present
# Add user to groups
- name: Add alice to developers and wheel
ansible.builtin.user:
name: alice
groups:
- developers
- wheel
append: true # IMPORTANT: preserve existing group memberships
# Deploy a sudoers.d rule
- name: Deploy sudoers rule for deploy user
ansible.builtin.template:
src: sudoers-deploy.j2
dest: /etc/sudoers.d/deploy
owner: root
group: root
mode: '0440'
validate: visudo -c -f %s
Troubleshooting
# User can't log in
# Check if account is locked
passwd -S alice # LK = locked, PS = has password, NP = no password
chage -l alice # check expiry dates
# User not in expected groups after usermod -aG
# Groups only update after the user's session ends and starts again
# Check current session groups vs /etc/group
id alice # session groups
getent group wheel # check wheel group membership in /etc/group
# "sudo: PAM account management error"
# SSSD or PAM misconfiguration — check sssd.conf and /etc/pam.d/sudo
journalctl -u sssd
# "sudo: alice is not in the sudoers file. This incident will be reported."
# Add to wheel/sudo group, or create a sudoers.d rule
usermod -aG wheel alice # RHEL
usermod -aG sudo alice # Debian
# visudo reports syntax error after edits
# The error line number is shown — fix and run visudo again
# Emergency recovery: boot to single-user mode and edit manually
useradd / visudo is for standalone servers, service accounts, and bootstrap situations.
faillock — account unlock after PAM lockout
When PAM's pam_faillock (or its predecessor pam_tally2) is configured, repeated failed logins lock the account automatically. The faillock command shows and clears the failure count.
# Show all accounts with recorded failures
faillock
# Show failures for a specific user
faillock --user alice
# Example output:
# alice:
# When Type Source Valid
# 2024-03-15 10:23:01 TTY ssh V
# 2024-03-15 10:23:05 TTY ssh V
# 2024-03-15 10:23:09 TTY ssh V ← locked after this
# Reset (unlock) a user's failure count
faillock --user alice --reset
# Verify the counter is cleared
faillock --user alice # should now show no entries
The lockout settings (max tries, unlock time) are configured in /etc/security/faillock.conf or in /etc/pam.d/system-auth. Common settings: deny = 5 (lock after 5 failures), unlock_time = 600 (10 minute auto-unlock). Manual unlock with faillock --reset works immediately regardless of the unlock_time timer.
--maxfail and --lockouttime password policy. Use ipa user-unlock alice to unlock in that case.
/etc/login.defs
/etc/login.defs sets system-wide defaults that affect useradd, password aging, UID/GID ranges, and home directory creation. Many of these defaults are applied at user creation time and do not retroactively affect existing accounts.
# Key directives in /etc/login.defs
# Password aging defaults (applied to new accounts)
PASS_MAX_DAYS 90 # password expires after 90 days
PASS_MIN_DAYS 0 # minimum days before password can be changed
PASS_WARN_AGE 7 # warn user 7 days before expiry
# Minimum password length (PAM usually enforces this too)
PASS_MIN_LEN 8
# UID/GID ranges for normal users (useradd picks from these)
UID_MIN 1000
UID_MAX 60000
# UID/GID ranges for system accounts (useradd --system)
SYS_UID_MIN 201
SYS_UID_MAX 999
# Default umask for user home directories
UMASK 022
# Create home directory by default
CREATE_HOME yes
# Encrypt passwords with SHA-512
ENCRYPT_METHOD SHA512
Changes to /etc/login.defs only affect future useradd commands and future password changes. To apply new aging settings to existing accounts, use chage per-user.
pwck and grpck
After bulk user or group modifications (especially manual edits to /etc/passwd, /etc/shadow, or /etc/group), run these utilities to check for inconsistencies.
# Check /etc/passwd and /etc/shadow for consistency
pwck
# Read-only check (report only, no fixes)
pwck -r
# Check /etc/group and /etc/gshadow for consistency
grpck
# Read-only check
grpck -r
What pwck checks:
- Every user in
/etc/passwdhas a matching entry in/etc/shadow - Home directories exist
- Login shells listed in
/etc/passwdexist in/etc/shells - UID/GID values are valid numbers
What grpck checks:
- Every group in
/etc/grouphas a matching entry in/etc/gshadow - All users listed as group members exist in
/etc/passwd - Duplicate GIDs
# If pwck finds fixable issues, it will prompt to remove them
# Run non-interactively and just report
pwck -r 2>&1 | grep -v "^$"
useradd / visudo is for standalone servers, service accounts, and bootstrap situations.