在通过 Python 脚本将私有 SSH 密钥推送到 Jenkins 时,我遇到了完全相同的问题。我正在使用 Requests 库在 Jenkins 服务器上的任意凭据存储中创建和更新 SSH 密钥凭据集。
一般问题是您的 XML 结构部分错误。标签
<directEntryPrivateKeySource>
必须替换为
<privateKeySource class="com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource">
获取基本的 XML 结构
按照以下步骤,您可以自己从 Jenkins 服务器获取正确的 XML 结构:
- 手动创建 SSH 密钥凭据项。在下面的示例中,密钥的 id 是 test-sshkey。让我们将它放在位于文件夹“API-Test”的凭证存储中,该文件夹是“Playground”的子文件夹,即 Playground/API-Test。
-
当您在 Jenkins UI 中单击新创建的凭据项时,其 URL 应如下所示:
https://JENKINS_HOSTNAME/job/Playground/job/API-Test/credentials/store/folder/domain/_/credential/test-sshkey/
-
将 /config.xml 添加到上面的 URL 中,使其看起来像这样:
https://JENKINS_HOSTNAME/job/Playground/job/API-Test/credentials/store/folder/domain/_/credential/test-sshkey/config.xml
第 3 步中 URL 返回的 XML 结构几乎具有我们在使用 Credentials API 时所需的结构,但部分不完整:
<com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey plugin="ssh-credentials@1.18.1">
<id>test-sshkey</id>
<description>DELETE AFTER USE</description>
<username>test</username>
<privateKeySource class="com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource">
<privateKey>
<secret-redacted/>
</privateKey>
</privateKeySource>
</com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey>
使用凭据 API
添加标签 <scope> 和 <passphrase> 以获得可以 POST 到 Credentials API 的有效 XML 脚手架:
<com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey>
<scope>GLOBAL</scope>
<id>CREDENTIAL_ID</id>
<description>MY_DESCRIPTION</description>
<username>A_USERNAME</username>
<passphrase>OPTIONAL_KEY_PASSWORD</passphrase>
<privateKeySource class="com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource">
<privateKey>YOUR_PRIVATE_SSH_KEY_GOES_HERE</privateKey>
</privateKeySource>
</com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey>
警告:如果提交的 XML 结构错误,凭据插件的 REST API 仍会接受它并返回误导性 HTTP 状态代码 200!
现在我们可以使用这个 XML 结构将其发布到 API 端点,以便通过 cURL 或类似工具创建或更新。我们假设所有操作都在文件夹“Playground/API-Test”的凭证存储中执行。
以下 Python 代码示例完全“简化”以专注于一般方法:
def addCredentialSetSshPrivateKey(self, credentialDataObj):
"""
Adds a credential set with a private SSH key to a credential store
credentialDataObj: An instance of a simple DTO
"""
jenkinsRequestUrl = "https://ci-yoda-new.codemanufaktur.com/job/Playground/job/API-Test/credentials/store/folder/domain/_/createCredentials"
authentication = ("jenkins_admin_user", "API-TOKEN_FOR_THE_USER")
completeSamlData = """
<com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey>
<scope>GLOBAL</scope>
<id>{0}</id>
<description>{1}</description>
<username>{2}</username>
<passphrase>{3}</passphrase>
<privateKeySource class="com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource">
<privateKey>{4}</privateKey>
</privateKeySource>
</com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey>
""".format(credentialDataObj.id(), credentialDataObj.description(), credentialDataObj.username(), credentialDataObj.key_passphrase(), credentialDataObj.private_ssh_key())
# When using CSRF protection in Jenkins a API crumb must be included in the actual REST call.
# The following method requests the Jenkins Crumb Issuer for a API crumb and returns a JSON object like this:
# {'_class': 'hudson.security.csrf.DefaultCrumbIssuer', 'crumb': 'a5d36ef09e063322169888f0b81341fe13b4109482a7936bc08c6f9a01badd39', 'crumbRequestField': 'Jenkins-Crumb'}
jsonCrumb = self._requestApiCrumb()
# The actual REST call with headers, XML payload and all other bells and whistles.
remoteSession = requests.Session()
return remoteSession.post(jenkinsRequestUrl, auth = authentication, headers = {"content-type":"application/xml", jsonCrumb['crumbRequestField']:jsonCrumb['crumb']}, data = completeSamlData)
用于创建 SSH 凭证项的 REST 端点:
https://JENKINS_HOSTNAME/job/Playground/job/API-Test/credentials/store/folder/domain/_/createCredentials
用于更新 SSH 凭证项的 REST 端点:
https://ci-yoda-new.codemanufaktur.com/job/Playground/job/API-Test/credentials/store/folder/domain/_/credential/credential_ci-yoda-new-project-apex_privatekey/config.xml
显然在后一种情况下,您只需更新现有凭据项的 config.xml 文件。
另请参阅凭据插件的user guide,REST API 部分,特别是用于构建正确的 REST URL。如需使用 Python 请求 Jenkins crumb issuer,请参阅this StackOverflow answer。
解决方案测试:
- 詹金斯 2.214
- 凭据插件 2.3.1
- SSH 凭据插件 1.18.1