HTTP NTLM authentication in jenkins pipeline script - jenkins

I am trying to post request which requires NTLM authentication. The curl command works fine when i do post call but same method request won't work with jenkins pipeline script.
Curl command:
curl -X POST -k -v -H \"Content-Type: application/json\" -H \"Content-Length: 0\" --ntlm -u domain/username:password http://blrapi/ExeWebAPI/testplans/run/username/89cd1093-6558-4321-b689-cb1
Jenkins Pipeline code
def getClient(){
def server = ""
def username = "username"
def userpassword = "password"
def domain = "domain"
def client = new HttpClient()
client.state.setCredentials(
AuthScope.ANY,
new NTCredentials(username, password, "", domain)
)
return client
}
def RunPlan( planId ){
SknetPost("hhttp://blrapi/ExeWebAPI/testplans/run/username/89cd1093-6558-4321-b689-cb1","")
}
def skynetExecute(httpMethod){
def httpResponse = ""
def sknetClient = getClient()
try {
int result = sknetClient.executeMethod(httpMethod)
println "Return code: ${result}"
httpResponse = httpMethod.getResponseBodyAsString()
}
finally {
httpMethod.releaseConnection()
}
return httpResponse
}
void SknetPost(url, jsondata) {
def post = new PostMethod( url )
post.doAuthentication = true
post.setRequestHeader("Content-type", "application/json")
StringRequestEntity requestEntity = new StringRequestEntity( jsonData , "text/html", "UTF-8");
post.setRequestEntity(requestEntity);
httpResponse = sknetExecute(post)
return httpResponse
}
}
When i execute the program it gives 401- unauthorized access error. Same credentials were used curl command it works fine but in jenkins pipeline it fails.
Please help me to solve this issue.

Web requests with NTLM authentication from Jenkins pipeline could be realized with the HTTP Request Plugin.
Add the Credential (user/password) in the jenkins credential store.
You could then use httpRequest in your pipeline:
script {
def response = httpRequest httpMode: 'GET',
url: "http://localhost:80",
authentication: '3bb9use-your-id-from-store',
useNtlm: true
println("Status: "+response.status)
println("Content: "+response.content)
}
Regards.
work for me with jenkins 2.324, HTTP Request Plugin 1.12

Related

Not able to construct proper header for groovy function Get request

Writing my first function here for Groovy native lib and running into an issue. A method to get Github Labels for pull-requests. #param github_token String token with permission to access and read PR information.
getLabelsPerPullRequest.groovy: 19: expecting '}', found ':' # line 19, column 28. 'Authorization': 'token '+ github_token, ^
Here is my function
import groovy.json.JsonSlurper
def getLabelsPerPullRequest(String github_token) {
def labels
def scmHead = jenkins.scm.api.SCMHead.HeadByItem.findHead(currentBuild.rawBuild.getParent())
def repo = scmHead.getSourceRepo()
def prId = scmHead.getId()
if(github_token && github_token != null) {
// Set the call headers with Oauth token.
def headers = "{'Authorization': 'token '+ ${github_token},'Accept': 'application/vnd.github.v3+json'}"
// Construct request number URL in Github
def pr_url = "https://github.optum.com/api/v3/repos/SOP-Bot/${repo}/pulls/${prNbr}"
def json = sh(
script: "curl -X Get ${pr_url} -H ${headers}",
returnStdout: true
)
def prInfo = JsonOutput.toJson(text: json)
labels = prInfo.labels
}
return labels
}
Each header needs to be a separate -H argument. You can do something like this. I added -s to curl because you probably don't want the extra progress output but it may not actually be an issue.
def headers = [
"-H 'Authorization: token ${gitub_token}'",
"-H 'Accept: application/vnd.github.v3+json'"
]
...
def json = sh(
script: "curl -s -X GET ${headers.join(' ')} '${pr_url}'",
returnStdout: true
)

Jenkins Docker container - 403 no valid crumb was included in the request

