使用 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.17

      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.17

      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 参数确定配置的连接模式,默认为 sniff。由于 cluster_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 中的 Stack Management 管理用户和角色。您还可以使用角色管理 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 特权以及 readview_index_metadata 特权,用于 logstash-* 索引。

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"
          ]
    }
  ]
}