JWT 认证

编辑

可以将 Elasticsearch 配置为信任来自外部服务颁发的 JSON Web Tokens (JWT),将其作为身份验证的持有者令牌。

当使用 JWT 域进行 Elasticsearch 身份验证时,会区分连接到 Elasticsearch 的客户端和代表其运行请求的用户。JWT 验证用户身份,而单独的凭据验证客户端身份。

JWT 域支持两种令牌类型,id_token(默认)和 access_token。它们分别设计用于以下两种场景:

  1. id_token - 应用程序使用身份验证流程(例如 OpenID Connect (OIDC))进行身份验证并识别用户,然后使用符合 OIDC ID 令牌规范的 JSON Web Token (JWT) 代表经过身份验证的用户访问 Elasticsearch。
  2. access_token - 应用程序使用其自己的身份(编码为 JWT)访问 Elasticsearch,例如,应用程序使用 OAuth2 客户端凭据流程向中央身份平台进行身份验证,然后使用生成的基于 JWT 的访问令牌连接到 Elasticsearch。

单个 JWT 域只能使用一种令牌类型。要处理两种令牌类型,必须至少配置两个 JWT 域。应根据用例仔细选择令牌类型,因为它会影响执行验证的方式。

JWT 域会根据其配置的令牌类型验证传入的 JWT。两种类型的 JSON Web Tokens (JWT) 都必须包含以下 5 个信息。虽然基于 OIDC 规范的 ID 令牌对声明应提供这些信息有严格的规则,但访问令牌允许配置某些声明。

声明

信息

ID 令牌

访问令牌

颁发者

iss

iss

主题

sub

默认为 sub,但如果 sub 不存在,则可以回退到其他声明

受众

aud

默认为 aud,但如果 aud 不存在,则可以回退到其他声明

颁发时间

iat

iat

过期时间

exp

exp

此外,如果 ID 令牌中存在 nbfauth_time 声明,Elasticsearch 也会对其进行验证。但这些声明对于访问令牌将被忽略。

总的来说,访问令牌类型具有更宽松的验证规则,适用于更通用的 JWT,包括自签名 JWT。

来自 OIDC 工作流程的 ID 令牌

编辑

Elasticsearch 中的 JWT 认证源自 OIDC 用户工作流程,其中 OIDC 提供程序 (OP) 可以颁发不同的令牌,包括 ID 令牌。来自 OIDC 提供程序的 ID 令牌是定义明确的 JSON Web Tokens (JWT),应始终与 id_token 令牌类型的 JWT 域兼容。ID 令牌的主题声明表示最终用户。这意味着 ID 令牌通常会有许多允许的主题。因此,id_token 令牌类型的 JWT 域强制要求 allowed_subjects(或 allowed_subject_patterns)验证。

由于 JWT 是在 Elasticsearch 外部获取的,因此可以定义自定义工作流程,而不是使用 OIDC 工作流程。但是,JWT 格式仍然必须是 JSON Web Signature (JWS)。JWS 标头和 JWS 签名使用 OIDC ID 令牌验证规则进行验证。

Elasticsearch 支持单独的 OpenID Connect 域。对于 Elasticsearch 可以充当 OIDC RP 的任何用例,它都是首选。OIDC 域是启用 Kibana 中 OIDC 认证的唯一受支持方式。

使用 JWT 域进行身份验证的用户可以选择使用 run_as 功能模拟另一个用户。另请参见 run_as 权限应用于 JWT 域用户

访问令牌

编辑

获取访问令牌的常用方法是使用 OAuth2 客户端凭据流程。此流程的典型用法是应用程序获取其自身的凭据。这是 access_token 令牌类型设计的用例。此应用程序也可能为其最终用户获取 ID 令牌。为了防止最终用户 ID 令牌被用于使用为应用程序配置的 JWT 域进行身份验证,当 JWT 域的令牌类型为 access_token 时,我们强制执行 allowed_subjectsallowed_subject_patterns 验证。

并非每个访问令牌都格式化为 JSON Web Token (JWT)。为了与 JWT 域兼容,它必须至少使用 JWT 格式并满足上表中的相关要求。

配置 Elasticsearch 以使用 JWT 域

编辑

要使用 JWT 身份验证,请在 elasticsearch.yml 文件中创建域,以在 Elasticsearch 身份验证链中配置它。

JWT 域有一些强制性设置,以及 JWT 域设置中描述的可选设置。

