> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bicola.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Linux 用户管理

> 用户账号、用户组、sudo 提权、环境变量 —— 部署中关于「身份」的所有事

<Note>
  本页覆盖部署中和"身份"相关的命令：建账号、改密码、加入用户组（如 Rocky Linux 的 `wheel`）、用 sudo 提权、配置环境变量。生产环境绝大多数操作都不该用 root，理解这一组命令是基础。
</Note>

## 1. 用户账号管理

<AccordionGroup>
  <Accordion title="useradd / userdel / passwd / usermod — 增删改用户">
    ```bash theme={null}
    # 创建用户
    sudo useradd -m -s /bin/bash alice
    # -m = --create-home，建家目录 /home/alice
    # -s = --shell，指定登录 shell

    # 设密码
    sudo passwd alice            # passwd = password
    # 然后按提示输入两次密码

    # 修改用户：加入管理员组（详见第 2 章）
    sudo usermod -aG wheel alice    # Rocky / RHEL / CentOS / AlmaLinux
    # sudo usermod -aG sudo alice   # Ubuntu / Debian
    # -a = --append，追加（不要漏！否则会替换掉原有组）
    # -G = --groups，指定要加入的组

    # 删除用户
    sudo userdel -r alice        # -r = --remove，连同家目录一起删

    # 查看用户所属组
    groups alice
    id alice                     # 更详细：UID / GID / 所有 group
    ```
  </Accordion>

  <Accordion title="who am I（自我确认）">
    ```bash theme={null}
    whoami                       # 我是哪个用户
    id                           # 我的 UID / GID / 所属组
    groups                       # 我属于哪些组
    echo $HOME                   # 我的家目录在哪
    ```

    **场景**：sudo 切换后、ssh 不同账户登入后，先确认身份再操作，避免改错文件归属。
  </Accordion>

  <Accordion title="用户、UID 与 /etc/passwd">
    每个用户在 `/etc/passwd` 里一行，字段以 `:` 分隔：

    ```text theme={null}
    alice:x:1001:1001:Alice Wang:/home/alice:/bin/bash
    │     │ │    │    │          │           │
    用户名 │ UID  GID  备注       家目录       登录 shell
           密码占位（实际密码在 /etc/shadow）
    ```

    | 关键字段     | 说明                                                |
    | -------- | ------------------------------------------------- |
    | UID      | 用户 ID。`0` 是 root；`< 1000` 一般是系统账户；`>= 1000` 是普通用户 |
    | GID      | 主组 ID（对应 `/etc/group`）                            |
    | 登录 shell | 设为 `/usr/sbin/nologin` 即「能存在但不能登录」，常用于服务账户        |

    **不需要登录的服务账户**：

    ```bash theme={null}
    sudo useradd -r -s /usr/sbin/nologin myapp
    # -r = --system，创建系统账户（UID 通常 < 1000）
    # 然后让 myapp.service 用 User=myapp 运行
    ```
  </Accordion>
</AccordionGroup>

## 2. 用户组与管理员权限（以 Rocky Linux 为例）

<Note>
  Linux 通过 **「用户组」** 实现权限的批量管理 —— 把权限授予一个组，组内所有成员立即获得。本节用 Rocky Linux 演示**用户、组、sudo 权限**三者的关系，这套模型同样适用于 RHEL / CentOS / AlmaLinux。
</Note>

### 2.1 用户、组、权限：三个关键文件

每个用户都属于至少一个**主组**，可以加入多个**附加组**。

```bash theme={null}
id aimee
# uid=1001(aimee) gid=1001(aimee) groups=1001(aimee),10(wheel),100(developers)
#                                          │           │           │
#                                       主组       附加组1      附加组2
```

| 文件             | 内容                   | 谁能读    |
| -------------- | -------------------- | ------ |
| `/etc/passwd`  | 用户列表（一行一个）           | 所有人    |
| `/etc/group`   | 用户组列表                | 所有人    |
| `/etc/shadow`  | 加密后的用户密码             | 仅 root |
| `/etc/sudoers` | 谁能用 sudo、能用 sudo 干什么 | 仅 root |

```bash theme={null}
cat /etc/group | grep wheel
# wheel:x:10:aimee,bob       <-- aimee 和 bob 都在 wheel 组
#  │    │  │   └ 该组成员
#  │    │  └─ GID
#  │    └─ 密码占位（极少使用）
#  └─ 组名
```

### 2.2 wheel 组：Rocky Linux 的「管理员组」

不同发行版的「管理员组」叫法不同：

