使用 TLS 证书认证添加远程集群

编辑

使用 TLS 证书认证添加远程集群

编辑

要使用 TLS 证书认证添加远程集群:

如果您遇到任何问题,请参考 故障排除

先决条件

编辑
  1. 需要在两个集群的每个节点上启用 Elasticsearch 安全功能。默认情况下启用安全性。如果已禁用,请在 elasticsearch.yml 中将 xpack.security.enabled 设置为 true。请参考 常规安全设置
  2. 本地和远程集群的版本必须兼容。

    • 任何节点都可以与相同主版本的另一个节点通信。例如,7.0 可以与任何 7.x 节点通信。
    • 只有特定主版本的最后一个次要版本的节点才能与下一个主版本的节点通信。在 6.x 系列中,6.8 可以与任何 7.x 节点通信,而 6.7 只能与 7.0 通信。
    • 版本兼容性是对称的,这意味着如果 6.7 可以与 7.0 通信,则 7.0 也可以与 6.7 通信。下表描述了本地和远程节点之间的版本兼容性。

      版本兼容性表

      本地集群

      远程集群

      5.0–5.5

      5.6

      6.0–6.6

      6.7

      6.8

      7.0

      7.1–7.16

      7.17

      8.0–8.16

      5.0–5.5

      Yes

      Yes

      No

      No

      No

      No

      No

      No

      No

      5.6

      Yes

      Yes

      Yes

      Yes

      Yes

      No

      No

      No

      No

      6.0–6.6

      No

      Yes

      Yes

      Yes

      Yes

      No

      No

      No

      No

      6.7

      No

      Yes

      Yes

      Yes

      Yes

      Yes

      No

      No

      No

      6.8

      No

      Yes

      Yes

      Yes

      Yes

      Yes

      Yes

      Yes

      No

      7.0

      No

      No

      No

      Yes

      Yes

      Yes

      Yes

      Yes

      No

      7.1–7.16

      No

      No

      No

      No

      Yes

      Yes

      Yes

      Yes

      No

      7.17

      No

      No

      No

      No

      Yes

      Yes

      Yes

      Yes

      Yes

      8.0–8.16

      No

      No

      No

      No

      No

      No

      No

      Yes

      Yes

      Elastic 仅支持这些配置中的一个子集上的跨集群搜索。请参阅 支持的跨集群搜索配置

与远程集群建立信任关系

编辑

为了安全地使用跨集群复制或跨集群搜索远程集群,请在所有连接的集群上启用安全性并在每个节点上配置传输层安全性 (TLS)。在传输接口上配置 TLS 安全性是远程集群的最低要求。为了提高安全性,也可以在 HTTP 接口 上配置 TLS。

所有连接的集群都必须相互信任,并在传输接口上使用 TLS 进行相互认证。这意味着本地集群信任远程集群的证书颁发机构 (CA),而远程集群信任本地集群的 CA。建立连接时,所有节点都将验证来自另一侧节点的证书。这种相互信任对于安全连接远程集群是必需的,因为所有连接的节点实际上形成了一个单一的安全域。

用户身份验证在本地集群上执行,并将用户及其角色名称传递到远程集群。远程集群会根据其本地角色定义检查用户的角色名称,以确定用户允许访问哪些索引。

在使用安全 Elasticsearch 集群进行跨集群复制或跨集群搜索之前,请完成以下配置任务:

  1. 在每个节点上配置传输层安全性 (TLS) 以加密节点间流量并在本地集群中的节点与所有远程集群中的节点之间进行身份验证。有关配置安全性的必要步骤,请参考 为 Elastic Stack 设置基本安全性

    此过程使用相同的 CA 为所有节点生成证书。或者,您可以将本地集群的证书添加到每个远程集群中作为受信任的 CA。您还必须将远程集群的证书添加到本地集群中作为受信任的 CA。使用相同的 CA 为所有节点生成证书可以简化此任务。

连接到远程集群

编辑

您必须具有 manage 集群权限才能连接远程集群。

本地集群使用 传输接口 与远程集群建立通信。本地集群中的协调节点与远程集群中的特定节点建立 长期 TCP 连接。Elasticsearch 要求这些连接保持打开状态,即使连接在较长时间内处于空闲状态。

要在 Kibana 的 Stack Management 中添加远程集群:

  1. 从侧边导航中选择 远程集群
  2. 输入远程集群的名称(集群别名)。
  3. 指定 Elasticsearch 端点 URL,或远程集群的 IP 地址或主机名,后跟传输端口(默认为 9300)。例如,cluster.es.eastus2.staging.azure.foundit.no:9300192.168.1.1:9300

