JumpServer PAM 资产账号密码查询 Python SDK 集成指南:从 REST API 到 jms-pam 客户端实战
本篇技术指南以 JumpServer 开源仓库中 Python 演示目录 的官方文档(README.ja.md)与示例代码为主体,系统讲解如何通过 RESTful API 查询 PAM 资产账号密码(Account Secret)、如何通过集成应用(Integration Application)机制获取 API 密钥并完成 HTTP 签名认证,以及官方 jms-pam Python 客户端(demo.py、jms_pam/main.py)的完整实现原理与使用方式。读完本文,你将能够独立编写基于 Python 的资产密码拉取脚本,并将其接入自己的运维自动化与 IT 工单系统。
1. 接口概览:PAM 资产账号密码查询服务
在 JumpServer 的账号(accounts)应用模块中,官方为第三方系统提供了一组“集成应用”相关的 API。本文聚焦的核心接口用于查询 PAM 资产上指定账号的密码(Secret),它以 RESTful 风格对外提供,并以 JSON 格式返回数据。
- 请求方式:
GET - 请求路径:
api/v1/accounts/integration-applications/account-secret/ - 返回格式:JSON
从路由注册看,该接口对应源码中 apps/accounts/urls.py 注册的 integration-applications 路由,其具体实现位于 apps/accounts/api/account/application.py 的 IntegrationApplicationViewSet.get_account_secret 动作(url_path='account-secret',detail=False),因此最终拼出的访问地址即 api/v1/accounts/integration-applications/account-secret/。该动作通过 IntegrationAccountSecretSerializer 校验查询参数,并以 accounts.view_integrationapplication 作为 RBAC 权限点,意味着只有被授予相应权限的应用账号才能调用。
2. 环境要求
官方 Python 演示对运行环境提出了明确要求:
| 依赖项 | 版本要求 | 用途 |
|---|---|---|
| Python | 3.11+ | 运行时 |
| requests | 2.31.0 | HTTP 客户端 |
| httpsig | 1.3.0 | HTTP 请求签名(HTTPSignature) |
其中 requests 负责发起 HTTP 请求,httpsig 用于实现 HTTP Signature 认证——这是 JumpServer 集成应用鉴权的核心,稍后会在第 4 节详细展开。
安装依赖可通过 pip 完成:
pip install "requests==2.31.0" "httpsig==1.3.0"
此外,官方演示还提供了打包配置 apps/accounts/demos/python/setup.py,声明包名为 jms-pam(版本 0.0.1),并同样将 requests、httpsig 列为安装依赖,说明该目录既可作为独立脚本运行,也可作为 Python 包安装到你的项目中复用。
3. 请求参数与响应格式
3.1 请求参数
GET api/v1/accounts/integration-applications/account-secret/ 接口需要携带以下查询参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| asset | str | 是 | 资产名称 |
| account | str | 是 | 账号名称 |
在源码层面,官方 SDK 的 SecretRequest 类(apps/accounts/demos/python/jms_pam/main.py)对参数校验做了更严格的定义,可以作为实战参考:
- 当提供了
account_id时,asset、asset_id、account均不得再提供; - 当提供了
account时,asset或asset_id至少提供其一; account_id与asset_id不允许同时提供;account_id与asset_id必须是合法 UUID,否则抛出ValueError;- 若
asset、asset_id均未提供,抛出RequestParamsError(['asset', 'asset_id']);若account未提供,抛出RequestParamsError(['account', 'account_id'])。
也就是说,除了按名称(asset + account)查询外,还支持按 ID(asset_id / account_id)精确查询,参数组合受严格约束。
3.2 响应示例
成功时接口返回 JSON:
{
"id": "72b0b0aa-ad82-4182-a631-ae4865e8ae0e",
"secret": "123456"
}
字段说明:
id:集成应用(Integration Application)的 ID,也是发起请求的应用身份标识;secret:目标资产账号的密码明文。
需要特别注意的是,返回密码与否受 JumpServer 全局安全配置 SECURITY_DISABLE_VIEW_SECRET 控制。从 apps/accounts/api/account/application.py 的源码可以看到:
secret = None if settings.SECURITY_DISABLE_VIEW_SECRET else account.secret
当系统开启“禁止查看密钥”策略时,secret 字段会返回 None。此外,每次查询都会记录安全审计日志,包含服务名称、服务 ID、账号(含用户名)、资产(含地址)与来源 IP 等信息,确保密码查询行为全程可追踪。
4. 集成应用与 API Key 获取(FAQ 核心问题)
Q:API Key 如何获取?
A:在 PAM 的“应用管理”中创建应用,即可生成 KEY_ID 与 KEY_SECRET。
这是 README 文档 FAQ 中明确给出的答案,也是使用本接口的前提:集成应用是 JumpServer 面向第三方系统开放的“服务身份”。创建应用后获得的一对密钥:
KEY_ID:应用 ID,作为签名请求中的身份标识,对应响应中的id字段;KEY_SECRET:应用密钥,用于计算 HMAC 签名,任何情况下都不应泄露。
在 apps/accounts/demos/python/demo.py 中,官方将这两个值通过环境变量注入:
KEY_ID = os.getenv("API_KEY_ID", "72b0b0aa-ad82-4182-a631-ae4865e8ae0e")
KEY_SECRET = os.getenv("API_KEY_SECRET", "6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8")
从源码结构看,IntegrationApplicationViewSet 还提供了另外两个与密钥生命周期相关的动作:
GET .../integration-applications/<id>/secret/(get_once_secret):一次性查看应用自身的 Secret,需要 MFA 二次确认(UserConfirmation.require(ConfirmType.MFA));GET .../integration-applications/<id>/refresh-secret/(refresh_secret):刷新应用 Secret,调用后旧的 KEY_SECRET 立即失效,用于密钥轮换场景。
这两个接口与 get_account_secret 一起,构成了集成应用密钥从“创建 → 查看 → 轮换”的完整闭环,可用于编写密钥管理脚本。
5. HTTP 签名认证原理与 Header 约定
调用该接口必须使用 HTTP Signature(httpsig)认证,而不是简单的 Bearer Token。签名过程在 apps/accounts/demos/python/demo.py 与 apps/accounts/demos/python/jms_pam/main.py 中都有完整实现,其核心逻辑一致:
from httpsig.requests_auth import HTTPSignatureAuth
auth = HTTPSignatureAuth(
key_id=KEY_ID, secret=KEY_SECRET,
algorithm='hmac-sha256',
headers=['(request-target)', 'accept', 'date', 'x-jms-org'] # demo.py 版本
)
要点说明:
- 算法:
hmac-sha256,以 KEY_SECRET 为密钥对选定的 Header 做 HMAC 签名; - 签名覆盖范围:demo.py 将
(request-target)、accept、date、x-jms-org纳入签名;而jms_pam包内实现(main.py 的_get_auth)签名的 Header 为['(request-target)', 'accept', 'date']。两种签名范围都包含请求目标与时间戳,可有效防止请求被篡改或重放; - Date 时间戳:必须使用 RFC 1123 GMT 格式,例如
Wed, 09 Sep 2026 02:37:44 GMT,且应与服务器时间保持同步,否则签名校验可能失败。
请求必须携带的 Header 汇总如下:
| Header | 示例值 | 说明 |
|---|---|---|
| Accept | application/json | 期望响应格式 |
| Date | Wed, 09 Sep 2026 02:37:44 GMT | 签名时间戳(RFC 1123 GMT) |
| X-JMS-ORG | 00000000-0000-0000-0000-000000000002 | 目标组织 ID |
| X-Source | jms-pam | 调用方标识 |
其中 X-JMS-ORG 用于指定查询所属组织,demo.py 中默认值为 00000000-0000-0000-0000-000000000002,可通过环境变量 ORG_ID 覆盖;X-Source 标记调用来源,便于服务端审计区分不同调用方。
6. 完整 Python 实战:两种调用方式
6.1 方式一:基于 requests + httpsig 的最小实现(demo.py)
官方提供了开箱即用的示例脚本 apps/accounts/demos/python/demo.py,其核心是一个 APIClient 类:
import requests
import os
from datetime import datetime
from httpsig.requests_auth import HTTPSignatureAuth
API_URL = os.getenv("API_URL", "http://127.0.0.1:8080")
KEY_ID = os.getenv("API_KEY_ID", "72b0b0aa-ad82-4182-a631-ae4865e8ae0e")
KEY_SECRET = os.getenv("API_KEY_SECRET", "6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8")
ORG_ID = os.getenv("ORG_ID", "00000000-0000-0000-0000-000000000002")
class APIClient:
def __init__(self):
self.session = requests.Session()
self.auth = HTTPSignatureAuth(
key_id=KEY_ID, secret=KEY_SECRET,
algorithm='hmac-sha256', headers=['(request-target)', 'accept', 'date', 'x-jms-org']
)
def get_account_secret(self, asset, account):
url = f"{API_URL}/api/v1/accounts/integration-applications/account-secret/"
headers = {
'Accept': 'application/json',
'X-JMS-ORG': ORG_ID,
'Date': datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'),
'X-Source': 'jms-pam'
}
params = {"asset": asset, "account": account}
try:
response = self.session.get(url, auth=self.auth, headers=headers, params=params, timeout=10)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
print(f"API request failed: {e}")
return None
if __name__ == "__main__":
client = APIClient()
result = client.get_account_secret(asset="ubuntu_docker", account="root")
print(result)
使用步骤:
- 设置环境变量
API_URL、API_KEY_ID、API_KEY_SECRET(ORG_ID可选); - 实例化
APIClient; - 调用
get_account_secret(asset="ubuntu_docker", account="root")查询指定资产上root账号的密码。
脚本内置 10 秒超时并对 RequestException 做了兜底处理,查询失败时返回 None 而非抛出异常,便于在自动化流程中做容错。
6.2 方式二:官方 jms-pam SDK(jms_pam 包)
apps/accounts/demos/python/jms_pam/ 目录封装了一个更完整的 SDK 雏形,包含三个核心类:
SecretRequest:请求参数对象,负责 UUID 校验与参数互斥校验(见第 3.1 节),并提供get_url()与get_query()生成请求地址与查询串;Secret:响应结果对象,valid属性表示请求是否成功,失败时desc携带错误描述;from_response会将非 200 响应的错误字段拼接为可读文本;JumpServerPAM:客户端主体,构造函数接收endpoint、key_id、key_secret、org_id,内部懒加载HTTPSignatureAuth,通过send(secret_request)完成一次签名请求。
JumpServerPAM 的请求地址由 SecretRequest.get_url() 固定为 /api/v1/accounts/service-integrations/account-secret/,与 README 文档中提到的 integration-applications 路径略有出入——从源码结构看,前者应是该 SDK 早期约定的路由,实际使用时应以当前版本 api/v1/accounts/integration-applications/account-secret/ 为准。这一点也提醒使用者:将 demo 接入生产环境前,务必先在本仓库 apps/accounts/urls.py 中核对当前版本的接口路径。
使用 jms_pam 的示例:
from jms_pam import JumpServerPAM, SecretRequest
client = JumpServerPAM(
endpoint="http://127.0.0.1:8080",
key_id="72b0b0aa-ad82-4182-a631-ae4865e8ae0e",
key_secret="6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8",
org_id="00000000-0000-0000-0000-000000000002",
)
req = SecretRequest(asset="ubuntu_docker", account="root")
result = client.send(req)
print(result.secret, result.valid, result.desc)
该封装适合需要将密码查询能力复用到多个业务场景(如 CI/CD 发布、数据库巡检、工单系统取密)的团队:只需初始化一次客户端,用不同的 SecretRequest 即可反复查询。
6.3 集成应用密钥的生命周期管理
结合第 4 节的源码分析,可将集成应用密钥管理归纳为三步:
- 创建:在 Web 控制台「PAM → 应用管理」中创建应用,获得 KEY_ID / KEY_SECRET;
- 查询:通过
get_account_secret查询资产账号密码,请求携带签名 Header; - 轮换:定期调用
refresh-secret刷新应用 Secret,降低密钥泄露风险;密钥泄露时可用get_once_secret(需 MFA 确认)查看当前 Secret 以便核对。
7. 常见问题(FAQ)
Q:API Key 如何获取?
A:在 PAM 的「应用管理」中创建应用,即可生成 KEY_ID 与 KEY_SECRET。创建完成后,将两者配置到环境变量 API_KEY_ID、API_KEY_SECRET 中即可开始调用。
Q:返回的 secret 为什么是 null?
A:当 JumpServer 开启了 SECURITY_DISABLE_VIEW_SECRET(禁止查看密钥)配置时,接口会返回 secret: null。需要联系管理员评估该策略后调整。
Q:签名校验失败怎么办?
A:检查三点:1) KEY_ID / KEY_SECRET 是否正确且未过期;2) 本机时间与服务器时间是否同步(Date 头参与签名);3) 签名覆盖的 Header 是否与实现一致,特别是 X-JMS-ORG 是否在签名范围内。
Q:如何限制调用权限?
A:get_account_secret 的 RBAC 权限点为 accounts.view_integrationapplication,可在角色权限中为指定应用/用户授予或回收该权限,实现最小权限控制。
8. 版本历史(Changelog)
| 版本号 | 变更内容 | 日期 |
|---|---|---|
| 1.0.0 | 初始版本 | 2025-02-11 |
9. 延伸阅读
- 接口路由注册与账号 API 全貌:apps/accounts/urls.py
- 集成应用 ViewSet 实现(含 get_account_secret / refresh_secret / get_once_secret):apps/accounts/api/account/application.py
- 官方 Python 示例脚本(最小实现):apps/accounts/demos/python/demo.py
- 官方 jms-pam SDK 源码(参数校验与客户端封装):apps/accounts/demos/python/jms_pam/main.py
- Python 包打包配置:
setup.py(apps/accounts/demos/python/setup.py) - 同一接口的其他语言实现,可参考 Go 示例、Java 示例、Node 示例 与 curl 示例,便于在多语言技术栈中保持一致的接入方式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00