3. 证书与密钥管理

在 Elasticsearch 集群中启用 TLS 之前,必须先解决一个前置问题:谁来签发证书、证书里写什么身份、私钥放在哪里、各组件如何信任同一套根证书。Elasticsearch 自带的 elasticsearch-certutil 工具正是为这一流程设计的,它把 OpenSSL 中繁琐的 CA 创建、CSR 生成、签名和打包步骤收敛成几条命令,同时生成符合 Elastic 各组件(Elasticsearch、Kibana、Logstash、Beats)要求的证书结构。

本节围绕一条完整链路展开:先用 certutil ca 建立私有 CA,再用 certutil cert 为节点和客户端签发证书,最后把证书、私钥和 CA 证书落到配置目录并写入 elasticsearch.yml。理解这条链路的关键,是分清“谁签发、谁持有、谁信任”三个角色。

一、证书体系中的三个角色

Elasticsearch 的 TLS 信任模型是标准的私有 PKI 模型,包含三类材料:

  • CA 证书与 CA 私钥:由 elasticsearch-certutil ca 生成,是整个信任链的根。CA 证书(elastic-stack-ca.p12)需要分发给所有节点和客户端用于验证对端;CA 私钥只用于签发,必须离线保管,绝不能复制到节点上。
  • 节点证书:由 elasticsearch-certutil cert 用 CA 签发,每个 Elasticsearch 节点一份,包含节点私钥、节点证书和 CA 证书。节点证书的 SAN(Subject Alternative Name)必须覆盖该节点被访问时使用的所有主机名和 IP。
  • 客户端证书:同样由 CA 签发,用于 Kibana、Logstash、Beats 或 REST 客户端向 Elasticsearch 证明身份。当集群开启 xpack.security.authc 的证书认证时,客户端证书的 CN 会被映射为用户名。

三者关系可以用下面的信任链示意:CA 是唯一的签发者,节点证书和客户端证书都由它签发,所有实体都持有同一份 CA 证书作为信任锚。

私有 CA 信任链与证书签发关系 CA(根信任锚) elastic-stack-ca.p12 仅用于签发,离线保管 签发 签发 节点证书(每节点一份) node1.p12 / node2.p12 / node3.p12 含节点私钥 + 节点证书 + CA 证书 SAN:node1.es.local、10.0.0.11 客户端证书 kibana.p12 / logstash.p12 含客户端私钥 + 客户端证书 + CA 证书 CN 映射为认证用户名 所有节点与客户端均持有同一份 CA 证书 用于验证对端证书是否由可信 CA 签发

二、生成 CA

第一步是创建 CA。在任意一台安装了 Elasticsearch 的机器上执行:

cd /usr/share/elasticsearch
bin/elasticsearch-certutil ca \
  --out config/certs/elastic-stack-ca.p12 \
  --pass ""

参数说明:--out 指定输出的 PKCS#12 文件路径;--pass 是 CA 私钥的保护口令,示例中留空表示不加密,生产环境应设置强口令。执行后会生成 elastic-stack-ca.p12,其中同时包含 CA 证书和 CA 私钥。这个文件是整套信任体系的根,建议立即备份到安全位置,之后签发证书时反复使用。

CA 私钥一旦泄露,攻击者可以签发任意被集群信任的证书。因此 elastic-stack-ca.p12 不应长期留在 Elasticsearch 节点上,签发完成后应转移到受控的离线存储。

三、生成节点证书

节点证书需要为每个节点单独签发,且 SAN 必须准确。推荐先用一个实例描述文件批量生成,避免逐台手写命令出错。创建 instance.yml:

instances:
  - name: node1
    ip: ["10.0.0.11"]
    dns: ["node1.es.local", "localhost"]
  - name: node2
    ip: ["10.0.0.12"]
    dns: ["node2.es.local", "localhost"]
  - name: node3
    ip: ["10.0.0.13"]
    dns: ["node3.es.local", "localhost"]

然后执行:

bin/elasticsearch-certutil cert \
  --ca config/certs/elastic-stack-ca.p12 \
  --ca-pass "" \
  --in instance.yml \
  --out config/certs/nodes.zip \
  --pass ""

命令会读取 CA、按 instance.yml 中的 name/ip/dns 生成每个节点的证书,并打包为 nodes.zip。解压后得到 node1/node1.p12、node2/node2.p12、node3/node3.p12,每个文件都包含该节点的私钥、节点证书和 CA 证书。把对应文件分发到各节点的 config/certs/ 目录即可。

这里有一个容易出错的点:如果节点间通信使用主机名,SAN 里就必须有该主机名;如果使用 IP,SAN 里就必须有该 IP。TLS 握手时客户端会校验对端证书的 SAN 是否匹配自己连接的目标地址,不匹配会直接报 No subject alternative names matching IP address 或 No name matching ... found。

四、生成客户端证书

客户端证书用于 Kibana、Logstash、Beats 等组件。以 Kibana 为例:

bin/elasticsearch-certutil cert \
  --ca config/certs/elastic-stack-ca.p12 \
  --ca-pass "" \
  --name kibana \
  --dns kibana.es.local \
  --ip 10.0.0.21 \
  --out config/certs/kibana.p12 \
  --pass ""

与节点证书不同,客户端证书通常用 --name 直接指定 CN。这个 CN 在 Elasticsearch 开启证书认证后会被映射为用户名,因此命名应遵循可识别的规范,例如 kibana、logstash_shipper。若客户端需要访问多个地址,同样用 --dns 和 --ip 补充 SAN。

五、写入 TLS 配置

证书就位后,在 elasticsearch.yml 中配置传输层和 HTTP 层 TLS。传输层用于节点间通信,HTTP 层用于对外 REST 接口:

xpack.security.enabled: true

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: certs/node1.p12
xpack.security.transport.ssl.truststore.path: certs/node1.p12

xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: certs/node1.p12
xpack.security.http.ssl.truststore.path: certs/node1.p12

由于节点证书的 p12 文件中已经包含 CA 证书,truststore.path 可以直接指向同一个文件。如果私钥设置了口令,还需要把口令写入 Elasticsearch keystore:

bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password
bin/elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password
bin/elasticsearch-keystore add xpack.security.http.ssl.truststore.secure_password

六、验证与常见问题

配置完成后重启节点,用以下方式确认 TLS 是否生效:

curl --cacert config/certs/elastic-stack-ca.p12 \
  --cert config/certs/kibana.p12:kibana \
  https://node1.es.local:9200/_cluster/health?pretty

若返回集群健康信息,说明 HTTP 层 TLS 与客户端证书认证均已工作。常见失败及判断依据:

现象原因处理
No subject alternative names matching IP address证书 SAN 未包含连接使用的 IP重新签发证书并补充 --ip
unable to find valid certification path对端不信任签发该证书的 CA确认 truststore 中包含正确的 CA 证书
Keystore was tampered with, or password was incorrectp12 口令错误或未写入 keystore检查 elasticsearch-keystore 中的 secure_password
节点无法加入集群传输层证书 CN/SAN 与节点配置不一致核对 node.name 与证书 SAN
证书管理不是一次性工作。CA 证书和节点证书都有有效期,默认 certutil 生成的证书有效期为 3 年。建议在证书到期前 3 个月规划轮换,轮换时先更新 CA 信任库、再逐节点替换证书,避免集群同时失去信任锚。

把上述流程串起来看,elasticsearch-certutil 的价值在于用统一命令生成结构一致的 p12 文件,使节点、客户端和 CA 之间的信任关系清晰可控。真正需要人工判断的只有三件事:SAN 是否覆盖实际访问地址、CN 是否符合认证映射预期、私钥口令是否安全地进入 keystore。这三点确认无误,TLS 配置基本就能一次通过。