From 68d26bed2dd9a1c97d9c8cb416b8c7abcb5989cf Mon Sep 17 00:00:00 2001 From: Talat Uyarer Date: Fri, 3 Jun 2022 01:55:25 -0700 Subject: [PATCH 1/3] Initial version of GCP Auth support without gcloud --- kubernetes-client-api/pom.xml | 5 + .../client/utils/GCPAuthenticatorUtils.java | 133 ++++++++++++++++++ .../client/utils/TokenRefreshInterceptor.java | 5 +- .../fabric8/kubernetes/client/ConfigTest.java | 10 ++ .../utils/GCPAuthenticatorUtilsTest.java | 50 +++++++ .../utils/TokenRefreshInterceptorTest.java | 45 ++++++ .../src/test/resources/test-kubeconfig-gcp | 37 +++++ .../token-refresh-interceptor/kubeconfig-gcp | 37 +++++ pom.xml | 7 + 9 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java create mode 100644 kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java create mode 100644 kubernetes-client-api/src/test/resources/test-kubeconfig-gcp create mode 100644 kubernetes-client-api/src/test/resources/token-refresh-interceptor/kubeconfig-gcp diff --git a/kubernetes-client-api/pom.xml b/kubernetes-client-api/pom.xml index 50de747b8a3..36eac56ee67 100644 --- a/kubernetes-client-api/pom.xml +++ b/kubernetes-client-api/pom.xml @@ -155,6 +155,11 @@ bcpkix-jdk15on true + + com.google.auth + google-auth-library-oauth2-http + true + diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java new file mode 100644 index 00000000000..0cd32183bcf --- /dev/null +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2015 Red Hat, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.fabric8.kubernetes.client.utils; + +import com.google.auth.oauth2.AccessToken; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.common.annotations.VisibleForTesting; +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class for GCP token refresh. + */ +public class GCPAuthenticatorUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(GCPAuthenticatorUtils.class); + + public static final String EMPTY = ""; + public static final String ACCESS_TOKEN_PARAM = "access_token"; + public static final String EXPIRY_PARAM = "expiry"; + public static final String SCOPES = "scopes"; + public static final String[] DEFAULT_SCOPES = + new String[]{ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email" + }; + private static GoogleCredentials credentials; + + private GCPAuthenticatorUtils() { + } + + /** + * Google Application Credentials-based refresh + * https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud + * @param currentAuthProviderConfig current AuthInfo's AuthProvider config as a map + * @return access token for interacting with Google Kubernetes API + */ + public static CompletableFuture resolveTokenFromAuthConfig( + Map currentAuthProviderConfig) { + String[] scopes = parseScopes(currentAuthProviderConfig); + try { + if (credentials == null) { + credentials = GoogleCredentials.getApplicationDefault().createScoped(scopes); + } + AccessToken accessToken = credentials.getAccessToken(); + currentAuthProviderConfig.put(ACCESS_TOKEN_PARAM, accessToken.getTokenValue()); + currentAuthProviderConfig.put(EXPIRY_PARAM, + accessToken.getExpirationTime().toInstant().toString()); + persistKubeConfigWithUpdatedToken(currentAuthProviderConfig); + CompletableFuture.completedFuture(accessToken.getTokenValue()); + } catch (IOException e) { + throw new RuntimeException("The Application Default Credentials are not available.", e); + } + return CompletableFuture.completedFuture(currentAuthProviderConfig.get(ACCESS_TOKEN_PARAM)); + } + + public static String[] parseScopes(Map config) { + String scopes = (String) config.get(SCOPES); + if (scopes == null) { + return DEFAULT_SCOPES; + } + if (scopes.isEmpty()) { + return new String[]{}; + } + return scopes.split(","); + } + + /** + * Save Updated Access and Refresh token in local KubeConfig file. + * + * @param kubeConfigPath Path to KubeConfig (by default .kube/config) + * @param updatedAuthProviderConfig updated AuthProvider configuration + * @return boolean value whether update was successful not not + * @throws IOException in case of any failure while writing file + */ + static boolean persistKubeConfigWithUpdatedToken(String kubeConfigPath, + Map updatedAuthProviderConfig) + throws IOException { + io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig( + new File(kubeConfigPath)); + NamedContext currentNamedContext = KubeConfigUtils.getCurrentContext(config); + + if (currentNamedContext != null) { + // Update users > auth-provider > config + int currentUserIndex = KubeConfigUtils.getNamedUserIndexFromConfig(config, + currentNamedContext.getContext().getUser()); + Map authProviderConfig = config.getUsers().get(currentUserIndex).getUser() + .getAuthProvider().getConfig(); + authProviderConfig.put(ACCESS_TOKEN_PARAM, updatedAuthProviderConfig.get(ACCESS_TOKEN_PARAM)); + authProviderConfig.put(EXPIRY_PARAM, updatedAuthProviderConfig.get(EXPIRY_PARAM)); + config.getUsers().get(currentUserIndex).getUser().getAuthProvider() + .setConfig(authProviderConfig); + + // Persist changes to KUBECONFIG + try { + KubeConfigUtils.persistKubeConfigIntoFile(config, kubeConfigPath); + return true; + } catch (IOException exception) { + LOGGER.warn("failed to write file {}", kubeConfigPath, exception); + } + } + return false; + } + + private static boolean persistKubeConfigWithUpdatedToken( + Map updatedAuthProviderConfig) throws IOException { + return persistKubeConfigWithUpdatedToken( + io.fabric8.kubernetes.client.Config.getKubeconfigFilename(), + updatedAuthProviderConfig); + } + + @VisibleForTesting + static void setCredentials(GoogleCredentials cre){ + credentials = cre; + } +} diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java index 0d306935818..c8f3dafb537 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptor.java @@ -52,7 +52,10 @@ public CompletableFuture afterFailure(BasicBuilder headerBuilder, HttpR if (newestConfig.getAuthProvider() != null && newestConfig.getAuthProvider().getName().equalsIgnoreCase("oidc")) { newAccessToken = OpenIDConnectionUtils.resolveOIDCTokenFromAuthConfig(newestConfig.getAuthProvider().getConfig(), factory.newBuilder()); - } else { + } else if(newestConfig.getAuthProvider() != null && newestConfig.getAuthProvider().getName().equalsIgnoreCase("gcp")){ + newAccessToken = GCPAuthenticatorUtils.resolveTokenFromAuthConfig(newestConfig.getAuthProvider().getConfig()); + } + else { newAccessToken = CompletableFuture.completedFuture(newestConfig.getOauthToken()); } diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java index aea85da034b..9af23ef2ff5 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java @@ -474,6 +474,16 @@ void testKubeConfigWithAuthConfigProvider() throws URISyntaxException { config.getOauthToken()); } + @Test + void testKubeConfigWithAuthConfigGCPProvider() throws URISyntaxException { + System.setProperty("kubeconfig", new File(getClass().getResource("/test-kubeconfig-gcp").toURI()).getAbsolutePath()); + Config config = Config.autoConfigure("production/172-28-128-4:8443/mmosley"); + + assertEquals("https://172.28.128.4:8443/", config.getMasterUrl()); + assertEquals(null, config.getOauthToken()); + assertEquals("gcp", config.getAuthProvider().getName()); + } + @Test void testEmptyConfig() { // Given diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java new file mode 100644 index 00000000000..90fa3541c31 --- /dev/null +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java @@ -0,0 +1,50 @@ +package io.fabric8.kubernetes.client.utils; + +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.CLIENT_ID_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.CLIENT_SECRET_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.ID_TOKEN_KUBECONFIG; +import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.ID_TOKEN_PARAM; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.google.auth.oauth2.AccessToken; +import com.google.auth.oauth2.GoogleCredentials; +import java.io.File; +import java.net.HttpURLConnection; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.Instant; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class GCPAuthenticatorUtilsTest { + + @Test + void testRefreshToken() throws Exception { + // Given + String fakeToken = "new-fake-token"; + String fakeTokenExpiry = "2121-08-05T02:30:24Z"; + Map currentAuthProviderConfig = new HashMap<>(); + File tempFile = Files.createTempFile("test", "kubeconfig").toFile(); + Files.copy(getClass().getResourceAsStream("/test-kubeconfig-gcp"), Paths.get(tempFile.getPath()), + StandardCopyOption.REPLACE_EXISTING); + System.setProperty("kubeconfig", tempFile.getAbsolutePath()); + + GoogleCredentials mockGC = Mockito.mock(GoogleCredentials.class); + GCPAuthenticatorUtils.setCredentials(mockGC); + Mockito.when(mockGC.getAccessToken()) + .thenReturn(new AccessToken(fakeToken, Date.from(Instant.parse(fakeTokenExpiry)))); + + // When + String token = GCPAuthenticatorUtils.resolveTokenFromAuthConfig(currentAuthProviderConfig).get(); + + // Then + assertNotNull(token); + assertEquals(fakeToken, token); + } + +} diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java index 01b08fa4c45..9c07c95d304 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java @@ -15,10 +15,16 @@ */ package io.fabric8.kubernetes.client.utils; +import com.google.auth.oauth2.AccessToken; +import com.google.auth.oauth2.GoogleCredentials; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.http.HttpRequest; import io.fabric8.kubernetes.client.http.TestHttpResponse; +import java.time.Instant; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -125,4 +131,43 @@ void shouldRefreshOIDCToken() throws Exception { } } + + @Test + void shouldRefreshGCPToken() throws Exception { + try { + // Prepare kubeconfig for autoconfiguration + File tempFile = Files.createTempFile("test", "kubeconfig").toFile(); + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/token-refresh-interceptor/kubeconfig-gcp")), + Paths.get(tempFile.getPath()), StandardCopyOption.REPLACE_EXISTING); + System.setProperty(KUBERNETES_KUBECONFIG_FILE, tempFile.getAbsolutePath()); + + String fakeToken = "new-fake-token"; + String fakeTokenExpiry = "2121-08-05T02:30:24Z"; + GoogleCredentials mockGC = Mockito.mock(GoogleCredentials.class); + GCPAuthenticatorUtils.setCredentials(mockGC); + Mockito.when(mockGC.getAccessToken()) + .thenReturn(new AccessToken(fakeToken, Date.from(Instant.parse(fakeTokenExpiry)))); + + // Prepare HTTP call that will fail with 401 Unauthorized to trigger GCP token renewal. + HttpRequest.Builder builder = Mockito.mock(HttpRequest.Builder.class, Mockito.RETURNS_SELF); + + // Loads the initial kubeconfig. + Config config = Config.autoConfigure(null); + + // Copy over new config with following gcp auth provider configuration: + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/token-refresh-interceptor/kubeconfig-gcp")), + Paths.get(tempFile.getPath()), StandardCopyOption.REPLACE_EXISTING); + + TokenRefreshInterceptor interceptor = new TokenRefreshInterceptor(config, Mockito.mock(HttpClient.Factory.class)); + boolean reissue = interceptor.afterFailure(builder, new TestHttpResponse<>().withCode(401)).get(); + + // Make the call and check that renewed token was read at 401 Unauthorized. + Mockito.verify(builder).setHeader("Authorization", "Bearer new-fake-token"); + assertTrue(reissue); + } finally { + // Remove any side effect. + System.clearProperty(KUBERNETES_KUBECONFIG_FILE); + } + + } } diff --git a/kubernetes-client-api/src/test/resources/test-kubeconfig-gcp b/kubernetes-client-api/src/test/resources/test-kubeconfig-gcp new file mode 100644 index 00000000000..ba37790b1c8 --- /dev/null +++ b/kubernetes-client-api/src/test/resources/test-kubeconfig-gcp @@ -0,0 +1,37 @@ +apiVersion: v1 +clusters: +- cluster: + certificate-authority: testns/ca.pem + server: https://172.28.128.4:8443 + name: 172-28-128-4:8443 +contexts: +- context: + cluster: 172-28-128-4:8443 + namespace: testns + user: user/172-28-128-4:8443 + name: testns/172-28-128-4:8443/user +- context: + cluster: 172-28-128-4:8443 + namespace: production + user: root/172-28-128-4:8443 + name: production/172-28-128-4:8443/root +- context: + cluster: 172-28-128-4:8443 + namespace: production + user: mmosley + name: production/172-28-128-4:8443/mmosley +current-context: production/172-28-128-4:8443/mmosley +kind: Config +preferences: {} +users: +- name: user/172-28-128-4:8443 + user: + token: token +- name: root/172-28-128-4:8443 + user: + token: supertoken +- name: mmosley + user: + auth-provider: + name: gcp + diff --git a/kubernetes-client-api/src/test/resources/token-refresh-interceptor/kubeconfig-gcp b/kubernetes-client-api/src/test/resources/token-refresh-interceptor/kubeconfig-gcp new file mode 100644 index 00000000000..ba37790b1c8 --- /dev/null +++ b/kubernetes-client-api/src/test/resources/token-refresh-interceptor/kubeconfig-gcp @@ -0,0 +1,37 @@ +apiVersion: v1 +clusters: +- cluster: + certificate-authority: testns/ca.pem + server: https://172.28.128.4:8443 + name: 172-28-128-4:8443 +contexts: +- context: + cluster: 172-28-128-4:8443 + namespace: testns + user: user/172-28-128-4:8443 + name: testns/172-28-128-4:8443/user +- context: + cluster: 172-28-128-4:8443 + namespace: production + user: root/172-28-128-4:8443 + name: production/172-28-128-4:8443/root +- context: + cluster: 172-28-128-4:8443 + namespace: production + user: mmosley + name: production/172-28-128-4:8443/mmosley +current-context: production/172-28-128-4:8443/mmosley +kind: Config +preferences: {} +users: +- name: user/172-28-128-4:8443 + user: + token: token +- name: root/172-28-128-4:8443 + user: + token: supertoken +- name: mmosley + user: + auth-provider: + name: gcp + diff --git a/pom.xml b/pom.xml index 2b7cefd939b..a9e1d27e892 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,7 @@ 5.8.2 0.3.0 1.7.36 + 1.3.0 4.6.0 1.18.24 1.30 @@ -764,6 +765,12 @@ slf4j-api ${slf4j.version} + + com.google.auth + google-auth-library-oauth2-http + ${google.version} + true + From 06655cc1ff63ebb6528c1a9345435b9525b5451b Mon Sep 17 00:00:00 2001 From: Talat Uyarer Date: Fri, 3 Jun 2022 08:21:07 -0700 Subject: [PATCH 2/3] Fixed coding style and updated document --- CHANGELOG.md | 1 + doc/FAQ.md | 5 +++- .../client/utils/GCPAuthenticatorUtils.java | 23 +++++++++++-------- .../utils/GCPAuthenticatorUtilsTest.java | 21 ++++++++++++----- .../utils/TokenRefreshInterceptorTest.java | 1 - 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df811dab761..32656efa0b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ And Store.getKey can be used rather than directly referencing static Cache funct #### New Features +* Fabric8 now support GKE's service account auth method without gcloud. It can update auth token whenever it is expired. * Fix #3407: Added Itemable.withItem to directly associate a resource with the DSL. It can be used as an alternative to Loadable.load when you already have the item. There is also client.resourceList(...).getResources() - that will provide the resource list as Resources. This allows you to implement composite operations easily with lambda: client.resourceList(...).getResources().forEach(r -> r.delete()); * Fix #3922: added Client.supports and Client.hasApiGroup methods * KubernetesMockServer has new methods - unsupported and reset - to control what apis are unsupported and to reset its state. diff --git a/doc/FAQ.md b/doc/FAQ.md index dba3167c281..e96117ed927 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -32,4 +32,7 @@ For non-ResourceEventHandlers call backs long-running operation can be a problem On top of the http client threads the fabric8 client maintains a task thread pool for scheduled tasks and for potentially long-running tasks that are called from WebSocket operations, such as handling input and output streams and ResourceEventHandler call backs. This thread pool defaults to an unlimited number of cached threads, which will be shutdown when the client is closed - that is a sensible default with either http client as the amount of concurrently running async tasks will typically be low. If you would rather take full control over the threading use KubernetesClientBuilder.withExecutor or KubernetesClientBuilder.withExecutorSupplier - however note that constraining this thread pool too much will result in a build up of event processing queues. -Finally the fabric8 client will use 1 thread per PortForward and an additional thread per socket connection - this may be improved upon in the future. \ No newline at end of file +Finally the fabric8 client will use 1 thread per PortForward and an additional thread per socket connection - this may be improved upon in the future. + +### How to enable GKE auth support? +Fabric8 now suppoer GKE auth token. Please read for more details to enable it [https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud] diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java index 0cd32183bcf..c8fd7290e10 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java @@ -1,16 +1,19 @@ /** * Copyright (C) 2015 Red Hat, Inc. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package io.fabric8.kubernetes.client.utils; import com.google.auth.oauth2.AccessToken; diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java index 90fa3541c31..d7103c266d6 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtilsTest.java @@ -1,16 +1,26 @@ +/** + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.fabric8.kubernetes.client.utils; -import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.CLIENT_ID_KUBECONFIG; -import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.CLIENT_SECRET_KUBECONFIG; -import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.ID_TOKEN_KUBECONFIG; -import static io.fabric8.kubernetes.client.utils.OpenIDConnectionUtils.ID_TOKEN_PARAM; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; import java.io.File; -import java.net.HttpURLConnection; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -46,5 +56,4 @@ void testRefreshToken() throws Exception { assertNotNull(token); assertEquals(fakeToken, token); } - } diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java index 9c07c95d304..3d4cc5225fe 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/TokenRefreshInterceptorTest.java @@ -168,6 +168,5 @@ void shouldRefreshGCPToken() throws Exception { // Remove any side effect. System.clearProperty(KUBERNETES_KUBECONFIG_FILE); } - } } From 4f99602f4f4dcf14cc29e527ed584e1140d3b36a Mon Sep 17 00:00:00 2001 From: Talat Uyarer Date: Tue, 7 Jun 2022 23:43:54 -0700 Subject: [PATCH 3/3] Added additional information and a logging statement --- doc/FAQ.md | 10 +++++++++- .../kubernetes/client/utils/GCPAuthenticatorUtils.java | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index e96117ed927..8f1511ddecc 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -35,4 +35,12 @@ On top of the http client threads the fabric8 client maintains a task thread poo Finally the fabric8 client will use 1 thread per PortForward and an additional thread per socket connection - this may be improved upon in the future. ### How to enable GKE auth support? -Fabric8 now suppoer GKE auth token. Please read for more details to enable it [https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud] +Fabric8 now support GKE auth token. To enable please add google-auth-library-oauth2-http library on your project + + + com.google.auth + google-auth-library-oauth2-http + ${google.version} + + +Further information [https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud](https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud) diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java index c8fd7290e10..09a0c147ff5 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/GCPAuthenticatorUtils.java @@ -61,7 +61,9 @@ public static CompletableFuture resolveTokenFromAuthConfig( try { if (credentials == null) { credentials = GoogleCredentials.getApplicationDefault().createScoped(scopes); + LOGGER.debug("GoogleCredentials: ", credentials); } + credentials.refresh(); AccessToken accessToken = credentials.getAccessToken(); currentAuthProviderConfig.put(ACCESS_TOKEN_PARAM, accessToken.getTokenValue()); currentAuthProviderConfig.put(EXPIRY_PARAM, @@ -75,7 +77,7 @@ public static CompletableFuture resolveTokenFromAuthConfig( } public static String[] parseScopes(Map config) { - String scopes = (String) config.get(SCOPES); + String scopes = config.get(SCOPES); if (scopes == null) { return DEFAULT_SCOPES; }