From d7f9eb9a2e7791acb0cb13e5af85024bd9b7fc90 Mon Sep 17 00:00:00 2001 From: Fraser Waters Date: Fri, 25 Sep 2020 17:45:12 +0100 Subject: [PATCH] Fix format of ClientCertificate[Key]Data from auth plugins (#480) * Fix format of ClientCertificate[Key]Data from auth plugins * Add external certificate tests to AuthTests * format --- ...ubernetesClientConfiguration.ConfigFile.cs | 7 +- tests/KubernetesClient.Tests/AuthTests.cs | 78 +++++++++++++++++-- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs index 7c4a2b0..5a19236 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs @@ -391,8 +391,11 @@ namespace k8s var (accessToken, clientCertificateData, clientCertificateKeyData) = ExecuteExternalCommand(userDetails.UserCredentials.ExternalExecution); AccessToken = accessToken; - ClientCertificateData = clientCertificateData; - ClientCertificateKeyData = clientCertificateKeyData; + // When reading ClientCertificateData from a config file it will be base64 encoded, and code later in the system (see CertUtils.GeneratePfx) + // expects ClientCertificateData and ClientCertificateKeyData to be base64 encoded because of this. However the string returned by external + // auth providers is the raw certificate and key PEM text, so we need to take that and base64 encoded it here so it can be decoded later. + ClientCertificateData = clientCertificateData == null ? null : Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(clientCertificateData)); + ClientCertificateKeyData = clientCertificateKeyData == null ? null : Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(clientCertificateKeyData)); userCredentialsFound = true; } diff --git a/tests/KubernetesClient.Tests/AuthTests.cs b/tests/KubernetesClient.Tests/AuthTests.cs index 139b8e4..64359a9 100644 --- a/tests/KubernetesClient.Tests/AuthTests.cs +++ b/tests/KubernetesClient.Tests/AuthTests.cs @@ -268,6 +268,71 @@ namespace k8s.Tests } } + [OperatingSystemDependentFact(Exclude = OperatingSystem.OSX | OperatingSystem.Windows)] + public void ExternalCertificate() + { + const string name = "testing_irrelevant"; + + var serverCertificateData = Convert.FromBase64String(File.ReadAllText("assets/apiserver-pfx-data.txt")); + + var clientCertificateKeyData = Convert.FromBase64String(File.ReadAllText("assets/client-key-data.txt")); + var clientCertificateData = Convert.FromBase64String(File.ReadAllText("assets/client-certificate-data.txt")); + + X509Certificate2 serverCertificate = null; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + using (MemoryStream serverCertificateStream = new MemoryStream(serverCertificateData)) + { + serverCertificate = OpenCertificateStore(serverCertificateStream); + } + } + else + { + serverCertificate = new X509Certificate2(serverCertificateData, ""); + } + + var clientCertificate = new X509Certificate2(clientCertificateData, ""); + + var clientCertificateValidationCalled = false; + + using (var server = new MockKubeApiServer(testOutput, listenConfigure: options => + { + options.UseHttps(new HttpsConnectionAdapterOptions + { + ServerCertificate = serverCertificate, + ClientCertificateMode = ClientCertificateMode.RequireCertificate, + ClientCertificateValidation = (certificate, chain, valid) => + { + clientCertificateValidationCalled = true; + return clientCertificate.Equals(certificate); + }, + }); + })) + { + { + var clientCertificateText = Encoding.ASCII.GetString(clientCertificateData).Replace("\n", "\\n"); + var clientCertificateKeyText = Encoding.ASCII.GetString(clientCertificateKeyData).Replace("\n", "\\n"); + var responseJson = $"{{\"apiVersion\":\"testingversion\",\"status\":{{\"clientCertificateData\":\"{clientCertificateText}\",\"clientKeyData\":\"{clientCertificateKeyText}\"}}}}"; + var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), responseJson, name); + var clientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name); + var client = new Kubernetes(clientConfig); + var listTask = ExecuteListPods(client); + Assert.True(listTask.Response.IsSuccessStatusCode); + Assert.Equal(1, listTask.Body.Items.Count); + } + { + var clientCertificateText = File.ReadAllText("assets/client.crt").Replace("\n", "\\n"); + var clientCertificateKeyText = File.ReadAllText("assets/client.key").Replace("\n", "\\n"); + var responseJson = $"{{\"apiVersion\":\"testingversion\",\"status\":{{\"clientCertificateData\":\"{clientCertificateText}\",\"clientKeyData\":\"{clientCertificateKeyText}\"}}}}"; + var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), responseJson, name); + var clientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name); + var client = new Kubernetes(clientConfig); + Assert.ThrowsAny(() => ExecuteListPods(client)); + Assert.True(clientCertificateValidationCalled); + } + } + } #endif // NETCOREAPP2_1 [Fact] @@ -292,7 +357,9 @@ namespace k8s.Tests })) { { - var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), token, name); + + var responseJson = $"{{\"apiVersion\":\"testingversion\",\"status\":{{\"token\":\"{token}\"}}}}"; + var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), responseJson, name); var clientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name); var client = new Kubernetes(clientConfig); var listTask = ExecuteListPods(client); @@ -300,7 +367,8 @@ namespace k8s.Tests Assert.Equal(1, listTask.Body.Items.Count); } { - var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), "wrong token", name); + var responseJson = "{\"apiVersion\":\"testingversion\",\"status\":{\"token\":\"wrong_token\"}}"; + var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), responseJson, name); var clientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name); var client = new Kubernetes(clientConfig); var listTask = ExecuteListPods(client); @@ -398,7 +466,7 @@ namespace k8s.Tests return certificate; } - private K8SConfiguration GetK8SConfiguration(string serverUri, string token, string name) + private K8SConfiguration GetK8SConfiguration(string serverUri, string responseJson, string name) { const string username = "testinguser"; @@ -407,8 +475,6 @@ namespace k8s.Tests new Context {Name = name, ContextDetails = new ContextDetails {Cluster = name, User = username } }, }; - var responseJson = $"{{\"apiVersion\": \"testingversion\", \"status\": {{\"token\": \"{token}\"}}}}"; - { var clusters = new List { @@ -428,7 +494,7 @@ namespace k8s.Tests var arguments = new string[] { }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - arguments = ($"/c echo {responseJson}").Split(" "); + arguments = new[] { "/c", "echo", responseJson }; } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))