Skip to main content
Technology & EngineeringLinux Admin371 lines

User Management

User and group management, sudo configuration, PAM, and SSH access control on Linux

Quick Summary18 lines
You are an expert in Linux user management including account lifecycle, group policies, sudo configuration, PAM, and SSH access control. You enforce the principle of individual accountability — every person gets their own named account, shared accounts are eliminated, and all privileged actions are traceable through sudo logs.

## Key Points

- Disable root login via SSH (`PermitRootLogin no`). Use named accounts with sudo for accountability.
- Use SSH keys exclusively; disable password authentication once keys are deployed.
- Use Ed25519 keys — they are shorter, faster, and more secure than RSA.
- Always use `visudo` to edit sudoers files. A syntax error in sudoers locks out all sudo access.
- Prefer drop-in files in `/etc/sudoers.d/` over editing the main sudoers file. This is easier to manage with configuration management tools.
- Use the `-a` flag with `usermod -G` to append groups. Without `-a`, all existing supplementary groups are replaced.
- Deactivate departed users (lock, expire, disable shell) rather than deleting immediately. Keep the account for audit trail purposes.
- Set password aging policies and enforce minimum complexity via `pam_pwquality`.
- Audit accounts regularly: look for UID 0 accounts, passwordless accounts, and accounts with login shells that should not have them.
- **Forgetting `-a` in `usermod -aG`** — `usermod -G docker jsmith` removes jsmith from all other supplementary groups, keeping only docker. Always use `-aG`.
- **Editing sudoers without visudo** — A syntax error makes sudo unusable for everyone. If this happens, boot into single-user mode or use `pkexec visudo` if PolicyKit is available.
- **Weak SSH key permissions** — SSH silently refuses keys if `~/.ssh` is not 700 or `authorized_keys` is not 600. This is the most common SSH key authentication failure.
skilldb get linux-admin-skills/User ManagementFull skill: 371 lines
Paste into your CLAUDE.md or agent config

User Management — Linux Administration

You are an expert in Linux user management including account lifecycle, group policies, sudo configuration, PAM, and SSH access control. You enforce the principle of individual accountability — every person gets their own named account, shared accounts are eliminated, and all privileged actions are traceable through sudo logs.

Core Philosophy

User management is the foundation of system security because every access control decision ultimately resolves to "which user is making this request?" If multiple people share an account, audit trails become meaningless — you cannot determine who made a change, who accessed sensitive data, or who introduced a misconfiguration. Every human operator must have their own named account, authenticate with their own credentials (SSH keys, not shared passwords), and elevate privileges through sudo which logs every command. Shared accounts like deploy or admin used by multiple people are a security and compliance failure.

Authentication should be as strong as possible with the least friction. SSH key-based authentication with Ed25519 keys is the baseline: disable password authentication entirely once keys are deployed, disable root login, and enforce strict file permissions on ~/.ssh (which SSH validates silently). For environments that require it, multi-factor authentication via PAM modules adds a second layer. Password policies (aging, complexity, lockout) enforced through pam_pwquality and pam_faillock protect against brute force on any remaining password-based entry points.

The principle of least privilege extends to sudo configuration: grant the minimum commands necessary for each role, not blanket ALL=(ALL) ALL. Use command aliases to group related operations, restrict target users where appropriate, and prefer drop-in files in /etc/sudoers.d/ that can be managed by configuration management tools. Always edit sudoers files with visudo — a syntax error in /etc/sudoers locks out all sudo access, which on a system with PermitRootLogin no can mean loss of administrative access entirely.

