Call a URL without DNS Entry from Azure Function .Net [duplicate] - c#

Basically, I need to be able to make an HTTP Request to a Website on the same machine I am on, without modifying the host file to create a pointer to the domain name.
For example.
I am running the code on one website, let's say www.bobsoft.com which is on a server.
I need to make an HTTP request to www.tedsoft.com which is on the same server.
How can I make a call using a C# HttpClient without modifying the host file? Take into account that the websites are routed by bindings in IIS. I do know the domain I am going to use ahead of time, I just have to make it all internal in the code without server changes.
Thanks!

IIS bindings on the same port but different hostnames are routed based on the http Host header. The best solution here is really to configure local DNS so requests made to www.tedsoft.com don't leave the machine. That being said, if these kinds of configuration aren't an option you can easily set the host header as a part of your HttpRequestMessage.
I have 2 test sites configured on IIS.
Default Web Site - returns text "test1"
Default Web Site 2 - returns text "test2"
The following code uses http://127.0.0.1 (http://localhost also works) and sets the host header appropriately based on the IIS bindings to get the result you're looking for.
class Program
{
static HttpClient httpClient = new HttpClient();
static void Main(string[] args)
{
string test1 = GetContentFromHost("test1"); // gets content from Default Web Site - "test1"
string test2 = GetContentFromHost("test2"); // gets content from Default Web Site 2 - "test2"
}
static string GetContentFromHost(string host)
{
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1");
msg.Headers.Add("Host", host);
return httpClient.SendAsync(msg).Result.Content.ReadAsStringAsync().Result;
}
}

Related

Azure Function App - HttpContext IServerVariablesFeature not supported in local environment

I am running an azure function app with runtime 4, .NET 6.0.
When I call the function app in a locally running environment the req.HttpContext.GetServerVariable() always returns null because the IServerVariablesFeature is not supported in the list of context.Features.
When I deploy this to an azure hosted instance of the function app, the variables are populated correctly. I have done extensive research and have been unable to find out if this is intentionally not supported or if I am missing some form of additional configuration in my local environment.
Here is a test function that attempts to read 3 different server variables and returns them as a string and can be called using a get request in postman or via a browser http://localhost:7071/api/GetServerVariable:
public static class TestFunction
{
[FunctionName("GetServerVariable")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "GetServerVariable")] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var url = req.HttpContext.GetServerVariable("URL");
var remoteAddr = req.HttpContext.GetServerVariable("REMOTE_ADDR");
var https = req.HttpContext.GetServerVariable("HTTPS");
var response = $"Current server variables: URL: {url} - REMOTE_ADDR: {remoteAddr} - HTTPS: {https}";
log.LogWarning(response);
return new OkObjectResult(response);
}
}
According to this document, GetServerVariable returns
null if the server does not support the IServerVariablesFeature feature. May return null or empty if the variable does not exist or is not set.
One of the workaround is to include Forwarded headers Middleware:
Before the request reaches the app, proxy servers, load balancers, and other network appliances usually hide information about the request. The original scheme is lost when HTTPS requests are proxied via HTTP and must be transmitted in a header. The originating client IP address must also be forwarded in a header because an app receives a request from the proxy rather than its true source on the Internet or corporate network.
From the MSDN article:
Although retrieving just the REMOTE_ADDR server variable should be enough, I found resources online that suggested that code like this should also check the HTTP_X_FORWARDED_FOR variable; if the request comes through a proxy server that translates the address, it's this variable that contains the correct address. If you request a server variable that doesn't exist, the ServerVariables property returns an empty string. Therefore, even though this property doesn't appear in my tests, attempting to retrieve its value doesn't cause trouble.
The Forwarded Headers Middleware reads the headers X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto and fills in the associated fields on HttpContext.
REFERENCES:- How to access server variables in ASP.Net Core

C# SoapHttpClientProtocol - set local binding address (source address)

I am trying to make a Soap call to a webservice hosted by business partner. The problem I am having is that our client is hosted on a virtual PC with multiple IPs. I would like to call the hosted webservice with a different (not default) IP address. Why is that? There is VPN connection between our client application and hosted webservice but it is set up LAN2LAN between two IPs. Now I have to change local source IP address to match with the VPN requirements.
I have tried using SoapHttpClientProtocol's WebProxy, setting its value to the new IP but it does not seem to work. I am getting HTTP 404 error.
Here is some of the code:
//soapApiClient is typeof SoapHttpClientProtocol
//endpoint url -> webservice, url from appSettings
var url = SettingsProvider.ClientSapGetUserDataUrl;
soapApiClient.Url = url;
//proxy settings -> setting new IP, defined in appSettings
var proxy = SettingsProvider.ClientProxyAddress;
soapApiClient.Proxy = new WebProxy(proxy);
//credentials
soapApiClient.Credentials = GetCredentials();
I HTTP post user code and return value should be user data from SAP, currently I am getting 404 http error code. For comparison, it works like a charm from SoapUI. Maybe setting proxy is not what I am looking for? Any help would be much appreciated.
EDIT: To be more clear
Currently, by default service is called from default ip 91.185.201.88. Service's IP is 10.67.145.70. I want to change it like so: service is called from 192.168.4.2 to service's 10.67.145.70.
I finally managed to make it work. If anyone else will have problem with this, just follow these instructions.
Before calling remote service, you have to find service point for it. This is done via ServicePointManager like so
Example:
var servicePointUserReader = ServicePointManager.FindServicePoint(new Uri(FULLY QUALIFIED REMOTE SERVICE URL));
servicePointUserReader.BindIPEndPointDelegate = (sp, remote, retryCount) => new IPEndPoint(IPAddress.Parse(SOURCE IP FROM WHICH YOU WANT TO SEND REQUEST), 0);
In my example, fully qualified remote service url was something like http://65.145.63.71:8010/sapService (this is the one we are trying to call). Then I wanted to make a call from a different IP on our virtual PC which has many IPs addressed to it. You just need to input desired IP as shown in the second line of the code, for example 192.168.5.1.
Make sure you use "http://" when calling FindServicePoint with new Uri() constrcutor otherwise it will not work!
After that just proceed with the call to the service. Two lines of code, that's all you need. :)

