ES集群不识别节点SSL证书的问题处理

问题描述

在启动ES服务并试图加入其他节点上已启动的集群时,出现报错(原文是一大段话,我按语义拆成了几段):

[2024-03-19T16:32:02,844][WARN ][o.e.c.s.DiagnosticTrustManager] [node-2-master] failed to establish trust with server at [<unknown host>]; the server provided a certificate with subject name [CN=workerxx], fingerprint [85e5xxx], no keyUsage and extendedKeyUsage [serverAuth];

the certificate is valid between [2024-03-19T02:34:51Z] and [2026-03-19T02:34:51Z] (current time is [2024-03-19T08:32:02.843439860Z], certificate dates are valid);

the session uses cipher suite [TLS_AES_256_GCM_SHA384] and protocol [TLSv1.3]; the certificate has subject alternative names [DNS:localhost,IP:fe80:0:0:0:xxxx:xx:dfc5:1816,IP:192.xx.xx.xxx,IP:0:0:0:0:0:0:0:1,IP:127.0.0.1,IP:fe80:0:0:0:503:d573:42c0:8add,IP:10.xx.xx.xx,DNS:workerxx];

the certificate is issued by [CN=Elasticsearch security auto-configuration HTTP CA]; the certificate is signed by (subject [CN=Elasticsearch security auto-configuration HTTP CA] fingerprint [0439xxx]) which is self-issued;

the [CN=Elasticsearch security auto-configuration HTTP CA] certificate is not trusted in this ssl context ([xpack.security.transport.ssl (with trust configuration: StoreTrustConfig{path=certs/transport.p12, password=<non-empty>, type=PKCS12, algorithm=PKIX})]);

这段日志警告来自Elasticsearch节点node-2-node2,显示它未能与服务器建立信任连接,具体服务器地址为<unknown host>。服务器提供的证书包含如下信息:

  • 主题名称:CN=workerxx
  • 指纹
  • 该证书没有指定keyUsage和extendedKeyUsage的serverAuth属性
  • 证书日期有效
  • 会话使用的加密套件:TLS_AES_256_GCM_SHA384,协议:TLSv1.3
  • 证书包含了多个主题备用名称(Subject Alternative Names, SANs),包括localhost和其他几个IP地址以及DNS名称workerxx
  • 证书由名为CN=Elasticsearch security auto-configuration HTTP CA的机构颁发
  • 签名证书同样是自签发的(self-issued)

问题分析

日志显示,在Elasticsearch节点node-2-master上出现了SSL握手失败的情况,原因是该节点未能与目标服务器建立信任关系。具体错误是:

当前Elasticsearch节点的SSL上下文(xpack.security.transport.ssl)信任的证书主体也是CN=Elasticsearch security auto-configuration HTTP CA,但已信任的证书指纹与服务器提供的不一致,造成信任链校验失败。

为了解决这个问题,需要采取以下步骤:

  1. 确认节点node-2-master上的SSL配置文件(在这个例子中是certs/transport.p12)是否包含正确的、应该被信任的证书及其链。目前,节点信任的证书指纹是c7bd...,而服务器提供的证书指纹是85e5...

  2. 更新Elasticsearch节点上的信任证书存储以包含正确的CA证书和服务器证书,确保它们能形成有效的信任链。

  3. 检查并确保所有节点间通信使用的都是同一个信任根CA,并且证书链完整无误。

  4. 对于Elasticsearch的安全性配置,尤其是xpack.security.transport.ssl的部分,应确保所有的节点都有相同且正确的信任设置。

但是,这些证书都是自动生成且直接拷贝到其他节点的,并未进行任何修改。并且还有一个现象就是,与master在同一台机器上的ES实例与其通信时是正常的,不过这也可以推测相同机器用的是本地回环地址进行的通信,故信任链在证书中是生效的。

排查与解决

指纹相关?

尝试使用ssh登录远程节点,并记录远程节点的指纹(询问时回答yes),再次尝试无效。

hosts文件记录错误?

由于我的机器是由两个不同网段接通的,一个千兆,一个万兆,故将本地的/etc/hosts文件中的改为配置文件中填写的千兆IP地址,报错相同。

证书文件损坏?

由于本机器的文件都是从其他节点拷贝来的,可能出现损坏,故重新检查和拷贝一遍后问题依然存在。

重新生成ssl证书并分发?

我重新解压开一个编译包,让它自动生成一份http、Transport的p12证书,然后覆盖到所有需要的节点,结果本地节点仍然可以正常启动组成集群,但是另一个机器还是无法通过信任链校验,无法通信,至此,问题有点陷入僵局的意思。

采用token方案进行观察(解决)

翻看之前搭建集群的记录,似乎不能直接把证书拷贝过去使用。由于之前是采用的token的方案,没有过多配置,我先清空远程节点的discovery相关属性,使用token的方案加入集群观察一下。

主节点先生成token:

./bin/elasticsearch-create-enrollment-token -s node

使用指定token的方式加入集群非常顺利,几乎是秒过!

./bin/elasticsearch -d --enrollment-token eyJ2ZXIiOi...

执行上述命令后节点启动正常,并立即识别了远程master节点,顺利加入集群。

这种方案只修改了以下三个配置:

cluster.name: my-app-es
node.name: node-2-master
node.roles: [master,ingest]

需要注意的是,这个服务由于我指定的是master角色,所以没有指定任何端口。

data节点指定端口的尝试

前面新增了一个master角色,现在尝试增加一个data角色。

修改配置:

cluster.name: my-app-es
node.name: node-2-node1
node.roles: [ data, ingest ]
http.port: 9201
transport.port: 9301