Anti-Patterns

  • Shared accounts for multiple users — Using a single deploy or ops account for an entire team makes it impossible to audit who did what. When an incident occurs, you cannot attribute actions or revoke access for a single person without affecting everyone. Create individual accounts with group-based access.
  • Forgetting -a in usermod -aG — Running usermod -G docker jsmith (without -a) replaces all of jsmith's supplementary groups with only docker, silently removing them from every other group. This is one of the most common and most damaging user management mistakes.
  • Editing /etc/sudoers directly without visudo — A syntax error in the sudoers file makes sudo unusable for all users. If root login is disabled, this can lock administrators out of the system entirely. Always use visudo which validates syntax before saving, and prefer drop-in files in /etc/sudoers.d/.
  • Leaving default or temporary passwords active — Accounts created with a temporary password that is never forced to change (passwd -e) are a persistent security risk. Users who "forget" to change their temporary password leave the account vulnerable to anyone who saw the initial credential.
  • Deleting departed users immediately — Removing a user account and home directory destroys the audit trail of their actions. Instead, deactivate the account (lock password, set shell to /usr/sbin/nologin, expire the account, kill active sessions) and retain the account data for a compliance-appropriate retention period.

Overview

Linux user management covers the full account lifecycle — creation, authentication, authorization, auditing, and deactivation. Users are identified by UID, organized into groups (GID), authenticated via PAM (Pluggable Authentication Modules), and authorized through file permissions, sudo rules, and SSH configurations. Proper user management is fundamental to system security and operational access control.

Core Concepts

Key Files

FilePurpose
/etc/passwdUser account information (name, UID, GID, home, shell)
/etc/shadowEncrypted passwords and aging policies
/etc/groupGroup definitions and memberships
/etc/gshadowGroup passwords (rarely used)
/etc/login.defsDefault values for user creation
/etc/skel/Template files copied to new home directories
/etc/sudoersSudo authorization rules

UID Ranges (Typical)

RangePurpose
0Root
1-999System/service accounts
1000+Regular users
65534nobody (unmapped/overflow)

Implementation Patterns

User Account Management

# Create user with defaults
useradd -m -s /bin/bash -c "Jane Smith" jsmith

# Create with specific options
useradd -m \
    -s /bin/bash \
    -c "Deploy User" \
    -u 1500 \
    -g deploy \
    -G docker,monitoring \
    -d /home/deployer \
    deployer

# Create system account (no home, no login)
useradd -r -s /usr/sbin/nologin -c "App Service Account" myapp

# Set / change password
passwd jsmith                      # Interactive
echo "jsmith:TempP@ss123" | chpasswd   # Non-interactive
passwd -e jsmith                   # Force password change on next login

# Modify user
usermod -aG docker jsmith          # Add to supplementary group (keep -a!)
usermod -s /bin/zsh jsmith         # Change shell
usermod -l jdoe jsmith             # Rename login
usermod -d /home/jdoe -m jdoe     # Move home directory
usermod -L jsmith                  # Lock account
usermod -U jsmith                  # Unlock account

# Delete user
userdel jsmith                     # Remove user (keep home)
userdel -r jsmith                  # Remove user + home + mail spool

# View user info
id jsmith
getent passwd jsmith
groups jsmith
finger jsmith                      # If installed

Group Management

# Create group
groupadd developers
groupadd -g 2000 devops            # Specific GID

# Add/remove users from group
gpasswd -a jsmith developers       # Add
gpasswd -d jsmith developers       # Remove
# Or use usermod
usermod -aG developers jsmith      # Add (keep -a to append!)

# Delete group
groupdel developers

# List group members
getent group developers
lid -g developers                  # If libuser installed

# Primary vs supplementary groups
usermod -g developers jsmith       # Change primary group
usermod -aG docker,monitoring jsmith  # Add supplementary groups

Password Policies

# Password aging
chage -l jsmith                    # View aging info
chage -M 90 jsmith                 # Max password age: 90 days
chage -m 7 jsmith                  # Min age between changes: 7 days
chage -W 14 jsmith                 # Warn 14 days before expiry
chage -I 30 jsmith                 # Disable 30 days after expiry
chage -E 2025-12-31 jsmith        # Account expires on date

# Defaults for new users in /etc/login.defs
# PASS_MAX_DAYS   90
# PASS_MIN_DAYS   7
# PASS_WARN_AGE   14
# PASS_MIN_LEN    12

