工程

揭开 Elasticsearch 中身份验证和授权的神秘面纱

我在 Elastic 工作已经 4 年多了,其中有三年半都在支持和咨询部门工作,最近半年在销售部工作。然而,无论我在哪个部门,用户向我询问最多的问题都是如何确保 Elasticsearch 的安全性。Elasticsearch 支持哪种类型的身份验证?如何设置?如何确保用户不会看到他们不应看到的数据?我撰写此篇博文的目的就是回答这些问题,同时提供一些指导以帮助您解决安全功能配置过程中的某些常见问题。

首先,介绍一下背景。如果是 Elasticsearch 的老用户,您肯定知道我们之前曾通过 X-Pack 中一个名为 Shield 的插件提供安全功能。现在,安全功能(以及 X-Pack 中的其他功能)已集成到 Elastic Stack 中,而且最常用功能在默认分发版本中免费提供。安全功能包括加密通信 (TLS/SSL)、身份验证(原生、LDAP、SSO 等)、授权(RBAC、ABAC 等等)、IP 筛选审计日志等等。本篇博文将会关注身份验证和授权。

Elasticsearch 中的身份验证

简单来说,如果用户或 API 想访问 Elastic,其必须完成身份验证

Elasticsearch 为多种安全方法提供原生支持,例如:

如果上面这些均不适用,您甚至还可以创建自己的集成方法。然而,我们一般建议用户使用既有集成方法,因为这些方案均经过验证并且我们不断针对它们进行开发以确保提供相应支持。

Elasticsearch 中最基本的安全组件是 Realm,验证用户身份并解决相关问题时便会用到 Realm。上面列出的每种身份验证方法均可视为一个 Realm。而且,为了能够正常运行,Elasticsearch 使用 Realm 链路。Realm 链路是一个由已配置 Realm 构成且已对其进行优先级排序的列表(从第 1 个到第 N 个 Realm),按偏好程度升序排列。当用户尝试访问 Elasticsearch 时,此请求会按顺序遍历列表,直到身份验证成功或者没有 Realm 可供尝试。

从本质上来说,这一过程会尝试列表中的配置的第一个 Realm,如果失败就尝试下一个,直至在其中一个 Realm 项上成功,或者尝试完毕整个列表中的所有 Realm。访问 Elasticsearch 过程中的这个第一步叫做身份验证。

一旦用户身份验证成功,Elasticsearch 便会尝试给用户分配一个或多个角色。在身份验证过程中,基于用户的某些属性,可以通过静态或动态方式来为用户分配角色。按照对用户进行身份验证时所用 Realm 的类型,分配角色时所用的用户属性可以是他们在外部系统中的群成员身份,或者他们 Elasticsearch 用户名的前缀,等等。不仅如此,Elasticsearch 还有一项特色 run as functionality(作为功能运行),借助此选项用户无需再次进行身份验证便能代表其他用户提交请求。

身份验证阶段完成之后,下一步就是授权。您可以通过下图了解整个工作流程:

Elasticsearch 中的 Realm 链路

Elasticsearch 中的授权

身份验证成功后,用户便会来到第二个安全检查点:授权。授权流程用来确定可否允许用户执行某一请求,实现方法是将用户映射预定义和/或用户定义的角色。Elasticsearch 设置了一些默认角色,但您也可以为自己的用例创建特定角色。

角色由下列内容组成:

  • 具有访问权限的用户列表
  • 集群权限
  • 全局权限
  • 索引权限
  • 应用程序权限

在此第二阶段,Elasticsearch 会使用下列方法中的一种:

  • 我们推荐的方法是 role_mapping API,因为它能让您基于 API 集中管理每个角色的映射关系。在 Elastic Cloud 上的 Elasticsearch Service 中,这是配置映射关系的唯一方法。
  • 角色映射文件 (role_mapping.yml),其位于每个节点内的配置文件夹中。

role_mapping API 需要由拥有正确权限来管理角色的用户进行调用。Elastic 用户拥有的 superuser(超级用户)角色便是一个示例,但是,您也可以为此目的创建一个特定角色。

Realm 和角色的定义截然不同。Realm 和 Realm 链路用来进行身份验证,身份验证阶段之后,我们需要使用角色来对用户进行映射。虽然会使用 Realm 链路中的单一 Realm 来进行身份验证,但在第二个阶段中,同一用户可映射至一个或多个角色。这也称作基于角色的访问控制。

身份验证和授权问题的故障排查步骤

现在已经了解了身份验证和授权过程的基础知识,我们接下来看一下您遇到任何问题时可以采取哪些故障排查步骤。

401 未授权

如果试遍所有 Realm 后都无法完成身份验证,您可能会得到一条状态代码为 401 未授权的响应。针对已配置 Realm 列表尝试您的凭据时,最简单方法就是使用 cURL,并搭配一个可允许检查的旗标(例如 -v)。您可能会得到类似下面的响应:

curl https://xxxxxx:9200 -u test:test -v 
> User-Agent: curl/7.54.0 
> Accept: */* 
> 
< HTTP/1.1 401 Unauthorized 
< Content-Type: application/json; charset=UTF-8 
< Date:Tue, 10 Sep 2019 15:59:33 GMT 
< Www-Authenticate:Bearer realm="security" 
< Www-Authenticate:ApiKey 
< Www-Authenticate:Basic realm="security" charset="UTF-8" 
< Content-Length:455 
< Connection: keep-alive 
< 
* Connection #0 to host 06618318cff64c829af1cd5a2beb91a5.us-east-1.aws.found.io left intact 
{"error":{"root_cause":[{"type":"security_exception","reason":"unable to authenticate user [test] for REST request [/]","header":{"WWW-Authenticate":["Bearer realm=\"security\"","ApiKey","Basic realm=\"security\" charset=\"UTF-8\""]}}],"type":"security_exception","reason":"unable to authenticate user [test] for REST request [/]","header":{"WWW-Authenticate":["Bearer realm=\"security\"","ApiKey","Basic realm=\"security\" charset=\"UTF-8\""]}},"status":401}%

这条错误消息能够帮您了解问题的根本原因。举例说明,对一些特定 Realm(例如 SAML)而言,其需要 Kibana 或定制网络应用程序与 Elasticsearch 或身份服务提供商进行交互。

启用错误日志

如果在设置完 Realm、Realm 链路、角色和角色映射后,您仍遇到问题,您可以轻松配置一些额外日志来获取我们之前所讲身份验证和授权过程的更多相关信息。

使用集群 settings API 中的日志配置,您可为任何 Realm 定义任何日志级别。

我们看一个有关 LDAP Realm 的示例。如果用户身份验证或授权失败,您可以设置下面的日志级别来获得详细的相关信息:

PUT /_cluster/settings 
{ 
  "transient": { 
     "logger.org.elasticsearch.xpack.security.authc.ldap":"DEBUG",
     "logger.org.elasticsearch.xpack.security.authz":"DEBUG" 
   } 
}

这会将 LDAP 数据包的日志级别从默认提升为 DEBUG。您还可以启用 TRACE 日志,以获取更加详尽的信息,例如发送给服务器的每次 LDAP 调用,以及服务器给出的响应。使用我们的 GitHub 存储库,您可以找到在每个特定 REALM 上需启用的数据包名称。举例说明,下面便是适用于 LDAP Realm 的代码。通过使用第一行代码,您可以看到确定日志级别时所用的数据包:

package org.elasticsearch.xpack.security.authc.ldap;

启用之后,您立即便会收到大量日志行,例如下面的示例:

[2019-09-12T08:41:20,628][DEBUG][o.e.x.s.a.l.LdapRealm   ] [xxxxxxx] user not found in cache, proceeding with normal authentication
[2019-09-12T08:41:21,180][TRACE][o.e.x.s.a.l.s.LdapUtils ] LDAP Search SearchRequest(baseDN='dc=xxx,dc=xxxx,dc=domain,dc=com', scope=SUB, deref=NEVER, sizeLimit=0, timeLimit=5, filter='(cn=vchatzig)', attrs={1.1}) => SearchResult(resultCode=0 (success), messageID=2, entriesReturned=1, referencesReturned=0) ([SearchResultEntry(dn='cn=xxxxxxx,ou=xxxxx,dc=intc,dc=xxxx,dc=domain,dc=com', messageID=2, attributes={}, controls={})])

从这里,我们可以看到 LDAP 正尝试从缓存(有可能已过期)中获得用户,然后再使用我们所提供的特定配置向服务器发送 LDAP Search 请求。您会收到诸多日志行,这些只是其中的一些示例。

注意:启用日志对诊断来说有很大帮助,但对性能却并无裨益。我们建议您在确认一切都正常运行之后,将日志级别再调回默认值,可以使用下面的代码:

PUT /_cluster/settings 
{ 
  "transient": { 
     "logger.org.elasticsearch.xpack.security.authc.ldap": null,
     "logger.org.elasticsearch.xpack.security.authz": null 
   } 
}
        

请注意:在不同版本之间,这些数据包中有一些可能会存在差异,所以我们建议您仔细查看适用于您 Elasticsearch 特定版本的文档链接,以及针对此具体发布版本的 GitHub 类别链接。

常见的 SAML 问题

SAML Realm 要求使用身份服务提供商(例如 Okta 或 Auth0)与网络应用程序(默认为 Kibana),这二者与 Elasticsearch 一起作为服务提供商。我们遇到的一些常见问题如下:

  • 服务提供商 (IdP) 配置中的断言消费服务 URL 设置不正确。通常您在服务提供商处需要配置的端点为 https://kibana.xxxx.com/api/security/v1/saml
  • 服务提供商的元数据无法通过互联网访问。Elasticsearch 中 idp.metadata.path 配置的值(无论是本地部署,还是在 Elastic Cloud 中)应该能够通过 Elasticsearch/Kibana 运行所在的网络进行访问。如果不能访问,您需要从能够访问的主机上下载元数据,或者向 IdP 管理员索要这一元数据,然后将其作为本地文件添加到 Elasticsearch 中并加以参考。所显示的错误将会和下面内容类似:
    Caused by: net.shibboleth.utilities.java.support.resolver.ResolverException: net.shibboleth.utilities.java.support.resolver.ResolverException:Non-ok status code 404 returned from remote metadata source https://xxxx.xxxx/xxx/yyyyyyyy/sso/saml/metadata
        
  • 您完成了身份验证,但是用户却无法打开 Kibana。对于这一问题,我们一般建议复查用户的角色映射,以确认他们的至少一个角色能够访问 Kibana

服务提供商配置与 Elasticsearch/Kibana 配置同等重要,我们必须随时注意是否有拼写错误,以及期望设置与所配置设置之间是否有不相符之处。尽管每个特定 IdP 都有其自身的设置,我们仍建议针对所需的每个特定设置仔细查阅相关文档

如需了解更多常见问题以及 SAML 的故障排查步骤,欢迎访问我们针对每个发行版本维护的故障排查文档

总结

只要我们理解了背后的所有理念,Elasticsearch 中的身份验证其实非常简单。而且,要想了解哪些地方运行正常、哪些地方存在问题以及为何存在问题,这有时会十分困难,但是在本篇博文中,我们提供了很多选项让您能够深入查看。您可以使用我们的文档以开始在 Elasticsearch 中使用安全功能,也可以使用我们文档中针对安全性的故障排查指南。观看 Elastic 安全的高级产品经理的视频,他为您详细讲解了入门使用体验

我们还建议您仔细查看我们的订阅页面,以详细了解每个订阅级别中所包含的功能。最后,如果您遇到任何具体问题,欢迎联系我们的支持代表,也可在我们的讨论论坛上发言。

祝您轻松体验安全功能!