> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/di-17-zhang-yong-hu-zhang-hu-yu-quan-xian/di-17.1-jie-yong-hu-he-ji-ben-zhang-hu-guan-li.md).

# 17.1 用户和基本账户管理

FreeBSD 系统的所有访问均通过账户实现，所有进程都由用户运行，因此用户与账户管理是系统安全的基础。

FreeBSD 提供了多种用户管理工具。`adduser` 命令以交互方式添加新用户，自动完成创建 passwd 条目、构建新用户主目录、从 **/usr/share/skel** 复制默认配置文件等操作。

adduser(8) 是一个 shell 脚本，内部调用 pw(8) 完成实际的用户数据库操作。adduser(8) 并非 FreeBSD 独有，OpenBSD 和 DragonFly BSD 也有各自的 adduser(8) 实现，但选项和行为有所不同。

`pw` 命令是更底层的用户和组管理工具，支持非交互式批量操作，可直接修改系统用户数据库文件。

用户账户信息存储在 master.passwd(5) 文件中，该文件包含用户名、加密密码、UID、GID、登录类、密码过期时间、账户过期时间、GECOS 信息、主目录和登录 shell 等字段。

## 账户类型

登录 FreeBSD 系统需具备有效的用户账户。

以下直接通过密码文件 **/etc/master.passwd** 观察用户：

```ini
root:$6$huh5iMfeueumGM3B$ycd9HsGOzKfFq6hbWMxceNBRCLibbSj5Ofjv/ed6Kq60M2F.syaGaxfdfYMqB79DZzqyhQlIiRZ4.D9ST90Gv/:0:0::0:0:Charlie &:/root:/bin/sh
toor:*:0:0::0:0:Bourne-again Superuser:/root:
daemon:*:1:1::0:0:Owner of many system processes:/root:/usr/sbin/nologin
operator:*:2:5::0:0:System &:/:/usr/sbin/nologin

……省略一部分……

www:*:80:80::0:0:World Wide Web Owner:/nonexistent:/usr/sbin/nologin
ntpd:*:123:123::0:0:NTP Daemon:/var/db/ntp:/usr/sbin/nologin
nobody:*:65534:65534::0:0:Unprivileged user:/nonexistent:/usr/sbin/nologin
ykla:$6$SqMJXrv5aC6Wq.by$nmbZs078aHNBVyh9noLFouJsGHyFSvQIzH0W4zpdfXuPtGtt.FHgWfXDHVBa.g9P0eZ32UwfByzRKdVnTaO7W.:1001:1001::0:0:User &:/home/ykla:/bin/sh
```

可以看到系统中存在若干用户账户。

FreeBSD 中主要有三类账户：系统账户、普通用户账户，以及超级用户账户。

### 系统账户

系统账户用于运行 DNS、邮件和 Web 服务器等服务。如果所有服务均以超级用户身份运行，其操作将不受限制，此即使用系统账户的安全性考量。

系统账户由源代码中的 [main/etc/master.passwd](https://github.com/freebsd/freebsd-src/blob/main/etc/master.passwd) 文件定义，截至写作时共计 27 个（26 个 UID < 1000 的账户加上 nobody(65534)，该数量随版本迭代可能变化），`_dhcp`、`ntpd` 便属于此类系统账户。系统账户是具有受限权限的专用账户，通常用于运行系统服务和守护进程。

`nobody` 是通用的非特权系统账户，但使用 `nobody` 的服务越多，该用户关联的文件和进程就越多，该用户特权越大，因此最佳实践是为每个服务分配独立的系统账户，而非共用 `nobody`。

系统默认的组定义于源代码 **/etc/group** 中。

### 普通用户账户

普通用户账户分配给实际使用者，用于登录和使用系统。为便于管理员追踪用户操作并防止用户互相干扰彼此的设置，每个访问系统的人都应拥有唯一的用户账户。

`ykla` 是在安装系统时创建的普通用户账户。如果希望通过 `su` 命令切换为 `root` 用户，必须将该用户加入 `wheel` 用户组。此外，部分用户账户是 Port 自动创建的系统用户。

每个用户账户在 FreeBSD 系统上都关联着若干属性信息：

| 属性             | 说明                                                                                          |
| -------------- | ------------------------------------------------------------------------------------------- |
| **用户名**        | 在 `login:` 提示符处输入的名称，每个用户必须拥有唯一的用户名。passwd(5) 中记载了创建有效用户名的规则。建议使用八个或更少的全部小写字符作为用户名，以保持向后兼容性 |
| **密码**         | 每个账户都有一个关联的密码                                                                               |
| **用户 ID（UID）** | 用于在 FreeBSD 系统中唯一标识用户的数字。由于较高的值可能导致某些软件的兼容性问题，建议使用小于 65535 的 UID                            |
| **组 ID（GID）**  | 用于唯一标识用户所属主组的数字。建议使用 65535 或更低的 GID                                                         |
| **登录类**        | 组机制的扩展，在为不同用户定制系统时提供额外的灵活性                                                                  |
| **密码更改时间**     | 在默认情况下密码不会过期，但可以按用户启用密码过期                                                                   |
| **账户过期时间**     | 在默认情况下 FreeBSD 不会使账户过期                                                                      |
| **用户全名**       | 用户名唯一标识 FreeBSD 的账户，但不一定反映用户的真实姓名                                                           |
| **主目录**        | 用户登录时的起始目录。常见约定是将所有用户主目录放在 **/home/username** 或 **/usr/home/username** 下                    |
| **用户 shell**   | shell 提供用户与系统交互的默认环境                                                                        |

需要注意，普通用户权限虽有限，但其运行的软件越多，系统暴露的攻击面也越大——可供攻击者利用的漏洞入口随之增多。攻击者仅在程序存在漏洞或配置不当的情况下，才能利用这些进程实现权限提升。

### 超级用户账户

超级用户账户，通常称为 root，用于无限制地管理系统。超级用户与普通用户账户不同，其操作不受限制，误用可能导致灾难性后果。普通用户账户无法因误操作而破坏操作系统，因此建议以普通用户账户登录，仅在命令需要额外特权时才切换为超级用户。

事实上，内核根据账户的 EUID（有效用户 ID）是否为 `0` 来判定某账户是否拥有 root 权限。参见：main/sys/kern/kern\_priv.c\[EB/OL]. \[2026-03-26]. <https://github.com/freebsd/freebsd-src/blob/main/sys/kern/kern_priv.c> 中的 `if (suser_enabled(cred))` 代码块部分。

获取超级用户特权有多种方式。虽然可以直接以 root 登录，但不推荐这种做法，建议使用 su(1) 命令切换为超级用户。

## 账户管理

FreeBSD 提供了多种不同的命令来管理用户账户。

**管理用户账户的工具**

| 命令           | 概要                  |
| ------------ | ------------------- |
| adduser(8)   | 推荐用于添加新用户的命令行应用程序。  |
| rmuser(8)    | 推荐用于删除用户的命令行应用程序。   |
| chpass(1)    | 用于更改用户数据库信息的灵活工具。   |
| passwd(1)    | 用于更改用户密码的命令行工具。     |
| pw(8)        | 可修改用户账户所有方面的强大灵活工具。 |
| bsdconfig(8) | 带有账户管理支持的系统配置工具。    |

### adduser 创建用户

推荐使用的添加新用户的程序是脚本文件 adduser(8)。添加新用户时，此程序会自动更新 **/etc/passwd** 和 **/etc/group**。

adduser 还会为新用户创建主目录，从 **/usr/share/skel** （源代码路径为 `share/skel`）复制默认配置文件。`adduser` 的源代码路径为 `usr.sbin/adduser/adduser.sh`。

adduser(8) 是交互式的，会逐步引导创建新用户账户。如下所示，输入所需信息或按 **回车键** 接受方括号中的默认值。

在此示例中，用户受邀加入 `wheel` 组，使其可以通过 su(1) 成为超级用户。

完成后，该工具会提示是创建另一名用户还是退出。

示例：创建普通用户 `ykla`，并添加到 `video` 组：

```sh
# adduser -G video -s sh -w yes
# Username: ykla
```

示例：创建用户 test，并添加到 wheel 组，设置其默认 shell 为 sh：

```sh
# adduser # 此工具必须由超级用户运行
Username: test # 用户名 ①
Full name:  # 全名，可留空
Uid (Leave empty for default): # UID 设置，可留空
Login group [test]: # 登录组
Login group is test. Invite test into other groups? []: wheel # 设置要加入的组，多个用空格隔开，可留空
Login class [default]: # 登录分类，可留空
Shell (sh csh tcsh nologin) [sh]: sh  # 除非手动设置默认 Shell，否则默认 Shell 为 sh
Home directory [/home/test]: # 指定家目录
Home directory permissions (Leave empty for default): # 指定家目录权限
Enable ZFS encryption? (yes/no) [no]: # 是否使用 ZFS 加密
Use password-based authentication? [yes]:  # 是否使用密码
Use an empty password? (yes/no) [no]:   # 是否空密码
Use a random password? (yes/no) [no]:   # 是否随机密码
Enter password: # 输入密码
Enter password again: # 重复输入密码
Lock out the account after creation? [no]: # 锁定账户？
Username   : test
Password   : *****
Full Name  :
Uid        : 1002
ZFS dataset : zroot/home/test
Class      :
Groups     : test wheel
Home       : /home/test
Home Mode  :
Shell      : /bin/sh
Locked     : no
OK? (yes/no): yes # 检查是否有错误
adduser: INFO: Successfully created ZFS dataset (zroot/home/test).
adduser: INFO: Successfully added (test) to the user database.
Add another user? (yes/no): no # 还需要创建另一个账户吗？
Goodbye!
```

> **注意**
>
> 由于输入密码时不会打印在屏幕上，也不会显示为掩码 `*`，请谨慎创建用户账户。

* ① 登录名命名有一些限制，参见 passwd(5)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=passwd&sektion=5>。但请注意，登录名不支持八位编码字符集，例如不支持中文（即仅支持特定 ASCII 字符）。

只有 root 才能使用此命令，否则将提示错误如下：

```sh
$ adduser test
adduser: ERROR: you must be the super-user (uid 0) to use this utility.
```

提示只有 UID 0 的用户（通常是 root）才能调用 adduser 命令。

### rmuser 删除用户

> **警告**
>
> `rmuser` 将永久删除用户账户及其主目录、邮件等所有相关信息，操作不可逆。使用 `-y` 参数将跳过确认步骤，请务必确认用户名正确无误。

`rmuser` 用于删除用户，与 `adduser` 命令一样，也是交互式的脚本。`rmuser` 源代码路径是 `usr.sbin/adduser/rmuser.sh`。

示例：删除用户 test1 test2。

```sh
# rmuser -y test1 test2 # 同时删除用户 test1 和 test2
Removing user (test1): mailspool home passwd.
Removing user (test2): home passwd.
```

参数 `-y` 用于跳过确认步骤。

### chpass 更改用户信息

所有用户都可以使用 chpass(1) 更改其默认 shell 和账户的个人信息。chpass 源代码位于 `usr.bin/chpass`。

示例：普通用户使用 nvi 文本编辑器打开当前用户信息并修改。

```sh
$ chpass ykla	# 修改 ykla 的账户信息数据库
#Changing user information for ykla.
Shell: /bin/sh	# 用户 shell
Full Name: User &	# 用户全名
Office Location:	# 办公地点
Office Phone:	# 办公电话
Home Phone:	# 家庭电话
Other information:	# 其他信息
~
~
~
/etc/pw.ogzb33: unmodified: line 1
```

root 可使用此工具更改任意用户的额外账户信息。

```sh
# chpass ykla	# 修改 ykla 的账户信息数据库
#Changing user information for ykla.
Login: ykla	# 指定 ykla
Password: $6$SqMJXrv5aC6Wq.by$nmbZs078aHNBVyh9noLFouJsGHyFSvQIzH0W4zpdfXuPtGtt.FHgWfXDHVBa
.g9P0eZ32UwfByzRKdVnTaO7W.	# 用户密码
Uid [#]: 1001
Gid [# or name]: 1001
Change [month day year]:	# 密码更改日期
Expire [month day year]:	# 账户过期日期
Class:	# 用户分级
Home directory: /home/ykla	# 用户家目录
Shell: /bin/sh
Full Name: User &
Office Location:
Office Phone:
Home Phone:
Other information:
~
~
~
/etc/pw.mDp9q3: unmodified: line 1
```

示例：修改用户 test1 的登录环境为 **/bin/sh**。

```sh
# chpass -s sh test1
chpass: user information updated
```

常用参数：`-s`，用于修改登录 shell。

> **技巧**
>
> chfn(1) 与 chsh(1) 是 chpass(1) 的链接命令，ypchpass(1)、ypchfn(1) 和 ypchsh(1) 也是。由于 NIS 支持是自动的，无需在命令前加 `yp`。这一点可以从源代码 `usr.bin/chpass/Makefile` 进行推断：
>
> ```makefile
> SYMLINKS=	chpass ${BINDIR}/chfn
> SYMLINKS+=	chpass ${BINDIR}/chsh
> .if ${MK_NIS} != "no"	# 如果系统启用了 NIS
> SYMLINKS+=	chpass ${BINDIR}/ypchfn
> SYMLINKS+=	chpass ${BINDIR}/ypchpass
> SYMLINKS+=	chpass ${BINDIR}/ypchsh
> .endif
>
> MLINKS=	chpass.1 chfn.1 chpass.1 chsh.1
> .if ${MK_NIS} != "no"
> MLINKS+= chpass.1 ypchpass.1 chpass.1 ypchfn.1 chpass.1 ypchsh.1
> .endif
> ```

### passwd 更改用户密码

修改用户密码，如不指定用户则默认为当前用户。普通用户只能修改自己的密码，否则将提示错误如下：

```sh
$ passwd test
passwd: permission denied
```

示例：使用 ykla 更改自己的密码。

```sh
$ passwd ykla
Changing local password for ykla
Old Password:	# 输入旧密码
New Password:	# 输入新密码
Retype New Password:	# 再次输入新密码
```

root 用户可以修改所有用户的密码，且无需旧密码。

示例：使用 root 更改用户 ykla 的密码。

```sh
# passwd ykla
Changing local password for ykla
New Password:	# 输入新密码
Retype New Password:	# 再次输入新密码
```

> **技巧**
>
> FreeBSD 的 `passwd` 选项与 Linux 不同，锁定/解锁账户请使用 `pw lock/unlock`。

## 组管理

组是用户的列表。组由其组名和 GID 标识。在 FreeBSD 中，内核使用进程的 UID 和其所属的组列表来确定进程可执行的操作范围。在大多数情况下，用户或进程的 GID 通常指列表中的第一个组。

组名到 GID 的映射列在 **/etc/group** 中。**/etc/group** 是纯文本文件，有四个以冒号分隔的字段。

```sh
# cat /etc/group
wheel:*:0:root,ykla	#
operator:*:5:root

……省略部分输出……

ykla:*:1001:
test:*:1002:
```

可以看到 **/etc/group** 的格式形如 `组名:加密后的密码:GID:成员列表`，通过英文冒号分隔。

超级用户可以使用文本编辑器修改 **/etc/group**，但因编辑错误可能导致严重后果，不建议这样做。建议使用 pw(8) 添加和编辑组。

> **警告**
>
> `operator` 组可能授予意外的类似超级用户的访问权限，包括但不限于关机（`shutdown`）和访问 **/dev** 中的部分设备（如磁盘设备），因此使用该组时请务必小心。注意 `reboot` 和 `halt` 仅限 root 用户，operator 组成员无法执行。

在 FreeBSD 中，可以使用 `pw` 命令管理用户和组：它是系统用户和组文件的前端。pw(8) 提供了非常强大的命令行选项，适合用于 shell 脚本，但对于新用户来说可能比本节中的其他命令更复杂。

### 添加组

使用 pw(8) 添加组：

```sh
# pw groupadd ykla2
# pw groupshow ykla2
ykla2:*:1002:
```

在此示例中，1002 是 ykla2 的 GID。此时，ykla2 没有成员。

### 向组中添加用户

使用 pw(8) 将用户添加到组中。

示例：设置组 test5 的成员为 test1。

```sh
# pw groupmod test5 -M test1 # 原有成员会被删除！
```

示例：将用户 `ykla` 同时添加到 `ykla2` 和 `wheel` 组：

```sh
# pw usermod ykla -G ykla2,wheel
```

`-G` 组列表，逗号分隔。

验证如下：

```sh
# id ykla
uid=1001(ykla) gid=1001(ykla) groups=1001(ykla),0(wheel),1002(ykla2)
```

示例：将用户 `test` 添加到 `wheel` 组（`wheel` 组为系统默认组，无需创建）：

```sh
# pw groupmod wheel -m test
```

在此示例中，传递给 `-m` 的参数是用户列表（逗号分隔），这些用户会追加到组中，并不会替换已有用户。

### 删除组

示例：删除 `admin` 组：

```sh
# pw groupdel admin
```

### 删除组中的用户

示例：从 `admin` 组里移除用户 `ykla`：

```sh
# pw groupmod admin -d ykla
```

### 添加用户

`pw useradd` 命令用于新建用户。

示例：创建用户 test1。

```sh
# pw useradd test1 # uid 系统默认，test1 组，登录环境 /bin/sh，未创建主目录
```

示例：创建用户 test2。

```sh
# pw useradd test2 -u 1200 -m -d /tmp/test -g test2 -G wheel -s sh -c test2 # 创建用户 test2：UID 1200，创建主目录 /tmp/test，主组 test2，附加 wheel 组，登录 shell 为 sh，全名 test2
```

### 修改用户信息

`pw usermod` 命令用于修改用户信息。

示例：

```sh
# pw usermod test1 -l test2 # 将用户 test1 重命名为 test2
```

选项 `-l` 修改用户名。

### 删除用户

`pw userdel` 命令用于删除用户。

> **警告**
>
> `pw userdel -r` 将永久删除用户及其主目录和所有相关信息，操作不可逆。如果主目录为 ZFS 数据集，pw(8) 仅删除其中的文件，不会销毁该数据集，需手动执行 `zfs destroy` 销毁数据集及其子数据集和快照。请确认用户名正确无误。

示例：删除用户 test2 及其主目录。

```sh
# pw userdel -r test2
```

常用参数：`-r` 删除用户，同时删除用户主目录及所有相关信息；如果不使用该参数则信息保留，仅删除用户。如果主目录为 ZFS 数据集，pw(8) 仅删除其中的文件，不会销毁该数据集，需手动执行 `zfs destroy` 销毁数据集及其子数据集和快照。

### 显示用户信息

`pw usershow` 命令用于显示用户信息。

示例：显示用户 test2 的详细信息。

```sh
# pw usershow test2
test2:$6$FkxPcs2y.Y8cxyuj$kVDoV1LC.IWKGlSitll3oLArF18aHQYID0JYE.TUuD0YFgba.c7MbGs3xLnmpCZyu1nVKHhNqW2X7a57qN0xg/:1201:1201::0:0:User &:/home/test2:/bin/sh
```

### 修改组信息

`pw groupmod` 命令用于修改组信息。

示例：修改 test 组的 gid 为 1300。

```sh
# pw groupmod test -g 1300
```

示例：将 test 组重命名为 test2。

```sh
# pw groupmod test -l test2
```

## 参考文献

* FreeBSD Project. pw(8)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=pw&sektion=8>. 手册页，介绍了用户和组管理命令。

## 课后习题

1. 修改 FreeBSD 源代码，使操作的用户名支持 UTF-8 编码。
2. 查看 FreeBSD 中 pw 命令的源代码实现，使其更加现代化。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/di-17-zhang-yong-hu-zhang-hu-yu-quan-xian/di-17.1-jie-yong-hu-he-ji-ben-zhang-hu-guan-li.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
