Skip to content

Commit

Permalink
fix (kubernetes-client-api) : Check KubeConfig file contents if null/…
Browse files Browse the repository at this point in the history
…empty before deserialization (fabric8io#5221)

Rather than directly deserializing `.kube/config` file content in `io.fabric8.kubernetes.api.model.Config`, do it only when
`.kube/config` file content is non blank.

Signed-off-by: Rohan Kumar <[email protected]>
  • Loading branch information
rohanKanojia authored and manusa committed Jul 5, 2023
1 parent 615b00c commit fa1bc51
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 60 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 6.8-SNAPSHOT

#### Bugs
* Fix #5221: Empty kube config file causes NPE
* Fix #5281: Ensure the KubernetesCrudDispatcher's backing map is accessed w/lock

#### Improvements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -702,73 +702,98 @@ private static String getKubeconfigContents(File kubeConfigFile) {
// the kubeconfig references some assets via relative paths.
private static boolean loadFromKubeconfig(Config config, String context, String kubeconfigContents) {
try {
io.fabric8.kubernetes.api.model.Config kubeConfig = KubeConfigUtils.parseConfigFromString(kubeconfigContents);
config.setContexts(kubeConfig.getContexts());
Context currentContext = setCurrentContext(context, config, kubeConfig);
Cluster currentCluster = KubeConfigUtils.getCluster(kubeConfig, currentContext);
if (currentContext != null) {
config.setNamespace(currentContext.getNamespace());
}
if (currentCluster != null) {
config.setMasterUrl(currentCluster.getServer());
config.setTrustCerts(currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify());
config.setDisableHostnameVerification(
currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify());
config.setCaCertData(currentCluster.getCertificateAuthorityData());
AuthInfo currentAuthInfo = KubeConfigUtils.getUserAuthInfo(kubeConfig, currentContext);
if (currentAuthInfo != null) {
// rewrite tls asset paths if needed
String caCertFile = currentCluster.getCertificateAuthority();
String clientCertFile = currentAuthInfo.getClientCertificate();
String clientKeyFile = currentAuthInfo.getClientKey();
File configFile = config.file;
if (configFile != null) {
caCertFile = absolutify(configFile, currentCluster.getCertificateAuthority());
clientCertFile = absolutify(configFile, currentAuthInfo.getClientCertificate());
clientKeyFile = absolutify(configFile, currentAuthInfo.getClientKey());
}
config.setCaCertFile(caCertFile);
config.setClientCertFile(clientCertFile);
config.setClientCertData(currentAuthInfo.getClientCertificateData());
config.setClientKeyFile(clientKeyFile);
config.setClientKeyData(currentAuthInfo.getClientKeyData());
config.setClientKeyAlgo(getKeyAlgorithm(config.getClientKeyFile(), config.getClientKeyData()));
config.setAutoOAuthToken(currentAuthInfo.getToken());
config.setUsername(currentAuthInfo.getUsername());
config.setPassword(currentAuthInfo.getPassword());

if (Utils.isNullOrEmpty(config.getAutoOAuthToken()) && currentAuthInfo.getAuthProvider() != null) {
if (currentAuthInfo.getAuthProvider().getConfig() != null) {
config.setAuthProvider(currentAuthInfo.getAuthProvider());
if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN))) {
// GKE token
config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN));
} else if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN))) {
// OpenID Connect token
config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN));
}
}
} else if (config.getOauthTokenProvider() == null) { // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
ExecConfig exec = currentAuthInfo.getExec();
if (exec != null) {
ExecCredential ec = getExecCredentialFromExecConfig(exec, configFile);
if (ec != null && ec.status != null && ec.status.token != null) {
config.setAutoOAuthToken(ec.status.token);
} else {
LOGGER.warn("No token returned");
}
}
}
}
if (kubeconfigContents != null && !kubeconfigContents.isEmpty()) {
io.fabric8.kubernetes.api.model.Config kubeConfig = KubeConfigUtils.parseConfigFromString(kubeconfigContents);
mergeKubeConfigContents(config, context, kubeConfig);
return true;
}
} catch (Exception e) {
throw KubernetesClientException.launderThrowable("Failed to parse the kubeconfig.", e);
} catch (IOException ioException) {
throw KubernetesClientException.launderThrowable("Failed to parse the kubeconfig.", ioException);
}

