# 基于角色的访问控制

> 一个基于角色的基本身份认证和访问控制指南

---

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

---

## 概述 {#overview}

身份认证功能自 etcd 2.1 版本起引入。etcd v3 API 对身份认证功能的 API 和用户界面进行了轻微调整，以更好地适配新的数据模型。本文旨在帮助用户在 etcd v3 中设置基本的身份认证和基于角色的访问控制。

## 特殊用户和角色 {#special-users-and-roles}

有一个特殊用户 `root`，以及一个特殊角色 `root`。

### 用户 `root` {#user-root}

`root` 用户在激活身份认证前必须先创建，该用户拥有对 etcd 的完全访问权限。`root` 用户的设计初衷是用于系统管理：管理角色和普通用户。`root` 用户必须拥有 `root` 角色，并被允许修改 etcd 内的任意内容。

### 角色 `root` {#role-root}

角色 `root` 可授予任意用户，包括根用户。拥有 `root` 角色的用户具备全局读写权限，并可更新集群的身份认证配置。此外，`root` 角色授予用户执行常规集群维护的权限，包括修改集群成员关系、整理碎片以及创建快照。

## 使用用户 {#working-with-users}

`user` 子命令用于 `etcdctl`，负责处理与用户账户相关的所有事项。

用户列表可通过以下方式获取：

```
$ etcdctl user list
```

创建用户的方法如下：

```
$ etcdctl user add myusername
```

创建新用户时将提示输入新密码。当提供选项 `--interactive=false` 时，可从标准输入提供密码。也可使用 `--new-user-password` 来提供密码。

创建无法通过密码认证的用户也是可行的，方法如下：

```
$ etcdctl user add myusername --no-password
```

此类用户只能通过 TLS 通用名称 [进行认证](#using-tls-common-name)。

> [!NOTE]
> etcd 不支持通过 `--user username:` 使用空密码进行身份认证。例如，使用空密码创建的用户，如 `etcdctl user add anonymous:''`，无法通过用户名/密码请求进行身份认证，类似 `etcdctl --user anonymous: get foo` 的请求将失败并返回 `user name is empty`。

用户的角色可使用以下方式授予或撤销：

```
$ etcdctl user grant-role myusername foo
$ etcdctl user revoke-role myusername bar
```

用户设置可通过以下方式检查：

```
$ etcdctl user get myusername
```

用户密码可通过以下方式更改：

```
$ etcdctl user passwd myusername
```

更改密码后，将再次提示输入新密码。当提供选项 `--interactive=false` 时，密码可从标准输入提供。

使用以下命令删除账户：
```
$ etcdctl user delete myusername
```


## 使用角色 {#working-with-roles}

`role` 子命令用于 `etcdctl`，负责处理与特定角色访问控制相关的所有事项，这些权限已授予个别用户。

列出角色：

```
$ etcdctl role list
```

创建新角色，使用：

```
$ etcdctl role add myrolename
```

角色无密码；它仅用于定义一组新的访问权限。

角色被授予对单个键或键范围的访问权限。

范围可指定为区间 [起始键、结束键)，其中起始键在字典序上应小于结束键。

访问权限可授予为读取、写入或两者兼有，例如以下示例所示：

```
# Give read access to a key /foo
$ etcdctl role grant-permission myrolename read /foo

# Give read access to keys with a prefix /foo/. The prefix is equal to the range [/foo/, /foo0)
$ etcdctl role grant-permission myrolename --prefix=true read /foo/

# Give write-only access to the key at /foo/bar
$ etcdctl role grant-permission myrolename write /foo/bar

# Give full access to keys in a range of [key1, key5)
$ etcdctl role grant-permission myrolename readwrite key1 key5

# Give full access to keys with a prefix /pub/
$ etcdctl role grant-permission myrolename --prefix=true readwrite /pub/
```

要查看已授予的权限，可随时查看角色：

```
$ etcdctl role get myrolename
```

权限撤销以相同逻辑方式进行：

```
$ etcdctl role revoke-permission myrolename /foo/bar
```

如移除角色本身：

```
$ etcdctl role delete myrolename
```

## 启用身份认证 {#enabling-authentication}

启用身份认证的最小步骤如下。系统管理员可根据偏好，在启用身份认证之前或之后设置用户和角色。

确保已创建 root 用户：

```
$ etcdctl user add root
Password of root:
```

启用身份认证：

```
$ etcdctl auth enable
```

此后，etcd 已启用身份认证运行。如需出于任何原因禁用身份认证，请使用对应的反向命令：

```
$ etcdctl --user root:rootpw auth disable
```

## 身份认证的安全范围 {#security-scope-of-authentication}

当启用身份认证 `etcdctl auth enable` 时，可保护 V3 gRPC API 操作（get、put、delete、watch 等）。

`/metrics` 和 `/health` HTTP 端点使用独立的处理器，**不**受 V3 RBAC 身份认证保护。此设计允许 Prometheus 和负载均衡器在无需 gRPC 身份认证的情况下抓取指标，同时仍可保护键值数据。

为保障可观测性端点的安全：

- 使用 `--cert-file`、`--key-file` 和 `--client-cert-auth` 启用 mTLS
- 或通过 `--listen-metrics-urls` 将指标绑定到私有接口
- 或使用网络策略/防火墙规则限制访问

## 使用 `etcdctl` 进行身份认证 {#using-etcdctl-to-authenticate}

`etcdctl` 支持与 `curl` 类似的身份认证标志。

```
$ etcdctl --user user:password get foo
```

密码可从提示中获取：

```
$ etcdctl --user user get foo
```

密码也可以从命令行标志 `--password` 获取：

```
$ etcdctl --user user --password password get foo
```


否则，所有 `etcdctl` 命令保持不变。用户和角色仍可创建和修改，但需由具备根角色的用户进行身份认证。

## 使用 TLS 共用名称 {#using-tls-common-name}
从 v3.2 版本起，若 etcd 服务器以选项 `--client-cert-auth=true` 启动，则客户端 TLS 证书中的通用名称（CN）字段将用作 etcd 用户。在此情况下，通用名称用于身份认证，客户端无需提供密码。请注意，若同时满足以下两个条件：1. `--client-cert-auth=true` 被传递且客户端提供了通用名称，以及 2. 客户端提供了用户名和密码，则基于用户名和密码的身份认证将被优先使用。请注意，此功能无法与 gRPC-proxy 或 gRPC-gateway 一同使用。这是因为 gRPC-proxy 会终止来自其客户端的 TLS 连接，导致所有客户端共享代理的证书。gRPC-gateway 内部使用 TLS 连接将 HTTP 请求转换为 gRPC 请求，因此存在相同的限制。因此，客户端无法正确向服务器提供其通用名称。若给定证书的通用名称非空，gRPC-proxy 将报错并停止运行。gRPC-proxy 返回错误，提示客户端证书中包含非空的通用名称。

## 密码强度说明 {#notes-on-password-strength}
`etcdctl` 和 etcd API 在用户创建或更新用户密码操作期间不强制要求特定密码长度。系统管理能力应负责实施此类要求。为避免与密码强度相关的安全风险，可使用 [TLS Common Name 基于的身份认证](#using-tls-common-name)，或通过 `--no-password` 选项创建的用户。

---

反链：

- [将 etcd 从 3.5 降级到 3.4](/zh/docs/etcd/downgrades/downgrade_3_5/)
