4

I am using Ansible (v 2.8) as the provisioner behind a Packer template to build an AMI for a Jenkins master node. For previous versions, the playbook passed successfully. However, as of Jenkins version 2.176.3, the jenkins_plugin module has been throwing:

HTTP Error 403: No valid crumb was included in the request

I have retrieved the crumb and registered it in a variable. I have tried passing it to jenkins_plugin with the http_agent field, but that doesn't work. I tried using attributes, but that didn't help either. Unless I am missing something incredibly basic, I am at the end of my tether.

- name:               Get Jenkins Crumb
  uri:
    force_basic_auth: yes
    url_username:     ****
    url_password:     ****
    url:              http://localhost:8080/crumbIssuer/api/json
    return_content:   yes
  register:           jenkins_crumb
  until:              jenkins_crumb.content.find('Please wait while Jenkins is getting ready') == -1
  retries:            10
  delay:              5

- name:               Install plugin
  jenkins_plugin:
    name:             "{{ item }}"
    version:          latest
    force_basic_auth: yes
    url_username:     ****
    url_password:     ****
    http_agent:       "Jenkins-Crumb:{{ jenkins_crumb.json.crumb }}"
  with_items:         "{{ jenkins_plugins }}"

I expected installed plugins and a happily built AMI. What I got was "HTTP Error 403: No valid crumb was included in the request" and the Packer build failed.

Yuri
  • 4,254
  • 1
  • 29
  • 46
  • Did the CRUMB request work before or is this a new requirement? Have you verified the `jenkins_crumb.json.crumb` with a `debug`? – Matthew Schuchard Aug 30 '19 at 13:03
  • Unless the crumb is changing between calls, I haven't seen any indication of it being invalid. The retrieval of the crumb is successful (I can see it in the logs), but I'm stumped as to how to pass it in as you would using curl. – Hartnell Foster Aug 30 '19 at 14:29
  • If this was succeeding in a prior version instead of being a new requirement, this could be a breaking change in a newer version of Jenkins or the Ansible module. Otherwise it could be a bug. – Matthew Schuchard Aug 30 '19 at 14:39
  • I suspect it to be a breaking change in Jenkins. If I can't pass the crumb in a header, I may have to disable CSRF and re-enable it at the end of the play. – Hartnell Foster Aug 30 '19 at 14:54

6 Answers6

8

Looks like a change to the crumb issuer in the 2.176 LTS release forces the inclusion of the web session id of the initial token generation call along with the crumb in subsequent calls that use said crumb.

CSRF tokens (crumbs) are now only valid for the web session they were created in to limit the impact of attackers obtaining them. Scripts that obtain a crumb using the /crumbIssuer/api URL will now fail to perform actions protected from CSRF unless the scripts retain the web session ID in subsequent requests.

In addition to the suggestion that you temporarily disable CSRF, the same doc suggests that you could only disable the new functionality, rather than CSRF as a whole, which should allow your packer/ansible to complete as it previously did, as-written.

To disable this improvement you can set the system property hudson.security.csrf.DefaultCrumbIssuer.EXCLUDE_SESSION_ID to true.

EDIT :

Adding the following line in /etc/default/jenkins cleared the CSRF issues in my own playbook (Ansible 2.8.4, Ubuntu 18.04, OpenJDK 11.0.4)

JAVA_ARGS="$JAVA_ARGS -Dhudson.security.csrf.DefaultCrumbIssuer.EXCLUDE_SESSION_ID=true"

Might be a good-enough crutch until tool maintainers catch up with the API changes.

runningEagle
  • 116
  • 5
2

I was facing this issue too and given the pointer the work needed to be done in a session I opened a PR for ansible:

https://github.com/ansible/ansible/issues/61672 https://github.com/ansible/ansible/issues/61673

It is a small change and it should be possible to patch your local installation.

UPDATE: The patch was applied in Ansbile v2.8.9 and v2.9.1 be sure to upgrade ansible if you have an older version.

Adam
  • 2,800
  • 1
  • 13
  • 15
  • thanks for the fix. it is good to mention that the fix is available since ansible 2.8.9 and 2.9.1 in case people use an older version from their linux distro. – Adam Jun 08 '20 at 07:33
2

It's exactly the cause @runningEagle mentioned. You need to propagate the initial session cookie value to all subsequent requests along with the crumb.

Required new Ansible code modifications:

...

# Requesting the crumb
uri:
  url: "<crumb_URL>"
register: response

...

# Actual action request
uri:
  url: "<action_URL>"
  headers: '{ ... , "Cookie": "{{ response.set_cookie }}", ... }'

...
Yuri
  • 4,254
  • 1
  • 29
  • 46
  • Thanks for sharing the code. Here's good for me. ;) ... headers: '{ "Jenkins-Crumb" : "{{ crumbid }}", "Cookie": "{{ crumb.set_cookie }}" }' ... – Shirley Jun 17 '20 at 03:32
1

The solution I ended up applying was to disable CSRF using a handy piece of Groovy, and then re-enable it at the end of the play.

Thanks all for your help and recommendations.

0

Had tried setting the below per the documentation for /etc/default/jenkins (Most Linux) and /etc/sysconfig/jenkins (RHEL):

hudson.security.csrf.DefaultCrumbIssuer.EXCLUDE_SESSION_ID to true

Thus adding:

JAVA_ARGS="-Dhudson.security.csrf.DefaultCrumbIssuer.EXCLUDE_SESSION_ID=true

But to no avail. Using @Yuri answer fixed it for me. See below:

Requested the crumb the same as I did before. Crumb:

- uri:
    url: "http://localhost:8080/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)"
    return_content: yes
  register: crumb

Action request:

    uri:
      method: POST
      url: "http://localhost:8080/credentials/store/system/domain/_/createCredentials"
      headers:
        Jenkins-Crumb: "{{ crumb.content.split(':')[1] }}"
        Cookie: "{{ crumb.set_cookie }}"
      body: |
        json={
          "": "0",
          "credentials": {
            "scope": "GLOBAL",
            "id": "identification",
            "username": "manu",
            "password": "bar",
            "description": "linda",
            "$class": "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
          }
        }
      status_code: 302
Gloorfindel
  • 404
  • 3
  • 8
0

Resolution:

  • Install a plugin named Strict Crumb Issue

  • Go to Manage Jenkins -> Configure Global Security -> CSRF Protection.

  • Select Strict Crumb Issuer.

  • Click on Advanced.

  • Uncheck the Check the session ID box.

  • Save it.