return false;
}

private static void mergeKubeConfigContents(Config config, String context, io.fabric8.kubernetes.api.model.Config kubeConfig)
throws IOException {
config.setContexts(kubeConfig.getContexts());
Context currentContext = setCurrentContext(context, config, kubeConfig);
Cluster currentCluster = KubeConfigUtils.getCluster(kubeConfig, currentContext);
if (currentContext != null) {
config.setNamespace(currentContext.getNamespace());
}
if (currentCluster != null) {
config.setMasterUrl(currentCluster.getServer());
config.setTrustCerts(currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify());
config.setDisableHostnameVerification(
currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify());
config.setCaCertData(currentCluster.getCertificateAuthorityData());
AuthInfo currentAuthInfo = KubeConfigUtils.getUserAuthInfo(kubeConfig, currentContext);
if (currentAuthInfo != null) {
mergeKubeConfigAuthInfo(config, currentCluster, currentAuthInfo);
}
}
}

private static void mergeKubeConfigAuthInfo(Config config, Cluster currentCluster, AuthInfo currentAuthInfo)
throws IOException {
// rewrite tls asset paths if needed
String caCertFile = currentCluster.getCertificateAuthority();
String clientCertFile = currentAuthInfo.getClientCertificate();
String clientKeyFile = currentAuthInfo.getClientKey();
File configFile = config.file;
if (configFile != null) {
caCertFile = absolutify(configFile, currentCluster.getCertificateAuthority());
clientCertFile = absolutify(configFile, currentAuthInfo.getClientCertificate());
clientKeyFile = absolutify(configFile, currentAuthInfo.getClientKey());
}
config.setCaCertFile(caCertFile);
config.setClientCertFile(clientCertFile);
config.setClientCertData(currentAuthInfo.getClientCertificateData());
config.setClientKeyFile(clientKeyFile);
config.setClientKeyData(currentAuthInfo.getClientKeyData());
config.setClientKeyAlgo(getKeyAlgorithm(config.getClientKeyFile(), config.getClientKeyData()));
config.setAutoOAuthToken(currentAuthInfo.getToken());
config.setUsername(currentAuthInfo.getUsername());
config.setPassword(currentAuthInfo.getPassword());

if (Utils.isNullOrEmpty(config.getAutoOAuthToken()) && currentAuthInfo.getAuthProvider() != null) {
mergeKubeConfigAuthProviderConfig(config, currentAuthInfo);
} else if (config.getOauthTokenProvider() == null) { // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
mergeKubeConfigExecCredential(config, currentAuthInfo.getExec(), configFile);
}
}

private static void mergeKubeConfigAuthProviderConfig(Config config, AuthInfo currentAuthInfo) {
if (currentAuthInfo.getAuthProvider().getConfig() != null) {
config.setAuthProvider(currentAuthInfo.getAuthProvider());
if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN))) {
// GKE token
config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN));
} else if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN))) {
// OpenID Connect token
config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN));
}
}
}

private static void mergeKubeConfigExecCredential(Config config, ExecConfig exec, File configFile)
throws IOException {
if (exec != null) {
try {
ExecCredential ec = getExecCredentialFromExecConfig(exec, configFile);
if (ec != null && ec.status != null && ec.status.token != null) {
config.setAutoOAuthToken(ec.status.token);
} else {
LOGGER.warn("No token returned");
}
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable("failure while running exec credential ", interruptedException);
}
}
}

protected static ExecCredential getExecCredentialFromExecConfig(ExecConfig exec, File configFile)
throws IOException, InterruptedException {
String apiVersion = exec.getApiVersion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
Expand Down Expand Up @@ -824,4 +825,17 @@ void refresh_whenOAuthTokenSourceSetToUser_thenConfigUnchanged() {
.isSameAs(config)
.hasFieldOrPropertyWithValue("oauthToken", "token-from-user");
}

@Test
void givenEmptyKubeConfig_whenConfigCreated_thenShouldNotProduceNPE() throws URISyntaxException {
// Given
System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE,
new File(Objects.requireNonNull(getClass().getResource("/test-empty-kubeconfig")).toURI()).getAbsolutePath());

// When
Config config = new ConfigBuilder().build();

// Then
assertThat(config).isNotNull();
}
}
Empty file.

0 comments on commit fa1bc51

Please sign in to comment.