JSON Web Tokens (JWT) 认证

Slurm 提供了一个符合 RFC7519JSON Web Tokens (JWT) 实现。 这种认证可以作为 AuthAltType 使用,通常与 auth/munge 一起作为 AuthType。唯一支持的通信 方向是客户端连接到 slurmctldslurmdbd。 这意味着某些场景(特别是使用 srun 的交互式作业)目前不支持启用 auth/jwt 的客户端(或 在其环境中有 SLURM_JWT 的客户端)。

先决条件

JWT 需要 libjwt。 在编译 Slurm 时,必须提供库和开发头文件。

对 JWT 创建的完全信任

创建 JWT 的权力是集群中 root 的权力。这是一个每个站点的决定,关于信任什么/谁/何时/如何。如果某个认证 系统不能完全信任整个集群的 root 权限,则需要使用认证代理来分配信任,并在请求到达 Slurm (特别是 slurmrestd)之前实施站点的特定策略。虽然可能效率不高,但如果缺乏信任但希望允许创建较低权限的认证令牌,则没有技术 原因不能使用多层认证代理。每个站点需要在实施任何系统之前权衡信任哪些 JWT 的风险和收益。一旦作业被排队,代理认证系统将不再参与,作业将以该用户的权限和 访问权限根据 Linux/POSIX 的 ACL 和信任运行。

信任模型

有几种方法可以处理 JWT 认证和访问控制。 Slurm JWT 插件实现故意简单,无法支持大多数站点所需的信任模型。已经存在大量的 认证系统,期望任何希望比默认提供更复杂的站点将使用其中一个系统。

  • 外部 JWT 生成

    我们提供了一个生成新 JWT 的示例 Python 脚本,但它们是标准的,大多数语言都有现成的工具。这通常是 站点最简单的途径,但确实需要每个站点直接为其用户实现工具。

  • 认证代理

    这是最灵活的选项,因为任何认证系统都可以放在 slurmrestd 前面。它需要创建一个 slurmuser/root 令牌,然后可以用来代理任何用户。对于此目的,已经存在 Nginx 和 Apache 的解决方案,可能还有其他每个非平凡代理。我们建议选择首选代理并找到现有的设置指南以通过该代理进行认证。代理需要定义 X-SLURM-USER-TOKENX-SLURM-USER-NAME 头。

    没有要求认证代理为客户端实现 JWT。这是认证代理的主要好处;它们可以使用任何认证方法,因为它们是告诉 Slurm 请求来自哪个用户的受信任点。这些认证令牌仅由 代理使用,并不会传递给作业。这通常不是一个 问题,因为一旦作业进入 Slurm,它以具有该用户所有固有信任的 Posix 用户身份运行,然后使用 auth/mungeauth/slurm 进行后续操作。

  • JWKS

    这类似于认证代理,因为使用另一个系统来创建 令牌,但它通过使用签名的公钥跳过在 Slurm 前面的认证系统。这通常是使用 云认证系统的站点的首选解决方案,例如:

独立使用的设置

  1. 配置并构建支持 JWT 的 Slurm
  2. 将相同的 JWT 密钥添加到控制器和 slurmdbd(如果使用)。对于 控制器,建议将 JWT 密钥放在 StateSaveLocation 中。 例如,使用 /var/spool/slurm/statesave/:
    dd if=/dev/random of=/var/spool/slurm/statesave/jwt_hs256.key bs=32 count=1
    chown slurm:slurm /var/spool/slurm/statesave/jwt_hs256.key
    chmod 0600 /var/spool/slurm/statesave/jwt_hs256.key
    chown slurm:slurm /var/spool/slurm/statesave
    chmod 0755 /var/spool/slurm/statesave
    
    密钥不必放在 StateSaveLocation 中,但如果您有多个控制器,这是一个方便的位置,因为它在它们之间共享。 密钥不应放置在非管理员用户可能访问的目录中。 密钥文件应由 SlurmUserroot 拥有,推荐权限为 0400。该文件不得被“其他”用户访问。
  3. 在 slurm.conf 和 slurmdbd.conf 中,将 JWT 添加为替代 认证类型:
    AuthAltTypes=auth/jwt
    AuthAltParameters=jwt_key=/var/spool/slurm/statesave/jwt_hs256.key
    
  4. 重启 slurmctld
  5. 根据需要为用户创建令牌:
    scontrol token username=$USER
    
    可以使用可选的 lifespan=$LIFESPAN 选项将令牌的 生命周期从默认的 1800 秒更改。root 账户或 SlurmUser 账户可以用于为任何用户生成令牌。或者,用户可以通过简单地调用
    scontrol token
    
    来为自己生成令牌。 请注意,管理员可以通过在 slurm.conf 中设置以下参数来防止用户生成令牌:
    AuthAltParameters=disable_token_creation
    
    此功能提供给站点以控制何时以及如何向用户提供令牌,并控制令牌的生命周期。
  6. 在调用任何 Slurm 命令之前导出 SLURM_JWT 环境变量。
  7. 在启动 slurmrestd 守护进程之前导出 SLURM_JWT=daemon 环境变量,以激活 AuthAltTypes=auth/jwt 作为主要 认证机制。

