# 安全模式

etcd 支持自动 TLS 以及通过客户端证书的身份验证, 包括客户端到服务器以及对等（服务器到服务器/集群）的通信。

要开始运行，首先要为成员设置 CA 证书和已签名的密钥对。建议为集群中的每个成员创建并签署一个新的密钥对。

为方便起见，[cfssl](https://github.com/cloudflare/cfssl)工具为证书生成提供了简单的界面，我们使用[这里](https://github.com/doczhcn/etcd/tree/7b0933e2f73373e907e67fabb9f04c50c0e48181/hack/tls-setup/README.md)的工具提供了示例。或者，尝试[生成自签名密钥对的指南](https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md)。

## 基本设置

etcd 通过命令行标志或环境变量来设置几个与证书相关的配置选项:

**客户端到服务器端通讯:**

`--cert-file=<path>`: 用于**到** etcd d的 SSL / TLS连接的证书。当设置此选项时，advertise-client-urls 可以使用 HTTPS 模式。

`--key-file=<path>`: 证书的秘钥, 必须是不加密的.

`--client-cert-auth`: 当这个选项被设置时，etcd 将为受信任CA签名的客户端证书检查所有的传入的 HTTPS 请求，不能提供有效客户端证书的请求将会失败。

`--trusted-ca-file=<path>`: 受信任的认证机构

`--auto-tls`: 为客户端的 TLS 连接，使用自动生成的自签名证书

**对等通讯 (服务器到服务器 / 集群):**

对等选项的工作方式与客户端到服务器的选项相同：

`--peer-cert-file=<path>`: 用于对等体之间的 SSL / TLS 连接的证书。这将用于在对等地址上监听以及向其他对等体发送请求

`--peer-key-file=<path>`: 证书的秘钥, 必须是不加密的.

`--peer-client-cert-auth`: 当这个选项被设置时，etcd 将为受信任CA签名的客户端证书检查所有的传入的对等请求。

`--peer-trusted-ca-file=<path>`: 受信任的认证机构.

`--peer-auto-tls`: 为对等体之间的 TLS 连接使用自动生成的自签名证书

如果提供了客户端到服务器或对等证书，则必须设置密钥。所有这些配置选项也可以通过环境变量 “ETCD\_CA\_FILE”，“ETCD\_PEER\_CA\_FILE”等提供。

## 示例 1: 用HTTPS的客户端到服务器端传输安全

为此，准备好CA证书（`ca.crt`）和签名密钥对（`server.crt`, `server.key`）。

Let us configure etcd to provide simple HTTPS transport security step by step:

让我们一步一步配置 etcd 来提供简单的 HTTPS 传输安全：

```bash
$ etcd --name infra0 --data-dir infra0 \
  --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
  --advertise-client-urls=https://127.0.0.1:2379 --listen-client-urls=https://127.0.0.1:2379
```

应该可以启动，可以通过使用 HTTPS 访问 etcd 来测试配置：

```bash
$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
```

该命令应该显示握手成功。 由于我们用自己的证书颁发机构使用自签名证书，CA必须使用`--cacert`选项传递给curl。 另一种可能性是将CA证书添加到系统的可信证书目录（通常位于 `/etc/pki/tls/certs` 或 `/etc/ssl/certs`）中。

**OSX 10.9+ 用户**: curl 7.30.0 在 OSX 10.9+ 不能理解通过命令行传递的证书. 取而代之的，将虚拟 ca.crt 直接导入 keychain 或给 curl 添加`-k` 标志来忽略错误。 要测试没有`-k'标志，运行`open ./fixtures/ca/ca.crt\` 并按照提示进行操作。 测试后请删除此证书！ 如果有解决方法，请告诉我们。

## 示例 2: 用HTTPS客户端证书的客户端到服务器端认证

现在我们已经给了 etcd 客户端验证服务器身份和提供传输安全性的能力。我们也可以使用客户端证书来防止对 etcd 未经授权的访问。

客户端将向服务器提供证书，服务器将检查证书是否由CA签名，并决定是否服务请求。

为此需要第一个示例中提到的相同文件，以及由同一证书颁发机构签名的客户端(`client.crt`, `client.key`) 密钥对。

```bash
$ etcd --name infra0 --data-dir infra0 \
  --client-cert-auth --trusted-ca-file=/path/to/ca.crt --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
  --advertise-client-urls https://127.0.0.1:2379 --listen-client-urls https://127.0.0.1:2379
```

现在尝试发送与上面相同的请求到服务器：

```bash
$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
```

该请求会被服务器拒绝：

```bash
...
routines:SSL3_READ_BYTES:sslv3 alert bad certificate
...
```

要想请求成功，我们需要将CA签名的客户端证书发送给服务器：

```bash
$ curl --cacert /path/to/ca.crt --cert /path/to/client.crt --key /path/to/client.key \
  -L https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
```

输出为：

```bash
...
SSLv3, TLS handshake, CERT verify (15):
...
TLS handshake, Finished (20)
```

还有来自服务器的响应：

```javascript
{
    "action": "set",
    "node": {
        "createdIndex": 12,
        "key": "/foo",
        "modifiedIndex": 12,
        "value": "bar"
    }
}
```

## 示例 3: 集群中的传输安全和客户端证书

对于**对等通信**，etcd 支持与上述相同的模型，这意味着集群中的 etcd 成员之间的通信。

Assuming we have our `ca.crt` and two members with their own keypairs (`member1.crt` & `member1.key`, `member2.crt` & `member2.key`) signed by this CA, we launch etcd as follows:

假设我们有我们的 `ca.crt` 和两个成员，他们有这个CA签名的自己的 keypairs（`member1.crt`＆`member1.key`，`member2.crt`＆`member2.key`），我们如下启动 etcd：

```bash
DISCOVERY_URL=... # from https://discovery.etcd.io/new

# member1
$ etcd --name infra1 --data-dir infra1 \
  --peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member1.crt --peer-key-file=/path/to/member1.key \
  --initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
  --discovery ${DISCOVERY_URL}

# member2
$ etcd --name infra2 --data-dir infra2 \
  --peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member2.crt --peer-key-file=/path/to/member2.key \
  --initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
  --discovery ${DISCOVERY_URL}
```

etcd 成员将组成一个集群，集群中成员之间的所有通信将使用客户端证书进行加密和验证。etcd的输出将显示其连接的地址使用HTTPS。

## Example 4: 自动自签名安全

对于需要通信加密而需要认证的情况，etcd 支持使用自动生成的自签名证书来加密其消息。这样可以简化部署，因为不需要管理 etcd 以外的证书和密钥。

使用标志`--auto-tls`和`--peer-auto-tls` 配置 etcd 为客户端和对等连接使用自签名证书：

```bash
DISCOVERY_URL=... # from https://discovery.etcd.io/new

# member1
$ etcd --name infra1 --data-dir infra1 \
  --auto-tls --peer-auto-tls \
  --initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
  --discovery ${DISCOVERY_URL}

# member2
$ etcd --name infra2 --data-dir infra2 \
  --auto-tls --peer-auto-tls \
  --initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
  --discovery ${DISCOVERY_URL}
```

自签名证书不会对身份进行验证，因此 crul 将返回错误：

```bash
curl: (60) SSL certificate problem: Invalid certificate chain
```

要禁用证书链检查，请使用 `-k` 标志调用curl：

```bash
$ curl -k https://127.0.0.1:2379/v2/keys/foo -Xput -d value=bar -v
```

## etcd proxy 注意事项

如果连接是安全的，etcd proxy 从其客户端终止TLS，并且使用 \`--peer-key-file' 和 '--peer-cert-file' 中指定的代理自己的密钥/证书与 etcd 成员进行通信。

proxy 通过给定成员的 `--advertise-client-urls` 和 `--advertise-peer-urls` 与 etcd 成员进行通信。它将客户端请求转发到 etcd 成员的 advertised client url，并通过 etcd 成员的 advertised peer url 同步初始集群配置。

当 etcd 成员启用客户端身份验证时，管理员必须确保代理的 `--peer-cert-file` 选项中指定的对等证书对于该验证是有效的。如果启用对等身份验证，proxy 的对等证书也必须对对等身份验证有效。

## FAQ

### 使用TLS客户端身份验证时，我看到 SSLv3 警报握手失败？

`golang` 的 `crypto / tls` 包在使用它之前检查证书公钥的 `key usage`。

要使用证书公钥进行客户端认证，我们需要在创建证书公钥时将 `clientAuth` 添加到 `Extended Key Usage`。

这是怎么做的:

1. 将以下部分添加到 openssl.cnf:

   ```bash
    [ ssl_client ]
    ...
      extendedKeyUsage = clientAuth
    ...
   ```
2. 创建证书时，确保在 `-extensions` 标志中引用它:

   ```bash
    $ openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr
   ```

### 使用对等证书认证，我收到"证书对127.0.0.1有效，而不是$MY\_IP"

确保使用成员的公共IP地址为 Subject 名称来签署证书。例如 `etcd-ca` 工具为它的 `new-cert` 命令提供 `--ip=` 选项。

证书需要在其 Subject 名称中为成员的 FQDN 签名，使用 Subject Alternative Names（短IP SAN）来添加IP地址。`etcd-ca` 工具为它的 `new-cert` 命令提供 `--domain=` 选项，而且 openssl 也可以[这样实现](http://wiki.cacert.org/FAQ/subjectAltName)。


---

# Agent Instructions: 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:

```
GET https://doczhcn.gitbook.io/etcd/index/index-1/security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