I'm setting up my Jenkins server, and on simple requests in the web interface, like creating a folder, a pipeline, a job, etc., I periodically get the following error:
HTTP ERROR 403
Problem accessing /job/Mgmt/createItem. Reason:
No valid crumb was included in the request
The server is using the Jenkins/Jenkins container, orchestrated by Kubernetes on a cluster on AWS created with kops. It sits behind a class ELB.
Why might I be experiencing this? I thought the crumb was to combat certain CSRF requests, but all I'm doing is using the Jenkins web interface.
Enabling proxy compatibility may help to solve this issue.
Go to Settings -> Security -> Enable proxy compatibility in CSRF Protection section
Some HTTP proxies filter out information that the default crumb issuer uses to calculate the nonce value. If an HTTP proxy sits between your browser client and your Jenkins server and you receive a 403 response when submitting a form to Jenkins, checking this option may help. Using this option makes the nonce value easier to forge.
After a couple of hours of struggling, I was able to make it work with curl:
export JENKINS_URL=http://localhost
export JENKINS_USER=user
export JENKINS_TOKEN=mytoken
export COOKIE_JAR=/tmp/cookies
JENKINS_CRUMB=$(curl --silent --cookie-jar $COOKIE_JAR $JENKINS_URL'/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' -u $JENKINS_USER:$JENKINS_TOKEN)
echo $JENKINS_CRUMB
curl --cookie $COOKIE_JAR $JENKINS_URL/createItem?name=yourJob --data-binary #jenkins/config.xml -H $JENKINS_CRUMB -H "Content-Type:text/xml" -u $JENKINS_USER:$JENKINS_TOKEN -v
when calling the http://JENKINS_SERVER:JENKINS_PORT/JENKINS_PREFIX/crumbIssuer/api/json you receive a header ("Set-Cookie") to set a JSESSIONID, so you must supply it in the upcoming requests you issue,
the reason is that jenkins test for valid crumb in this manner: comparing the crumb you send in the request with a crumb it generates on the server side (using your session id),
you can see it in jenkins code: scroll down to method:
public boolean validateCrumb(ServletRequest request, String salt, String crumb)
it means you HAVE to include a session in the next requests (after fetching the crumb)!
so the curl --cookie must be used as ThiagoAlves stated in his solution
i use java so i used this next tester (HTTPClient would be prefered, but i wanted a simple java only example):
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
public class JobRunner
{
String jenkinsUser = "tester";
String jenkinsPassword = "1234"; // password or API token
String jenkinsServer = "localhost";
String jenkinsPort = "8080";
String jenkinsPrefix = "/jenkins";
String jSession = null;
String crumb = null;
HttpURLConnection connection = null;
String responseBody = "";
public void openConnection(String requestMethod, String relativeURL) throws Exception
{
// prepare the authentication string
String authenticationString = jenkinsUser + ":" + jenkinsPassword;
String encodedAuthenticationString = Base64.getEncoder().encodeToString(authenticationString.getBytes("utf-8"));
// construct the url and open a connection to it
URL url = new URL("http://" + jenkinsServer + ":" + jenkinsPort + jenkinsPrefix + relativeURL);
connection = (HttpURLConnection) url.openConnection();
// set the login info as a http header
connection.setRequestProperty("Authorization", "Basic " + encodedAuthenticationString);
// set the request method
connection.setRequestMethod(requestMethod);
}
public void readResponse() throws Exception
{
// get response body and set it in the body member
int responseCode = connection.getResponseCode();
switch (responseCode)
{
case 401:
System.out.println("server returned 401 response code - make sure your user/password are correct");
break;
case 404:
System.out.println("server returned 404 response code - make sure your url is correct");
break;
case 201:
case 200:
System.out.println("server returned " + responseCode + " response code");
InputStream responseBodyContent = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(responseBodyContent));
String currentLine;
while ((currentLine = bufferedReader.readLine()) != null)
{
responseBody = responseBody + currentLine + "\n";
}
break;
default:
System.out.println("server returned error response code: " + responseCode);
break;
}
}
public void setSessionCookie() throws Exception
{
jSession = connection.getHeaderField("Set-Cookie");
System.out.println("jSession: " + jSession);
}
public void disconnect() throws Exception
{
if(connection!=null)
{
connection.disconnect();
connection = null;
responseBody = "";
}
}
public void getCrumb() throws Exception
{
try
{
openConnection("GET", "/crumbIssuer/api/json");
readResponse();
setSessionCookie();
int crumbIndex = responseBody.indexOf("crumb\":\"");
if(crumbIndex!=-1)
{
int crumbIndexEnd = responseBody.indexOf("\",\"", crumbIndex);
crumb = responseBody.substring(crumbIndex + "crumb\":\"".length(), crumbIndexEnd);
System.out.println(crumb);
}
}
finally
{
disconnect();
}
}
public void runJob() throws Exception
{
try
{
openConnection("POST", "/job/test/build");
connection.setDoOutput(true);
connection.setRequestProperty("Cookie", jSession);
connection.setRequestProperty("Jenkins-Crumb", crumb);
readResponse();
System.out.println("Post response: " + responseBody);
}
finally
{
disconnect();
}
}
public static void main(String[] args)
{
JobRunner jobRunner = new JobRunner();
try
{
jobRunner.getCrumb();
jobRunner.runJob();
}
catch (Exception err)
{
err.printStackTrace();
}
}
}

How to use HTTP builder within Jenkins