与 JWKS 和 RS256 令牌的外部认证集成

从 21.08 版本开始,Slurm 可以支持 RS256 令牌,例如由 Amazon CognitoAzure ADKeycloak 生成的令牌。

要启用 Slurm 的 RS256 令牌支持,必须下载并配置适当的 JWKS 文件,如下所示:

AuthAltTypes=auth/jwt
AuthAltParameters=jwks=/var/spool/slurm/statesave/jwks.json

jwks 文件应由 SlurmUserroot 拥有,必须 可被 SlurmUser 读取,推荐权限为 0400。 该文件不得被“其他”用户写入。

请注意,默认情况下,当启用 JWKS 支持时,内置生成 HS256 令牌的能力将被禁用。可以通过显式配置 jwt_key= 选项与 jwks= 一起重新启用。

注意:Slurm 忽略 x5cx5t 字段,并且如果在 JWKS 文件中提供,则不会尝试验证证书链。JWT 仅根据通过 en 字段提供的 RSA 256 位密钥进行验证。

JWKS 具有通过放置在 jwks.json 中获得信任的签名密钥。这些受信任的密钥可以为 任何用户创建新的令牌(即 JWT),通过对其进行签名。JWKS 不支持为单个用户添加密钥,仅支持添加受信任的签名密钥。

JWT 和 JWKS 可以在 Slurm 中共存。当 JWKS 被配置时,Slurm 将自动禁用 JWT,作为安全机制,以避免意外同时启用两者。

Keycloak

KeyCloak 可用于通过 JWT 或 JWKS 管理 认证令牌。在与 Slurm 一起使用时,某些项目应进行自定义。请参考 官方文档 进行初始设置。或者,如果您需要自动化初始设置,可以实施以下自定义:

  1. startup.sh(将 secret 替换为随机生成的客户端密钥):
    #!/bin/bash
    export SECRET=secret
    /opt/keycloak/bin/kc.sh bootstrap-admin service --client-id test --client-secret:env=SECRET
    exec /opt/keycloak/bin/kc.sh start-dev --log=console --log-console-level=debug --http-enabled true
    
  2. Dockerfile:
    FROM keycloak/keycloak
    COPY startup.sh /startup.sh
    ENTRYPOINT [ "/startup.sh" ]
    
  3. 环境变量(替换为实际的用户名和密码):
    KC_BOOTSTRAP_ADMIN_USERNAME=admin
    KC_BOOTSTRAP_ADMIN_PASSWORD=password
    

一旦您有一个正在运行的 KeyCloak 实例,登录到管理控制台并 创建一个新的 Realm、用户和客户端,如其 官方文档 中所述。创建客户端时,请确保选中 直接 访问授权。然后,您可以设置一个机制,将此认证方法集成到 Slurm 中。

例如,使用 JWKS

  1. 创建一个包含有效 RS256 密钥的 JSON 文件:
    curl "http://10.11.1.23:8080/realms/master/protocol/openid-connect/certs" > /etc/slurm/jwks.json
    
  2. 将 Slurm 指向该文件进行认证:
    AuthAltTypes=auth/jwt
    AuthAltParameters=jwks=/etc/slurm/jwks.json,userclaimfield=preferred_username
    