Check is a request is coming from the server local to the app with OWIN

I need to create an AuthroizationFilter for my Hangfire Dashboard.
Its running on an Azure VM and by design should only accept request from local requests.
I want to create an AuthorizationFilter that validates only those requests from the web browser on the same VM as the web app is running on.
I need to determine this form the OwinContext :-
public class MyRestrictiveAuthorizationFilter : IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
// In case you need an OWIN context, use the next line,
// `OwinContext` class is the part of the `Microsoft.Owin` package.
var context = new OwinContext(owinEnvironment);
// Allow all local request to see the Dashboard
return true;
}
}
I had a similar issue working on a custom CookieAuthenticationProvider where only IOwinContext was exposed. context.Request did not expose an IsLocal property, however the following was available:
context.Request.Uri.IsLoopback
The property was true for 127.0.0.1 and localhost requests.
What about checking the headers in the request context?
context.Request.Headers["Referer"]
If the value includes localhost or 127.0.0.1 or whatever you're restricting it to.
CAUTION: this header can be spoofed. There may be a better way to limit access to the dashboard. Does it need internet access?
Can you use HttpRequest.IsLocal? This will tell you if the IP address of the request is the same as the server's IP address.
return context.Request.IsLocal;

Is it possible to spoof a hosts file entry within a web application?

I have a rest application sitting on an IIS server (on the public internet) with a configuration as follows:
IP Address: A.B.C.D
Host Header: something-not-public-dns.example.com
(The entry isn't in the public dns simply because it doesn't have to be. This isn't merely security by obscuring host headers.)
In order to get my client to connect to the application, I simply add an entry in my hosts file:
A.B.C.D something-not-public-dns.example.com
With the hosts file entry everything works great.
The problem is one of my clients is sitting inside of an ASP.NET web application, and I cannot modify the hosts file for the web server the application resides on.
Thus my question- is it possible to spoof a hosts file entry inside of a web application?
I was thinking of something like this:
string url = "something-not-public.example.com/myservice/mymethod";
var client = new WebClient();
client.Headers[HttpRequestHeader.ContentType] = "application/json";
// Set the destination IP for this request here!
string json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(MyObject);
string response = client.UploadString(url, json);
I would consider the above example request level spoofing, which would be great, but I'm also curious to know if application level spoofing could be done as well. This way the application could set up the host entries and all requests made from within that application would use them.
You're going about this the wrong way around - instead of trying to setup a mapping from the domain name to the IP address you should simply make the request to the IP and explicitly override the Host header of your HTTP request with the domain name you want to use:
string url = "A.B.C.D/myservice/mymethod";
var client = new WebClient();
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.Headers[HttpRequestHeader.Host] = "something-not-public.example.com";

Is it possible to get the hostname(sitename) at the time of registering middleware in Owin startup?

I have a middleware that needs options object to start. One of the properties is hostname.(I mean its to be set up like http://mywebsite.com and not the physical machine name). The middleware is configured to do some business logic at startup and so app.Run() and app.Use() isn't an option.
I also cannot hardcode hostname(as I run multiple instances with different site names), or get it from the config file, because:
1. its duplication of information which I could otherwise get from database, if only I knew the hostname
2. I wouldn't still be sure which one out of the many site instances I have, was invoked
var options = new MyMiddlewareOptions{
hostname = <hostname of the server hosting this app>,
.
..
...
//other properties
};
app.UseMyMiddleware(options);
How can I get the hostname at the time of Owin Startup since there is no request or Environment data available?
I have tried using inline middleware, and I get the hostname but by then its too late.
something on the lines of:
app.Use((ctx,next) =>
{
host = ctx.Request.Uri.Host;
var options = new MyMiddlewareOptions
{
hostname = host
};
app.UseMyMiddleware(options);
return next.Invoke();
}
The above does give me the Hostname, but it is too late to register a new middleware when a request is already in progress and therefore it doesn't really do things it should.
Is there a way to get the hostname at the time of startup when middlewares are getting registered?
There is no absolute way of getting the host name until the first request has been received. It depends on what you're hosting the site on. When it's self hosting, you can do app.Properties["host.Addresses"]. IIS Express however is a different story - that key for a start doesn't exist (I don't if there is a way at all).
Use the app.map('pathYouWant', methodYouNeed);
I have a example in Portuguese using this method.
Access here, you can learn more about this.

Categories