Amazon Web Services ブログ

インターネットアクセスのないプライベートな Amazon Virtual Private Cloud でセキュアな CI/CD パイプラインを構築する

クラウドが台頭し、セキュリティ意識が高まるとともに、インターネットアクセスのない Amazon VPC の利用が急速に拡大しています。これは、隔離によって適切なセキュリティを確保する際の推奨構成です。そして、隔離要件はパイプラインにも当てはまります。開発者は、開発のライフサイクルを通してアプリケーションモジュールやソフトウェアパッケージ、その他依存関係やバンドルをパイプラインにデプロイします。これは、より大きなバンドルを開発環境からステージングなどのターゲット環境にプッシュすることなく行われます。さらに、AWS CodeArtifact がアーティファクト管理サービスとして使用され、あらゆる規模の組織がソフトウェア開発プロセスにおいてソフトウェアパッケージをセキュアに保存、公開、および共有するのを助けます。

これから、インターネットアクセスのないセキュアでプライベートな CI/CD パイプラインを構築するステップを段階的に説明します。ログの保持は Amazon CloudWatch で継続的に行います。また、ソースコード管理に AWS CodeCommit を、モジュールとソフトウェアパッケージの管理に CodeArtifact を、アーティファクトの保管に Amazon Simple Storage Service (Amazon S3) を使用します。

前提条件

この記事の内容に従うための前提条件は以下の通りです。

  • AWS アカウント
  • Virtual Private Cloud (Amazon VPC)
  • CI/CD パイプライン (CodePipeline や Jenkins など、CodeArtifact と統合したい任意の CI/CD ツールを指します。ここでは CodePipeline を使って説明します。)

ソリューション解説

焦点となるメインのサービスは CodeArtifact です。CodeArtifact はフルマネージド型のアーティファクトリポジトリサービスで、あらゆる規模の組織がソフトウェア開発プロセスにおいてソフトウェアパッケージをセキュアに保存、公開、および共有することを容易にします。CodeArtifact は、Maven や Gradle (Java)、npm や yarn (JavaScript)、pip や twine (Python)、およびNuGet (.NET)といった一般に使用されるパッケージマネージャーやビルドツールに対応しています。

ユーザーが CodeCommit にソースコードをプッシュすると、CodePipeline が変更を検知してパイプラインの実行を開始します。CodeBuild ではビルドステージが行われ、プライベートなエンドポイントを利用して、インターネットに出ることなく必要なソフトウェアパッケージをダウンロードします。

上記の図は、リクエストが Internet Gateway を経由せず、VPC 内にプライベートで留まる方法を示しています。これは、リクエストが CodeBuild からプライベートエンドポイントを通って CodeArtifact に至るまで、すべてプライベートサブネット内にあることによって実現しています。
(訳注: アーキテクチャ図では簡単のために CodeBuild のアイコンを VPC 内に配置していますが、実際には CodeBuild が管理するコンテナ上でビルドが行われ、Elastic Network Interface を経由して VPC に接続します。)

リクエストが各種 AWS サービスに接続するために、次の VPC エンドポイントを使用します。

  • CloudWatch Logs エンドポイント (CodeBuild が CloudWatch にログを送信するため)
  • CodeArtifact エンドポイント
  • AWS Security Token Service (AWS STS) エンドポイント
  • Amazon Simple Storage Service (Amazon S3) エンドポイント

手順

  1. CodeCommit のリポジトリを作成します。
    1. CodeCommit コンソールを開き、「リポジトリを作成」をクリックします。

      Figure 2. スクリーンショット: リポジトリ作成ボタン

    2. リポジトリ名を入力し、「作成」をクリックします。

      Figure 3. スクリーンショット: リポジトリの設定画面。リポジトリ名を「Private」、説明を空欄のままとしている

    3. 下にスクロールし、「ファイルの作成」をクリックします。

      Figure 4. スクリーンショット: ファイル作成ボタン

    4. buildspec.yml のサンプル(下記)をコピーし、コンソール上のエディタに貼り付ける

      buildspec.yml ファイルのサンプル:

      
      version: 0.2
      phases:
        install:
          runtime-versions:
            nodejs: 16
          
          commands:
            - export AWS_STS_REGIONAL_ENDPOINTS=regional
            - ACCT=`aws sts get-caller-identity --region ${AWS_REGION} --query Account --output text`
            - aws codeartifact login --tool npm --repository Private --domain private --domain-owner ${ACCT}
            - npm install
        build:
          commands:
            - node index.js
      				
    5. ファイル名に buildspec.yml を指定し、作成者名と E メールアドレスを入力したら、「変更のコミット」をクリックします。

      Figure 5. スクリーンショット: 「ファイルの作成」画面

  2. CodeArtifact リポジトリを作成します。
    1. CodeArtifact コンソールを開き、「リポジトリを作成」をクリックします。
    2. リポジトリ名をつけ、パブリックアップストリームリポジトリとして「npm-store」を選択します。

      Figure 6. スクリーンショット: 「リポジトリを作成」画面。リポジトリ名を「Private」としている

    3. 「ドメインを選択」で「この AWS アカウント」を選択し、ドメイン名を入力します。

      Figure 7. スクリーンショット: 「ドメインを選択」画面

    4. 「次へ」、「リポジトリを作成」の順にクリックします。

      Figure 8. スクリーンショット: リポジトリの「確認および作成」画面

  3. CodePipeline を使用して CI/CD パイプラインを作成します。
    1. CodePipeline コンソールを開き、「パイプラインを作成」をクリックします。

      Figure 9. スクリーンショット: パイプライン作成ボタン

    2. 名前を入力し、サービスロールを「新しいサービスロール」として「次に」をクリックします。

      Figure 10. スクリーンショット: 「パイプラインの設定を選択する」画面。パイプライン名を「Private」としている

    3. ソースプロバイダーとして「AWS CodeCommmit」を選択します。
    4. 先ほど作成した CodeCommmit リポジトリを選択し、ブランチ名に「main」を指定したら「次に」をクリックします。

      Figure 11. スクリーンショット: 「ソースステージを追加する」画面

    5. ビルドステージの設定では、AWS CodeBuild をプロバイダーとして指定し、「プロジェクトを作成する」をクリックします。

      Figure 12. スクリーンショット: 「ビルドステージを追加する」画面

    6. 新たなウィンドウが開くので、プロジェクトの作成を行います。プロジェクト名をつけます。

      Figure 13. スクリーンショット: 「ビルドプロジェクトを作成する」画面の「プロジェクトの設定」セクション

    7. 「環境」セクションまで下にスクロールし、環境イメージに「マネージド型イメージ」を選択します。
    8. オペレーティングシステムに「Amazon Linux 2」を選択します。
    9. ランタイムに「Standard」を選択します。
    10. イメージに「aws/codebuild/amazonlinux2-x86+64-standard:4.0」を選択し、イメージのバージョンに「このランタイムバージョンには常に最新のイメージを使用してください」を選択します。
    11. 環境タイプに「Linux」を選択します。
    12. 特権付与オプションはチェックボックスを空にし、サービスロールに「新しいサービスロール」を選択します。

      Figure 14. スクリーンショット: 「ビルドプロジェクトを作成する」画面の「環境」セクション

    13. 「追加設定」を展開して VPC セクションまで下にスクロールし、希望する VPC とサブネット、セキュリティグループを選択します。可用性を高めるため、サブネットには複数のアベイラビリティーゾーンを選択することを推奨します。セキュリティグループのルールは、VPC エンドポイントを使用して AWS サービスとの通信を行うリソースが、エンドポイントのネットワークインターフェースと通信できるように設定する必要があります。ここでは例として、VPC のデフォルトセキュリティグループを使用します。

      Figure 15. スクリーンショット: 「ビルドプロジェクトを作成する」画面のネットワーキング設定部分

    14. 「Buildspec」セクションまで下にスクロールし、「buildspec ファイルを使用する」を選択、Buildspec 名に「buildspec.yml」を指定します。

      Figure 16. スクリーンショット: 「ビルドプロジェクトを作成する」画面の「Buildspec」セクション

    15. CloudWatch Logs のチェックボックスを選択します。グループ名とストリーム名はサービスのデフォルト設定が使用されるため、空欄にしておいて構いません。 「CodePipelineに進む」をクリックします。

      Figure 17. スクリーンショット: 「ビルドプロジェクトを作成する」画面の「ログ」セクション

    16. 新たな CodeBuild プロジェクトが作成され、CodePipeline の設定画面が更新されたら「次に」をクリックします。

      Figure 18. スクリーンショット: 「ビルドステージを追加する」画面

    17. 今回はデプロイを行わないため、「導入段階をスキップ」をクリックしてデプロイステージの設定をスキップします。

      Figure 19. スクリーンショット: 「デプロイステージを追加する」画面


      Figure 20. スクリーンショット: デプロイステージを省略する確認画面

    18. ポップアップが表示されたら「スキップ」をクリックします。「レビュー」画面が表示されるので、ページの最下部までスクロールして「パイプラインを作成」をクリックします。
  4. Amazon CloudWatch Logs の VPC エンドポイントを作成します。これにより、CodeBuild が実行ログを CloudWatch に送信することが可能になります。
    1. VPC コンソールを開き、画面左のナビゲーションメニューから「エンドポイント」を選択します。

      Figure 21. スクリーンショット: エンドポイントメニュー

    2. 「エンドポイントを作成」をクリックします。

      Figure 22. スクリーンショット: エンドポイント作成ボタン

    3. サービスカテゴリに「AWS のサービス」を選択します。新たなエンドポイントの名前をわかりやすく設定します。

      Figure 23. スクリーンショット: 「エンドポイントを作成」画面の「エンドポイントの設定」セクション

    4. サービスのリストの検索窓に「logs」と入力して検索し、「com.amazonaws.us-west-2.logs」を選択します。
      この記事の内容は使用するサービスをサポートするどのリージョンでも行うことができます。ここでは us-west-2 を使用していますが、ご自身のワークロードに適したリージョンを選択してください。

      Figure 24. スクリーンショット: 「エンドポイントを作成」画面の「サービス」セクションで「com.amazonaws.us-west-2.logs」を選択

    5. エンドポイントを関連づけたい VPC を選択します。「追加設定」内の DNS 名を有効化のチェックボックスにチェックがついていることを確認してください。

      Figure 25. スクリーンショット: 「エンドポイントを作成」画面の「VPC」セクションで VPC を選択

    6. エンドポイントを関連づけたいサブネットを選択します。セキュリティグループは default、ポリシーは空欄とします。

      Figure 26. スクリーンショット: 「エンドポイントを作成」画面の「サブネット」セクションで 2 つのサブネットを選択、「セキュリティグループ」セクションでデフォルトセキュリティグループを選択

    7. 「エンドポイントを作成」をクリックします。

      Figure 27. スクリーンショット: エンドポイント作成ボタン

  5. CodeArtifact の VPC エンドポイントを作成します。記事執筆時点では、CodeArtifact には 2 種類のエンドポイントがあります。1 つはサービスレベルの操作や認証などの API オペレーションのためのもので、もう 1 つはコードのモジュールを取得するなどサービスを利用するためのものです。CodeArtifact との連携を自動化するためには両方のエンドポイントが必要になります。したがって、両方のエンドポイントを DNS を有効にした状態で作成します。

    加えて、get-caller-identity API を呼び出す必要があるため、AWS Security Token Service (AWS STS) エンドポイントも必要になります。

    まず、先ほど CloudWatch Logs のエンドポイントを作成した際の a-c の手順に従ってください。

    1. サービスのリストの検索窓に「codeartifact」と入力して検索し、「com.amazonaws.us-west-2.codeartifact.api」を選択します。

      Figure 28. スクリーンショット: 「エンドポイントを作成」画面の「サービス」セクションで「com.amazonaws.us-west-2.codeartifact.api」を選択

      次に、4. e-g の手順に従ってください。

      完了したら、同じ手順を「com.amazonaws.us-west-2.codeartifact.repositories」に対しても行います。

      Figure 29. スクリーンショット: 「エンドポイントを作成」画面の「サービス」セクションで「com.amazonaws.us-west-2.codeartifact.repositories」を選択

  6. AWS STS の VPC エンドポイントを有効化します

    まず、4. a-c の手順に従ってください。

    1. サービスのリストの検索窓に「sts」と入力して検索し、「com.amazonaws.us-west-2.sts」を選択します

      Figure 30. スクリーンショット: 「エンドポイントを作成」画面の「サービス」セクションで「com.amazonaws.us-west-2.sts」を選択

    次に、4. e-g の手順に従ってください。

  7. S3 の VPC エンドポイントを作成します。

    まず、4. a-c の手順に従ってください。

    1. サービスのリストの検索窓に「s3」と入力して検索し、「com.amazonaws.us-west-2.s3」を選択します。Gateway タイプのものを選択してください。

      次に、VPC を選択し、サブネットに合ったルートテーブルを選択してください。この操作によって、ルートテーブルが自動で更新され、新しい S3 エンドポイントが追加されます。

      Figure 31. スクリーンショット: 「エンドポイントを作成」画面の「サービス」セクションで「com.amazonaws.us-west-2.s3」を選択

  8. これで全てのエンドポイントを作成することができました。最後に、コードの依存関係を解決する際にパイプラインが CodeArtifact を使用するように、パイプラインを更新しましょう。ここでは、例として CodeBuild の buildspec.yml を使用します。

    CodeBuild の AWS Identity and Access Management (IAM)ロールに、STS と CodeArtifact を操作する権限を追加します。

    IAM コンソールを開き、左のナビゲーションメニューから「ロール」をクリックします。次に、IAM ロール名で検索します。今回は、ステップ 2.k で「新しいサービスロール」を選択したので、「codebuild-Private-service-role」という名前で作成されています。(codebuild-<BUILD PROJECT NAME>-service-role)

    Figure 32. スクリーンショット: IAM ロール一覧の検索で codebuild-Private-service-role ロールが表示されている様子

    「許可を追加」メニューから「インラインポリシーを作成」をクリックします。

    Figure 33. インラインポリシー作成ボタン

    「サービスの選択」で「STS」と検索し、STS を選択します。

    Figure 34. スクリーンショット: IAM ビジュアルエディタで「STS」と検索している様子

    「GetCallerIdentity」と検索し、アクションを選択します。

    Figure 35. スクリーンショット: IAM ビジュアルエディタで「GetCallerIdentity」と検索しアクションを選択している様子

    同じ操作を「GetServiceBearerToken」についても行います。

    Figure 36. スクリーンショット: IAM ビジュアルエディタで「GetServiceBearerToken」と検索しアクションを選択している様子

    「ポリシーの確認」をクリックし、名前を入力して「ポリシーの作成」をクリックします。

    Figure 37. スクリーンショット: 「ポリシーの確認」画面と「ポリシーの作成」ボタン

    リストに新しいインラインポリシーが追加されたのが確認できるはずです。

    Figure 38. スクリーンショット: リストに新しいインラインポリシーが追加されている様子

    CodeArtifact のアクションも同様にロールに追加します。「インラインポリシーを作成」をクリックします。

    Figure 39. スクリーンショット: インラインポリシー作成ボタン

    「サービスの選択」で「CodeArtifact」と検索し、CodeArtifact を選択します。

    Figure 40. スクリーンショット: CodeArtifact と検索して選択している様子

    「GetAuthorizationToken」と検索し、アクションを選択します。

    Figure 41. スクリーンショット: 「GetAuthorizationToken」と検索しアクションを選択している様子

    同様の操作を「GetRepositoryEndpoint」「ReadFromRepository」についても行う

    2 つの警告を修正するため、「リソース」をクリックします。まず、1 つ目の「domain リソース ARN を指定します。 GetAuthorizationToken アクション」について、「ARN の追加」をクリックします。

    Figure 42. スクリーンショット: すべて選択して 2 つの警告が出ている様子

    Region, Account, Domain name の入力欄がポップアップが表示されるので、リージョンとアカウント ID とドメイン名を入力します。先ほどドメインを作成した際には、「private」を使用しました。

    Figure 43. スクリーンショット: 「ARN の追加」画面

    入力したら、「追加」をクリックします。

    同じことを「repository リソース ARN を指定します。 ReadFromRepository および、さらに 1 つのアクション。」についても行います。今回は、リージョン、アカウント ID、ドメイン名とリポジトリ名を入力します。先ほど各リソースを作成した際には、リポジトリ名に「Private」、ドメイン名に「private」を使用しました。

    Figure 44. スクリーンショット: 「ARN の追加」画面

    「すべて」のチェックボックスを使用することもできますが、IAM ロールのスコープをなるべく狭めたいので、対象となるリソースを指定することがベストプラクティスであることに注意してください。

  9. CodeCommit コンソールを開き、ステップ 1 で作成したリポジトリをクリックします。

    Figure 45. スクリーンショット: CodeCommit のリポジトリ

    「ファイルの追加」ドロップダウンをクリックし、「ファイルの作成」ボタンをクリックします。

    エディタスペースに以下をペーストします。

    
    {
      "dependencies": {
        "mathjs": "^11.2.0"
      }
    }
    		

    「package.json」というファイル名をつけて、作成者名、E メールアドレス、任意でコミットメッセージを入力します。

    「index.js」にも同様の作業を行います。エディタスペースには以下をペーストします。

    
    const { sqrt } = require('mathjs')
    console.log(sqrt(49).toString())
    		

    Figure 46. スクリーンショット: CodeCommit コンソールの「変更のコミット」ボタン

    この操作でパイプラインが起動してアプリケーションのビルドが開始されます。

    Figure 47. スクリーンショット: CodePipeline

このアプリケーションは、49 の平方根を求めて画面に出力するという非常にシンプルなアプリケーションです。パイプラインの Build ステージから「詳細」リンクをクリックすると、NodeJS アプリケーションを実行した結果の出力が確認できます。ログは CloudWatch に格納されており、「ビルドログの最後の xx 行を表示しています。ログ全体の表示」リンクをクリックすることで CloudWatch コンソールを開くことができます。

Figure 48. スクリーンショット: ビルドログの最後の 54 行を表示しています。ログ全体の表示

今回の buildspec.yml では npm を例にしました。pip や twine では同じようなセットアップを使用することができます。
Maven、Gradle、NuGet においては、環境変数の設定、settings.xml や build.gradle の変更、IDE へのプラグインのインストールなどが必要になります。詳しくはこちらをご覧ください。

クリーンアップ

AWS コンソールから VPC エンドポイントを表示して、作成したエンドポイントを削除します。

CodePipeline コンソールを表示して、作成したパイプラインを削除します。

CodeBuild コンソールを表示して、作成したビルドプロジェクトを削除します。

CodeCommit コンソールを表示して、作成したリポジトリを削除します。

CodeArtifact コンソールを表示して、作成したリポジトリとドメインを削除します。

IAM コンソールを表示して、作成したロールを削除します:

CodeBuild 用ロール: codebuild-<Build Project Name>-service-role

CodePipeline 用ロール: AWSCodePipelineServiceRole-<Region>-<Project Name>

まとめ

この記事では、CodeBuild を使用した小さな NodeJS アプリケーションのビルドとテスト、CodeArtifact を使用したアプリケーションコードの依存関係のダウンロードを CodePipeline を使用して統合し、完全な CI/CD パイプラインをデプロイしました。パブリックなインターネットに出ず、CloudWatch にログを格納することを実現しています。

この記事は、Setting up a secure CI/CD pipeline in a private Amazon Virtual Private Cloud with no public internet access を翻訳したものです。翻訳は Solutions Architect の三尾 泰士が担当しました。