Linux Fundamentals

Users, Groups & sudo

18 min Lesson 5 of 26

Users, Groups & sudo

Every process, file, and network connection on a Linux system runs under an identity. That identity determines what it can read, write, and execute. Understanding Linux's user and group model is not optional background knowledge for a DevOps engineer — it is the foundation of least-privilege security, the reason automated CI runners don't run as root, and the first thing an auditor checks after a breach. This lesson teaches the model the way production engineers think about it: mechanically precise and security-first.

Key idea: Linux access control is entirely identity-based. The kernel does not care about passwords or certificates at runtime — it cares about the numeric user ID (UID) and group IDs (GIDs) attached to a process. Every permission decision reduces to: "does this UID/GID combination have the right bits set on this inode?"

The /etc/passwd File

Despite its name, /etc/passwd no longer stores passwords (those moved to the shadow file decades ago). It stores the seven fields that define every user account on the system. Read it directly — it is world-readable and no tool hides its structure.

# Read the passwd file cat /etc/passwd # Typical lines (format: login:x:UID:GID:comment:home:shell) root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash deploy:x:1001:1001:Deploy service account:/home/deploy:/bin/bash # Dissect one line # Field 1 login name : deploy # Field 2 password marker : x (real hash is in /etc/shadow, root-only) # Field 3 UID : 1001 # Field 4 primary GID : 1001 # Field 5 GECOS comment : Deploy service account # Field 6 home directory : /home/deploy # Field 7 login shell : /bin/bash # /usr/sbin/nologin = cannot open interactive shell

The UID ranges you will encounter on every Linux distribution:

  • UID 0root. One account, unlimited power. The kernel bypasses permission checks for UID 0.
  • UID 1–999 — System accounts. Created by package managers for daemons (www-data, postgres, redis). They have /usr/sbin/nologin as their shell — no one can log in as them interactively.
  • UID 1000+ — Human users. First human account is conventionally 1000 on Debian/Ubuntu, 1000 on RHEL/Amazon Linux.
Production practice: Every application daemon should run as its own dedicated system account with no login shell and a home directory it owns. A web server running as www-data can only read files world-readable or owned by www-data. A compromised www-data process cannot touch /home/ubuntu or read other services' secrets. This is the principle of least privilege applied to process identity.

Groups — Sharing Access Without Sharing Identity

A group is a named set of users that can be granted permissions collectively. Every file has an owner (a UID) and an owning group (a GID). The permission bits are split across three classes: owner, group, and other.

Group membership is stored in /etc/group:

# /etc/group format: groupname:x:GID:member1,member2,... cat /etc/group | grep -E 'docker|sudo|deploy' # Typical output sudo:x:27:ubuntu,alice docker:x:999:ubuntu,deploy deploy:x:1001: # Check your own groups id # uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),27(sudo),999(docker) # Check groups for another user id deploy # uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),999(docker) # Add a user to a group (takes effect on next login) sudo usermod -aG docker deploy # Add a user to multiple groups at once sudo usermod -aG docker,deploy alice

The most operationally important groups in a typical DevOps environment:

  • sudo (Debian/Ubuntu) / wheel (RHEL/Amazon Linux) — grants sudo access
  • docker — allows running Docker commands without sudo (be careful: this is effectively root on the host)
  • adm — allows reading system log files in /var/log
  • www-data — used by nginx/Apache; deploy scripts often add the deploy user here to write to web roots
Linux user, group, and file permission model alice (UID 1002) primary GID 1002 deploy (UID 1001) primary GID 1001 www-data (UID 33) system account docker (GID 999) alice, deploy www-data (GID 33) deploy, www-data /var/run/docker.sock owner: root group: docker perms: srw-rw---- /var/www/html/ owner: deploy group: www-data perms: rwxrwsr-x How a kernel access check works: 1. Is process UID == file owner UID? Use owner bits. 2. Else: is any process GID == file GID? Use group bits. 3. Else: use other bits. 4. UID 0 bypasses all three steps.
How Linux resolves access: process identity (UID + GIDs) checked against file owner, group, and other permission bits in order.

Creating and Managing Users

On production servers you will create two kinds of accounts: human operator accounts and service accounts. The commands differ slightly between them.

# ── Human operator account ────────────────────────────────────────────────── # useradd creates the account; -m creates /home/alice; -s sets the shell sudo useradd -m -s /bin/bash -c "Alice Smith, SRE" alice # Set a password (prompts interactively) sudo passwd alice # Or set an expired password to force change on first login (more secure) sudo chage -d 0 alice # ── Service / daemon account ───────────────────────────────────────────────── # --system: UID in system range (1-999); no home dir by default; nologin shell sudo useradd --system --no-create-home --shell /usr/sbin/nologin prometheus # Verify id prometheus # uid=997(prometheus) gid=997(prometheus) groups=997(prometheus) # ── Modify an existing account ──────────────────────────────────────────────── sudo usermod -c "Alice Smith, Staff SRE" alice # update comment sudo usermod -s /bin/zsh alice # change shell sudo usermod -L alice # Lock account (disable login) sudo usermod -U alice # Unlock account # ── Delete an account ──────────────────────────────────────────────────────── sudo userdel alice # remove account, leave home dir sudo userdel -r alice # remove account AND home directory

