From 8c9f0520d293b82128d292d9d93debbdbe45cc8a Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Wed, 6 Mar 2019 21:00:08 -0800 Subject: [PATCH 1/7] initial --- src/KubernetesClient/CertUtils.cs | 41 +++++++++++-------- src/KubernetesClient/Kubernetes.ConfigInit.cs | 34 ++++++++++++--- ...ubernetesClientConfiguration.ConfigFile.cs | 7 ++-- .../KubernetesClientConfiguration.cs | 3 +- src/KubernetesClient/WebSocketBuilder.cs | 3 +- tests/KubernetesClient.Tests/AuthTests.cs | 3 +- .../KubernetesClient.Tests/CertUtilsTests.cs | 10 ----- .../CertificateValidationTests.cs | 31 +++++++++++++- .../assets/ca-bundle-intermediate.crt | 19 +++++++++ .../assets/ca-bundle.crt | 38 +++++++++++++++++ 10 files changed, 147 insertions(+), 42 deletions(-) create mode 100644 tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt create mode 100644 tests/KubernetesClient.Tests/assets/ca-bundle.crt diff --git a/src/KubernetesClient/CertUtils.cs b/src/KubernetesClient/CertUtils.cs index 6ac4a787..d0ea6041 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,28 @@ 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 IList 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 List(); - using (var pkcs = new MemoryStream()) + var certdata = File.ReadAllText(file) + .Replace("\r", "") + .Replace("\n", ""); + + var r = new Regex("-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----"); + + var matches = r.Matches(certdata); + + foreach (Match match in matches) { - store.Save(pkcs, new char[0], new SecureRandom()); - // TODO not a chain - return new X509Certificate2(pkcs.ToArray()); + string certData = match.Value + .Replace("-----BEGIN CERTIFICATE-----", "") + .Replace("-----END CERTIFICATE-----", ""); + certs.Add(new X509Certificate2(Convert.FromBase64String(certData))); } + + return certs; } /// diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index 1e6f228f..f635fd0d 100644 --- a/src/KubernetesClient/Kubernetes.ConfigInit.cs +++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; @@ -58,7 +59,9 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler throw new KubeConfigException("a CA must be set when SkipTlsVerify === false"); } - using (System.IO.MemoryStream certStream = new System.IO.MemoryStream(config.SslCaCert.RawData)) + var caCert = CaCert[CaCert.Count - 1]; + + using (System.IO.MemoryStream certStream = new System.IO.MemoryStream(caCert.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; @@ -152,7 +155,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler } #endif - private X509Certificate2 CaCert { get; } + private IList CaCert { get; } private bool SkipTlsVerify { get; } @@ -241,7 +244,7 @@ private void SetCredentials(KubernetesClientConfiguration config, HttpClientHand [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Unused by design")] public static bool CertificateValidationCallBack( object sender, - X509Certificate2 caCert, + IList caCerts, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) @@ -257,13 +260,32 @@ 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 + // + foreach (var caCert in caCerts) + { + chain.ChainPolicy.ExtraStore.Add(caCert); + } + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; var isValid = chain.Build((X509Certificate2)certificate); + var certMatch = false; + var rootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; - isValid = isValid && rootCert.RawData.SequenceEqual(caCert.RawData); + + // 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)) + { + certMatch = true; + break; + } + } + + isValid = isValid && certMatch; return isValid; } diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs index 41fc3656..9f73bdaa 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)); + SslCaCert = new List() { new X509Certificate2(Convert.FromBase64String(data)) }; } else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) { - SslCaCert = new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority)); + SslCaCert = new List() { new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority)) }; } } } diff --git a/src/KubernetesClient/KubernetesClientConfiguration.cs b/src/KubernetesClient/KubernetesClientConfiguration.cs index a16cd6cb..e03ed760 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 @@ -20,7 +21,7 @@ public partial class KubernetesClientConfiguration /// /// Gets SslCaCert /// - public X509Certificate2 SslCaCert { get; set; } + public IList SslCaCert { get; set; } /// /// Gets ClientCertificateData diff --git a/src/KubernetesClient/WebSocketBuilder.cs b/src/KubernetesClient/WebSocketBuilder.cs index e850e7b3..85245be0 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(IList serverCertificate) { Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { diff --git a/tests/KubernetesClient.Tests/AuthTests.cs b/tests/KubernetesClient.Tests/AuthTests.cs index 0d83999a..972f732a 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, + SslCaCert = new List() { serverCertificate }, SkipTlsVerify = false }); diff --git a/tests/KubernetesClient.Tests/CertUtilsTests.cs b/tests/KubernetesClient.Tests/CertUtilsTests.cs index 549c26be..d093c059 100644 --- a/tests/KubernetesClient.Tests/CertUtilsTests.cs +++ b/tests/KubernetesClient.Tests/CertUtilsTests.cs @@ -70,15 +70,5 @@ public void LoadFromInlineDataRelativePath() var cert = CertUtils.GeneratePfx(cfg); Assert.NotNull(cert.PrivateKey); } - - /// - /// Checks - /// - [Fact] - public void LoadPemWithMultiCert() - { - var cert = CertUtils.LoadPemFileCert("assets/ca3.crt"); - Assert.NotNull(cert.PublicKey); - } } } diff --git a/tests/KubernetesClient.Tests/CertificateValidationTests.cs b/tests/KubernetesClient.Tests/CertificateValidationTests.cs index 0fc79225..74047bbe 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 = new List() { new X509Certificate2("assets/ca.crt") }; var testCert = new X509Certificate2("assets/ca.crt"); var chain = new X509Chain(); var errors = SslPolicyErrors.RemoteCertificateChainErrors; @@ -24,7 +25,33 @@ public void ValidCert() [Fact] public void InvalidCert() { - var caCert = new X509Certificate2("assets/ca.crt"); + var caCert = new List() { new X509Certificate2("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"); + var testCert = new X509Certificate2("assets/ca-bundle-intermediate.crt"); + 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/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.crt b/tests/KubernetesClient.Tests/assets/ca-bundle.crt new file mode 100644 index 00000000..8e71905c --- /dev/null +++ b/tests/KubernetesClient.Tests/assets/ca-bundle.crt @@ -0,0 +1,38 @@ +-----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----- +-----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----- From 48b4cc242035f82ecb627add7cf28cc4d3d55da8 Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Wed, 6 Mar 2019 21:34:06 -0800 Subject: [PATCH 2/7] add some comments --- src/KubernetesClient/CertUtils.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/KubernetesClient/CertUtils.cs b/src/KubernetesClient/CertUtils.cs index d0ea6041..bf813928 100644 --- a/src/KubernetesClient/CertUtils.cs +++ b/src/KubernetesClient/CertUtils.cs @@ -27,10 +27,14 @@ public static IList LoadPemFileCert(string file) .Replace("\r", "") .Replace("\n", ""); + // Retrieve all certificates from the file + // var r = new Regex("-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----"); var matches = r.Matches(certdata); + // Strip the header and footer from each cert and store them in the certificate list + // foreach (Match match in matches) { string certData = match.Value From c884eaefaeaf91f3edc1b3b340d07028c5aa3351 Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Wed, 6 Mar 2019 21:52:36 -0800 Subject: [PATCH 3/7] cleanup --- src/KubernetesClient/Kubernetes.ConfigInit.cs | 8 +++----- .../CertificateValidationTests.cs | 9 ++++++--- .../assets/ca-bundle-intermediate.crt | 19 ------------------- 3 files changed, 9 insertions(+), 27 deletions(-) delete mode 100644 tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index f635fd0d..420ed167 100644 --- a/src/KubernetesClient/Kubernetes.ConfigInit.cs +++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs @@ -270,7 +270,7 @@ public static bool CertificateValidationCallBack( chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; var isValid = chain.Build((X509Certificate2)certificate); - var certMatch = false; + var doesCertMatch = false; var rootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; @@ -280,14 +280,12 @@ public static bool CertificateValidationCallBack( { if (rootCert.RawData.SequenceEqual(cert.RawData)) { - certMatch = true; + doesCertMatch = true; break; } } - isValid = isValid && certMatch; - - return isValid; + return isValid && doesCertMatch; } // In all other cases, return false. diff --git a/tests/KubernetesClient.Tests/CertificateValidationTests.cs b/tests/KubernetesClient.Tests/CertificateValidationTests.cs index 74047bbe..d653c24f 100644 --- a/tests/KubernetesClient.Tests/CertificateValidationTests.cs +++ b/tests/KubernetesClient.Tests/CertificateValidationTests.cs @@ -12,7 +12,7 @@ public class CertificateValidationTests [Fact] public void ValidCert() { - var caCert = new List() { 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; @@ -25,7 +25,7 @@ public void ValidCert() [Fact] public void InvalidCert() { - var caCert = new List() { 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; @@ -39,7 +39,10 @@ public void InvalidCert() public void ValidBundleCert() { var caCert = CertUtils.LoadPemFileCert("assets/ca-bundle.crt"); - var testCert = new X509Certificate2("assets/ca-bundle-intermediate.crt"); + + // Load the intermediate cert + // + var testCert = caCert[0]; var chain = new X509Chain(); var errors = SslPolicyErrors.RemoteCertificateChainErrors; diff --git a/tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt b/tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt deleted file mode 100644 index e8546082..00000000 --- a/tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt +++ /dev/null @@ -1,19 +0,0 @@ ------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----- From 10871bb01684103c3dc6a3e2089015be5a4c7b8b Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Wed, 6 Mar 2019 22:19:56 -0800 Subject: [PATCH 4/7] var --- src/KubernetesClient/Kubernetes.ConfigInit.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index 420ed167..fdcd11ad 100644 --- a/src/KubernetesClient/Kubernetes.ConfigInit.cs +++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs @@ -270,7 +270,7 @@ public static bool CertificateValidationCallBack( chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; var isValid = chain.Build((X509Certificate2)certificate); - var doesCertMatch = false; + var isTrusted = false; var rootCert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; @@ -280,12 +280,12 @@ public static bool CertificateValidationCallBack( { if (rootCert.RawData.SequenceEqual(cert.RawData)) { - doesCertMatch = true; + isTrusted = true; break; } } - return isValid && doesCertMatch; + return isValid && isTrusted; } // In all other cases, return false. From 0f1f1238648715a843a877c815b01b8713e836d6 Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Thu, 7 Mar 2019 11:08:40 -0800 Subject: [PATCH 5/7] Use X509Certificate2cCollection --- src/KubernetesClient/CertUtils.cs | 27 ++++++------------- src/KubernetesClient/Kubernetes.ConfigInit.cs | 10 +++---- ...ubernetesClientConfiguration.ConfigFile.cs | 4 +-- .../KubernetesClientConfiguration.cs | 2 +- src/KubernetesClient/WebSocketBuilder.cs | 2 +- tests/KubernetesClient.Tests/AuthTests.cs | 2 +- .../KubernetesClient.Tests/CertUtilsTests.cs | 20 ++++++++++++++ .../assets/ca-bundle.crt | 6 +++++ 8 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/KubernetesClient/CertUtils.cs b/src/KubernetesClient/CertUtils.cs index bf813928..dd0ba71b 100644 --- a/src/KubernetesClient/CertUtils.cs +++ b/src/KubernetesClient/CertUtils.cs @@ -19,31 +19,20 @@ public static class CertUtils /// /// Path to pem encoded cert file /// List of x509 instances. - public static IList LoadPemFileCert(string file) + public static X509Certificate2Collection LoadPemFileCert(string file) { - var certs = new List(); + var certs = new X509CertificateParser().ReadCertificates(File.OpenRead(file)); + var certCollection = new X509Certificate2Collection(); - var certdata = File.ReadAllText(file) - .Replace("\r", "") - .Replace("\n", ""); - - // Retrieve all certificates from the file - // - var r = new Regex("-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----"); - - var matches = r.Matches(certdata); - - // Strip the header and footer from each cert and store them in the certificate list + // Convert BouncyCastle X509Certificates to the .NET cryptography implementation and add + // it to the certificate collection // - foreach (Match match in matches) + foreach (Org.BouncyCastle.X509.X509Certificate cert in certs) { - string certData = match.Value - .Replace("-----BEGIN CERTIFICATE-----", "") - .Replace("-----END CERTIFICATE-----", ""); - certs.Add(new X509Certificate2(Convert.FromBase64String(certData))); + certCollection.Add(new X509Certificate2(cert.GetEncoded())); } - return certs; + return certCollection; } /// diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index fdcd11ad..11e01a25 100644 --- a/src/KubernetesClient/Kubernetes.ConfigInit.cs +++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; @@ -155,7 +154,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler } #endif - private IList CaCert { get; } + private X509Certificate2Collection CaCert { get; } private bool SkipTlsVerify { get; } @@ -244,7 +243,7 @@ private void SetCredentials(KubernetesClientConfiguration config, HttpClientHand [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Unused by design")] public static bool CertificateValidationCallBack( object sender, - IList caCerts, + X509Certificate2Collection caCerts, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) @@ -262,10 +261,7 @@ public static bool CertificateValidationCallBack( // Added our trusted certificates to the chain // - foreach (var caCert in caCerts) - { - chain.ChainPolicy.ExtraStore.Add(caCert); - } + chain.ChainPolicy.ExtraStore.AddRange(caCerts); chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; var isValid = chain.Build((X509Certificate2)certificate); diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs index 9f73bdaa..fa8c333b 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs @@ -196,11 +196,11 @@ private void SetClusterDetails(K8SConfiguration k8SConfig, Context activeContext if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) { var data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; - SslCaCert = new List() { new X509Certificate2(Convert.FromBase64String(data)) }; + SslCaCert = new X509Certificate2Collection(new X509Certificate2(Convert.FromBase64String(data))); } else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) { - SslCaCert = new List() { new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority)) }; + SslCaCert = new X509Certificate2Collection(new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority))); } } } diff --git a/src/KubernetesClient/KubernetesClientConfiguration.cs b/src/KubernetesClient/KubernetesClientConfiguration.cs index e03ed760..04366d93 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.cs @@ -21,7 +21,7 @@ public partial class KubernetesClientConfiguration /// /// Gets SslCaCert /// - public IList SslCaCert { get; set; } + public X509Certificate2Collection SslCaCert { get; set; } /// /// Gets ClientCertificateData diff --git a/src/KubernetesClient/WebSocketBuilder.cs b/src/KubernetesClient/WebSocketBuilder.cs index 85245be0..3e9ad3c8 100644 --- a/src/KubernetesClient/WebSocketBuilder.cs +++ b/src/KubernetesClient/WebSocketBuilder.cs @@ -53,7 +53,7 @@ public void CleanupServerCertificateValidationCallback(RemoteCertificateValidati #endif #if NETCOREAPP2_1 - public WebSocketBuilder ExpectServerCertificate(IList 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 972f732a..13c319b9 100644 --- a/tests/KubernetesClient.Tests/AuthTests.cs +++ b/tests/KubernetesClient.Tests/AuthTests.cs @@ -222,7 +222,7 @@ public void Cert() Host = server.Uri.ToString(), ClientCertificateData = clientCertificateData, ClientCertificateKeyData = clientCertificateKeyData, - SslCaCert = new List() { serverCertificate }, + SslCaCert = new X509Certificate2Collection(serverCertificate), SkipTlsVerify = false }); diff --git a/tests/KubernetesClient.Tests/CertUtilsTests.cs b/tests/KubernetesClient.Tests/CertUtilsTests.cs index d093c059..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 { @@ -70,5 +73,22 @@ public void LoadFromInlineDataRelativePath() var cert = CertUtils.GeneratePfx(cfg); Assert.NotNull(cert.PrivateKey); } + + /// + /// Checks that the bundle certificate was loaded correctly + /// + [Fact] + public void LoadPemWithMultiCert() + { + 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/assets/ca-bundle.crt b/tests/KubernetesClient.Tests/assets/ca-bundle.crt index 8e71905c..ff887ab9 100644 --- a/tests/KubernetesClient.Tests/assets/ca-bundle.crt +++ b/tests/KubernetesClient.Tests/assets/ca-bundle.crt @@ -1,3 +1,7 @@ +--- Comments to make sure we still parse the cert correctly + +Intermediate Certificate + -----BEGIN CERTIFICATE----- MIIDDTCCAfWgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UEAwwKa3Vi ZXJuZXRlczAeFw0xOTAzMDMxNzA4MDlaFw0yOTAyMjgxNzA4MDlaMBYxFDASBgNV @@ -17,6 +21,8 @@ s3u2XXx5s1M7tqqj3tAEOPCKlohS6mQ4X3wulgpZ1XpJ0WTvcvoPXEtA56k7vX3a 4E6x66LZCFA2ZR/5COv5D055AhrihKL8kbAutxhfA27SJ/MGowzmTT7kVQha3Su3 aoOYZgcUww+SkRSGVrtgMgQ= -----END CERTIFICATE----- + +Root certificate -----BEGIN CERTIFICATE----- MIIDEDCCAfigAwIBAgIJAJpb9irKg2JjMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV BAMMCmt1YmVybmV0ZXMwHhcNMTkwMzAzMDAyMTI3WhcNMzkwMjI2MDAyMTI3WjAV From b7324ab304246b3f328b4a6780fde01de5ae88a9 Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Thu, 7 Mar 2019 11:22:35 -0800 Subject: [PATCH 6/7] add missing asset files --- .../assets/ca-bundle-intermediate.crt | 19 +++++++++++++++++++ .../assets/ca-bundle-root.crt | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/KubernetesClient.Tests/assets/ca-bundle-intermediate.crt create mode 100644 tests/KubernetesClient.Tests/assets/ca-bundle-root.crt 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----- From 621644fb170ffcc9e0a46a8ed410c100cf3961a4 Mon Sep 17 00:00:00 2001 From: Ethan Chang Date: Thu, 7 Mar 2019 14:08:07 -0800 Subject: [PATCH 7/7] address comments --- src/KubernetesClient/Kubernetes.ConfigInit.cs | 35 ++++++++++--------- src/KubernetesClient/Kubernetes.WebSocket.cs | 10 +++--- ...ubernetesClientConfiguration.ConfigFile.cs | 4 +-- ...KubernetesClientConfiguration.InCluster.cs | 2 +- .../KubernetesClientConfiguration.cs | 4 +-- tests/KubernetesClient.Tests/AuthTests.cs | 2 +- .../KubernetesClientConfigurationTests.cs | 4 +-- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index 11e01a25..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,23 +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"); } - var caCert = CaCert[CaCert.Count - 1]; + var certList = new System.Collections.Generic.List(); - using (System.IO.MemoryStream certStream = new System.IO.MemoryStream(caCert.RawData)) + foreach (X509Certificate2 caCert in CaCerts) { - Java.Security.Cert.Certificate cert = Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream); - Xamarin.Android.Net.AndroidClientHandler handler = (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler; - - handler.TrustedCerts = new System.Collections.Generic.List() + 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; } } @@ -102,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") @@ -124,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"); } @@ -132,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 } @@ -154,7 +157,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler } #endif - private X509Certificate2Collection CaCert { get; } + private X509Certificate2Collection CaCerts { get; } private bool SkipTlsVerify { get; } 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 fa8c333b..84a23a66 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs @@ -196,11 +196,11 @@ private void SetClusterDetails(K8SConfiguration k8SConfig, Context activeContext if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) { var data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; - SslCaCert = new X509Certificate2Collection(new X509Certificate2(Convert.FromBase64String(data))); + SslCaCerts = new X509Certificate2Collection(new X509Certificate2(Convert.FromBase64String(data))); } else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) { - SslCaCert = new X509Certificate2Collection(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 04366d93..73e6c6fc 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.cs @@ -19,9 +19,9 @@ public partial class KubernetesClientConfiguration public string Host { get; set; } /// - /// Gets SslCaCert + /// Gets SslCaCerts /// - public X509Certificate2Collection SslCaCert { get; set; } + public X509Certificate2Collection SslCaCerts { get; set; } /// /// Gets ClientCertificateData diff --git a/tests/KubernetesClient.Tests/AuthTests.cs b/tests/KubernetesClient.Tests/AuthTests.cs index 13c319b9..22e3f03a 100644 --- a/tests/KubernetesClient.Tests/AuthTests.cs +++ b/tests/KubernetesClient.Tests/AuthTests.cs @@ -222,7 +222,7 @@ public void Cert() Host = server.Uri.ToString(), ClientCertificateData = clientCertificateData, ClientCertificateKeyData = clientCertificateKeyData, - SslCaCert = new X509Certificate2Collection(serverCertificate), + SslCaCerts = new X509Certificate2Collection(serverCertificate), SkipTlsVerify = false }); 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); }