I am trying to use HTTP Builder to make a POST request in a pipeline script, (do have it in a shared lib where it works via the command line) but need it to work in Jenkins
I am getting the following error when running in Jenkins.
No suitable ClassLoader found for grab
My script looks as follows
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7')
import static groovyx.net.http.ContentType.JSON
import static groovyx.net.http.Method.POST
import groovyx.net.http.HTTPBuilder
def gitUpdateStatus() {
String targetUrl = 'https://api.github.com/repos/myOrg/'
def http = new HTTPBuilder(targetUrl)
http.request(POST) {
uri.path = "myRepo/statuses/commit_id_here"
requestContentType = JSON
body = [state: 'failure', description: 'Jenkins Unit Tests', target_url: 'http://test.co.uk', context: 'unit tests']
headers.'Authorization' = "token 123"
headers.'User-Agent' = 'Jenkins Status Update'
headers.Accept = 'application/json'
response.success = { resp, json ->
println "GitHub updated successfully! ${resp.status}"
}
response.failure = { resp, json ->
println "GitHub update Failure! ${resp.status} " + json.message
}
}
node {
stage('Echo Client JS')
git branch: 'master', credentialsId: '${JENKINS_CREDENTIALS_ID}', url: 'git#github.com:myOrg/myRepo.git'
gitUpdateStatus()
}
I have seen many posts where the same issue has arisen but I just cannot seem to figure out how this has been fixed, can anyone assist please ?
Thank You
You cannot use #Grab directly in pipelines. You need to move gitUpdateStatus() function into Jenkins shared library. See Using third party libraries.

Set headers in a groovy post request

I need to set a header in a post request: ["Authorization": request.token]
I have tried with wslite and with groovyx.net.http.HTTPBuilder but I always get a 401-Not authorized which means that I do cannot set the header right.
I have also thought of logging my request to debug it but I am not able to do that either.
With wslite this is what I do
Map<String, String> headers = new HashMap<String, String>(["Authorization": request.token])
TreeMap responseMap
def body = [amount: request.amount]
log.info(body)
try {
Response response = getRestClient().post(path: url, headers: headers) {
json body
}
responseMap = parseResponse(response)
} catch (RESTClientException e) {
log.error("Exception !: ${e.message}")
}
Regarding the groovyx.net.http.HTTPBuilder, I am reading this example https://github.com/jgritman/httpbuilder/wiki/POST-Examples but I do not see any header setting...
Can you please give me some advice on that?
I'm surprised that specifying the headers map in the post() method itself isn't working. However, here is how I've done this kind of thing in the past.
def username = ...
def password = ...
def questionId = ...
def responseText = ...
def client = new RestClient('https://myhost:1234/api/')
client.headers['Authorization'] = "Basic ${"$username:$password".bytes.encodeBase64()}"
def response = client.post(
path: "/question/$questionId/response/",
body: [text: responseText],
contentType: MediaType.APPLICATION_JSON_VALUE
)
...
Hope this helps.
Here is the method that uses Apache HTTPBuilder and that worked for me:
String encodedTokenString = "Basic " + base64EncodedCredentials
// build HTTP POST
def post = new HttpPost(bcTokenUrlString)
post.addHeader("Content-Type", "application/x-www-form-urlencoded")
post.addHeader("Authorization", encodedTokenString)
// execute
def client = HttpClientBuilder.create().build()
def response = client.execute(post)
def bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))
def authResponse = new JsonSlurper().parseText(bufferedReader.getText())

Groovy Httpbuilder authentication with Realm + RABBIT MQ

I am trying to to make a rabbitmq http api call to know how queues are there and other infos...
I need 3 variables to pass on to the api
1) url: (http://localhost:55672/api) 2) username/password: guest/guest
3) realm: "RabbitMQ Management" //i am not sure if this is important
4) path: "/queues"
when i make curl statement it gives a positive response
sudo curl -i -u guest:guest (http://localhost:55672)/api/queues
HTTP/1.1 200 OK
Server: MochiWeb/1.1 WebMachine/1.7 (participate in the frantic)
Date: Tue, 03 Jul 2012 01:39:05 GMT
Content-Type: application/json
Content-Length: 6176
Cache-Control: no-cache
but using httpbuilder from groovy. here is the code
def http = new HTTPBuilder("(http://localhost:55672/api)")
http.auth.basic 'guest','guest'
http.request(GET) { req ->
uri.path = '/queues'
response.success = { resp, reader ->
assert resp.statusLine.statusCode == 200
println "Got response: ${resp.statusLine}"
println "Content-Type: ${resp.headers.'Content-Type'}"
println reader.json
}
response.'404' = { println 'Not found' }
}
I am getting "not found" as the result. I am not including realm because I am unable to if i can insert "realm" in httpbuilder. it only comes with OAuth however I need to use basic auth for rabbit mq http api calls.
Does anyone knows how to include realm name in httpbuilder groovy for basic authentication? is there any other way. Kindly let me know! thanks!
Does this work?
def http = new HTTPBuilder( 'http://localhost:55672' )
http.auth.basic 'guest','guest'
http.request(GET) { req ->
uri.path = '/api/queues'
response.success = { resp, reader ->
assert resp.statusLine.statusCode == 200
println "Got response: ${resp.statusLine}"
println "Content-Type: ${resp.headers.'Content-Type'}"
println reader.json
}
response.'404' = { println 'Not found' }
}
Took the braces and the path out of your base url, added /api to the path

Resources