Skip to content

107.1 Manage user and group accounts and related system files

Weight: 5

Candidates should be able to add, remove, suspend and change user accounts.

Objectives

  • Add, modify and remove users and groups.
  • Manage user/group info in password/group databases.
  • Create and manage special purpose and limited accounts.

Terms

/etc/passwd, /etc/shadow, /etc/group, /etc/skel/, chage, getent, groupadd, groupdel, groupmod, passwd, useradd, userdel, usermod

Users and groups

Linux is a multi-user system, and managing accounts is a core part of an administrator's job. Every user has a name and a number, the UID (user ID). Users are collected in groups, each with a name and a GID (group ID). You give permissions to a group, then add the users who need them. For example, the group cdrom may read /dev/cdrom, and you add to it whoever needs the CD drive.

  • A user can belong to many groups, but exactly one of them is the user's primary group. The others are secondary (or supplementary) groups.
  • A file belongs to one user and one group.
  • Accounts created by the administrator usually get IDs from 1000 up: 1000, 1001, 1002 and so on. Lower numbers belong to the system.

Desktops have graphical tools for this, but the command line tools below do everything, and they are what the exam asks about. Almost all of them need root.

Adding users: useradd

useradd creates an account. It updates the user and group databases, creates a group with the same name as the user, and, if asked, creates the home directory:

# useradd michael
# passwd michael
Changing password for user michael.
New UNIX password:
Retype new UNIX password:
passwd: all authentication tokens updated successfully.
# id michael
uid=1000(michael) gid=100(michael) groups=100(michael)
# groups michael
michael : michael

A new account has no password until you set one with passwd. id shows the UID, the primary GID and all the groups, and groups shows just the group names. Without a name, both show your own account.

The main useradd options:

Option Sets
-m create the home directory (if it does not exist)
-M do not create the home directory
-d /home/dir a custom home directory
-s /bin/bash the login shell
-c "Full Name" a comment, usually the full name. Quote it if it has spaces
-u 1500 a specific UID
-g group the primary group (GID)
-G group1,group2 the secondary groups
-e 2026-12-31 the date the account is disabled
-f 14 the days after the password expires before the account is disabled
-k /dir copy the starting files from this directory instead of /etc/skel (only with -m)

On some systems useradd creates the home directory by itself, on others only with -m, so it is good practice to always give -m. The groups you name with -g and -G must already exist. A fuller example:

# useradd -m -d /home/michael -s /bin/bash -c "Michael User Account" -G developer michael
# useradd -m -d /home/nagato -s /bin/bash -c "Nagato the geek" -G sudo nagato

adduser is a friendlier alternative on some distributions (Debian): it asks for the password, full name and the rest, step by step. addgroup is its group counterpart.

The skeleton directory: /etc/skel

When the home directory is created, it is filled with a copy of /etc/skel/. So every new user starts with the same files, such as .bashrc and .profile. To give all new users a file or a directory, put it in /etc/skel. The files start with a dot, so list them with ls -al /etc/skel. (105.1 shows this in action.)

Modifying users: usermod

usermod changes an existing account. It accepts most of the useradd options, plus some of its own:

# usermod -s /bin/tcsh michael
# usermod -s /bin/csh nagato
# usermod -c "Michael User Account" michael
Option Changes
-s /bin/tcsh the login shell
-c "text" the comment
-d /new/home the home directory. With -m, the files are moved there too
-l newname the login name
-u 1501 the UID
-g group the primary group (it must exist)
-G group1,group2 the secondary groups. Replaces the current list
-aG group adds secondary groups, keeping the current ones
-e 2026-12-31 the account expiry date
-f 14 the inactive days after password expiry
-L locks the account: puts ! in front of the password in /etc/shadow
-U unlocks it: removes the !

The -G versus -aG trap is a classic exam question:

# usermod -G wheel,users nagato      # nagato is now in wheel and users ONLY
# usermod -aG wheel nagato           # nagato keeps all groups and joins wheel too

-G alone means "nagato's groups are exactly these". -aG means "add nagato to these groups as well". Group names are separated by commas, with no spaces.

