Skip to content
Open
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
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,43 @@ var config = KubernetesClientConfiguration.InClusterConfig()
var client = new Kubernetes(config);
```

### Exec credential plugin security

Kubeconfig `exec` plugins start local processes, including while loading configuration.
Only load trusted kubeconfigs with the default `AllowAll` policy. For untrusted input,
applications can deny exec plugins or explicitly allow approved executables:

This policy is a defense-in-depth measure, not a hard security boundary. Loading a
kubeconfig from an untrusted source is inherently dangerous; handle that risk as part
of your application's overall security model, and do not rely on this policy alone.

```c#
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile(
kubeConfigPath, execCredentialPluginPolicy: ExecCredentialPluginPolicy.DenyAll);

var config = KubernetesClientConfiguration.BuildConfigFromConfigFile(
kubeConfigPath,
execCredentialPluginPolicy: ExecCredentialPluginPolicy.Allowlist(
"kubelogin", @"C:\Program Files\Contoso\trusted-plugin.exe"));
```

The same policy parameter is available on default, stream, async, and pre-loaded-object
builders. Policies are supplied by the application, not read from kubeconfig, and
apply to both initial execution and token refresh. Denials throw
`k8s.Exceptions.ExecCredentialPluginDeniedException` before the process starts.
An empty or null allowlist is invalid, as are invalid entries.

An entry matches when its command text exactly matches the kubeconfig command or
when both resolve to the same executable using the application's `PATH`. Thus a
basename does not allow a same-named executable in an arbitrary directory. Path
resolution does not follow symlinks or support shell globs. Prefer absolute paths to
executables in application-controlled directories. The policy does not validate
arguments or executable contents. Allowlist mode rejects kubeconfig-supplied
environment variables that can alter executable resolution or inject code, including
`PATH`, dynamic-loader settings, and common language-runtime injection settings.
Ordinary variables such as `AWS_PROFILE` remain allowed. Allowing an interpreter or
other general-purpose launcher can permit arbitrary code execution. It is not a sandbox.

### Listing Objects
```c#
var namespaces = client.CoreV1.ListNamespace();
Expand Down
2 changes: 2 additions & 0 deletions src/KubernetesClient.Aot/KubernetesClient.Aot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<Compile Include="..\KubernetesClient\Kubernetes.cs" />
<Compile Include="..\KubernetesClient\KubernetesClientConfiguration.InCluster.cs" />
<Compile Include="..\KubernetesClient\KubernetesClientConfiguration.cs" />
<Compile Include="..\KubernetesClient\ExecCredentialPluginPolicy.cs" />
<Compile Include="..\KubernetesClient\KubernetesException.cs" />

<Compile Include="..\KubernetesClient\ChannelIndex.cs" />
Expand All @@ -96,6 +97,7 @@
<Compile Include="..\KubernetesClient\LineSeparatedHttpContent.cs" />

<Compile Include="..\KubernetesClient\Exceptions\KubeConfigException.cs" />
<Compile Include="..\KubernetesClient\Exceptions\ExecCredentialPluginDeniedException.cs" />
<Compile Include="..\KubernetesClient\Exceptions\KubernetesClientException.cs" />

<Compile Include="..\KubernetesClient\Authentication\ExecTokenProvider.cs" />
Expand Down
102 changes: 83 additions & 19 deletions src/KubernetesClient.Aot/KubernetesClientConfiguration.ConfigFile.cs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/KubernetesClient.Classic/KubernetesClient.Classic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
<Compile Include="..\KubernetesClient\KubernetesClientConfiguration.ConfigFile.cs" />
<Compile Include="..\KubernetesClient\KubernetesClientConfiguration.InCluster.cs" />
<Compile Include="..\KubernetesClient\KubernetesClientConfiguration.cs" />
<Compile Include="..\KubernetesClient\ExecCredentialPluginPolicy.cs" />
<Compile Include="..\KubernetesClient\KubernetesException.cs" />

<Compile Include="..\KubernetesClient\ChannelIndex.cs" />
Expand All @@ -110,6 +111,7 @@
<Compile Include="..\KubernetesClient\LineSeparatedHttpContent.cs" />

<Compile Include="..\KubernetesClient\Exceptions\KubeConfigException.cs" />
<Compile Include="..\KubernetesClient\Exceptions\ExecCredentialPluginDeniedException.cs" />
<Compile Include="..\KubernetesClient\Exceptions\KubernetesClientException.cs" />

<Compile Include="..\KubernetesClient\Authentication\ExecTokenProvider.cs" />
Expand Down
9 changes: 8 additions & 1 deletion src/KubernetesClient/Authentication/ExecTokenProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ namespace k8s.Authentication
public class ExecTokenProvider : ITokenProvider
{
private readonly ExternalExecution exec;
private readonly ExecCredentialPluginPolicy policy;
private ExecCredentialResponse response;

public ExecTokenProvider(ExternalExecution exec)
: this(exec, ExecCredentialPluginPolicy.AllowAll)
{
}

public ExecTokenProvider(ExternalExecution exec, ExecCredentialPluginPolicy policy)
{
this.exec = exec;
this.policy = policy ?? ExecCredentialPluginPolicy.AllowAll;
}

private bool NeedsRefresh()
Expand Down Expand Up @@ -41,7 +48,7 @@ public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(Cancel
private async Task RefreshToken()
{
response =
await Task.Run(() => KubernetesClientConfiguration.ExecuteExternalCommand(this.exec)).ConfigureAwait(false);
await Task.Run(() => KubernetesClientConfiguration.ExecuteExternalCommand(this.exec, policy)).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace k8s.Exceptions
{
/// <summary>
/// An exec credential plugin was denied by the application policy before starting a process.
/// </summary>
public class ExecCredentialPluginDeniedException : KubeConfigException
{
/// <summary>
/// Initializes a new instance of the <see cref="ExecCredentialPluginDeniedException"/> class.
/// </summary>
/// <param name="command">The denied executable.</param>
public ExecCredentialPluginDeniedException(string command)
: base($"Exec credential plugin '{command}' was denied by the application policy.")
{
Command = command;
}

/// <summary>
/// Gets the denied executable.
/// </summary>
public string Command { get; }
}
}
Loading
Loading