本文提供了在 Kubernetes 上执行 Open Distro for Elasticsearch 生产级部署的逐步说明。 Ring 是 Amazon 的子公司,专门生产用于家居安防的智能设备。Ring 的特色产品 Ring Video Doorbell 和 Neighborhood Security feed 在许多大城市均有销售,Ring 借此致力于减少世界各地的社区犯罪。Ring 需要找到一种可扩展的解决方案,以便存储和查询 Ring 设备生成的大量安全日志数据。
我们对这种日志聚合和查询平台有一些要求。其中包括访问日志时的用户身份验证和基于角色的访问控制 (RBAC),以及用于将身份验证与我们现有的单点登录基础设施相集成的 SAML 支持。我们还要求对与该平台的通信以及该平台内部的通信进行传输中加密,因为日志可能包含敏感数据。最后,我们需要一种用于根据传入的日志数据发出安全警报的监控系统。
Open Distro for Elasticsearch 提供了多种身份验证方法,从 HTTP 基本身份验证到基于 Kerberos 票证的身份验证一应俱全。Open Distro for Elasticsearch 还提供了一系列丰富的基于角色的访问控制 (RBAC) 功能,允许在非常精细的级别上锁定对所提取的日志数据的访问。这让我们能够十分轻松地保护中心日志记录平台。
此外,Open Distro for Elasticsearch 可为 Elasticsearch 的开源前端用户界面 Kibana 提供 SAML 支持。这种 SAML 支持允许将身份验证与 AWS Single Sign-On 或 Okta 等多种身份提供程序相集成。与平台之间的所有通信均采用 TLS 加密,这也符合我们的加密要求。
最后,Open Distro for Elasticsearch 提供警报和监控服务,允许设置自定义安全警报和系统运行状况监控。Open Distro for Elasticsearch 满足了 Ring 的安全可观测性基础设施的诸多需求。
作为 Ring 的安全运营工作的一部分,我们已在使用 Amazon Elastic Container Service for Kubernetes (Amazon EKS) 来部署和维护负责托管安全工具的 Kubernetes 集群。
安全运营团队决定以横向扩展部署的形式,在 Kubernetes 中部署 Open Distro for Elasticsearch。Kubernetes 是一个备受青睐的容器协调平台,随着我们日志记录需求的增长,Kubernetes 使我们能够继续轻松、敏捷地纵向扩展该平台。它还可减少我们对于对配置管理基础设施的依赖。
在本文中,我们将分享我们学到的一些经验,希望能帮助其他人解决类似的难题。
先决条件
本分步说明着重关注 Amazon EKS(AWS 推出的容器即服务产品)中的部署。
请确保所有相关 Kubernetes 插件均已部署到所用集群中,例如 external-dns 或 KIAM。
确保使用 kubectl 二进制文件和相应的 kubeconfig 凭证文件访问集群。
如果未部署 external-dns 服务,则 external-dns 的注释将不起作用。您可以使用社区开发的 Helm 图表部署这项服务。
如果未部署 KIAM,则 Pod IAM 角色的注释将无法正常生效。您可以使用社区开发的 Helm 图表部署 KIAM。
此部署需要引导启动 TLS 证书,此外还需要具备负责颁发这些证书的现有证书颁发机构。有关如何生成您自己的证书的详细信息,请参阅我们之前发布的博客将您自己的 SSL 证书添加到 Open Distro for Elasticsearch。
项目计划
根据我们之前在 Kubernetes 上部署社区开发的 Elasticsearch 版本的经验,我决定为 Open Distro for Elasticsearch 采用相同的部署模式。
我们的目标架构如下:
Ring Security EKS 集群的架构。
考虑到以下因素,我们决定对托管的 Kubernetes 集群使用 Amazon EKS:
Ring Security 在 Amazon EKS 中已有一个正在运行的 Kubernetes 集群,能够为安全工具扩缩工作线程节点,可以轻松用于托管此 Open Distro for Elasticsearch 集群。
该集群包含 8 个用作工作线程节点的 m5.2xlarge 实例,规模足以托管我们的 Elasticsearch 集群。
Amazon EKS 提供了托管服务器,可免除我们自行管理 Kubernetes API 服务器的麻烦,这能大大简化 Kubernetes 的补丁安装和安全保障工作。
我们从一个 8 节点测试部署开始,最终成功扩展到了生产部署。
我们还决定使用 Open Distro 团队提供的官方 Docker 映像,以免费力地管理自己的容器映像和容器注册表。
我们所规划的 Elasticsearch 集群包括三个主节点、两个客户端/协调节点和三个数据节点。
我们为各 Elasticsearch 节点类型选择了 Kubernetes 资源类型,如下所示:
主节点的部署(无状态)
客户端节点的部署(无状态)
数据节点的 StatefulSet(有状态)
我们集群的 Elasticsearch API 以 AWS 网络负载均衡器 (NLB) 为前端,使用 Kubernetes Service 资源类型进行部署。
我们决定使用 Kubernetes 污点和反相关性 API 规范来确保 Elasticsearch 主节点、客户端节点和数据节点在不同的 EC2 工作节点上建立。与此同时,我们决定使用 Kubernetes 容忍 API 规范来确保 Elasticsearch 主节点和客户端节点在每个容器的专用 EC2 工作节点上建立。
创建初始资源
首先克隆 Open Distro for Elasticsearch 社区存储库。该存储库包含 Open Distro for Elasticsearch 示例部署的 Kubernetes 清单。这些文件根据其创建的资源类型命名,其名称以一个数字开始,指示优先部署哪个文件。
在该存储库的根目录下,导航到 open-distro-elasticsearch-kubernetes 文件夹:
进入此文件夹后,使用 cd elasticsearch 命令导航到 elasticsearch 子文件夹。此文件夹包含我们在 Kubernetes 上的 Open Distro for Elasticsearch 示例部署。
接下来,使用 10-es-namespace.yml 文件创建一个 Kubernetes 命名空间,用于存放 Elasticsearch 集群资产:
在 20-es-svc-discovery.yml 文件中,使用 Kubernetes Services 资源类型创建一项发现服务,以允许通过广播端口 9300 发现主节点:
使用 20-es-service-account.yml 文件创建一个 Kubernetes ServiceAccount,作为对未来 StatefulSets 的要求:
使用 25-es-sc-gp2.yml 文件为 AWS Elastic Block Storage 驱动器创建一个 Kubernetes StorageClass 资源,作为 gp2 存储(附加到数据节点):
使用 30-es-configmap.yml 文件创建一个 Kubernetes ConfigMap 资源类型(此资源类型用于在部署时将相关的 Elasticsearch 配置 [例如 elasticsearch.yml 和 logging.yml] 引导启动到容器上):
这个 ConfigMap 资源包含 Open Distro for Elasticsearch 所需的两个配置文件:elasticsearch.yml 和 logging.yml。我们所提供的这些文件使用的是符合我们要求的设置,您可能需要根据自己的特定部署需求做出更改。
使用 Kubernetes 服务和 AWS 网络负载均衡器执的 API 入口
将用于入口点的 Kubernetes Service 资源部署到 Elasticsearch API。
使用 35-es-service.yml 文件创建资源:
该资源类型使用 annotations 键在 AWS 中创建相应的内部网络负载均衡器 (NLB)。
下面的 annotations 部分定义了此清单将设置的 AWS 网络负载均衡器中的配置设置的键/值对:
external-dns.alpha.kubernetes.io/hostname 键用于为这个新创建的 NLB 设置 AWS Route53 DNS A Record 条目。
此注释规范中的其他键/值对用于定义正在建立的 ELB 的配置选项。这包括负载均衡器的后端协议、从 AWS Certificate Manager (ACM) 获取的 TLS 证书,或者 NLB 属于外部还是外部 NLB。
Kubernetes 清单内的注释带有说明,阐明了各自的用途。
Open Distro for Elasticsearch 要求我们在入口点开启三个端口:端口 9200(用于 HTTPS/REST 访问)、9300(用于传输层访问)和 9600(用于使用性能分析器或其他服务访问度量指标)。我们在同一个 Kubernetes 清单中开启这些端口。
安全性和 TLS 配置
本节介绍使用 Kubernetes Secrets 对象引导启动 TLS 证书的步骤。
创建一个 Kubernetes Secrets 资源,该资源将用于在部署时使用 35-es-bootstrap-secrets.yml 文件将相关的 TLS 证书和私钥引导启动到容器上:
所需证书包括在 Secrets 对象的 elk-crt.pem 部分中定义的已颁发证书的一个证书链、elk-key.pem 部分中颁发的证书对应的一个私钥,最后还需要您的根 CA 证书,以便将其添加到 elk-root-ca.pem 部分中的可信 CA 链。
您还需要引导启动管理员证书,以使用默认提供的集群安全初始化脚本,以及设置 Elasticsearch 用户密码。这些证书与前文所述的用于配置 TLS 的相同证书和密钥类型相对应。
Secrets 对象中处理管理员证书的部分是用于可信证书链的 admin-crt.pem、用于该证书相应私钥的 admin-key.pem,以及用于根 CA 证书的 admin-root-ca.pem。
当然,出于安全原因,我们的证书数据已经在 35-es-bootstrap-secrets.yml 的基础上进行了修改。请可以添加您自己的证书,以及由您的证书颁发机构颁发的私钥。
在我们之前创建的 ConfigMap 中,用于加载相关证书的配置选项有两个独立的部分:一个处理 Elasticsearch 的 REST 层的 TLS 配置,另一个处理 Elasticsearch 的传输层上的 SSL 配置。您可以在 ConfigMap 的 elasticsearch.yml 部分的注释中看到这些内容。使用环境变量将传输层和 REST 层私钥的密码短语加载到容器中,后面的部分将介绍相关内容。
节点配置和部署
主节点
创建 Kubernetes Deployment 资源,以使用 40-es-master-deploy.yml 文件部署三个 Elasticsearch 主节点。此部署中的某些参数需要根据您自己的部署进行配置。在 40-es-master-deploy.yml 文件的 spec.template.annotations 中,您需要提供此 Pod 可以从 AWS IAM 承担的角色名称:
您可以使用 iam.amazonaws.com/role 注释,通过将 iam.amazonaws.com/role 的值更改为所需的 IAM 角色名称来定义您希望 Pod 承担的角色。这对于允许 Elasticsearch 节点安全地访问 AWS API 非常有用。
主节点的数量可以通过将 spec.replicas 的值更改为所需的数字来进行更改:
Elasticsearch 最佳实践建议使用三个主节点,以避免数据同步错误和裂脑问题。
容器内的环境变量将用于通过 Open Distro 中的传输套接字层和 HTTP 层输入 TLS 所用私钥的私钥密码短语。以下部分涉及到这两个密码短语的配置:
我们先前创建的 ConfigMap 和 Secrets 资源加载为 volumes 规范下的卷:
这些卷随即使用 volumeMounts 规范装载到容器上,其中 config 和 certificate 文件会加载到指定的文件路径:
默认情况下,initContainers 脚本用于将工作线程节点的 vm.max_map_count 值增加到 262144(否则,操作系统的虚拟内存分配对于 Elasticsearch 来说太小,无法在不产生内存不足问题的前提下为数据编制索引)。Elasticsearch 文档中的“虚拟内存”对此进行了更详细的说明。
由于我们使用节点标签来定义将会部署这些 Pod 的工作线程节点,因此需要以下相关性规范,并使用适当的节点标签:
应根据 EKS 集群设置中使用的工作线程节点标签更改这些键/值对。
我们还在生成的容器上公开了端口 9200(HTTPS/REST API 访问)、9300(传输套接字)和 9600(度量指标访问)以允许实现集群。这是使用 Kubernetes 部署清单的以下部分完成的:
完成上述所有配置后,您可以部署主节点:
检查要显示的主节点 Pod:
如果主节点已启动并正常运行,则输出应如下所示:
您可以通过检查以下任意主节点的日志输出,查看主节点是否成功运行:
如果日志输出包含以下消息字符串,则 Elasticsearch 主节点已成功集群化:
客户端节点
创建 Kubernetes Deployment 资源,以便使用 50-es-client-deploy.yml 文件部署两个 Elasticsearch 客户端/协调节点。此文件中有某些参数需要针对您的部署进行配置。
在 50-es-client-deploy.yml 文件的 spec.template.annotations 中,您需要提供一个角色名称,让 Pod 能够从 AWS IAM 担任角色:
客户端节点的数量可以通过将 spec.replicas 的值更改为所需的数字来进行更改:
我们为此测试集群选择了两个协调/客户端节点。此数量可根据传入流量进行增加。
使用为主节点部署提供的相同私钥密码来替换以下环境变量的值:
name: TRANSPORT_TLS_PEM_PASS
value: “REPLACE_WITH_TLS_PRIVATE_KEY_PASSPHRASE”
name: HTTP_TLS_PEM_PASS
value: “REPLACE_WITH_TLS_PRIVATE_KEY_PASSPHRASE”
此部署使用的 ConfigMap 和 Secrets 资源与先前的主节点部署使用的资源相同。它们具有相同的配置和相同的引导启动方法,因此我们将跳过此内容,不再在本节中重复讲述。
虚拟内存分配的执行方式与主节点部署相同。
需要注意的一个关键差异是,已将加权反相关性应用到此客户端节点部署,以防止客户端节点在与主节点相同的工作节点上进行调度:
此处应用的是与主节点部署相同的端口配置。
完成上述所有配置后,您可以部署客户端节点:
检查要显示的客户端节点 Pod:
如果客户端节点已启动且正在运行,则输出将类似于:
您可以通过检查以下任意客户端节点的日志输出,查看客户端节点是否成功运行:
如果日志输出包含以下消息字符串,则 Elasticsearch 客户端节点已成功集群化:
数据节点
使用 60-es-data-svc.yml 文件为 Kubernetes 内部入口点至 Elasticsearch 数据节点创建 Kubernetes 服务资源:
这将在 EKS 集群内创建本地服务资源,以允许访问 Elasticsearch 数据节点。
创建 Kubernetes StatefulSet 资源,以便使用 70-es-data-sts.yml 文件部署三个 Elasticsearch 数据节点。这些是将用于存储索引数据的状态节点。
某些参数需要专门针对您自己的部署进行配置。
在 70-es-data-sts.yml 文件中的 spec.template.annotations 中,您需要提供 Pod 可从 AWS IAM 承担的角色名称:
通过更改 spec.replicas 的值,可以更改数据节点的数量:
我们为此测试集群选择了三个数据节点。此数量可根据您自己的要求增加。
使用为主节点和客户端节点部署提供的相同私钥密码来替换以下环境变量的值,如下所示:
此部署使用的 ConfigMap 和 Secrets 资源与先前的主节点和客户端节点部署使用的资源相同。它们使用相同的配置和相同的引导启动方法,因此我们不会在本节中重复讲述这些内容。
虚拟内存分配的执行方式与主节点部署相同。
serviceName: elasticsearch-data 定义配置为使用我们之前在 60-es-data-svc.yml 文件中创建的数据服务。
70-es-data-sts.yml 文件中的 volumeClaimTemplates 部分为以下状态节点预配置存储卷:
这定义了 StatefulSet 中每个 Pod 的 EBS 存储卷的资源调配,并将存储卷作为装载点附加到 Pod。此处引用的 storageClassName 密钥是我们最初在 25-es-sc-gp2.yml 文件中定义的 StorageClass 资源的名称
我们还在此处使用额外的 initContainers 部分,以允许具有 UID 和 GID 1000 读写权限的 Elasticsearch 用户使用 fixmount 脚本对预配置 EBS 存储卷进行读写:
完成所有配置后,我们可以部署以下 Elasticsearch 数据节点:
检查要显示的数据节点 Pod:
如果数据节点已启动且正在运行,则输出将类似于:
您可以通过检查以下任意数据节点的日志输出,查看数据节点是否成功运行:
如果日志输出包含以下消息字符串,则 Elasticsearch 数据节点已成功集群化,并准备开始编制数据索引:
此时,集群已成功部署,但您仍需要使用默认用户及其密码初始化集群。下一节将介绍此安全初始化。
集群安全初始化
如 Open Distro for Elasticsearch 文档所述,部署完成后,必须先安全初始化集群,然后才可使用。
这是通过驻留在运行“Open Distro for Elasticsearch”的容器上的两个文件完成的。
要启动初始化过程,请使用以下命令获取对其中一个主节点的 shell 访问权限:
一旦您拥有正在运行的 Elasticsearch pod 的 shell 访问权限,请导航至带有 cd /usr/share/elasticsearch/plugins/opendistro_security/tools 的 Open Distro 工具目录并执行以下操作:
$ chmod +x hash.sh
这将使密码哈希脚本可执行。现在,您可以使用此脚本为默认用户生成加密哈希密码。默认用户可显示在 /usr/share/elasticsearch/plugins/opendistro_security/securityconfig/internal_users.yml 文件中,默认情况下管理员用户的示例如下(为简洁起见,我省略了文件的其余部分):
要更改 internal_users.yml 文件中的密码,首先使用 hash.sh 脚本为文件中的每个用户生成哈希密码:
例如,如果我想更改管理员用户的密码,我将执行以下操作:
输出字符串是加密的哈希密码。现在,我们将使用此哈希替换 internal_users.yml 文件中管理员用户的哈希。
此代码段显示更新后的 internal_users.yml 文件:
此步骤需要对 internal_users.yml 文件中的所有用户执行。为此文件中定义的每个用户执行此操作,并安全地存储密码的纯文本版本,以备将来使用。
初始化过程使用 /usr/share/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh 脚本执行。
初始化命令需要某些参数,应类似于:
此命令指定用于成功执行脚本所使用的管理客户端 TLS 证书和私钥。这是我们先前为集群部署加载的第二组证书,它是 ConfigMap 的一部分。
-cd 标记指定存储初始化配置的目录。必须将 -keypass 标记设置为生成管理员客户端私钥时选择的密码。-h 标记指定要使用的主机名,在这种情况下,是指我们要访问的 pod 的内部 IP 地址。
如果它成功运行并且能够初始化集群,则输出将类似于:
现已成功部署、配置和初始化“Open Distro for Elasticsearch”集群! 现在,您可以转到 Kibana 部署。
Kibana 部署
成功运行 Elasticsearch 后,您需要通过 Kibana 用户界面进行访问。
从您之前克隆的社区存储库的根目录中,导航到 open-distro-elasticsearch-kubernetes 文件夹:
之后,使用命令 cd kibana 导航至 kibana 子文件夹。
然后创建 Kubernetes 命名空间,以使用 10-kb-namespace.yml 文件存放 Kibana 资产:
创建 Kubernetes ConfigMap 资源,该资源将用于在使用 20-kb-configmap.yml 文件部署后,将 Kibana 的主配置文件 kibana.yml 引导启动至 Kibana 容器:
创建 Kubernetes Secrets 资源,以使用 25-kb-bootstrap-secrets.yml 文件引导启动 Kibana Pod 上的 TLS 证书和私钥配置。
在 25-kb-bootstrap-secrets.yml 文件中,您将使用每个相关证书和私钥替换空证书数据部分。
使用您在 35-es-service.yml 文件中部署 Elasticsearch 服务时选择的 DNS 名称替换 elasticsearch.url 参数。
创建 Kubernetes Deployment 资源,以便使用 30-kb-deploy.yml 文件部署单个 Kibana 节点:
在此部署中,有多个环境变量:
需要配置的环境变量在 30-kb-deploy.yml 文件中进行注释。
首先,将<URL_OF_ELASTICSEARCH_API> 部分替换为您在 Elasticsearch 部署期间选择的 DNS 名称。这是在 35-es-service.yml 文件中的 external-dns.alpha.kubernetes.io/hostname 注释下配置的。
在集群初始化过程中,将 <PASSWORD_CHOSEN_DURING_CLUSTER_INITIALIZATION> 替换为 kibanaserver 用户的密码设置。
接下来,将 <PASSPHRASE_FOR_KIBANA_TLS_PRIVATE_KEY> 替换为引导启动的 Kibana TLS 证书的私钥选择的密码。
最后,将 <COOKIE_PASS_FOR_SECURITY_PLUGIN_32CHARS> 替换为 32 个字符的随机字符串,该字符串将由安全插件用于加密会话 Cookie。
为入口点至 Kibana 的 Web 用户界面创建 Kubernetes Service 资源类型,其使用注释在 AWS 中创建面向外部的相应网络负载均衡器。这允许使用 40-kb-service.yml 文件进入集群:
此服务部署将创建一个面向外部的网络负载均衡器,用于对 Kibana 进行用户界面访问,并将端口 443 映射到运行 Kibana API 的端口 5601。
它还将为所选 DNS 主机名注册带 ACM 证书的网络负载均衡器,前提是它在 service.beta.kubernetes.io/aws-load-balancer-ssl-cert 注释下提供了有效的 ACM 证书 ARN。
Kibana 将需要一些时间来启动和运行。Kibana 运行之后,您应该能够使用您在使用 40-kb-service.yml 文件部署 Kibana 服务时选择的 DNS 地址访问 Kibana 用户界面。
此参数在 external-dns.alpha.kubernetes.io/hostname 注释中设置,例如 kibana.sec.example.com。在 Kibana 变得可用之后,您将看到:
Open Distro for Elasticsearch Kibana 登录。
使用之前配置的管理员凭证登录,以获得对集群的访问权限并使用 Elasticsearch。
小结
祝贺您! 您现在拥有 Open Distro for Elasticsearch 的生产级部署。您部署了三个主节点、两个客户端节点和三个数据节点。您可以使用内部 TLS 和基于角色的访问控制来保护集群。您可以轻松扩展或重新调整集群以适应您的工作负载。
有问题或疑问? 希望参与讨论? 您可以在我们的论坛上获得帮助并讨论 Open Distro for Elasticsearch。您可以在这里提出问题。
致谢
特别感谢:
Zack Doherty(高级 SRE – Tinder Engineering),感谢他对 Kubernetes 内部的所有帮助。
Pires,他负责 Kubernetes 中的 Open-source Elasticsearch 部署。
感谢 Open Distro for Elasticsearch 团队为撰写此帖提供的支持和指导。
Ring Security 团队,非常感谢他们的支持和鼓励。
作者介绍:
Saad Rana
Saad Rana 是 Amazon Ring 的安全运营工程师。他擅长构建可扩展的日志聚合数据管道,以处理来自 Ring 大型智能安全设备网络的安全日志。他利用下一代基础设施编排、数据提取、存储和查询技术,如 Elasticsearch、Apache Kafka 和 Kubernetes,为支持 Ring 产品提供了可靠的安全观测主干。他还曾在 Tinder Inc. 从事安全观测方面的工作。
本文转载自 AWS 技术博客
文章链接:
https://amazonaws-china.com/cn/blogs/china/open-distro-for-elasticsearch-on-kubernetes/
评论