JWT 认证
编辑JWT 认证
编辑Elasticsearch 可以配置为信任来自外部服务的 JSON Web 令牌 (JWT) 作为身份验证的承载令牌。
当使用 JWT 领域对 Elasticsearch 进行身份验证时,会区分连接到 Elasticsearch 的客户端和应代表其运行请求的用户。JWT 对用户进行身份验证,而单独的凭据对客户端进行身份验证。
JWT 领域支持两种令牌类型,id_token
(默认)和access_token
。它们分别设计用于以下两种场景
-
id_token
- 应用程序使用身份验证流程(例如 OpenID Connect (OIDC))对用户进行身份验证和识别,然后使用符合 OIDC ID 令牌规范的 JSON Web 令牌 (JWT) 代表经过身份验证的用户访问 Elasticsearch。 -
access_token
- 应用程序使用其自身身份访问 Elasticsearch,该身份以 JWT 编码,例如:应用程序使用 OAuth2 客户端凭据流向中央身份平台进行自身身份验证,然后使用生成的基于 JWT 的访问令牌连接到 Elasticsearch。
单个 JWT 领域只能与一种令牌类型一起使用。要处理两种令牌类型,必须配置至少两个 JWT 领域。应根据用例仔细选择令牌类型,因为它会影响如何执行验证。
JWT 领域根据其配置的令牌类型验证传入的 JWT。两种类型的 JSON Web 令牌 (JWT) 必须包含以下 5 个信息。虽然基于 OIDC 规范的 ID 令牌对哪些声明应提供这些信息有严格的规则,但访问令牌允许配置某些声明。
声明 |
||
信息 |
ID 令牌 |
访问令牌 |
发行者 |
|
|
主体 |
|
默认为 |
受众 |
|
默认为 |
发行时间 |
|
|
过期时间 |
|
|
此外,如果存在这些声明,Elasticsearch 还会验证 ID 令牌的nbf
和auth_time
声明。但这些声明对于访问令牌将被忽略。
总的来说,访问令牌类型具有更宽松的验证规则,适用于更通用的 JWT,包括自签名 JWT。
来自 OIDC 工作流程的 ID 令牌
编辑Elasticsearch 中的 JWT 身份验证源自 OIDC 用户工作流程,其中不同的令牌可以由 OIDC 提供商 (OP) 发行,包括 ID 令牌。来自 OIDC 提供商的 ID 令牌是定义良好的 JSON Web 令牌 (JWT),应始终与id_token
令牌类型的 JWT 领域兼容。ID 令牌的主体声明代表最终用户。这意味着 ID 令牌通常将具有许多允许的主体。因此,id_token
令牌类型的 JWT 领域_不_强制执行allowed_subjects
(或allowed_subject_patterns
)验证。
由于 JWT 是在 Elasticsearch 外部获得的,因此您可以定义自定义工作流程而不是使用 OIDC 工作流程。但是,JWT 格式仍然必须是 JSON Web 签名 (JWS)。JWS 标头和 JWS 签名使用 OIDC ID 令牌验证规则进行验证。
Elasticsearch 支持单独的OpenID Connect 领域。对于 Elasticsearch 可以充当 OIDC RP 的任何用例,都建议使用它。OIDC 领域是唯一支持在 Kibana 中启用 OIDC 身份验证的方法。
使用 JWT 领域进行身份验证的用户可以选择使用run_as
功能模拟另一个用户。另请参见将run_as
权限应用于 JWT 领域用户。
访问令牌
编辑获取访问令牌的一种常用方法是使用 OAuth2 客户端凭据流。此流的典型用法是应用程序获取其自身的凭据。这是access_token
令牌类型设计的用例。此应用程序也可能为其最终用户获取 ID 令牌。为了防止使用为应用程序配置的 JWT 领域对最终用户 ID 令牌进行身份验证,当 JWT 领域的令牌类型为access_token
时,我们强制执行allowed_subjects
或allowed_subject_patterns
验证。
并非每个访问令牌都格式化为 JSON Web 令牌 (JWT)。为了使其与 JWT 领域兼容,它必须至少使用 JWT 格式并满足上表中的相关要求。
配置 Elasticsearch 以使用 JWT 领域
编辑要使用 JWT 身份验证,请在elasticsearch.yml
文件中创建领域以在 Elasticsearch 身份验证链中对其进行配置。
JWT 领域有一些必填设置,以及在JWT 领域设置中描述的可选设置。
JWT 领域的客户端身份验证默认启用。可以禁用客户端身份验证,但不建议这样做。
-
将您的 JWT 领域添加到
elasticsearch.yml
文件。以下示例包含最常见的设置,这些设置并非适用于每个用例xpack.security.authc.realms.jwt.jwt1: order: 3 token_type: id_token client_authentication.type: shared_secret allowed_issuer: "https://issuer.example.com/jwt/" allowed_audiences: [ "8fb85eba-979c-496c-8ae2-a57fde3f12d0" ] allowed_signature_algorithms: [RS256,HS256] pkc_jwkset_path: jwt/jwkset.json claims.principal: sub
-
order
- 指定领域
order
为3
,这表示在对用户进行身份验证时检查配置的领域的顺序。按升序查阅领域,其中具有最低 order 值的领域首先被查阅。 -
token_type
- 指示领域将传入的 JWT 视为 ID 令牌(
id_token
)。 -
client_authentication.type
- 将客户端身份验证类型指定为
shared_secret
,这意味着使用必须与预配置的秘密值匹配的 HTTP 请求标头对客户端进行身份验证。客户端必须在ES-Client-Authentication
标头中使用SharedSecret
方案提供此共享密钥以及每个请求。标头值必须与领域的client_authentication.shared_secret
大小写匹配。 -
allowed_issuer
- 设置 JWT 发行者的可验证标识符。此值通常是 URL、UUID 或其他一些区分大小写的字符串值。
-
allowed_audiences
- 指定领域将允许的 JWT 受众列表。这些值通常是 URL、UUID 或其他区分大小写的字符串值。
-
allowed_signature_algorithms
- 指示 Elasticsearch 应使用
RS256
或HS256
签名算法来验证来自 JWT 发行者的 JWT 签名。 -
pkc_jwkset_path
- 包含 JWT 领域用于验证令牌签名的公钥材料的 JSON Web 密钥集 (JWKS) 的文件名或 URL。如果值不以
https
开头,则认为它是一个文件名。文件名相对于 Elasticsearch 配置目录解析。如果提供 URL,则它必须以https://
开头(不支持http://
)。Elasticsearch 自动缓存 JWK 集,并在签名验证失败时尝试刷新 JWK 集,因为这可能表示 JWT 提供商已轮换签名密钥。 -
claims.principal
- 包含用户主体(用户名)的 JWT 声明的名称。
以下是配置用于处理访问令牌的 JWT 领域的示例代码段
xpack.security.authc.realms.jwt.jwt2: order: 4 token_type: access_token client_authentication.type: shared_secret allowed_issuer: "https://issuer.example.com/jwt/" allowed_subjects: [ "[email protected]" ] allowed_subject_patterns: [ "wild*@developer?.example.com", "/[a-z]+<1-10>\\@dev\\.example\\.com/"] allowed_audiences: [ "elasticsearch" ] required_claims: token_use: access version: ["1.0", "2.0"] allowed_signature_algorithms: [RS256,HS256] pkc_jwkset_path: "https://idp-42.example.com/.well-known/configuration" fallback_claims.sub: client_id fallback_claims.aud: scope claims.principal: sub
-
token_type
- 指示领域将传入的 JWT 视为访问令牌(
access_token
)。 -
allowed_subjects
- 指定领域将允许的 JWT 主体列表。这些值通常是 URL、UUID 或其他区分大小写的字符串值。
-
allowed_subject_patterns
- 类似于
allowed_subjects
,但它接受允许的 JWT 主体的Lucene 正则表达式和通配符列表。通配符使用*
和?
特殊字符(分别由\
转义)分别表示“任何字符串”和“任何单个字符”,例如“a?\**”匹配“a1*”和“ab*whatever”,但不匹配“a”、“abc”或“abc*”(在 Java 字符串中\
本身必须由另一个\
转义)。Lucene 正则表达式必须用/
括起来,例如“/https?://[^/]+/?/”匹配任何没有路径组件的 http 或 https URL(匹配“https://elastic.ac.cn/”但不匹配“https://elastic.ac.cn/guide”)。
当
token_type
为access_token
时,必须指定至少一个allowed_subjects
或allowed_subject_patterns
设置(并且不为空)。如果同时指定了
allowed_subjects
和allowed_subject_patterns
设置,则如果传入 JWT 的sub
声明与这两个列表中的任何一个匹配,则接受该声明。-
required_claims
- 指定键值对列表,用于对 JWT 执行其他验证。这些值是字符串或字符串数组。
-
fallback_claims.sub
- 如果
sub
声明不存在,则提取主体信息的 JWT 声明的名称。此设置仅在token_type
为access_token
时可用。回退应用于使用sub
声明的任何地方。在上例中,这意味着如果sub
不存在,claims.principal
也将回退到client_id
。 -
fallback_claims.aud
- 如果
aud
声明不存在,则提取受众信息的 JWT 声明的名称。此设置仅在token_type
为access_token
时可用。回退应用于使用aud
声明的任何地方。
-
-
定义设置后,请使用
elasticsearch-keystore
工具将安全设置的值存储在 Elasticsearch 密钥库中。-
存储
client_authentication.type
的shared_secret
值bin/elasticsearch-keystore add xpack.security.authc.realms.jwt.jwt1.client_authentication.shared_secret
-
存储
allowed_signature_algorithms
的HMAC密钥,示例中使用HMAC SHA-256算法HS256
推荐使用JWKS。但是,您可以使用以下命令添加字符串格式的HMAC密钥。此格式与HMAC UTF-8密钥兼容,但仅支持单个密钥且没有属性。您只能同时使用一种HMAC格式(
hmac_jwkset
或hmac_key
)。bin/elasticsearch-keystore add xpack.security.authc.realms.jwt.jwt1.hmac_key
-
JWT编码和验证
编辑JWT可以解析成三个部分
- 头部
- 提供有关如何验证令牌的信息。
- 声明
- 包含有关调用用户或应用程序的数据。
- 签名
- 用于验证令牌的数据。
Header: {"typ":"JWT","alg":"HS256"} Claims: {"aud":"aud8","sub":"security_test_user","iss":"iss8","exp":4070908800,"iat":946684800} Signature: UnnFmsoFKfNmKMsVoDQmKI_3-j95PCaKdgqqau3jPMY
此示例说明了JWT的部分解码。有效期为2000年至2099年(含),由发行时间(iat
)和过期时间(exp
)定义。JWT的有效期通常短于100年,例如1-2小时或1-7天,而不是整个人生。
此示例中的签名是确定性的,因为头部、声明和HMAC密钥是固定的。JWT通常包含nonce
声明以使签名非确定性。支持的JWT编码是JSON Web签名 (JWS),并且使用OpenID Connect ID令牌验证规则验证JWS Header
和Signature
。一些验证可以通过JWT领域设置自定义。
头部声明
编辑头部声明指示令牌类型和用于签名令牌的算法。
-
alg
- (必需,字符串)指示用于签名令牌的算法,例如
HS256
。该算法必须在领域的允许列表中。 -
typ
- (可选,字符串)指示令牌类型,必须为
JWT
。
有效负载声明
编辑令牌包含多个声明,这些声明提供有关发行令牌的用户以及令牌本身的信息。根据令牌类型,这些信息可以选择由不同的声明标识。
JWT有效负载声明
编辑以下声明由OIDC ID令牌规则的子集验证。
Elasticsearch不验证nonce
声明,但自定义JWT发行者可以添加随机nonce
声明以将熵引入签名。
您可以通过设置allowed_clock_skew
来放宽对任何基于时间的声明的验证。此值设置在根据其身份验证时间(auth_time
)、创建时间(iat
)、不得早于时间(nbf
)和过期时间(exp
)验证JWT之前允许的最大时钟偏差。
-
iss
- (必需,字符串)表示创建ID令牌的发行者。该值必须与
allowed_issuer
设置中的值完全一致,区分大小写。 -
sub
- (必需*,字符串)指示为其创建ID令牌的主体。如果JWT领域是
id_token
类型,则此声明是必需的。id_token
类型的JWT领域默认情况下接受所有主体。access_token
类型的JWT领域必须指定allowed_subjects
设置,并且主体值必须与allowed_subjects
设置中任何CSV值的完全一致,区分大小写。access_token
类型的JWT领域可以指定一个回退声明,该声明将用于sub
声明不存在的地方。 -
aud
- (必需*,字符串)指示ID令牌的目标受众,以逗号分隔值 (CSV) 表示。其中一个值必须与
allowed_audiences
设置中任何CSV值的完全一致,区分大小写。如果JWT领域是id_token
类型,则此声明是必需的。access_token
类型的JWT领域可以指定一个回退声明,该声明将用于aud
声明不存在的地方。 -
exp
- (必需,整数)ID令牌的过期时间,以自纪元以来的UTC秒表示。
-
iat
- (必需,整数)ID令牌的发行时间,以自纪元以来的UTC秒表示。
-
nbf
- (可选,整数)指示在该时间之前不得接受JWT的时间,以自纪元以来的UTC秒表示。此声明是可选的。如果存在,
id_token
类型的JWT领域将验证它,而access_token
类型的JWT领域将忽略它。 -
auth_time
- (可选,整数)用户向JWT发行者进行身份验证的时间,以自纪元以来的UTC秒表示。此声明是可选的。如果存在,
id_token
类型的JWT领域将验证它,而access_token
类型的JWT领域将忽略它。
Elasticsearch使用JWT声明的设置
编辑Elasticsearch使用JWT声明进行以下设置。
-
principal
- (必需,字符串)包含用户的用户名。可以使用领域设置
claims.principal
配置该值。您可以使用claim_patterns.principal
配置可选的正则表达式以提取子字符串。 -
groups
- (可选,JSON数组)包含用户的组成员资格。可以使用领域设置
claims.groups
配置该值。您可以使用领域设置claim_patterns.groups
配置可选的正则表达式以提取子字符串值。 -
name
- (可选,字符串)包含易于理解的标识符,用于标识令牌的主体。可以使用领域设置
claims.name
配置该值。您可以使用领域设置claim_patterns.name
配置可选的正则表达式以提取子字符串值。 -
mail
- (可选,字符串)包含与用户关联的电子邮件地址。可以使用领域设置
claims.mail
配置该值。您可以使用领域设置claim_patterns.mail
配置可选的正则表达式以提取子字符串值。 -
dn
- (可选,字符串)包含用户的可分辨名称 (DN),它唯一标识用户或组。可以使用领域设置
claims.dn
配置该值。您可以使用领域设置claim_patterns.dn
配置可选的正则表达式以提取子字符串值。
JWT领域授权
编辑JWT领域支持使用创建或更新角色映射API进行授权,或将授权委派给另一个领域。您不能同时使用这些方法,因此请选择最适合您的环境的方法。
您不能使用role_mapping.yml
文件在JWT领域中映射角色。
使用角色映射API进行授权
编辑您可以使用创建或更新角色映射API来定义角色映射,这些映射确定应根据用户的用户名、组或其他元数据为每个用户分配哪些角色。
resp = client.security.put_role_mapping( name="jwt1_users", refresh=True, roles=[ "user" ], rules={ "all": [ { "field": { "realm.name": "jwt1" } }, { "field": { "username": "principalname1" } }, { "field": { "dn": "CN=Principal Name 1,DC=example.com" } }, { "field": { "groups": "group1" } }, { "field": { "metadata.jwt_claim_other": "other1" } } ] }, enabled=True, ) print(resp)
const response = await client.security.putRoleMapping({ name: "jwt1_users", refresh: "true", roles: ["user"], rules: { all: [ { field: { "realm.name": "jwt1", }, }, { field: { username: "principalname1", }, }, { field: { dn: "CN=Principal Name 1,DC=example.com", }, }, { field: { groups: "group1", }, }, { field: { "metadata.jwt_claim_other": "other1", }, }, ], }, enabled: true, }); console.log(response);
PUT /_security/role_mapping/jwt1_users?refresh=true { "roles" : [ "user" ], "rules" : { "all" : [ { "field": { "realm.name": "jwt1" } }, { "field": { "username": "principalname1" } }, { "field": { "dn": "CN=Principal Name 1,DC=example.com" } }, { "field": { "groups": "group1" } }, { "field": { "metadata.jwt_claim_other": "other1" } } ] }, "enabled": true }
如果您在JWT领域中使用此API,则可以使用以下声明进行角色映射
-
principal
- (必需,字符串)用作Elasticsearch用户用户名的主体声明。
-
dn
- (可选,字符串)用作Elasticsearch用户DN的可分辨名称 (DN)。
-
groups
- (可选,字符串)用作Elasticsearch用户的组列表的逗号分隔值 (CSV) 列表。
-
metadata
- (可选,对象)有关用户的附加元数据,例如字符串、整数、布尔值和用作Elasticsearch用户元数据的集合。这些值是格式为
metadata.jwt_claim_<key>
=<value>
的键值对。
将JWT授权委派给另一个领域
编辑如果您将授权委派给JWT领域的其它领域,则只有principal
声明可用于角色查找。当将角色的分配和查找从JWT领域委派给另一个领域时,dn
、groups
、mail
、metadata
和name
的声明不用于Elasticsearch用户的 value。只有JWT principal
声明被传递到委派的授权领域。负责授权的领域(而不是JWT领域)负责填充所有Elasticsearch用户的 value。
以下示例显示了如何在elasticsearch.yml
文件中定义从JWT领域到多个其他领域的委派授权。名为jwt2
的JWT领域将授权委派给多个领域
xpack.security.authc.realms.jwt.jwt2.authorization_realms: file1,native1,ldap1,ad1
然后,您可以使用创建或更新角色映射API将角色映射到授权领域。以下示例为principalname1
JWT主体映射native1
领域的中的角色。
resp = client.security.put_role_mapping( name="native1_users", refresh=True, roles=[ "user" ], rules={ "all": [ { "field": { "realm.name": "native1" } }, { "field": { "username": "principalname1" } } ] }, enabled=True, ) print(resp)
const response = await client.security.putRoleMapping({ name: "native1_users", refresh: "true", roles: ["user"], rules: { all: [ { field: { "realm.name": "native1", }, }, { field: { username: "principalname1", }, }, ], }, enabled: true, }); console.log(response);
PUT /_security/role_mapping/native1_users?refresh=true { "roles" : [ "user" ], "rules" : { "all" : [ { "field": { "realm.name": "native1" } }, { "field": { "username": "principalname1" } } ] }, "enabled": true }
如果领域jwt2
成功使用JWT对principalname1
主体进行身份验证,并将授权委派给列出的领域之一(例如native1
),则该领域可以查找Elasticsearch用户的 value。通过此定义的角色映射,该领域还可以查找此与领域native1
关联的角色映射规则。
将run_as
权限应用于JWT领域用户
编辑Elasticsearch可以通过角色映射或委派授权检索JWT用户的角色。无论您选择哪个选项,您都可以将run_as
权限应用于角色,以便用户可以提交经过身份验证的请求以“充当”另一个用户。要以另一个用户身份提交请求,请在请求中包含es-security-runas-user
标头。请求就像从该用户发出的那样运行,并且Elasticsearch使用其角色。
例如,假设有一个用户名为user123_runas
的用户。以下请求创建一个名为jwt_role1
的用户角色,该角色指定具有user123_runas
用户名的run_as
用户。任何具有jwt_role1
角色的用户都可以以指定的run_as
用户身份发出请求。
resp = client.security.put_role( name="jwt_role1", refresh=True, cluster=[ "manage" ], indices=[ { "names": [ "*" ], "privileges": [ "read" ] } ], run_as=[ "user123_runas" ], metadata={ "version": 1 }, ) print(resp)
const response = await client.security.putRole({ name: "jwt_role1", refresh: "true", cluster: ["manage"], indices: [ { names: ["*"], privileges: ["read"], }, ], run_as: ["user123_runas"], metadata: { version: 1, }, }); console.log(response);
POST /_security/role/jwt_role1?refresh=true { "cluster": ["manage"], "indices": [ { "names": [ "*" ], "privileges": ["read"] } ], "run_as": [ "user123_runas" ], "metadata" : { "version" : 1 } }
然后,您可以将该角色映射到特定领域中的用户。以下请求将jwt_role1
角色映射到jwt2
JWT 领域中用户名为user2
的用户。这意味着 Elasticsearch 将使用jwt2
领域来验证名为user2
的用户。因为user2
拥有一个包含run_as
权限的角色(jwt_role1
角色),Elasticsearch 将检索user123_runas
用户的角色映射,并使用该用户的角色来提交请求。
resp = client.security.put_role_mapping( name="jwt_user1", refresh=True, roles=[ "jwt_role1" ], rules={ "all": [ { "field": { "realm.name": "jwt2" } }, { "field": { "username": "user2" } } ] }, enabled=True, metadata={ "version": 1 }, ) print(resp)
const response = await client.security.putRoleMapping({ name: "jwt_user1", refresh: "true", roles: ["jwt_role1"], rules: { all: [ { field: { "realm.name": "jwt2", }, }, { field: { username: "user2", }, }, ], }, enabled: true, metadata: { version: 1, }, }); console.log(response);
POST /_security/role_mapping/jwt_user1?refresh=true { "roles": [ "jwt_role1"], "rules" : { "all" : [ { "field": { "realm.name": "jwt2" } }, { "field": { "username": "user2" } } ] }, "enabled": true, "metadata" : { "version" : 1 } }
映射角色后,您可以使用 JWT 对 Elasticsearch 发出经过身份验证的调用,并包含ES-Client-Authentication
标头。
curl -s -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiZXMwMSIsImVzMDIiLCJlczAzIl0sInN1YiI6InVzZXIyIiwiaXNzIjoibXktaXNzdWVyIiwiZXhwIjo0MDcwOTA4ODAwLCJpYXQiOjk0NjY4NDgwMCwiZW1haWwiOiJ1c2VyMkBzb21ldGhpbmcuZXhhbXBsZS5jb20ifQ.UgO_9w--EoRyUKcWM5xh9SimTfMzl1aVu6ZBsRWhxQA" -H "ES-Client-Authentication: sharedsecret test-secret" https://127.0.0.1:9200/_security/_authenticate
响应中包含提交请求的用户(user2
),包括您在 JWT 领域中映射到此用户的jwt_role1
角色。
{"username":"user2","roles":["jwt_role1"],"full_name":null,"email":"[email protected]", "metadata":{"jwt_claim_email":"[email protected]","jwt_claim_aud":["es01","es02","es03"], "jwt_claim_sub":"user2","jwt_claim_iss":"my-issuer"},"enabled":true,"authentication_realm": {"name":"jwt2","type":"jwt"},"lookup_realm":{"name":"jwt2","type":"jwt"},"authentication_type":"realm"} %
如果要将请求指定为run_as
用户,请包含es-security-runas-user
标头以及要作为其提交请求的用户名。以下请求使用user123_runas
用户。
curl -s -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiZXMwMSIsImVzMDIiLCJlczAzIl0sInN1YiI6InVzZXIyIiwiaXNzIjoibXktaXNzdWVyIiwiZXhwIjo0MDcwOTA4ODAwLCJpYXQiOjk0NjY4NDgwMCwiZW1haWwiOiJ1c2VyMkBzb21ldGhpbmcuZXhhbXBsZS5jb20ifQ.UgO_9w--EoRyUKcWM5xh9SimTfMzl1aVu6ZBsRWhxQA" -H "ES-Client-Authentication: sharedsecret test-secret" -H "es-security-runas-user: user123_runas" https://127.0.0.1:9200/_security/_authenticate
在响应中,您将看到user123_runas
用户提交了请求,并且 Elasticsearch 使用了jwt_role1
角色。
{"username":"user123_runas","roles":["jwt_role1"],"full_name":null,"email":null,"metadata":{}, "enabled":true,"authentication_realm":{"name":"jwt2","type":"jwt"},"lookup_realm":{"name":"native", "type":"native"},"authentication_type":"realm"}%
PKC JWKS 重新加载
编辑JWT 身份验证支持使用 PKC(公钥密码术)或 HMAC 算法进行签名验证。
PKC JSON Web 令牌密钥集 (JWKS) 可以包含公钥 RSA 和 EC 密钥。HMAC JWKS 或 HMAC UTF-8 JWK 包含密钥。JWT 发行者通常更频繁地轮换 PKC JWKS(例如每天一次),因为 RSA 和 EC 公钥的设计比 HMAC 等密钥更容易分发。
JWT 领域在启动时加载 PKC JWKS 和 HMAC JWKS 或 HMAC UTF-8 JWK。JWT 领域还可以在运行时重新加载 PKC JWKS 内容;签名验证失败将触发重新加载。
目前不支持 HMAC JWKS 或 HMAC UTF-8 JWK 重新加载。
加载失败、解析错误和配置错误会阻止节点启动(和重新启动)。但是,运行时 PKC 重新加载错误和恢复将被优雅地处理。
在签名失败能够触发 PKC JWKS 重新加载之前,将检查所有其他 JWT 领域验证。如果多个 JWT 身份验证签名失败同时发生在一个 Elasticsearch 节点上,则会合并重新加载以减少发送到外部的重新加载次数。
如果 JWT 签名失败触发,则无法合并单独的重新加载请求。
- 不同 Elasticsearch 节点中的 PKC JWKS 重新加载
- 同一 Elasticsearch 节点在不同时间进行的 PKC JWKS 重新加载
强烈建议启用客户端身份验证(client_authentication.type
)。只有受信任的客户端应用程序和特定于领域的 JWT 用户才能触发 PKC 重新加载尝试。此外,建议配置以下JWT 安全设置
-
allowed_audiences
-
allowed_clock_skew
-
allowed_issuer
-
allowed_signature_algorithms
使用 HMAC UTF-8 密钥授权到 JWT 领域
编辑以下设置适用于 JWT 发行者、Elasticsearch 和 Elasticsearch 的客户端。示例 HMAC 密钥采用与 HMAC 兼容的 OIDC 格式。密钥字节是 Unicode 字符的 UTF-8 编码。
HMAC UTF-8 密钥需要比 HMAC 随机字节密钥更长才能达到相同的密钥强度。
JWT 发行者
编辑以下值适用于定制 JWT 发行者。
Issuer: iss8 Audiences: aud8 Algorithms: HS256 HMAC UTF-8: hmac-oidc-key-string-for-hs256-algorithm
JWT 领域设置
编辑要定义 JWT 领域,请将以下领域设置添加到elasticsearch.yml
。
xpack.security.authc.realms.jwt.jwt8.order: 8 xpack.security.authc.realms.jwt.jwt8.allowed_issuer: iss8 xpack.security.authc.realms.jwt.jwt8.allowed_audiences: [aud8] xpack.security.authc.realms.jwt.jwt8.allowed_signature_algorithms: [HS256] xpack.security.authc.realms.jwt.jwt8.claims.principal: sub xpack.security.authc.realms.jwt.jwt8.client_authentication.type: shared_secret
JWT 领域安全设置
编辑定义领域设置后,请使用elasticsearch-keystore
工具将以下安全设置添加到 Elasticsearch 密钥库。在 Elastic Cloud 中,您可以在部署中的安全下定义 Elasticsearch 密钥库的设置。
xpack.security.authc.realms.jwt.jwt8.hmac_key: hmac-oidc-key-string-for-hs256-algorithm xpack.security.authc.realms.jwt.jwt8.client_authentication.shared_secret: client-shared-secret-string
JWT 领域角色映射规则
编辑以下请求为jwt8
领域中的用户principalname1
创建 Elasticsearch 的角色映射。
resp = client.security.put_role_mapping( name="jwt8_users", refresh=True, roles=[ "user" ], rules={ "all": [ { "field": { "realm.name": "jwt8" } }, { "field": { "username": "principalname1" } } ] }, enabled=True, ) print(resp)
const response = await client.security.putRoleMapping({ name: "jwt8_users", refresh: "true", roles: ["user"], rules: { all: [ { field: { "realm.name": "jwt8", }, }, { field: { username: "principalname1", }, }, ], }, enabled: true, }); console.log(response);
PUT /_security/role_mapping/jwt8_users?refresh=true { "roles" : [ "user" ], "rules" : { "all" : [ { "field": { "realm.name": "jwt8" } }, { "field": { "username": "principalname1" } } ] }, "enabled": true }
请求标头
编辑以下标头设置适用于 Elasticsearch 客户端。
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3M4IiwiYXVkIjoiYXVkOCIsInN1YiI6InNlY3VyaXR5X3Rlc3RfdXNlciIsImV4cCI6NDA3MDkwODgwMCwiaWF0Ijo5NDY2ODQ4MDB9.UnnFmsoFKfNmKMsVoDQmKI_3-j95PCaKdgqqau3jPMY ES-Client-Authentication: SharedSecret client-shared-secret-string
您可以在curl
请求中使用此标头对 Elasticsearch 发出经过身份验证的调用。承载令牌和客户端授权令牌必须使用-H
选项分别指定为单独的标头。
curl -s -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3M4IiwiYXVkIjoiYXVkOCIsInN1YiI6InNlY3VyaXR5X3Rlc3RfdXNlciIsImV4cCI6NDA3MDkwODgwMCwiaWF0Ijo5NDY2ODQ4MDB9.UnnFmsoFKfNmKMsVoDQmKI_3-j95PCaKdgqqau3jPMY" -H "ES-Client-Authentication: SharedSecret client-shared-secret-string" https://127.0.0.1:9200/_security/_authenticate
如果您在 JWT 领域中使用了角色映射,则响应将包含用户的username
、其roles
、有关用户的元数据以及有关 JWT 领域本身的详细信息。
{"username":"user2","roles":["jwt_role1"],"full_name":null,"email":"[email protected]", "metadata":{"jwt_claim_email":"[email protected]","jwt_claim_aud":["es01","es02","es03"], "jwt_claim_sub":"user2","jwt_claim_iss":"my-issuer"},"enabled":true,"authentication_realm": {"name":"jwt2","type":"jwt"},"lookup_realm":{"name":"jwt2","type":"jwt"},"authentication_type":"realm"}