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

Schema Registry Config SslCaLocation doesn't work #2140

Open
8 tasks done
danielbojczuk opened this issue Nov 8, 2023 · 2 comments
Open
8 tasks done

Schema Registry Config SslCaLocation doesn't work #2140

danielbojczuk opened this issue Nov 8, 2023 · 2 comments

Comments

@danielbojczuk
Copy link

danielbojczuk commented Nov 8, 2023

Description

Nuget Version: Confluent.SchemaRegistry 2.3.0.
Apache Kafka version: Doesn't have influence
Operating system: Windows

SchemaRegistry Client is adding the CA certificates from the configuration SslCaLocation and SslKeystoreLocation to the HttpClientHandler.ClientCertificates Property:

But from the documentation, only the SSlKeystoreLocation should be placed on this HttpHandlers property for MTLS. The CA certificates should be handled differently: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.clientcertificates?view=netcore-2.0

So, the current implementation doesn't allow you to connect to a schema registry server if it uses a certificate signed by a local CA. It throws the following exception:

Local: Key serialization error
 ---> System.Net.Http.HttpRequestException: [SERVER_URL] HttpRequestException: The SSL connection could not be established, see inner exception.
   at Confluent.SchemaRegistry.RestService.ExecuteOnOneInstanceAsync(Func`1 createRequest) in...

How to reproduce

Execute this code using a SchemaRegistry server using a locally signed SSL certificate:

var schemaConfig = new SchemaRegistryConfig
{
    Url = SCHEMA_REGISTRY_URL,
    SslKeystoreLocation = CERTIFICATE_PATH,
    SslKeystorePassword = CERTIFICATE_PASSWORD,
    SslCaLocation = TRUSTED_CA_FILEPATH,
};

var schemaregistry = new CachedSchemaRegistryClient(schemaConfig);
var keySerializer = new AvroSerializer<Application>(schemaregistry).AsSyncOverAsync();
var valueSerializer = new AvroSerializer<Event>(schemaregistry).AsSyncOverAsync();


var privKeyBytes = Convert.FromBase64String(PRIVATE_KEY_BASE64);
var privKeyPem = PemEncoding.Write("RSA PRIVATE KEY", privKeyBytes);

var producerConfig = new ProducerConfig
{
    BootstrapServers = BOOTSTRAP_SERVERS,

    SocketTimeoutMs = 30_000,
    ReconnectBackoffMaxMs = 10_000,

    SecurityProtocol = SecurityProtocol.Ssl,
    SslEndpointIdentificationAlgorithm = SslEndpointIdentificationAlgorithm.None,
    EnableSslCertificateVerification = true,
    SslCertificateLocation = PUBLIC_KEY_FILE_PATH,
    SslCaCertificateStores = "My",
    SslKeyPem = new string(privKeyPem),
};

var producer = new ProducerBuilder<Application, Event>(producerConfig)
    .SetKeySerializer(keySerializer)
    .SetValueSerializer(valueSerializer)
.Build();

var message = new Message<Application, Event>
{
    Key = EventHelper.CreateApplication(),
    Value = EventHelper.CreateEvent()
};

var result = await producer.ProduceAsync(TOPIC, message);

Console.WriteLine($"Written to offset {result.Offset.Value}");

The SchemaRegistry is hiding the InnerException, but with some debugging, you can check the real exception on

The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
   at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.WaitForConnectionAsync(Boolean async, CancellationToken requestCancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Confluent.SchemaRegistry.RestService.ExecuteOnOneInstanceAsync(Func`1 createRequest) in

Workarounds

  • Disable the SSL Certificate Checking using the EnableSslCertificateVerification=false, which is not recommended at all.

  • If you add the same file used in TRUSTED_CA_FILEPATH to the Trust Root Certificate Authorities in Windows Certificate Stores, you can execute the code with success. The problem is that in some cases (Azure Function Apps), you can't do this operation
    image

Sugestion for solution

We could use the same approach as the Confluent.Kafka.ProducerConfig: being able to specify which certificate store you want to use:

public string SslCaCertificateStores { get { return Get("ssl.ca.certificate.stores"); } set { this.SetObject("ssl.ca.certificate.stores", value); } }

From .NET 5 and on, it is possible to add new Certificate Stores to the HttpClientHandler
dotnet/runtime#39835 (comment)

I would be happy to send a PR implementing the suggestion or another approach.

Checklist

Please provide the following information:

  • A complete (i.e., we can run it) minimal program demonstrating the problem. No need to supply a project file.
  • Confluent.Kafka nuget version.
  • Apache Kafka version.
  • Client configuration.
  • Operating system.
  • Provide logs (with "debug" : "..." as necessary in configuration).
  • Provide broker log excerpts.
  • Critical issue.
@danielbojczuk danielbojczuk changed the title Schema Registry Config SslCaLocation doesn't work properly Schema Registry Config SslCaLocation doesn't work Nov 8, 2023
@ahmar-husain
Copy link

I too face the same !

@teimyBr
Copy link

teimyBr commented Feb 13, 2024

any news about that when the pr getting merged ?

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

No branches or pull requests

3 participants