| 发行版                                         | 管理员组名   |
| ------------------------------------------- | ------- |
| **Rocky Linux / RHEL / CentOS / AlmaLinux** | `wheel` |
| Ubuntu / Debian                             | `sudo`  |

`wheel` 这个名字源自 BSD 传统，意思是「掌握方向盘的人」。**在 Rocky Linux 上，把用户加进 wheel 组 ≈ 给它 sudo 权限**（具体规则见 2.4 节的 `/etc/sudoers`）。

### 2.3 让 aimee 拥有 sudo 权限：完整流程

<Steps>
  <Step title="确认或创建 aimee 用户">
    ```bash theme={null}
    id aimee 2>/dev/null || sudo useradd -m -s /bin/bash aimee
    sudo passwd aimee                    # 设置密码
    ```
  </Step>

  <Step title="把 aimee 加入 wheel 组">
    ```bash theme={null}
    sudo usermod -aG wheel aimee
    # -a = --append    追加到现有组
    # -G = --groups    指定附加组
    ```

    <Warning>
      **`-a` 务必加上！** 单独使用 `usermod -G wheel aimee` 会把 aimee 从其他所有附加组中**踢出**，只剩 wheel —— 这是新手最常踩的坑。
    </Warning>
  </Step>

  <Step title="验证组关系">
    ```bash theme={null}
    id aimee
    # uid=1001(aimee) gid=1001(aimee) groups=1001(aimee),10(wheel)

    getent group wheel
    # wheel:x:10:aimee
    ```
  </Step>

  <Step title="aimee 重新登录后即可使用 sudo">
    ```bash theme={null}
    su - aimee
    sudo whoami                          # 输入 aimee 自己的密码，输出 root = 配置成功
    ```

    <Note>
      **附加组变更需要重新登录才生效**。aimee 当前已登录的会话仍持有旧组身份，必须 `exit` 重连。
    </Note>
  </Step>
</Steps>

### 2.4 看清谁有管理员权限：/etc/sudoers

`/etc/sudoers` 是 sudo 权限的总规则文件。先 `cat` 看一下默认内容：

```bash theme={null}
sudo cat /etc/sudoers
```

Rocky Linux 默认的关键几行（去掉注释）：

```text theme={null}
root    ALL=(ALL)       ALL
%wheel  ALL=(ALL)       ALL
# %wheel ALL=(ALL) NOPASSWD: ALL          ← 注释掉的「免密」版本
#includedir /etc/sudoers.d
```

**这一行 `%wheel ALL=(ALL) ALL` 怎么读：**

```text theme={null}
%wheel    ALL    =    (ALL)         ALL
  │        │          │              │
  │        │          │              └ 可以执行哪些命令（ALL = 任何命令）
  │        │          └ 可以切换到哪些用户身份（ALL = 任何用户，包括 root）
  │        └ 在哪些主机生效（sudoers 可被多机共享，ALL = 所有主机）
  └ 谁。% 前缀 = 用户组；不带 % = 用户名
```

**关键认识**：正是这一行让"加入 wheel 组"等价于"获得 sudo 权限"。如果删掉它，wheel 组就失去特权了。

`#includedir /etc/sudoers.d`（虽然以 `#` 开头但**不是注释**，是 sudoers 特殊语法）会加载该目录下所有规则文件。**生产环境推荐把自定义规则放进独立文件**，不要直接动 `/etc/sudoers`：

```bash theme={null}
sudo visudo -f /etc/sudoers.d/deploy      # visudo 会带语法校验
```

写入：

```text theme={null}
# deploy 用户免密重启 nginx 与 myapp（其他命令不行）
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart myapp
```

<Warning>
  **任何时候编辑 sudoers 都用 `visudo`，不要直接 `vim`！** visudo 会做语法校验 —— 文件一旦写错，**所有人都用不了 sudo，可能把自己锁死**。`visudo -c` 可单独检查语法。
</Warning>

### 2.5 「管理员权限」具体能干什么

`sudo` 拿到的是 **root 权限**。在 Linux 里，root 几乎无所不能：

| 类别     | 例子                                  |
| ------ | ----------------------------------- |
| 系统配置   | 改 `/etc/` 下任何文件、`dnf install` 装卸软件包 |
| 用户管理   | 增删改用户和组、改任何人密码                      |
| 服务管理   | `systemctl` 启停服务、改 `.service` 文件    |
| 网络与防火墙 | 配置网卡、`firewall-cmd` 规则              |
| 文件系统   | 挂载分区、读写所有人的文件                       |
| 进程     | 杀死任何进程、改进程优先级                       |
| 内核     | 加载内核模块、改 sysctl 参数                  |

