Carpe Diem

備忘録

Backstage でGitHub Orgを用いた認可を導入する

概要

christina04.hatenablog.com

ではGitHubのIDを持っていればログインできる認証機能を追加しました。

今回は「そのGitHubアカウントが特定のGitHub Organizationに所属しているかどうか」を使ってページを閲覧できるかどうか認可する仕組みを導入します。

認証と認可の違い

認証と認可の違いは以下です。

用語 定義
認証 誰であるかを確認すること パスワード、指紋、etc...
認可 権限を与えること 切符を購入した人には電車に乗る権限を与える
※誰かを特定する必要はない

ただ多くのケースでは認証と認可はセットになっていることが多いです。

環境

  • backstage v1.21.1

導入方法

GitHub Appの変更

Organizationの情報を利用するので、GitHub Integrationの機能(GitHub APIを使ってBackstageがアクセスできる情報を増やす)を使います。

Permission

GitHub Apps | Backstage Software Catalog and Developer Platform

で説明されている通りに設定してください。

カテゴリ 対象リソース どんな操作を許可するか 備考
Repository Contents Read & write Software Catalog用。Templateでも使うならWrite権限も
Repository Administration Read & write Templateでリポジトリ作成のため
Repository Metadata Read-only -
Repository Pull requests Read & write -
Repository Issues Read & write -
Repository Workflows Read & write TemplateがGitHub workflowsを使うなら必要
Repository Commit statuses Read-only -
Repository Variables Read & write TemplateがGitHub ActionのVariablesを使うなら必要
Repository Secrets Read & write TemplateがGitHub ActionのSecretsを使うなら必要
Repository Environments Read & write TemplateがGitHub Environmentsを使うなら必要
Organization Members Read-only 認証やSoftware CatalogのOrg情報読み込み用

Private Keysの生成

https://github.com/organizations/<Org名>/settings/apps/<GitHub App名>

にてPrivate Keyを生成します。このKeyを使ってGitHubのOrg情報やOrg内のリポジトリにアクセスします。

生成すると自動的に.pemファイルをダウンロードします。

Backstageにcredentialの登録

github-app-credentials.yamlのようなファイルを作ります。

appId: <app id> # GitHub AppのID
clientId: <client id> # GitHub AppのClientID
clientSecret: <client secret> # GitHub AppのClientSecret
webhookSecret: <webhook secret> # 任意
privateKey: |
  -----BEGIN RSA PRIVATE KEY-----
  ...Key content...
  -----END RSA PRIVATE KEY-----

RSA PRIVATEの部分に先程の.pemの中身を書きます。インデントに気をつけてください。

そしてapp-config.yamlに次の様に登録します。

integrations:
  github:
    - host: github.com
      apps:
        - $include: github-app-credentials.yaml

インストール

特定のOrganizationにアクセスしているかどうかを取得するために、そのOrganizationに作成したGitHub Appをインストールします。

https://github.com/organizations/<Org名>/settings/apps/<GitHub App名>/installations にアクセスし、installボタンを押します。

Organizationやリポジトリへの認可処理が挟まります。Backstageを新規リポジトリ作成時にも使う場合はAll repositoriesを選択してください。

実装

GitHub Orgのimport

GitHub Organizational Data | Backstage Software Catalog and Developer Platform

に則って実装します。

まずプラグインのインストール。

$ yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-github

packages/backend/src/plugins/catalog.tsを次のように実装します。orgUrlは自分の組織のOrg名にしてください。

import { GithubOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-github';

export default async function createPlugin(
  env: PluginEnvironment,
): Promise<Router> {
  const builder = await CatalogBuilder.create(env);

  // The org URL below needs to match a configured integrations.github entry
  // specified in your app-config.
  builder.addEntityProvider(
    GithubOrgEntityProvider.fromConfig(env.config, {
      id: 'production',
      orgUrl: 'https://github.com/jun06t-org',
      logger: env.logger,
      schedule: env.scheduler.createScheduledTaskRunner({
        frequency: { minutes: 60 },
        timeout: { minutes: 15 },
      }),
    }),
  );
...

またapp-config.yamlのcatalog部分にprovidersを追加します。

catalog:
  providers:
    githubOrg:
      id: 'production'
      orgs:
        - jun06t-org

すると起動時に次のようにOrg情報を読み込むようになります。

[1] 2024-01-01T00:12:43.170Z catalog info Read 1 GitHub users and 3 GitHub teams in 1.3 seconds. Committing... type=plugin target=https://github.com/jun06t-org class=GithubOrgEntityProvider taskId=GithubOrgEntityProvider:development:refresh taskInstanceId=31fa7cab-2fa9-4077-92b5-3face253ecaa
[1] 2024-01-01T00:12:43.175Z catalog info Committed 1 GitHub users and 3 GitHub teams in 0.0 seconds. type=plugin target=https://github.com/jun06t-org class=GithubOrgEntityProvider taskId=GithubOrgEntityProvider:development:refresh taskInstanceId=31fa7cab-2fa9-4077-92b5-3face253ecaa

読み込みが完了すると

  • OrgのMembers情報がBackstageのUser
  • OrgのTeam情報がBackstageのGroup

として登録されます。

認証部分の変更

次は認証部分に認可処理を追加します。

packages/backend/src/plugins/auth.ts を次のように変更します。デフォルトのresolverを削除して、コメントアウトされていた部分をアンコメントするだけで済みます。

export default async function createPlugin(
  env: PluginEnvironment,
): Promise<Router> {
  return await createRouter({
    logger: env.logger,
    config: env.config,
    database: env.database,
    discovery: env.discovery,
    tokenManager: env.tokenManager,
    providerFactories: {
      ...defaultAuthProviderFactories,
      github: providers.github.create({
        signIn: {
          resolver: providers.github.resolvers.usernameMatchingUserEntityName(),
        },
      }),
    },
  });
}

動作確認

ユーザがOrgにいない(=Entityに存在しない)場合、以下のようにエラーになります。

ユーザがOrgにいる場合(=Entityに存在する)場合、問題なくログインできます。

その他

サンプルコード

今回の変更点はこちらで確認できます。

github.com

GitHub Appインストール時のコールバックでエラーが出る

"error":{"name":"InputError","message":"Must specify 'env' query to select environment","stack":"InputError: Must specify 'env' query to select environment

というエラーが出るケースです。

OKTA redirect_uri is broken due to configurable authentication environments · Issue #4464 · backstage/backstage · GitHub

を読むとコールバックURLにenvパラメータがないとそうなるようですが、手動でいじらない限り付かなそうでした。
インストール自体は問題ないのでそのまま閉じるでも問題ないです。

Kubernetesでの機密情報の管理

github-app-credentials.yamlをバージョン管理するのは良くないので、これ自体をSecretで管理してマウントし、

integrations:
  github:
    - host: github.com
      apps:
        - $include: ${GITHUB_CREDENTIALS_PATH}

として$includeすると良いでしょう。

また本番用ビルドを作る際はapp-config.yamlに↑の設定がある一方で、ローカルにgithub-app-credentials.yamlを用意していない(Secretに登録後削除した)と次のエラーが発生します。

$ yarn build:backend --config ../../app-config.yaml
yarn run v1.22.19
$ yarn workspace backend build --config ../../app-config.yaml
$ backstage-cli package build --config ../../app-config.yaml
Building app separately because it is a bundled package

$ backstage-cli package build --config github.com/jun06t/backstage-sample/app/app-config.yaml
app:
app:
app:  Error: Failed to read config file at "github.com/jun06t/backstage-sample/app/app-config.yaml", error at .integrations.github[0].apps[0], failed to include "github.com/jun06t/backstage-sample/app/github-app-credentials.yaml", file does not exist

なのでapp-config.yamlでなくapp-config.production.yamlにintegrationsの設定を書くと良いです。

まとめ

GitHubのOrg情報を使った認可機能を実装しました。

参考