Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update credentials for more valid values. #13

Merged
merged 1 commit into from
Aug 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/simple/PodList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
}
}
}
}
2 changes: 1 addition & 1 deletion examples/simple/simple.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

</Project>
3 changes: 3 additions & 0 deletions src/KubeConfigModels/ClusterEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
3 changes: 3 additions & 0 deletions src/KubeConfigModels/K8SConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
/// </summary>
public class K8SConfiguration
{
[YamlMember(Alias = "preferences")]
public IDictionary<string, object> preferences{ get; set; }

[YamlMember(Alias = "apiVersion")]
public string ApiVersion { get; set; }

Expand Down
10 changes: 10 additions & 0 deletions src/KubeConfigModels/UserCredentials.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace k8s.KubeConfigModels
{
using System.Collections.Generic;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;

Expand All @@ -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; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to review unit tests. Thought that client-certifidate-data hold the route to a X509 certificate, but concluded now it is the certificate itself. Additional tests should be written to cover the client-certificate scenario.


[YamlMember(Alias = "client-key-data")]
public string ClientKeyData { get; set; }

[YamlMember(Alias = "client-key")]
public string ClientKey { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as client-certificate. Thought client-key-data stored a path, but they are the certificate itself. Now several tests are broken.


[YamlMember(Alias = "token")]
public string Token { get; set; }

Expand All @@ -19,5 +26,8 @@ public class UserCredentials

[YamlMember(Alias = "password")]
public string Password { get; set; }

[YamlMember(Alias = "auth-provider")]
public Dictionary<string, dynamic> AuthProvider { get; set; }
}
}
11 changes: 7 additions & 4 deletions src/Kubernetes.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,7 +45,7 @@ public Kubernetes(KubernetesClientConfiguration config)
this.InitializeHttpClient(handler);
}

private string CaCert { get; set; }
private X509Certificate2 CaCert { get; set; }

/// <summary>
/// Set credentials for the Client
Expand All @@ -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))
Expand Down Expand Up @@ -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;
Expand Down
33 changes: 29 additions & 4 deletions src/KubernetesClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -49,7 +50,7 @@ public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentC
/// <summary>
/// Gets SslCaCert
/// </summary>
public string SslCaCert { get; private set; }
public X509Certificate2 SslCaCert { get; private set; }

/// <summary>
/// Gets ClientCertificateData
Expand All @@ -61,6 +62,16 @@ public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentC
/// </summary>
public string ClientCertificateKey { get; private set; }

/// <summary>
/// Gets ClientCertificate filename
/// </summary>
public string ClientCertificate { get; private set; }

/// <summary>
/// Gets ClientCertificate Key filename
/// </summary>
public string ClientKey { get; private set; }

/// <summary>
/// Gets a value indicating whether to skip ssl server cert validation
/// </summary>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");
Expand Down
36 changes: 24 additions & 12 deletions src/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,35 @@ public static async Task<string> 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()
{
Expand Down
30 changes: 26 additions & 4 deletions tests/KubernetesClientConfigurationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/// <summary>
/// This file contains a sample kubeconfig file
/// </summary>
Expand Down Expand Up @@ -110,16 +115,33 @@ public void ContextUserTokenTest(string context, string token)
/// <param name="clientCertData">'client-certificate-data' node content</param>
/// <param name="clientCertKey">'client-key-data' content</param>
[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);
}

/// <summary>
/// Checks if certificate-based authentication is loaded properly from the config file, per context
/// </summary>
/// <param name="context">Context to retreive the configuration</param>
[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);
}


