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:
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:
# 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
# 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.