The Root Account and the sudo Model

UID 0 (root) is the superuser. The Linux kernel grants it unconditional access to every file, every process, every device. Direct root login is almost universally disabled on production servers — the root password is unknown, and SSH PermitRootLogin is set to no. Instead, access is mediated through sudo.

su vs sudo:

  • su - root — opens a root shell. Requires knowing the root password. Leaves no per-command audit trail. Strongly discouraged in modern production.
  • sudo command — elevates a single command. Requires your own password. Every invocation is logged to /var/log/auth.log (or journald) with the calling user, the command, and the working directory. This is the model all major cloud providers use.
Production pitfall: Never add a service account to the sudo group. A CI runner, a deploy user, or an application process should accomplish its work through file permissions and group membership, not elevated privileges. If your deploy script needs to restart nginx, give it a specific sudoers entry for that one command — not unrestricted sudo. An attacker who compromises that process should find a narrow blast radius.

Configuring sudo with /etc/sudoers

The sudo policy lives in /etc/sudoers and the directory /etc/sudoers.d/. Always edit with visudo — it validates syntax before saving, preventing a malformed file from locking you out.

# NEVER edit /etc/sudoers directly — always use visudo sudo visudo # /etc/sudoers syntax: # WHO WHERE=(AS_WHOM) WHAT # Full sudo for the sudo group (Ubuntu default) %sudo ALL=(ALL:ALL) ALL # Allow deploy user to restart nginx and reload systemd — nothing else deploy ALL=(root) NOPASSWD: /bin/systemctl restart nginx, /bin/systemctl reload nginx # Allow alice to run any command but she must enter her password alice ALL=(ALL) ALL # Prefer drop-in files to avoid editing the main sudoers file # Create /etc/sudoers.d/deploy (chmod 440, owned root:root) sudo tee /etc/sudoers.d/deploy <<'EOF' deploy ALL=(root) NOPASSWD: /bin/systemctl restart nginx, /bin/systemctl reload nginx EOF sudo chmod 440 /etc/sudoers.d/deploy # Audit: list what sudo allows for a given user sudo -l -U deploy # Matching Defaults entries for deploy on server1: # env_reset, mail_badpass # User deploy may run the following commands on server1: # (root) NOPASSWD: /bin/systemctl restart nginx # (root) NOPASSWD: /bin/systemctl reload nginx

sudo Audit Trail

Every sudo invocation is logged. On systemd distributions the authoritative source is the journal; the traditional file is /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (RHEL). At big-tech scale these logs are forwarded to a SIEM (Splunk, Elastic, Chronicle) and alerted on for anomalies — unexpected root commands during off-hours, commands from a service account, or failed sudo attempts (password spraying).

# View recent sudo activity from journald journalctl _COMM=sudo --since "24 hours ago" --no-pager # Typical successful entry: # Jun 10 14:32:11 prod-01 sudo[12345]: alice : TTY=pts/0 ; PWD=/home/alice ; # USER=root ; COMMAND=/bin/systemctl restart nginx # Typical failed entry (wrong password or not in sudoers): # Jun 10 14:33:07 prod-01 sudo[12346]: bob : user NOT in sudoers ; # TTY=pts/1 ; PWD=/home/bob ; USER=root ; COMMAND=/bin/bash # From auth.log grep sudo /var/log/auth.log | tail -20
Pro practice — passwordless sudo for automation: CI/CD pipelines and Ansible playbooks need to run privileged commands unattended. The correct approach is a dedicated service account with NOPASSWD sudo rights scoped to exactly the commands it needs. Never NOPASSWD: ALL — that is equivalent to handing out root access unconditionally. Combine with SSH key-only authentication and no interactive shell (/usr/sbin/nologin) for the tightest possible automation account.

Switching Users and Running as Another Identity

Understanding the difference between su, sudo -i, and sudo -u matters when debugging permission issues on production:

  • sudo -i — opens a root login shell (loads root's environment). Use sparingly; prefer per-command sudo.
  • sudo -u deploy command — run a single command as the deploy user. Essential for testing "what can this service account actually do?"
  • sudo -s — opens a root shell preserving your current environment. Useful for diagnosing environment-variable issues.
  • su - deploy — switch to the deploy user with a full login. Requires knowing deploy's password or having root. Used less often now that sudo -u is available.

In Lesson 6 you will see how these identities and the permission bits on files interlock — the rwxr-xr-x strings you have seen in ls -l output will finally make complete mechanical sense once you understand the user model you have built here.