User and group management is central to Linux security. Every process runs as a user, every file is owned by a user and a group, and all access decisions flow from these identities. For RHCA, you must understand the complete user management ecosystem — from the internal file formats to advanced sudo policies and password aging.
The Linux User Identity Model
Linux identifies users by a numeric UID (User ID), not by name. The username is a human-readable label stored in /etc/passwd and mapped to the UID at login. Internally, the kernel works exclusively with UIDs and GIDs. This means:
- Two accounts with different usernames but the same UID are treated as the same user by the kernel
- Renaming a user in
/etc/passwddoes not change file ownership — the UID stays the same - If you delete a user with UID 500 and create a new user who gets UID 500, they will own all files the previous user owned
User Types and UID Ranges
| Type | UID Range (RHEL 7+) | Shell | Home Dir | Purpose |
|---|---|---|---|---|
| Root | 0 | /bin/bash | /root | Full administrative access |
| Static system users | 1–200 | /sbin/nologin | /var/svcname | Created by OS install for core services (sync, shutdown, halt) |
| Dynamic system users | 201–999 | /sbin/nologin | /var/svcname | Created when packages are installed (apache, mysql, postfix) |
| Normal users | 1000–60000 | /bin/bash | /home/username | Human users |
| Sudo users | same as normal | /bin/bash | /home/username | Normal users with elevated privileges |
Why do system users have /sbin/nologin as shell? Setting the shell to /sbin/nologin prevents interactive login for that account. Even if someone knows the password (or exploits a vulnerability), they cannot get a shell. This is a security best practice — services should run as non-interactive users.
The Authentication File System
/etc/passwd — User Account Database
This file has been part of Unix since the 1970s. Originally it stored password hashes in the second field — but since passwords are now in /etc/shadow, the second field always contains x.
# Format (7 colon-separated fields):
username:x:UID:GID:Comment/GECOS:HomeDir:LoginShell
# Real example:
root:x:0:0:root:/root:/bin/bash
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
raju:x:1001:1001:Raju Kumar:/home/raju:/bin/bash
# Fields explained:
# username = login name (max 32 chars, no spaces)
# x = password placeholder (actual hash is in /etc/shadow)
# UID = numeric user ID
# GID = primary group ID
# Comment = GECOS field — full name, room, phone (used by finger, mail)
# HomeDir = absolute path to home directory
# LoginShell = program executed at login (/bin/bash for users, /sbin/nologin for services)
/etc/shadow — Secure Password Database
When shadow passwords are enabled (the default since the 1990s), hashed passwords are moved from the world-readable /etc/passwd to the root-only /etc/shadow. This prevents offline password cracking attacks by non-privileged users.
# Format (9 colon-separated fields):
username:hashed_password:last_change:min_days:max_days:warn_days:inactive:expire:reserved
# Real example:
raju:$6$rounds=5000$Ab1c2...$xyz....:19200:0:90:7:3:19600:
# Fields explained:
# username = same as /etc/passwd
# hashed_pwd = $algorithm$salt$hash
# $1 = MD5 (old, weak)
# $5 = SHA-256
# $6 = SHA-512 (current standard)
# !! = locked account
# * = account cannot login
# last_change = days since Jan 1 1970 password was last changed
# min_days = minimum days before password can be changed (0 = anytime)
# max_days = maximum days password is valid (99999 = never expires)
# warn_days = days before expiry to warn user
# inactive = days after expiry before account is locked
# expire = absolute expiry date (days since Jan 1 1970)
/etc/group — Group Database
# Format:
groupname:x:GID:member1,member2,...
# Example:
wheel:x:10:raju,sam
oracle:x:1002:raju,gopal,ram
/etc/gshadow — Group Shadow File
Stores group passwords (rarely used) and group administrators.
# Format:
groupname:password:admin_list:member_list
User Creation In Depth
# Full useradd syntax:
# useradd [OPTIONS] username
# Options:
# -u UID specify UID (must be unique)
# -g GID specify primary group (GID or group name)
# -G grp1,grp2 specify secondary groups
# -c "comment" GECOS field (full name, etc.)
# -d /home/dir home directory path
# -s /bin/bash login shell
# -m create home directory (default on most systems)
# -M do NOT create home directory
# -r create a system account (UID < 1000, no home dir by default)
# -e YYYY-MM-DD account expiry date
# -f days password inactive days after expiry
# -o allow non-unique UID (create duplicate UID)
# Full example:
# useradd -u 1500 -g 1500 -G wheel,oracle -c "Database Admin" \
-d /home/dba -s /bin/bash dba
# Set password immediately:
# passwd dba
# Check what was created:
# id dba
# grep dba /etc/passwd /etc/shadow /etc/group
What Happens When useradd Runs
- A new line is added to
/etc/passwd - A new line is added to
/etc/shadowwith locked password (!!) - A new group is created in
/etc/group(same name as user, same GID as UID) - Home directory is created at the specified path
- Files from
/etc/skel/are copied to the home directory (.bash_profile,.bashrc,.bash_logout) - A mailbox is created at
/var/spool/mail/username
The /etc/skel Directory
Every file in /etc/skel/ is copied to new users' home directories. This is where you place default configuration files that should appear for all new users.
# Standard /etc/skel contents:
# .bash_logout — executed when user logs out
# .bash_profile — executed at login (sets PATH, environment variables)
# .bashrc — executed for each new shell (aliases, functions)
# Add a custom file for all new users:
# echo "export EDITOR=vim" >> /etc/skel/.bashrc
# Apply to existing user manually:
# cp /etc/skel/.bashrc /home/raju/
# chown raju:raju /home/raju/.bashrc
The Bash Environment Files
Understanding which file is sourced when is critical for diagnosing login/shell issues:
| File | When Executed | Typical Use |
|---|---|---|
| /etc/profile | Login shell — ALL users | System-wide environment, PATH |
| /etc/bashrc | Non-login interactive shell — ALL users | System-wide aliases, PS1 |
| ~/.bash_profile | Login shell — specific user | User-specific environment, calls .bashrc |
| ~/.bashrc | Non-login interactive shell | User aliases, functions, custom prompt |
| ~/.bash_logout | On logout | Cleanup tasks, clear history |
Password Aging and Security Policies
Password aging prevents users from using the same password indefinitely and implements grace periods for expired accounts.
# View current password aging for a user:
# chage -l raju
# Set comprehensive password policy:
# chage -m 7 raju # minimum 7 days before they can change password again
# chage -M 90 raju # maximum 90 days validity
# chage -W 14 raju # warn 14 days before expiry
# chage -I 7 raju # 7-day inactive grace period after expiry
# chage -E 2026-12-31 raju # account expires Dec 31 2026
# Force password change on next login:
# chage -d 0 raju # sets last_change to 0 (epoch) = expired immediately
# System-wide defaults in /etc/login.defs:
PASS_MAX_DAYS 90 # maximum password age
PASS_MIN_DAYS 7 # minimum password age
PASS_WARN_AGE 14 # warning days before expiry
PASS_MIN_LEN 8 # minimum password length
# Apply changes: edit the file, restart nothing (read at useradd time)
Account Locking and Unlocking
# Lock account (adds ! prefix to password hash in /etc/shadow):
# usermod -L raju
# passwd -l raju
# Unlock account:
# usermod -U raju
# passwd -u raju
# Check lock status:
# passwd -S raju
# grep raju /etc/shadow # look for ! prefix in password field
# Expire account immediately:
# usermod -e 1970-01-01 raju # set expiry to past date
# Disable login without locking (change shell to nologin):
# usermod -s /sbin/nologin raju
# Restore login shell:
# usermod -s /bin/bash raju
Groups In Depth
Primary vs Secondary Groups
Every user has exactly one primary group (stored in /etc/passwd 4th field). New files are owned by the primary group by default. A user can belong to up to 15 secondary groups, which grant additional access to group-owned resources.
# Check all groups for a user:
# id raju # shows uid, gid, and all groups
# groups raju # simplified list
# Create group:
# groupadd -g 2000 oracle
# Add user to secondary group (preserves existing groups):
# usermod -aG oracle raju # -a = append, -G = secondary groups
# WARNING: usermod -G oracle raju (without -a) REPLACES all secondary groups
# Remove user from group:
# gpasswd -d raju oracle
# Change user's primary group:
# usermod -g oracle raju
# Create group with admin user:
# groupadd developers
# gpasswd -A raju developers # raju is now group admin
# gpasswd -a sam developers # group admin can add members
sudo — Delegated Root Privilege
sudo (superuser do) allows specific users to run specific commands as root (or another user) without knowing the root password. All sudo commands are logged to /var/log/secure.
Why sudo Instead of su?
- Accountability: Every sudo command is logged with the user who ran it
- Principle of least privilege: Users only get access to commands they need
- No root password sharing: Each admin uses their own password
- Time-limited tokens: sudo prompts for password after timeout (default 5 minutes)
The sudoers File Structure
# ALWAYS edit with visudo (validates syntax before saving):
# visudo
# Basic format:
# WHO WHERE=(AS_WHOM) WHAT
# user host=(runas) command
# Give user all privileges:
raju ALL=(ALL) ALL
# Give user all privileges without password:
raju ALL=(ALL) NOPASSWD: ALL
# Allow only specific commands:
raju ALL=/usr/sbin/useradd,/usr/sbin/passwd
# Allow specific commands without password:
raju ALL=NOPASSWD:/usr/sbin/useradd,/usr/bin/systemctl restart httpd
# Allow a group:
%oracle ALL=(ALL) ALL
%wheel ALL=(ALL) ALL # wheel group = traditional admin group
# Command aliases (group commands):
Cmnd_Alias NETWORKING = /sbin/ifconfig,/sbin/route,/usr/sbin/iptables
Cmnd_Alias SERVICES = /usr/sbin/service,/usr/bin/systemctl
raju ALL=NETWORKING,SERVICES
# User aliases:
User_Alias DBAS = raju, gopal, ram
DBAS ALL=/usr/bin/mysql
# Host aliases (restrict sudo to specific servers):
Host_Alias WEBSERVERS = web1,web2,web3
raju WEBSERVERS=(ALL) ALL
# Force re-authentication for every sudo command:
Defaults timestamp_timeout=0
# Only for specific users:
Defaults:raju,gopal timestamp_timeout=0
File Permissions — Complete Reference
# Permission structure:
# -rwxrwxrwx (type + owner + group + others)
#
# Types: - regular file, d directory, l symlink,
# c char device, b block device, s socket, p pipe
#
# Permissions: r=4 (read), w=2 (write), x=1 (execute), -=0 (none)
# For FILES: r=cat/read, w=modify/delete, x=execute
# For DIRECTORIES: r=ls (list), w=create/delete files inside, x=cd (enter)
# Numeric (octal) examples:
# chmod 755 file = rwxr-xr-x (owner full, group/others read+execute)
# chmod 644 file = rw-r--r-- (owner read+write, others read only)
# chmod 700 dir = rwx------ (only owner can access)
# chmod 777 dir = rwxrwxrwx (everyone full access — use carefully)
# Symbolic examples:
# chmod u+x file add execute for owner
# chmod g-w file remove write for group
# chmod o=r file set others to read-only exactly
# chmod a+r file add read for all (user, group, others)
# chmod u+x,g-w,o-r file compound changes
# Recursive (apply to directory and all contents):
# chmod -R 755 /var/www/html
# Change ownership:
# chown raju file change owner
# chown raju:oracle file change owner and group
# chown :oracle file change only group
# chgrp oracle file change only group
# chown -R raju:oracle /data/ recursive
umask — Default Permission Control
umask subtracts permissions from the maximum default. It is a safety filter that ensures new files are not created world-writable by default.
# Default maximum:
# Files: 666 (rw-rw-rw-) — execute never set by default for files
# Dirs: 777 (rwxrwxrwx)
# Default umask: 0022 for root, 0002 for normal users
# Result for root:
# files: 666 - 022 = 644 (rw-r--r--)
# dirs: 777 - 022 = 755 (rwxr-xr-x)
# Check current umask:
# umask # shows 0022
# Set temporarily:
# umask 027 # owner full, group read/execute, others none
# Set permanently for a user:
# echo "umask 027" >> ~/.bashrc
# source ~/.bashrc
# Set system-wide:
# echo "umask 022" >> /etc/bashrc
Special Permission Bits
SUID (Set User ID)
When SUID is set on an executable, it runs as its owner (not as the user who runs it). Classic example: /usr/bin/passwd — a normal user needs to write to /etc/shadow (root-owned), so passwd has SUID set to root.
# Find all SUID files (security audit):
# find / -perm /4000 -type f 2>/dev/null
# Set SUID:
# chmod u+s /usr/sbin/some_command
# chmod 4755 /usr/sbin/some_command
# Visible in ls:
# ls -l /usr/bin/passwd
# -rwsr-xr-x (s in owner execute position = SUID)
SGID (Set Group ID)
On directories: files created inside inherit the directory's group (not the creator's primary group). Used for shared project directories where all team members need to share ownership.
# Set SGID on shared directory:
# chmod g+s /shared/project
# chmod 2775 /shared/project
# Verify:
# ls -ld /shared/project
# drwxrwsr-x (s in group execute position = SGID)
Sticky Bit
On directories: users can only delete or rename files they own, even if they have write permission on the directory. This is why /tmp is world-writable but users cannot delete each other's temporary files.
# Set sticky bit:
# chmod o+t /tmp
# chmod 1777 /tmp
# Verify:
# ls -ld /tmp
# drwxrwxrwt (t in others execute position = sticky)
ACL — Access Control Lists
Standard Unix permissions (owner/group/others) are sometimes too coarse. ACLs allow you to grant specific permissions to specific users or groups without changing ownership.
# Check ACL support:
# tune2fs -l /dev/sda1 | grep "Default mount options" # ext4
# xfs_info /data | grep "acl" # xfs
# Set ACL for specific user:
# setfacl -m u:raju:rwx /shared/file
# Set ACL for specific group:
# setfacl -m g:oracle:rw /shared/file
# Set default ACL (applied to new files in directory):
# setfacl -dm u:raju:rwx /shared/
# View ACLs:
# getfacl /shared/file
# ls -l file # + sign at end means ACL is set
# Remove ACL:
# setfacl -x u:raju /shared/file
# setfacl -b /shared/file # remove all ACLs
Recovering the Root Password
RHEL 6 Method
- Reboot, select boot entry, press e
- Select the kernel line, press e
- Add a space and
1(single user mode) at end, press Enter - Press b to boot
- At root prompt:
passwd root
RHEL 7/8 Method
- Reboot, select boot entry, press e
- Find the
linux16line, press End - Add:
rd.break console=tty1 selinux=0 - Press Ctrl+X to boot
- At
switch_root:/#:mount -o remount,rw /sysroot chroot /sysrootpasswd root, set new passwordexit,exit(or reboot)