4. 启用TLS实战

Elasticsearch 集群默认以明文方式通信,这意味着任何能访问到 9200(HTTP)或 9300(传输)端口的人,都可能窃听数据、伪造节点加入集群,甚至直接读取索引内容。TLS 加密要解决的就是这两个层面的问题:传输层(transport)负责节点与节点之间的内部通信,HTTP 层负责客户端与集群之间的外部通信。两者使用不同的证书、不同的端口、不同的信任链,必须分别配置、分别验证。

本节以一套三节点集群为例,节点为 es01、es02、es03,操作系统为 Linux,Elasticsearch 版本为 8.x。示例中的证书、密码、IP 均为教学假设值,实际部署时需替换为真实值。

一、证书体系:先想清楚谁信任谁

TLS 的核心是信任链。Elasticsearch 官方推荐使用 elasticsearch-certutil 工具生成证书,它内置在发行包中,无需额外安装 OpenSSL。整个证书体系分三层:

  • CA 证书(ca.crt / ca.key):自建根证书,是整个信任链的起点,所有节点和客户端都信任它。
  • 节点证书(transport):每个节点一张,用于 9300 端口,证书的 SAN 中必须包含节点的主机名或 IP。
  • HTTP 证书:用于 9200 端口,客户端(如 Kibana、curl、应用)通过它验证服务端身份。

关键点在于:传输层证书的 SAN 必须与节点实际被访问的主机名一致,否则节点间握手会因主机名验证失败而拒绝连接。这是实战中最常见的坑。

Elasticsearch TLS 信任链与通信路径 自建 CA 根证书 ca.crt / ca.key(信任起点) 传输层节点证书 es01/es02/es03 各一张 SAN: 主机名 + IP HTTP 层证书 客户端访问 9200 使用 SAN: 集群访问域名 签发 签发 es01 9300 / 9200 es02 9300 / 9200 es03 9300 / 9200 传输层 TLS(9300,双向证书验证) 客户端 / Kibana curl / 应用 / 浏览器 HTTPS HTTP 层 TLS(9200) 验证要点:节点间握手成功 → 集群状态 green;客户端 HTTPS 访问 → 证书链校验通过,无明文流量 传输层证书 SAN 必须匹配节点主机名,HTTP 证书 SAN 必须匹配客户端访问地址

二、生成证书:从 CA 到节点证书

第一步,生成 CA 证书。执行以下命令,交互式输入密码(示例密码为 CaPass123):

./bin/elasticsearch-certutil ca --pem --out ca.zip
# 解压后得到 ca.crt 和 ca.key

第二步,生成节点证书。准备一个 instances.yml 描述每个节点的身份:

instances:
  - name: es01
    ip: ["192.168.10.11"]
    dns: ["es01.example.com"]
  - name: es02
    ip: ["192.168.10.12"]
    dns: ["es02.example.com"]
  - name: es03
    ip: ["192.168.10.13"]
    dns: ["es03.example.com"]

然后签发:

./bin/elasticsearch-certutil cert --pem \
  --ca ca.crt --ca-key ca.key \
  --in instances.yml --out certs.zip
# 解压后每个节点得到 node.crt 和 node.key

第三步,生成 HTTP 证书。如果客户端通过统一域名 es.example.com 访问,则 SAN 中写入该域名:

./bin/elasticsearch-certutil http --pem \
  --ca ca.crt --ca-key ca.key \
  --dns es.example.com --ip 192.168.10.11

三、配置 elasticsearch.yml

将 CA 证书、节点证书、HTTP 证书分发到各节点的 /etc/elasticsearch/certs/ 目录,并设置权限为 640,属主为 elasticsearch。然后在每个节点的 elasticsearch.yml 中加入以下配置(以 es01 为例):

xpack.security.enabled: true

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.key: certs/es01.key
xpack.security.transport.ssl.certificate: certs/es01.crt
xpack.security.transport.ssl.certificate_authorities: certs/ca.crt

xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: certs/http.key
xpack.security.http.ssl.certificate: certs/http.crt
xpack.security.http.ssl.certificate_authorities: certs/ca.crt

注意 verification_mode 设为 certificate 表示验证证书链但不强制校验主机名;生产环境建议设为 full,强制校验主机名,安全性更高,但要求 SAN 配置绝对准确。

四、启动与验证

依次启动三个节点,观察日志中是否出现 SSL configuration is enabled 以及 published address 使用 https 协议。启动完成后,用 curl 验证 HTTP 层:

curl --cacert ca.crt https://es.example.com:9200/_cluster/health?pretty

预期返回类似:

{
  "cluster_name" : "es-cluster",
  "status" : "green",
  "number_of_nodes" : 3,
  ...
}

如果返回 status: green 且 number_of_nodes: 3,说明节点间传输层 TLS 握手成功,集群完整。若某个节点未加入,通常是该节点证书 SAN 与 discovery.seed_hosts 中配置的主机名不匹配,需检查证书 SAN 与节点配置是否一致。

验证传输层是否真正加密,可以在节点上抓包观察 9300 端口:

tcpdump -i eth0 port 9300 -c 20 -X

若看到的是 TLS 握手记录(16 03 01 开头)而非明文 JSON,说明传输层已加密。同理,抓 9200 端口应看到 TLS 记录而非 HTTP 明文。

验证客户端证书时,如果使用 curl --cacert 仍报 SSL certificate problem,优先检查访问地址是否在 HTTP 证书的 SAN 列表中。SAN 不匹配是 TLS 验证失败最常见的原因。

五、常见问题与判断依据

现象可能原因判断依据
节点无法加入集群传输层证书 SAN 与主机名不符日志出现 No subject alternative names matching
curl 返回证书错误HTTP 证书 SAN 不含访问域名错误信息含 doesn't match any of the subject alternative names
集群状态 red部分节点 TLS 配置缺失_cluster/health 显示节点数少于预期
抓包看到明文SSL 未启用或配置未生效tcpdump 中出现可读 JSON 或 HTTP 头

完成以上配置后,集群的节点间通信和客户端通信均已处于 TLS 保护之下。下一步需要为内置用户设置密码并配置基于角色的访问控制,使加密通道之上的身份认证与授权真正生效。