无法在 Kubernetes 上使用 Node.Js 连接到 Elasticsearch(证书链中的自签名证书)

问题描述 投票:0回答:6

我有一个 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 应用程序中配置它?

node.js typescript elasticsearch kubernetes microk8s
6个回答
8
投票

解决方案是在创建时配置 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

4
投票

我想在@Florian Ludewig 的回答的基础上提出两点,因为我努力让它在我这边发挥作用。

1.不要关机
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 证书的想法一开始就毫无用处。

2.确保您提供 PEM CA 证书 - 而不是 base64 编码版本

也许您从自己的配置文件中提供 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
  },
});


3
投票

这是 SSL 问题 请尝试禁用SSL验证或向CA申请SSL。 Cloudflare SSL 也是一个不错的选择。

以下是如何禁用 NodeJs 的 SSL 验证

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

1
投票

为了解决您的问题,您需要信任 CA,您应该能够使用 以下 来做到这一点。还发现以下问题在这里

如果您希望将 CA 作为环境变量导入,如所讨论的,您可能可以执行以下操作:

- name: NODE_EXTRA_CA_CERTS
    valueFrom:
      secretKeyRef:
        name: elasticsearch-ca
        key: tls.crt

注意:我没有尝试过上述方法,另一种方法是将秘密作为卷堆起来并以这种方式导入:)

请注意,如果您希望在 Elasticsearch 部署上禁用 TLS,可以按以下步骤操作:

spec:
  http:
    tls:
      selfSignedCertificate:
        disabled: true

请注意,不建议禁用 TLS。


0
投票

我在使用 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
  1. 访问ES集群的端口转发:
kubectl port-forward service/<CUSTOM_NAME>-es-http 9200:9200

  1. ES JS 客户端设置:
    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文件。

  1. 这是最重要的一步。将此行附加到本地计算机上的
    /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/


0
投票

@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')"'"
  }'
© www.soinside.com 2019 - 2024. All rights reserved.