Skip to content

Improve error messaging with OpenSSL #17

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

Merged
merged 1 commit into from
Aug 19, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,58 @@
# csharp
Work In Progress
Currently only supported on Linux

[![Travis](https://img.shields.io/travis/kubernetes-client/csharp.svg)](https://travis-ci.org/kubernetes-client/csharp)

# Generating the client code
# Generating the Client Code

## Prerequisites

Check out the generator project into some other directory
(henceforth `$GEN_DIR`)

```
```bash
cd $GEN_DIR/..
git clone https://github.com/kubernetes-client/gen
```

Install the [`autorest` tool](https://github.com/azure/autorest):

```
```bash
npm install autorest
```

## Generating code

```
```bash
# Where REPO_DIR points to the root of the csharp repository
cd ${REPO_DIR}/csharp/src
${GEN_DIR}/openapi/csharp.sh generated csharp.settings
```

# Testing
# Usage

## Prerequisities

* [OpenSSL](https://www.openssl.org/)
* For Linux/Mac:
* LibCurl built with OpenSSL (Mac: `brew install curl --with-nghttp2`)

## Running the Examples

```bash
git clone git@github.com:kubernetes-client/csharp.git
cd csharp\examples\simple
dotnet run
```

## Testing

The project uses [XUnit](https://xunit.github.io) as unit testing framework.

To run the tests you need to:
To run the tests

```bash
cd tests
cd csharp\tests
dotnet restore
dotnet xunit
```
4 changes: 0 additions & 4 deletions src/Kubernetes.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ private async Task SetCredentialsAsync(KubernetesClientConfiguration config, Htt
!string.IsNullOrWhiteSpace(config.ClientKey)))
{
var pfxFilePath = await Utils.GeneratePfxAsync(config).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(pfxFilePath))
{
throw new KubernetesClientException("Failed to generate pfx file");
}

var cert = new X509Certificate2(pfxFilePath, string.Empty, X509KeyStorageFlags.PersistKeySet);
handler.ClientCertificates.Add(cert);
Expand Down
106 changes: 44 additions & 62 deletions src/KubernetesClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,12 @@ public class KubernetesClientConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> class.
/// Initializes a new instance of the ClientConfiguration class
/// </summary>
/// <param name="kubeconfig">kubeconfig file info</param>
/// <param name="currentContext">Context to use from kube config</param>
public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentContext = null)
{
if (kubeconfig == null)
{
kubeconfig = new FileInfo(KubeConfigDefaultLocation);
}
var k8SConfig = this.LoadKubeConfig(kubeconfig);
var k8SConfig = this.LoadKubeConfig(kubeconfig ?? new FileInfo(KubeConfigDefaultLocation));
this.Initialize(k8SConfig, currentContext);
}

Expand Down Expand Up @@ -108,86 +103,73 @@ public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentC
/// <param name="currentContext">Current Context</param>
private void Initialize(K8SConfiguration k8SConfig, string currentContext = null)
{
Context activeContext;

if (k8SConfig.Contexts == null)
{
throw new KubeConfigException("No contexts found in kubeconfig");
}
}

// set the currentCOntext to passed context if not null
if (!string.IsNullOrWhiteSpace(currentContext))
if (k8SConfig.Clusters == null)
{

activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase));
if (activeContext != null)
{
this.CurrentContext = activeContext.Name;
}
else
{
throw new KubeConfigException($"CurrentContext: {0} not found in contexts in kubeconfig");
}
throw new KubeConfigException($"No clusters found in kubeconfig");
}
// otherwise set current context from kubeconfig
else

if (k8SConfig.Users == null)
{
activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(k8SConfig.CurrentContext, StringComparison.OrdinalIgnoreCase));

if (activeContext == null)
{
throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig");
}
throw new KubeConfigException($"No users found in kubeconfig");
}

this.CurrentContext = activeContext.Name;
// current context
currentContext = currentContext ?? k8SConfig.CurrentContext;
Context activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase));
if (activeContext == null)
{
throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig");
}

if (k8SConfig.Clusters == null)
this.CurrentContext = activeContext.Name;

// cluster
var clusterDetails = k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster, StringComparison.OrdinalIgnoreCase));
if (clusterDetails?.ClusterEndpoint == null)
{
throw new KubeConfigException($"clusters not found for current-context :{activeContext} in kubeconfig");
throw new KubeConfigException($"Cluster not found for context {activeContext} in kubeconfig");
}

var clusterDetails = k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster, StringComparison.OrdinalIgnoreCase));
if (clusterDetails?.ClusterEndpoint != null)
if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server))
{
if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server))
{
throw new KubeConfigException($"server not found for current-context :{activeContext} in kubeconfig");
}
throw new KubeConfigException($"Server not found for current-context {activeContext} in kubeconfig");
}

if (!clusterDetails.ClusterEndpoint.SkipTlsVerify &&
string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData) &&
string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthority))
{
throw new KubeConfigException($"neither certificate-authority-data nor certificate-authority not found for current-context :{activeContext} in kubeconfig");
}
if (!clusterDetails.ClusterEndpoint.SkipTlsVerify &&
string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData) &&
string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthority))
{
throw new KubeConfigException($"neither certificate-authority-data nor certificate-authority not found for current-context :{activeContext} in kubeconfig");
}

this.Host = clusterDetails.ClusterEndpoint.Server;
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;
this.Host = clusterDetails.ClusterEndpoint.Server;
if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData))
{
string data = clusterDetails.ClusterEndpoint.CertificateAuthorityData;
this.SslCaCert = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(Utils.Base64Decode(data)));
}
else
else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority))
{
throw new KubeConfigException($"Cluster details not found for current-context: {activeContext} in kubeconfig");
this.SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority, null);
}
this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify;

// set user details from kubeconfig
var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase));

this.SetUserDetails(userDetails);
// user
this.SetUserDetails(k8SConfig, activeContext);
}

private void SetUserDetails(User userDetails)
private void SetUserDetails(K8SConfiguration k8SConfig, Context activeContext)
{
var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase));

if (userDetails == null)
{
throw new KubeConfigException("User not found for the current context in kubeconfig");
throw new KubeConfigException("User not found for context {activeContext.Name} in kubeconfig");
}

if (userDetails.UserCredentials == null)
Expand Down Expand Up @@ -229,7 +211,7 @@ private void SetUserDetails(User userDetails)

if (!userCredentialsFound)
{
throw new KubeConfigException($"User: {userDetails.Name} does not have appropriate auth credentials in kube config");
throw new KubeConfigException($"User: {userDetails.Name} does not have appropriate auth credentials in kubeconfig");
}
}

Expand Down
40 changes: 27 additions & 13 deletions src/Utils.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
namespace k8s
{
using k8s.Exceptions;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -50,32 +51,36 @@ public static async Task<string> GeneratePfxAsync(KubernetesClientConfiguration

var filePrefix = config.CurrentContext;
var pfxFilePath = Path.Combine(certDirPath, filePrefix + "pfx");
if (!string.IsNullOrWhiteSpace(config.ClientCertificateKey)) {
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);
}
}
if (!string.IsNullOrWhiteSpace(config.ClientKey)) {
if (!string.IsNullOrWhiteSpace(config.ClientKey))
{
keyFilePath = config.ClientKey;
}

if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) {
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)) {
if (!string.IsNullOrWhiteSpace(config.ClientCertificate))
{
certFilePath = config.ClientCertificate;
}
var process = new Process();
process.StartInfo = new ProcessStartInfo()

var processStartInfo = new ProcessStartInfo
{
FileName = @"openssl",
Arguments = $"pkcs12 -export -out {pfxFilePath} -inkey {keyFilePath} -in {certFilePath} -passout pass:",
Expand All @@ -84,14 +89,23 @@ public static async Task<string> GeneratePfxAsync(KubernetesClientConfiguration
RedirectStandardOutput = true
};

process.Start();
process.WaitForExit();
if (process.ExitCode == 0)
try
{
using (Process process = Process.Start(processStartInfo))
{
process.WaitForExit();
if (process.ExitCode != 0)
{
throw new KubernetesClientException($"Failed to generate pfx file with openssl. ExitCode = {process.ExitCode}.");
}
}
}
catch (Win32Exception e)
{
return pfxFilePath;
throw new KubernetesClientException("Failed to generate pfx file with openssl.", e);
}

return null;
return pfxFilePath;
}
}
}
4 changes: 1 addition & 3 deletions tests/KubernetesClientConfigurationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using Xunit;
using k8s;
using Xunit;
using System.IO;

namespace k8s.Tests
Expand Down