/// <summary>
/// Test that an Exception is thrown when initializating a KubernetClientConfiguration whose config file Context is not present
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions tests/assets/ca-data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURERENDQWZTZ0F3SUJBZ0lSQUo5ZCtLeThkTDJVSzRjdXplMmo2WnN3RFFZSktvWklodmNOQVFFTEJRQXcKTHpFdE1Dc0dBMVVFQXhNa1lXRTBZVFV3T0RZdE0yVm1aaTAwWWpCa0xUbGxORGt0WmpNeVpXWXpabUpqWWpNNApNQjRYRFRFM01ESXlOakExTURRek5Gb1hEVEl5TURJeU5UQTFNRFF6TkZvd0x6RXRNQ3NHQTFVRUF4TWtZV0UwCllUVXdPRFl0TTJWbVppMDBZakJrTFRsbE5Ea3Raak15WldZelptSmpZak00TUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBM2dkandhdHNsdCsvQVpqV3hmbkNQeGZqMzNHUUxlOU00VU42VmEwRQpKd0FYL2R3L1ZVa0dvVjlDc3NKRUZMdEdTUnM2K2h0RTEvOUN3ak1USDh2WExKcURHTE9KdFQ5dW9sR2c2Q2k1ClBKNDNKelVLWmJlYVE4Z3hhZndzQjdQU05vTTJOYzROVm9lZzBVTUw0bndGeEhXeTNYWHlFZ0QxTWxTUnVrb3oKTTNoRUVxUjJNVFdrNm9KK3VJNFF4WVZWMnZuWXdXaEJwUDlDR3RWUTlyUW9MVFowcmFpOCtDYURBMVltTWRhbQpRYUVPdURlSFRqU2FYM2dyR0FBVVFWNWl6MC9qVVBuK3lJNm1iV0trbzFzNytPY1dZR2F1aDFaMzFYSjJsc0RTCnU4a3F0d215UEcyUVl2aUQ4YjNOWFAyY0dRK2EwZlpRZnBrbTF0U3IxQnhhaXdJREFRQUJveU13SVRBT0JnTlYKSFE4QkFmOEVCQU1DQWdRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQQpuVzFXVXlLbVJ0TlNzU1VzVFBSUnhFRzhhek9kdjdYeUhRL0R5VWNqWm9rUEJVVHY4VjdvNG96RHgyVHV6UEdYCmZ2YlMvT2g0VDd6ZlYxdjJadmU3dTBxelNiRTl5OGpsaDNxYXJEcEd5ZmlTamwycmhIOFBmay9sZGR0VFpVL04KSkVtYW5ReGl6R20xV2pCSklRSE5LZENneVIwN3A1c0MwNnR3K25YUytla1MxMlBUTG45WjBuRDBKVDdQSzRXQgpQc3ZXeDVXN0w5dnJIdVN5SGRSTkt5eEEvbWI1WHdXMDBkZUpmaHZub0p3ZWRYNDVKZVRiME5MczUzaURqVEU1CnRpdU03Z1RVSjlCcGZTL0gvYSt2SmovVWQ2bHM0QndrWmpUNHNhOTA1bnNzdnRqamlwZ1N5a0QzVkxCQ3VueTkKd1NnbE1vSnZNWmg0bC9FVFJPeFE3Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
18 changes: 18 additions & 0 deletions tests/assets/ca.crt
Original file line number Diff line number Diff line change
@@ -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-----
1 change: 1 addition & 0 deletions tests/assets/client-certificate-data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlEUkRDQ0FpeWdBd0lCQWdJSUVIVm8zVzZXcnJjd0RRWUpLb1pJaHZjTkFRRUxCUUF3RXpFUk1BOEdBMVVFQXd3SVlXTnphemh6DQpZMkV3SGhjTk1UY3dOREU1TURBd01EQXdXaGNOTVRrd05ERTVNREF3TURBd1dqQVZNUk13RVFZRFZRUUREQXByZFdKbFkyOXVabWxuDQpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXVEZ0ttVUxsU0ZSTStCN0pNMWMxUEJqS25mWlZpL1d1DQpSUks2TE9xNHpzL1UxanUyN21TbE9HbWJxRWVYTlpCRVFCblYvVGptQXpqM0Zwd2VKbW5PdDY0TTFIeXZRUW8yTC9LS2JlOVlPbUxNDQpRWXZyeE9QeFRnZHVJeDYzTGxKSUJRZERqdVFhWUhBc0FqTllhbTB3UmQwZTNhVkUxeEJnVDA4QkVWZnVwZmUrUnpXUWhVemJXYkhDDQpydmU2b2s4aHQzRk9LYjNxRzhyR3UrYzN4bGM1MXNVa2ovMGhMK0xrZVBWOTBRYVVCRjF6czlDMVZGaVhoT1Z6WlRjQ0lWQThSTDNvDQpxRTdGUEwvczBNc0RsdkRoNkZ3ZFkvUWhwSUtRMkdKTHQ0ZGRobk5yYW5GbS9pM1RzQ3FJSWxieHl5TVRsT09YYytOUG9mZW9EUUcvDQpvQldjTVFJREFRQUJvNEdaTUlHV01FSUdBMVVkSXdRN01EbUFGSmcrY0p4ZHpaUzNabzdjajFjQXJFWjNxREh1b1Jla0ZUQVRNUkV3DQpEd1lEVlFRRERBaGhZM05yT0hOallZSUlGTjZKejlmRStOVXdIUVlEVlIwT0JCWUVGTmJxZURyeHRvbWZFYnd5bmFUaXp4WXVmeVRXDQpNQXdHQTFVZEV3RUIvd1FDTUFBd0RnWURWUjBQQVFIL0JBUURBZ1dnTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNQ01BMEdDU3FHDQpTSWIzRFFFQkN3VUFBNElCQVFCYkFKdjJDRzRnTDJtNlpiQmJKSEtqMVF5MGtmbUFJdWgyZHNISU5vUHZ4V0lqY3VXeHZlbUpRcndUDQpuUEZPeWFMY3VtNmVmcGZhWG04T2hxNWxjT3NldUtYaDVya2ZuRUp1VmNWYk9UNXY2aUU3TEY5eGMrelVlTkZDWjA1a1QwbU52eW9lDQpIVmhkYlljeG8yOXVwbGVqa3RqZXd0eGZBZjM2a25hQytOVGE3dVZyNVQ0aTd4MUNQMGkvcUY5ZVgwVHJqZ0diM09MTHVwcVhMSzd5DQpUWUllK3NCajNjOGZRNit6RUJDVFNib05DRlRldXpjOTMxclltc1VyZGtIdEp2SkI2aSthMG5iRjB4MnRYY3ZRbVJ6aUwzTGR0UG84DQptSWh0bDRRWXBQZG5Qdk1RWk1MMlZ4T0FqN3V0RnhNMW44OEpKV0NaTkNyUUdYZjBLSDFpdHhwNg0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ0K