diff --git a/.changeset/brown-keys-raise.md b/.changeset/brown-keys-raise.md new file mode 100644 index 0000000000..1d288d9f57 --- /dev/null +++ b/.changeset/brown-keys-raise.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +The Kubernetes plugin now requests AKS access tokens from Azure when retrieving +objects from clusters configured with `authProvider: aks` and sets `auth.aks` in +its request bodies appropriately. diff --git a/docs/features/kubernetes/authentication.md b/docs/features/kubernetes/authentication.md index d44d3a4a64..ddeabb5db0 100644 --- a/docs/features/kubernetes/authentication.md +++ b/docs/features/kubernetes/authentication.md @@ -66,6 +66,7 @@ as `401` or similar. The providers available as client side are: +- `aks` - `google` - `oidc` diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 458637f316..9280c4f020 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -104,12 +104,13 @@ cluster. Valid values are: | Value | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `serviceAccount` | This will use a Kubernetes [service account](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/) to access the Kubernetes API. When this is used the `serviceAccountToken` field should also be set. | -| `google` | This will use a user's Google auth token from the [Google auth plugin](https://backstage.io/docs/auth/) to access the Kubernetes API. | +| `aks` | This will use a user's AKS access token from the [Microsoft auth provider](https://backstage.io/docs/auth/microsoft/provider) to access the Kubernetes API on AKS clusters. | | `aws` | This will use AWS credentials to access resources in EKS clusters | -| `googleServiceAccount` | This will use the Google Cloud service account credentials to access resources in clusters | | `azure` | This will use [Azure Identity](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview) to access resources in clusters | +| `google` | This will use a user's Google access token from the [Google auth provider](https://backstage.io/docs/auth/google/provider) to access the Kubernetes API on GKE clusters. | +| `googleServiceAccount` | This will use the Google Cloud service account credentials to access resources in clusters | | `oidc` | This will use [Oidc Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens) to authenticate to the Kubernetes API. When this is used the `oidcTokenProvider` field should also be set. Please note the cluster must support OIDC, at the time of writing AKS clusters do not support OIDC. | +| `serviceAccount` | This will use a Kubernetes [service account](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/) to access the Kubernetes API. When this is used the `serviceAccountToken` field should also be set. | Check the [Kubernetes Authentication][4] section for additional explanation. diff --git a/plugins/kubernetes/api-report.md b/plugins/kubernetes/api-report.md index 60f5d3fc99..5529b7910f 100644 --- a/plugins/kubernetes/api-report.md +++ b/plugins/kubernetes/api-report.md @@ -281,6 +281,7 @@ export const kubernetesApiRef: ApiRef; // @public (undocumented) export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { constructor(options: { + microsoftAuthApi: OAuthApi; googleAuthApi: OAuthApi; oidcProviders?: { [key: string]: OpenIdConnectApi; diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/AksKubernetesAuthProvider.ts b/plugins/kubernetes/src/kubernetes-auth-provider/AksKubernetesAuthProvider.ts new file mode 100644 index 0000000000..401fc1e127 --- /dev/null +++ b/plugins/kubernetes/src/kubernetes-auth-provider/AksKubernetesAuthProvider.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ +import { OAuthApi } from '@backstage/core-plugin-api'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; +import { KubernetesAuthProvider } from './types'; + +export class AksKubernetesAuthProvider implements KubernetesAuthProvider { + constructor(private readonly microsoftAuthApi: OAuthApi) {} + + async decorateRequestBodyForAuth( + requestBody: KubernetesRequestBody, + ): Promise { + return { + ...requestBody, + auth: { ...requestBody.auth, aks: (await this.getCredentials()).token }, + }; + } + + async getCredentials(): Promise<{ token?: string }> { + return { + token: await this.microsoftAuthApi.getAccessToken( + '6dae42f8-4368-4678-94ff-3960e28e3630/user.read', + ), + }; + } +} diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.test.ts b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.test.ts index a83cbcfaa4..14e574022a 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.test.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.test.ts @@ -39,11 +39,18 @@ const requestBody: KubernetesRequestBody = { }; describe('KubernetesAuthProviders tests', () => { - const kap = new KubernetesAuthProviders({ - googleAuthApi: new MockAuthApi('googleToken'), - oidcProviders: { - okta: new MockAuthApi('oktaToken'), - }, + let microsoftAuthApi: MockAuthApi; + let kap: KubernetesAuthProviders; + + beforeEach(() => { + microsoftAuthApi = new MockAuthApi('aksToken'); + kap = new KubernetesAuthProviders({ + microsoftAuthApi, + googleAuthApi: new MockAuthApi('googleToken'), + oidcProviders: { + okta: new MockAuthApi('oktaToken'), + }, + }); }); it('adds token to request body for google authProvider', async () => { @@ -52,6 +59,15 @@ describe('KubernetesAuthProviders tests', () => { expect(details.auth?.google).toBe('googleToken'); }); + it('adds token to request body for aks authProvider', async () => { + const details = await kap.decorateRequestBodyForAuth('aks', requestBody); + + expect(details.auth?.aks).toBe('aksToken'); + expect(microsoftAuthApi.getAccessToken).toHaveBeenCalledWith( + '6dae42f8-4368-4678-94ff-3960e28e3630/user.read', + ); + }); + it('adds token to request body for oidc authProvider', async () => { const details = await kap.decorateRequestBodyForAuth( 'oidc.okta', diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts index 836641f1ed..84154d5e96 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts @@ -20,6 +20,7 @@ import { GoogleKubernetesAuthProvider } from './GoogleKubernetesAuthProvider'; import { ServerSideKubernetesAuthProvider } from './ServerSideAuthProvider'; import { OAuthApi, OpenIdConnectApi } from '@backstage/core-plugin-api'; import { OidcKubernetesAuthProvider } from './OidcKubernetesAuthProvider'; +import { AksKubernetesAuthProvider } from './AksKubernetesAuthProvider'; export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { private readonly kubernetesAuthProviderMap: Map< @@ -28,6 +29,7 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { >; constructor(options: { + microsoftAuthApi: OAuthApi; googleAuthApi: OAuthApi; oidcProviders?: { [key: string]: OpenIdConnectApi }; }) { @@ -56,6 +58,10 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { 'localKubectlProxy', new ServerSideKubernetesAuthProvider(), ); + this.kubernetesAuthProviderMap.set( + 'aks', + new AksKubernetesAuthProvider(options.microsoftAuthApi), + ); if (options.oidcProviders) { Object.keys(options.oidcProviders).forEach(provider => { diff --git a/plugins/kubernetes/src/plugin.ts b/plugins/kubernetes/src/plugin.ts index e10b4856b7..02c7bfd2d6 100644 --- a/plugins/kubernetes/src/plugin.ts +++ b/plugins/kubernetes/src/plugin.ts @@ -87,7 +87,11 @@ export const kubernetesPlugin = createPlugin({ onelogin: oneloginAuthApi, }; - return new KubernetesAuthProviders({ googleAuthApi, oidcProviders }); + return new KubernetesAuthProviders({ + microsoftAuthApi, + googleAuthApi, + oidcProviders, + }); }, }), ],