From 68af4d556b6643593b02e0df6b876f826bc4224f Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 14 Jun 2021 13:50:36 +0100 Subject: [PATCH] Add allowedInstallationIds list to each github app This allows a single instance of backstage to optionally limit the set of github app installations that may be used by backstage. Previously, if you had github app installations for tenant1 and tenant2 there was nothing stopping the first from accessing resources of the second. The default behaviour of the GithubCredentialsProvider remains the same. Signed-off-by: Brian Fletcher --- .changeset/ninety-worms-kick.md | 5 +++++ docs/plugins/github-apps.md | 20 +++++++++++++++++++ .../src/github/GithubCredentialsProvider.ts | 13 ++++++++++++ packages/integration/src/github/config.ts | 7 +++++++ 4 files changed, 45 insertions(+) create mode 100644 .changeset/ninety-worms-kick.md diff --git a/.changeset/ninety-worms-kick.md b/.changeset/ninety-worms-kick.md new file mode 100644 index 0000000000..5170dbbdce --- /dev/null +++ b/.changeset/ninety-worms-kick.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +adds an allow list of github installations diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 87d23b45d5..5e69ae8294 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -84,3 +84,23 @@ integrations: apps: - $include: example-backstage-app-credentials.yaml ``` + +### Limiting the Github App installations + +If you want to limit the Github app installations visible to backstage you +may optionally include the `allowedInstallationIds` option. + +```yaml +appId: 1 +allowedInstallationIds: [1234] +clientId: client id +clientSecret: client secret +webhookSecret: webhook secret +privateKey: | + -----BEGIN RSA PRIVATE KEY----- + ...Key content... + -----END RSA PRIVATE KEY----- +``` + +This will result in backstage preventing the use of any installation that is not within the +allow list. diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index 9bc09deab3..8dbc3e7a69 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -68,8 +68,10 @@ class GithubAppManager { private readonly baseAuthConfig: { appId: number; privateKey: string }; private installations?: RestEndpointMethodTypes['apps']['listInstallations']['response']; private readonly cache = new Cache(); + private readonly allowedInstallations: number[] | undefined; // undefined allows all installations constructor(config: GithubAppConfig, baseUrl?: string) { + this.allowedInstallations = config.allowedInstallations; this.baseAuthConfig = { appId: config.appId, privateKey: config.privateKey, @@ -91,6 +93,17 @@ class GithubAppManager { suspended, repositorySelection, } = await this.getInstallationData(owner); + if (this.allowedInstallations) { + if (!this.allowedInstallations?.includes(installationId)) { + throw new Error( + `The GitHub application for ${[owner, repo] + .filter(Boolean) + .join( + '/', + )} is not included in the allowed installation list (${installationId}).`, + ); + } + } if (suspended) { throw new Error( `The GitHub application for ${[owner, repo] diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts index 94ed00731e..370aed9c8b 100644 --- a/packages/integration/src/github/config.ts +++ b/packages/integration/src/github/config.ts @@ -93,6 +93,10 @@ export type GithubAppConfig = { * Client secrets can be generated at https://github.com/organizations/$org/settings/apps/$AppName */ clientSecret: string; + /** + * List of installations allowed to be used by this backstage https://github.com/app/installations/$InstallationId + */ + allowedInstallations?: number[]; }; /** @@ -113,6 +117,9 @@ export function readGitHubIntegrationConfig( clientSecret: c.getString('clientSecret'), webhookSecret: c.getString('webhookSecret'), privateKey: c.getString('privateKey'), + allowedInstallations: c + .getOptionalStringArray('allowedInstallations') + ?.map(allowedInstallation => Number(allowedInstallation)), })); if (!isValidHost(host)) {