gRPC Server with SSL on .NET6 and OpenSSL - c#

gRPC server part in .NET 6 also looks different, now there is no Startup.cs, only Program.cs and all the examples I found go through creating a new instance of the SERVER class. But what should it look like if I use .NET 6 (Kestrel)?
This is "server" default code with one sevice (MyTestService) of .NET 6 Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGrpcService<MyTestService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
This is the solution for the client from the official gRPC documentation:
var channelCredentials = new SslCredentials(File.ReadAllText("roots.pem")); // Load a custom
roots file.
var channel = new Channel("myservice.example.com", channelCredentials);
var client = new Greeter.GreeterClient(channel);
But there is no server solution.
UPDATED - CODE FOR CLIENT for gRPC .NET6:
string certificatePem = File.ReadAllText("clientcrt.pem");
string privateKeyPem = File.ReadAllText("clientkey.pem");
var cert = X509Certificate2.CreateFromPem(certificatePem,
privateKeyPem);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(cert);
using HttpClient httpClient = new(handler);
var channel = GrpcChannel.ForAddress("https://0.0.0.0:5000", new GrpcChannelOptions
{
HttpClient = httpClient
});
var grpc = new Test.TestClient(channel);

You can still configure the Kestrel like you used to do with the new "simplified" .NET 6 layout, like it is explained in the MS Docs here. So, for the server-side Program.cs you posted, you can just configure the builder to use TLS. For example, if you have a certificate "server_certificate.pfx" for the server in the default code you posted, configure the builder like this:
// the code you posted, but with Kestrel configuration
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
// configure the builder to use the TLS certificate
builder.WebHost.ConfigureKestrel(opt =>
{
string file = "server_certificate.pfx";
string password = "P#ssw0rd!";
var cert = new X509Certificate2(file, password);
opt.ConfigureHttpsDefaults(h => {
// Choose RequireCertificate instead of AllowCertificate if it is required
h.ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode.AllowCertificate;
// this checks whether the certificate has been signed by some greater authority
h.CheckCertificateRevocation = false;
h.ServerCertificate = cert;
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGrpcService<MyTestService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
Another option is to just use the old .NET 5 programming style for the Program.cs in .NET 6 (which I prefer), like it is described here in the MS Docs for console apps. An example for how this could look like is this Program.cs file, which can be found in the repo that #Mihal By linked in the comments. If you go back to the old style, you can also just write your own Startup.cs for the Kestrel like you used to.

Related

APIM endpoint is not working through latest elastic client library

I am using latest "Elastic.Clients.Elasticsearch" library in .NET console application to talk to my elastic latest version 8.X. However, in my scenario I will not be talking directly to elastic I have a middle layer APIM endpoint. This is working fine when I am using NEST package with EnableAPIVersioningHeader setting. But in case of new library it throws 404 "resource not found error". Please can you let me know what are the changes that needs to be done to get this working.
Note: I am removing NEST package dependency from code, as Elastic will not support it in the future.
Sample Code:
public static ElasticsearchClient NewSearchClusterClient
{
get
{
var connectionSettings = new ElasticsearchClientSettings(new Uri("<apimendpoint>"));
connectionSettings.MaximumRetries(5);
connectionSettings.DefaultIndex("test");
connectionSettings.IncludeServerStackTraceOnError(true);
connectionSettings.EnableTcpStats(true);
connectionSettings.DisableDirectStreaming(true);
NameValueCollection collection = new NameValueCollection
{
};
connectionSettings.GlobalHeaders(collection);
var client = new ElasticsearchClient(connectionSettings);
return client;
}
}
Call this --> var respone = NewSearchClusterClient.Search(q => q.Query(m => m.MatchAll()));

SignalR is reconnecting every view seconds in .net 3.1 core

I'm using SignalR under .net 3.1 in a kubernetes cluster. When connecting to the cluster, my SignalR client is reconnecting every 1 - 5sec. My current setup:
.net 3.1 client --> Ambassador-Api-Gateway -> K8S -> Docker-Container
When starting everything locally, I've no problems with to many reconnects. The client connects to the service using the following setup:
Connection = new HubConnectionBuilder()
.WithUrl($"{clientBase.Url}/{ClientBase.ApiVersion}/{api}-api/{hubName}", options =>
{
options.AccessTokenProvider = () => Task.FromResult(clientBase.User.Token);
})
.WithAutomaticReconnect()
.Build();
Connection.ServerTimeout = TimeSpan.FromSeconds(30);
Connection.KeepAliveInterval = TimeSpan.FromMilliseconds(100);
I've tried to reduce the KeepAliveInterval and increase the ServerTimeout but this didn't help.
Based on this link
You can skip the negotiation part like
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/myHub", options => {
options.SkipNegotiation = true;
options.Transport = SignalR.HttpTransportType.WebSockets;
})
.Build();
And also, for broadcasting to all clients but connected to different pods, you can use Backplane using
services.AddSignalR().AddStackExchangeRedis("<connection_string>");

How to make HttpClient to use the app pool identity

I'm trying to make an http call from website A to website B, using the website A's identity.
Using .Net fwk 4.x, I just have to make something like that:
using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) {
// Do something
}
In aspnetcore 2.2, an IHttpClientBuilder has been provided to manage http clients.
The same code is supposed to look to something like this:
services.AddHttpClient("myOtherSite", httpClient => {
httpClient.BaseAddress = new Uri("http://something");
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {
UseDefaultCredentials = true
});
Then I just need to inject the IHttpBuilder where I need a client then call clientBuilder.buildClient("myOtherSite").
Using this, http request seems to be made without the pool user, despite the documentation..
Has someone done something like this?
I'm not sure if this is your issue but this Microsoft Documentation states that starting with core 2.1, the System.Net.Http.SocketsHttpHandler class is used instead.
It has information on how to configure to allow continuing use of HttpClientHandler but you may want to switch or even try using HttpMessageHandler in place of the type you're using for the handler currently.

Request.HttpContext.Connection.ClientCertificate is always null

I have an ASP.Net core website deployed on Azure app service for Linux.
In the controller, I am trying to get the client certificate like below:
var callerCertificate = Request.HttpContext.Connection.ClientCertificate;
I always get callerCertificate as null.
I have tried await Request.HttpContext.Connection.GetClientCertificateAsync() with same result null.
My website webhost creation looks like below:
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseStartup<Startup>()
.UseSerilog();
I have also set SSL setting for the website (in Azure) as below:
The client side caller is a net462 project that uses Microsoft.Rest.CertificateCredentials to set the certificate to HTTP request.
var cred = new CertificateCredentials(_deviceCertificate)
...
await this.cred.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
You could try to add the certificate using HttpClient directly instead of using Microsoft.Rest.CertificateCredential.
var clientHandler = new HttpClientHandler();
clientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
clientHandler.ClientCertificates.Add(_deviceCertificate);
var client = new HttpClient(clientHandler);
var result = client.GetAsync("https://yourservice").GetAwaiter().GetResult();
You may also need to configure the SSL protocol (SSL2, SSL3, TLS, etc.):
clientHandler.SslProtocols = SslProtocols.Tls;
Answering my own question:
I am able to get the client certificate from header
string clientCertFromHeader = Request.Headers["X-ARR-ClientCert"];
Though, it is still a mystery as to why Request.HttpContext.Connection.ClientCertificate is not giving the certificate.

Connect to signalR hub from c# web api

I've been tasked with trying to move our signalR hub to an azure cloud service with a service bus backplane. No problems there. The javascript client is able to get the hubs.js and connect without errors. We also have a web api project that needs to send messages to the hub but I cannot get it to connect. Everything I've tried doesn't work and the connection times out. I must be missing something but, since this is my first time working with signalR and Azure, I don't know what it is. The web api is hosted on IIS.
Here is the code I am trying to use to connect:
private async void InitializeHub()
{
string connectionString = "http://xxxx-xxxxx.cloudapp.net/signalr";
var hubConnection = new HubConnection(connectionString, useDefaultUrl: false);
//var hubConnection = new HubConnection(connectionString);
HubProxy = hubConnection.CreateHubProxy("clientPortalHub");
await hubConnection.Start();
}
Is there some configuration I am missing? Anything need to be done in IIS? I'm not getting any helpful error messages, just that the connection times out. I can hit the url (the real one, not the one I pasted) in a browser and get "Unknown transport".
If it helps here is the startup from the hub:
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
string connectionString = "<omitted>";
GlobalHost.DependencyResolver.UseServiceBus(connectionString, "clientPortalHub");
app.Map("/signalr", map =>
{
map. UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
hubConfiguration.EnableDetailedErrors = true;
map.RunSignalR(hubConfiguration);
});
}

Categories