> For the complete documentation index, see [llms.txt](https://anjia1.gitbook.io/cs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anjia1.gitbook.io/cs/linux/account/username-and-password.md).

# 2.1 用户名和密码

当我们登录 Linux 系统时，需要输入用户名和密码。虽然我们输入的是用户名，但其实 Linux 主机并不直接认识我们的用户名，它只认识 ID，用户名只是为了让人们更容易记住而已。

> <mark style="color:purple;">UID</mark>，User ID，用户 ID\ <mark style="color:purple;">GID</mark>，Group ID，用户组 ID

今天，我们就来了解下 Linux 是如何辨认每个用户的。

## 1. 流程

当我们通过 `ssh` 命令输入用户名和密码的时候，Linux 系统会做以下工作：

1. 先查找 `/etc/passwd` 里是否有输入的用户名
   * 如果没有，则退出
   * 如果有，那就将该用户名对应的 UID 和 GID（在 `/etc/group` 中）取出来，同时也会取出该用户的 home 目录和 shell 设置
2. 核对密码表 `/etc/shadow`
   * Linux 会根据用户名，从 `/etc/shadow` 里找出对应的密码进行校验
   * 如果校验成功，则会进入 shell 管理的阶段

上面提到了两个和用户账号相关的重要文件：

* `/etc/passwd`：管理着用户名、UID 和 GID
* `/etc/shadow`：专门管理着密码

## 2. `/etc/passwd`

### 2.1 文件内容

比如，在阿里云上，运行命令 `cat /etc/passwd`，会显示如下内容：

```shell
## UID = 0, 系统管理员
root:x:0:0:root:/root:/bin/bash

## UID ∈ [1,999], 系统账号
##   由于在系统上启动的网络服务或后台服务，希望使用较小的权限去运行
##      所以，不希望使用 root 的身份来执行这些服务
##      因此，就为运行中的程序提供了这些系统账号
##   这些系统账号通常是不可登录的（即没法使用 bash 或其它 shell 来登录系统）
##      所以才会有 /sbin/nologin 这个特殊的 shell 存在
# 1~200 由 Linux 自行建立的系统账号
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
# 201~999 若用户有系统账号的需求时，可以使用的账号 UID
dockerroot:x:996:993:Docker User:/var/lib/docker:/sbin/nologin
nginx:x:997:995:Nginx web server:/var/lib/nginx:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
polkitd:x:999:997:User for polkitd:/:/sbin/nologin

## UID >= 1000, 一般用户
shareread:x:1000:1000::/home/shareread:/bin/bash
```

### 2.2 文件格式

文件 `/etc/passwd` 里的每一行就代表一个账号，有几行就代表系统中有几个账号。

每行以冒号`:`为分隔符，分为 7 列，如下：

<table><thead><tr><th width="78.33333333333331">列</th><th width="155">含义</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td>账号名称</td><td></td></tr><tr><td>2</td><td>密码</td><td>值都为 x</td></tr><tr><td>3</td><td>UID</td><td>0 是系统管理员<br>1~999 是系统账号，通常是不可登录的<br>1000+ 是可登录的账号，给一般用户用的</td></tr><tr><td>4</td><td>GID</td><td>和 <code>/etc/group</code> 有关，用来规范组名的</td></tr><tr><td>5</td><td>用户信息说明</td><td>一般没啥用<br>但是如果提供了 figure 功能，这个字段可以提供很多信息</td></tr><tr><td>6</td><td>home 目录</td><td>当用户登录成功之后，就会自动进入到 home 目录里</td></tr><tr><td>7</td><td>shell</td><td>当用户登录系统之后，就会获取一个 shell 来和<br>系统的内核进行沟通，以完成用户的操作任务。<br>eg.<br>- <code>/bin/sync</code><br>- <code>/sbin/shutdown</code><br>- <code>/sbin/halt</code><br>- <code>/bin/false</code><br>- <code>/sbin/nologin</code> 为不可登录的系统账号准备的<br>- <code>/bin/bash</code> 可正常登录的用户</td></tr></tbody></table>

如果将一般用户的 UID 改成 0，那他也就具有 root 的权限了。不过，非常不建议这么做。

### 2.3 文件权限

很多程序的运行都和权限有关，而权限又和 UID 和 GID 相关，所以各个程序都要读取 `/etc/passwd` 来了解不同账号的权限，所以它的权限需要设置成 `-rw -r--r--`。

```shell
# ls -l /etc/passwd
-rw-r--r-- 1 root root 1236 Dec 18  2018 /etc/passwd
```

早期的 Unix 系统，密码是存在 `/etc/passwd` 文件里的，虽然也有加密，但因为这个文件的特性是所有的程序都能读取，这就导致密码容易被窃取进而被暴力破解出来，所以后来就将密码移到了 `/etc/shadow` 文件里，该文件的权限是 `-rw-------` 或者 `----------`，即只有 root 才能读写。

```shell
# ls -l /etc/shadow
---------- 1 root root 862 Dec 18  2018 /etc/shadow
```

## 3. `/etc/shadow`

### 3.1 文件内容

比如，在阿里云上，运行命令 `cat /etc/shadow`，会显示如下内容：

```shell
# 系统账号的
bin:*:17110:0:99999:7:::
daemon:*:17110:0:99999:7:::
adm:*:17110:0:99999:7:::
lp:*:17110:0:99999:7:::
sync:*:17110:0:99999:7:::
shutdown:*:17110:0:99999:7:::
halt:*:17110:0:99999:7:::
mail:*:17110:0:99999:7:::
operator:*:17110:0:99999:7:::
games:*:17110:0:99999:7:::
ftp:*:17110:0:99999:7:::
mysql:!!:17624::::::
nscd:!!:17454::::::
ntp:!!:17454::::::
apache:!!:17883::::::
tcpdump:!!:17454::::::
sshd:!!:17454::::::
dbus:!!:17454::::::
postfix:!!:17454::::::
nobody:*:17110:0:99999:7:::
systemd-network:!!:17454::::::

dockerroot:!!:17883::::::
nginx:!!:17624::::::
chrony:!!:17454::::::
polkitd:!!:17454::::::

# root 用户的
root:$6$.PENTCpQ$L55HQMW5tf4Yj4x5Fvls95MyeWI1J5Rp1xbtbsXw0EQhW/X0QBuO1vwGLQ3OqJxdm6py40IJvvPG8yYZQx0Pw0:17623:0:99999:7:::

# 一般用户的
shareread:$6$RqqEr.T/$Ld84B.mtqKmrXdL3KUgLKiujpo.udClGH1JlKr4Nps5R6ZQa/X5a2voE65Qorqmcbm6rl9tkaYo1c3wyEH3rk/:17686:0:99999:7:::
```

### 3.2 文件格式

每行以冒号`:`为分隔符，分为 9 列，如下：

<table><thead><tr><th width="74.33333333333331">列</th><th width="261">含义</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td>账号名称</td><td></td></tr><tr><td>2</td><td>密码</td><td>是经过编码的密码（摘要）</td></tr><tr><td>3</td><td>最近修改密码的日期</td><td>整数，是以1970年1月1日作为1而累加的</td></tr><tr><td>4</td><td>密码不可被修改的天数</td><td>相对于第3个字段<br>若是0则表示密码可以随时修改</td></tr><tr><td>5</td><td>密码需要重新修改的天数</td><td>相对于第3个字段<br>如果是99999(即273年)则表示不强制</td></tr><tr><td>6</td><td>密码需要修改期限前的警告天数</td><td>相对于第5个字段</td></tr><tr><td>7</td><td>密码过期后的账号宽限时间</td><td>相对于第5个字段<br><br>虽然密码过期但是该账号还是能执行其它任务的<br>不过如果密码过期了，当我们登录系统时，<br>系统会强制要求必须要重新设置密码之后<br>才能继续使用，这就是密码过期特性<br><br>如果在密码过去的n天后，用户还是没有登录<br>更改密码，那么这个账号的密码就会失效</td></tr><tr><td>8</td><td>账号失效日期</td><td>账号在此日期之后，将无法再使用</td></tr><tr><td>9</td><td>保留字段</td><td></td></tr></tbody></table>

要想知道 shadow 使用的加密机制，可以使用 `authconfig` 命令查询。如下：

```shell
# authconfig --test | grep hashing
password hashing algorithm is sha512
```

### 3.3 密码忘了，怎么办？

如果是一般用户，可以让系统管理员帮忙。他可以直接重置我们的密码而不需要知道旧密码，即以 root 的身份使用 `passwd` 命令。

如果是 root 用户，那可以使用各种可行的方法进入 Linux 后再去修改。比如重新启动系统之后进入单人维护模式，系统会主动给予 root 权限的 bash 接口，此时再以 `passwd` 命令修改密码即可。或者以 Live CD 启动后挂载根目录去修改 `/etc/shadow`，将里面 root 的密码字段清空，再重新启动之后，root 不用密码即可登录，登录后再用 `passwd` 命令设置 root 的密码即可。
