HttpClient request on networked docker container - c#

I am new to docker containers and Im running into the following issue. I am developing a web page that makes requests to an exposed api. Both of them are published on different containers that are linked through a network.
This is my docker-compose.yml file:
version: '2'
services:
api:
image: my/api
networks:
- api
ports:
- "8080:5000"
container_name: api
web:
build:
context: ./DockerWeb
networks:
- api
ports:
- "80:5000"
container_name: web
networks:
api:
driver: bridge
I have noticed that if I attach to the container running my webpage I can ping the api container without any issue using ping api
The issue comes when trying to make any web service call to the api container. I am trying to initialize an HttpClient object, however the BaseAddress property of such an object must be set to a valid Uri.
_client = new HttpClient();
_client.BaseAddress = new Uri(config.ApiUrl); //config.ApiUrl = "api"
This throws the following error
UriFormatException: Invalid URI: The format of the URI could not be determined.
Is there a way to either a) Expose the container name in a different way, or b) make a call to a web service without the need of using a URI?
How can I accomplish this?
UPDATE
I have updated the ApiUrl to contain the value http:api:8080, however now I am getting an exception when performing the call
System.AggregateException: One or more errors occurred. (An error occurred while sending the request.) ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.Http.CurlException: Couldn't connect to server
I have also tried the calling the api endpoint directly from my web container using wget
wget http://api:8080/
However I still receive back an error:
Connecting to api (api)|172.24.0.4|:8080... failed: Connection refused
Could this be an API configuration error?

api is just the hostname. The URI would be http://api:8080 (or https://api:someotherport if you set up SSL/TLS.)

The internal port is used for connections inside the docker network. So in this case it is the request needs to go to http://api:5000

Here there is 2 kinds of communication:
Container web > Container api
In this case, indeed, you can just use the hostname "api" to connect to your api.
This is what you would have to do if your html is generated server side.
Web Client > Container api
Here however I suppose that you are using some kind of JavaScript web application (Angular or other). In that case the html (and the call) is not made from the web container but directly from your web browser.
So in that case you have to use http://localhost:8080 (I suppose that Docker is running on your local machine).
I hope it helps

Related

Request between pods on kubernetes returning null

Hi there I am trying to communicate two aplications (both APIs) in the same cluster on kubernetes(from openshift) but in a different namespace...
When I send the httpclient request using regular URL I get a SSL error because the route "leaves" the internal network and "comes back". With a advice I changed the comunication using my service name (mapped on the pods)... Now I don't get the error but the HTTRESPONSE I gete is Null, with no error or statuscode:
Here is how I configured the service name:
http://servicename.projectname.svc.cluster.local:8080/
If i change the port to 8080 or use http I get the error "no route to host" but the route is set. But the HOST field below is set to the URL and not the cluster host, could the problem be this?
Solved by setting the certificate in the pipeline (yaml) so it is applied in the pods

Self-hosted SignalR in Windows .Net Service blocked by CORS on same server, works on other servers

