-
Notifications
You must be signed in to change notification settings - Fork 305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #197 - AWSEKSResourceDetector fails to detect resources due to exception "The SSL connection could not be established" #208
Fix #197 - AWSEKSResourceDetector fails to detect resources due to exception "The SSL connection could not be established" #208
Conversation
@@ -56,6 +56,12 @@ public void FailedToInjectActivityContext(string format, string error) | |||
this.WriteEvent(2, format, error); | |||
} | |||
|
|||
[Event(2, Message = "Failed to validate certificate in format: '{0}', error: '{1}'.", Level = EventLevel.Warning)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should event id here be 4 (same as few lines bellow)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
{ | ||
var clientHandler = new HttpClientHandler() | ||
{ | ||
ClientCertificateOptions = ClientCertificateOption.Manual, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we still need it. ClientCertificateOptions
(with ClientCertificates
)used in case if client need to be authenticated on server with SSL certificate (mutual SSL) - and not in case if we need to validate server certificate only.
{ | ||
foreach (var certificate in collection) | ||
{ | ||
if (chainElement.Certificate.GetPublicKey().SequenceEqual(certificate.GetPublicKey())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use LINQ instead
Enumerable.SequenceEqual(chainElement.Certificate.GetPublicKey(), certificate.GetPublicKey())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree. And that way we can remove the extension method OpenTelemetry.Contrib.Extensions.AWSXRay.Resources.Http.X509Extensions.SequenceEqual(this byte[] first, byte[] second)
} | ||
|
||
private bool ValidateCertificate(X509Certificate2 cert, X509Chain chain, SslPolicyErrors errors) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this function we will try do full validation of certificate chain - it will still apply standard checks that certificate is not expired, is not revoked, created for proper domain.
It may be overkill if we would like just trust certificate provided in file on disk. So, we can discuss if we can validate just that server public key match public key on disk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@srprash What's your thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it may be a bit overkill, but since this validation will be done only 1 time on TraceProvider
initialization, it should be okay. BTW, do you by any chance know what's the performance impact of this validation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have not done performance measurement, but it should be neglectable as we are not doing much more than standard certificate validation will do - we try to build second chain (one chain is already built by normal check), validate results and scan certificate keys to match certificate on disk. I'd expect it may be up to 3 time slower then normal SSL certificate check, but it never will be on hot path - and in our case, it will be only at init time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Thanks!
src/OpenTelemetry.Contrib.Extensions.AWSXRay/OpenTelemetry.Contrib.Extensions.AWSXRay.csproj
Show resolved
Hide resolved
{ | ||
try | ||
{ | ||
collection.Import(certFileName, null, (X509KeyStorageFlags)32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
32 means EphemeralKey. If provided certificate will have private key to prevent creating temporary files on disk. I believe it is not required if we guarantee to pass certificate only file, not pfx.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise if we have to use EphemeralKeySet flag I would suggest checking for the runtimes lower than net472 or lower than netstandard2.1 where X509KeyStorageFlags.EphemeralKeySet (32) doesn't exist. Otherwise we will eventually throw here. If we need I'd recommend doing this check much earlier so we can fallback gracefully
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out. I have removed EphemeralKey.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this code should work as expected. Just few comments for improvements
}; | ||
clientHandler.ClientCertificates.Add(new X509Certificate2(certificateFile)); | ||
|
||
ServerCertificateValidationProvider serverCertificateValidationProvider = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should you check if serverCertificateValidationProvider is not ServerCertificateValidationProvider.InvalidProvider before adding a validation callback? If the cert file doesn't exist or cannot be parsed you cannot use it for validation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it is required. If serverCertificateValidationProvider is ServerCertificateValidationProvider.InvalidProvider, ValidationCallback will be null and no call will be made for ServerCertificateCustomValidationCallback.
{ | ||
internal static class X509Extensions | ||
{ | ||
public static bool SequenceEqual(this byte[] first, byte[] second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you really need this extension method? It should be available in .net starting from 3.5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As suggested, used LINQ instead
{ | ||
try | ||
{ | ||
collection.Import(certFileName, null, (X509KeyStorageFlags)32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we expect that the certFileName always contains just one certificate? Because if it has multiple certificates than only the first one will be added to the collection. Maybe still worth mentioning in the comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certificate File will be loaded from Kubernetes machine ("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt") which is hardcoded. There is not gonna be multiple certificates being loaded.
ServerCertificateValidationProvider serverCertificateValidationProvider = | ||
ServerCertificateValidationProvider.FromCertificateFile(certificateFile); | ||
|
||
clientHandler.ServerCertificateCustomValidationCallback = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can simplify this assignment to
clientHandler.ServerCertificateCustomValidationCallback = serverCertificateValidationProvider.ValidationCallback;
if you change the signature of the method in ServerCertificateValidationProvider
class:
bool ValidationCallback(HttpRequestMessage requestMessage, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslErrors) { ... }
{ | ||
try | ||
{ | ||
collection.Import(certFileName, null, (X509KeyStorageFlags)32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise if we have to use EphemeralKeySet flag I would suggest checking for the runtimes lower than net472 or lower than netstandard2.1 where X509KeyStorageFlags.EphemeralKeySet (32) doesn't exist. Otherwise we will eventually throw here. If we need I'd recommend doing this check much earlier so we can fallback gracefully
{ | ||
foreach (var certificate in collection) | ||
{ | ||
if (chainElement.Certificate.GetPublicKey().SequenceEqual(certificate.GetPublicKey())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree. And that way we can remove the extension method OpenTelemetry.Contrib.Extensions.AWSXRay.Resources.Http.X509Extensions.SequenceEqual(this byte[] first, byte[] second)
It is fix #197 |
Codecov Report
@@ Coverage Diff @@
## main #208 +/- ##
==========================================
+ Coverage 24.35% 25.25% +0.89%
==========================================
Files 124 126 +2
Lines 3703 3778 +75
==========================================
+ Hits 902 954 +52
- Misses 2801 2824 +23
|
|
||
using System; | ||
using System.Net.Http; | ||
using System.Security.Cryptography.X509Certificates; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unused import. Can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@ievgen-platonov I think you'll need to complete CLA authorization. |
… due to exception "The SSL connection could not be established" Issue Description: With the current code, it requires an additional step of installing kubernetes self-signed certificate to the trusted store as .NET core expects them to the trsuted store to make a secure connection. Fix Provided: ServerCertificationValidationProvider - Safely loads the certificate to the trusted collection along with ServerSideValidation. This would avoid the unnecessary step of installing the certificate manually. Handler - Creates HttpClientHandler with client certificate. (cherry picked from commit 41e68d3)
(cherry picked from commit 0b5d9a9)
(cherry picked from commit 4aa1c62)
8567907
to
363d5ce
Compare
@srprash, It was my mistake as I had fetched the main branch in the GITHUB UI where main and my commits were not in sync. I fixed it and it should not fail in CLA authorization |
|
||
public void Dispose() | ||
{ | ||
for (int tries = 0; tries < 3; tries++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be:
for (int tries = 0; ; tries++)
You already limit amount of tries in when on catch.
Fixed For loop tries
This reverts commit b982325.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot! :)
Issue Description:
With the current code, it requires an additional step of installing kubernetes self-signed certificate to the trusted store as .NET core expects them to the trusted store to make a secure connection.
Fix Provided:
Handler - Moved the creation of HttpClientHandler with client certificate into a separate class
ServerCertificationValidationProvider - Safely loads the certificate to the trusted collection along with ServerSideValidation and passed to HttpClient. This would avoid the unnecessary step of installing the certificate manually.