User & Group Management

Creating and managing local Linux users, groups, passwords, and sudo access — the foundational admin tasks before FreeIPA enters the picture.

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
Always use -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
Use 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
Note: In environments using FreeIPA, users and groups are managed centrally — see FreeIPA Core Basics and FreeIPA HBAC & Sudo. Local 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.

FreeIPA environments: In IPA-managed systems, account lockout may be managed centrally by IPA's --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:

What grpck checks:

# If pwck finds fixable issues, it will prompt to remove them
# Run non-interactively and just report
pwck -r 2>&1 | grep -v "^$"
Note: In environments using FreeIPA, users and groups are managed centrally — see FreeIPA Core Basics and FreeIPA HBAC & Sudo. Local useradd / visudo is for standalone servers, service accounts, and bootstrap situations.