如何为 Jenkins 请求 crumb 发行者

问题描述 投票:0回答:8

我想使用 Jenkins Remote API,并且正在寻找安全的解决方案。我遇到了

Prevent Cross Site Request Forgery exploits
,我想使用它,但我在某处读到您必须提出面包屑请求。

如何获取 crumb 请求以使 API 正常工作?

我发现了这个

https://github.com/entagen/jenkins-build-per-branch/pull/20
,但我仍然不知道如何解决它。

我的 Jenkins 版本是 1.50.x。

使用 POST 请求时,经过身份验证的远程 API 请求响应 403

api security jenkins
8个回答
47
投票

我在文档中也没有找到这一点。此代码针对较旧的 Jenkins (1.466) 进行了测试,但应该仍然有效。

要发出面包屑,请使用

crumbIssuer

// left out: you need to authenticate with user & password -> sample below
HttpGet httpGet = new HttpGet(jenkinsUrl + "crumbIssuer/api/json");
String crumbResponse = toString(httpclient, httpGet);
CrumbJson crumbJson = new Gson().fromJson(crumbResponse, CrumbJson.class);

这会给你这样的回应

{"crumb":"fb171d526b9cc9e25afe80b356e12cb7","crumbRequestField":".crumb"}

这包含您需要的两条信息

  1. 您需要传递面包屑的字段名称
  2. 面包屑本身

如果您现在想从 Jenkins 获取某些内容,请将面包屑添加为标头。在下面的示例中,我获取了最新的构建结果。

HttpPost httpost = new HttpPost(jenkinsUrl + "rssLatest");
httpost.addHeader(crumbJson.crumbRequestField, crumbJson.crumb);

这是整个示例代码。我使用 gson 2.2.4 来解析响应,并使用 Apache 的 httpclient 4.2.3 来解析其余部分。

import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.*;

import com.google.gson.Gson;

public class JenkinsMonitor {

    public static void main(String[] args) throws Exception {

        String protocol = "http";
        String host = "your-jenkins-host.com";
        int port = 8080;
        String usernName = "username";
        String password = "passwort";

        DefaultHttpClient httpclient = new DefaultHttpClient();
        httpclient.getCredentialsProvider().setCredentials(
                new AuthScope(host, port), 
                new UsernamePasswordCredentials(usernName, password));

        String jenkinsUrl = protocol + "://" + host + ":" + port + "/jenkins/";

        try {
            // get the crumb from Jenkins
            // do this only once per HTTP session
            // keep the crumb for every coming request
            System.out.println("... issue crumb");
            HttpGet httpGet = new HttpGet(jenkinsUrl + "crumbIssuer/api/json");
            String crumbResponse= toString(httpclient, httpGet);
            CrumbJson crumbJson = new Gson()
                .fromJson(crumbResponse, CrumbJson.class);

            // add the issued crumb to each request header
            // the header field name is also contained in the json response
            System.out.println("... issue rss of latest builds");
            HttpPost httpost = new HttpPost(jenkinsUrl + "rssLatest");
            httpost.addHeader(crumbJson.crumbRequestField, crumbJson.crumb);
            toString(httpclient, httpost);

        } finally {
            httpclient.getConnectionManager().shutdown();
        }

    }

    // helper construct to deserialize crumb json into 
    public static class CrumbJson {
        public String crumb;
        public String crumbRequestField;
    }

    private static String toString(DefaultHttpClient client, 
        HttpRequestBase request) throws Exception {
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        String responseBody = client.execute(request, responseHandler);
        System.out.println(responseBody + "\n");
        return responseBody;
    }

}

10
投票

或者您可以使用 Python 和

requests
代替

req = requests.get(
    'http://JENKINS_URL/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)',
     auth=(username, password),
)
print(req.text)

会给你名字和面包屑:

Jenkins-Crumb:e2e41f670dc128f378b2a010b4fcb493

8
投票

此 Python 函数获取 crumb,并另外使用 crumb 发布到 Jenkins 端点。这是使用 Jenkins 2.46.3 进行测试的,并且打开了CSRF保护

import urllib.parse
import requests

def build_jenkins_job(url, username, password):
    """Post to the specified Jenkins URL.

    `username` is a valid user, and `password` is the user's password or
    (preferably) hex API token.
    """
    # Build the Jenkins crumb issuer URL
    parsed_url = urllib.parse.urlparse(url)
    crumb_issuer_url = urllib.parse.urlunparse((parsed_url.scheme,
                                                parsed_url.netloc,
                                                'crumbIssuer/api/json',
                                                '', '', ''))
    # Use the same session for all requests
    session = requests.session()

    # GET the Jenkins crumb
    auth = requests.auth.HTTPBasicAuth(username, password)
    r = session.get(crumb_issuer_url, auth=auth)
    json = r.json()
    crumb = {json['crumbRequestField']: json['crumb']}

    # POST to the specified URL
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    headers.update(crumb)
    r = session.post(url, headers=headers, auth=auth)

username = 'jenkins'
password = '3905697dd052ad99661d9e9f01d4c045'
url = 'http://jenkins.example.com/job/sample/build'
build_jenkins_job(url, username, password)

4
投票

同时,您可以生成 API 令牌,以避免必须在上述解决方案提供的源代码中包含您的密码:

https://wiki.jenkins.io/display/JENKINS/Authenticating+scripted+clients


4
投票

enter image description here

参考 - https://support.cloudbees.com/hc/en-us/articles/219257077-CSRF-Protection-Explained

如果您使用用户名和用户 API 令牌进行身份验证,则 Jenkins 2.96 每周/2.107 LTS 不需要 crumb。有关更多信息,请参阅使用 API 令牌或 JENKINS-22474 进行身份验证时不再需要 CSRF crumb。


3
投票

用户cheffe的回答帮助了90%。感谢您给我们正确的方向。

缺失的 10% 涉及 HTTP 用户名和密码身份验证。

由于我使用的 Codenameone Java API 没有身份验证类,

new UsernamePasswordCredentials(usernName, password));

我用过:

String apiKey = "yourJenkinsUsername:yourJenkinsPassword";
httpConnection.addRequestHeader("Authorization", "Basic " + Base64.encode(apiKey.getBytes()));

1
投票

用户 Cheffe 的 Java 片段 在 Jenkins v2.89.3 (Eclipse.org) 和我使用的另一个 Jenkins 实例 v2.60.3 上非常适合我(一旦启用1)。

我已将其添加到 Maven mojo2,我用它来将本地编辑的

config.xml
更改推回到服务器。

1 CSRF 保护
2 Hudson 作业同步插件


0
投票

在这些答案中,我没有找到使用

Jenkins API token
的选项。 我确实尝试了所有这些选项,但如果您启用
CSRF
保护,则应该使用
Jenkins API token
而不是普通的
password
来访问 Jenkins API。 该令牌可以由每个单独的用户在用户配置页面中生成。 代币可以如下使用-

JenkinsApi::Client.new(server_url: jenkins_url, username: jenkins_user, password: jenkins_token)

附注- 此初始化适用于 Ruby Jenkins API 客户端

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.