From 05f1d74539df2bcd0858a367dda6700132c93820 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Mon, 27 Feb 2023 14:14:31 -0500 Subject: [PATCH] Backend supports AKS auth provider Signed-off-by: Jamie Klassen --- .changeset/fluffy-mirrors-happen.md | 7 +++++ .changeset/thirty-jobs-sort.md | 5 ++++ plugins/kubernetes-backend/api-report.md | 9 ++++++ .../ConfigClusterLocator.test.ts | 15 ++++++++++ .../cluster-locator/ConfigClusterLocator.ts | 3 ++ .../AksKubernetesAuthTranslator.test.ts | 29 ++++++++++++++++++ .../AksKubernetesAuthTranslator.ts | 30 +++++++++++++++++++ .../src/kubernetes-auth-translator/index.ts | 1 + .../src/service/KubernetesBuilder.ts | 2 ++ plugins/kubernetes-common/api-report.md | 2 ++ plugins/kubernetes-common/src/types.ts | 1 + 11 files changed, 104 insertions(+) create mode 100644 .changeset/fluffy-mirrors-happen.md create mode 100644 .changeset/thirty-jobs-sort.md create mode 100644 plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.test.ts create mode 100644 plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.ts diff --git a/.changeset/fluffy-mirrors-happen.md b/.changeset/fluffy-mirrors-happen.md new file mode 100644 index 0000000000..6dce398217 --- /dev/null +++ b/.changeset/fluffy-mirrors-happen.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Kubernetes clusters now support `authProvider: aks`. When configured this way, +the `retrieveObjectsByServiceId` action will use the `auth.aks` value in the +request body as a bearer token to authenticate with Kubernetes. diff --git a/.changeset/thirty-jobs-sort.md b/.changeset/thirty-jobs-sort.md new file mode 100644 index 0000000000..8a40302502 --- /dev/null +++ b/.changeset/thirty-jobs-sort.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-common': patch +--- + +AKS access tokens can now be sent over the wire to the Kubernetes backend. diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 668061cd31..ad8ff32891 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -21,6 +21,15 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import type { RequestHandler } from 'express'; import { TokenCredential } from '@azure/identity'; +// @public (undocumented) +export class AksKubernetesAuthTranslator { + // (undocumented) + decorateClusterDetailsWithAuth( + clusterDetails: ClusterDetails, + auth: KubernetesRequestAuth, + ): Promise; +} + // @public (undocumented) export interface AWSClusterDetails extends ClusterDetails { // (undocumented) diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts index 5d8fefc5d2..7f545a95de 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts @@ -256,4 +256,19 @@ describe('ConfigClusterLocator', () => { }, ]); }); + + it('supports aks authProvider', async () => { + const cluster = { + name: 'aks-cluster', + url: 'https://aks.test', + authProvider: 'aks', + }; + const sut = ConfigClusterLocator.fromConfig( + new ConfigReader({ clusters: [cluster] }), + ); + + const result = await sut.getClusters(); + + expect(result).toMatchObject([cluster]); + }); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts index b0143c2010..a759cb4bb0 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts @@ -76,6 +76,9 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier { case 'googleServiceAccount': { return clusterDetails; } + case 'aks': { + return clusterDetails; + } default: { throw new Error( `authProvider "${authProvider}" has no config associated with it`, diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.test.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.test.ts new file mode 100644 index 0000000000..d0d3cf87bb --- /dev/null +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.test.ts @@ -0,0 +1,29 @@ +/* + * 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 { AksKubernetesAuthTranslator } from './AksKubernetesAuthTranslator'; + +describe('AksKubernetesAuthTranslator', () => { + it('uses auth.aks value as bearer token', async () => { + const translator = new AksKubernetesAuthTranslator(); + + const details = await translator.decorateClusterDetailsWithAuth( + { name: '', authProvider: 'aks', url: '' }, + { aks: 'aksToken' }, + ); + + expect(details.serviceAccountToken).toBe('aksToken'); + }); +}); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.ts new file mode 100644 index 0000000000..13d784293e --- /dev/null +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AksKubernetesAuthTranslator.ts @@ -0,0 +1,30 @@ +/* + * 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 { ClusterDetails } from '../types/types'; +import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; + +/** + * + * @public + */ +export class AksKubernetesAuthTranslator { + async decorateClusterDetailsWithAuth( + clusterDetails: ClusterDetails, + auth: KubernetesRequestAuth, + ): Promise { + return { ...clusterDetails, serviceAccountToken: auth.aks }; + } +} diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/index.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/index.ts index 4ee83b0da0..71dcedfee6 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/index.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export * from './AksKubernetesAuthTranslator'; export * from './AwsIamKubernetesAuthTranslator'; export * from './AzureIdentityKubernetesAuthTranslator'; export * from './GoogleKubernetesAuthTranslator'; diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 75b619b412..8f9d0769ed 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -33,6 +33,7 @@ import { GoogleServiceAccountAuthTranslator, AzureIdentityKubernetesAuthTranslator, OidcKubernetesAuthTranslator, + AksKubernetesAuthTranslator, } from '../kubernetes-auth-translator'; import { addResourceRoutesToRouter } from '../routes/resourcesRoutes'; @@ -355,6 +356,7 @@ export class KubernetesBuilder { protected buildAuthTranslatorMap() { this.authTranslatorMap = { google: new GoogleKubernetesAuthTranslator(), + aks: new AksKubernetesAuthTranslator(), aws: new AwsIamKubernetesAuthTranslator({ config: this.env.config }), azure: new AzureIdentityKubernetesAuthTranslator(this.env.logger), serviceAccount: new NoopKubernetesAuthTranslator(), diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index 6c88074e50..56faeea7d9 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -226,6 +226,8 @@ export const kubernetesProxyPermission: BasicPermission; // @public (undocumented) export interface KubernetesRequestAuth { + // (undocumented) + aks?: string; // (undocumented) google?: string; // (undocumented) diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index f5701d49fe..8eabb16d24 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -35,6 +35,7 @@ import { Entity } from '@backstage/catalog-model'; /** @public */ export interface KubernetesRequestAuth { google?: string; + aks?: string; oidc?: { [key: string]: string; };