Some follow-up work is yours: after renaming a user with -l, you probably want to rename the home directory and mail spool too. After changing the UID with -u, files inside the home directory are fixed automatically, but files elsewhere keep the old number and must be fixed by hand.

Deleting users: userdel

# userdel michael          # remove the account, keep the files
# userdel -r michael       # also remove the home directory and mail spool

Files the user owned elsewhere on the system are not touched by -r; you have to find and remove them yourself.

Groups: groupadd, groupmod, groupdel

The group tools work like the user tools:

# groupadd -g 1090 developer                       # new group with GID 1090
# groupmod -n web-developer -g 1050 developer      # rename it and change its GID
# groupdel web-developer                           # delete it
Command Option Does
groupadd -g GID creates a group, with a chosen GID
groupmod -n newname renames a group
groupmod -g GID changes its GID
groupdel deletes a group

Things to remember:

  • You cannot delete a group that is some user's primary group. Remove or change that user first.
  • Deleting a group does not delete its members. They just stop being members.
  • Files owned by a deleted group stay where they are, still showing the old GID. The same happens to files when you change a group's GID with groupmod -g, so fix them afterwards.

Passwords: passwd

Every user can change their own password with passwd. It asks for the current password first, to be sure it is really you, and may refuse a new password that is too short, too like the old one, a dictionary word, or the same as the user name:

$ passwd
Changing password for nagato.
(current) UNIX password:
New password:
Retype new password:
passwd: password updated successfully

Root can change anyone's password, without knowing the old one. The quality checks only warn root:

# passwd nagato
New password:
BAD PASSWORD: it does not contain enough DIFFERENT characters
BAD PASSWORD: is too simple
Retype new password:
passwd: password updated successfully

