Sign in
← Linux: tools
11 min · easy · Lesson 1 of 4

Users, groups, and sudo

How users and groups work in Linux, where they are stored, and how sudo grants precise permissions without handing out root.

A new developer needs access to the server. A service needs a user to run under. A deploy script needs to restart the application, but nobody wants to give it the root password. All three problems are solved with users, groups, and sudo.

Where to practice

All examples below are for an ubuntu:24.04 container. Start one the same way:

docker run --rm -it ubuntu:24.04 bash

Inside it you are root, so you can run useradd, usermod, and edit system files without sudo. To check what an ordinary user sees, use su <name> -c '<command>': it runs one command as that user. After exit the container is deleted along with all changes.

sudo is not installed in the ubuntu:24.04 image. You can repeat its examples in lab 2 of this lesson, where the lab environment is built with sudo.

Users and their id

In the lesson about permissions you saw that file access is decided by a numeric uid. That uid belongs to a user. Every user has a name, a uid, a primary group, a home directory, and a shell. id shows your own:

id
uid=0(root) gid=0(root) groups=0(root)

/etc/passwd stores every user, one line per user:

grep ubuntu /etc/passwd
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash

Fields separated by colons: name, x, uid, primary group's gid, description, home directory, shell. x means the password hash is not here. It lives in /etc/shadow, and only root and the shadow group can read that file:

ls -l /etc/passwd /etc/shadow
-rw-r--r-- 1 root root   888 Sep 17 02:17 /etc/passwd
-rw-r----- 1 root shadow 502 Sep 17 02:17 /etc/shadow

/etc/passwd is world-readable: programs use it to turn a uid into a name, for example in ls -l output.

On macOS, regular users are not in /etc/passwd; Open Directory stores them instead. id works the same way there, and the file itself is only used in single-user mode.

System and regular users

Users are split by uid:

The boundaries come from /etc/login.defs (in Ubuntu 24.04, UID_MIN 1000, with the default system range 100-999). System users exist so that a service doesn't run as root. On Debian and Ubuntu, the packaged nginx runs as www-data, postgres as postgres. These users usually have /usr/sbin/nologin as their shell: you cannot log in as them interactively.

grep www-data /etc/passwd
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

Create and delete a user

useradd -m -s /bin/bash alice

-m creates the home directory /home/alice, -s sets the shell. Without -s, Ubuntu takes the value from /etc/default/useradd, where SHELL=/bin/sh. The user gets a stripped-down shell with no history and no autocomplete, and that's a common cause of "nothing works in my terminal" complaints.

grep alice /etc/passwd
alice:x:1001:1001::/home/alice:/bin/bash

Uid 1001, because 1000 is already taken by the ubuntu user in the image. Along with the user, a same-named group alice was created (gid 1001), and it became the primary group.

Other commands:

passwd alice                 # set the password, asks for it twice
useradd -r -s /usr/sbin/nologin app   # -r: system user, uid below 1000
userdel -r alice             # -r: delete along with the home directory

Debian and Ubuntu also have adduser alice (not present in the minimal ubuntu:24.04 image). It's a wrapper that creates the home directory itself, sets up bash, and asks for a password. Scripts and Dockerfiles usually use useradd with explicit flags instead: it works the same way across distributions.

Groups

A group lets you grant permissions to several users at once. The g permission on a file from the permissions lesson applies to members of its group. A user has one primary group (its gid is in /etc/passwd) and any number of supplementary groups. /etc/group stores the list of groups:

grep sudo /etc/group
sudo:x:27:ubuntu

Format: group name, x, gid, comma-separated list of supplementary members.

To see a user's groups:

id alice
uid=1001(alice) gid=1001(alice) groups=1001(alice),1003(docker),1004(devs)

Create a group and add a user to it:

groupadd devs
usermod -aG devs alice

The -a (append) flag is the important part here. usermod -G docker alice without -a replaces the entire supplementary group list with just docker:

uid=1001(alice) gid=1001(alice) groups=1001(alice),1003(docker)

The devs group is gone, and with it access to every file that group owns. The primary group stays. You can remove a user from one group without touching the rest with gpasswd -d alice devs.

A new group only takes effect at the next login. A process gets its group list at login time, and an already-open session does not re-read it. Hence the classic: you added yourself to docker, but docker ps still says permission denied. You need to log out and back in (or open a new session with su - alice).

Membership in some groups is equivalent to granting major privileges. The sudo group lets you run any command as root. The docker group grants access to the Docker daemon, and Docker's own documentation calls it root-equivalent: through it you can run a container that mounts the host's /. Add users to groups like these as carefully as you'd hand out the root password.

sudo: pinpoint root

Handing out the root password is bad: nobody can tell who did what, and you can't revoke access from one person. sudo lets a regular user run a command as root without knowing the root password, and only if the rules allow it.

deploy@srv:~$ sudo systemctl restart nginx
[sudo] password for deploy:

sudo asks for the user's own password, not root's. Every sudo call is logged (on Ubuntu with rsyslog that's /var/log/auth.log, on journald-only systems check journalctl). So after an incident you can see who ran what as root.

Where the rules live

The main file is /etc/sudoers. In Ubuntu 24.04 it includes these lines:

root	ALL=(ALL:ALL) ALL
%sudo	ALL=(ALL:ALL) ALL
@includedir /etc/sudoers.d

Rule format: who, on which host, as whom, which commands.

ci  ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
│   │   │      │         └ command with arguments, full path
│   │   │      └ don't ask for a password
│   │   └ run as root
│   └ on any host
└ user (or %group)

The line %sudo ALL=(ALL:ALL) ALL means: members of the sudo group can do anything. On RHEL, Rocky, and Fedora the wheel group plays the same role.

@includedir /etc/sudoers.d loads every file in that folder. Put your own rules there as a separate file rather than editing the main file: a package update can overwrite it.

Rules about files in sudoers.d that often trip people up:

How to edit and check

Edit sudoers through visudo: it opens the file in an editor and checks the syntax before saving. A broken /etc/sudoers can leave the server without sudo at all. A separate file opens like this:

visudo -f /etc/sudoers.d/ci

Check every file at once:

visudo -c
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/ci: parsed OK

Notice which files are listed. If the one you need isn't there, sudo isn't reading it.

sudo -l shows what a specific user is allowed to do. Your own permissions: sudo -l, someone else's (as root): sudo -l -U ci.

User ci may run the following commands on srv:
    (root) NOPASSWD: /usr/bin/systemctl restart nginx

The -n (non-interactive) flag stops sudo from asking for a password. If it can't run without one, the command fails right away with sudo: a password is required. In scripts and CI that's better than hanging on a password prompt.

Least privilege

The rule above allows ci exactly one command, no password. That's the principle of least privilege: grant exactly what's needed for the job, not all of root. Adding the deploy user to the sudo group "to make it work" is easier, but then it can do anything, and any vulnerability in the deploy process becomes root access to the whole server.

Another gap: a command allowed through sudo must itself be protected from being written to. If a user can edit a script that sudo lets them run, they can write anything into it and run it as root. Scripts referenced from sudoers should be owned by root with permissions like 755.

Common mistakes

What to remember

A user is a name, a uid, a primary group, a home directory, and a shell. All of that is in /etc/passwd; password hashes live separately in /etc/shadow. Services run as system users, not as root.

Groups grant permissions to several users at once. Add with usermod -aG, always with -a. Membership in sudo or docker is equivalent to granting root.

sudo gives pinpoint root access based on rules in /etc/sudoers and /etc/sudoers.d, asks for the user's own password, and logs everything. Check rules with visudo -c and sudo -l.

In the next lesson we'll cover networking on the host itself: ip, ss, and routes.

What you'll need for the tasks

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson linux-tools-01-users-groups