Attempting to integrate with SSRS over HTTP. We have an Azure Web App Service on a VNET with a VM hosting both SQL and SSRS. We can verify that network connectivity is established, we have an ADO.NET SQL connection that succeeds to VM SQL.
curl success
From a web SSH console in Azure Portal on Web App Service, we are able to run
curl -v --ntlm -u SomeDomain\\SomeUser:SomePassword http://[some ip]/reports/api/v2.0/Reports
output below. Note initial 401 followed by 200. We presume this is an NTLM handshake/challenge.
root@xxxxxxxxxxxx:/# curl -v --ntlm -u SomeDomain\\SomeUser:SomePassword http://[some ip]/reports/api/v2.0/Reports
* Trying [some ip]:80...
* Connected to [some ip] ([some ip]) port 80 (#0)
* Server auth using NTLM with user 'SomeDomain\SomeUser'
> GET /reports/api/v2.0/Reports HTTP/1.1
> Host: [some ip]
> Authorization: NTLM [some base64 value]
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Content-Length: 0
< Server: Microsoft-HTTPAPI/2.0
< WWW-Authenticate: NTLM [Another base64 value]
< Date: Mon, 09 Dec 2024 18:31:20 GMT
<
* Connection #0 to host [some ip] left intact
* Issue another request to this URL: 'http://[some ip]/reports/api/v2.0/Reports'
* Found bundle for host: 0x56e3158ace40 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection #0 with host [some ip]
* Server auth using NTLM with user 'SomeDomain\SomeUser'
> GET /reports/api/v2.0/Reports HTTP/1.1
> Host: [some ip]
> Authorization: NTLM [yet another base64 value]
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Content-Length: 1868
< Content-Type: application/json; odata.metadata=minimal
< Server: Microsoft-HTTPAPI/2.0
< X-Content-Type-Options: nosniff
< Set-Cookie: XSRF-NONCE=EYQGqwO3ZSX4wJ4gAt7zUvHyoCxWWoFI%2B0I4m3zkliw%3D; path=/reports; HttpOnly
< Set-Cookie: XSRF-TOKEN=deprecated; path=/reports
< OData-Version: 4.0
< Date: Mon, 09 Dec 2024 18:31:20 GMT
<
{
"@odata.context":"http://[some ip]/reports/api/v2.0/$metadata#Reports","value":[
...
]
* Connection #0 to host [some ip] left intact
HttpClient failure
Attempting to perform same action but from C# Core Web App (hosted on same Azure Web App as web SSH Shell above), we use
// test endpoint
[AllowAnonymous]
[HttpGet("report")]
public async Task<ActionResult> GetReport([FromQuery] string password)
{
string user = "SomeUser";
string domain = "SomeDomain";
string url = "http://[some ip]";
HttpClient client = new HttpClient(
new HttpClientHandler
{
Credentials = new CredentialCache
{
{
new Uri(url),
"NTLM",
new NetworkCredential(user, password, domain)
}
},
})
{
BaseAddress = new Uri(url),
};
HttpResponseMessage? result = null;
Exception? exception = null;
try
{
result = await client.GetAsync("/reports/api/v2.0/Reports");
}
catch (Exception e)
{
exception = e;
}
return Ok(
new
{
client.BaseAddress,
client.DefaultRequestHeaders,
user,
domain,
password,
url,
Result = result,
Exception = exception?.ToString(),
});
}
we get output like
{
"baseAddress": "http://[some ip]",
"defaultRequestHeaders": [
{
"key": "Expect",
"value": [
"100-continue"
]
}
],
"user": "SomeUser",
"domain": "SomeDomain",
"password": "SomePassword",
"url": "http://[some ip]",
"result": {
"version": "1.1",
"content": {
"headers": [
{
"key": "Content-Length",
"value": [
"0"
]
}
]
},
"statusCode": "Unauthorized",
"reasonPhrase": "Unauthorized",
"headers": [
{
"key": "Server",
"value": [
"Microsoft-HTTPAPI/2.0"
]
},
{
"key": "WWW-Authenticate",
"value": [
"NTLM"
]
},
{
"key": "Date",
"value": [
"Mon, 09 Dec 2024 18:49:16 GMT"
]
}
],
"trailingHeaders": [],
"requestMessage": {
"version": "1.1",
"versionPolicy": "RequestVersionOrLower",
"method": {
"method": "GET"
},
"requestUri": "http://[some ip]/reports/api/v2.0/Reports",
"headers": [
{
"key": "Expect",
"value": [
"100-continue"
]
},
{
"key": "Request-Context",
"value": [
"appId=cid-v1:e81bb4ec-82a5-4ff3-a0fe-37bdc20cf3e6"
]
},
{
"key": "Request-Id",
"value": [
"|f49543737031b257e9269e9f2fe9e719.19a134c67a9ab46f."
]
},
{
"key": "traceparent",
"value": [
"00-f49543737031b257e9269e9f2fe9e719-19a134c67a9ab46f-00"
]
}
],
"properties": {},
"options": {}
},
"isSuccessStatusCode": false
}
}
Questions
I believe that this note: https://learn.microsoft.com/en-us/dotnet/api/system.net.networkcredential?view=net-8.0#remarks
Means that NetworkCredential with a provided password only works for BASIC auth with this library. For NTLM and Kerberos it uses the identity of the running program. Examine the outgoing auth header to confirm.
If you are comfortable sending the password to the report server, you can switch to HTTP Basic auth.
And if this is Windows you can set UseDefaultCredentials = true and either run the process as or impersonate the target user, or inject the username/password into the Windows Credential Manager.