Linux user management is surprisingly fragile, and the biggest misconception is that useradd and passwd are all you need for secure access.

Let’s watch a user get added and then escalate privileges.

First, we create a user, alice.

sudo useradd -m -s /bin/bash alice
sudo passwd alice

alice can now log in with a password. But to do anything useful, she needs to be in groups. Groups are the fundamental access control mechanism in Linux for resources and commands.

sudo usermod -aG sudo,docker alice

Here, we added alice to the sudo group (allowing her to run commands as root) and the docker group (allowing her to manage Docker containers without root). The -aG flags are crucial: -a for append, -G for supplementary groups. Without -a, she’d replace all existing supplementary groups.

Now, alice can run commands as root using sudo.

sudo -i
# Now running as root

But sudo itself is configured by /etc/sudoers. This file dictates who can run what commands on which hosts, and how. It’s not just about being in the sudo group.

To edit sudoers safely, we use visudo. This locks the file and performs syntax checks.

sudo visudo

A common entry looks like this:

%sudo ALL=(ALL:ALL) ALL

This means members of the sudo group (%sudo) can run any command (ALL) on any host (ALL), as any user ((ALL:ALL) - the first ALL is the effective user, the second is the effective group).

Let’s say we want alice to only be able to restart the web server. We’d add a line like this:

alice ALL=/usr/sbin/service apache2 restart, /usr/sbin/service nginx restart

This is far more granular and secure than just dumping her into the sudo group.

This brings us to PAM (Pluggable Authentication Modules). PAM is the magic behind the scenes that handles authentication, authorization, account management, and session management. When you run login, sudo, sshd, or even passwd, PAM is involved.

The configuration for PAM is in /etc/pam.d/. For example, /etc/pam.d/sudo dictates how sudo authenticates users.

#%PAM-1.0
session    required     pam_env.so readenv=1 user_readenv=0
session    required     pam_env.so readenv=1 user_readenv=1
auth       required     pam_env.so
auth       sufficient   pam_rootok.so
auth       required     pam_group.so use_uid
auth       required     pam_unix.so try_first_pass
auth       optional     pam_sss.so
account    required     pam_unix.so
account    required     pam_group.so use_uid
account    optional     pam_sss.so
password   required     pam_unix.so minlen=4 maxrepeat=999 default=password
password   optional     pam_gnome_keyring.so
session    required     pam_unix.so
session    optional     pam_keyinit.so forceok
session    optional     pam_loginuid.so
session    optional     pam_keyinit.so revoke
session    required     pam_limits.so
session    required     pam_env.so user_readenv=1
session    required     pam_env.so readenv=1 user_readenv=0
session    optional     pam_sss.so -i

This file defines a chain of modules for each type of service (auth, account, password, session). The keywords required, sufficient, optional, and requisite determine how the chain proceeds. sufficient means if this module succeeds, the stack for this service is considered successful, and no more modules of that type are run. required means it must succeed for the service to succeed, but the stack continues even if it fails (the overall service will fail later if any required module fails).

A common security pitfall is misconfiguring PAM, for instance, by making password changes too lax. A line like password requisite pam_cracklib.so retry=3 minlen=8 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1 would enforce stronger passwords than the default pam_unix.so might.

The most surprising thing most people don’t realize is how PAM’s sufficient keyword can short-circuit authentication. If a module marked sufficient succeeds, the rest of the modules for that type (e.g., auth) are skipped entirely. This is why the order of modules in PAM configuration files is critically important. A poorly placed sufficient module, like pam_permit.so (which always succeeds), can effectively disable authentication for that service.

The next problem you’ll likely encounter is managing user home directories and their permissions across different systems or when moving users.

Want structured learning?

Take the full Cdk course →