NTLM authentication

I am using gatling for stress testing a web app that uses NTLM authentication. When i recorded the scripts I got a NTLM authentication in the header something like the following

authorizationHeader(""“NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==”"")

I ran the same test script after few hours and got 401 error. I believe NTLM authentication expired. Is there a way I can handle this in gatling?
or
Is there a way I can generate this authentication and replace it in the test scripts?

Thanks,
Abhi

Hi Abhi,

Yes, there should be a way, as underlying AsyncHttpClient is supposed to support NTLM.
Honestly, I personally never had a chance to get my hands on such an application and test it.

In your HttpProtocol, you can pass a com.ning.http.client.Realm to the authRealm.
You can build a Realm from a RealmBuilder.

I can try and help you debug this, but you’d have to dig too.
Typical problem for open source projects with Microsoft enterprise only protocols. :frowning:
Would I be able to work on such a system, I’m pretty confident I could have it running in a matter of minutes-hours, but until then…

Thank you Stephane! I will try to figure out how to integrate it. May need your help on it later.

I was able to get to past NTLM using following HttpClient class but can’t figure out how to get the authorization header with ntlm token/String out of it. Since gatling does get info out when using recorder, can you point me into some direction as to what can be done.

Here is the code I used:

DefaultHttpClient httpclient = new DefaultHttpClient();
List authpref = new ArrayList();
authpref.add(AuthPolicy.NTLM);
httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, authpref);
NTCredentials creds = new NTCredentials(“userid”, “Password”, “”, “corp”);
httpclient.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);

HttpHost target = new HttpHost(“work.org”);

HttpContext localContext = new BasicHttpContext();

