Update certificate validation to use custom trust store for .NET 5.0+ (#1653)

* Update certificate validation to use custom trust store for .NET 5.0 and greater

* Update src/KubernetesClient/Kubernetes.ConfigInit.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update apiserver-pfx-data.txt with new certificate data

* Update tests/KubernetesClient.Tests/CertificateValidationTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Brendan Burns <5751682+brendandburns@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Boshi Lian
2025-08-27 05:48:09 -07:00
committed by GitHub
parent 9b5e710c47
commit 1f8e5da0c2
3 changed files with 66 additions and 6 deletions

View File

@@ -1,3 +1,5 @@
using System;
using System.Security.Cryptography;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Xunit;
@@ -6,6 +8,62 @@ namespace k8s.Tests
{
public class CertificateValidationTests
{
[Fact]
public void ShouldRejectCertFromDifferentCA()
{
// Load our "trusted" Kubernetes CA
var trustedCaCert = CertUtils.LoadPemFileCert("assets/ca.crt");
// Generate a completely different CA and server cert in memory
using (var differentCA = CreateSelfSignedCA("CN=Different CA"))
using (var untrustedServerCert = CreateServerCert(differentCA, "CN=fake-server.com"))
{
var chain = new X509Chain();
// Pre-populate the chain like SSL validation would do
// This will likely succeed because we allow unknown CAs in the validation
chain.Build(untrustedServerCert);
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
var result = Kubernetes.CertificateValidationCallBack(this, trustedCaCert, untrustedServerCert, chain, errors);
// This SHOULD be false because the server cert wasn't signed by our trusted CA
// But the current K8s validation logic might incorrectly return true
Assert.False(result, "Should reject certificates not signed by trusted CA");
}
// Cleanup
// differentCA.Dispose();
// untrustedServerCert.Dispose();
}
// Helper methods to create test certificates
private static X509Certificate2 CreateSelfSignedCA(string subject)
{
using (var rsa = RSA.Create(2048))
{
var req = new CertificateRequest(subject, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, true));
return req.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(365));
}
}
private static X509Certificate2 CreateServerCert(X509Certificate2 issuerCA, string subject)
{
using (var rsa = RSA.Create(2048))
{
var req = new CertificateRequest(subject, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true));
req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true));
req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, true));
return req.Create(issuerCA, DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(90), new byte[] { 1, 2, 3, 4 });
}
}
[Fact]
public void ValidCert()
{