我有一个 NodeJs 应用程序在 Kubernetes 集群内运行(我使用的是 microk8s)。我还按照官方步骤在 Kubernetes 上设置 Elasticsearch。
但我无法连接到 Elasticsearch 集群。我收到此错误:
ConnectionError: self signed certificate in certificate chain
这是我的连接的代码片段:
const client = new elasticsearch.Client({
node: process.env.elasticsearch_node,
// https://elasticsearch-es-http.default.svc.cluster.local:9200
});
我在这里创建了此问题的最小重现:https://github.com/flolu/elasticsearch-k8s-connection。 (设置说明位于自述文件中)
基本上,在 Docker compose 中运行 Elasticsearch 时一切正常,但在 Kubernetes 中运行时无法连接。
出现这种情况的原因可能是因为我没有正确设置TLS证书,但我还没有找到任何相关信息。 我是否在创建 ES 客户端时或在集群级别在 NodeJs 应用程序中配置它?
解决方案是在创建时配置 SSL 和 Elastic 用户
Client
const client = new elasticsearch.Client({
node: process.env.elasticsearch_node,
auth: {
username: "elastic",
password: process.env.elasticsearch_password || "changeme",
},
ssl: {
ca: process.env.elasticsearch_certificate,
rejectUnauthorized: false,
},
});
密码和证书由Elastic提供。它们存储在 Kubernetes 秘密中。 所以我刚刚通过环境变量将密码和证书传递到我的 NodeJs 服务中,如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: search-deployment
spec:
selector:
matchLabels:
app: search
replicas: 1
template:
metadata:
labels:
app: search
spec:
containers:
- name: search
image: search:placeholder_name
imagePullPolicy: Always
env:
- name: elasticsearch_node
value: https://elasticsearch-es-http.default.svc.cluster.local:9200
- name: elasticsearch_certificate
valueFrom:
secretKeyRef:
name: elasticsearch-es-http-ca-internal
key: tls.crt
- name: elasticsearch_password
valueFrom:
secretKeyRef:
name: elasticsearch-es-elastic-user
key: elastic
我想在@Florian Ludewig 的回答的基础上提出两点,因为我努力让它在我这边发挥作用。
rejectUnauthorized
const client = new elasticsearch.Client({
node: 'node httpS url here',
ssl: {
ca: process.env.elasticsearch_certificate,
rejectUnauthorized: true, // <-- this is important
},
});
如果将
rejectUnauthorized
设置为 false,底层的 Nodejs https 代理将绕过证书检查。当然,如果您对集群的安全性有信心,您可以禁用它,但这使得提供 CA 证书的想法一开始就毫无用处。
也许您从自己的配置文件中提供 CA 证书,而不使用 Kubernetes 的秘密注入 - 可能是因为 ES 客户端应用程序位于不同的命名空间中,因此无法访问 CA 秘密。
在这种情况下,您可能会发现将 CA 证书作为 Base64 字符串存储在配置文件中很有用,但您不应该忘记向客户端提供解码后的字符串:
const config = loadConfigFromFile('config.yml');
const caCertificate = Buffer.from(config.base64CaCertificate, 'base64').toString();
const client = new elasticsearch.Client({
node: 'node httpS url here',
ssl: {
ca: caCertificate,
rejectUnauthorized: true
},
});
这是 SSL 问题 请尝试禁用SSL验证或向CA申请SSL。 Cloudflare SSL 也是一个不错的选择。
以下是如何禁用 NodeJs 的 SSL 验证
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
为了解决您的问题,您需要信任 CA,您应该能够使用 以下 来做到这一点。还发现以下问题在这里。
如果您希望将 CA 作为环境变量导入,如所讨论的,您可能可以执行以下操作:
- name: NODE_EXTRA_CA_CERTS
valueFrom:
secretKeyRef:
name: elasticsearch-ca
key: tls.crt
注意:我没有尝试过上述方法,另一种方法是将秘密作为卷堆起来并以这种方式导入:)
请注意,如果您希望在 Elasticsearch 部署上禁用 TLS,可以按以下步骤操作:
spec:
http:
tls:
selfSignedCertificate:
disabled: true
请注意,不建议禁用 TLS。
我在使用 Elasticsearch JS 客户端的 Minikube 中遇到了这个错误:
getaddrinfo ENOTFOUND <CUSTOM_NAME>-es-default-0.<CUSTOM_NAME>-es-default.default.svc {"name":"ConnectionError","meta":{"body":null,"statusCode":null,"headers":null,"meta":{"context":null,"request":{"params":{"method":"HEAD","path":"/xxx","body":null,"querystring":"","headers":{"user-agent":"elasticsearch-js/7.12.0 (linux 5.11.16-arch1-1-x64; Node.js v16.1.0)","x-elastic-client-meta":"es=7.12.0,js=16.1.0,t=7.12.0,hc=16.1.0"},"timeout":30000},"options":{},"id":2}
以下解决方案不需要使用自签名证书在客户端禁用 TLS 验证。
es-stack yaml:
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: <CUSTOM_NAME>
namespace: default
spec:
version: 7.12.1
auth:
roles:
- secretName: roles
fileRealm:
- secretName: filerealm
http:
tls:
selfSignedCertificate:
subjectAltNames:
- ip: 127.0.0.1
kubectl port-forward service/<CUSTOM_NAME>-es-http 9200:9200
const client = new Client(
{
node: `https://127.0.0.1:9200`,
sniffOnStart: true,
ConnectionPool: MyConnectionPool,
auth: {
username,
password,
},
ssl: {
ca: fs.readFileSync("/tmp/ca.pem"),
rejectUnauthorized: true,
},
};
)
我使用
ca.crt
中的 <CUSTOM_NAME>-es-http-certs-public
键来表示 /tmp/ca.pem
。如果在启动node.js之前将系统环境NODE_EXTRA_CA_CERTS
设置为/tmp/ca.pem
,你也可以摆脱这一行,如下所示:
NODE_EXTRA_CA_CERTS=/tmp/ca.pem node index.js
如果您不确定需要参考哪个CA证书,只需ssh进入ES容器,然后检查
config/http-certs/
中的ca.crt文件。
/etc/hosts
。127.0.0.1 <CUSTOM_NAME>-es-default-0.<CUSTOM_NAME>-es-default.default.svc
你应该可以走了。在阅读了有关 Ingress 的教程后,我想到了这个想法:
https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/
@Florian 的回答对我帮助很大,但是我需要对当前版本的 npm elasticsearch 模块进行一些更新。
const client = new elasticsearch.Client({
node: ES_NODE,
auth: {
username: 'elastic',
password: ELASTIC_PASSWORD
},
tls: {
ca: ES_CERT,
rejectUnauthorized: false,
}
})
注意证书是通过字段tls而不是ssl传递的。
我还创建了一个pod来测试与curl的连接。请注意,pod 使用简单的 url https://quickstart-es-http:9200 而不是完整的服务 url https://quickstart-es-http.default.svc.cluster.local:9200
apiVersion: v1
kind: Pod
metadata:
name: curl-with-env
spec:
containers:
- name: curl
image: alpine/curl
command: ["/bin/sh", "-c", "trap : TERM INT; sleep infinity & wait"]
env:
- name: ES_NODE
value: https://quickstart-es-http:9200
- name: ES_CERT
valueFrom:
secretKeyRef:
name: quickstart-es-http-ca-internal
key: tls.crt
- name: ELASTIC
valueFrom:
secretKeyRef:
name: quickstart-es-elastic-user
key: elastic
进入curl-with-env pod并使用此curl来确认您有连接:
curl -v \
--cacert <(echo "$ES_CERT") \
-u "elastic:$ELASTIC" \
"$ES_NODE/_search?pretty"
尝试使用curl来上传数据:
curl -X POST \
--cacert <(echo "$ES_CERT") \
-u "elastic:$ELASTIC" \
-H "Content-Type: application/json" \
"$ES_NODE/test-index-1/_doc" \
-d '{
"title": "Example Document",
"content": "This is a sample document to upload to Elasticsearch.",
"timestamp": "'"$(date -u +'%Y-%m-%dT%H:%M:%SZ')"'"
}'