Skip to content
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

Merged
merged 16 commits into from
May 12, 2022

Conversation

swetharavichandrancisco
Copy link
Contributor

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.

@@ -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)]
Copy link
Contributor

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)?

Copy link
Contributor Author

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,
Copy link
Contributor

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()))
Copy link
Contributor

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())

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)
{
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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?

Copy link
Contributor

@iskiselev iskiselev Apr 20, 2022

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Thanks!

{
try
{
collection.Import(certFileName, null, (X509KeyStorageFlags)32);
Copy link
Contributor

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.

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

Copy link
Contributor Author

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.

Copy link

@alexeypukhov alexeypukhov left a 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 =

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

Copy link
Contributor Author

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)

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

Copy link
Contributor Author

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);

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

Copy link
Contributor Author

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 =

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);

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()))

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)

@iskiselev
Copy link
Contributor

It is fix #197

@swetharavichandrancisco swetharavichandrancisco changed the title #197 - AWSEKSResourceDetector fails to detect resources due to exception "The SSL connection could not be established" Fix #197 - AWSEKSResourceDetector fails to detect resources due to exception "The SSL connection could not be established" Mar 28, 2022
@codecov
Copy link

codecov bot commented Mar 30, 2022

Codecov Report

Merging #208 (7f76cf5) into main (3883de8) will increase coverage by 0.89%.
The diff coverage is 61.44%.

Impacted file tree graph

@@            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     
Impacted Files Coverage Δ
...nsions.AWSXRay/Resources/AWSEKSResourceDetector.cs 0.00% <0.00%> (ø)
...ources/Http/ServerCertificateValidationProvider.cs 61.29% <61.29%> (ø)
...ntrib.Extensions.AWSXRay/Resources/Http/Handler.cs 73.33% <73.33%> (ø)
...y.Contrib.Extensions.AWSXRay/AWSXRayEventSource.cs 14.28% <100.00%> (+14.28%) ⬆️

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Apr 4, 2022

CLA Signed

The committers listed above are authorized under a signed CLA.


using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@srprash
Copy link
Contributor

srprash commented Apr 20, 2022

@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)
@swetharavichandrancisco
Copy link
Contributor Author

@ievgen-platonov I think you'll need to complete CLA authorization.

@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

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented May 6, 2022

CLA Signed

The committers listed above are authorized under a signed CLA.


public void Dispose()
{
for (int tries = 0; tries < 3; tries++)
Copy link
Contributor

@iskiselev iskiselev May 6, 2022

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.

Copy link
Contributor

@srprash srprash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot! :)

@srprash srprash merged commit 5608616 into open-telemetry:main May 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants