在 Elastic Stack 上配置 SAML 单点登录
编辑在 Elastic Stack 上配置 SAML 单点登录
编辑Elastic Stack 支持使用 Elasticsearch 作为后端服务的 Kibana SAML 单点登录 (SSO)。在 SAML 术语中,Elastic Stack 充当服务提供商。
启用 SAML 单点登录所需的另一个组件是身份提供商,这是一个处理您的凭据并执行实际用户身份验证的服务。
如果您有兴趣配置 Kibana 的 SSO,则需要向 Elasticsearch 提供有关您的身份提供商的信息,并且需要在该身份提供商中注册 Elastic Stack 作为已知的服务提供商。Kibana 中还需要进行一些配置更改才能激活 SAML 身份验证提供程序。
Kibana 中的 SAML 支持的设计预期是它将成为该 Kibana 实例用户的首要(或唯一)身份验证方法。在 Kibana 中启用 SAML 身份验证后,它将影响所有尝试登录的用户。配置 Kibana 部分提供了有关其工作方式的更多详细信息。
身份提供商
编辑Elastic Stack 支持 SAML 2.0Web 浏览器 SSO和 SAML 2.0单点注销配置文件,并且可以与任何至少支持 SAML 2.0Web 浏览器 SSO 配置文件的身份提供商 (IdP) 集成。它已通过许多流行的 IdP 实现进行了测试,例如Microsoft 活动目录联合服务 (ADFS)、Azure 活动目录 (AAD)和Okta。
本指南假设您拥有现有的 IdP 并希望添加 Kibana 作为服务提供商。
Elastic Stack 使用标准 SAML元数据文档(XML 格式),该文档定义了 IdP 的功能和特性。您应该能够在 IdP 管理界面中下载或生成此类文档。
下载 IdP 元数据文档并将其存储在每个 Elasticsearch 节点的config
目录中。出于本指南的目的,我们将假设您将其存储为config/saml/idp-metadata.xml
。
IdP 将被分配一个标识符(SAML 术语中的EntityID),最常用的是统一资源标识符 (URI) 形式。您的管理界面可能会告诉您这是什么,或者您可能需要阅读元数据文档才能找到它——查找EntityDescriptor
元素上的entityID
属性。
大多数 IdP 将提供包含 Elastic Stack 需要的所有功能的适当元数据文件,并且只需要下面描述的配置步骤。为了完整起见,Elastic Stack 对 IdP 元数据的最低要求是:
- 具有与 Elasticsearch 配置匹配的
entityID
的<EntityDescriptor>
- 支持 SAML 2.0 协议(
urn:oasis:names:tc:SAML:2.0:protocol
)的<IDPSSODescriptor>
。 - 至少一个配置为签名的
<KeyDescriptor>
(即,它具有use="signing"
或未指定use
) - 具有 HTTP-Redirect 绑定的
<SingleSignOnService>
(urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
) - 如果您希望支持单点注销,则具有 HTTP-Redirect 绑定的
<SingleLogoutService>
(urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
)
Elastic Stack 要求 IdP 的所有消息都已签名。对于身份验证<Response>
消息,签名可以应用于响应本身或各个断言。对于<LogoutRequest>
消息,消息本身必须签名,并且签名应作为 URL 参数提供,正如 HTTP-Redirect 绑定所需的那样。
配置 Elasticsearch 以进行 SAML 身份验证
编辑在 Elasticsearch 中启用 SAML 身份验证需要五个配置步骤:
- 为 HTTP 启用 SSL/TLS
- 启用令牌服务
- 创建一个或多个 SAML 领域
- 配置角色映射
- 生成供您的身份提供商使用的 SAML 元数据文件(可选)
为 HTTP 启用 TLS
编辑如果您的 Elasticsearch 集群在生产模式下运行,则必须配置 HTTP 接口以使用 SSL/TLS,然后才能启用 SAML 身份验证。
有关更多信息,请参阅加密 Elasticsearch 的 HTTP 客户端通信。
启用令牌服务
编辑Elasticsearch SAML 实现使用 Elasticsearch 令牌服务。如果您在 HTTP 接口上配置了 TLS,则此服务会自动启用,并且可以通过在您的elasticsearch.yml
文件中包含以下内容来显式配置:
xpack.security.authc.token.enabled: true
创建一个 SAML 领域
编辑通过在 Elasticsearch 的身份验证链中配置 SAML 领域来启用 SAML 身份验证。
此领域有一些必填设置和一些可选设置。可用设置在安全设置中详细描述。例如,SAML 领域设置、SAML 领域签名设置、SAML 领域加密设置、SAML 领域 SSL 设置。本指南将引导您完成最常见的设置。
通过将以下内容添加到您的elasticsearch.yml
配置文件来创建领域。下面解释每个配置值。
xpack.security.authc.realms.saml.saml1: order: 2 idp.metadata.path: saml/idp-metadata.xml idp.entity_id: "https://sso.example.com/" sp.entity_id: "https://kibana.example.com/" sp.acs: "https://kibana.example.com/api/security/saml/callback" sp.logout: "https://kibana.example.com/logout" attributes.principal: "urn:oid:0.9.2342.19200300.100.1.1" attributes.groups: "urn:oid:1.3.6.1.4.1.5923.1.5.1."
在通过 Kibana 进行身份验证时使用 SAML,但它不是直接向 Elasticsearch REST API 进行身份验证的有效方法。因此,我们建议您在身份验证链中至少包含一个附加领域,例如本地领域,供 API 客户端使用。
上面示例中使用的配置值是:
- xpack.security.authc.realms.saml.saml1
- 这定义了一个名为“saml1”的新
saml
身份验证领域。有关领域的更多说明,请参阅领域。 - 顺序
- 领域在领域链中的顺序。顺序较低的领域具有最高优先级,并且最先被查询。我们建议为基于密码的领域(例如文件、本地、LDAP 和活动目录)赋予最低顺序(最高优先级),然后是 SSO 领域(例如 SAML 和 OpenID Connect)。如果您有多个相同类型的领域,请为最常访问的领域赋予最低顺序,使其首先被查询。
- idp.metadata.path
- 这是您为身份提供商保存的元数据文件的路径。您在此处输入的路径相对于您的
config/
目录。Elasticsearch 将自动监控此文件的更改,并在更新时重新加载配置。 - idp.entity_id
- 这是您的 IdP 使用的标识符(SAML EntityID)。它应与元数据文件中的
entityID
属性匹配。 - sp.entity_id
- 这是 Kibana 实例的唯一标识符,表示为 URI。当您在 IdP 中将 Kibana 添加为服务提供商时,将使用此值。我们建议您使用 Kibana 实例的基 URL 作为实体 ID。
- sp.acs
- 断言使用者服务 (ACS) 端点是 Kibana 中接受来自 IdP 的身份验证消息的 URL。此 ACS 端点仅支持 SAML HTTP-POST 绑定。它必须是尝试登录 Kibana 的用户的 Web 浏览器可以访问的 URL,Elasticsearch 或 IdP 不需要直接访问它。正确的值可能因您安装 Kibana 的方式以及是否有任何代理参与而异,但通常是
${kibana-url}/api/security/saml/callback
,其中${kibana-url}是 Kibana 实例的基 URL。 - sp.logout
- 这是 Kibana 中接受来自 IdP 的注销消息的 URL。与
sp.acs
URL 一样,它必须可从 Web 浏览器访问,但 Elasticsearch 或 IdP 不需要直接访问它。正确的值可能因您安装 Kibana 的方式以及是否有任何代理参与而异,但通常是${kibana-url}/logout
,其中${kibana-url}是 Kibana 实例的基 URL。 - attributes.principal
- 请参阅属性映射。
- attributes.groups
- 请参阅属性映射。
属性映射
编辑当用户通过您的身份提供商连接到 Kibana 时,身份提供商将提供有关用户的 SAML 断言。断言将包含一个身份验证声明,指示用户已成功向 IdP 进行身份验证,以及一个或多个属性声明,这些声明将包含用户的属性。
这些属性可能包括以下内容:
- 用户的用户名
- 用户的电子邮件地址
- 用户的组或角色
SAML 中的属性使用 URI(例如urn:oid:0.9.2342.19200300.100.1.1
或http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn
)命名,并具有一个或多个与其关联的值。
这些属性标识符在 IdP 之间有所不同,大多数 IdP 提供自定义 URI 及其关联值的方法。
Elasticsearch 使用这些属性来推断已登录用户的相关信息,并且可以将它们用于角色映射(如下所示)。
为了使这些属性发挥作用,Elasticsearch 和 IdP 需要为属性名称使用相同的公共值。这需要手动完成,通过配置 IdP 和 SAML 领域来为每个逻辑用户属性使用相同的 URI 名称。
配置这些 SAML 属性的推荐步骤如下所示:
- 查阅您的 IdP 文档,了解其可以提供的用户属性。这在不同的提供商之间差异很大,但您应该能够从文档或本地管理员处获得列表。
- 阅读 Elasticsearch 支持的用户属性列表,并确定哪些属性对您有用,以及您的 IdP 是否可以提供这些属性。至少需要
principal
属性。 - 配置您的 IdP 向您的 Kibana SAML 服务提供商“发布”这些属性。此过程因提供商而异——有些提供商会为此提供用户界面,而另一些提供商可能需要您编辑配置文件。通常,IdP(或您的本地管理员)会对每个属性使用哪个 URI 提供建议。您可以简单地接受这些建议,因为 Elasticsearch 服务是完全可配置的,不需要使用任何特定的 URI。
- 配置 Elasticsearch 中的 SAML 领域,将 Elasticsearch 用户属性(参见下面的列表)与您在 IdP 中配置的 URI 关联起来。在上面的示例中,我们配置了
principal
和groups
属性。
特殊属性名称
编辑通常,Elasticsearch 期望属性的配置值为一个 URI,例如 urn:oid:0.9.2342.19200300.100.1.1
,但是也可以使用一些其他名称。
-
nameid
- 这使用 SAML
NameID
值(删除所有前导和尾随空格),而不是 SAML 属性。SAMLNameID
元素是 SAML 断言中一个可选但经常提供的字段,IdP 可以用它来标识该断言的主体。在某些情况下,NameID
将与用户在 IdP 中的登录标识符(用户名)相关联,但在许多情况下,它们将是内部生成的标识符,在 IdP 之外没有明显的含义。 -
nameid:persistent
- 这使用 SAML
NameID
值(删除所有前导和尾随空格),但仅当 NameID 格式为urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
时。SAMLNameID
元素具有一个可选的Format
属性,用于指示提供的名称的语义。IdP 通常配置为使用“临时”NameID,为每个会话提供一个新的标识符。由于将临时 NameID 用作属性映射的一部分很少有用,“nameid:persistent”属性名称可以用作安全机制,如果尝试从没有持久值的NameID
进行映射,则会引发错误。
身份提供商可以静态配置为发布具有特定格式的 NameID
,也可以配置为尝试符合 SP 的要求。SP 使用名为 NameIDPolicy
的元素声明其要求作为身份验证请求的一部分。如果需要,您可以设置相关的设置(名为 nameid_format
),以请求 IdP 发布具有特定格式的 NameID
。
- friendlyName
- SAML 属性除了基于 URI 的名称外,还可以具有friendlyName。例如,名称为
urn:oid:0.9.2342.19200300.100.1.1
的属性还可以具有uid
的 friendlyName。您可以在属性映射中使用这些 friendlyName,但建议您使用基于 URI 的名称,因为 friendlyName 既不是标准化的,也不是强制性的。
下面的示例配置一个领域,以便为 principal 使用持久性 nameid,并为用户的组使用 friendlyName 为“roles”的属性。
xpack.security.authc.realms.saml.saml1: order: 2 idp.metadata.path: saml/idp-metadata.xml idp.entity_id: "https://sso.example.com/" sp.entity_id: "https://kibana.example.com/" sp.acs: "https://kibana.example.com/api/security/saml/callback" attributes.principal: "nameid:persistent" attributes.groups: "roles" nameid_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
Elasticsearch 用户属性
编辑Elasticsearch SAML 领域可以配置为将 SAML attributes
映射到已认证用户的以下属性:
- principal
-
(必需)这是将应用于针对此领域进行身份验证的用户的用户名。
principal
出现在 Elasticsearch 审计日志等位置。 - groups
-
(推荐)如果您希望使用 IdP 的组或角色概念作为用户 Elasticsearch 权限的基础,则应使用此属性对其进行映射。
groups
直接传递给您的角色映射规则。一些 IdP 配置为将
groups
列表作为单个值(逗号分隔的字符串)发送。要将此 SAML 属性映射到 Elasticsearch 领域中的attributes.groups
设置,您可以使用attribute_delimiters.group
设置配置字符串分隔符。例如,在分隔符值为
,
的情况下,拆分 SAML 属性值engineering,elasticsearch-admins,employees
将产生engineering
、elasticsearch-admins
和employees
作为用户的组列表。 - name
- (可选)用户的全名。
- (可选)用户的电子邮件地址。
- dn
- (可选)用户的 X.500 可分辨名称。
从 SAML 属性中提取部分值
编辑在某些情况下,IdP 的属性可能包含比您希望在 Elasticsearch 中使用的更多信息。一个常见的例子是 IdP 专门使用电子邮件地址,但您希望用户的 principal
使用电子邮件地址的本地名称部分。例如,如果他们的电子邮件地址是 [email protected]
,那么您希望他们的 principal 只是 james.wong
。
这可以通过使用 Elasticsearch 领域中的 attribute_patterns
设置来实现,如下面的领域配置所示。
xpack.security.authc.realms.saml.saml1: order: 2 idp.metadata.path: saml/idp-metadata.xml idp.entity_id: "https://sso.example.com/" sp.entity_id: "https://kibana.example.com/" sp.acs: "https://kibana.example.com/api/security/saml/callback" attributes.principal: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" attribute_patterns.principal: "^([^@]+)@staff\\.example\\.com$"
在这种情况下,用户的 principal
从电子邮件属性映射,但在将其分配给用户之前,会对该值应用正则表达式。如果正则表达式匹配,则第一组的结果将用作有效值。如果正则表达式不匹配,则属性映射失败。
在这个例子中,电子邮件地址必须属于 staff.example.com
域名,然后使用本地部分(@之前的任何内容)作为 principal。任何尝试使用不同电子邮件域登录的用户都将失败,因为正则表达式将不匹配他们的电子邮件地址,因此他们的 principal 属性(这是必需的)将不会填充。
这些正则表达式中的小错误可能会产生重大的安全后果。例如,如果我们不小心从上面的示例中省略了尾随的 $
,那么我们将匹配域名以 staff.example.com
开头的任何电子邮件地址,这将接受诸如 [email protected]
之类的电子邮件地址。务必确保您的正则表达式尽可能精确,以免无意中为用户模拟攻击打开途径。
请求特定的身份验证方法
编辑SAML SP 有时需要能够对 IdP 中将要进行的身份验证施加具体的限制,以便评估其可以对相应身份验证响应的信任程度。这些限制可能与身份验证方法(密码、客户端证书等)、注册期间的用户标识方法以及其他细节有关。Elasticsearch 实现SAML 2.0 身份验证上下文,这可以用于 SAML 2.0 核心规范中定义的此目的。
简而言之,SAML SP 定义了一组身份验证上下文类引用值,这些值描述了要对 IdP 施加的限制,并将这些值发送到身份验证请求中。IdP 尝试授予这些限制。如果无法授予这些限制,则身份验证尝试失败。如果用户成功进行身份验证,则 SAML 响应的身份验证声明包含已满足限制的指示。
您可以通过在 SAML 领域配置中使用 req_authn_context_class_ref
选项来定义身份验证上下文类引用值。参见SAML 领域设置。
Elasticsearch 只支持身份验证上下文的 exact
比较方法。当它从 IdP 接收身份验证响应时,Elasticsearch 会检查作为 SAML 断言的身份验证声明一部分的身份验证上下文类引用的值。如果它与请求的值之一匹配,则身份验证被认为成功。否则,身份验证尝试失败。
SAML 注销
编辑SAML 协议支持单点注销 (SLO) 的概念。对 SLO 的支持水平因身份提供商而异。您应该查阅 IdP 的文档,以确定其提供的注销服务。
默认情况下,Elastic Stack 将支持 SAML SLO,如果满足以下条件:
- 您的 IdP 元数据指定 IdP 提供 SLO 服务
- 您的 IdP 在其为您的用户发布的 SAML 断言的主体中发布 NameID
- 您配置
sp.logout
- 设置
idp.use_single_logout
不是false
IdP SLO 服务
编辑Elasticsearch 从 IdP 的 SAML 元数据读取的值之一是 <SingleLogoutService>
。为了使单点注销与 Elastic Stack 一起工作,Elasticsearch 要求此项存在并支持 urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
的绑定。
Elastic Stack 将根据需要向此服务发送 <LogoutRequest>
和 <LogoutResponse>
消息。
sp.logout 设置
编辑Elasticsearch 的 realm 设置 sp.logout
指定 Kibana 中一个 URL,IdP 可以向该 URL 发送 <LogoutRequest>
和 <LogoutResponse>
消息。此服务使用 SAML HTTP-Redirect 绑定。
Elasticsearch 将处理 <LogoutRequest>
消息,并执行全局注销,使与提供的 SAML 会话关联的任何现有 Elasticsearch 安全令牌失效。
如果未为 sp.logout
配置值,则 Elasticsearch 将拒绝所有 <LogoutRequest>
消息。
IdP 通常要求对 LogoutRequest
消息进行签名,因此您可能需要配置 签名凭据。
idp.use_single_logout 设置
编辑如果您的 IdP 提供 <SingleLogoutService>
,但您不想使用它,则可以在 SAML realm 中配置 idp.use_single_logout: false
,Elasticsearch 将忽略您的 IdP 提供的 SLO 服务。在这种情况下,当用户注销 Kibana 时,它将使他们的 Elasticsearch 会话(安全令牌)失效,但不会在 IdP 上执行任何注销操作。
在没有单点注销的情况下使用 Kibana
编辑如果您的 IdP 不支持单点注销,或者您选择不使用它,则 Kibana 将仅执行“本地注销”。
这意味着 Kibana 将使它用于与 Elasticsearch 通信的会话令牌失效,但无法使身份提供商会话失效。在大多数情况下,这意味着 Kibana 用户仍被认为已登录 IdP。因此,如果用户导航到 Kibana 首页,他们将自动重新进行身份验证,并将开始一个新的 Kibana 会话,而无需输入任何凭据。
解决此问题的可能方案是:
- 请求您的 IdP 管理员或供应商提供单点注销服务。
- 如果您的 IdP 提供单点注销服务,请确保将其包含在 IdP 元数据文件中,并且不要将
idp.use_single_logout
设置为false
。 - 建议您的用户在注销 Kibana 后关闭浏览器。
- 在您的 SAML realm 上启用
force_authn
设置。此设置使 Elastic Stack 每次用户尝试登录 Kibana 时都向 IdP 请求新的身份验证。此设置默认为false
,因为它可能会带来更繁琐的用户体验,但它也可以有效地防止用户利用现有 IdP 会话。
加密和签名
编辑Elastic Stack 支持生成签名的 SAML 消息(用于身份验证和/或注销),验证来自 IdP 的签名的 SAML 消息(用于身份验证和注销),并可以处理加密内容。
您可以使用相同或单独的密钥为签名、加密或两者配置 Elasticsearch。
Elastic Stack 使用带有 RSA 私钥的 X.509 证书进行 SAML 加密。可以使用任何标准的 SSL 工具生成这些密钥,包括 elasticsearch-certutil
工具。
您的 IdP 可能要求 Elastic Stack 具有用于签名 SAML 消息的加密密钥,并且您需要在服务提供商配置中提供相应的签名证书(在 Elastic Stack SAML 元数据文件内或在 IdP 管理界面内手动配置)。虽然大多数 IdP 不希望对身份验证请求进行签名,但通常情况下,注销请求需要签名。您的 IdP 将根据为 Elastic Stack 服务提供商配置的签名证书验证这些签名。
加密证书很少需要,但 Elastic Stack 支持它们,以应对 IdP 或本地策略强制使用它们的情况。
生成证书和密钥
编辑Elasticsearch 支持 PEM、PKCS#12 或 JKS 格式的证书和密钥。某些身份提供商对其支持的格式限制更多,并将要求您以特定格式的文件形式提供证书。您应该查阅 IdP 的文档以确定它们支持哪些格式。由于 PEM 格式是最常用的格式,因此以下示例将生成该格式的证书。
使用 elasticsearch-certutil
工具,您可以使用以下命令生成签名证书:
bin/elasticsearch-certutil cert --self-signed --pem --days 1100 --name saml-sign --out saml-sign.zip
这将:
- 生成证书和密钥对(
cert
子命令) - 以 PEM 格式创建文件(
-pem
选项) - 生成有效期为 3 年的证书(
-days 1100
) - 将证书命名为
saml-sign
(-name
选项) - 将证书和密钥保存在
saml-sign.zip
文件中(-out
选项)
生成的 zip 存档将包含 3 个文件:
-
saml-sign.crt
,用于签名的公共证书 -
saml-sign.key
,证书的私钥 -
ca.crt
,不需要的 CA 证书,可以忽略。
可以使用相同的过程生成加密证书。
配置 Elasticsearch 以进行签名
编辑默认情况下,如果已配置签名密钥,则 Elasticsearch 将对所有传出的 SAML 消息进行签名。
如果要使用PEM 格式的密钥和证书进行签名,则应在 SAML realm 上配置以下设置:
-
signing.certificate
- PEM 格式证书文件的路径,例如:
saml/saml-sign.crt
-
signing.key
- PEM 格式密钥文件的路径,例如:
saml/saml-sign.key
-
signing.secure_key_passphrase
- 密钥的密码短语(如果文件已加密)。这是一个 安全设置,必须使用
elasticsearch-keystore
工具进行设置。
如果要使用PKCS#12 格式的文件或Java 密钥库进行签名,则应在 SAML realm 上配置以下设置:
-
signing.keystore.path
- PKCS#12 或 JKS 密钥库的路径,例如:
saml/saml-sign.p12
-
signing.keystore.alias
- 密钥在密钥库中的别名,例如:
signing-key
-
signing.keystore.secure_password
- 密钥库的密码短语(如果文件已加密)。这是一个 安全设置,必须使用
elasticsearch-keystore
工具进行设置。
如果只想对某些传出的SAML 消息进行签名,则应在 SAML realm 上配置以下设置:
-
signing.saml_messages
- 要签名的消息类型的列表。消息类型由用于消息的 XML 元素的本地名称标识。支持的值为:
AuthnRequest
、LogoutRequest
和LogoutResponse
。
配置 Elasticsearch 以处理加密消息
编辑Elasticsearch 安全功能支持单个密钥用于消息解密。如果配置了密钥,则 Elasticsearch 将尝试使用它来解密身份验证响应中的 EncryptedAssertion
和 EncryptedAttribute
元素,以及注销请求中的 EncryptedID
元素。
Elasticsearch 将拒绝任何包含无法解密的 EncryptedAssertion
的 SAML 消息。
如果 Assertion
包含加密和纯文本属性,则无法解密加密属性不会导致自动拒绝。相反,Elasticsearch 将处理可用的纯文本属性(以及可以解密的任何 EncryptedAttributes
)。
如果要使用PEM 格式的密钥和证书进行 SAML 加密,则应在 SAML realm 上配置以下设置:
-
encryption.certificate
- PEM 格式证书文件的路径,例如:
saml/saml-crypt.crt
-
encryption.key
- PEM 格式密钥文件的路径,例如:
saml/saml-crypt.key
-
encryption.secure_key_passphrase
- 密钥的密码短语(如果文件已加密)。这是一个 安全设置,必须使用
elasticsearch-keystore
工具进行设置。
如果要使用PKCS#12 格式的文件或Java 密钥库进行 SAML 加密,则应在 SAML realm 上配置以下设置:
-
encryption.keystore.path
- PKCS#12 或 JKS 密钥库的路径,例如:
saml/saml-crypt.p12
-
encryption.keystore.alias
- 密钥在密钥库中的别名,例如:
encryption-key
-
encryption.keystore.secure_password
- 密钥库的密码短语(如果文件已加密)。这是一个 安全设置,必须使用
elasticsearch-keystore
工具进行设置。
生成 SP 元数据
编辑某些身份提供商支持从服务提供商导入元数据文件。这将自动配置 IdP 和 SP 之间的许多集成选项。
Elastic Stack 支持使用 bin/elasticsearch-saml-metadata
命令 或 SAML 服务提供商元数据 API 生成此类元数据文件。
您可以通过向 Elasticsearch 发出 API 请求并使用 jq
等工具将其存储为 XML 文件来生成 SAML 元数据。例如,以下命令为 SAML realm realm1
生成元数据并将其保存到 metadata.xml
文件:
curl -u user_name:password -X GET https://127.0.0.1:9200/_security/saml/metadata/saml1 -H 'Content-Type: application/json' | jq -r '.[]' > metadata.xml
配置角色映射
编辑当用户使用 SAML 进行身份验证时,他们会被 Elastic Stack 识别,但这不会自动授予他们执行任何操作或访问任何数据的权限。
您的 SAML 用户在被分配角色之前无法执行任何操作。这可以通过 添加角色映射 API 或使用 授权 realm 来完成。
您无法使用 角色映射文件 来向通过 SAML 进行身份验证的用户授予角色。
这是一个简单的角色映射示例,它将 example_role
角色授予针对 saml1
realm 进行身份验证的任何用户。
resp = client.security.put_role_mapping( name="saml-example", roles=[ "example_role" ], enabled=True, rules={ "field": { "realm.name": "saml1" } }, ) print(resp)
const response = await client.security.putRoleMapping({ name: "saml-example", roles: ["example_role"], enabled: true, rules: { field: { "realm.name": "saml1", }, }, }); console.log(response);
PUT /_security/role_mapping/saml-example { "roles": [ "example_role" ], "enabled": true, "rules": { "field": { "realm.name": "saml1" } } }
通过领域配置映射的属性用于处理角色映射规则,这些规则决定授予用户的角色。
提供给角色映射的用户字段由 SAML 属性派生,如下所示
-
username
:principal
属性 -
dn
:dn
属性 -
groups
:groups
属性 -
metadata
:请参见 用户元数据
更多信息,请参见 将用户和组映射到角色 和 角色映射。
如果您的 IdP 能够向服务提供商提供组或角色,则应将此 SAML 属性映射到 Elasticsearch 领域中的 attributes.groups
设置,然后根据以下示例在角色映射中使用它。
此映射将 Elasticsearch finance_data
角色授予通过 saml1
领域使用 finance-team
组进行身份验证的任何用户。
resp = client.security.put_role_mapping( name="saml-finance", roles=[ "finance_data" ], enabled=True, rules={ "all": [ { "field": { "realm.name": "saml1" } }, { "field": { "groups": "finance-team" } } ] }, ) print(resp)
const response = await client.security.putRoleMapping({ name: "saml-finance", roles: ["finance_data"], enabled: true, rules: { all: [ { field: { "realm.name": "saml1", }, }, { field: { groups: "finance-team", }, }, ], }, }); console.log(response);
PUT /_security/role_mapping/saml-finance { "roles": [ "finance_data" ], "enabled": true, "rules": { "all": [ { "field": { "realm.name": "saml1" } }, { "field": { "groups": "finance-team" } } ] } }
|
如果您的用户也存在于 Elasticsearch 可以直接访问的存储库(例如 LDAP 目录)中,则可以使用 授权领域 而不是角色映射。
在这种情况下,请执行以下步骤:1. 在您的 SAML 领域中,分配一个 SAML 属性作为查找用户 ID,方法是配置 attributes.principal
设置。2. 创建一个可以从本地存储库(例如 ldap
领域)查找用户的新的领域。3. 在您的 SAML 领域中,将 authorization_realms
设置为您在步骤 2 中创建的领域的名称。
用户元数据
编辑默认情况下,通过 SAML 进行身份验证的用户将拥有一些额外的元数据字段。
-
saml_nameid
将设置为 SAML 身份验证响应中NameID
元素的值 -
saml_nameid_format
将设置为 NameID 的format
属性的完整 URI - 身份验证响应中提供的每个 SAML 属性(无论是否映射到 Elasticsearch 用户属性),都将作为元数据字段
saml(name)
添加,其中“name”是属性的完整 URI 名称。例如saml(urn:oid:0.9.2342.19200300.100.1.3)
。 - 对于每个具有 *friendlyName* 的 SAML 属性,也将作为元数据字段
saml_friendlyName
添加,其中“name”是属性的完整 URI 名称。例如saml_mail
。
可以通过在 saml 领域中添加 populate_user_metadata: false
作为设置来禁用此行为。
配置 Kibana
编辑Kibana 中的 SAML 身份验证需要一些额外的设置,除了标准的 Kibana 安全配置之外。Kibana 安全文档 提供了有关您可以应用的可用配置选项的详细信息。
特别是,由于您的 Elasticsearch 节点已配置为在 HTTP 接口上使用 TLS,因此您必须将 Kibana 配置为使用 https
URL 连接到 Elasticsearch,并且您可能需要配置 elasticsearch.ssl.certificateAuthorities
以信任 Elasticsearch 已配置为使用的证书。
Kibana 中的 SAML 身份验证受 kibana.yml
中的以下超时设置影响
您可能需要根据您的安全要求调整这些超时。
SAML 支持所需的三个额外设置如下所示
xpack.security.authc.providers: saml.saml1: order: 0 realm: "saml1"
上面示例中使用的配置值是:
-
xpack.security.authc.providers
- 添加
saml
提供程序以指示 Kibana 使用 SAML SSO 作为身份验证方法。 -
xpack.security.authc.providers.saml.<provider-name>.realm
- 将其设置为在 Elasticsearch 领域配置 中使用的 SAML 领域的名称,例如:
saml1
支持 Kibana 中的 SAML 和基本身份验证
编辑Kibana 中的 SAML 支持的设计预期它是该 Kibana 实例用户的首要(或唯一)身份验证方法。但是,可以通过设置 xpack.security.authc.providers
(如下例所示)在一个 Kibana 实例中同时支持 SAML 和基本身份验证。
xpack.security.authc.providers: saml.saml1: order: 0 realm: "saml1" basic.basic1: order: 1
如果 Kibana 以这种方式配置,则用户会在登录选择器 UI 中看到一个选择。他们可以使用 SAML 登录,或者提供用户名和密码,并依赖于 Elasticsearch 中的其他安全领域之一。只有拥有已配置 Elasticsearch 身份验证领域的用户名和密码的用户才能通过 Kibana 登录表单登录。
或者,启用 basic
身份验证提供程序后,可以在 Kibana 前面放置一个反向代理,并将其配置为为每个请求发送基本身份验证标头 (Authorization: Basic ....
)。如果此标头存在且有效,则 Kibana 将不会启动 SAML 身份验证过程。
操作多个 Kibana 实例
编辑如果您希望拥有多个针对同一 Elasticsearch 集群进行身份验证的 Kibana 实例,则每个配置为 SAML 身份验证的 Kibana 实例都需要其自己的 SAML 领域。
每个 SAML 领域必须具有其自己的唯一实体 ID (sp.entity_id
) 和其自己的 *断言使用者服务* (sp.acs
)。每个 Kibana 实例都将通过查找匹配的 sp.acs
值映射到正确的领域。
这些领域可以使用相同的身份提供商,但不强制要求。
以下是如何拥有 3 个不同的 Kibana 实例的示例,其中 2 个使用相同的内部 IdP,另一个使用不同的 IdP。
xpack.security.authc.realms.saml.saml_finance: order: 2 idp.metadata.path: saml/idp-metadata.xml idp.entity_id: "https://sso.example.com/" sp.entity_id: "https://kibana.finance.example.com/" sp.acs: "https://kibana.finance.example.com/api/security/saml/callback" sp.logout: "https://kibana.finance.example.com/logout" attributes.principal: "urn:oid:0.9.2342.19200300.100.1.1" attributes.groups: "urn:oid:1.3.6.1.4.1.5923.1.5.1." xpack.security.authc.realms.saml.saml_sales: order: 3 idp.metadata.path: saml/idp-metadata.xml idp.entity_id: "https://sso.example.com/" sp.entity_id: "https://kibana.sales.example.com/" sp.acs: "https://kibana.sales.example.com/api/security/saml/callback" sp.logout: "https://kibana.sales.example.com/logout" attributes.principal: "urn:oid:0.9.2342.19200300.100.1.1" attributes.groups: "urn:oid:1.3.6.1.4.1.5923.1.5.1." xpack.security.authc.realms.saml.saml_eng: order: 4 idp.metadata.path: saml/idp-external.xml idp.entity_id: "https://engineering.sso.example.net/" sp.entity_id: "https://kibana.engineering.example.com/" sp.acs: "https://kibana.engineering.example.com/api/security/saml/callback" sp.logout: "https://kibana.engineering.example.com/logout" attributes.principal: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
可以拥有一个或多个使用 SAML 的 Kibana 实例,而其他实例则使用针对其他领域类型的基本身份验证(例如 本地 或 LDAP)。
SAML 领域配置疑难解答
编辑SAML 2.0 规范为标准的实施者提供了许多选项和灵活性,这反过来又增加了复杂性和服务提供商 (Elastic Stack) 和身份提供商中可用的配置选项的数量。此外,不同的安全域具有不同的安全要求,需要特定的配置才能满足。我们已经努力通过合理的默认值和上面的详细文档来掩盖这种复杂性,但是如果您在配置 SAML 领域时遇到问题,您可以查看我们的 SAML 疑难解答文档,其中包含常见问题的建议和解决方案。
无 Kibana 的 SAML
编辑Elasticsearch 中的 SAML 领域旨在允许用户向 Kibana 进行身份验证,因此,上面指南的大部分内容都假设使用了 Kibana。本节描述自定义 Web 应用程序如何使用相关的 SAML REST API 来使用 SAML 向 Elasticsearch 进行用户身份验证。
本节假设读者熟悉 SAML 2.0 标准,更具体地说,熟悉 SAML 2.0 Web 浏览器单点登录配置文件。
诸如 OpenID Connect 和 SAML 之类的单点登录领域使用 Elasticsearch 中的令牌服务,原则上将 SAML 或 OpenID Connect 身份验证响应交换为 Elasticsearch 访问令牌和刷新令牌。访问令牌用作后续对 Elasticsearch 的调用的凭据。刷新令牌使用户能够在当前令牌过期后获取新的 Elasticsearch 访问令牌。
SAML 领域
编辑您必须创建一个 SAML 领域并在 Elasticsearch 中相应地进行配置。请参见 配置 Elasticsearch 以进行 SAML 身份验证
用于访问 API 的服务帐户用户
编辑该领域的设想是需要一个特权实体充当身份验证代理。在这种情况下,自定义 Web 应用程序是处理最终用户身份验证的认证代理(更准确地说,是“将”身份验证委托给 SAML 身份提供商)。SAML 相关的 API 需要身份验证和已认证用户的必要授权级别。因此,您必须创建一个服务帐户用户并为其分配一个角色,该角色赋予其 manage_saml
集群权限。身份验证后,将需要使用 manage_token
集群权限,以便服务帐户用户可以维护访问权限,以便代表已认证用户刷新访问令牌或随后注销他们。
resp = client.security.put_role( name="saml-service-role", cluster=[ "manage_saml", "manage_token" ], ) print(resp)
const response = await client.security.putRole({ name: "saml-service-role", cluster: ["manage_saml", "manage_token"], }); console.log(response);
POST /_security/role/saml-service-role { "cluster" : ["manage_saml", "manage_token"] }
resp = client.security.put_user( username="saml-service-user", password="<somePasswordHere>", roles=[ "saml-service-role" ], ) print(resp)
const response = await client.security.putUser({ username: "saml-service-user", password: "<somePasswordHere>", roles: ["saml-service-role"], }); console.log(response);
POST /_security/user/saml-service-user { "password" : "<somePasswordHere>", "roles" : ["saml-service-role"] }
处理 SP 发起的身份验证流程
编辑从高级角度来看,自定义 Web 应用程序需要执行以下步骤才能使用 SAML 对 Elasticsearch 进行用户身份验证
-
向
_security/saml/prepare
发出 HTTP POST 请求,并作为saml-service-user
用户进行身份验证。在请求正文中使用 Elasticsearch 配置中 SAML 领域的名称或断言使用者服务 URL 的值。有关更多详细信息,请参见 SAML 预准备身份验证 API。resp = client.security.saml_prepare_authentication( realm="saml1", ) print(resp)
const response = await client.security.samlPrepareAuthentication({ realm: "saml1", }); console.log(response);
POST /_security/saml/prepare { "realm" : "saml1" }
- 处理来自
/_security/saml/prepare
的响应。来自 Elasticsearch 的响应将包含 3 个参数:redirect
、realm
和id
。自定义 Web 应用程序需要将id
的值存储在用户的会话中(客户端在 cookie 中或如果以这种方式持久化会话信息则存储在服务器端)。它还必须将用户的浏览器重定向到在redirect
参数中返回的 URL。id
值不应忽略,因为它在 SAML 中用作 nonce 以减轻重放攻击。 -
处理来自 SAML IdP 的后续响应。用户成功通过身份提供商身份验证后,将被重定向回断言使用者服务 URL。此
sp.acs
需要定义为自定义 Web 应用程序处理的 URL。当它收到此 HTTP POST 请求时,自定义 Web 应用程序必须对其进行解析,并自行向_security/saml/authenticate
API 发出 HTTP POST 请求。它必须以saml-service-user
用户身份进行身份验证,并将作为请求正文发送的 Base64 编码的 SAML 响应传递过去。它还必须传递之前保存在用户会话中的id
值。更多详情请参见 SAML 身份验证 API。
resp = client.security.saml_authenticate( content="PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMD.....", ids=[ "4fee3b046395c4e751011e97f8900b5273d56685" ], ) print(resp)
const response = await client.security.samlAuthenticate({ content: "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMD.....", ids: ["4fee3b046395c4e751011e97f8900b5273d56685"], }); console.log(response);
POST /_security/saml/authenticate { "content" : "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMD.....", "ids" : ["4fee3b046395c4e751011e97f8900b5273d56685"] }
Elasticsearch 将对此进行验证,如果一切正确,将返回可作为后续请求的
Bearer
令牌使用的访问令牌。它还会提供一个刷新令牌,稍后可用于刷新给定的访问令牌,如 获取令牌 API 中所述。 - 调用
/_security/saml/authenticate
的响应将仅包含已验证用户的用户名。如果您需要获取 SAML 响应中包含的该用户的 SAML 属性值,您可以使用访问令牌作为Bearer
令牌调用身份验证 API/_security/_authenticate/
,SAML 属性值将作为 用户元数据 的一部分包含在响应中。
处理 IdP 发起的身份验证流程
编辑Elasticsearch 还可以处理 SAML 2 Web 浏览器 SSO 配置文件的 IdP 发起的单点登录流程。在这种情况下,身份验证从 SAML 身份提供商发出的主动身份验证响应开始。与 SP 发起的 SSO 的区别在于,Web 应用程序需要处理对 sp.acs
的请求,这些请求不会作为对先前重定向的响应出现。因此,它不会预先拥有用户的会话,也不会拥有 id
参数的任何存储值。在这种情况下,对 _security/saml/authenticate
API 的请求将如下所示
resp = client.security.saml_authenticate( content="PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMD.....", ids=[], ) print(resp)
const response = await client.security.samlAuthenticate({ content: "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMD.....", ids: [], }); console.log(response);
POST /_security/saml/authenticate { "content" : "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMD.....", "ids" : [] }
处理注销流程
编辑-
如有必要,自定义 Web 应用程序可以使用 SAML 注销 API 并将访问令牌和刷新令牌作为参数传递来注销用户。例如
resp = client.security.saml_logout( token="46ToAxZVaXVVZTVKOVF5YU04ZFJVUDVSZlV3", refresh_token="mJdXLtmvTUSpoLwMvdBt_w", ) print(resp)
const response = await client.security.samlLogout({ token: "46ToAxZVaXVVZTVKOVF5YU04ZFJVUDVSZlV3", refresh_token: "mJdXLtmvTUSpoLwMvdBt_w", }); console.log(response);
POST /_security/saml/logout { "token" : "46ToAxZVaXVVZTVKOVF5YU04ZFJVUDVSZlV3", "refresh_token": "mJdXLtmvTUSpoLwMvdBt_w" }
如果 SAML 领域相应地配置并且 IdP 支持它(请参阅 SAML 注销),此请求将触发 SAML SP 发起的单点注销。在这种情况下,响应将包含一个
redirect
参数,指示用户需要在 IdP 中重定向到哪里才能完成注销。 -
或者,IdP 可能会在某些时候启动单点注销流程。为了处理这个问题,自定义 Web 应用需要处理注销 URL(
sp.logout
)。用户将被重定向到的 URL 的查询部分将包含 SAML 注销请求,并且此查询部分需要使用 SAML 无效 API 传递给 Elasticsearch。resp = client.security.saml_invalidate( query="SAMLRequest=nZFda4MwFIb%2FiuS%2BmviRpqFaClKQdbvo2g12M2KMraCJ9cRR9utnW4Wyi13sMie873MeznJ1aWrnS3VQGR0j4mLkKC1NUeljjA77zYyhVbIE0dR%2By7fmaHq7U%2BdegXWGpAZ%2B%2F4pR32luBFTAtWgUcCv56%2Fp5y30X87Yz1khTIycdgpUW9kY7WdsC9zxoXTvMvWuVV98YyMnSGH2SYE5pwALBIr9QKiwDGpW0oGVUznGeMyJZKFkQ4jBf5HnhUymjIhzCAL3KNFihbYx8TBYzzGaY7EnIyZwHzCWMfiDnbRIftkSjJr%2BFu0e9v%2B0EgOquRiiZjKpiVFp6j50T4WXoyNJ%2FEWC9fdqc1t%2F1%2B2F3aUpjzhPiXpqMz1%2FHSn4A&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=MsAYz2NFdovMG2mXf6TSpu5vlQQyEJAg%2B4KCwBqJTmrb3yGXKUtIgvjqf88eCAK32v3eN8vupjPC8LglYmke1ZnjK0%2FKxzkvSjTVA7mMQe2AQdKbkyC038zzRq%2FYHcjFDE%2Bz0qISwSHZY2NyLePmwU7SexEXnIz37jKC6NMEhus%3D", realm="saml1", ) print(resp)
const response = await client.security.samlInvalidate({ query: "SAMLRequest=nZFda4MwFIb%2FiuS%2BmviRpqFaClKQdbvo2g12M2KMraCJ9cRR9utnW4Wyi13sMie873MeznJ1aWrnS3VQGR0j4mLkKC1NUeljjA77zYyhVbIE0dR%2By7fmaHq7U%2BdegXWGpAZ%2B%2F4pR32luBFTAtWgUcCv56%2Fp5y30X87Yz1khTIycdgpUW9kY7WdsC9zxoXTvMvWuVV98YyMnSGH2SYE5pwALBIr9QKiwDGpW0oGVUznGeMyJZKFkQ4jBf5HnhUymjIhzCAL3KNFihbYx8TBYzzGaY7EnIyZwHzCWMfiDnbRIftkSjJr%2BFu0e9v%2B0EgOquRiiZjKpiVFp6j50T4WXoyNJ%2FEWC9fdqc1t%2F1%2B2F3aUpjzhPiXpqMz1%2FHSn4A&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=MsAYz2NFdovMG2mXf6TSpu5vlQQyEJAg%2B4KCwBqJTmrb3yGXKUtIgvjqf88eCAK32v3eN8vupjPC8LglYmke1ZnjK0%2FKxzkvSjTVA7mMQe2AQdKbkyC038zzRq%2FYHcjFDE%2Bz0qISwSHZY2NyLePmwU7SexEXnIz37jKC6NMEhus%3D", realm: "saml1", }); console.log(response);
POST /_security/saml/invalidate { "query" : "SAMLRequest=nZFda4MwFIb%2FiuS%2BmviRpqFaClKQdbvo2g12M2KMraCJ9cRR9utnW4Wyi13sMie873MeznJ1aWrnS3VQGR0j4mLkKC1NUeljjA77zYyhVbIE0dR%2By7fmaHq7U%2BdegXWGpAZ%2B%2F4pR32luBFTAtWgUcCv56%2Fp5y30X87Yz1khTIycdgpUW9kY7WdsC9zxoXTvMvWuVV98YyMnSGH2SYE5pwALBIr9QKiwDGpW0oGVUznGeMyJZKFkQ4jBf5HnhUymjIhzCAL3KNFihbYx8TBYzzGaY7EnIyZwHzCWMfiDnbRIftkSjJr%2BFu0e9v%2B0EgOquRiiZjKpiVFp6j50T4WXoyNJ%2FEWC9fdqc1t%2F1%2B2F3aUpjzhPiXpqMz1%2FHSn4A&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=MsAYz2NFdovMG2mXf6TSpu5vlQQyEJAg%2B4KCwBqJTmrb3yGXKUtIgvjqf88eCAK32v3eN8vupjPC8LglYmke1ZnjK0%2FKxzkvSjTVA7mMQe2AQdKbkyC038zzRq%2FYHcjFDE%2Bz0qISwSHZY2NyLePmwU7SexEXnIz37jKC6NMEhus%3D", "realm" : "saml1" }
然后,自定义 Web 应用程序还需要处理响应,该响应将包含一个
redirect
参数,其中包含 IdP 中的 URL,其中包含 SAML 注销响应。应用程序应将用户重定向到那里以完成注销。
对于 SP 发起的单点注销,IdP 可能会发送回注销响应,Elasticsearch 可以使用 SAML 完成注销 API 验证此响应。