I've been using a Self-Hosted SignalR Windows service accessed from multiple production servers (now in Azure) for 6+ years without a problem. I created an identical server for development in Azure but when I'm accessing SignalR from a browser on the SAME SERVER, SignalR gives me the following error when using either http:6287 or https:6286:
Access to XMLHttpRequest at 'http://myserver.learn.net:6287/signalr/negotiate?clientProtocol=1.5&xxxxxxx' from origin 'http://myserver.learn.net' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
However... It WORKS when connecting from OTHER SERVERS! I'm starting the connection with no errors using:
SignalR = WebApp.Start("http://myserver.learn.net:6287/");
SignalRSSL = WebApp.Start("https://myserver.learn.net:6286/");
(also SignalR = WebApp.Start("*:628x/" for both);
In my client code, I include the following script:
<script src="http://myserver.learn.net:6287/signalr/hubs"></script>
When I enter that url (or https version) in a browser ON THE SAME OR DIFFERENT SERVER, it shows the ASP.NET SignalR JavaScript Library v2.3.0-rtm page correctly! I've turned off the firewall with no change, added Microsoft.Owin.Host.HttpListener (someone suggested). I have also entered the wildcard certificate with netsh so the SignalR service can deal with the SSL connection using:
netsh http add sslcert ipport=0.0.0.0:6286 appid={12345678-db90-4b66-8b01-88f7af2e36bf} certhash=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Edit: I've also tried changing the ipport value to the real internal IP of the server as well as the public IP but no change.
So, why can't I access SignalR from the same server?
I found a solution in another answer here that worked. I changed:
$j.connection.hub.start().done(function () {
To:
$j.connection.hub.start({ jsonp: true, xdomain: true }).done(function () {
Which worked for both internal and external clients. xdomain:true alone didn't work but when I added jsonp:true it did. I have no real idea why, just that it's working now.

Which environment variable to use on Heroku for services that only listen to https

I'm setting up an https only web API service using AspNet core which is to be deployed on Heroku. The deployment is done via docker, so I have a Dockerfile configured. But I'm getting an error relating to the https port binding whenever I deploy to Heroku. I want to know if Heroku provides a special env variable for https where a service can bind to specify the https port the service should listen to.
AspNet core provides 2 environment variables ASPNETCORE_HTTPS_PORT=5001 and ASPNETCORE_URLS=https://*:5001 where one can specify the https port the service should listen to.
But since Heroku does not allow a web process to manually specify the port it should listen to, they provide a PORT env variable the web app can bind to.
I've tried using ASPNETCORE_HTTPS_PORT=$PORT and ASPNETCORE_URLS=https://*:$PORT, but I'm getting the following errors:
crit: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to start Kestrel.
System.Net.Sockets.SocketException (13): Permission denied at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, String callerName)
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.<>c__DisplayClass21_0`1.<<StartAsync>g__OnBind|0>d.MoveNext()
So I was able to resolve the issue after going through Heroku's documentation (extensively). Here are the key findings relating to this problem:
Heroku uses a load balancer which proxies all its request.
Heroku terminates any SSL communication from your app, so all requests are forwarded to your app over http; your clients' https requests are proxied over http.
Since the requests are proxied, Heroku passes Forwarded headers for the originating request.
Your app can only receive requests and should only listen to the PORT env variable.
Now that i have the in mind, I was able to configure my app correctly.
Listening on http not https: ASPNETCORE_URLS=http://*:$PORT
Using FowardedHeaders and Rewriter middlewares in AspNet core middleware pipeline:
var forwardedHeadersOptions = new ForwardedHeadersOptions {
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardedHeadersOptions.KnownNetworks.Clear();
forwardedHeadersOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardedHeadersOptions);
var rewriteOptions = new RewriteOptions ().AddRedirectToHttps(308);
app.UseRewriter(rewriteOptions);
...
The ForwardedHHeaders middleware maps the Forwarded headers to HttpContext.Request. And the Rewriter middleware will redirect http request scheme to https.

Azure App Service terminating https before application? [duplicate]

This question already has answers here:
Is current request being made over SSL with Azure deployment
(2 answers)
Closed 3 months ago.
I'm building an asp.net core web application on framework 2.2 and hosting on an azure app service on a linux app service plan.
Inside my application I inspect HttpRequest.Scheme. Running locally this returns https if I make a request using https. Running on azure it returns http.
It appears Azure App Services is terminating the SSL connection and proxying to my app. Is there a way to configure Azure App Services so the https request makes it to my application unmodified? Or at least HttpRequest.Scheme matches the original request?
I've built a sample diagnostic page to show this behavior:
var healthStatus = new
{
Port = context.Request.Host.Port?.ToString() ?? "unknown",
context.Request.Scheme,
context.Request.IsHttps,
Headers = context.Request.Headers.Select(x => $"{x.Key}:{x.Value}").ToArray()
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(healthStatus));
Debugging in VS Locally: https://localhost:1234/ping:
{
"Port":1234,
"Scheme": "https",
"IsHttps": true,
"Headers": <standard headers - nothing interesting>
}
Deploying to Azure App Services: https://appServiceExample.myDomain.com/ping:
{
"Port":"unknown",
"Scheme": "http",
"IsHttps": false,
Headers: [
// there are several more headers, but only these looked interesting:
"X-Forwarded-For:195.206.xxx.xxx:6922",
"X-Forwarded-Proto:https",
"X-AppService-Proto:https"
]
}
As a workaround: Could I solve this problem my relying on the X-AppService-Proto or X-Forwarded-Proto header? But this seems a bit of a hack, as I'd rather inspect the original incoming request - and I'm unsure how reliable these headers are.
Just summarize your comment.
The Azure App Service frontend layer TERMINATES the TLS channel (aka TLS offloading) and opens a new plain HTTP connection to your Web Worker, where your code lives. Routing is performed by ARR (Application Request Routing).
Therefore, from the point of view of your code every single request is "insecure".
X-Forwarded-Proto=https hints about the original request (that hit the frontends).
If checks have to be made, make them against X-ARR-SSL instead.
For more details, you could refer to this SO thread.

asp.net core 2.0 deployment - InvalidOperationException: The antiforgery token could not be decrypted

Recently I developed a asp.net core 2.0 web app in my company and in debug mode works perfect, however when I deployed in our testing server into IIS and we try to execute from a client machine it ran into a problem:
An unhandled exception occurred while processing the request.
CryptographicException: The key {0851ad3b-df33-4cf7-8c3a-5c637adaa713} was not found in the key ring.
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
InvalidOperationException: The antiforgery token could not be decrypted.
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenSerializer.Deserialize(string serializedToken)
The problem starts when I submmit login page. I investigated links with same problems here and other blogs, but I found that has to be with ValidateAntiForgeryToken and solution is related with Microsoft.AspNetCore.DataProtection. I added nuget package Microsoft.AspNetCore.DataProtection.Redis to my project and I added in ConfigureServices of startup class following code:
var redis = ConnectionMultiplexer.Connect("192.168.10.151:80");
services.AddDataProtection().PersistKeysToRedis(redis, "DataProtection-Keys");
services.AddOptions();
Our testing server ip is 192.168.10.151, however app throws following exception:
RedisConnectionException: It was not possible to connect to the redis server(s); to create a disconnected multiplexer, disable AbortOnConnectFail. InternalFailure on PING
¿Why it doesn't connect since is resolving in the same web app server?
¿Where is DataProtection-Keys database located?
as a workaround, I changed method by using PersistKeysToFileSystem as follows:
services.AddDataProtection()
.SetApplicationName("myapp-portal")
.PersistKeysToFileSystem(new System.IO.DirectoryInfo (#"c:\ProgramData\dpkeys"));
However running app in test server 192.168.10.151, when login form is submitted, goes back to login page. Checking stdout log file, only shows:
Hosting environment: Production
Content root path: C:\inetpub\wwwroot\OmniPays
Now listening on: http://localhost:30064
Application started. Press Ctrl+C to shut down.
Checking network messages by chrome's developers tools I noticed something:
Request URL: http://192.168.10.151/OmniPays/Account/Login
Request Method: POST
Status Code: 302 Found
Remote Address: 192.168.10.151:80
Referrer Policy: no-referrer-when-downgrade
and then ...
Request URL: http://192.168.10.151/OmniPays/Home/Main
Request Method: GET
Status Code: 302 Found
Remote Address: 192.168.10.151:80
Referrer Policy: no-referrer-when-downgrade
AccountController's Login action redirect request to HomeController's Main action only if authentication succeded, and Main action has [Authorize] attribute. For some reasons I can't achieve understand, Main action fails and return to Login page. URL in chrome shows: http://192.168.10.151/OmniPays/Account/Login?ReturnUrl=%2FOmniPays%2FHome%2FMain
I'm using Microsoft Identity. In debug mode works fine and if I deploy app in my local PC on IIS also works fine. ¿Maybe any SDK is missing in the server?
Please need help!!
Solution was found! the cause of problem was not in IIS neither the Server, connection to the server is using http rather than https, no certifies involved to validate secure connection, however testing in differents servers app works ok, so I felt really disappointed. Solution was to remove cookies an any data related with this URL pointing to Development Server (failing) in all browsers, data that was previously stored, and voila!!, now app works perfect. By default, as bhmahler comments data protection is made in memory and I left configuration by default, I mean, not explicitly persistence in redis nor PersistKeysToFileSystem and works fine, however is important to set DataProtection to strong data sensitive protection.
I'm newbie about these topics and It's unbelievable such a simple thing caused on me that waste of time. Thanks to all!.

Categories