Closed Sayan751 closed 1 year ago
Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones See info in area-owners.md if you want to be subscribed.
Author: | Sayan751 |
---|---|
Assignees: | - |
Labels: | `api-suggestion`, `area-System.Net.Security`, `untriaged` |
Milestone: | - |
Thanks @MihaZupan for the related issue! It is great to know that such telemetry for HTTP request as well as for Kestrel exist. I am referring to this https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/networking-telemetry.
However, I am a bit lost in the sea of telemetry metrics. Also, I could not unfortunately locate the information I need in those metrics, and those seem to be purely statistical data. If I am missing anything, can you please kindly point out to a fuller example that I can leverage for my use case?
A little more information on why we need this information. We're using the HttpClient to send documents to partners in the energy sector. This area is highly regulated and we need to make sure (and document) that we only use the permitted security settings. This includes the TLS version used and the negotiated cipher suite and the server certificates used.
For inbound messages we're able to extract this information from Kestrel in a safe way, but with the HttpClient we currently need to use Reflection. As @Sayan751 pointed out, this removes the option to use AOT and further we're now dependent on the .NET team not to change the internals we rely on.
Starting with .NET 7.0, you can set the SocketsHttpHandler.ConnectCallback
to a custom implementation that logs information about what parameters were used/negotiated for a specific connection/host.
using var handler = new SocketsHttpHandler();
using HttpClient client = new(handler);
handler.ConnectCallback = async (context, ct) =>
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
try
{
await socket.ConnectAsync(context.DnsEndPoint, ct);
var sslStream = new SslStream(new NetworkStream(socket, ownsSocket: true));
// Use whatever options you want (e.g. handle client certs)
// When using HTTP/2, you must also keep in mind to set things like ApplicationProtocols
var options = new SslClientAuthenticationOptions
{
TargetHost = context.DnsEndPoint.Host
};
await sslStream.AuthenticateAsClientAsync(options, ct);
// Log all the things you care about
Console.WriteLine($"Connection to {socket.RemoteEndPoint} for {context.DnsEndPoint.Host} is using {sslStream.NegotiatedCipherSuite}");
return sslStream;
}
catch
{
socket.Dispose();
throw;
}
};
Connection to [::ffff:54.175.87.239]:443 for httpbin.org is using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
If you also want to tie that information to each individual request, it gets a bit more complicated.
As of .NET 8.0, the only real option available is to use the newly added EventSource
events that allow you to correlate requests & connections to some degree.
Example.cs
is a modified example from https://github.com/dotnet/runtime/issues/63159#issuecomment-1515432131.
ConnectCallback
like shown above to gather the connection info and store it into an AsyncLocal
.EventListener
that tracks which connections were established, and what security parameters were negotiated for them (it gets this info from that AsyncLocal
).Request to https://httpbin.org/get used a connection with TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
If you don't want to hook up to the EventListener, you also have an option to create HttpClient
with just 1 connection and then store the associated SslOptions
from ConnectCallback
for it. Whenever you use the HttpClient, you will have the info handy.
You will be in charge of load balancing requests among the connections (each represented with HttpClient), so there will be more work there.
Overall, looks like it is answered, so closing.
Background and motivation
I have a use case, where when I am sending messages via an HTTP endpoint, I need to know the following information:
At this point, it is extremely cumbersome to procure this information. For example, in .NET 7.0 I am using a wrapper over an HTTP client, the holds a reference to my
SslClientAuthenticationOptions
, which uses reflection to extract theInnerStream
from theSslStream
(sender
in theRemoteCertificateValidationCallback
) sets the reference of theSslStream
in theInnerStream
which happens to be my customNetworkStream
. The use of reflection also means that my code won't be AOT compatible.From my point of view, it would have been a lot simpler if this information were available via the
HttpResponseMessage
.API Proposal
API Usage
See above.
Alternative Designs
No response
Risks
No response