或者,使用 JWT

  1. 创建脚本以从 KeyCloak 生成 JWT:
    #!/bin/bash
    [ -z "$1" -o -z "$2" ] && echo "用法:\n$0 {用户名} {用户密码}" && exit 1
    
    curl -s \
      -d "client_id=test" \
      -d "client_secret=secret" \
      -d "username=$1" \
      -d "password=$2" \
      -d "grant_type=password" \
      -d "scope=openid" \
      "http://10.11.1.23:8080/realms/master/protocol/openid-connect/token" | \
      jq -r '.id_token'
    
  2. 然后,您可以使用该脚本为 Slurm 命令或 REST API 请求设置 JWT 变量:
    env SLURM_JWT=$(get_keycloak_jwt.sh username password) \
    sbatch -o none -e none --wrap 'unset SLURM_JWT; srun uptime'
    

用户映射

根据用于生成令牌的服务,您可能会遇到将令牌映射到用户名的问题。Slurm 默认使用 sun (Slurm 用户名)字段。如果服务使用不同的字段,则需要对此进行更正,以使其与 Slurm 一起工作。

选项 1:更改 Slurm 以使用不同的字段。这可以通过使用 AuthAltParameters=userclaimfield 进行自定义。例如,使用 KeyCloak 的默认字段:

AuthAltParameters=jwks=/local/path/to/jwks.json,userclaimfield=preferred_username

选项 2:更改身份服务以使用不同的字段。在 KeyCloak 25.0 中,您应该在 客户端 -> 客户端详情 -> 专用范围 -> 映射详情 下找到此选项。将用户名映射更改为使用 sun 字段。

兼容性

Slurm 使用 libjwt 查看和验证 RFC7519 JWT 令牌。 符合要求的令牌可以由其他解决方案生成,只要满足以下要求:
  1. Slurm 所需的令牌存在:
    • iat:创建日期的 Unix 时间戳。
    • exp:过期日期的 Unix 时间戳。
    • sun 或 username:Slurm 用户名 ( POSIX.1-2017 用户名 ).
  2. 令牌使用符合 RFC7518 的 HS256 算法签名。RS256 也 支持用于验证令牌,尽管 Slurm 不能直接创建它们。
  3. 签名密钥提供给 slurmctld 和 slurmdbd,以允许解密 令牌。Slurm 当前只支持单个签名密钥。
以下脚本需要安装 JWT Python 模块。 该脚本可以作为您可能用于生成 用于 Slurm 的 jwt 密钥的示例。
#!/usr/bin/env python3
import sys
import os
import pprint
import json
import time
from datetime import datetime, timedelta, timezone

from jwt import JWT
from jwt.jwa import HS256
from jwt.jwk import jwk_from_dict
from jwt.utils import b64decode,b64encode

if len(sys.argv) != 3:
    sys.exit("gen_jwt.py [用户名] [过期时间(秒)]");

with open("/var/spool/slurm/statesave/jwt.key", "rb") as f:
    priv_key = f.read()

signing_key = jwk_from_dict({
    'kty': 'oct',
    'k': b64encode(priv_key)
})

message = {
    "exp": int(time.time() + int(sys.argv[2])),
    "iat": int(time.time()),
    "sun": sys.argv[1]
}

a = JWT()
compact_jws = a.encode(message, signing_key, alg='HS256')
print("SLURM_JWT={}".format(compact_jws))
同样,以下脚本可以作为您可能 验证 jwt 密钥是否有效以用于 Slurm 的示例。
#!/usr/bin/env python3
import sys
import os
import pprint
import json
import time
from datetime import datetime, timedelta, timezone

from jwt import JWT
from jwt.jwa import HS256
from jwt.jwk import jwk_from_dict
from jwt.utils import b64decode,b64encode

if len(sys.argv) != 2:
    sys.exit("verify_jwt.py [JWT 令牌]");

with open("/var/spool/slurm/statesave/jwt.key", "rb") as f:
    priv_key = f.read()

signing_key = jwk_from_dict({
    'kty': 'oct',
    'k': b64encode(priv_key)
})

a = JWT()
b = a.decode(sys.argv[1], signing_key, algorithms=["HS256"])
print(b)

最后修改于 2025 年 6 月 11 日