API 密钥身份验证使本地集群能够通过跨集群 API 密钥(cross-cluster API key)向远程集群进行身份验证。API 密钥需要由远程集群的管理员创建。本地集群配置为在向远程集群发出每个请求时提供此 API 密钥。远程集群将根据 API 密钥的权限验证 API 密钥并授予访问权限。
来自本地集群的所有跨集群请求都受 API 密钥权限的约束,无论与请求关联的本地用户是谁。例如,如果 API 密钥仅允许对远程集群上的 my-index 进行读取访问,那么即使是本地集群的超级用户也会受到此约束的限制。此机制使远程集群的管理员能够完全控制谁可以通过跨集群搜索和/或跨集群复制访问哪些数据。远程集群的管理员可以确信,除了明确分配给 API 密钥的内容之外,不可能进行任何访问。
在本地集群方面,并非每个本地用户都需要访问 API 密钥允许的每一部分数据。本地集群的管理员可以进一步配置对本地用户的额外权限限制,以便每个用户只能访问必要的远程数据。请注意,只能进一步减少 API 密钥允许的单个本地用户的权限。无法增加超出 API 密钥允许的权限。
在此模型中,跨集群操作使用专用服务器端口(远程集群接口)进行集群间通信。远程集群必须启用此端口才能连接本地集群。为此端口配置传输层安全性 (TLS) 以最大限度地提高安全性(如与远程集群建立信任中所述)。
本地集群必须在远程集群接口上信任远程集群。这意味着本地集群信任签署远程集群接口使用的服务器证书的远程集群证书颁发机构 (CA)。建立连接时,参与跨集群通信的所有本地集群节点都会根据 TLS 信任配置验证来自另一侧节点的证书。
注意 :为了使得两个不同的集群之间能够互相信任,除了本文章使用 API 秘钥的机制之外,我们还可以使用 TLS certificate authentication。我们可以详细地阅读文章 "Elasticsearch:如何为 CCR 及 CCS 建立带有安全的集群之间的互信"。
使用 API 密钥验证添加远程集群
前提条件
- 需要在两个集群的每个节点上启用 Elasticsearch 安全功能。默认情况下启用安全性。如果禁用,请在 elasticsearch.yml 中将 xpack.security.enabled 设置为 true。请参阅常规安全设置。
- 本地和远程集群的节点必须为 8.10 或更高版本。
- 本地和远程集群必须具有适当的许可证。有关更多信息,请参阅 https://www.elastic.co/cn/subscriptions。
安装
Elasticsearch 及 Kibana
如果你还没有安装好自己的 Elasticsearch 及 Kibana,请按照如下的文章来进行安装:
针对我们今天的展示,我们讲使用最新的 Elastic Stack 14.3 来进行展示。我们的架构如下:
如上所示,我们在 macOS 安装一个远程集群,而在 Ubuntu 22.04 上安装一个本地的集群。通过配置后,我们可以在 Local 集群里访问 Remote 集群。为此,我们需要为两个集群做不同的配置。
在 Remote 集群上
- 在远程集群的每个节点上启用远程集群服务器。在 elasticsearch.yml 中:
- 将 remote_cluster_server.enabled 设置为 true。
- 配置远程集群服务器流量的绑定和发布地址,例如使用 remote_cluster.host。如果不配置地址,远程集群流量可能会绑定到本地接口,而其他机器上运行的远程集群则无法连接。
- (可选)使用 remote_cluster.port 配置远程服务器端口(默认为 9443)。
- 接下来,生成证书颁发机构 (CA) 和服务器证书/密钥对。在远程集群的某个节点上,从已安装 Elasticsearch 的目录中:
-
如果你还没有 CA,请创建一个 CA:
./bin/elasticsearch-certutil ca --pem --out=cross-cluster-ca.zip --pass CA_PASSWORD
将 CA_PASSWORD 替换为你要使用的 CA 密码。如果你不部署到生产环境,则可以删除 --pass 选项及其参数。
针对我们的情况:
./bin/elasticsearch-certutil ca --pem --out=cross-cluster-ca.zip --pass 123456
上面的命令将上传一个叫做 cross-cluster-ca.zip 的文件:
$ pwd
/Users/liuxg/elastic1/elasticsearch-8.14.3
$ ls
LICENSE.txt bin cross-cluster-ca.zip lib plugins
NOTICE.txt ca data logs
README.asciidoc config jdk.app modules
-
解压生成的 cross-cluster-ca.zip 文件。此压缩文件包含以下内容:
unzip cross-cluster-ca.zip
$ pwd
/Users/liuxg/elastic1/elasticsearch-8.14.3
$ ls
LICENSE.txt bin data logs
NOTICE.txt config jdk.app modules
README.asciidoc cross-cluster-ca.zip lib plugins
$ unzip cross-cluster-ca.zip
Archive: cross-cluster-ca.zip
creating: ca/
inflating: ca/ca.crt
inflating: ca/ca.key -
为远程集群中的节点生成证书和私钥对:
./bin/elasticsearch-certutil cert --out=cross-cluster.p12 --pass=CERT_PASSWORD --ca-cert=ca/ca.crt --ca-key=ca/ca.key --ca-pass=CA_PASSWORD --dns=example.com --ip=127.0.0.1
- 将 CA_PASSWORD 替换为上一步中的 CA 密码。
- 将 CERT_PASSWORD 替换为要用于生成的私钥的密码。
- 使用 --dns 选项指定证书的相关 DNS 名称。你可以为多个 DNS 多次指定它。
- 使用 --ip 选项指定证书的相关 IP 地址。你可以为多个 IP 地址多次指定它。
针对我们的情况:
./bin/elasticsearch-certutil cert --out=cross-cluster.p12 --pass=123456 --ca-cert=ca/ca.crt --ca-key=ca/ca.key --ca-pass=123456 --dns=mac --ip=192.168.0.3
上面的命令将生成一个叫做 cross-cluster.p12 的文件。
$ pwd
/Users/liuxg/elastic1/elasticsearch-8.14.3
$ ls
LICENSE.txt bin cross-cluster-ca.zip jdk.app modules
NOTICE.txt ca cross-cluster.p12 lib plugins
README.asciidoc config data logs
- 如果远程集群有多个节点,你可以:
- 为所有节点创建单个通配符证书;
- 或者,手动或使用 silent mode 批量为每个节点创建单独的证书。
- 在远程集群的每个节点上:
-
将上一步中的 cross-cluster.p12 文件复制到 config 目录。如果你没有创建通配符证书,请确保复制正确的特定于节点的 p12 文件。
$ pwd
/Users/liuxg/elastic1/elasticsearch-8.14.3
$ cp cross-cluster.p12 config/$ pwd
/Users/liuxg/elastic1/elasticsearch-8.14.3
$ ls config/
certs elasticsearch.yml role_mapping.yml
cross-cluster.p12 jvm.options roles.yml
elasticsearch-plugins.example.yml jvm.options.d users
elasticsearch.keystore log4j2.properties users_roles -
在 elasticsearch.yml 中添加以下配置:
xpack.security.remote_cluster_server.ssl.enabled: true
xpack.security.remote_cluster_server.ssl.keystore.path: cross-cluster.p12
最终的 elasticsearch.yml 配置文件如下:
-
将 SSL 密钥库密码添加到 Elasticsearch 密钥库:
./bin/elasticsearch-keystore add xpack.security.remote_cluster_server.ssl.keystore.secure_password
出现提示时,输入上一步中的 CERT_PASSWORD。针对我们的设计,我们输入 123456 作为密码。我们可以使用如下的命令来查看密码:
$ pwd
/Users/liuxg/elastic1/elasticsearch-8.14.3
$ ./bin/elasticsearch-keystore show xpack.security.remote_cluster_server.ssl.keystore.secure_password
123456
- 重新启动 remote 集群
启动完毕后,我们可以使用如下的命令来查看 remote server 的运行:
sudo lsof -i -P | grep LISTEN | grep 9443
$ sudo lsof -i -P | grep LISTEN | grep 9443
Password:
java 33531 liuxg 473u IPv6 0x356b6aaee02cbca9 0t0 TCP mac:9443 (LISTEN)
从上面,我们可以看出来 9443 端口正被使用。
5)在远程集群上,生成跨集群 API 密钥,该密钥提供对要用于跨集群搜索或跨集群复制的索引的访问权限。你可以使用创建跨集群 API 密钥 API 或 Kibana。
在 Kibana 上,我们可以这么操作:
这样我们就创建了一个 cross cluster key。我们也可以直接使用 API 来进行生成:
POST /_security/cross_cluster/api_key
{
"name": "my-cross-cluster-api-key",
"expiration": "5d",
"access": {
"search": [
{
"names": ["twitter*"]
}
],
"replication": [
{
"names": ["archive*"]
}
]
},
"metadata": {
"description": "phase one",
"environment": {
"level": 1,
"trusted": true,
"tags": ["dev", "staging"]
}
}
}
在上面,我们生成一个秘钥。这个密钥只可以提供针对 twitter* 索引的搜索,而且它的有效期只有5天的时间。我们可以针对 archive* 的文档进行跨级群复制。
6)将编码的密钥(在响应中编码)复制到安全位置。稍后你将需要它来连接到远程集群。
在 Local 集群上
1)在本地群集的每个节点上:
-
将之前在远程集群上生成的 ca.crt 文件复制到 config 目录中,重命名文件 remote-cluster-ca.crt。
-
在 elasticsearch.yml 中添加以下配置:
xpack.security.remote_cluster_client.ssl.enabled: true
xpack.security.remote_cluster_client.ssl.certificate_authorities: [ "remote-cluster-ca.crt" ] -
将之前在远程集群上创建的跨集群 API 密钥添加到密钥库:
./bin/elasticsearch-keystore add cluster.remote.ALIAS.credentials
将 ALIAS 替换为你稍后将用于创建远程集群条目的相同名称。出现提示时,输入之前在远程集群上创建的编码跨集群 API 密钥。特别值得指出的是这里的 ALIAS 其实就是在下面创建 remote 连接时所填入的名称。针对我们的情况:
./bin/elasticsearch-keystore add cluster.remote.mac.credentials
这里的 mac 就是我们稍后会填入的连接名称。
2)重新启动本地集群以加载对密钥库和设置的更改。
注意 :如果你仅配置跨集群 API 密钥,则可以调用 Nodes reload secure settings API,而不必重新启动集群。配置 elasticsearch.yml 中的 remote_cluster_client 设置仍需要重新启动。
我们可以通过如下的 API 来确定我们的节点具有 remote cluster client node。详细的节点角色可以参考 "Elasticsearch:Node roles 介绍 - 7.9 之后版本"。
GET /_cat/nodes
上面的命令返回:
127.0.0.1 24 98 13 0.32 0.45 0.54 cdfhilmrstw * ubuntu2204
我们可以看到上面的 r,它代表 remote cluster client node。
连接到 remote 集群
注意:你必须具有 manage 集群权限才能连接远程集群。
本地集群使用远程集群接口与远程集群建立通信。本地集群中的协调节点与远程集群中的特定节点建立长寿命 TCP 连接。Elasticsearch 要求这些连接保持打开状态,即使连接长时间处于空闲状态。
要从 Kibana 中的 Stack Management 添加远程集群:
上面显示我们的远程连接是成功的。
另外,我们也可以使用 API 来完成这个动作。以下请求添加一个别名为 mac 的远程集群。此集群别名 mac 是一个唯一标识符,代表与远程集群的连接,用于区分本地索引和远程索引。
PUT /_cluster/settings
{
"persistent" : {
"cluster" : {
"remote" : {
"mac" : {
"seeds" : [
"192.168.0.3:9443"
]
}
}
}
}
}
我们可以通过如下的 API 来进行检查结果:
GET /_remote/info
检查 CCS
我们在远程集群,也就是 macOS 的集群上创建一个索引 twitter:
PUT twitter/_doc/1
{
"content": "This is Xiaoguo from Elastic"
}
我们在 Local 集群里进行如下的搜索:
GET mac:twitter/_search
很显然我们的搜索是成功的。
静态配置远程集群
如果你在 elasticsearch.yml 中指定设置,则只有具有这些设置的节点才能连接到远程集群并处理远程集群请求。
注意 :使用cluster update settings API 指定的远程集群设置优先于你在 elasticsearch.yml 中为各个节点指定的设置。
在以下示例中,cluster_one、cluster_two 和 cluster_three 是任意集群别名,表示与每个集群的连接。这些名称随后用于区分本地索引和远程索引。
cluster:
remote:
cluster_one:
seeds: 127.0.0.1:9443
cluster_two:
mode: sniff
seeds: 127.0.0.1:9444
transport.compress: true # 1
skip_unavailable: true # 2
cluster_three:
mode: proxy
proxy_address: 127.0.0.1:9445 # 3
- 已明确启用对 cluster_two 的请求的压缩。
- 对于 cluster_two,断开连接的远程集群是可选的。
- 用于连接到 cluster_three 的代理端点的地址。