**理解关键**：root 的权限不是"无视所有规则"，而是"权限检查的例外" —— 内核遇到 UID=0 直接放行（少数例外：SELinux 强制策略、`chattr +i` 不可变属性等）。

### 2.6 用户组与文件权限的关系

回顾[文件权限](/linux/file-management)中的 `rwxr-xr-x` 三段：第一段是**所有者**、第二段是**所属组**、第三段是**其他人**。**这就是用户组存在的最大意义** —— 让一个文件的访问权限可以被一群人共享。

**典型场景**：让 aimee 和 bob 协作维护 `/opt/myapp/`。

<Steps>
  <Step title="建一个团队组">
    ```bash theme={null}
    sudo groupadd developers
    sudo usermod -aG developers aimee
    sudo usermod -aG developers bob
    ```
  </Step>

  <Step title="改目录所属组 + 给组写权限">
    ```bash theme={null}
    sudo chgrp -R developers /opt/myapp           # 改所属组（递归）
    sudo chmod -R 770 /opt/myapp                  # u=rwx g=rwx o=---
    sudo chmod g+s /opt/myapp                     # ⭐ setgid：新建文件自动继承组
    ```

    `chmod g+s` 给目录加 **setgid 位**：之后在里面新建的文件 / 子目录会**自动属于 developers 组**，不用每次手动 chgrp。
  </Step>

  <Step title="验证">
    ```bash theme={null}
    ls -ld /opt/myapp
    # drwxrws---  3 root developers ... /opt/myapp
    #         │
    #         └ s 表示 setgid 已生效
    ```
  </Step>
</Steps>

**对比：sudo 权限 vs 文件组权限**

| 维度   | sudo（管理员权限）                            | 用户组（文件权限）               |
| ---- | -------------------------------------- | ----------------------- |
| 控制什么 | 命令的执行能力（提权）                            | 文件 / 目录的读写执行            |
| 配置在哪 | `/etc/sudoers`、`/etc/sudoers.d/`       | 文件本身的元数据（chgrp / chmod） |
| 颗粒度  | 可精细到单条命令                               | 仅 r / w / x 三种          |
| 典型场景 | 让 deploy 用户能 `systemctl restart nginx` | 让团队共享 `/opt/myapp/`     |

简而言之：**sudo 决定"你能不能跑某条命令"，文件组决定"你能不能动某个文件"**。两者互为补充。

### 2.7 用户组管理速查

```bash theme={null}
# ── 查看 ──
groups aimee                          # aimee 在哪些组
id aimee                              # 含 UID/GID 的完整信息
getent group wheel                    # wheel 组的所有成员（推荐，容器/LDAP 也通用）

# ── 创建 / 删除组 ──
sudo groupadd developers
sudo groupdel developers

# ── 加入 / 移出 ──
sudo usermod -aG developers aimee     # 加入（务必带 -a）
sudo gpasswd -d aimee developers      # 从组中移出
sudo gpasswd -M aimee,bob developers  # ⚠️ -M 直接重设组成员（覆盖式，慎用）

# ── 改文件所属组 ──
sudo chgrp developers file.txt
sudo chown :developers file.txt       # chown 也能改组（用户名留空）
```

## 3. sudo / su — 提权

