JWT 认证

编辑

Elasticsearch 可以配置为信任来自外部服务的 JSON Web 令牌 (JWT) 作为身份验证的承载令牌。

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

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

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

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

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

声明

信息

ID 令牌

访问令牌

发行者

iss

iss

主体

sub

默认为sub,但如果sub不存在,则可以回退到另一个声明

受众

aud

默认为aud,但如果aud不存在,则可以回退到另一个声明

发行时间

iat

iat

过期时间

exp

exp

此外,如果存在这些声明,Elasticsearch 还会验证 ID 令牌的nbfauth_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_subjectsallowed_subject_patterns验证。

并非每个访问令牌都格式化为 JSON Web 令牌 (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,这表示在对用户进行身份验证时检查配置的领域的顺序。按升序查阅领域,其中具有最低 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 应使用RS256HS256签名算法来验证来自 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_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.typeshared_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),并且使用OpenID Connect ID令牌验证规则验证JWS HeaderSignature。一些验证可以通过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领域委派给另一个领域时,dngroupsmailmetadataname的声明不用于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

在 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 发出经过身份验证的调用。承载令牌和客户端授权令牌必须使用-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"}