# 13. 安全注意事项

> 特权隔离、攻击面、Linux 能力及安全运行

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

<!-- Generated by scripts/generate-haproxy-docs.py from pinned upstream text. -->

HAProxy 旨在以极低的权限运行。使用它的标准方式是将其隔离到 chroot 环境中，并将其权限降级为非 root 用户，且该用户在该环境内没有任何权限，从而确保未来若发现任何漏洞，其被攻破也不会影响系统其余部分。

为执行 chroot 操作，进程必须首先以 root 用户身份启动。手动构建 chroot 环境并在其中启动进程毫无意义，这类 chroot 环境难以构建，通常无法得到妥善维护，且包含的缺陷远多于主文件系统。一旦发生入侵，攻击者可利用特意构建的文件系统。不幸的是，许多系统管理员混淆了“以 root 身份启动”与“以 root 身份运行”的区别，导致在启动 HAProxy 之前就更改了用户 ID，从而削弱了实际的安全限制。

HAProxy 必须以 root 身份启动，以实现以下目的：

- 调整文件描述符限制
- 绑定到特权端口编号
- 绑定到特定网络接口
- 透明地监听外部地址
- 在 chroot 沙箱内隔离自身
- 降权至另一个非特权 UID

HAProxy 可能需要以 root 身份运行，以实现以下目的：

- 绑定到接口以发起出站连接
- 绑定到特权源端口以发起出站连接
- 透明地绑定到外部地址以发起出站连接

大多数用户无需使用“以 root 身份运行”的情况。但“以 root 身份启动”涵盖了大多数使用场景。

安全的配置应满足以下要求：

- 一个指向空位置且无任何访问权限的 chroot 语句。可通过 UNIX 命令行按如下方式准备：

```shell
# mkdir /var/empty && chmod 0 /var/empty || echo "Failed"
```

并在 HAProxy 配置的 global 段中以如下方式引用：

```text
chroot /var/empty
```

- 在 global 段中同时使用 uid/user 和 gid/group 语句：

```text
user haproxy
group haproxy
```

- 设置统计信息套接字的模式、所有者用户 ID 和所有者组 ID，使其与允许访问 CLI 的用户和/或组匹配，以确保无人可访问：

```text
stats socket /var/run/haproxy.stat uid hatop gid hatop mode 600
```

## 13.1. Linux 能力支持 {#section-13-1}

自 v2.9 版本起，HAProxy 支持 Linux 能力。若二进制文件编译时 USE_LINUX_CAP=1，则在从 root 用户切换至非 root 用户时，能够保留通过 'setcap' 关键字赋予的能力。

自 v3.1 版本起，HAProxy 也会检查通过 'setcap' 关键字指定的权限是否已在二进制文件中由管理员设置为允许集（通过 capget 系统调用）。若存在此情况，HAProxy 在以非 root 用户运行时，将通过 capset 系统调用将其权限从允许集转移到进程的有效集。

此举旨在避免 HAProxy 以 root 用户启动并运行时可能出现的所有使用场景：透明代理模式、绑定到特权端口。

'setcap' 关键字支持以下网络能力：

- cap_net_admin：透明代理、将套接字绑定到特定网络接口、使用 set-mark 动作；
- cap_net_raw（cap_net_admin 的子集）：透明代理；
- cap_net_bind_service：将套接字绑定到特定网络接口；
- cap_sys_admin：在特定网络命名空间中创建套接字。

HAProxy 不会在未将这些能力列为 'setcap' 参数的情况下，将其从允许集（Permitted set）转移到有效集（Effective）。有关 'setcap' 关键字及支持能力的更多信息，请参见配置指南第 3.1 节“进程管理与安全”。

系统管理员可在 HAProxy 二进制文件中添加所需功能，允许集可通过以下命令设置：

示例：

```haproxy
# setcap cap_net_admin,cap_net_bind_service=p /usr/local/sbin/haproxy
```

新增的功能将在进程启动后于其允许集（Permitted set）中体现。若相同的功能作为 'setcap' 关键字的参数，也可在进程有效集（Effective set）中观察到。可通过以下命令进行验证：

示例：

```haproxy
# grep Cap /proc/<haproxy PID>/status
```

CapInh: 0000000000000000
CapPrm: 0000000000001400
CapEff: 0000000000001400
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000

有关 setcap 和能力集的更多详细信息，请参阅 Linux 手册页（capabilities(7)）。

在某些使用场景中，例如透明代理或在特定网络命名空间中创建套接字时，配置文件解析器会检测到需要 `cap_net_raw`、`cap_sys_admin` 或其他受支持的能力。随后，在初始化阶段，HAProxy 进程会检查这些能力是否可被添加到其有效能力集（Effective set）中。若因 `capget` 或 `capset` 系统调用失败（某些安全模块如 SELinux、Seccomp 等对系统调用施加了限制）而导致无法完成，进程将发出诊断警告（以 -dD 开头）。

由于支持多种不同平台及其各异的系统设置，解析器无法从配置文件中推断出是否将绑定到特权端口。因此，在权限不足（以非 root 用户运行）的情况下，进程仅会以如下告警信息终止。用户需自行检查配置文件及 HAProxy 二进制文件的权限能力设置。

示例：

```shell
$ haproxy -dD -f haproxy.cfg
...
[ALERT]    (96797): Binding [haproxy.cfg:36] for frontend fe: cannot bind socket (Permission denied) for [0.0.0.0:80]
[ALERT]    (96797): [haproxy.main()] Some protocols failed to start their listeners! Exiting.
```
