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:
0is root, read and write permission checks do not apply to it;- 100 through 999 are system users for services;
- 1000 and up are regular users, people.
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:
- sudo skips files whose name contains a dot or ends in
~. A file namedci.confis silently ignored; you need a name with no dot; - the file must be owned by root and not writable by everyone. For a file
with
666permissions, sudo printsis world writableand does not apply its rules. The right permissions are440; - the rule needs the command's full path, and it has to match exactly what actually runs.
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
usermod -Gwithout-a. The user falls out of every other supplementary group. Ifsudowas among them, you might have just taken away your own admin rights.- "Added to the group, but it's not working." Groups take effect at
the next login. Log out and back in, then check with
id. useraddwithout-s. On Ubuntu the user gets/bin/shinstead of bash.- A file in
sudoers.dwith a dot in its name.ci.confis not read, andvisudo -cwon't even list it. - Editing
/etc/sudoerswith a regular editor. A syntax error withoutvisudochecking it can break sudo entirely. - A service account in the
sudoordockergroup. That's root on the server, just with an extra step.
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
- Enter the container with
docker exec -it <name> bashand leave withexit. - Look at users and groups:
id,grep <name> /etc/passwd,getent group <group>,ls -ld <folder>. - Create and change users:
useradd -m -s,usermod -aG,gpasswd -d. - Run a command as another user:
su <name> -c '<command>'. - For sudo:
sudo -l -U <name>,visudo -c,visudo -f <file>, the "Where the rules live" and "How to edit and check" sections.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
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.
Tell the agent: use MCP izi, give me lesson linux-tools-01-users-groups
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress