echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【AWS CDK】AWS CodePipelineからGitHubと認証する方法

本日のお題

前回、「GitHubからAWSに認証を行う(OIDCを使用)」というのをAWS CDKで実装しました。

今回はその逆で、「AWSからGitHubに認証を行う」というのをAWS CDKで実装します。(AWS CodePipelineでCI/CDを構成する際に必要になります)

なお、よく使われる&ブログ記事でも多いのは「個人アクセストークン(GitHub Personal Access Token、以下PAT)を使う方法」なのですが、この記事では「接続(CodeStar接続)」で行う方法を扱います。

上記を実施する際に必要な手順

  1. CodeStar接続の作成&GitHub Appの作成&インストール
  2. AWS CDKの実装

CodeStar接続を使うメリット

個人ユーザーに依存しない

PATは個人ユーザーに紐づくため、例えば部署移動や退職などでそのユーザーが抜けてしまった場合、使用できなくなります。
そのため、アプリでPAT使用していると、思わぬ事故(=突然接続できなくなった)の原因になりますし、またそれを管理しておく必要があります。

CodeStar接続は該当のGitHubリポジトリに紐づくので、そういったことは起こりません。(間違ってGitHubアプリを削除してしまった...などが起こる可能性はありますが...)

セキュリティ的な問題

PATは有効期限があり、期限切れ前にアプリ側のPATも更新しなければならず、管理が手間になりますし、無期限だとセキュリティ的に問題です。
またPATが漏洩したらそれだけで該当GitHubに接続できてしまうので、セキュリティ的にも問題になります。

CodeStar接続には有効期限はありませんし、仮にARNが漏洩してもそれだけで該当GitHubに接続できてしまうということはありません。(操作者に該当CodeStar接続を使用する権限がなければ接続できない)

GitHub Enterpriseでも使える

AWS CDKのソースアクション(aws-cdk-lib.aws_codepipeline_actions)には、GitHub Enterprise(以下GHE)専用のアクションがないので、GHEにPATで接続できません。
そのためGHEに接続する場合、PAT以外での接続が必須になります。

ちなみにGitHubの場合、codepipeline_actions.GitHubSourceAction から下記のようにPATを使って接続できます。(AWS CDK公式より)

const sourceAction = new codepipeline_actions.GitHubSourceAction({
  actionName: 'GitHub_Source',
  owner: '<GitHubアカウントまたはorganization名>',
  repo: '<リポジトリ名>',
  oauthToken: `<PATの値>`,
  output: sourceOutput,  // アーティファクトの出力先
  branch: 'develop', // default: 'master'
});

なおAWS CDKでは、CodeStar接続以外に下記ソースプロバイダのアクションが用意されています。

CodeStar接続の作成&GitHub Appの作成&インストール

以下の手順で、CodeStar接続を作成します。

  • CodePipeline左ツリーの [設定] - [接続] から、「接続の作成」をクリックする

  • プロバイダー(GitHub, GHEなど)、及び接続名を選択・入力する

  • AWS Connector for <プロバイダー名> by ...」という画面が表示されるので、「Authorize AWS Connector...」ボタンをクリックする

  • GitHubに接続する」という画面が表示されるので「新しいアプリをインストールする」をクリックする

    • すでに別でGitHubアプリを作成済なら既存のアプリを選択します。
  • この後GitHubの認証画面が表示されるので、ログイン情報を入力する。

    • なおOwners権限をもったアカウントでログインしないとGitHubアプリの作成ができません。
  • GitHubアプリをインストールするリポジトリを選択します。(全て/選択リポジトリのみ)

  • GitHubに接続する」画面で「接続」をクリックする

    • GitHubアプリ名に何か値が表示されているはずです
  • 「<接続名>」画面で「ステータス」が「接続可能」になっていればOKです

CodeStar接続の作成が完了したら、そのARNをコピペしておきます。(AWS CDKで使います)

ちなみに、CodeStar接続自体はAWS CDKでも作成できますが「作成できるのは接続名のみで、GitHubアプリは手作業で作成しないといけない」という理由から、今回は手作業で作成しました。

AWS CDKの実装

では、AWS CDKの実装になります。
なおボリュームの関係で、CodeStar接続に関する部分(=ソースアクション)のみ記載します。

AWS CodePipelineとCDK Pipeline、及びその違いの説明は省略します。(とりあえず「そういうものがあるんだ」くらいの認識で問題ありません。)

AWS CodePipelineの場合
// CodePipelineで使うIAM Role
const pipelineRole = new iam.Role(this, 'codePipelineRole', {
   assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
   roleName: 'codePipelineRole',
   managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess')],
});
    
// サービスプリンシパルにCodeBuildを追加(接続だけなら不要かも)
pipelineRole.assumeRolePolicy.addStatements(
  new iam.PolicyStatement({
    // Restrict to listing and describing tables
    principals: [new iam.ServicePrincipal('codebuild.amazonaws.com')],
    actions: ['sts:AssumeRole'],
    effect: iam.Effect.ALLOW,
   }),
);
  
// 上記Roleに追加するIAMポリシー
const pipelineInlinePolicy = new iam.Policy(this, 'pipelineInlinePolicy', {
  // CodePipelineにcodestar-connections:UseConnectionを許可するポリシー。
  // これがないとCodeStar接続ができないので注意!!!
  policyName: 'pipelineInlinePolicy',
  statements: [
    new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['codestar-connections:UseConnection'],
    resources: [<作成したCodeStar接続のArn>],
  }),
  // あと、CodeBuildで必要なアクションの許可だったり、
  // Artifactバケットへのアクセス許可だったりを定義
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['codebuild:StartBuild', 'codebuild:BatchGetBuilds'],
    resources: [`arn:aws:codebuild:${this.region}:${this.account}:project/*`],
  }),
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['s3:*'],
    resources: [`<ArtifactバケットのARN>`, `<ArtifactバケットのARN>/*`],
  }),
});
  
pipelineRole.attachInlinePolicy(pipelineInlinePolicy);  
  
// ソースアクションの設定
const sourceArtifact = new codepipeline.Artifact();
const sourceAction = new actions.CodeStarConnectionsSourceAction({
  actionName: 'CodeStarConnectionsSourceAction',
  // ここで作成したCodeStar接続のARNを設定するのが重要
  connectionArn: `<作成したCodeStar接続のARN>`,
  output: sourceArtifact,
  owner: '<GitHubのアカウントororganization名(=repoの「/」の左側)>',
  repo: '<対象のリポジトリ名>',
  // デフォルトはmaster(mainじゃない)
  branch: '<対象のブランチ名>', 
  // 先に作成しておいたIAM Roleを設定する。
  // これがないと接続に失敗するので注意
  role: pipelineRole,
  // この2つはデフォルト値(同じなら設定不要)
  // runOrder: 1,
  // triggerOnPush: true,
});
CDK Pipelineの場合
// IAM Roleや割り当てポリシーはAWS CodePipelineと同じなので省略
// AWS CodePipeline同様、IAM Roleでcodestar-connections:UseConnectionを
// CodePipeline(CodeBuildも?)に許可しないと接続が失敗するので注意
const steps = new CodeBuildStep('CodeBuildStep', {
   commands: ['npm run build', 'npx cdk synth'],
   actionRole: pipelineRole,
   // inputにCodePipelineSource.connectionメソッドで接続情報を指定する。
  // ここが重要!!!
   input: CodePipelineSource.connection('<対象のリポジトリ名>', '<対象のブランチ名>', {
     connectionArn: `<作成したCodeStar接続のARN>`,
     triggerOnPush: true,
   }),
   installCommands: ['npm install'],
   role: pipelineRole,
});
  

new CodePipeline(this, 'Pipeline', {
  pipelineName: ’CDKPipelineSample’,
  synth: steps,
  role: pipelineRole,
});

接続できない場合

GitHubと接続ができない場合、主に下記の原因が考えられるので、確認してみてください。(もちろん他にもあります)

CDKの定義ミス

そもそもCDKの定義が間違っていないかを確認します。(意外とARNが間違っていたり、typoが原因だったりします)

GHE側の接続制限(GHE)

GHEの場合、セキュリティ上の理由で、GitHub側で「ホワイトリストに登録したIPアドレスしか接続を許可していない」というケースがあります。

この場合、下記の対応が必要になります。

  • AWS CodePipelineをVPCに入れる
  • そのVPCから外部にアクセスするリソース(NATなど)にパブリックIP(Elastic IPなど)を割り当てる
  • 上記IPアドレスホワイトリストに追加する

細かい説明は省略しますが、この場合は [設定] - [接続] の「ホスト」タブでVPCを使用するホストを作成することで接続可能になります。

まとめ

以上、AWS CodePipelineでGitHubと認証する方法でした。

PATは簡単な判明、今回記載したような事項もあったり、GitHub Enterpriseだといろいろ制限があったりするので、その場合は今回のCodeStar接続を使うことになります。
ちょっとややこしいですが、いろいろ便利な面もあるので、ぜひ有効に活用してください。

それでは、今回はこの辺で。