クラウドの継続的インテグレーション & 継続的デリバリー (CI/CD) は、開発チームの戦力を大きく高めるツールであり、特にリモートで働いているチームには効果てきめんです。CI/CD を自動化すれば、開発者の負担は軽くなり、より良いプロダクトの開発に力を集中できます。SaaS 型の CI/CD にはさらにメリットがあります。CI/CD によって新たに得られたキャパシティをテストやデプロイ用のインフラの管理に費やす必要がなく、しかもリモートチームが簡単に CI/CD ツールを利用できるのです。
こうした点は、プラットフォームエンジニアにとってありがたいことです。しかし、極めて重要な開発やデプロイのタスクをサードパーティに任せるにあたり、大きな懸念事項が 1 つあります。認証情報の管理です。認証情報とはパスワード、API キー、トークンなど、アプリケーションがデータベースやサードパーティのサービスに接続するための機密情報であり、シークレットとも呼ばれます。そのようなシークレットが、予想外の流出、セキュリティ侵害、単純な推測、総当たり攻撃などで意図せず漏れてしまうと、さらなるデータ侵害やネットワーク侵入、インフラストラクチャリソースの悪用につながるおそれがあります。
このチュートリアルでは、プラットフォームエンジニア向けに、静的認証情報や長期的な認証情報を管理するためのベストプラクティスの実装方法を解説します。CircleCI コンテキストの使用方法、制限付きコンテキスト内でのワークフローの実行方法、CircleCI CLI または API を使って環境変数として保存されているシークレットをローテーションする方法を順に説明します。リスク対策を強化したいとお考えの場合は、一時的な OpenID Connect (OIDC) 認証トークンをセットアップおよび管理する方法もあわせてご覧ください。
認証情報管理がプラットフォームチームにとって重要な理由
規模の大きな組織では、プラットフォームエンジニアは複数の開発チームのサポートを担当しており、各チームのプロジェクトはクラウド、デスクトップ、モバイルアプリのデプロイと多岐にわたります。
プラットフォームチームの仕事は、開発チームが最大効率で仕事ができるように、開発プロセスを簡素化、標準化、合理化することです。そのために、複雑なインフラ管理作業を自動化する、テスト用とデプロイ用に信頼性の高いセルフサービスワークフローを作成する、ビルド/テスト/デプロイ用のツールチェーンを管理するなどの作業を行います。こうした作業には、強力な CI/CD パイプラインが不可欠です。しかし、パイプラインを他のツールとシームレスに連携させるには、アクセス認証情報を不要なリスクにさらさないように、プログラムでそれらの情報を管理し、ビルドパイプラインに挿入する必要があります。
組織のセキュリティを確保するには、認証情報を安全に保存したうえで、定期的にローテーションしなくてはなりません。CircleCI には、CI/CD パイプラインを外部サービスと安全に接続するのに役立つ機能がいくつも用意されています。たとえば、OIDC と権限委譲/Web ID を組み合わせたり、OIDC で静的認証情報を一元的なシークレット管理システムから取得したり、CircleCI のコンテキストに静的認証情報を保存したりすることが可能です。
オンラインサービスへの接続を保護する方法としては、OIDC と権限委譲または Web ID を組み合わせる方法が現在の業界標準です。長期間有効なシークレットを使わず、リモートシステムに関係要素間の信頼関係の確立を任せます。OIDC はまだ普及の初期段階にあり、現時点では対応しているサービスは多くありません。とはいえ、静的認証情報を OIDC でアクセスするシークレット管理サービスに保存すれば、認証情報の安全を確保しながら、管理を一元化しローテーションも効率化できます。前述の方法がいずれも現実的でない場合は、CI/CD パイプライン用のシークレットを CircleCI コンテキストを使って保存し、分離することをおすすめします。
CircleCI で静的認証情報をローテーションするための前提条件
以降は、CircleCI コンテキストに保存されている静的認証情報をローテーションする方法を実践形式で紹介します。よりセキュアで簡単なシークレット管理プロセスを CI/CD パイプラインで実現するための参考としてください。手順の説明では、以下の準備を完了していることを前提としています。
- CircleCI 組織と管理者権限を持つユーザー
- CircleCI 組織の 1 つ以上のプロジェクト
GitHub または Bitbucket に登録済みで、CircleCI の無料プランにも登録している場合は、Git リポジトリを CircleCI プロジェクトとしてインポートするだけで、自分を組織の管理者に設定できます。
他のソースからプロジェクトを追加するには、CircleCI のユーザー設定にある [Account Integrations (アカウント連携)] にアクセスします。組織でリポジトリのホスト用に別のアカウントを使用している場合は、他のユーザーにプロジェクトへのアクセス権の付与が必要になることがあります。
CircleCI でプロジェクトのシークレットを管理するためのコンテキストをセットアップする
CircleCI コンテキストを使うと、環境変数を保護したまま、プロジェクト間で共有できます。CircleCI で複数のプロジェクトにまたがって静的認証情報を扱う場合、この方法が推奨されます。プロジェクトごとにシークレットを個別に管理することなく、一元的に管理してローテーションできるからです。以下では、コンテキストを作成してプロジェクト内で使用する手順と、CircleCI CLI を使ってコンテキスト内のシークレットをプログラムによりローテーションする方法を説明します。
みなさんが試せるように、GitHub リポジトリを用意してありますので、フォークして CircleCI にインポートしてください。これはシンプルな Fastify アプリであり、実行にはユーザー名とパスワードを使った HTTP Basic 認証が必要です。また、HTTP 200 ステータスコードが返されるかどうかを確認する Node.js TAP テストを 1 つ用意しています。HTTP Basic 認証用のユーザー名とパスワードは、.env ファイル (https://www.npmjs.com/package/dotenv) で指定します。
このリポジトリには、CircleCI のパイプライン設定ファイルが含まれています。パイプラインでは、指定したコンテキストからシークレットを取得して .env
ファイルに書き込んでから、用意されているテストを実行してすべてが問題なく機能するか確認します。
サンプルプロジェクトをクローンするか、独自のプロジェクトをインポートしたら、コンテキストを作成し、作成したコンテキスト名にあわせて CircleCI パイプラインの config.yml で指定するコンテキスト名のリストを更新します。作成したコンテキストをクリックして編集します。コンテキスト内の環境変数にアクセスする必要のあるプロジェクトだけが環境変数にアクセスできるように、プロジェクトの制限を追加することをおすすめします。次に、2 つの環境変数 (HTTP_USERNAME
と HTTP_PASSWORD
) を追加して、値を適宜設定します。
CircleCI は、プロジェクトで利用可能なコンテキストから環境変数の値を取得します。コンテキストのプロジェクト制限がどのように動作するかを確認してみましょう。コンテキスト設定に戻り、プロジェクトのアクセス許可を無効にして、パイプラインを再実行してみると、パイプラインは失敗します。パイプラインが機能するには、現在アクセス不可能になっているコンテキスト変数が必要だからです。
CircleCI コンテキストに保存されているシークレットを自動的にローテーションする方法
認証情報の管理にどのような戦略を用いる場合でも、シークレットの定期的なローテーションは欠かせません。定期的なローテーションは、予想外の漏えいに対する基本的な防御策になります。気づかない内に API キーや認証情報ペアが流出しても、流出した情報が使えるのは限られた期間だけになるからです。もちろん、理想は、悪用前にシークレットを自動的にローテーションすることです。さらに、シークレットを即座にローテーションできるツールを導入しておくと、サプライチェーンインシデントなどのセキュリティ侵害が発生したときでも迅速に対応できます。
CircleCI コンテキストの管理には、CircleCI コマンドラインインターフェース (CLI) を使用します。この CLI では、コマンドラインとプログラムによりコンテキスト (環境変数を含む) を管理できます。
参考として、サンプルの Bash スクリプトを以下に示します。このスクリプトは、テキストファイルから 2 つのシークレットを受け取り、CircleCI コンテキストに環境変数として保存します。このスクリプトを使うには、CircleCI で CLI を認証する必要があります。認証が完了したら、管理したいコンテキストのパスが必要になります。このパスは、コンテキストの一覧表示コマンドで確認できます。
circleci context list
必要な情報がわかったら、以下のスクリプトのうち、シークレットの部分を更新してください。
#!/bin/bash
# Secrets will be read from a CSV file
# containing a comma-separated username/password
SECRETS_FILE="./secrets.csv"
# Specify the path to the context that will be updated
VCS_TYPE="github"
ORGANIZATION_NAME="your_org_name"
CONTEXT_NAME="example_context"
# Get the username/password values by splitting
# the contents of the secrets file at the comma (,)
USERNAME="$(cat $SECRETS_FILE | cut -d',' -f1)"
PASSWORD="$(cat $SECRETS_FILE | cut -d',' -f2)"
# Update the environment variable in the CircleCI context
# Existing environment variables will be overwritten
echo "$USERNAME" | circleci context store-secret $VCS_TYPE $ORGANIZATION_NAME $CONTEXT_NAME "USERNAME"
echo "$PASSWORD" | circleci context store-secret $VCS_TYPE $ORGANIZATION_NAME $CONTEXT_NAME "PASSWORD"
このスクリプトは、ローカルマシンで cron により自動化できるほか、 CircleCI プロジェクトに追加してパイプラインのスケジュール実行で呼び出すことも可能です。また、オンデマンドでトリガーすれば、現在進行中のセキュリティ上の脅威にすばやく対応できます。実行環境が異なる場合でも、CircleCI API を使用すれば同じタスクを簡単に実行可能です。
CircleCI パイプラインを使ったローテーションのスケジュール実行とオンデマンド実行
CircleCI パイプラインを使ってシークレットのローテーションを自動化するには、その設定ファイル用に個別のリポジトリを作成して、他のプロジェクトに関連する他のテストタスクやビルドタスクを実行することなく呼び出せるようにする必要があります。以下のサンプル CircleCI 設定ファイルでは、パイプライン実行時に上記のスクリプトを呼び出しています。
workflows:
rotate-secrets:
jobs:
- rotate-secrets-script:
# Set the contexts this job will run in - this must match the name of the context in your organization Settings
context:
- example_context
jobs:
rotate-secrets-script:
# Run in a basic Ubuntu environment https://circleci.com/developer/images/image/cimg/base
docker:
- image: cimg/base:2023.04
steps:
# Install the latest version of the CircleCI CLI https://github.com/CircleCI-Public/circleci-cli
- run:
command: curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | sudo bash
name: Install CircleCI CLI
- run:
command: circleci setup --no-prompt --token=$CIRCLECI_API_TOKEN
name: Configure CircleCI CLI
# Get the code from the repository
- checkout
# Run script
- run:
command: sh rotate.sh
name: Run secrets rotation script
このパイプラインをスケジュールに従って自動的に実行するには、CircleCI Web アプリでプロジェクトにスケジュール実行のトリガーを追加します。
ビジネスのニーズに応じた頻度 (セキュリティニーズにもよりますが最多で 1 時間に複数回) でシークレットをローテーションするように、パイプラインをスケジュールできます。
セキュリティインシデントが発生した際は、オンデマンドでシークレットローテーション用のパイプラインを呼び出すこともできます。CircleCI ダッシュボードでプロジェクトに移動し、ブランチを選択して、[Trigger Pipeline (パイプラインのトリガー)] ボタンをクリックします。
パイプラインをトリガーした結果は下図から確認できます。更新対象のコンテキストのシークレットについて、[Last Updated (最終更新日時)] の値が変化していれば成功です。
通常、API キーと認証情報は、アクセスにそれらの情報が必要となるサービスによって生成されます。たとえば、AWS IAM (ID とアクセス管理) の認証情報は、 .csv ファイルまたは CLI/API により生成された出力として提供されます。これらの認証情報を VCS にコミットしないようにしてください。上記のスクリプトは、参考として示しただけです。認証情報を独自のセキュアなストレージから取得する場合 (たとえば、SFTP を使ったり、セルフホストランナーによりローカルリソースにアクセスするマシンでパイプラインを実行したりする場合) は、スクリプトを更新する必要があります。こうした要件がある場合、キーのローテーションは実現不能になる可能性があります。その場合、OIDC を実装し、OIDC を使って HashiCorp Vault (英語) などの専用のシークレット管理ソリューションにアクセスすることで対処できます。
プラットフォームエンジニアリングで認証情報管理を改善すれば、セキュリティと生産性を高められる
自動 CI/CD は、リーンかつアジャイルな開発体制での迅速なイテレーションとイノベーションを実現するツールであり、現代では不可欠な存在です。CircleCI は、インフラ管理の手間を増やすことなく開発者を負担から開放する強力なクラウドホスト型 CI/CD パイプラインを提供しています。
ただし、こうしたツールを導入するには、利用する CI/CD プラットフォームや他のサードパーティサービスにある程度の信頼を置く必要があります。CircleCI は、お客様からの信頼に足る存在となれるよう、セキュリティ手法とツールに関する完全な透明性を確保し、ユーザーのみなさまが外部システムへのアクセス権をパイプラインにセキュアに付与できるように力を尽くしています。
この記事では、CircleCI の API と CLI ツールを使用して、CircleCI コンテキストに保存された有効期間の長い静的認証情報を手軽に管理、ローテーションする方法について説明しました。CircleCI では、長期認証情報よりもセキュアな一時的シークレットである OIDC ロールベースのアクセスもサポートしています。こちらの OIDC を使ったロールベースの認証情報管理に関するチュートリアル(英語)では、HashiCorp Vault を使った認証情報管理のセットアップ方法を説明しています。シンプルなシークレットローテーション方法から、一元的なシークレット管理システムで静的な認証情報を保存する方法まで順を追って紹介していますので、ぜひご覧ください。