diff --git a/.changeset/big-teachers-dress.md b/.changeset/big-teachers-dress.md new file mode 100644 index 0000000000..4545939c0a --- /dev/null +++ b/.changeset/big-teachers-dress.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-kubernetes-backend': patch +--- + +add Azure Identity auth provider diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index cb30451c7d..d880aaf6cd 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -90,7 +90,8 @@ cluster. Valid values are: | `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. | | `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 | +| `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 | ##### `clusters.\*.skipTLSVerify` diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 9eca295f96..03682807e2 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -24,6 +24,11 @@ export interface AWSClusterDetails extends ClusterDetails { externalId?: string; } +// Warning: (ae-missing-release-tag) "AzureClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface AzureClusterDetails extends ClusterDetails {} + // Warning: (ae-missing-release-tag) "ClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 125f03322e..4d4cd8dfd0 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -35,6 +35,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { + "@azure/identity": "^2.0.4", "@backstage/backend-common": "^0.13.3-next.0", "@backstage/catalog-model": "^1.0.1", "@backstage/config": "^1.0.0", diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts index 1bde1226dd..5598740873 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts @@ -61,6 +61,9 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier { return { assumeRole, externalId, ...clusterDetails }; } + case 'azure': { + return clusterDetails; + } case 'serviceAccount': { return clusterDetails; } diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts new file mode 100644 index 0000000000..ab6469b4e4 --- /dev/null +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 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 { KubernetesAuthTranslator } from './types'; +import { AzureClusterDetails } from '../types/types'; +import { DefaultAzureCredential } from '@azure/identity'; + +const aksScope = "6dae42f8-4368-4678-94ff-3960e28e3630/.default" // This scope is the same for all Azure Managed Kubernetes + +export class AzureIdentityKubernetesAuthTranslator + implements KubernetesAuthTranslator +{ + async decorateClusterDetailsWithAuth( + clusterDetails: AzureClusterDetails, + ): Promise { + const clusterDetailsWithAuthToken: AzureClusterDetails = Object.assign( + {}, + clusterDetails, + ); + + const credentials = new DefaultAzureCredential(); + + // TODO: can we cache this? It's inneficiant to get a new token every time + const accessToken = await credentials.getToken(aksScope); + clusterDetailsWithAuthToken.serviceAccountToken = accessToken.token + return clusterDetailsWithAuthToken; + } +} diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts index 337ae899dc..e9a8a00ae9 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts @@ -19,6 +19,7 @@ import { GoogleKubernetesAuthTranslator } from './GoogleKubernetesAuthTranslator import { ServiceAccountKubernetesAuthTranslator } from './ServiceAccountKubernetesAuthTranslator'; import { AwsIamKubernetesAuthTranslator } from './AwsIamKubernetesAuthTranslator'; import { GoogleServiceAccountAuthTranslator } from './GoogleServiceAccountAuthProvider'; +import { AzureIdentityKubernetesAuthTranslator } from './AzureIdentityKubernetesAuthTranslator'; export class KubernetesAuthTranslatorGenerator { static getKubernetesAuthTranslatorInstance( @@ -31,6 +32,9 @@ export class KubernetesAuthTranslatorGenerator { case 'aws': { return new AwsIamKubernetesAuthTranslator(); } + case 'azure': { + return new AzureIdentityKubernetesAuthTranslator(); + } case 'serviceAccount': { return new ServiceAccountKubernetesAuthTranslator(); } diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 12c4d2fb48..706582f439 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -147,6 +147,7 @@ export interface ClusterDetails { } export interface GKEClusterDetails extends ClusterDetails {} +export interface AzureClusterDetails extends ClusterDetails {} export interface ServiceAccountClusterDetails extends ClusterDetails {} export interface AWSClusterDetails extends ClusterDetails { assumeRole?: string; diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/AzureKubernetesAuthProvider.ts b/plugins/kubernetes/src/kubernetes-auth-provider/AzureKubernetesAuthProvider.ts new file mode 100644 index 0000000000..59dd0321b3 --- /dev/null +++ b/plugins/kubernetes/src/kubernetes-auth-provider/AzureKubernetesAuthProvider.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2020 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 { KubernetesAuthProvider } from './types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; + +export class AzureKubernetesAuthProvider implements KubernetesAuthProvider { + async decorateRequestBodyForAuth( + requestBody: KubernetesRequestBody, + ): Promise { + // No-op, with aws auth, server's Azire credentials are used for access + return requestBody; + } +} diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts index be6712f103..5bccccc43d 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts @@ -21,6 +21,7 @@ import { ServiceAccountKubernetesAuthProvider } from './ServiceAccountKubernetes import { AwsKubernetesAuthProvider } from './AwsKubernetesAuthProvider'; import { OAuthApi } from '@backstage/core-plugin-api'; import { GoogleServiceAccountAuthProvider } from './GoogleServiceAccountAuthProvider'; +import { AzureKubernetesAuthProvider } from './AzureKubernetesAuthProvider'; export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { private readonly kubernetesAuthProviderMap: Map< @@ -43,6 +44,7 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { new GoogleServiceAccountAuthProvider(), ); this.kubernetesAuthProviderMap.set('aws', new AwsKubernetesAuthProvider()); + this.kubernetesAuthProviderMap.set('azure', new AzureKubernetesAuthProvider()); } async decorateRequestBodyForAuth(