默认情况下,为 JWT 域启用客户端身份验证。禁用客户端身份验证是可能的,但强烈不建议这样做。

  1. 将 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
    指定域的 order3,这表示在验证用户身份时检查已配置域的顺序。域按升序顺序进行检查,其中首先检查具有最低顺序值的域。
    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 应使用 RS256HS256 签名算法来验证来自 JWT 颁发者的 JWT 签名。
    pkc_jwkset_path
    包含 JWT 域用于验证令牌签名的公钥材料的 JSON Web Key Set (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_typeaccess_token 时,必须指定(且不能为空)allowed_subjectsallowed_subject_patterns 设置中的至少一个。

    当同时指定 allowed_subjectsallowed_subject_patterns 设置时,如果传入 JWT 的 sub 声明与两个列表中的任何一个匹配,则会被接受。

    required_claims
    指定键/值对列表,用于针对 JWT 执行额外的验证。这些值可以是字符串或字符串数组。
    fallback_claims.sub
    如果 sub 声明不存在,则用于提取主题信息的 JWT 声明的名称。此设置仅当 token_typeaccess_token 时可用。回退应用于使用 sub 声明的任何地方。在上面的代码段中,这意味着如果 sub 不存在,则 claims.principal 也会回退到 client_id
    fallback_claims.aud
    如果 aud 声明不存在,则用于提取受众信息的 JWT 声明的名称。此设置仅当 token_typeaccess_token 时可用。回退应用于使用 aud 声明的任何地方。
  2. 定义设置后,使用 elasticsearch-keystore 工具在 Elasticsearch 密钥库中存储安全设置的值。

    1. client_authentication.type 存储 shared_secret

      bin/elasticsearch-keystore add xpack.security.authc.realms.jwt.jwt1.client_authentication.shared_secret
    2. allowed_signature_algorithms 存储 HMAC 密钥,该密钥在示例中使用 HMAC SHA-256 算法 HS256

      bin/elasticsearch-keystore add-file xpack.security.authc.realms.jwt.jwt1.hmac_jwkset <path> 

      JWKS 的路径,它是 JSON 编码的密钥集合的资源。将内容加载到 Elasticsearch 密钥库后,可以删除该文件。

      建议使用 JWKS。但是,您可以使用以下命令以字符串格式添加 HMAC 密钥。此格式与 HMAC UTF-8 密钥兼容,但仅支持单个没有属性的密钥。您只能同时使用一种 HMAC 格式(hmac_jwksethmac_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),并且 JWS 标头签名使用 OpenID Connect ID 令牌验证规则进行验证。可以通过JWT realm 设置自定义一些验证。

标头声明

编辑

标头声明指示令牌类型和用于签署令牌的算法。

alg
(必需,字符串)指示用于签署令牌的算法,例如 HS256。该算法必须在 realm 的允许列表中。
typ
(可选,字符串)指示令牌类型,必须为 JWT

有效负载声明

编辑

令牌包含多个声明,这些声明提供有关颁发令牌的用户以及令牌本身的信息。根据令牌类型,这些信息可以选择通过不同的声明进行标识。

JWT 有效负载声明
编辑

以下声明由 OIDC ID 令牌规则的子集验证。

Elasticsearch 不验证 nonce 声明,但自定义 JWT 颁发者可以添加随机的 nonce 声明,以在签名中引入熵。

您可以通过设置 allowed_clock_skew 来放宽对任何基于时间的声明的验证。此值设置验证 JWT 时相对于其身份验证时间(auth_time)、创建时间(iat)、不早于时间(nbf)和过期时间(exp)的最大允许时钟偏差。

iss
(必需,字符串)表示创建 ID 令牌的颁发者。该值必须与 allowed_issuer 设置中的值完全匹配(区分大小写)。
sub
(必需*,字符串)指示为之创建 ID 令牌的主题。如果 JWT realm 的类型为 id_token,则此声明是强制性的。默认情况下,id_token 类型的 JWT realm 接受所有主题。access_token 类型的 JWT realm 必须指定 allowed_subjects 设置,并且主题值必须与 allowed_subjects 设置中的任何 CSV 值完全匹配(区分大小写)。access_token 类型的 JWT realm 可以指定一个回退声明,该声明将在 sub 声明不存在时使用。
aud
(必需*,字符串)指示 ID 令牌的目标受众,表示为逗号分隔值 (CSV)。其中一个值必须与 allowed_audiences 设置中的任何 CSV 值完全匹配(区分大小写)。如果 JWT realm 的类型为 id_token,则此声明是强制性的。access_token 类型的 JWT realm 可以指定一个回退声明,该声明将在 aud 声明不存在时使用。
exp
(必需,整数)ID 令牌的过期时间,以自 epoch 以来的 UTC 秒数表示。
iat
(必需,整数)颁发 ID 令牌的时间,以自 epoch 以来的 UTC 秒数表示。
nbf
(可选,整数)指示必须不接受 JWT 的时间之前的时间,以自 epoch 以来的 UTC 秒数表示。此声明是可选的。如果它存在,则 id_token 类型的 JWT realm 将对其进行验证,而 access_token 类型的 JWT realm 将直接忽略它。
auth_time
(可选,整数)用户向 JWT 颁发者进行身份验证的时间,以自 epoch 以来的 UTC 秒数表示。此声明是可选的。如果它存在,则 id_token 类型的 JWT realm 将对其进行验证,而 access_token 类型的 JWT realm 将直接忽略它。
用于使用 JWT 声明的 Elasticsearch 设置
编辑

Elasticsearch 将 JWT 声明用于以下设置。

principal
(必需,字符串)包含用户的主体(用户名)。该值可以使用 realm 设置 claims.principal 进行配置。您可以使用 claim_patterns.principal 配置一个可选的正则表达式来提取子字符串。
groups
(可选,JSON 数组)包含用户的组成员资格。该值可以使用 realm 设置 claims.groups 进行配置。您可以使用 realm 设置 claim_patterns.groups 配置一个可选的正则表达式来提取子字符串值。
name
(可选,字符串)包含一个人类可读的标识符,用于标识令牌的主题。该值可以使用 realm 设置 claims.name 进行配置。您可以使用 realm 设置 claim_patterns.name 配置一个可选的正则表达式来提取子字符串值。
mail
(可选,字符串)包含要与用户关联的电子邮件地址。该值可以使用 realm 设置 claims.mail 进行配置。您可以使用 realm 设置 claim_patterns.mail 配置一个可选的正则表达式来提取子字符串值。
dn
(可选,字符串)包含用户的可分辨名称 (DN),该名称唯一标识用户或组。该值可以使用 realm 设置 claims.dn 进行配置。您可以使用 realm 设置 claim_patterns.dn 配置一个可选的正则表达式来提取子字符串值。

JWT realm 授权

编辑

JWT realm 支持通过创建或更新角色映射 API 进行授权,或者将授权委托给另一个 realm。您不能同时使用这些方法,因此请选择最适合您环境的方法。

您不能使用 role_mapping.yml 文件在 JWT realm 中映射角色。

使用角色映射 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 realm 中使用此 API,则以下声明可用于角色映射

principal
(必需,字符串)用作 Elasticsearch 用户名的 Principal 声明。
dn
(可选,字符串)用作 Elasticsearch 用户 DN 的可分辨名称 (DN)。
groups
(可选,字符串)用作 Elasticsearch 用户组列表的逗号分隔值 (CSV) 列表。
metadata
(可选,对象)有关用户的其他元数据,例如字符串、整数、布尔值和集合,这些元数据用作 Elasticsearch 用户的元数据。这些值是键值对,格式为 metadata.jwt_claim_<key> = <value>

将 JWT 授权委托给另一个 realm

编辑

如果您将授权委托从 JWT realm 委托给其他 realm,则只有 principal 声明可用于角色查找。将角色的分配和查找委托给来自 JWT realm 的另一个 realm 时,dngroupsmailmetadataname 的声明不用于 Elasticsearch 用户的值。只有 JWT principal 声明传递给委托的授权 realm。授权委托到的 realm(而不是 JWT realm)负责填充所有 Elasticsearch 用户的值。

以下示例说明如何在 elasticsearch.yml 文件中定义从 JWT realm 到多个其他 realm 的委托授权。名为 jwt2 的 JWT realm 将授权委托给多个 realm

xpack.security.authc.realms.jwt.jwt2.authorization_realms: file1,native1,ldap1,ad1

然后,您可以使用创建或更新角色映射 API来将角色映射到授权 realm。以下示例将 native1 realm 中的角色映射到 principalname1 JWT 主体。

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
}

如果 realm jwt2 成功使用 JWT 为主体 principalname1 验证客户端,并将授权委托给其中一个列出的 realm(例如 native1),则该 realm 可以查找 Elasticsearch 用户的值。通过此定义的角色映射,realm 还可以查找链接到 realm native1 的此角色映射规则。

run_as 权限应用于 JWT realm 用户

编辑

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 }
}

然后,您可以将该角色映射到特定 realm 中的用户。以下请求将 jwt_role1 角色映射到 jwt2 JWT realm 中名为 user2 的用户。这意味着 Elasticsearch 将使用 jwt2 realm 对名为 user2 的用户进行身份验证。由于 user2 具有一个角色(jwt_role1 角色),其中包括 run_as 权限,因此 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 realm 中映射到此用户的 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 重新加载错误和恢复会得到优雅处理。

所有其他 JWT 领域验证都会在签名失败触发 PKC JWKS 重新加载之前进行检查。如果多个 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

在 Elastic Cloud 中,领域顺序从 2 开始。01 在 Elastic Cloud 的领域链中保留。

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 进行身份验证调用。必须将 bearer 令牌和客户端授权令牌都指定为单独的标头,并使用 -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"}