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)といった一般に使用されるパッケージマネージャーやビルドツールに対応しています。
上記の図は、リクエストが 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) エンドポイント
手順
- CodeCommit のリポジトリを作成します。
- CodeCommit コンソールを開き、「リポジトリを作成」をクリックします。
- リポジトリ名を入力し、「作成」をクリックします。
- 下にスクロールし、「ファイルの作成」をクリックします。
- 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
- ファイル名に buildspec.yml を指定し、作成者名と E メールアドレスを入力したら、「変更のコミット」をクリックします。
- CodeCommit コンソールを開き、「リポジトリを作成」をクリックします。
- CodeArtifact リポジトリを作成します。
- CodeArtifact コンソールを開き、「リポジトリを作成」をクリックします。
- リポジトリ名をつけ、パブリックアップストリームリポジトリとして「npm-store」を選択します。
- 「ドメインを選択」で「この AWS アカウント」を選択し、ドメイン名を入力します。
- 「次へ」、「リポジトリを作成」の順にクリックします。
- CodePipeline を使用して CI/CD パイプラインを作成します。
- CodePipeline コンソールを開き、「パイプラインを作成」をクリックします。
- 名前を入力し、サービスロールを「新しいサービスロール」として「次に」をクリックします。
- ソースプロバイダーとして「AWS CodeCommmit」を選択します。
- 先ほど作成した CodeCommmit リポジトリを選択し、ブランチ名に「main」を指定したら「次に」をクリックします。
- ビルドステージの設定では、AWS CodeBuild をプロバイダーとして指定し、「プロジェクトを作成する」をクリックします。
- 新たなウィンドウが開くので、プロジェクトの作成を行います。プロジェクト名をつけます。
- 「環境」セクションまで下にスクロールし、環境イメージに「マネージド型イメージ」を選択します。
- オペレーティングシステムに「Amazon Linux 2」を選択します。
- ランタイムに「Standard」を選択します。
- イメージに「aws/codebuild/amazonlinux2-x86+64-standard:4.0」を選択し、イメージのバージョンに「このランタイムバージョンには常に最新のイメージを使用してください」を選択します。
- 環境タイプに「Linux」を選択します。
- 特権付与オプションはチェックボックスを空にし、サービスロールに「新しいサービスロール」を選択します。
- 「追加設定」を展開して VPC セクションまで下にスクロールし、希望する VPC とサブネット、セキュリティグループを選択します。可用性を高めるため、サブネットには複数のアベイラビリティーゾーンを選択することを推奨します。セキュリティグループのルールは、VPC エンドポイントを使用して AWS サービスとの通信を行うリソースが、エンドポイントのネットワークインターフェースと通信できるように設定する必要があります。ここでは例として、VPC のデフォルトセキュリティグループを使用します。
- 「Buildspec」セクションまで下にスクロールし、「buildspec ファイルを使用する」を選択、Buildspec 名に「buildspec.yml」を指定します。
- CloudWatch Logs のチェックボックスを選択します。グループ名とストリーム名はサービスのデフォルト設定が使用されるため、空欄にしておいて構いません。 「CodePipelineに進む」をクリックします。
- 新たな CodeBuild プロジェクトが作成され、CodePipeline の設定画面が更新されたら「次に」をクリックします。
- 今回はデプロイを行わないため、「導入段階をスキップ」をクリックしてデプロイステージの設定をスキップします。
- ポップアップが表示されたら「スキップ」をクリックします。「レビュー」画面が表示されるので、ページの最下部までスクロールして「パイプラインを作成」をクリックします。
- CodePipeline コンソールを開き、「パイプラインを作成」をクリックします。
- Amazon CloudWatch Logs の VPC エンドポイントを作成します。これにより、CodeBuild が実行ログを CloudWatch に送信することが可能になります。
- VPC コンソールを開き、画面左のナビゲーションメニューから「エンドポイント」を選択します。
- 「エンドポイントを作成」をクリックします。
- サービスカテゴリに「AWS のサービス」を選択します。新たなエンドポイントの名前をわかりやすく設定します。
- サービスのリストの検索窓に「logs」と入力して検索し、「com.amazonaws.us-west-2.logs」を選択します。
この記事の内容は使用するサービスをサポートするどのリージョンでも行うことができます。ここでは us-west-2 を使用していますが、ご自身のワークロードに適したリージョンを選択してください。
- エンドポイントを関連づけたい VPC を選択します。「追加設定」内の DNS 名を有効化のチェックボックスにチェックがついていることを確認してください。
- エンドポイントを関連づけたいサブネットを選択します。セキュリティグループは default、ポリシーは空欄とします。
- 「エンドポイントを作成」をクリックします。
- VPC コンソールを開き、画面左のナビゲーションメニューから「エンドポイント」を選択します。
- CodeArtifact の VPC エンドポイントを作成します。記事執筆時点では、CodeArtifact には 2 種類のエンドポイントがあります。1 つはサービスレベルの操作や認証などの API オペレーションのためのもので、もう 1 つはコードのモジュールを取得するなどサービスを利用するためのものです。CodeArtifact との連携を自動化するためには両方のエンドポイントが必要になります。したがって、両方のエンドポイントを DNS を有効にした状態で作成します。
加えて、get-caller-identity API を呼び出す必要があるため、AWS Security Token Service (AWS STS) エンドポイントも必要になります。
まず、先ほど CloudWatch Logs のエンドポイントを作成した際の a-c の手順に従ってください。
- サービスのリストの検索窓に「codeartifact」と入力して検索し、「com.amazonaws.us-west-2.codeartifact.api」を選択します。
次に、4. e-g の手順に従ってください。
完了したら、同じ手順を「com.amazonaws.us-west-2.codeartifact.repositories」に対しても行います。
- サービスのリストの検索窓に「codeartifact」と入力して検索し、「com.amazonaws.us-west-2.codeartifact.api」を選択します。
- AWS STS の VPC エンドポイントを有効化します
まず、4. a-c の手順に従ってください。
- サービスのリストの検索窓に「sts」と入力して検索し、「com.amazonaws.us-west-2.sts」を選択します
次に、4. e-g の手順に従ってください。
- サービスのリストの検索窓に「sts」と入力して検索し、「com.amazonaws.us-west-2.sts」を選択します
- S3 の VPC エンドポイントを作成します。
まず、4. a-c の手順に従ってください。
- サービスのリストの検索窓に「s3」と入力して検索し、「com.amazonaws.us-west-2.s3」を選択します。Gateway タイプのものを選択してください。
次に、VPC を選択し、サブネットに合ったルートテーブルを選択してください。この操作によって、ルートテーブルが自動で更新され、新しい S3 エンドポイントが追加されます。
- サービスのリストの検索窓に「s3」と入力して検索し、「com.amazonaws.us-west-2.s3」を選択します。Gateway タイプのものを選択してください。
- これで全てのエンドポイントを作成することができました。最後に、コードの依存関係を解決する際にパイプラインが 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)
「許可を追加」メニューから「インラインポリシーを作成」をクリックします。
「サービスの選択」で「STS」と検索し、STS を選択します。
「GetCallerIdentity」と検索し、アクションを選択します。
同じ操作を「GetServiceBearerToken」についても行います。
「ポリシーの確認」をクリックし、名前を入力して「ポリシーの作成」をクリックします。
リストに新しいインラインポリシーが追加されたのが確認できるはずです。
CodeArtifact のアクションも同様にロールに追加します。「インラインポリシーを作成」をクリックします。
「サービスの選択」で「CodeArtifact」と検索し、CodeArtifact を選択します。
「GetAuthorizationToken」と検索し、アクションを選択します。
同様の操作を「GetRepositoryEndpoint」「ReadFromRepository」についても行う
2 つの警告を修正するため、「リソース」をクリックします。まず、1 つ目の「domain リソース ARN を指定します。 GetAuthorizationToken アクション」について、「ARN の追加」をクリックします。
Region, Account, Domain name の入力欄がポップアップが表示されるので、リージョンとアカウント ID とドメイン名を入力します。先ほどドメインを作成した際には、「private」を使用しました。
入力したら、「追加」をクリックします。
同じことを「repository リソース ARN を指定します。 ReadFromRepository および、さらに 1 つのアクション。」についても行います。今回は、リージョン、アカウント ID、ドメイン名とリポジトリ名を入力します。先ほど各リソースを作成した際には、リポジトリ名に「Private」、ドメイン名に「private」を使用しました。
「すべて」のチェックボックスを使用することもできますが、IAM ロールのスコープをなるべく狭めたいので、対象となるリソースを指定することがベストプラクティスであることに注意してください。
- CodeCommit コンソールを開き、ステップ 1 で作成したリポジトリをクリックします。
「ファイルの追加」ドロップダウンをクリックし、「ファイルの作成」ボタンをクリックします。
エディタスペースに以下をペーストします。
{ "dependencies": { "mathjs": "^11.2.0" } }
「package.json」というファイル名をつけて、作成者名、E メールアドレス、任意でコミットメッセージを入力します。
「index.js」にも同様の作業を行います。エディタスペースには以下をペーストします。
const { sqrt } = require('mathjs') console.log(sqrt(49).toString())
この操作でパイプラインが起動してアプリケーションのビルドが開始されます。
このアプリケーションは、49 の平方根を求めて画面に出力するという非常にシンプルなアプリケーションです。パイプラインの Build ステージから「詳細」リンクをクリックすると、NodeJS アプリケーションを実行した結果の出力が確認できます。ログは CloudWatch に格納されており、「ビルドログの最後の xx 行を表示しています。ログ全体の表示」リンクをクリックすることで CloudWatch コンソールを開くことができます。
今回の 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 の三尾 泰士が担当しました。