HttpGet httpget = new HttpGet(“http://work.org/web”);
HttpResponse response = httpclient.execute(target, httpget, localContext);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println(“Status Code:” + statusCode);

No, you’re confusing Apache HttpComponent and AsyncHttpClient.
Gatling is built on top of the latter.

AsyncHttpClient has NTLM support, though I never used it myself.
Check out Realm and RealmBuilder links from my previous message.

Ok Stephane. So I did some digging and came up with the following code. Current problem is I don’t know how to pass the realm back into the httpProtocol. When I append it to httpProtocol variable, I get the following compiler error “value realm is not a member of io.gatling.http.config.HttpProtocolBuilder”
Here is the code:

val builder = new AsyncHttpClientConfig.Builder();

val realm = new Realm.RealmBuilder().setUsePreemptiveAuth(true)
.setPrincipal(“gognaab”)
.setPassword(“password”)
.setNtlmDomain(“corp”)
.setNtlmHost(“http://work.org”)
.setScheme(AuthScheme.NTLM).build()

builder.setRealm(realm).build();

val httpProtocol = http
.baseURL(“https://esp.qa.finra.org”)
.inferHtmlResources()
.acceptHeader("""/""")
.acceptEncodingHeader(""“gzip, deflate”"")
.acceptLanguageHeader(""“en-US,en;q=0.5"”")
.authorizationHeader(""“fasfsafksjkl2342xxx”"")//changed
.connection(""“keep-alive”"")
.userAgentHeader(""“Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0"”")
.realm(realm)

val scn = scenario(“SearchSimulation”)
// Search
.exec(http(“request_90”)
.get("""/test/Search?q=test&rpp=20""")
.check(status.is(200)))

setUp(scn.inject(atOnceUsers(1))).protocols(httpProtocol)

What am I doing wrong here?

Thanks,
Abhinav

Method is named authRealm, not realm:

val realm = new Realm.RealmBuilder().setUsePreemptiveAuth(true)
.setPrincipal(“gognaab”)
.setPassword(“password”)
.setNtlmDomain(“corp”)
.setNtlmHost(“http://work.org”)
.setScheme(AuthScheme.NTLM).build

val httpProtocol = http
.baseURL(“https://esp.qa.finra.org”)
.inferHtmlResources()
.acceptHeader("""/""")
.acceptEncodingHeader(""“gzip, deflate”"")
.acceptLanguageHeader(""“en-US,en;q=0.5"”")
.connection(""“keep-alive”"")
.userAgentHeader(""“Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0"”")
.authRealm(realm)

Also:

  • don’t set the authorizationHeader, it will be generated by the Realm
  • don’t try to build an AHC config, Gatling will build one underneath.

Thanks. Now I am not getting 401 error but 400.

Problem is authRealm sets a header Realm, which for some reason creates an invalid header name. Is there a way I can remove this from request header?

Here is the realm header info:
realm=Realm{principal=‘yyy’, password=‘xxx’, scheme=NTLM, realmName=
‘’, nonce=’’, algorithm=‘MD5’, response=’’, qop=‘auth’, nc=‘00000001’, cnonce=’’
, uri=‘null’, methodName=‘GET’, useAbsoluteURI=‘true’, omitQuery=‘false’}

Sorry, which header name/value do you get exactly?

The following string was in the request header:

realm=Realm{principal=‘yyy’, password=‘xxx’, scheme=NTLM, realmName=’’, nonce=’’, algorithm=‘MD5’, response=’’, qop=‘auth’, nc=‘00000001’, cnonce=’’, uri=‘null’, methodName=‘GET’, useAbsoluteURI=‘true’, omitQuery=‘false’}

No, that’s just some logging, not the header that was sent.

Could you upgrade to RC6 (release today), please?
You should see the exact headers that were sent: https://github.com/gatling/gatling/issues/2131

From RC6, I get the following log info about http request.

HTTP request:
GET http://xxx.org/xxx/xxx?q=test&rpp=20
headers=
Authorization: NTLM TlRMTVNTUAABAAAANQIIIBAAEAAmAAAABgAGACAAAABlAHMAcABOAEEAUwBE
AEMATwBSAFAA
Connection: keep-alive
Host: xxxxxxxx
Authorization: NTLM TlRMTVNTUAABAAAANQIIIBoAGgAmAAAABgAGACAAAABlAHMAcABOAFQATABN
ACAATgBBAFMARABDAE8AUgBQAA==
Accept: /
realm=Realm{principal=‘yyy’, password=‘xxxx’, scheme=NTLM, realmName=
‘’, nonce=’’, algorithm=‘MD5’, response=’’, qop=‘auth’, nc=‘00000001’, cnonce=’’
, uri=‘null’, methodName=‘GET’, useAbsoluteURI=‘true’, omitQuery=‘false’}

How come is there 2 Authorization headers?
Did you forgot to remove authorizationHeader?

I didn’t add the extra authorization header. Here is the code that I have now

val httpProtocol = http
.baseURL(“xxx”)
.authRealm(realm)

val scn = scenario(“SearchSimulation”)
// Search
.exec(http(“test”)
.get(""“espweb/”"")
.check(status.is(200)))

So I compiled AsyncHttp client code separately to try to pinpoint the issue. It looks like Netty jar creates the problem. As soon as I remove it, I get Response code 200. Is there a workaround to this?

Here is the code

def main(args:Array[String])
{
val builder = new AsyncHttpClientConfig.Builder();
val realm = new Realm.RealmBuilder()
.setPrincipal(“xxx”)
.setPassword(“xxx”)//put password here
.setUsePreemptiveAuth(true)
.setScheme(AuthScheme.NTLM)
.build();

builder.setRealm(realm).build()

val client = new AsyncHttpClient(builder.build())
println(“before response”)

val response = client.prepareGet(“yyy”)
.execute().get();

println(response.getStatusCode());

Another update. Looks like Netty 4.0.23 jar works fine with standalone code but execution breaks Gatling since there are some dependencies( I got logger error, don’t of any other). If we just copy over those dependencies to netty 4.jar, we should be all set.

Thanks,
Abhinav

I’ve found 2 issues in the Netty provider code:

  1. the Type1 NTLM Authentication header is still there when sending the Type3 header
  2. preemptive NTLM handshake should only happen when connecting, not for every request
    Then, I’m very surprised that you got something running without setting the NTLM domain and host.

Are you sure you got the expected page, even though you got a 200 status?

You are right. The code just checks response code and I assumed when its 200 then response should be ok. Looks like its not.

Its a bummer netty breaks this stuff. Any chance we can workaround it?

Actually, my second point was invalid.
The first one was, and I fixed it (but you weren’t impacted as you didn’t make it this far).

You can upgrade your AsyncHttpClient source code, but make sure you build the 1.9.x branch, not master!

Then, IMHO, the first thing to fix is on YOUR side: NTLM domain and host (I think it’s the workstation) are/should be mandatory.

val realm = new Realm.RealmBuilder()
.setPrincipal(“xxx”)
.setPassword(“xxx”)//put password here
.setUsePreemptiveAuth(true)
.setScheme(AuthScheme.NTLM)
.setNtlmDomain(“YOUR_NTLM_DOMAIN_HERE”)
.setNtlmHost(“YOUR_NTLM_HOST_HERE”)
.build();

I downloaded and compiled asyncHttp client from https://github.com/AsyncHttpClient/async-http-client/tree/1.9.x (branch). It still has the same problem (double authentication). As soon as I remove netty jar it works fine. Also, I added back ntlmDomain and ntlmHost in realm builder and I got response body back with 200.

So far I have only seen logger error during gatling run with netty 4. I will try to compile netty4 with netty 3 logging to see if it works.