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 必须与节点实际被访问的主机名一致,否则节点间握手会因主机名验证失败而拒绝连接。这是实战中最常见的坑。
二、生成证书:从 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 保护之下。下一步需要为内置用户设置密码并配置基于角色的访问控制,使加密通道之上的身份认证与授权真正生效。