之后启动命令与前述相同,节点也顺利加入了集群。

正常情况的总结

观察上述两个过程,启动前certs目录是没有的,启动后自动生成了证书,并且配置自动修改好,显然这个证书就是本地可用的。

拷贝证书再测试

# 从node1拷贝证书到node2,覆盖node2
cp -r node1/config/certs/ node2/config/
cp  node1/config/elasticsearch.keystore node2/config

# 修改node2的配置,主要是增加发现信息,修改端口
cd node2/config/
vim config/elasticsearch.yml

rm -rf data/*
rm -rf logs/*

# 非token方式启动
./bin/elasticsearch -d

这样也是启动成功的,最终集群从3节点变成6个节点了(2主4数),如下图:

总结

所以,绕了一圈SSL证书仅需要在本地生成,踩这个坑主要是由于ES 8.x版本将证书生成进行了自动化(对用户透明了),所以忽略了这一点。这个举措本身是简化用户操作的,因此,建议使用指定token的方式组建集群,不要手动配置证书了。

另外,每个ES节点配置中的发现信息(discovery.seed_hosts)随着加入集群的顺序而改变,后加入的节点会自动添加前面已在集群内的节点信息,但是手动启动的不会自己变更。最后集群的节点配置各不相同,全部重启也不会同步该信息。

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 以下是一段使用 Elasticsearch Java API 编写的三节点 ES 集群代码示例: ``` import org.elasticsearch.client.Client; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.transport.client.PreBuiltTransportClient; import java.net.InetAddress; public class ThreeNodeESCluster { public static void main(String[] args) throws Exception { // 设置集群名称 Settings settings = Settings.builder().put("cluster.name", "my-es-cluster").build(); // 创建一个 TransportClient TransportClient client = new PreBuiltTransportClient(settings); // 添加三个节点 client.addTransportAddress(new TransportAddress(InetAddress.getByName("node1"), 9300)); client.addTransportAddress(new TransportAddress(InetAddress.getByName("node2"), 9300)); client.addTransportAddress(new TransportAddress(InetAddress.getByName("node3"), 9300)); // 关闭客户端连接 client.close(); } } ``` 请注意,在上面的代码中,我们假设您的三个节点分别命名为 `node1`,`node2` 和 `node3`,并且每个节点Elasticsearch 实例在 9300 端口上监听。此外,您需要在代码中添加更多的实际操作,以完整使用 ES 集群。 ### 回答2: 编写一个三节点ES集群代码的例子如下: ``` # 导入必要的模块 from elasticsearch import Elasticsearch # 创建一个ES集群实例 es = Elasticsearch([ {'host': '节点1的IP地址', 'port': '节点1的端口号'}, {'host': '节点2的IP地址', 'port': '节点2的端口号'}, {'host': '节点3的IP地址', 'port': '节点3的端口号'} ]) # 测试连接是否成功 if es.ping(): print("ES集群连接成功!") else: print("ES集群连接失败!") # 示例:创建索引 index_name = "my_index" index_body = { "settings": { "number_of_shards": 1, "number_of_replicas": 1 }, "mappings": { "properties": { "title": {"type": "text"}, "content": {"type": "text"} } } } es.indices.create(index=index_name, body=index_body) print("索引创建成功!") ``` 在这个例子中,我们通过导入`elasticsearch`模块来创建一个ES集群的实例。我们需要指定每个节点的IP地址和端口号来初始化连接。然后,我们可以使用`ping()`方法来测试集群的连接是否成功。 在示例中,我们展示了如何创建一个名为`my_index`的索引。我们使用`create()`方法来指定索引名称和索引的设置和映射。在这里,我们创建了一个只有一个分片和一个副本的索引,并定义了`title`和`content`字段的数据类型。 需要注意的是,这只是一个简单的示例,实际上一个真正的ES集群还需要进行更多的配置和设置,比如使用正确的用户名和密码、配置TLS/SSL等。在实际使用中,还需要根据具体需求来编写更复杂的代码。 ### 回答3: 要编写一个包含三个节点Elasticsearch集群,可以使用以下代码: ```python from elasticsearch import Elasticsearch from elasticsearch.exceptions import ElasticsearchException # 定义集群节点的主机地址和端口 nodes = [ {'host': 'node1', 'port': 9200}, {'host': 'node2', 'port': 9200}, {'host': 'node3', 'port': 9200} ] try: # 创建一个Elasticsearch集群对象 es = Elasticsearch(hosts=nodes) # 检查集群的健康状态 health = es.cluster.health() if health['status'] == 'green': print("集群健康状态:绿色") elif health['status'] == 'yellow': print("集群健康状态:黄色") elif health['status'] == 'red': print("集群健康状态:红色") # 查看集群中的节点信息 nodes_info = es.nodes.info() for node_id, node_info in nodes_info['nodes'].items(): print("节点ID:", node_id) print("主机名:", node_info['name']) print("协议:", node_info['http_address']) print("IP地址:", node_info['ip']) print() except ElasticsearchException as e: print("连接Elasticsearch集群出错:", e) ``` 这段代码使用elasticsearch-py库来与Elasticsearch集群进行通信。首先,定义了三个节点的主机地址和端口。然后,通过`Elasticsearch`类创建了一个Elasticsearch集群对象,并使用指定的节点进行连接。接下来,使用`cluster.health()`方法获取集群的健康状态,并根据状态输出相应的信息。最后,使用`nodes.info()`方法获取集群中所有节点的信息,并逐个输出每个节点的相关信息。 注意,你需要提前安装elasticsearch-py库(使用`pip install elasticsearch`命令)才能运行以上代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1024点线面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值