或者,使用 集群更新设置 API 添加远程集群。您还可以使用此 API 动态配置本地集群中每个节点的远程集群。要在本地集群中的各个节点上配置远程集群,请为每个节点在 elasticsearch.yml 中定义静态设置。

以下请求添加了一个别名为 cluster_one 的远程集群。此集群别名是表示与远程集群连接的唯一标识符,用于区分本地和远程索引。

resp = client.cluster.put_settings(
    persistent={
        "cluster": {
            "remote": {
                "cluster_one": {
                    "seeds": [
                        "127.0.0.1:{remote-interface-default-port}"
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.cluster.putSettings({
  persistent: {
    cluster: {
      remote: {
        cluster_one: {
          seeds: ["127.0.0.1:{remote-interface-default-port}"],
        },
      },
    },
  },
});
console.log(response);
PUT /_cluster/settings
{
  "persistent" : {
    "cluster" : {
      "remote" : {
        "cluster_one" : {    
          "seeds" : [
            "127.0.0.1:9300" 
          ]
        }
      }
    }
  }
}

此远程集群的集群别名为 cluster_one

指定远程集群中种子节点的主机名和传输端口。

您可以使用 远程集群信息 API 验证本地集群是否已成功连接到远程集群。

resp = client.cluster.remote_info()
print(resp)
response = client.cluster.remote_info
puts response
const response = await client.cluster.remoteInfo();
console.log(response);
GET /_remote/info

API 响应指示本地集群已连接到集群别名为 cluster_one 的远程集群。

{
  "cluster_one" : {
    "seeds" : [
      "127.0.0.1:9300"
    ],
    "connected" : true,
    "num_nodes_connected" : 1,  
    "max_connections_per_cluster" : 3,
    "initial_connect_timeout" : "30s",
    "skip_unavailable" : true, 
    "mode" : "sniff"
  }
}

本地集群连接到的远程集群中的节点数。

指示是否在通过跨集群搜索进行搜索但没有节点可用时跳过远程集群。

动态配置远程集群

编辑

使用 集群更新设置 API 动态配置集群中每个节点上的远程设置。以下请求添加三个远程集群:cluster_onecluster_twocluster_three

seeds 参数指定远程集群中种子节点的主机名和 传输端口(默认为 9300)。

mode 参数确定配置的连接模式,默认为 sniffcluster_one 没有指定 mode,因此它使用默认值。cluster_twocluster_three 都明确使用不同的模式。

resp = client.cluster.put_settings(
    persistent={
        "cluster": {
            "remote": {
                "cluster_one": {
                    "seeds": [
                        "127.0.0.1:{remote-interface-default-port}"
                    ]
                },
                "cluster_two": {
                    "mode": "sniff",
                    "seeds": [
                        "127.0.0.1:{remote-interface-default-port-plus1}"
                    ],
                    "transport.compress": True,
                    "skip_unavailable": True
                },
                "cluster_three": {
                    "mode": "proxy",
                    "proxy_address": "127.0.0.1:{remote-interface-default-port-plus2}"
                }
            }
        }
    },
)
print(resp)
const response = await client.cluster.putSettings({
  persistent: {
    cluster: {
      remote: {
        cluster_one: {
          seeds: ["127.0.0.1:{remote-interface-default-port}"],
        },
        cluster_two: {
          mode: "sniff",
          seeds: ["127.0.0.1:{remote-interface-default-port-plus1}"],
          "transport.compress": true,
          skip_unavailable: true,
        },
        cluster_three: {
          mode: "proxy",
          proxy_address: "127.0.0.1:{remote-interface-default-port-plus2}",
        },
      },
    },
  },
});
console.log(response);
PUT _cluster/settings
{
  "persistent": {
    "cluster": {
      "remote": {
        "cluster_one": {
          "seeds": [
            "127.0.0.1:9300"
          ]
        },
        "cluster_two": {
          "mode": "sniff",
          "seeds": [
            "127.0.0.1:9301"
          ],
          "transport.compress": true,
          "skip_unavailable": true
        },
        "cluster_three": {
          "mode": "proxy",
          "proxy_address": "127.0.0.1:9302"
        }
      }
    }
  }
}

您可以在初始配置后动态更新远程集群的设置。以下请求更新 cluster_two 的压缩设置,以及 cluster_three 的压缩和 ping 计划设置。

当压缩或 ping 计划设置更改时,所有现有节点连接都必须关闭并重新打开,这可能会导致正在进行的请求失败。

resp = client.cluster.put_settings(
    persistent={
        "cluster": {
            "remote": {
                "cluster_two": {
                    "transport.compress": False
                },
                "cluster_three": {
                    "transport.compress": True,
                    "transport.ping_schedule": "60s"
                }
            }
        }
    },
)
print(resp)
response = client.cluster.put_settings(
  body: {
    persistent: {
      cluster: {
        remote: {
          cluster_two: {
            'transport.compress' => false
          },
          cluster_three: {
            'transport.compress' => true,
            'transport.ping_schedule' => '60s'
          }
        }
      }
    }
  }
)
puts response
const response = await client.cluster.putSettings({
  persistent: {
    cluster: {
      remote: {
        cluster_two: {
          "transport.compress": false,
        },
        cluster_three: {
          "transport.compress": true,
          "transport.ping_schedule": "60s",
        },
      },
    },
  },
});
console.log(response);
PUT _cluster/settings
{
  "persistent": {
    "cluster": {
      "remote": {
        "cluster_two": {
          "transport.compress": false
        },
        "cluster_three": {
          "transport.compress": true,
          "transport.ping_schedule": "60s"
        }
      }
    }
  }
}

您可以通过为每个远程集群设置传递 null 值来从集群设置中删除远程集群。以下请求从集群设置中删除 cluster_two,保留 cluster_onecluster_three

resp = client.cluster.put_settings(
    persistent={
        "cluster": {
            "remote": {
                "cluster_two": {
                    "mode": None,
                    "seeds": None,
                    "skip_unavailable": None,
                    "transport.compress": None
                }
            }
        }
    },
)
print(resp)
response = client.cluster.put_settings(
  body: {
    persistent: {
      cluster: {
        remote: {
          cluster_two: {
            mode: nil,
            seeds: nil,
            skip_unavailable: nil,
            'transport.compress' => nil
          }
        }
      }
    }
  }
)
puts response
const response = await client.cluster.putSettings({
  persistent: {
    cluster: {
      remote: {
        cluster_two: {
          mode: null,
          seeds: null,
          skip_unavailable: null,
          "transport.compress": null,
        },
      },
    },
  },
});
console.log(response);
PUT _cluster/settings
{
  "persistent": {
    "cluster": {
      "remote": {
        "cluster_two": {
          "mode": null,
          "seeds": null,
          "skip_unavailable": null,
          "transport.compress": null
        }
      }
    }
  }
}

静态配置远程集群

编辑

如果您在 elasticsearch.yml 中指定设置,则只有具有这些设置的节点才能连接到远程集群并服务远程集群请求。

使用 集群更新设置 API 指定的远程集群设置优先于您为各个节点在 elasticsearch.yml 中指定的设置。

在以下示例中,cluster_onecluster_twocluster_three 是表示与每个集群连接的任意集群别名。这些名称随后用于区分本地和远程索引。

cluster:
    remote:
        cluster_one:
            seeds: 127.0.0.1:9300
        cluster_two:
            mode: sniff
            seeds: 127.0.0.1:9301
            transport.compress: true      
            skip_unavailable: true        
        cluster_three:
            mode: proxy
            proxy_address: 127.0.0.1:9302 

明确为对 cluster_two 的请求启用了压缩。

断开的远程集群对于 cluster_two 是可选的。

用于连接到 cluster_three 的代理端点的地址。

为远程集群配置角色和用户

编辑

连接远程集群 后,您需要在本地和远程集群上创建用户角色并分配必要的权限。这些角色是使用跨集群复制和跨集群搜索所必需的。

您必须在本地和远程集群上使用相同的角色名称。例如,以下跨集群复制配置在本地和远程集群上都使用 remote-replication 角色名称。但是,您可以在每个集群上指定不同的角色定义。

您可以通过 Kibana 的堆栈管理,选择侧边导航栏中的安全 > 角色来管理用户和角色。您也可以使用角色管理 API动态添加、更新、删除和检索角色。当您使用 API 管理native领域中的角色时,这些角色将存储在 Elasticsearch 的内部索引中。

以下请求使用创建或更新角色 API。您必须至少拥有manage_security集群权限才能使用此 API。

配置跨集群复制的权限

编辑

跨集群复制用户需要在远程集群和本地集群上拥有不同的集群和索引权限。使用以下请求在本地和远程集群上创建单独的角色,然后创建一个具有所需角色的用户。

远程集群
编辑

在包含领导者索引的远程集群上,跨集群复制角色需要read_ccr集群权限,以及领导者索引上的monitorread权限。

如果请求使用API 密钥进行身份验证,则 API 密钥需要在本地集群上拥有上述权限,而不是远程集群。

如果请求是代表其他用户发出的,则身份验证用户必须在远程集群上拥有run_as权限。

以下请求在远程集群上创建一个remote-replication角色

resp = client.security.put_role(
    name="remote-replication",
    cluster=[
        "read_ccr"
    ],
    indices=[
        {
            "names": [
                "leader-index-name"
            ],
            "privileges": [
                "monitor",
                "read"
            ]
        }
    ],
)
print(resp)
const response = await client.security.putRole({
  name: "remote-replication",
  cluster: ["read_ccr"],
  indices: [
    {
      names: ["leader-index-name"],
      privileges: ["monitor", "read"],
    },
  ],
});
console.log(response);
POST /_security/role/remote-replication
{
  "cluster": [
    "read_ccr"
  ],
  "indices": [
    {
      "names": [
        "leader-index-name"
      ],
      "privileges": [
        "monitor",
        "read"
      ]
    }
  ]
}
本地集群
编辑

在包含跟随者索引的本地集群上,remote-replication角色需要manage_ccr集群权限,以及跟随者索引上的monitorreadwritemanage_follow_index权限。

以下请求在本地集群上创建一个remote-replication角色

resp = client.security.put_role(
    name="remote-replication",
    cluster=[
        "manage_ccr"
    ],
    indices=[
        {
            "names": [
                "follower-index-name"
            ],
            "privileges": [
                "monitor",
                "read",
                "write",
                "manage_follow_index"
            ]
        }
    ],
)
print(resp)
const response = await client.security.putRole({
  name: "remote-replication",
  cluster: ["manage_ccr"],
  indices: [
    {
      names: ["follower-index-name"],
      privileges: ["monitor", "read", "write", "manage_follow_index"],
    },
  ],
});
console.log(response);
POST /_security/role/remote-replication
{
  "cluster": [
    "manage_ccr"
  ],
  "indices": [
    {
      "names": [
        "follower-index-name"
      ],
      "privileges": [
        "monitor",
        "read",
        "write",
        "manage_follow_index"
      ]
    }
  ]
}

在每个集群上创建remote-replication角色后,使用创建或更新用户 API在本地集群上创建一个用户并分配remote-replication角色。例如,以下请求将remote-replication角色分配给名为cross-cluster-user的用户

resp = client.security.put_user(
    username="cross-cluster-user",
    password="l0ng-r4nd0m-p@ssw0rd",
    roles=[
        "remote-replication"
    ],
)
print(resp)
const response = await client.security.putUser({
  username: "cross-cluster-user",
  password: "l0ng-r4nd0m-p@ssw0rd",
  roles: ["remote-replication"],
});
console.log(response);
POST /_security/user/cross-cluster-user
{
  "password" : "l0ng-r4nd0m-p@ssw0rd",
  "roles" : [ "remote-replication" ]
}

您只需要在本地集群上创建此用户。

然后,您可以配置跨集群复制以跨数据中心复制您的数据。

配置跨集群搜索的权限

编辑

跨集群搜索用户需要在远程集群和本地集群上拥有不同的集群和索引权限。以下请求在本地和远程集群上创建单独的角色,然后创建一个具有所需角色的用户。

远程集群
编辑

在远程集群上,跨集群搜索角色需要目标索引的readread_cross_cluster权限。

如果请求使用API 密钥进行身份验证,则 API 密钥需要在本地集群上拥有上述权限,而不是远程集群。

如果请求是代表其他用户发出的,则身份验证用户必须在远程集群上拥有run_as权限。

以下请求在远程集群上创建一个remote-search角色

resp = client.security.put_role(
    name="remote-search",
    indices=[
        {
            "names": [
                "target-indices"
            ],
            "privileges": [
                "read",
                "read_cross_cluster"
            ]
        }
    ],
)
print(resp)
const response = await client.security.putRole({
  name: "remote-search",
  indices: [
    {
      names: ["target-indices"],
      privileges: ["read", "read_cross_cluster"],
    },
  ],
});
console.log(response);
POST /_security/role/remote-search
{
  "indices": [
    {
      "names": [
        "target-indices"
      ],
      "privileges": [
        "read",
        "read_cross_cluster"
      ]
    }
  ]
}
本地集群
编辑

在本地集群(用于启动跨集群搜索的集群)上,用户只需要remote-search角色。角色权限可以为空。

以下请求在本地集群上创建一个remote-search角色

resp = client.security.put_role(
    name="remote-search",
)
print(resp)
const response = await client.security.putRole({
  name: "remote-search",
});
console.log(response);
POST /_security/role/remote-search
{}

在每个集群上创建remote-search角色后,使用创建或更新用户 API在本地集群上创建一个用户并分配remote-search角色。例如,以下请求将remote-search角色分配给名为cross-search-user的用户

resp = client.security.put_user(
    username="cross-search-user",
    password="l0ng-r4nd0m-p@ssw0rd",
    roles=[
        "remote-search"
    ],
)
print(resp)
const response = await client.security.putUser({
  username: "cross-search-user",
  password: "l0ng-r4nd0m-p@ssw0rd",
  roles: ["remote-search"],
});
console.log(response);
POST /_security/user/cross-search-user
{
  "password" : "l0ng-r4nd0m-p@ssw0rd",
  "roles" : [ "remote-search" ]
}

您只需要在本地集群上创建此用户。

拥有remote-search角色的用户可以跨集群搜索

配置跨集群搜索和 Kibana 的权限

编辑

当使用 Kibana 跨多个集群搜索时,一个两步授权流程决定用户是否可以访问远程集群上的数据流和索引。

  • 首先,本地集群确定用户是否有权访问远程集群。本地集群是 Kibana 连接到的集群。
  • 如果用户已授权,则远程集群将确定用户是否可以访问指定的数据流和索引。

要授予 Kibana 用户访问远程集群的权限,请为其分配一个本地角色,该角色具有对远程集群上索引的读取权限。您将远程集群中的数据流和索引指定为<remote_cluster_name>:<target>

要授予用户对远程数据流和索引的读取权限,您必须在远程集群上创建一个匹配的角色,该角色授予具有访问相应数据流和索引权限的read_cross_cluster权限。

例如,您可能正在本地集群上积极索引 Logstash 数据,并定期将较旧的基于时间的索引卸载到远程集群上的存档中。您希望跨两个集群进行搜索,因此必须在两个集群上启用 Kibana 用户。

本地集群
编辑

在本地集群上,创建一个logstash-reader角色,该角色授予本地logstash-*索引上的readview_index_metadata权限。

如果您将本地集群配置为 Elasticsearch 中的另一个远程集群,则本地集群上的logstash-reader角色也需要授予read_cross_cluster权限。

resp = client.security.put_role(
    name="logstash-reader",
    indices=[
        {
            "names": [
                "logstash-*"
            ],
            "privileges": [
                "read",
                "view_index_metadata"
            ]
        }
    ],
)
print(resp)
const response = await client.security.putRole({
  name: "logstash-reader",
  indices: [
    {
      names: ["logstash-*"],
      privileges: ["read", "view_index_metadata"],
    },
  ],
});
console.log(response);
POST /_security/role/logstash-reader
{
  "indices": [
    {
      "names": [
        "logstash-*"
        ],
        "privileges": [
          "read",
          "view_index_metadata"
          ]
    }
  ]
}

为您的 Kibana 用户分配一个角色,该角色授予对 Kibana 的访问权限,以及您的logstash_reader角色。例如,以下请求创建cross-cluster-kibana用户并分配kibana-accesslogstash-reader角色。

resp = client.security.put_user(
    username="cross-cluster-kibana",
    password="l0ng-r4nd0m-p@ssw0rd",
    roles=[
        "logstash-reader",
        "kibana-access"
    ],
)
print(resp)
const response = await client.security.putUser({
  username: "cross-cluster-kibana",
  password: "l0ng-r4nd0m-p@ssw0rd",
  roles: ["logstash-reader", "kibana-access"],
});
console.log(response);
PUT /_security/user/cross-cluster-kibana
{
  "password" : "l0ng-r4nd0m-p@ssw0rd",
  "roles" : [
    "logstash-reader",
    "kibana-access"
    ]
}
远程集群
编辑

在远程集群上,创建一个logstash-reader角色,该角色授予read_cross_cluster权限以及logstash-*索引的readview_index_metadata权限。

resp = client.security.put_role(
    name="logstash-reader",
    indices=[
        {
            "names": [
                "logstash-*"
            ],
            "privileges": [
                "read_cross_cluster",
                "read",
                "view_index_metadata"
            ]
        }
    ],
)
print(resp)
const response = await client.security.putRole({
  name: "logstash-reader",
  indices: [
    {
      names: ["logstash-*"],
      privileges: ["read_cross_cluster", "read", "view_index_metadata"],
    },
  ],
});
console.log(response);
POST /_security/role/logstash-reader
{
  "indices": [
    {
      "names": [
        "logstash-*"
        ],
        "privileges": [
          "read_cross_cluster",
          "read",
          "view_index_metadata"
          ]
    }
  ]
}