diff --git a/src/KubernetesClient/CertUtils.cs b/src/KubernetesClient/CertUtils.cs
index 6ac4a787..dd0ba71b 100644
--- a/src/KubernetesClient/CertUtils.cs
+++ b/src/KubernetesClient/CertUtils.cs
@@ -1,15 +1,14 @@
-using System;
-using System.IO;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
using k8s.Exceptions;
using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
-using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Text.RegularExpressions;
namespace k8s
{
@@ -19,22 +18,21 @@ public static class CertUtils
/// Load pem encoded cert file
///
/// Path to pem encoded cert file
- /// x509 instance.
- public static X509Certificate2 LoadPemFileCert(string file)
+ /// List of x509 instances.
+ public static X509Certificate2Collection LoadPemFileCert(string file)
{
- var certs = new X509CertificateParser().ReadCertificates(File.OpenRead(file));
- var store = new Pkcs12StoreBuilder().Build();
- foreach (X509Certificate cert in certs)
- {
- store.SetCertificateEntry(Guid.NewGuid().ToString(), new X509CertificateEntry(cert));
- }
+ var certs = new X509CertificateParser().ReadCertificates(File.OpenRead(file));
+ var certCollection = new X509Certificate2Collection();
- using (var pkcs = new MemoryStream())
+ // Convert BouncyCastle X509Certificates to the .NET cryptography implementation and add
+ // it to the certificate collection
+ //
+ foreach (Org.BouncyCastle.X509.X509Certificate cert in certs)
{
- store.Save(pkcs, new char[0], new SecureRandom());
- // TODO not a chain
- return new X509Certificate2(pkcs.ToArray());
+ certCollection.Add(new X509Certificate2(cert.GetEncoded()));
}
+
+ return certCollection;
}
///
diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs
index 1e6f228f..47f0342e 100644
--- a/src/KubernetesClient/Kubernetes.ConfigInit.cs
+++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs
@@ -39,7 +39,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
throw new KubeConfigException("Bad host url", e);
}
- CaCert = config.SslCaCert;
+ CaCerts = config.SslCaCerts;
SkipTlsVerify = config.SkipTlsVerify;
if (BaseUri.Scheme == "https")
@@ -53,21 +53,26 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
}
else
{
- if (CaCert == null)
+ if (CaCerts == null)
{
throw new KubeConfigException("a CA must be set when SkipTlsVerify === false");
}
- using (System.IO.MemoryStream certStream = new System.IO.MemoryStream(config.SslCaCert.RawData))
- {
- Java.Security.Cert.Certificate cert = Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream);
- Xamarin.Android.Net.AndroidClientHandler handler = (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler;
+ var certList = new System.Collections.Generic.List();
- handler.TrustedCerts = new System.Collections.Generic.List()
+ foreach (X509Certificate2 caCert in CaCerts)
+ {
+ using (System.IO.MemoryStream certStream = new System.IO.MemoryStream(caCert.RawData))
{
- cert
- };
+ Java.Security.Cert.Certificate cert = Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream);
+
+ certList.Add(cert);
+ }
}
+
+ Xamarin.Android.Net.AndroidClientHandler handler = (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler;
+
+ handler.TrustedCerts = certList;
}
}
@@ -100,7 +105,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
throw new KubeConfigException("Bad host url", e);
}
- CaCert = config.SslCaCert;
+ CaCerts = config.SslCaCerts;
SkipTlsVerify = config.SkipTlsVerify;
if (BaseUri.Scheme == "https")
@@ -122,7 +127,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
}
else
{
- if (CaCert == null)
+ if (CaCerts == null)
{
throw new KubeConfigException("a CA must be set when SkipTlsVerify === false");
}
@@ -130,18 +135,18 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
#if NET452
((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
- return Kubernetes.CertificateValidationCallBack(sender, CaCert, certificate, chain, sslPolicyErrors);
+ return Kubernetes.CertificateValidationCallBack(sender, CaCerts, certificate, chain, sslPolicyErrors);
};
#elif XAMARINIOS1_0
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
var cert = new X509Certificate2(certificate);
- return Kubernetes.CertificateValidationCallBack(sender, CaCert, cert, chain, sslPolicyErrors);
+ return Kubernetes.CertificateValidationCallBack(sender, CaCerts, cert, chain, sslPolicyErrors);
};
#else
HttpClientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
- return Kubernetes.CertificateValidationCallBack(sender, CaCert, certificate, chain, sslPolicyErrors);
+ return Kubernetes.CertificateValidationCallBack(sender, CaCerts, certificate, chain, sslPolicyErrors);
};
#endif
}
@@ -152,7 +157,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler
}
#endif
- private X509Certificate2 CaCert { get; }
+ private X509Certificate2Collection CaCerts { get; }
private bool SkipTlsVerify { get; }
@@ -241,7 +246,7 @@ private void SetCredentials(KubernetesClientConfiguration config, HttpClientHand
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Unused by design")]
public static bool CertificateValidationCallBack(
object sender,
- X509Certificate2 caCert,
+ X509Certificate2Collection caCerts,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
@@ -257,15 +262,29 @@ public static bool CertificateValidationCallBack(
{
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
- // add all your extra certificate chain
- chain.ChainPolicy.ExtraStore.Add(caCert);
+ // Added our trusted certificates to the chain
+ //
+ chain.ChainPolicy.ExtraStore.AddRange(caCerts);
+
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
var isValid = chain.Build((X509Certificate2)certificate);
+ var isTrusted = false;
+
var rootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
- isValid = isValid && rootCert.RawData.SequenceEqual(caCert.RawData);
- return isValid;
+ // Make sure that one of our trusted certs exists in the chain provided by the server.
+ //
+ foreach (var cert in caCerts)
+ {
+ if (rootCert.RawData.SequenceEqual(cert.RawData))
+ {
+ isTrusted = true;
+ break;
+ }
+ }
+
+ return isValid && isTrusted;
}
// In all other cases, return false.
diff --git a/src/KubernetesClient/Kubernetes.WebSocket.cs b/src/KubernetesClient/Kubernetes.WebSocket.cs
index 6a0623db..a8310883 100644
--- a/src/KubernetesClient/Kubernetes.WebSocket.cs
+++ b/src/KubernetesClient/Kubernetes.WebSocket.cs
@@ -260,16 +260,16 @@ public partial class Kubernetes
}
#if NET452
- if (this.CaCert != null)
+ if (this.CaCerts != null)
{
webSocketBuilder.SetServerCertificateValidationCallback(this.ServerCertificateValidationCallback);
}
#endif
#if NETCOREAPP2_1
- if (this.CaCert != null)
+ if (this.CaCerts != null)
{
- webSocketBuilder.ExpectServerCertificate(this.CaCert);
+ webSocketBuilder.ExpectServerCertificate(this.CaCerts);
}
if (this.SkipTlsVerify)
@@ -347,7 +347,7 @@ public partial class Kubernetes
ServiceClientTracing.Exit(invocationId, null);
}
#if NET452
- if (this.CaCert != null)
+ if (this.CaCerts != null)
{
webSocketBuilder.CleanupServerCertificateValidationCallback(this.ServerCertificateValidationCallback);
}
@@ -359,7 +359,7 @@ public partial class Kubernetes
#if NET452
internal bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
- return Kubernetes.CertificateValidationCallBack(sender, this.CaCert, certificate, chain, sslPolicyErrors);
+ return Kubernetes.CertificateValidationCallBack(sender, this.CaCerts, certificate, chain, sslPolicyErrors);
}
#endif
}
diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
index 41fc3656..84a23a66 100644
--- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
+++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -189,17 +190,17 @@ private void SetClusterDetails(K8SConfiguration k8SConfig, Context activeContext
{
throw new KubeConfigException($"Bad server host URL `{Host}` (cannot be parsed)");
}
-
+
if (uri.Scheme == "https")
{
if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData))
{
var data = clusterDetails.ClusterEndpoint.CertificateAuthorityData;
- SslCaCert = new X509Certificate2(Convert.FromBase64String(data));
+ SslCaCerts = new X509Certificate2Collection(new X509Certificate2(Convert.FromBase64String(data)));
}
else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority))
{
- SslCaCert = new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority));
+ SslCaCerts = new X509Certificate2Collection(new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority)));
}
}
}
diff --git a/src/KubernetesClient/KubernetesClientConfiguration.InCluster.cs b/src/KubernetesClient/KubernetesClientConfiguration.InCluster.cs
index 86c576e5..7fa46749 100644
--- a/src/KubernetesClient/KubernetesClientConfiguration.InCluster.cs
+++ b/src/KubernetesClient/KubernetesClientConfiguration.InCluster.cs
@@ -43,7 +43,7 @@ public static KubernetesClientConfiguration InClusterConfig()
{
Host = new UriBuilder("https", host, Convert.ToInt32(port)).ToString(),
AccessToken = token,
- SslCaCert = CertUtils.LoadPemFileCert(rootCAFile)
+ SslCaCerts = CertUtils.LoadPemFileCert(rootCAFile)
};
}
}
diff --git a/src/KubernetesClient/KubernetesClientConfiguration.cs b/src/KubernetesClient/KubernetesClientConfiguration.cs
index a16cd6cb..73e6c6fc 100644
--- a/src/KubernetesClient/KubernetesClientConfiguration.cs
+++ b/src/KubernetesClient/KubernetesClientConfiguration.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
namespace k8s
@@ -18,9 +19,9 @@ public partial class KubernetesClientConfiguration
public string Host { get; set; }
///
- /// Gets SslCaCert
+ /// Gets SslCaCerts
///
- public X509Certificate2 SslCaCert { get; set; }
+ public X509Certificate2Collection SslCaCerts { get; set; }
///
/// Gets ClientCertificateData
diff --git a/src/KubernetesClient/WebSocketBuilder.cs b/src/KubernetesClient/WebSocketBuilder.cs
index e850e7b3..3e9ad3c8 100644
--- a/src/KubernetesClient/WebSocketBuilder.cs
+++ b/src/KubernetesClient/WebSocketBuilder.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Net.WebSockets;
#if NET452
using System.Net.Security;
@@ -52,7 +53,7 @@ public void CleanupServerCertificateValidationCallback(RemoteCertificateValidati
#endif
#if NETCOREAPP2_1
- public WebSocketBuilder ExpectServerCertificate(X509Certificate2 serverCertificate)
+ public WebSocketBuilder ExpectServerCertificate(X509Certificate2Collection serverCertificate)
{
Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
diff --git a/tests/KubernetesClient.Tests/AuthTests.cs b/tests/KubernetesClient.Tests/AuthTests.cs
index 0d83999a..22e3f03a 100644
--- a/tests/KubernetesClient.Tests/AuthTests.cs
+++ b/tests/KubernetesClient.Tests/AuthTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
@@ -221,7 +222,7 @@ public void Cert()
Host = server.Uri.ToString(),
ClientCertificateData = clientCertificateData,
ClientCertificateKeyData = clientCertificateKeyData,
- SslCaCert = serverCertificate,
+ SslCaCerts = new X509Certificate2Collection(serverCertificate),
SkipTlsVerify = false
});
diff --git a/tests/KubernetesClient.Tests/CertUtilsTests.cs b/tests/KubernetesClient.Tests/CertUtilsTests.cs
index 549c26be..10679cbd 100644
--- a/tests/KubernetesClient.Tests/CertUtilsTests.cs
+++ b/tests/KubernetesClient.Tests/CertUtilsTests.cs
@@ -2,6 +2,9 @@
using Xunit;
using k8s;
using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Net.Security;
+using System.Linq;
namespace k8s.Tests
{
@@ -72,13 +75,20 @@ public void LoadFromInlineDataRelativePath()
}
///
- /// Checks
+ /// Checks that the bundle certificate was loaded correctly
///
[Fact]
public void LoadPemWithMultiCert()
{
- var cert = CertUtils.LoadPemFileCert("assets/ca3.crt");
- Assert.NotNull(cert.PublicKey);
+ var certCollection = CertUtils.LoadPemFileCert("assets/ca-bundle.crt");
+
+ var intermediateCert = new X509Certificate2("assets/ca-bundle-intermediate.crt");
+ var rootCert = new X509Certificate2("assets/ca-bundle-root.crt");
+
+ Assert.Equal(2, certCollection.Count);
+
+ Assert.True(certCollection[0].RawData.SequenceEqual(intermediateCert.RawData));
+ Assert.True(certCollection[1].RawData.SequenceEqual(rootCert.RawData));
}
}
}
diff --git a/tests/KubernetesClient.Tests/CertificateValidationTests.cs b/tests/KubernetesClient.Tests/CertificateValidationTests.cs
index 0fc79225..d653c24f 100644
--- a/tests/KubernetesClient.Tests/CertificateValidationTests.cs
+++ b/tests/KubernetesClient.Tests/CertificateValidationTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
@@ -11,7 +12,7 @@ public class CertificateValidationTests
[Fact]
public void ValidCert()
{
- var caCert = new X509Certificate2("assets/ca.crt");
+ var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
var testCert = new X509Certificate2("assets/ca.crt");
var chain = new X509Chain();
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
@@ -24,7 +25,36 @@ public void ValidCert()
[Fact]
public void InvalidCert()
{
- var caCert = new X509Certificate2("assets/ca.crt");
+ var caCert = CertUtils.LoadPemFileCert("assets/ca.crt");
+ var testCert = new X509Certificate2("assets/ca2.crt");
+ 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");
var testCert = new X509Certificate2("assets/ca2.crt");
var chain = new X509Chain();
var errors = SslPolicyErrors.RemoteCertificateChainErrors;
diff --git a/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs b/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
index d504c1f0..de55ca3b 100755
--- a/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
+++ b/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
@@ -79,7 +79,7 @@ public void ClientData(string context)
var fi = new FileInfo("assets/kubeconfig.yml");
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, context);
Assert.Equal(context, cfg.CurrentContext);
- Assert.NotNull(cfg.SslCaCert);
+ Assert.NotNull(cfg.SslCaCerts);
Assert.Equal(File.ReadAllText("assets/client-certificate-data.txt"), cfg.ClientCertificateData);
Assert.Equal(File.ReadAllText("assets/client-key-data.txt"), cfg.ClientCertificateKeyData);
}
@@ -94,7 +94,7 @@ public void CheckClusterTlsSkipCorrectness()
var fi = new FileInfo("assets/kubeconfig.tls-skip.yml");
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi);
Assert.NotNull(cfg.Host);
- Assert.Null(cfg.SslCaCert);
+ Assert.Null(cfg.SslCaCerts);
Assert.True(cfg.SkipTlsVerify);
}
diff --git a/tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt b/tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt
new file mode 100644
index 00000000..e8546082
--- /dev/null
+++ b/tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDTCCAfWgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UEAwwKa3Vi
+ZXJuZXRlczAeFw0xOTAzMDMxNzA4MDlaFw0yOTAyMjgxNzA4MDlaMBYxFDASBgNV
+BAMMC2V0Y2hhbmctdWI1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+x1Tp7Da3NbjdHmYdYZ/GNpCRGvFFap7EG1pokhfILKSbPusqiO9wnKDE4Afdn/ZE
+CQV0Whwtox3jczBOIRy+P6FvlPyhApUpynVTwgCiuhTM+thgODgpe6GXmVlVJGvv
+AoLw7CMndB5sMs5HH+qA2U1q4VFI/csr3/yeKzWBik3dZVoh04sI9WTVL+bl/1X5
+0dl5qrqkYiDx8ycAHyOnl8dhJW+RGl67HiliuUeSq6vwsfv9rh3TP9wHVF1PXFJp
+WfXy4WbLmuld5wxXnQVO2g51jqfqN9fD8FHIkae1IkO/PUTucloNlLiFsragQOTD
+RVSP+TV3gshATBs2MMVXMwIDAQABo2YwZDAdBgNVHQ4EFgQU/3w9AR2cnEepWH4E
+8a1xLZAnjykwHwYDVR0jBBgwFoAULs/lzct8CGvVdIiq4t9T4idu5OwwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEB
+AKw741V1wszIthHBV8dvCyQoyozBJuAo4IHbiiFmzuiQuyshMcX+Qs9a+g6OG5d1
+UbwFfUlqzmZQcbcR/Jc6wMz3wO6Hoy5pS3w/FR2UMGR39o95/7XCkTIOwCqau6Pw
+dpgvbnaiqPFPqD3ohdUuVRcXG3va5AmKTsUn7m+lR/93/qptt+SUVp6jwnbGcwoB
+s3u2XXx5s1M7tqqj3tAEOPCKlohS6mQ4X3wulgpZ1XpJ0WTvcvoPXEtA56k7vX3a
+4E6x66LZCFA2ZR/5COv5D055AhrihKL8kbAutxhfA27SJ/MGowzmTT7kVQha3Su3
+aoOYZgcUww+SkRSGVrtgMgQ=
+-----END CERTIFICATE-----
diff --git a/tests/KubernetesClient.Tests/assets/ca-bundle-root.crt b/tests/KubernetesClient.Tests/assets/ca-bundle-root.crt
new file mode 100644
index 00000000..21c71739
--- /dev/null
+++ b/tests/KubernetesClient.Tests/assets/ca-bundle-root.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDEDCCAfigAwIBAgIJAJpb9irKg2JjMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV
+BAMMCmt1YmVybmV0ZXMwHhcNMTkwMzAzMDAyMTI3WhcNMzkwMjI2MDAyMTI3WjAV
+MRMwEQYDVQQDDAprdWJlcm5ldGVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuoK+Na+LyKIipmftBw4Y+Z19g8yv6Y+dYt6KlBg24XHNA0v1cMwtOCDF
+Mlm4rsD7Jd5UO6ugdk3fxEvGGhmzWTXSBRUWcTbScAM49mALBFkCvNTPK2vVhk7P
+im2QQl8a5vjYz8HLKJTb/O+0q+Kktpd7XTaU2U7ZebiLVs5bvNbb3ZDtIjAARY9S
+alZ4hOzuVNaSX9MBRqTWq3HuKwDiVTT3dan/ABoU8NdedPfIbyY48wiQgjEYb64g
+3geYpArLQeffo8fmhUEPRR/1WrfvYvvm8sV8jT+rqxITKJ5Vo5kpZUpomDOtGVMS
+gGAle6mcTrqlrsCFc4gFRRoHiH1ODQIDAQABo2MwYTAdBgNVHQ4EFgQULs/lzct8
+CGvVdIiq4t9T4idu5OwwHwYDVR0jBBgwFoAULs/lzct8CGvVdIiq4t9T4idu5Oww
+DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
+ggEBAFMq5z4OoaIhqx/i/btpVLRnQDcpDwdUurE0iNPz3bgOI5QiIe95oUwXSFQL
+ciECvObfMmiVuz+p7ND5eNdxYR4hlq1W1PYcgRQgusXCC4Xd/XAGaLZXzH3SBrmp
+bs5sfokhXKNccsnQu5Ya3JRkALxxUJ+DcOn+vi9gmEAzi+nXbyqUjIhSD5nygClX
+0aSKbvhUmXyaJpUH0i7dSxWP3LqCDjtru/5ejNtB097dNcyF8js3Yuk3hwqyegQx
+ELn4c/TKPL9L8vE7tJg/M78DPAvRCiuwl0HQcasBE2AX0wdpY0UeXsNDyzUf/2WF
+fHY4DnuBdeVdHtl1yPlXmQkMoQM=
+-----END CERTIFICATE-----
diff --git a/tests/KubernetesClient.Tests/assets/ca-bundle.crt b/tests/KubernetesClient.Tests/assets/ca-bundle.crt
new file mode 100644
index 00000000..ff887ab9
--- /dev/null
+++ b/tests/KubernetesClient.Tests/assets/ca-bundle.crt
@@ -0,0 +1,44 @@
+--- Comments to make sure we still parse the cert correctly
+
+Intermediate Certificate
+
+-----BEGIN CERTIFICATE-----
+MIIDDTCCAfWgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UEAwwKa3Vi
+ZXJuZXRlczAeFw0xOTAzMDMxNzA4MDlaFw0yOTAyMjgxNzA4MDlaMBYxFDASBgNV
+BAMMC2V0Y2hhbmctdWI1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+x1Tp7Da3NbjdHmYdYZ/GNpCRGvFFap7EG1pokhfILKSbPusqiO9wnKDE4Afdn/ZE
+CQV0Whwtox3jczBOIRy+P6FvlPyhApUpynVTwgCiuhTM+thgODgpe6GXmVlVJGvv
+AoLw7CMndB5sMs5HH+qA2U1q4VFI/csr3/yeKzWBik3dZVoh04sI9WTVL+bl/1X5
+0dl5qrqkYiDx8ycAHyOnl8dhJW+RGl67HiliuUeSq6vwsfv9rh3TP9wHVF1PXFJp
+WfXy4WbLmuld5wxXnQVO2g51jqfqN9fD8FHIkae1IkO/PUTucloNlLiFsragQOTD
+RVSP+TV3gshATBs2MMVXMwIDAQABo2YwZDAdBgNVHQ4EFgQU/3w9AR2cnEepWH4E
+8a1xLZAnjykwHwYDVR0jBBgwFoAULs/lzct8CGvVdIiq4t9T4idu5OwwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEB
+AKw741V1wszIthHBV8dvCyQoyozBJuAo4IHbiiFmzuiQuyshMcX+Qs9a+g6OG5d1
+UbwFfUlqzmZQcbcR/Jc6wMz3wO6Hoy5pS3w/FR2UMGR39o95/7XCkTIOwCqau6Pw
+dpgvbnaiqPFPqD3ohdUuVRcXG3va5AmKTsUn7m+lR/93/qptt+SUVp6jwnbGcwoB
+s3u2XXx5s1M7tqqj3tAEOPCKlohS6mQ4X3wulgpZ1XpJ0WTvcvoPXEtA56k7vX3a
+4E6x66LZCFA2ZR/5COv5D055AhrihKL8kbAutxhfA27SJ/MGowzmTT7kVQha3Su3
+aoOYZgcUww+SkRSGVrtgMgQ=
+-----END CERTIFICATE-----
+
+Root certificate
+-----BEGIN CERTIFICATE-----
+MIIDEDCCAfigAwIBAgIJAJpb9irKg2JjMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV
+BAMMCmt1YmVybmV0ZXMwHhcNMTkwMzAzMDAyMTI3WhcNMzkwMjI2MDAyMTI3WjAV
+MRMwEQYDVQQDDAprdWJlcm5ldGVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuoK+Na+LyKIipmftBw4Y+Z19g8yv6Y+dYt6KlBg24XHNA0v1cMwtOCDF
+Mlm4rsD7Jd5UO6ugdk3fxEvGGhmzWTXSBRUWcTbScAM49mALBFkCvNTPK2vVhk7P
+im2QQl8a5vjYz8HLKJTb/O+0q+Kktpd7XTaU2U7ZebiLVs5bvNbb3ZDtIjAARY9S
+alZ4hOzuVNaSX9MBRqTWq3HuKwDiVTT3dan/ABoU8NdedPfIbyY48wiQgjEYb64g
+3geYpArLQeffo8fmhUEPRR/1WrfvYvvm8sV8jT+rqxITKJ5Vo5kpZUpomDOtGVMS
+gGAle6mcTrqlrsCFc4gFRRoHiH1ODQIDAQABo2MwYTAdBgNVHQ4EFgQULs/lzct8
+CGvVdIiq4t9T4idu5OwwHwYDVR0jBBgwFoAULs/lzct8CGvVdIiq4t9T4idu5Oww
+DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
+ggEBAFMq5z4OoaIhqx/i/btpVLRnQDcpDwdUurE0iNPz3bgOI5QiIe95oUwXSFQL
+ciECvObfMmiVuz+p7ND5eNdxYR4hlq1W1PYcgRQgusXCC4Xd/XAGaLZXzH3SBrmp
+bs5sfokhXKNccsnQu5Ya3JRkALxxUJ+DcOn+vi9gmEAzi+nXbyqUjIhSD5nygClX
+0aSKbvhUmXyaJpUH0i7dSxWP3LqCDjtru/5ejNtB097dNcyF8js3Yuk3hwqyegQx
+ELn4c/TKPL9L8vE7tJg/M78DPAvRCiuwl0HQcasBE2AX0wdpY0UeXsNDyzUf/2WF
+fHY4DnuBdeVdHtl1yPlXmQkMoQM=
+-----END CERTIFICATE-----