2025-08-27 05:48:09 -07:00
|
|
|
using System;
|
|
|
|
|
using System.Security.Cryptography;
|
2018-06-13 21:55:41 +04:00
|
|
|
using System.Net.Security;
|
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
2018-07-09 23:52:17 +10:00
|
|
|
namespace k8s.Tests
|
2018-06-13 21:55:41 +04:00
|
|
|
{
|
|
|
|
|
public class CertificateValidationTests
|
|
|
|
|
{
|
2025-08-27 05:48:09 -07:00
|
|
|
[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 });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-13 21:55:41 +04:00
|
|
|
[Fact]
|
|
|
|
|
public void ValidCert()
|
|
|
|
|
{
|
2019-03-11 06:39:28 -07:00
|
|
|
var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
|
2025-09-22 14:18:16 -07:00
|
|
|
#if NET9_0_OR_GREATER
|
|
|
|
|
var testCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca.crt");
|
|
|
|
|
#else
|
2018-06-13 21:55:41 +04:00
|
|
|
var testCert = new X509Certificate2("assets/ca.crt");
|
2025-09-22 14:18:16 -07:00
|
|
|
#endif
|
2018-06-13 21:55:41 +04:00
|
|
|
var chain = new X509Chain();
|
|
|
|
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
|
|
|
|
|
|
|
|
|
var result = Kubernetes.CertificateValidationCallBack(this, caCert, testCert, chain, errors);
|
|
|
|
|
|
|
|
|
|
Assert.True(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void InvalidCert()
|
|
|
|
|
{
|
2019-03-11 06:39:28 -07:00
|
|
|
var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
|
2025-09-22 14:18:16 -07:00
|
|
|
#if NET9_0_OR_GREATER
|
|
|
|
|
var testCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca2.crt");
|
|
|
|
|
#else
|
2019-03-11 06:39:28 -07:00
|
|
|
var testCert = new X509Certificate2("assets/ca2.crt");
|
2025-09-22 14:18:16 -07:00
|
|
|
#endif
|
2019-03-11 06:39:28 -07:00
|
|
|
var chain = new X509Chain();
|
|
|
|
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
|
|
|
|
|
|
|
|
|
var result = Kubernetes.CertificateValidationCallBack(this, caCert, testCert, chain, errors);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ValidBundleCert()
|
|
|
|
|
{
|
|
|
|
|
var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
|
|
|
|
|
|
|
|
|
|
// Load the intermediate cert
|
|
|
|
|
//
|
|
|
|
|
var testCert = caCert[0];
|
|
|
|
|
var chain = new X509Chain();
|
|
|
|
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
|
|
|
|
|
|
|
|
|
var result = Kubernetes.CertificateValidationCallBack(this, caCert, testCert, chain, errors);
|
|
|
|
|
|
|
|
|
|
Assert.True(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void InvalidBundleCert()
|
|
|
|
|
{
|
|
|
|
|
var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
|
2025-09-22 14:18:16 -07:00
|
|
|
#if NET9_0_OR_GREATER
|
|
|
|
|
var testCert = X509CertificateLoader.LoadCertificateFromFile("assets/ca2.crt");
|
|
|
|
|
#else
|
2018-06-13 21:55:41 +04:00
|
|
|
var testCert = new X509Certificate2("assets/ca2.crt");
|
2025-09-22 14:18:16 -07:00
|
|
|
#endif
|
2018-06-13 21:55:41 +04:00
|
|
|
var chain = new X509Chain();
|
|
|
|
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
|
|
|
|
|
|
|
|
|
var result = Kubernetes.CertificateValidationCallBack(this, caCert, testCert, chain, errors);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
}
|
2022-05-24 05:07:10 +02:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ValidBundleWithMultipleCerts()
|
|
|
|
|
{
|
|
|
|
|
var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle-correct.crt");
|
|
|
|
|
|
|
|
|
|
var testCert = caCert[0];
|
|
|
|
|
var chain = new X509Chain();
|
|
|
|
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
|
|
|
|
|
|
|
|
|
var result = Kubernetes.CertificateValidationCallBack(this, caCert, testCert, chain, errors);
|
|
|
|
|
|
|
|
|
|
Assert.True(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void InvalidBundleWithMultipleCerts()
|
|
|
|
|
{
|
|
|
|
|
var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle-incorrect.crt");
|
|
|
|
|
var testCert = caCert[0];
|
|
|
|
|
var chain = new X509Chain();
|
|
|
|
|
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
|
|
|
|
|
|
|
|
|
|
var result = Kubernetes.CertificateValidationCallBack(this, caCert, testCert, chain, errors);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
}
|
2018-06-13 21:55:41 +04:00
|
|
|
}
|
|
|
|
|
}
|