diff --git a/examples/simple/PodList.cs b/examples/simple/PodList.cs index f65f8d133..f1646f35d 100755 --- a/examples/simple/PodList.cs +++ b/examples/simple/PodList.cs @@ -10,11 +10,15 @@ static void Main(string[] args) { var k8sClientConfig = new KubernetesClientConfiguration(); IKubernetes client = new Kubernetes(k8sClientConfig); + Console.WriteLine("Starting Request!"); var listTask = client.ListNamespacedPodWithHttpMessagesAsync("default").Result; var list = listTask.Body; foreach (var item in list.Items) { Console.WriteLine(item.Metadata.Name); } + if (list.Items.Count == 0) { + Console.WriteLine("Empty!"); + } } } } diff --git a/examples/simple/simple.csproj b/examples/simple/simple.csproj index b965b51a8..93ece02c5 100755 --- a/examples/simple/simple.csproj +++ b/examples/simple/simple.csproj @@ -8,7 +8,7 @@ Exe - netcoreapp1.1 + netcoreapp2.0 diff --git a/src/KubeConfigModels/ClusterEndpoint.cs b/src/KubeConfigModels/ClusterEndpoint.cs index fef17864c..7bc60ba16 100644 --- a/src/KubeConfigModels/ClusterEndpoint.cs +++ b/src/KubeConfigModels/ClusterEndpoint.cs @@ -4,6 +4,9 @@ public class ClusterEndpoint { + [YamlMember(Alias = "certificate-authority")] + public string CertificateAuthority {get; set; } + [YamlMember(Alias = "certificate-authority-data")] public string CertificateAuthorityData { get; set; } diff --git a/src/KubeConfigModels/K8SConfiguration.cs b/src/KubeConfigModels/K8SConfiguration.cs index 7c490988e..723b360d1 100644 --- a/src/KubeConfigModels/K8SConfiguration.cs +++ b/src/KubeConfigModels/K8SConfiguration.cs @@ -8,6 +8,9 @@ /// public class K8SConfiguration { + [YamlMember(Alias = "preferences")] + public IDictionary preferences{ get; set; } + [YamlMember(Alias = "apiVersion")] public string ApiVersion { get; set; } diff --git a/src/KubeConfigModels/UserCredentials.cs b/src/KubeConfigModels/UserCredentials.cs index 04bef9985..2300de31d 100644 --- a/src/KubeConfigModels/UserCredentials.cs +++ b/src/KubeConfigModels/UserCredentials.cs @@ -1,5 +1,6 @@ namespace k8s.KubeConfigModels { + using System.Collections.Generic; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; @@ -8,9 +9,15 @@ public class UserCredentials [YamlMember(Alias = "client-certificate-data")] public string ClientCertificateData { get; set; } + [YamlMember(Alias = "client-certificate")] + public string ClientCertificate { get; set; } + [YamlMember(Alias = "client-key-data")] public string ClientKeyData { get; set; } + [YamlMember(Alias = "client-key")] + public string ClientKey { get; set; } + [YamlMember(Alias = "token")] public string Token { get; set; } @@ -19,5 +26,8 @@ public class UserCredentials [YamlMember(Alias = "password")] public string Password { get; set; } + + [YamlMember(Alias = "auth-provider")] + public Dictionary AuthProvider { get; set; } } } diff --git a/src/Kubernetes.Auth.cs b/src/Kubernetes.Auth.cs index d3722280a..7ea4fcdd3 100644 --- a/src/Kubernetes.Auth.cs +++ b/src/Kubernetes.Auth.cs @@ -21,7 +21,7 @@ public Kubernetes(KubernetesClientConfiguration config) { this.Initialize(); - this.CaCert = Utils.Base64Decode(config.SslCaCert); + this.CaCert = config.SslCaCert; this.BaseUri = new Uri(config.Host); // ssl cert validation @@ -45,7 +45,7 @@ public Kubernetes(KubernetesClientConfiguration config) this.InitializeHttpClient(handler); } - private string CaCert { get; set; } + private X509Certificate2 CaCert { get; set; } /// /// Set credentials for the Client @@ -65,7 +65,10 @@ private async Task SetCredentialsAsync(KubernetesClientConfiguration config, Htt this.Credentials = new KubernetesClientCredentials(config.Username, config.Password); } // othwerwise set handler for clinet cert based auth - else if (!string.IsNullOrWhiteSpace(config.ClientCertificateData) && !string.IsNullOrWhiteSpace(config.ClientCertificateKey)) + else if ((!string.IsNullOrWhiteSpace(config.ClientCertificateData) || + !string.IsNullOrWhiteSpace(config.ClientCertificate)) && + (!string.IsNullOrWhiteSpace(config.ClientCertificateKey) || + !string.IsNullOrWhiteSpace(config.ClientKey))) { var pfxFilePath = await Utils.GeneratePfxAsync(config).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(pfxFilePath)) @@ -110,7 +113,7 @@ private async Task SetCredentialsAsync(KubernetesClientConfiguration config, Htt chain0.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // add all your extra certificate chain - chain0.ChainPolicy.ExtraStore.Add(new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(this.CaCert))); + chain0.ChainPolicy.ExtraStore.Add(this.CaCert); chain0.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; var isValid = chain0.Build((X509Certificate2)certificate); return isValid; diff --git a/src/KubernetesClientConfiguration.cs b/src/KubernetesClientConfiguration.cs index 55fd5111d..71fcb42ac 100644 --- a/src/KubernetesClientConfiguration.cs +++ b/src/KubernetesClientConfiguration.cs @@ -3,6 +3,7 @@ namespace k8s using System; using System.IO; using System.Linq; + using System.Security.Cryptography.X509Certificates; using k8s.Exceptions; using k8s.KubeConfigModels; using YamlDotNet.Serialization; @@ -49,7 +50,7 @@ public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentC /// /// Gets SslCaCert /// - public string SslCaCert { get; private set; } + public X509Certificate2 SslCaCert { get; private set; } /// /// Gets ClientCertificateData @@ -61,6 +62,16 @@ public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentC /// public string ClientCertificateKey { get; private set; } + /// + /// Gets ClientCertificate filename + /// + public string ClientCertificate { get; private set; } + + /// + /// Gets ClientCertificate Key filename + /// + public string ClientKey { get; private set; } + /// /// Gets a value indicating whether to skip ssl server cert validation /// @@ -145,13 +156,20 @@ private void Initialize(K8SConfiguration k8SConfig, string currentContext = null } if (!clusterDetails.ClusterEndpoint.SkipTlsVerify && - string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) + string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData) && + string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthority)) { - throw new KubeConfigException($"certificate-authority-data not found for current-context :{activeContext} in kubeconfig"); + throw new KubeConfigException($"neither certificate-authority-data nor certificate-authority not found for current-context :{activeContext} in kubeconfig"); } this.Host = clusterDetails.ClusterEndpoint.Server; - this.SslCaCert = clusterDetails.ClusterEndpoint.CertificateAuthorityData; + if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) { + string data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; + this.SslCaCert = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(Utils.Base64Decode(data))); + } + else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) { + this.SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority, null); + } this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; } else @@ -202,6 +220,13 @@ private void SetUserDetails(User userDetails) userCredentialsFound = true; } + if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificate) && + !string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKey)) { + this.ClientCertificate = userDetails.UserCredentials.ClientCertificate; + this.ClientKey = userDetails.UserCredentials.ClientKey; + userCredentialsFound = true; + } + if (!userCredentialsFound) { throw new KubeConfigException($"User: {userDetails.Name} does not have appropriate auth credentials in kube config"); diff --git a/src/Utils.cs b/src/Utils.cs index 3459ff2c4..4db4fa6ea 100644 --- a/src/Utils.cs +++ b/src/Utils.cs @@ -45,23 +45,35 @@ public static async Task GeneratePfxAsync(KubernetesClientConfiguration var certDirPath = Path.Combine(userHomeDir, ".k8scerts"); Directory.CreateDirectory(certDirPath); + var keyFilePath = ""; + var certFilePath = ""; + var filePrefix = config.CurrentContext; - var keyFilePath = Path.Combine(certDirPath, filePrefix + "key"); - var certFilePath = Path.Combine(certDirPath, filePrefix + "cert"); var pfxFilePath = Path.Combine(certDirPath, filePrefix + "pfx"); - - using (FileStream fs = File.Create(keyFilePath)) - { - byte[] info = Convert.FromBase64String(config.ClientCertificateKey); - await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(config.ClientCertificateKey)) { + keyFilePath = Path.Combine(certDirPath, filePrefix + "key"); + using (FileStream fs = File.Create(keyFilePath)) + { + byte[] info = Convert.FromBase64String(config.ClientCertificateKey); + await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false); + } } - - using (FileStream fs = File.Create(certFilePath)) - { - byte[] info = Convert.FromBase64String(config.ClientCertificateData); - await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(config.ClientKey)) { + keyFilePath = config.ClientKey; } + if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) { + certFilePath = Path.Combine(certDirPath, filePrefix + "cert"); + + using (FileStream fs = File.Create(certFilePath)) + { + byte[] info = Convert.FromBase64String(config.ClientCertificateData); + await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false); + } + } + if (!string.IsNullOrWhiteSpace(config.ClientCertificate)) { + certFilePath = config.ClientCertificate; + } var process = new Process(); process.StartInfo = new ProcessStartInfo() { diff --git a/tests/KubernetesClientConfigurationTests.cs b/tests/KubernetesClientConfigurationTests.cs index 81c8b8d53..c4baefb3d 100755 --- a/tests/KubernetesClientConfigurationTests.cs +++ b/tests/KubernetesClientConfigurationTests.cs @@ -8,6 +8,11 @@ namespace k8s.Tests public class KubernetesClientConfigurationTests { + public static string readLine(string fileName) { + StreamReader reader = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read)); + return reader.ReadLine(); + } + /// /// This file contains a sample kubeconfig file /// @@ -110,16 +115,33 @@ public void ContextUserTokenTest(string context, string token) /// 'client-certificate-data' node content /// 'client-key-data' content [Theory] - [InlineData("federal-context", "path/to/my/client/cert" ,"path/to/my/client/key")] - public void ContextCertificateTest(string context, string clientCertData, string clientCertKey) + [InlineData("federal-context", "assets/client.crt" ,"assets/client.key")] + public void ContextCertificateTest(string context, string clientCert, string clientCertKey) + { + var fi = new FileInfo(kubeConfigFileName); + var cfg = new KubernetesClientConfiguration(fi, context); + Assert.Equal(context, cfg.CurrentContext); + Assert.Equal(cfg.ClientCertificate, clientCert); + Assert.Equal(cfg.ClientKey, clientCertKey); + } + + /// + /// Checks if certificate-based authentication is loaded properly from the config file, per context + /// + /// Context to retreive the configuration + [Theory] + [InlineData("victorian-context")] + public void ClientDataTest(string context) { var fi = new FileInfo(kubeConfigFileName); var cfg = new KubernetesClientConfiguration(fi, context); Assert.Equal(context, cfg.CurrentContext); - Assert.Equal(cfg.ClientCertificateData, clientCertData); - Assert.Equal(cfg.ClientCertificateKey, clientCertKey); + Assert.NotNull(cfg.SslCaCert); + Assert.Equal(readLine("assets/client-certificate-data.txt"), cfg.ClientCertificateData); + Assert.Equal(readLine("assets/client-key-data.txt"), cfg.ClientCertificateKey); } + /// /// Test that an Exception is thrown when initializating a KubernetClientConfiguration whose config file Context is not present /// diff --git a/tests/assets/ca-data.txt b/tests/assets/ca-data.txt new file mode 100644 index 000000000..91eaee791 --- /dev/null +++ b/tests/assets/ca-data.txt @@ -0,0 +1 @@ +LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURERENDQWZTZ0F3SUJBZ0lSQUo5ZCtLeThkTDJVSzRjdXplMmo2WnN3RFFZSktvWklodmNOQVFFTEJRQXcKTHpFdE1Dc0dBMVVFQXhNa1lXRTBZVFV3T0RZdE0yVm1aaTAwWWpCa0xUbGxORGt0WmpNeVpXWXpabUpqWWpNNApNQjRYRFRFM01ESXlOakExTURRek5Gb1hEVEl5TURJeU5UQTFNRFF6TkZvd0x6RXRNQ3NHQTFVRUF4TWtZV0UwCllUVXdPRFl0TTJWbVppMDBZakJrTFRsbE5Ea3Raak15WldZelptSmpZak00TUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBM2dkandhdHNsdCsvQVpqV3hmbkNQeGZqMzNHUUxlOU00VU42VmEwRQpKd0FYL2R3L1ZVa0dvVjlDc3NKRUZMdEdTUnM2K2h0RTEvOUN3ak1USDh2WExKcURHTE9KdFQ5dW9sR2c2Q2k1ClBKNDNKelVLWmJlYVE4Z3hhZndzQjdQU05vTTJOYzROVm9lZzBVTUw0bndGeEhXeTNYWHlFZ0QxTWxTUnVrb3oKTTNoRUVxUjJNVFdrNm9KK3VJNFF4WVZWMnZuWXdXaEJwUDlDR3RWUTlyUW9MVFowcmFpOCtDYURBMVltTWRhbQpRYUVPdURlSFRqU2FYM2dyR0FBVVFWNWl6MC9qVVBuK3lJNm1iV0trbzFzNytPY1dZR2F1aDFaMzFYSjJsc0RTCnU4a3F0d215UEcyUVl2aUQ4YjNOWFAyY0dRK2EwZlpRZnBrbTF0U3IxQnhhaXdJREFRQUJveU13SVRBT0JnTlYKSFE4QkFmOEVCQU1DQWdRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQQpuVzFXVXlLbVJ0TlNzU1VzVFBSUnhFRzhhek9kdjdYeUhRL0R5VWNqWm9rUEJVVHY4VjdvNG96RHgyVHV6UEdYCmZ2YlMvT2g0VDd6ZlYxdjJadmU3dTBxelNiRTl5OGpsaDNxYXJEcEd5ZmlTamwycmhIOFBmay9sZGR0VFpVL04KSkVtYW5ReGl6R20xV2pCSklRSE5LZENneVIwN3A1c0MwNnR3K25YUytla1MxMlBUTG45WjBuRDBKVDdQSzRXQgpQc3ZXeDVXN0w5dnJIdVN5SGRSTkt5eEEvbWI1WHdXMDBkZUpmaHZub0p3ZWRYNDVKZVRiME5MczUzaURqVEU1CnRpdU03Z1RVSjlCcGZTL0gvYSt2SmovVWQ2bHM0QndrWmpUNHNhOTA1bnNzdnRqamlwZ1N5a0QzVkxCQ3VueTkKd1NnbE1vSnZNWmg0bC9FVFJPeFE3Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/tests/assets/ca.crt b/tests/assets/ca.crt new file mode 100644 index 000000000..19c0b0d53 --- /dev/null +++ b/tests/assets/ca.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5zCCAc+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p +a3ViZUNBMB4XDTE3MDcyNDA1NDExNloXDTI3MDcyMjA1NDExNlowFTETMBEGA1UE +AxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMfj +J9bJarUsN5Ynt/sbDFQQLp6BeHPXOcUdbNan1YbXdFGN8qLKkkQz0YY1hcVGrXdj +3vd2s8x9XlOyQPZ1SX4vJa5x/67BzFdxbCLg6jBYAisGvYu0hV4jvhHYOZH8sWUp +6n+gPm5c3J8gjqAmM0VwpvtG9HBIr1MWQ4HSTCBVoPvuG9TkOyxrB9RCha16hG7j +B3m9XNEkRVl1xvW6wkeTO4n5cFSoDG0bfCnnjf0oz+pf0yJoSHbl/f2jI/rggMft +0R0LJfqdGlNCKCuN4g0jMmf26313oe+7i8uU4ut9iM1OBv6vD+xy115DGYG7EQIy +lC1rd+gNlGQSxDafAb8CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW +MBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAnXDZUdfC22zyFpZ+Rez3tyk9SqpOeiN1xGirZ5obDgvOS9vSR +GLrsdN4UtXfGpKeMNQJV4e9YDz1ehLd1MK1BoxDVZHB0Sm2QxuyA4EyPfpHH9zaY +qoRgDeUKBmCteLLcY3ukOzGf915j+lWQHv+tk52gvHfxvRyEuawSxSnowkGGFY9R +6AQ2cFm7G3SdygRWVXT1hk5hVQXvBY9DNU1YNvN0qWE6ss5RHJ/cxHFWtrdcr86K +DqW9Ylr1l2iwkWpnXR4OMK3ZFjwX/qi11Z8eMDOi+0FxZ/6BkGQxe7X6D2GjCZ3r +Lfbj0HBpynkd6lfLmIWgEzGYxrQjvczbAKBD +-----END CERTIFICATE----- diff --git a/tests/assets/client-certificate-data.txt b/tests/assets/client-certificate-data.txt new file mode 100644 index 000000000..5e1d8e7ad --- /dev/null +++ b/tests/assets/client-certificate-data.txt @@ -0,0 +1 @@ +LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlEUkRDQ0FpeWdBd0lCQWdJSUVIVm8zVzZXcnJjd0RRWUpLb1pJaHZjTkFRRUxCUUF3RXpFUk1BOEdBMVVFQXd3SVlXTnphemh6DQpZMkV3SGhjTk1UY3dOREU1TURBd01EQXdXaGNOTVRrd05ERTVNREF3TURBd1dqQVZNUk13RVFZRFZRUUREQXByZFdKbFkyOXVabWxuDQpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXVEZ0ttVUxsU0ZSTStCN0pNMWMxUEJqS25mWlZpL1d1DQpSUks2TE9xNHpzL1UxanUyN21TbE9HbWJxRWVYTlpCRVFCblYvVGptQXpqM0Zwd2VKbW5PdDY0TTFIeXZRUW8yTC9LS2JlOVlPbUxNDQpRWXZyeE9QeFRnZHVJeDYzTGxKSUJRZERqdVFhWUhBc0FqTllhbTB3UmQwZTNhVkUxeEJnVDA4QkVWZnVwZmUrUnpXUWhVemJXYkhDDQpydmU2b2s4aHQzRk9LYjNxRzhyR3UrYzN4bGM1MXNVa2ovMGhMK0xrZVBWOTBRYVVCRjF6czlDMVZGaVhoT1Z6WlRjQ0lWQThSTDNvDQpxRTdGUEwvczBNc0RsdkRoNkZ3ZFkvUWhwSUtRMkdKTHQ0ZGRobk5yYW5GbS9pM1RzQ3FJSWxieHl5TVRsT09YYytOUG9mZW9EUUcvDQpvQldjTVFJREFRQUJvNEdaTUlHV01FSUdBMVVkSXdRN01EbUFGSmcrY0p4ZHpaUzNabzdjajFjQXJFWjNxREh1b1Jla0ZUQVRNUkV3DQpEd1lEVlFRRERBaGhZM05yT0hOallZSUlGTjZKejlmRStOVXdIUVlEVlIwT0JCWUVGTmJxZURyeHRvbWZFYnd5bmFUaXp4WXVmeVRXDQpNQXdHQTFVZEV3RUIvd1FDTUFBd0RnWURWUjBQQVFIL0JBUURBZ1dnTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNQ01BMEdDU3FHDQpTSWIzRFFFQkN3VUFBNElCQVFCYkFKdjJDRzRnTDJtNlpiQmJKSEtqMVF5MGtmbUFJdWgyZHNISU5vUHZ4V0lqY3VXeHZlbUpRcndUDQpuUEZPeWFMY3VtNmVmcGZhWG04T2hxNWxjT3NldUtYaDVya2ZuRUp1VmNWYk9UNXY2aUU3TEY5eGMrelVlTkZDWjA1a1QwbU52eW9lDQpIVmhkYlljeG8yOXVwbGVqa3RqZXd0eGZBZjM2a25hQytOVGE3dVZyNVQ0aTd4MUNQMGkvcUY5ZVgwVHJqZ0diM09MTHVwcVhMSzd5DQpUWUllK3NCajNjOGZRNit6RUJDVFNib05DRlRldXpjOTMxclltc1VyZGtIdEp2SkI2aSthMG5iRjB4MnRYY3ZRbVJ6aUwzTGR0UG84DQptSWh0bDRRWXBQZG5Qdk1RWk1MMlZ4T0FqN3V0RnhNMW44OEpKV0NaTkNyUUdYZjBLSDFpdHhwNg0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ0K \ No newline at end of file diff --git a/tests/assets/client-key-data.txt b/tests/assets/client-key-data.txt new file mode 100644 index 000000000..73ad9a5f1 --- /dev/null +++ b/tests/assets/client-key-data.txt @@ -0,0 +1 @@ +LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRXBBSUJBQUtDQVFFQWtweU5GZ1F0N1Yxbml6U0N6TzdodmhLQXVyUFlSR2ExZEd3OXNQQVU4d2ovd1E4dA0KZUVGRTBwejRJTmxuUHhvQVZhUnRSVXowUHVnRmhLSUVVWEZCUnF1bEF5VS8ySjhMbzFWVWVUK2V1TTZ1cWdMUQ0KdFRqVlIxMUh3YU9jMktoUGl1VUlZaDMxNm5BSHVaMnBUbnpqTFBBM2lqY2RndVZxSUJydnpJTXVLU25lMmJQZA0KT2pqN2dBa3RIQ2Z0VHlTUlorOE94TzMzVFRqTEpDQ1RJZUYrUkFWQnl6VWUybDd5WDkrSzFjekwrbFh1TWh6Lw0KeSt6cFp1NW9VeUJXenA4T0M4aXNSNmxURFVmaCtGNU5zckxmcXIwV21wVlVlalV4VDU0ODNJeGp5SEJ2SENuUg0KYWdVZ3JOd080aWNEMzA4WmZYQjQ3TGk1TGJnRmlZdzNhMGhKOHdJREFRQUJBb0lCQVFDSExQSmtUend0TTBhSg0Kc2NPN3diQy9kLzNhcG9FTEtBdTdxU1VFSTRlZFFUblZ2ZkcxZzdBNTQrSmZIek96bVh5ZmVmZ0p3bGNjUFA4Qg0KZUFRMU53L3hZY2JGN0swK1EvNE4vVm5pWlFjcDY5NWRBRFdBTVVOc0JoUm45RHRrQTJNN2M4cFQ5TDNOQnJnSQ0KcXRYMGVPOVllcThxcjRCMU5LS3hzVVJSc3MxM3d5d2R4bzMwbFJ1WlhjMnUvS1EweFRRbTR4M1RRRVpIUWhBNg0KN2E2UWM3MThPMW1ISWQrRERTbStab2l0YnZpR24ySnQ1aWsrUFNIajhVY01ITkoxYVUvMyt5YmVGUU93L3MrTg0KMWMxZDJsS0c1K0k0UW9FeEFEUWxrWEZQeUFISGg3bWhmdGNJT3lXUUdMYVcraVQrSklGYUMyS3dBSCtmM1g0TA0KWTVJUTlzVzVBb0dCQU50NjFtTlNYTWlncVdvSVdzM1pMN1hpWDdJOThOU1ZrMjJGRHNLcldKSzN1bkRWSlVnbg0KbEpzaGo0L2cwc1IvSlRLT2FiUTFVNWJCSFpzRmF3UGd1K2RPWWp3TWtkL0tUcThmT2tUSXhpaHBvYVNvcDF0WA0KS001ZUpVcitjSVpRcXloc3dvUzg5R3ZGSnNFSXo5WUpzOHlIOGlvdElLL2N5ZnpnTTVZQmUzL25Bb0dCQUtzQg0KdjhIVzNkenEwTW1QY1VhTTk5L1VlOG9QYmcrOElaSFl2M0Jicm43TEM5czNVVUVRY1cxTUMrYXdYSHd0YXg0OA0KNjdvWWRaY2YzbWZxR2M1NlIwUWpBdm1jcGt4dXY2bVpWYzk1Z1p5TnoyV0Z4ZXdTdWlQQUQrSjZwLzE3WkF4ag0KU1NZejRSRDZ0bk1HNXhYd1BZcVRGdlNiajNJdlAzWjN1bVhlZVZRVkFvR0FJUklpU2JiNFdoYXpVRkhQOU9KNw0KNk9raVNFWkRDcFU3TUs1eTZleE5uVlhuMXVhWEJvZElRaFBOZ1AxOFpMVlAwbzFZNmhsNjdlVk5ZN1Z1U1poTQ0KVis4Y3FEV3dxVXJmamsyalJ3Z0ZxOE0yNHd1eW8vVy9KVkZVUnplTmx2K0N1c1dnamNXM0FxSWpVOFRUSkVveg0KK0dhbm02bFBHcENuTXQ1Smg1blRsUk1DZ1lCaDAyOVNRVXZNWEZ3eVlpZW45MjY1a2dGNjF4YmNVNEJVd2N6Sw0KUnZoc3VsVS9BOGNzMUc0KzJ6a1hzQjVJUDJjKzloVFg4YlFiVHZUWk4xZXFBZE9SREFKZ3J4ZUhNL2ZhMnNmMw0KRjRobytjc0NSZU95cUFmcCthNWNubFJKc3ZQS0RtWkRQM3hxVFRKZ1hUdUhGbGJ1VmpUVW04VVV5aWlnNmQ4cw0KOVhjcy9RS0JnUUMyZDdSSDc2VFBodFNCV1I3UVhJcVJqYitIeDBVcFk3WkJBRCtzZ1lXakZRZVBNVVE1V2JUbA0KTERZekl6WDNyQnlIVEF2TXptcHFoWTY3b28rRi9DazhmN3JCOS9IckduSUowY2pIL2twbnZLZWpqSnZDdWhOdA0KNUxsaFE4NDVteXIrVWlkVEc1cWVReXloaHZMVDFqNzhLRmRGdnpMRzZGWEREZ1d2emxBQXVRPT0NCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tDQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= \ No newline at end of file diff --git a/tests/assets/client.crt b/tests/assets/client.crt new file mode 100644 index 000000000..65def472b --- /dev/null +++ b/tests/assets/client.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbjCCAlagAwIBAgIJAJD2bN6nz4ieMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJXQTENMAsGA1UEBwwEVGVzdDEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE3MDcyNzA0NTEyNloXDTE4MDcyNzA0 +NTEyNlowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMQ0wCwYDVQQHDARUZXN0 +MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDG8khxm/q6ls7TTKlKtTJgiPh6Gh7w1dCJG04/ +hyKuXMpx1WM0i943/M51k/X9XTuHgCOB/s191yBpNa6pHYgtuQGnzoMdHbRayggH +3i9x9jwEaf6RXFIuedRANzih+pVlDkXJftebE/YJwoOWes9SBPKqPOaGrC2A0Nqc +s0iiZ/4oil3cXQYyzakwKQ9Mhb6M3Uw5OlfUCJXfAjsg5AIgrtYy//tXu3Cz+zIn +tGvA3iWRIJfpCOziR3mMQc9GnFSeUHZCu/bFPCbc5yQ2Jy5Pm1y0coo/iPTEOw10 +RYXocr54lr854rtihHvKat5c/bkJQjpr8Mgy8qeVbAXzVtgRAgMBAAGjUzBRMB0G +A1UdDgQWBBR9trLzkcB5I17ZGBeGhcjX8Ae5GTAfBgNVHSMEGDAWgBR9trLzkcB5 +I17ZGBeGhcjX8Ae5GTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IB +AQCXmgQLziUsWh0jDX/LhqwQxIM2YZDm8+K60my9gnWFv9B6OEvj3gilB69B0tuP +YaJcpWhhzO+/pE8vxOBGQz731M7Er3TGpTrmBD/5bj9NJ71u0FxYjVA8lwbaQced +l8bskTBAAiEZTzEEfdBHcHJXvpzLcnRZ1uNin3EYyyfl5dsZyHfyHUtMiMqzuEFP +8gusgANC3s0to41mZ6S3YJB/TcVKarFAYBg7FYQbIn2+qQg6B6itcZ8FzquffPDO +7od/iwL7Chh/AkrGQcRlHPZTzsDlpat2+0zHBYAVYVbuQMNaCD3IRh80Ufi+DpxL +osKY7Mkcwj1Nmoz4JPoL0wYI +-----END CERTIFICATE----- diff --git a/tests/assets/client.key b/tests/assets/client.key new file mode 100644 index 000000000..7a69840bf --- /dev/null +++ b/tests/assets/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDG8khxm/q6ls7T +TKlKtTJgiPh6Gh7w1dCJG04/hyKuXMpx1WM0i943/M51k/X9XTuHgCOB/s191yBp +Na6pHYgtuQGnzoMdHbRayggH3i9x9jwEaf6RXFIuedRANzih+pVlDkXJftebE/YJ +woOWes9SBPKqPOaGrC2A0Nqcs0iiZ/4oil3cXQYyzakwKQ9Mhb6M3Uw5OlfUCJXf +Ajsg5AIgrtYy//tXu3Cz+zIntGvA3iWRIJfpCOziR3mMQc9GnFSeUHZCu/bFPCbc +5yQ2Jy5Pm1y0coo/iPTEOw10RYXocr54lr854rtihHvKat5c/bkJQjpr8Mgy8qeV +bAXzVtgRAgMBAAECggEASrptPcdyOa42CCaOnJJNVvd8JhkzsBEQYL/R94jiHQ6b +uICH4A/9q5gZUQ7/4min2LDoJYc5VuB8uyg/8CQ4p7wLhCXNGB21RjkHJTVvKuZs +Cthpl95OvEhk0q4rZqSCg1AGJLaxc/3eeDIJTXfZ8hwLrqhriwCXowBQbXXmfaHa +YiOodBFs+B55si2NiO971wiz7jeFYYs/tEiT9wS6nvq0t2omSi7e8ryL103Lb1ge +SAelzS4ZMCf/Xt+kYz+UeC51RlCshHt9sif0Jgst84EVqX5by2PzSUrQu6YPNLrn +3WulO6a7oqHYfF8wV4ARD89SNRyWcF/Xh+vjPg538QKBgQDx5W/x+F/Nt+f0tlDl +ZeSoB/wWo2yQx0gK98R66M3qUtnoeWC+SSj15z1WPT+MfD+B2haytiFP1+uAveI/ +hup1oJMddciTcLMfc13s7luXe+OLMCxCwJTrgaSZeD79dUKKzHv2yU4v01gJFlMq +GnkAzUxolvb9sVzgFuCy7agHnQKBgQDSi8XxDoNhv9HAKHJDhQ1g3mmGIPPP4M72 +v8MxO5AXVCVcL3VFGj+7H6Z3CJuiE7diJVBSPMun/6q1xgMZNUe9EjIjCdmEKMOz +Nm/5P6Dea1Gktd5gb6NB9GTpE3JqwxhxkM0VNv/k8rCp6+4Oz1d8uF+p6jPwNEC5 +Z9vlge/aBQKBgQCkv0nl9+5v8rAVF9Ky2hnIY1/Kn1VCqackaSk1OLd9vx3QWlKM +ZtFx4SMCSEauzLSIINvSrX60nW80yJ59+8pVgJ6RsvV/jYNBiVZQFurkmikYVB/g ++r6yQyKyr5XfE+zVEX3gT6xjoEJWNhFAHLWK2UgP97mSgSirKomw83G8dQKBgHWl +fHlx7p/UG1QQRajM0+jo3nYAO7xQldTy2hLMgXtHnYihTBnMzQe2a8HfoXczJSlG +SFdreTDqf20Ks/iF+QwA+trxSgW68X9WT8Mqdq1RslEi/ptMRiE4eppyL2DQmvv6 +OV49WUeJBIYuOtszqGMccvfy0grKZ9Ax5IGd1XQxAoGAW7DObS33nO9/arcEg+yK +H0cOSP1h/8v4k4M4gorfuXfi6Xkegt5PaWUf7tJGDon5ArzZsCNRq5rdBMNtA4an +NbMlanRzfBuBqHYo/X8OgqThhn8Uwg/oY778qHEH+WaJdSiNg5TboDPKUyEBNRKA +suoE9MAZsAaIj4YP0RnMLk8= +-----END PRIVATE KEY----- diff --git a/tests/assets/kubeconfig-no-context.yml b/tests/assets/kubeconfig-no-context.yml index 46230a559..6aec2f343 100644 --- a/tests/assets/kubeconfig-no-context.yml +++ b/tests/assets/kubeconfig-no-context.yml @@ -8,7 +8,7 @@ clusters: server: http://cow.org:8080 name: cow-cluster - cluster: - certificate-authority-data: path/to/my/cafile + certificate-authority: assets/ca.crt server: https://horse.org:4443 name: horse-cluster - cluster: @@ -22,8 +22,8 @@ users: token: blue-token - name: green-user user: - client-certificate-data: path/to/my/client/cert - client-key-data: path/to/my/client/key + client-certificate: assets/client.crt + client-key: assets/client.key - name: black-user user: token: black-token \ No newline at end of file diff --git a/tests/assets/kubeconfig.cluster-missmatch.yml b/tests/assets/kubeconfig.cluster-missmatch.yml index 43fab470d..3af6c7369 100644 --- a/tests/assets/kubeconfig.cluster-missmatch.yml +++ b/tests/assets/kubeconfig.cluster-missmatch.yml @@ -5,7 +5,7 @@ current-context: federal-context apiVersion: v1 clusters: - cluster: - certificate-authority-data: path/to/my/cafile + certificate-authority: path/to/my/cafile server: https://horse.org:4443 name: bad-name-cluster contexts: diff --git a/tests/assets/kubeconfig.no-credentials.yml b/tests/assets/kubeconfig.no-credentials.yml index 5dffa7e03..c6cfd1dee 100644 --- a/tests/assets/kubeconfig.no-credentials.yml +++ b/tests/assets/kubeconfig.no-credentials.yml @@ -5,7 +5,7 @@ current-context: federal-context apiVersion: v1 clusters: - cluster: - certificate-authority-data: path/to/my/cafile + certificate-authority: assets/ca.crt server: https://horse.org:4443 name: horse-cluster contexts: diff --git a/tests/assets/kubeconfig.no-server.yml b/tests/assets/kubeconfig.no-server.yml index db5a3b60d..9857a9302 100644 --- a/tests/assets/kubeconfig.no-server.yml +++ b/tests/assets/kubeconfig.no-server.yml @@ -5,7 +5,7 @@ current-context: federal-context apiVersion: v1 clusters: - cluster: - certificate-authority-data: path/to/my/cafile + certificate-authority: path/to/my/cafile name: horse-cluster contexts: - context: diff --git a/tests/assets/kubeconfig.tls-no-skip-error.yml b/tests/assets/kubeconfig.tls-no-skip-error.yml index 1dce81637..31499fa5b 100644 --- a/tests/assets/kubeconfig.tls-no-skip-error.yml +++ b/tests/assets/kubeconfig.tls-no-skip-error.yml @@ -8,7 +8,7 @@ clusters: server: http://cow.org:8080 name: cow-cluster - cluster: - # certificate-authority-data: path/to/my/cafile + # certificate-authority: path/to/my/cafile server: https://horse.org:4443 name: horse-cluster contexts: @@ -21,5 +21,5 @@ kind: Config users: - name: green-user user: - client-certificate-data: path/to/my/client/cert - client-key-data: path/to/my/client/key \ No newline at end of file + client-certificate: assets/client.crt + client-key: assets/client.key \ No newline at end of file diff --git a/tests/assets/kubeconfig.user-pass.yml b/tests/assets/kubeconfig.user-pass.yml index b426ef57a..2f621c31a 100644 --- a/tests/assets/kubeconfig.user-pass.yml +++ b/tests/assets/kubeconfig.user-pass.yml @@ -5,7 +5,7 @@ current-context: federal-context apiVersion: v1 clusters: - cluster: - certificate-authority-data: path/to/my/cafile + certificate-authority: assets/ca.crt server: https://horse.org:4443 name: horse-cluster contexts: diff --git a/tests/assets/kubeconfig.yml b/tests/assets/kubeconfig.yml index 98730d2fb..e1a277969 100644 --- a/tests/assets/kubeconfig.yml +++ b/tests/assets/kubeconfig.yml @@ -8,13 +8,17 @@ clusters: server: http://cow.org:8080 name: cow-cluster - cluster: - certificate-authority-data: path/to/my/cafile + certificate-authority: assets/ca.crt server: https://horse.org:4443 name: horse-cluster - cluster: insecure-skip-tls-verify: true server: https://pig.org:443 name: pig-cluster +- cluster: + certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURERENDQWZTZ0F3SUJBZ0lSQUo5ZCtLeThkTDJVSzRjdXplMmo2WnN3RFFZSktvWklodmNOQVFFTEJRQXcKTHpFdE1Dc0dBMVVFQXhNa1lXRTBZVFV3T0RZdE0yVm1aaTAwWWpCa0xUbGxORGt0WmpNeVpXWXpabUpqWWpNNApNQjRYRFRFM01ESXlOakExTURRek5Gb1hEVEl5TURJeU5UQTFNRFF6TkZvd0x6RXRNQ3NHQTFVRUF4TWtZV0UwCllUVXdPRFl0TTJWbVppMDBZakJrTFRsbE5Ea3Raak15WldZelptSmpZak00TUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBM2dkandhdHNsdCsvQVpqV3hmbkNQeGZqMzNHUUxlOU00VU42VmEwRQpKd0FYL2R3L1ZVa0dvVjlDc3NKRUZMdEdTUnM2K2h0RTEvOUN3ak1USDh2WExKcURHTE9KdFQ5dW9sR2c2Q2k1ClBKNDNKelVLWmJlYVE4Z3hhZndzQjdQU05vTTJOYzROVm9lZzBVTUw0bndGeEhXeTNYWHlFZ0QxTWxTUnVrb3oKTTNoRUVxUjJNVFdrNm9KK3VJNFF4WVZWMnZuWXdXaEJwUDlDR3RWUTlyUW9MVFowcmFpOCtDYURBMVltTWRhbQpRYUVPdURlSFRqU2FYM2dyR0FBVVFWNWl6MC9qVVBuK3lJNm1iV0trbzFzNytPY1dZR2F1aDFaMzFYSjJsc0RTCnU4a3F0d215UEcyUVl2aUQ4YjNOWFAyY0dRK2EwZlpRZnBrbTF0U3IxQnhhaXdJREFRQUJveU13SVRBT0JnTlYKSFE4QkFmOEVCQU1DQWdRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQQpuVzFXVXlLbVJ0TlNzU1VzVFBSUnhFRzhhek9kdjdYeUhRL0R5VWNqWm9rUEJVVHY4VjdvNG96RHgyVHV6UEdYCmZ2YlMvT2g0VDd6ZlYxdjJadmU3dTBxelNiRTl5OGpsaDNxYXJEcEd5ZmlTamwycmhIOFBmay9sZGR0VFpVL04KSkVtYW5ReGl6R20xV2pCSklRSE5LZENneVIwN3A1c0MwNnR3K25YUytla1MxMlBUTG45WjBuRDBKVDdQSzRXQgpQc3ZXeDVXN0w5dnJIdVN5SGRSTkt5eEEvbWI1WHdXMDBkZUpmaHZub0p3ZWRYNDVKZVRiME5MczUzaURqVEU1CnRpdU03Z1RVSjlCcGZTL0gvYSt2SmovVWQ2bHM0QndrWmpUNHNhOTA1bnNzdnRqamlwZ1N5a0QzVkxCQ3VueTkKd1NnbE1vSnZNWmg0bC9FVFJPeFE3Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + server: https://llama.org:443 + name: llama-cluster contexts: - context: cluster: horse-cluster @@ -26,6 +30,11 @@ contexts: namespace: saw-ns user: black-user name: queen-anne-context +- context: + cluster: llama-cluster + namespace: saw-ns + user: red-user + name: victorian-context kind: Config users: - name: blue-user @@ -33,8 +42,12 @@ users: token: blue-token - name: green-user user: - client-certificate-data: path/to/my/client/cert - client-key-data: path/to/my/client/key + client-certificate: assets/client.crt + client-key: assets/client.key - name: black-user user: - token: black-token \ No newline at end of file + token: black-token +- name: red-user + user: + client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlEUkRDQ0FpeWdBd0lCQWdJSUVIVm8zVzZXcnJjd0RRWUpLb1pJaHZjTkFRRUxCUUF3RXpFUk1BOEdBMVVFQXd3SVlXTnphemh6DQpZMkV3SGhjTk1UY3dOREU1TURBd01EQXdXaGNOTVRrd05ERTVNREF3TURBd1dqQVZNUk13RVFZRFZRUUREQXByZFdKbFkyOXVabWxuDQpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXVEZ0ttVUxsU0ZSTStCN0pNMWMxUEJqS25mWlZpL1d1DQpSUks2TE9xNHpzL1UxanUyN21TbE9HbWJxRWVYTlpCRVFCblYvVGptQXpqM0Zwd2VKbW5PdDY0TTFIeXZRUW8yTC9LS2JlOVlPbUxNDQpRWXZyeE9QeFRnZHVJeDYzTGxKSUJRZERqdVFhWUhBc0FqTllhbTB3UmQwZTNhVkUxeEJnVDA4QkVWZnVwZmUrUnpXUWhVemJXYkhDDQpydmU2b2s4aHQzRk9LYjNxRzhyR3UrYzN4bGM1MXNVa2ovMGhMK0xrZVBWOTBRYVVCRjF6czlDMVZGaVhoT1Z6WlRjQ0lWQThSTDNvDQpxRTdGUEwvczBNc0RsdkRoNkZ3ZFkvUWhwSUtRMkdKTHQ0ZGRobk5yYW5GbS9pM1RzQ3FJSWxieHl5TVRsT09YYytOUG9mZW9EUUcvDQpvQldjTVFJREFRQUJvNEdaTUlHV01FSUdBMVVkSXdRN01EbUFGSmcrY0p4ZHpaUzNabzdjajFjQXJFWjNxREh1b1Jla0ZUQVRNUkV3DQpEd1lEVlFRRERBaGhZM05yT0hOallZSUlGTjZKejlmRStOVXdIUVlEVlIwT0JCWUVGTmJxZURyeHRvbWZFYnd5bmFUaXp4WXVmeVRXDQpNQXdHQTFVZEV3RUIvd1FDTUFBd0RnWURWUjBQQVFIL0JBUURBZ1dnTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNQ01BMEdDU3FHDQpTSWIzRFFFQkN3VUFBNElCQVFCYkFKdjJDRzRnTDJtNlpiQmJKSEtqMVF5MGtmbUFJdWgyZHNISU5vUHZ4V0lqY3VXeHZlbUpRcndUDQpuUEZPeWFMY3VtNmVmcGZhWG04T2hxNWxjT3NldUtYaDVya2ZuRUp1VmNWYk9UNXY2aUU3TEY5eGMrelVlTkZDWjA1a1QwbU52eW9lDQpIVmhkYlljeG8yOXVwbGVqa3RqZXd0eGZBZjM2a25hQytOVGE3dVZyNVQ0aTd4MUNQMGkvcUY5ZVgwVHJqZ0diM09MTHVwcVhMSzd5DQpUWUllK3NCajNjOGZRNit6RUJDVFNib05DRlRldXpjOTMxclltc1VyZGtIdEp2SkI2aSthMG5iRjB4MnRYY3ZRbVJ6aUwzTGR0UG84DQptSWh0bDRRWXBQZG5Qdk1RWk1MMlZ4T0FqN3V0RnhNMW44OEpKV0NaTkNyUUdYZjBLSDFpdHhwNg0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ0K + client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRXBBSUJBQUtDQVFFQWtweU5GZ1F0N1Yxbml6U0N6TzdodmhLQXVyUFlSR2ExZEd3OXNQQVU4d2ovd1E4dA0KZUVGRTBwejRJTmxuUHhvQVZhUnRSVXowUHVnRmhLSUVVWEZCUnF1bEF5VS8ySjhMbzFWVWVUK2V1TTZ1cWdMUQ0KdFRqVlIxMUh3YU9jMktoUGl1VUlZaDMxNm5BSHVaMnBUbnpqTFBBM2lqY2RndVZxSUJydnpJTXVLU25lMmJQZA0KT2pqN2dBa3RIQ2Z0VHlTUlorOE94TzMzVFRqTEpDQ1RJZUYrUkFWQnl6VWUybDd5WDkrSzFjekwrbFh1TWh6Lw0KeSt6cFp1NW9VeUJXenA4T0M4aXNSNmxURFVmaCtGNU5zckxmcXIwV21wVlVlalV4VDU0ODNJeGp5SEJ2SENuUg0KYWdVZ3JOd080aWNEMzA4WmZYQjQ3TGk1TGJnRmlZdzNhMGhKOHdJREFRQUJBb0lCQVFDSExQSmtUend0TTBhSg0Kc2NPN3diQy9kLzNhcG9FTEtBdTdxU1VFSTRlZFFUblZ2ZkcxZzdBNTQrSmZIek96bVh5ZmVmZ0p3bGNjUFA4Qg0KZUFRMU53L3hZY2JGN0swK1EvNE4vVm5pWlFjcDY5NWRBRFdBTVVOc0JoUm45RHRrQTJNN2M4cFQ5TDNOQnJnSQ0KcXRYMGVPOVllcThxcjRCMU5LS3hzVVJSc3MxM3d5d2R4bzMwbFJ1WlhjMnUvS1EweFRRbTR4M1RRRVpIUWhBNg0KN2E2UWM3MThPMW1ISWQrRERTbStab2l0YnZpR24ySnQ1aWsrUFNIajhVY01ITkoxYVUvMyt5YmVGUU93L3MrTg0KMWMxZDJsS0c1K0k0UW9FeEFEUWxrWEZQeUFISGg3bWhmdGNJT3lXUUdMYVcraVQrSklGYUMyS3dBSCtmM1g0TA0KWTVJUTlzVzVBb0dCQU50NjFtTlNYTWlncVdvSVdzM1pMN1hpWDdJOThOU1ZrMjJGRHNLcldKSzN1bkRWSlVnbg0KbEpzaGo0L2cwc1IvSlRLT2FiUTFVNWJCSFpzRmF3UGd1K2RPWWp3TWtkL0tUcThmT2tUSXhpaHBvYVNvcDF0WA0KS001ZUpVcitjSVpRcXloc3dvUzg5R3ZGSnNFSXo5WUpzOHlIOGlvdElLL2N5ZnpnTTVZQmUzL25Bb0dCQUtzQg0KdjhIVzNkenEwTW1QY1VhTTk5L1VlOG9QYmcrOElaSFl2M0Jicm43TEM5czNVVUVRY1cxTUMrYXdYSHd0YXg0OA0KNjdvWWRaY2YzbWZxR2M1NlIwUWpBdm1jcGt4dXY2bVpWYzk1Z1p5TnoyV0Z4ZXdTdWlQQUQrSjZwLzE3WkF4ag0KU1NZejRSRDZ0bk1HNXhYd1BZcVRGdlNiajNJdlAzWjN1bVhlZVZRVkFvR0FJUklpU2JiNFdoYXpVRkhQOU9KNw0KNk9raVNFWkRDcFU3TUs1eTZleE5uVlhuMXVhWEJvZElRaFBOZ1AxOFpMVlAwbzFZNmhsNjdlVk5ZN1Z1U1poTQ0KVis4Y3FEV3dxVXJmamsyalJ3Z0ZxOE0yNHd1eW8vVy9KVkZVUnplTmx2K0N1c1dnamNXM0FxSWpVOFRUSkVveg0KK0dhbm02bFBHcENuTXQ1Smg1blRsUk1DZ1lCaDAyOVNRVXZNWEZ3eVlpZW45MjY1a2dGNjF4YmNVNEJVd2N6Sw0KUnZoc3VsVS9BOGNzMUc0KzJ6a1hzQjVJUDJjKzloVFg4YlFiVHZUWk4xZXFBZE9SREFKZ3J4ZUhNL2ZhMnNmMw0KRjRobytjc0NSZU95cUFmcCthNWNubFJKc3ZQS0RtWkRQM3hxVFRKZ1hUdUhGbGJ1VmpUVW04VVV5aWlnNmQ4cw0KOVhjcy9RS0JnUUMyZDdSSDc2VFBodFNCV1I3UVhJcVJqYitIeDBVcFk3WkJBRCtzZ1lXakZRZVBNVVE1V2JUbA0KTERZekl6WDNyQnlIVEF2TXptcHFoWTY3b28rRi9DazhmN3JCOS9IckduSUowY2pIL2twbnZLZWpqSnZDdWhOdA0KNUxsaFE4NDVteXIrVWlkVEc1cWVReXloaHZMVDFqNzhLRmRGdnpMRzZGWEREZ1d2emxBQXVRPT0NCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tDQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=