Skip to content
Draft
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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@ See the wiki page and documentation [here](https://github.com/kubernetes-client/
- [Client Versioning and Compatibility](https://github.com/kubernetes-client/java/wiki/2.-Versioning-and-Compatibility)
- [Code Examples](https://github.com/kubernetes-client/java/wiki/3.-Code-Examples)

### Exec credential plugin policy

Kubeconfig `exec` credential plugins start local processes. Only load trusted kubeconfigs with the
default `AllowAll` policy. Applications can restrict which plugins may run using
`ExecCredentialPluginPolicy`, which follows the `credentialPluginPolicy` /
`credentialPluginAllowlist` semantics of [kuberc](https://kubernetes.io/docs/reference/kubectl/kuberc/):

```java
// Deny all exec plugins
ApiClient client = ClientBuilder.standard(ExecCredentialPluginPolicy.DENY_ALL).build();

// Only allow specific plugins (basenames resolved via PATH, or full paths)
ApiClient client = ClientBuilder.standard(
ExecCredentialPluginPolicy.allowlist("kubelogin", "/usr/local/bin/my-trusted-plugin")).build();

// Use the same policy as kubectl ($KUBERC or ~/.kube/kuberc)
ApiClient client = ClientBuilder.standard(ExecCredentialPluginPolicy.fromDefaultKubeRc()).build();

// Or on a KubeConfig loaded manually
kubeConfig.setExecCredentialPluginPolicy(ExecCredentialPluginPolicy.DENY_ALL);
```

An allowlist entry matches when it is exactly equal to the plugin command, or when both resolve to the
same executable via `PATH` (symlinks and globs are not resolved). Denied plugins are never started and
an `ExecCredentialPluginDeniedException` is thrown. The policy applies to both initial credential
acquisition and token refresh. It does not restrict plugin arguments or environment, and is a
defense-in-depth measure rather than a sandbox.

## Release

Starting from `20.0.0` (Kubernetes 1.28), `client-java-api` has introduced non-backward-compatible changes. Optional
Expand Down
45 changes: 42 additions & 3 deletions util/src/main/java/io/kubernetes/client/util/ClientBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,46 @@ public static ClientBuilder standard() throws IOException {
}

public static ClientBuilder standard(boolean persistConfig) throws IOException {
return standard(persistConfig, ExecCredentialPluginPolicy.ALLOW_ALL);
}

/**
* Creates a builder which is pre-configured as described in {@link #standard()}, applying the
* given policy to any exec credential plugin found in the kubeconfig.
*
* @param execCredentialPluginPolicy the exec credential plugin policy, for example {@link
* ExecCredentialPluginPolicy#fromDefaultKubeRc()}; null allows all plugins
* @return <tt>ClientBuilder</tt> pre-configured using the precedence of {@link #standard()}
* @throws IOException if the configuration file or a file specified in a configuration file
* cannot be read.
* @throws io.kubernetes.client.util.exception.ExecCredentialPluginDeniedException if the
* kubeconfig requires an exec credential plugin which the policy does not allow
*/
public static ClientBuilder standard(ExecCredentialPluginPolicy execCredentialPluginPolicy)
throws IOException {
return standard(true, execCredentialPluginPolicy);
}

/**
* Same as {@link #standard(ExecCredentialPluginPolicy)}, optionally persisting refreshed
* credentials to the kubeconfig file.
*
* @param persistConfig whether to persist refreshed credentials to the kubeconfig file
* @param execCredentialPluginPolicy the exec credential plugin policy; null allows all plugins
* @return <tt>ClientBuilder</tt> pre-configured using the precedence of {@link #standard()}
* @throws IOException if the configuration file or a file specified in a configuration file
* cannot be read.
*/
public static ClientBuilder standard(
boolean persistConfig, ExecCredentialPluginPolicy execCredentialPluginPolicy)
throws IOException {
final File kubeConfig = findConfigFromEnv();
ClientBuilder clientBuilderEnv = getClientBuilder(persistConfig, kubeConfig);
ClientBuilder clientBuilderEnv =
getClientBuilder(persistConfig, kubeConfig, execCredentialPluginPolicy);
if (clientBuilderEnv != null) return clientBuilderEnv;
final File config = findConfigInHomeDir();
ClientBuilder clientBuilderHomeDir = getClientBuilder(persistConfig, config);
ClientBuilder clientBuilderHomeDir =
getClientBuilder(persistConfig, config, execCredentialPluginPolicy);
if (clientBuilderHomeDir != null) return clientBuilderHomeDir;
final File clusterCa = new File(SERVICEACCOUNT_CA_PATH);
if (clusterCa.exists()) {
Expand All @@ -120,7 +155,10 @@ public static ClientBuilder standard(boolean persistConfig) throws IOException {
return new ClientBuilder();
}

private static ClientBuilder getClientBuilder(boolean persistConfig, File kubeConfig)
private static ClientBuilder getClientBuilder(
boolean persistConfig,
File kubeConfig,
ExecCredentialPluginPolicy execCredentialPluginPolicy)
throws IOException {
if (kubeConfig != null) {
try (BufferedReader kubeConfigReader =
Expand All @@ -132,6 +170,7 @@ private static ClientBuilder getClientBuilder(boolean persistConfig, File kubeCo
kc.setPersistConfig(new FilePersister(kubeConfig));
}
kc.setFile(kubeConfig);
kc.setExecCredentialPluginPolicy(execCredentialPluginPolicy);
return kubeconfig(kc);
}
}
Expand Down
Loading