How can a normal user write to /etc/shadow, which only root may change? passwd has the SUID bit (the s in the owner's permissions), so it always runs with its owner's rights, which are root's:

# ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 42096 May 17  2015 /usr/bin/passwd

Root can also manage password aging with passwd:

Option Does
-l user lock the account (adds ! before the password in /etc/shadow)
-u user unlock it
-d user delete the password
-e user expire it now, so the user must choose a new one at next login
-S user show the password status
-n days minimum days between changes
-x days maximum days a password is valid
-w days days of warning before it expires
-i days days after expiry before the account is disabled

Group passwords

Groups can have passwords too, set with gpasswd. A user who is not a member but knows the password can join the group for the current session with newgrp. gpasswd also adds and removes group members and sets the group's administrators. In practice group passwords are rarely used.

Password aging: chage

chage ("change age") reads and changes the password aging settings. Only root can change them, but any user can list their own with -l:

# chage -l nagato
Last password change                               : Apr 26, 2023
Password expires                                   : never
Password inactive                                  : never
Account expires                                    : never
Minimum number of days between password change     : 0
Maximum number of days between password change     : 99999
Number of days of warning before password expires  : 7

chage nagato with no options asks for every value one by one. Or set them directly:

Option Sets
-l list the settings
-d the date of the last password change. -d 0 forces a change at the next login
-m minimum days between changes
-M maximum days a password is valid
-W days of warning before expiry
-I inactive days after expiry before the account is disabled
-E the account expiry date, as YYYY-MM-DD, or -1 for never
# chage -M 90 -W 7 nagato        # password valid for 90 days, warn 7 days before
# chage -E 2026-12-31 nagato     # the account stops working after this date

The account files

The commands above all work on four plain-text files in /etc/. You can read them, but do not edit them by hand: use the tools.

File Fields Holds Readable by
/etc/passwd 7 user accounts everyone
/etc/shadow 9 users' encrypted passwords and aging root only
/etc/group 4 groups everyone
/etc/gshadow 4 groups' encrypted passwords and admins root only
# ls -l /etc/passwd /etc/shadow
-rw-r--r-- 1 root root   1.9K Oct 28 15:47 /etc/passwd
-rw-r----- 1 root shadow  851 Oct 29 19:06 /etc/shadow

/etc/passwd

One user per line, seven fields separated by colons:

emma:x:1020:1020:User Emma:/home/emma:/bin/bash
 |   |   |    |      |         |          |
 |   |   |    |      |         |          shell
 |   |   |    |      |         home directory
 |   |   |    |      GECOS: comment, usually the full name
 |   |   |    primary GID
 |   |   UID
 |   password: x means "look in /etc/shadow"
 user name

Why the x? Everyone must be able to read /etc/passwd (programs need it to show user names), so it is a bad place for passwords, even encrypted ones. The real password hash lives in /etc/shadow, which only root can read. The GECOS field can hold several entries, separated by commas.

$ tail /etc/passwd
sshd:x:493:491:SSH daemon:/var/lib/sshd:/bin/false
statd:x:488:65534:NFS statd daemon:/var/lib/nfs:/sbin/nologin
lightdm:x:10:14:Light Display Manager:/var/lib/lightdm:/bin/false
wwwrun:x:30:8:WWW daemon apache:/var/lib/wwwrun:/bin/false
nagato:x:1000:100:nagato:/home/nagato:/bin/bash
privoxy:x:484:480:Daemon user for privoxy:/var/lib/privoxy:/bin/false

Special purpose and limited accounts

Most lines in /etc/passwd are not people. They are system accounts, used by services such as sshd, lightdm or a web server. They have low UIDs, and their shell is /bin/false or /sbin/nologin, so nobody can log in with them. Attackers used to try to log in through accounts like these, and the dummy shell blocks that.

To limit an account, the same tools apply:

Goal How
no interactive login set the shell to /sbin/nologin or /bin/false (usermod -s)
temporarily block a user lock the password with usermod -L or passwd -l
an account that ends on a date useradd -e or chage -E
force a password change passwd -e or chage -d 0

/etc/shadow

One user per line, nine fields. Dates are counted in days since 1 January 1970:

nagato:$6$enk5I3bv$uSQrRpen7m9...:16737:0:99999:7:::
   |          |                     |   |    |   | | | |
   |          |                     |   |    |   | | | reserved
   |          |                     |   |    |   | | account expiry date (empty = never)
   |          |                     |   |    |   | inactive days after expiry
   |          |                     |   |    |   warning days before expiry
   |          |                     |   |    maximum days (99999 = practically never)
   |          |                     |   minimum days between changes
   |          |                     date of the last change (0 = must change at next login)
   |          encrypted password
   user name
Field Meaning
1 user name
2 encrypted password. A leading ! means the account is locked. * or ! alone means no password login is possible. Some systems (like Red Hat) show !! for an account that has never had a password set
3 date of the last password change. 0 forces a change at the next login
4 minimum days before the password may be changed again
5 maximum days before the password must be changed
6 days of warning before the password expires
7 days after expiry during which the user can still change it; after that the account is disabled
8 the date the account is disabled. Empty means never
9 reserved for future use
# tail /etc/shadow
sshd:!:16369::::::
uucp:*:16369::::::
lightdm:*:16369::::::
nagato:$6$enk5I3bv$uSQrRpen7m9xDapYLgwgh3P/71OLZUgj31n8AwzgIM2lA5Hc/BmRVAMC0eswdBGkseuXSvmaz0lsYFtduvuqUo:16737:0:99999:7:::

Only the real user has a password hash. The system accounts have ! or *: no password login.

/etc/group

One group per line, four fields:

db-admin:x:1050:grace,frank
   |     |   |      |
   |     |   |      members (secondary only)
   |     |   GID
   |     password: x means "look in /etc/gshadow"
   group name

The format is groupname:x:groupid:members. The group password is essentially never used in practice. The member list shows users for whom this is a secondary group. Users whose primary group it is (the GID in their /etc/passwd line) are not listed here.

/etc/gshadow

Root-only, four fields: the group name, the encrypted group password (used with newgrp; a leading ! means nobody may join with newgrp), the group administrators (who can manage it with gpasswd), and the members.

/etc/login.defs: the defaults

/etc/login.defs sets the defaults that useradd, passwd and the other tools use. Check it whenever the tools do something you did not expect:

Directive Sets
UID_MIN, UID_MAX the range of UIDs for new normal users
GID_MIN, GID_MAX the range of GIDs for new normal groups
CREATE_HOME whether a home directory is created by default
USERGROUPS_ENAB whether each new user gets a group with the same name (removed again with the user when it has no members left)
MAIL_DIR the mail spool directory
PASS_MAX_DAYS maximum days a password may be used
PASS_MIN_DAYS minimum days between password changes
PASS_MIN_LEN minimum password length
PASS_WARN_AGE days of warning before a password expires

Looking up users and groups

To search the account files, grep works:

# grep emma /etc/passwd
emma:x:1020:1020:User Emma:/home/emma:/bin/bash
# cat /etc/group | grep db-admin
db-admin:x:1050:grace,frank

The better tool is getent ("get entries"). It reads the databases configured in /etc/nsswitch.conf (passwd, group, shadow, hosts and others), so it finds entries from every source configured there, not only from the local files. That is handy when accounts come from LDAP rather than the flat files. Give it the database and a key:

# getent passwd emma
emma:x:1020:1020:User Emma:/home/emma:/bin/bash
# getent group db-admin
db-admin:x:1050:grace,frank

Without a key, it lists the whole database. getent does not need root, only permission to read the database you ask for, so getent shadow works only as root.

More examples

The same commands once more, with other names and values, as a quick reference:

# userdel nagato
# groupadd -g 1200 newgroup      # create with id 1200
# groupmod -g 2000 newgroup      # change its id
# groupdel newgroup              # delete it
# gpasswd -a nagato newgroup       # add a member (-d removes one)
$ tail -3 /etc/passwd
sshd:x:493:491:SSH daemon:/var/lib/sshd:/bin/false
nagato:x:1000:100:nagato:/home/nagato:/bin/bash
privoxy:x:484:480:Daemon user for privoxy:/var/lib/privoxy:/bin/false
username : x : userid : primary-group-id : name/comment : home dir : shell
$ sudo tail -2 /etc/shadow
lightdm:*:16369::::::
nagato:$6$enk5I3bv$uSQr...maz0:19473:0:99999:7:::
$ tail -3 /etc/group
vboxusers:x:481:
input:x:1000:nagato,joe
privoxy:x:480:
# id nagato
uid=1000(nagato) gid=100(users) groups=1000(input),100(users)
# getent passwd nagato
nagato:x:1000:100:nagato:/home/nagato:/bin/bash
# getent group input
input:x:1000:nagato,joe

Summary

This objective is the full life cycle of accounts. Every account has a user name and UID, one primary group and any number of secondary groups, and normal accounts usually start at UID 1000. I create accounts with useradd (always -m for a home directory seeded from /etc/skel/, plus -d, -s for the shell, -c for the comment and -G for secondary groups), passwd gives it a password, and id and groups show the result. I change accounts with usermod; the trap is that -G replaces the secondary groups while, crucially, -aG adds to them without wiping the others, and -L and -U lock and unlock. userdel -r removes the user with the home directory and mail spool. Groups use groupadd -g, groupmod -n or -g, and groupdel, which refuses to delete someone's primary group.

Users change their own password with passwd and need the old one; root does not. It works because passwd is SUID root, and root can change anyone's password and also lock (-l), unlock (-u) or expire (-e) it. chage manages aging: -l lists it, -M, -m and -W set the maximum, minimum and warning days, -I the inactive days, -E the account expiry and -d 0 forces a change at the next login. Defaults for all of this live in /etc/login.defs.

The data lives in four files I read but never edit by hand. /etc/passwd has seven fields, name, x (pointing at the shadow file), UID, GID, comment, home and shell, and is readable by all. /etc/shadow has nine fields, the password hash and its aging dates counted from 1970, with ! marking a locked account, and only root can read it. /etc/group lists each group's name, GID and secondary members, and /etc/gshadow holds group passwords and administrators. System accounts use /sbin/nologin or /bin/false as their shell so nobody can log in with them, which is how I build limited accounts. To inspect an account I use id, and to look users and groups up I use getent passwd and getent group, which follow /etc/nsswitch.conf.