<AccordionGroup>
  <Accordion title="sudo (superuser do) — 单条命令提权（推荐）">
    ```bash theme={null}
    sudo command                 # ⭐ 用 root 身份执行单条命令
    sudo -i                      # -i = --login，进入 root 交互 shell
    sudo -u alice command        # -u = --user，以 alice 身份执行
    sudo -k                      # -k = --kill，清除已缓存的 sudo 凭证
    sudo !!                      # 上一条命令前加 sudo（漏写时神器）
    ```

    **为什么推荐 `sudo` 而不是直接登 root：**

    | 维度    | sudo                              | root 登录  |
    | ----- | --------------------------------- | -------- |
    | 审计    | `/var/log/auth.log` 留下完整操作记录      | 几乎无追溯    |
    | 误操作风险 | 每次都要敲 sudo，多一道意识                  | 随时一发不可收拾 |
    | 权限粒度  | `/etc/sudoers` 可精细配置（哪个用户能执行哪些命令） | 全开       |
  </Accordion>

  <Accordion title="su (substitute user) — 切换身份">
    ```bash theme={null}
    su - alice                   # 切换到 alice，- 表示加载其登录环境
    su -                         # 切换到 root（- 表示完整登录环境）
    su alice                     # 仅切用户，环境变量保留当前的（不推荐）
    exit                         # 从切换出来的身份退出
    ```

    <Note>
      新系统通常默认禁用 root 直接 `su -`（root 密码未设），用 `sudo -i` 替代。
    </Note>
  </Accordion>

  <Accordion title="sudo 权限的精细配置 → 见 2.4 节">
    本章节聚焦 `sudo` / `su` 命令的**使用**。如何**配置**谁能 sudo、能 sudo 干什么（包括 `/etc/sudoers`、`visudo`、wheel 组），已在 [2.4 看清谁有管理员权限](#2-4-看清谁有管理员权限-etc-sudoers) 与 [2.7 用户组管理速查](#2-7-用户组管理速查) 中完整覆盖。
  </Accordion>
</AccordionGroup>

<Warning>
  **生产服务器尽量用 `sudo`，不要长期处于 root shell**。误操作风险大，且 `sudo` 在 `/var/log/auth.log` 留审计记录。
</Warning>

## 4. 环境变量

很多部署问题来自环境变量没设对（最经典：`command not found` 是 PATH 没配）。

<AccordionGroup>
  <Accordion title="查看当前环境变量">
    ```bash theme={null}
    env                          # 列出所有环境变量
    echo $PATH                   # 看某一个
    echo $HOME
    printenv USER                # 类似 echo
    set | less                   # 列出所有 shell 变量（含函数）
    ```
  </Accordion>

  <Accordion title="临时设置（仅当前 shell 有效）">
    ```bash theme={null}
    export DB_HOST=localhost           # 设置变量并导出
    export PATH=/opt/myapp/bin:$PATH   # 把目录加到 PATH 最前面
    unset DB_HOST                      # 删除变量

    # 或者只对单条命令有效（不污染当前 shell）：
    DB_HOST=localhost ./run.sh
    ```

    **`export` 的含义**：让子进程也能看到这个变量。不 export 的话，启动的应用读不到。
  </Accordion>

  <Accordion title="永久设置（每次登录都生效）">
    把 `export` 写进登录 shell 的初始化文件：

    | 文件                               | 适用              | 加载时机           |
    | -------------------------------- | --------------- | -------------- |
    | `~/.bashrc`                      | bash 用户每次开终端都加载 | 每次新开 bash（最常用） |
    | `~/.profile` 或 `~/.bash_profile` | 登录 shell        | 每次登录一次         |
    | `~/.zshrc`                       | zsh 用户          | 每次新开 zsh       |
    | `/etc/environment`               | 系统级（所有用户）       | 系统启动           |
    | `/etc/profile.d/*.sh`            | 系统级，登录时加载       | 每次登录           |

    改完后让它立即生效：

    ```bash theme={null}
    source ~/.bashrc      # 或者写成 . ~/.bashrc
    ```
  </Accordion>

  <Accordion title="$PATH 是什么">
    `$PATH` 是用 `:` 分隔的目录列表，shell 在这些目录里找你输入的命令：

    ```bash theme={null}
    echo $PATH
    # /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
    ```

    **「command not found」** 通常意味着该程序不在 `$PATH` 里。可以：

    1. 用绝对路径调用：`/opt/myapp/bin/myapp`
    2. 把目录加进 `$PATH`：`export PATH=/opt/myapp/bin:$PATH`
    3. 持久化：把上一行写到 `~/.bashrc`，下次登录依然有效

    <Tip>
      新加路径放最前面（`新路径:$PATH`）= 优先用新版；放最后面（`$PATH:新路径`）= 系统已有同名命令优先。
    </Tip>
  </Accordion>

  <Accordion title="部署常见环境变量">
    | 变量                 | 用途                                     |
    | ------------------ | -------------------------------------- |
    | `PATH`             | 命令搜索路径                                 |
    | `HOME`             | 当前用户家目录                                |
    | `USER` / `LOGNAME` | 当前用户名                                  |
    | `SHELL`            | 当前 shell 路径                            |
    | `LANG` / `LC_*`    | 语言与本地化（中文系统设 `zh_CN.UTF-8`）            |
    | `TZ`               | 时区，如 `Asia/Shanghai`                   |
    | `JAVA_HOME`        | Java 安装路径                              |
    | `NODE_ENV`         | Node.js 应用环境（development / production） |
    | `LD_LIBRARY_PATH`  | 动态链接库搜索路径                              |

    **场景**：systemd 服务里通过 `Environment=` 字段传应用的环境变量：

    ```ini theme={null}
    [Service]
    Environment="DB_HOST=db.internal"
    Environment="DB_PORT=5432"
    EnvironmentFile=/etc/myapp/env       # 或从文件加载
    ExecStart=/opt/myapp/run.sh
    ```
  </Accordion>
</AccordionGroup>
