我在使用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。让我们将其放置在凭据存储中,该存储位于“Playground”的子文件夹“API-Test”中,即Playground/API-Test。
当您单击Jenkins UI中新创建的凭据项目时,它的URL应如下所示:
https://JENKINS_HOSTNAME/job/Playground/job/API-Test/credentials/store/folder/domain/_/credential/test-sshkey/
将上面的URL添加/config.xml,使其看起来像这样:
https://JENKINS_HOSTNAME/job/Playground/job/API-Test/credentials/store/folder/domain/_/credential/test-sshkey/config.xml
步骤3返回的URL的XML结构几乎具备我们使用凭据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>
以获得一个有效的 XML 模板,您可以将其 POST 到凭据 API:
<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结构通过cURL或类似工具将其POST到API端点以创建或更新。我们假设所有操作都在文件夹“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())
jsonCrumb = self._requestApiCrumb()
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文件即可。
此外,还请查看Credentials插件的user guide,特别是关于构建正确的REST URL的REST API部分。如果您需要使用Python请求Jenkins crumb发行者,请参阅this StackOverflow answer。
解决方案测试版本:
- Jenkins 2.214
- Credentials Plugin 2.3.1
- SSH Credentials Plugin 1.18.1