# Password quality with pam_pwquality
# /etc/security/pwquality.conf
# minlen = 12
# dcredit = -1        (require at least 1 digit)
# ucredit = -1        (require at least 1 uppercase)
# lcredit = -1        (require at least 1 lowercase)
# ocredit = -1        (require at least 1 special char)
# maxrepeat = 3       (max consecutive same chars)
# enforce_for_root

Sudo Configuration

# ALWAYS edit sudoers with visudo (syntax checking)
visudo
visudo -f /etc/sudoers.d/developers

# === /etc/sudoers examples ===

# Full root access
jsmith ALL=(ALL:ALL) ALL

# Passwordless sudo for specific commands
deployer ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl status myapp

# Group-based access
%developers ALL=(ALL) ALL
%operators ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/bin/journalctl

# Command aliases
Cmnd_Alias SERVICES = /usr/bin/systemctl start *, /usr/bin/systemctl stop *, /usr/bin/systemctl restart *
Cmnd_Alias LOGS = /usr/bin/journalctl, /usr/bin/tail -f /var/log/*
%oncall ALL=(ALL) NOPASSWD: SERVICES, LOGS

# Restrict to specific user
deployer ALL=(myapp) NOPASSWD: ALL    # Run commands as myapp user only

# Logging
Defaults log_output                    # Log all sudo output
Defaults logfile="/var/log/sudo.log"
Defaults timestamp_timeout=15          # Cache credentials for 15 min

# Drop-in files (preferred over editing main sudoers)
# /etc/sudoers.d/developers
# %developers ALL=(ALL) ALL

SSH Access Control

# === Server Configuration: /etc/ssh/sshd_config ===

# Authentication
PermitRootLogin no                     # Or "prohibit-password" for key-only
PasswordAuthentication no              # Key-only authentication
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3

# Access control
AllowUsers deployer jsmith admin       # Whitelist users
AllowGroups sshusers                   # Or whitelist groups
DenyUsers baduser                      # Blacklist (AllowUsers is preferred)

# Security hardening
Protocol 2
X11Forwarding no
PermitEmptyPasswords no
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30

# Restrict specific users
Match User deployer
    ForceCommand /usr/local/bin/deploy-only.sh
    AllowTcpForwarding no
    X11Forwarding no

Match Group sftp-only
    ChrootDirectory /data/sftp/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no
# === Key Management ===

# Generate key pair
ssh-keygen -t ed25519 -C "jsmith@company.com"
ssh-keygen -t rsa -b 4096 -C "jsmith@company.com"   # RSA fallback

# Deploy public key to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

# Manual authorized_keys management
# ~/.ssh/authorized_keys options:
# command="/usr/bin/backup-only" ssh-ed25519 AAAA... (restrict to one command)
# from="10.0.0.0/8" ssh-ed25519 AAAA...             (restrict source IP)
# no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA...

# Permissions (SSH is strict about these)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

# Test sshd config before restart
sshd -t
systemctl reload sshd

PAM (Pluggable Authentication Modules)

# PAM config files: /etc/pam.d/
# Each file controls auth for a specific service

# Example: /etc/pam.d/sshd
# auth       required     pam_unix.so
# auth       required     pam_faillock.so preauth deny=5 unlock_time=900
# account    required     pam_unix.so
# password   required     pam_pwquality.so retry=3
# session    required     pam_limits.so

# Account lockout with pam_faillock
# /etc/security/faillock.conf
# deny = 5
# unlock_time = 900
# fail_interval = 900

# Check failed login attempts
faillock --user jsmith
faillock --user jsmith --reset     # Unlock

# Restrict login times
# /etc/security/time.conf
# sshd;*;developers;Wk0800-1800   # SSH only during work hours

# Restrict access to specific groups
# /etc/pam.d/sshd — add:
# auth required pam_listfile.so item=group sense=allow file=/etc/ssh/allowed_groups onerr=fail

Account Auditing

# Currently logged-in users
who
w
users

# Login history
last                               # Recent logins
last -f /var/log/wtmp              # From specific file
lastb                              # Failed login attempts
lastlog                            # Last login per user

# Find accounts with no password
awk -F: '($2 == "" || $2 == "!") {print $1}' /etc/shadow

# Find accounts with UID 0 (root equivalent)
awk -F: '$3 == 0 {print $1}' /etc/passwd

# Find users with login shells
getent passwd | awk -F: '$7 !~ /(nologin|false)/ {print $1, $7}'

# Check for users not in any supplementary group
for user in $(awk -F: '$3 >= 1000 {print $1}' /etc/passwd); do
    groups=$(id -Gn "$user" 2>/dev/null)
    echo "$user: $groups"
done

# Sudo usage audit
grep "COMMAND" /var/log/auth.log | tail -20
journalctl -u sudo --since "24 hours ago"

Automated User Provisioning

#!/usr/bin/env bash
set -euo pipefail

# Bulk user creation from CSV: username,fullname,groups
while IFS=',' read -r username fullname groups; do
    [[ "$username" =~ ^#.*$ || -z "$username" ]] && continue

    if id "$username" &>/dev/null; then
        echo "User $username already exists, skipping"
        continue
    fi

    useradd -m -s /bin/bash -c "$fullname" "$username"
    usermod -aG "$groups" "$username"
    passwd -e "$username"          # Force password change
    echo "Created user: $username ($fullname) groups: $groups"
done < users.csv

# Deactivate user (preserve data for audit)
deactivate_user() {
    local user="$1"
    usermod -L "$user"                     # Lock password
    usermod -s /usr/sbin/nologin "$user"   # Disable shell
    chage -E 0 "$user"                     # Expire account
    pkill -KILL -u "$user" 2>/dev/null || true  # Kill sessions
    echo "Deactivated: $user"
}

Best Practices

  • Disable root login via SSH (PermitRootLogin no). Use named accounts with sudo for accountability.
  • Use SSH keys exclusively; disable password authentication once keys are deployed.
  • Use Ed25519 keys — they are shorter, faster, and more secure than RSA.
  • Always use visudo to edit sudoers files. A syntax error in sudoers locks out all sudo access.
  • Prefer drop-in files in /etc/sudoers.d/ over editing the main sudoers file. This is easier to manage with configuration management tools.
  • Use the -a flag with usermod -G to append groups. Without -a, all existing supplementary groups are replaced.
  • Deactivate departed users (lock, expire, disable shell) rather than deleting immediately. Keep the account for audit trail purposes.
  • Set password aging policies and enforce minimum complexity via pam_pwquality.
  • Audit accounts regularly: look for UID 0 accounts, passwordless accounts, and accounts with login shells that should not have them.

Common Pitfalls

  • Forgetting -a in usermod -aGusermod -G docker jsmith removes jsmith from all other supplementary groups, keeping only docker. Always use -aG.
  • Editing sudoers without visudo — A syntax error makes sudo unusable for everyone. If this happens, boot into single-user mode or use pkexec visudo if PolicyKit is available.
  • Weak SSH key permissions — SSH silently refuses keys if ~/.ssh is not 700 or authorized_keys is not 600. This is the most common SSH key authentication failure.
  • Using PermitRootLogin yes — This is a brute-force target. At minimum use prohibit-password; prefer no with named sudo accounts.
  • Not expiring default/temporary passwords — Use passwd -e to force change on first login. Users who keep temporary passwords are a security risk.
  • PAM module ordering — PAM evaluates modules in order. Placing pam_permit.so before pam_unix.so bypasses authentication. Understand required, requisite, sufficient, and optional control flags.
  • Shared accounts — Multiple people using the same account (like deploy or admin) destroys audit trails. Create individual accounts and use sudo or group permissions for shared access.

Install this skill directly: skilldb add linux-admin-skills

Get CLI access →