1. Overview
In this tutorial, we’ll learn about the user ID values in Linux. Specifically, we’ll learn about the different ranges of reserved user IDs and their purpose.
2. User IDs in Linux
The user represents an individual or an entity that interacts with the system. One of the purposes for having the concept of users in the system is to manage isolation for processes and resources. For example, we can set the owner of files to user bob and prevents user alice from being able to read and write onto these files, achieving isolation. In Linux, every user in the system has an associated user ID, which is an integer value. The user ID is an important aspect of the Linux system because the system grants permissions by the user ID and not the name.
To check on the existing users and their respective user IDs, we can read the /etc/passwd file using the cat command:
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
...
Each row of the file displays a single user entity in the system. The first column indicates the user name associated with the user, and the third column shows the user ID.
2.1. User ID Starts With 1000
There’s a curious phenomenon in Linux whereby new users that we add to the system using useradd or similar commands seem to always start with a user ID of 1000. Then, it adds one for each subsequent new user.
For example, we can add a user to our system using the useradd command:
$ sudo useradd bob
Then, we can check the user ID of the user bob by reading the /etc/passwd file:
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
bob:x:1000:1000::/home/bob:/bin/sh
From the output, we can see that the user bob has a user ID value of 1000. So what happens to the user IDs before 1000? It turns out that, by convention, user IDs smaller than 1000 are reserved for system users.
2.2. User IDs Range Convention
The Linux Standard Base Core Specification defines three different ranges for user IDs, the value from 0 to 99, 100 to 999, and 1000 and above. Out of these three ranges, the first two ranges are reserved for system users. Therefore, for all the regular users we create, it takes up value in the third range, starting from 1000.
The main reason for reserving the integer value less than 1000 is for the possible future expansion of system users. Imagine that only our system only reserves the user ID from 0 to 10. Then the Linux developer cannot statically assign the user ID 11 to new system users in the future since there’s no guarantee that the user ID is available.
Do note that the reservation is a convention only. Therefore, it’s possible to create a normal user account with a user ID with a value of 0 to 999, though it’s not recommended.
3. The System Users
The first range of user ID consists of the value from 0 to 99. These user IDs are reserved for the system users, and importantly, the user IDs are statically assigned. In other words, the system creates these users and will always have the same user ID. For example, in Linux, there’s a bin user with a user ID of 2, which will always be the case as long as the Linux instance conforms to the Linux standard specification.
One characteristic of the system users is that they do not have a login shell. If we inspect the /etc/passwd files, we can see that those system users with a user ID less than 100 will have their shell set as /usr/sbin/nologin. This is because those users serve for system usage and typically do not undergo a typical login process like regular user accounts.
The root user is a special case in that it has a login shell, despite its user ID belonging to the range of system user IDs. This is because, unlike the rest, the root user is expected to undergo a normal login process most of the time.
4. The Application Users
The application users are system users that application programs create. The application programs create these users mostly as part of the installation process and use them to run the program and to own the resources used by the program. Despite being in the range of reserved values of 100 to 999, the user IDs are dynamically allocated. In other words, the same user name might assume a different user ID depending on the available user ID in the range at the time of creation.
One main benefit of using specific users for different programs is that we greatly reduce the surface of the attack in the event of any vulnerability escaping from the applications. For example, we run the PostgreSQL instance in our system using the postgres user. Then, we restrict this postgres user to resources that are necessary for the PostgreSQL instance. When the process is compromised, we can reduce the blast radius to other processes and resources on the system.
To demonstrate the application user creation, we can install the PostgreSQL package in Linux. For example, we can run apt-get install postgresql in Ubuntu Linux:
$ sudo apt-get install -y postgresql
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
bob:x:1000:1000::/home/bob:/bin/sh
postgres:x:101:103:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
After the installation, we can see a postgres user entry in the /etc/passwd file. Furthermore, we can see that the postgres application user has a user ID of 101, which is within the range of the application system user IDs.
4.1. Dynamic User ID Allocation
The dynamic allocation nature of this range of user IDs means that the user creation process will always create the user with the next available user ID in the range. For example, let’s start with a fresh Ubuntu Linux installation and create a user with user ID 101:
$ sudo useradd -u 101 bob
Then, we install the PostgreSQL package using apt-get install postgresql and check the user ID of the postgres user:
$ sudo apt-get install -y postgresql
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
bob:x:101:1000::/home/bob:/bin/sh
postgres:x:102:103:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
As we can see, the system assigns the postgres application user with the user ID 102. This is because, according to the /etc/passwd file, the user ID 102 is the next available value in the range.
5. The Regular Users
For regular user accounts, the user ID typically starts from 1000. Besides that, the /etc/login.defs file is the file that defines the range of regular users’ IDs. Commands such as useradd read the file to decide what user ID to assign the user during creation. Furthermore, there’s a restriction to the upper limits of this class of user IDs depending on several factors.
The first factor that affects the upper limit of the regular user IDs is the version of the Linux kernel. For Linux kernel version 2.4 and before, the maximum value is 65535, which is the maximum value of a 16-bit integer. After the Linux kernel version 2.4, the upper limit of this value has expanded to 4,294,967,295, which is 32 bits. Additionally, some other factors also cause the upper limit to be lower than the integer bit size.
For example, Debian Linux reserves the range of 60000 to 65533 for internal usage. Besides that, the systemd process also reserves user IDs in the range of 60001 to 65519 for home directories managed by the systemd-homed process.
6. Conclusion
In this tutorial, we’ve looked at the general concept of user IDs in Linux. Then, we’ve also learned that by convention, there’re three different groups of user ID ranges in the Linux standard. Firstly, the user ID ranging from 0 to 99 are reserved for system accounts, and they are statically assigned. Then, the user ID from 100 to 999 is dynamically assigned for application users. Finally, we’ve seen that for regular user accounts, the user ID, by convention, starts from 1000.