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

Can't connect to Elasticsearch with Node.Js on Kubernetes (self signed certificate in certificate chain)

我在 Kubernetes 集群中有一个 NodeJs 应用程序 运行(我正在使用 microk8s). I've also followed the official steps 在 Kubernetes 上设置 Elasticsearch。

问题

但是我无法连接到Elasticsearch 集群。我收到此错误:

ConnectionError: self signed certificate in certificate chain

这是我的 code snippet 连接:

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。 (设置说明在自述文件中)

基本上,当 运行 Elasticsearch inside Docker compose 时一切正常,但当 运行 inside Kubernetes 时我无法连接。

这可能是因为我没有正确设置TLS证书,但我没有找到任何相关信息。 我是在创建 ES 客户端时还是在集群级别在我的 NodeJs 应用程序中配置它?

为了解决您的问题,您需要信任 CA,您应该可以使用 following. Also found the following

如果您希望按照讨论的方式将 CA 作为环境变量导入,您可以这样做:

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

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

请注意,如果您希望在 Elasticsearch 部署中禁用 TLS,您可以按以下方式进行:

spec:
  http:
    tls:
      selfSignedCertificate:
        disabled: true

请注意,不建议禁用 TLS。

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

以下是为 NodeJs 禁用验证 SSL 的方法

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

解决方案是在创建 Client

时配置 SSL 和 Elastic 用户
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 的回答的基础上进一步阐述 2 点,因为我努力让它在我这边工作。

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 编码版本

也许您在没有使用 Kubernetes 的秘密注入的情况下从您自己的配置文件提供 CA 证书 - 可能是因为 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
  },
});

我 运行 使用 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,
        },
      };
    )

我在 <CUSTOM_NAME>-es-http-certs-public 中使用 ca.crt 键作为 /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/