こんにちは!クラウドインフラ・SREの世界へようこそ。
日々、AWSやGCPを相手にインフラのコード化(IaC)と格闘していることと思います。
今回は、インフラエンジニアなら誰もが一度は冷や汗をかくテーマ、「機密情報(シークレット)の管理」について徹底的に解説します。
パスワード、DBの接続文字列、APIキー、SSH秘密鍵……。これらを「うっかりGitにプッシュしてしまい、数分後にボットに検知されてAPIキーが無効化された」なんて苦い経験、ありませんか?(私はあります、遠い目)
世の中には様々なIaCツールがありますが、Pulumiはこの機密情報管理において、他のツールの一歩先を行く非常に洗練されたアプローチを持っています。今回は、Pulumiの標準機能からAWS KMSやGCP KMSといった外部暗号化プロバイダとの連携、そしてチーム開発で絶対に事故らないための極意まで、優しく、そして深く解説していきます。
これをマスターすれば、シークレット管理の恐怖から解放され、毎日のデプロイ作業が劇的に安心で楽しいものになりますよ!
—
1. IaCにおける機密情報管理の課題
まずは、「なぜインフラのコード管理においてシークレットが厄介なのか」その本質を確認しておきましょう。
TerraformやPulumiなどのIaCツールは、インフラのEstado(状態)やコードをGitなどのバージョン管理システムで共有します。ここで直面するのが、「コードの中に平文のパスワードを書きたくないが、リソースを作成するためには実値が必要不可欠である」というジレンマです。
よくあるアンチパターンを見てみましょう。
- やってはいけない例1: コードにハードコーディング
// 絶対にダメ!Gitの履歴に永遠に残ります
const dbPassword = “SuperSecretPassword123!”;
- やってはいけない例2: 平文の環境変数に頼る
CI/CDのログに環境変数がマスクされずに出力されてしまい、ログから漏洩するケースが後を絶ちません。
IaCにおけるシークレット管理の本質は、「通信経路上(In-transit)」「保存時(At-rest)」「コード上(In-code)」のすべてのフェーズで、人間が見ても暗号化されており、実行時(Runtime)に必要な権限を持つシステムだけが復号できる状態を作ることにあります。
—
2. Pulumi標準のSecrets機能の使い方
Pulumiの最大の特徴の一つが、「デフォルトで機密情報を強力に保護する仕組み(Secrets)」が組み込まれている点です。
Pulumiは、コード内の特定の文字列が「シークレット」であるとマークされると、それを自動的に暗号化して状態ファイル(State)に保存します。そして、AWSやGCPなどのクラウド上にリソースをデプロイする「その瞬間」にだけ、安全に復号してプロバイダに渡します。
実際にやってみよう(基礎セットアップとHelloWorld)
それでは、Pulumiでシークレットを扱う基本を体験してみましょう。ここではTypeScriptをベースに進めますが、PythonやGoでも概念は全く同じです。
① インストールとプロジェクトの初期化
まだPulumiをインストールしていない場合は、公式CLIを導入し、プロジェクトを作成します。
Pulumi CLIのインストール(Macの場合)
brew install pulumi
新しいディレクトリを作ってプロジェクト初期化
mkdir pulumi-secrets-demo && cd pulumi-secrets-demo
pulumi new aws-typescript –dir . –yes
② シークレットの作成とコードでの扱い
Pulumiでは、コマンドラインから値を暗号化して保存する方法と、コード内で明示的にシークレットとしてマークする方法があります。
ターミナルから暗号化された設定値を保存してみましょう。
–secret をつけることで、Pulumiのバックエンドで強力に暗号化されます
pulumi config set dbPassword “SuperSecretPassword123!” –secret
これを行うと、`Pulumi.dev.yaml`(設定ファイル)は以下のように自動的に暗号化された文字列(`[secret]`)に変換されます。
encryptionsalt: v1:xxxxxxxxxx==
config:
aws-typescript:dbPassword:
secure: v1:AbCdEfG…[暗号化された長い文字列]…
すごいですよね! これなら、このYAMLファイルをそのままGitHubにプッシュしても、中のパスワードが露見することはありません。
③ コードからの読み込みと利用
では、このシークレットをTypeScriptのコードから安全に読み込んでみましょう。`index.ts`を次のように書き換えます。
import as pulumi from “@pulumi/pulumi”;
import as aws from “@pulumi/aws”;
// 1. PulumiのConfigインスタンスを生成
const config = new pulumi.Config();
// 2. –secretで保存した設定値を安全に取得
// 型や戻り値は Output
const dbPassword = config.requireSecret(“dbPassword”);
// 3. 例として、AWSのSSMパラメータストアにこのシークレットを格納してみます
const secureParam = new aws.ssm.Parameter(“my-db-password”, {
name: “/dev/database/password”,
type: “SecureString”, // AWS側でも暗号化して保存
value: dbPassword, // Pulumiが自動的に復号してAWSに渡します
});
// エクスポート(Pulumiはシークレットを出力する際も自動でマスクしてくれます)
export const parameterName = secureParam.name;
`pulumi up`を実行してみてください。Pulumiは安全にシークレットを扱いながら、AWS上にセキュアなパラメータストアを作成してくれます。
—
3. AWS KMSやHashiCorp Vaultなど外部シークレットマネージャーの統合方法
先ほどの方法(Pulumi標準のSecrets)では、暗号化のための鍵(Secrets Provider)として、デフォルトでPulumi Service(SaaS)の暗号化キーや、ローカルの passphrase が使われます。
しかし、企業の本番環境(Enterprise)では、「暗号化の鍵(KMS)のライフサイクルを自社のAWSやGCPのアカウントで完全にコントロールしたい」という要件が必ず出てきます。
Pulumiは、暗号化のバックエンド(Secrets Provider)を外部のKMSに簡単に差し替えることができます。
AWS KMSをSecrets Providerとして使う場合
プロジェクトの初期化時、あるいは既存のプロジェクトで、暗号化にAWS KMSのCMK(Customer Managed Key)を指定してみましょう。
AWS KMSのキーARNを指定してスタックを初期化、または移行する
pulumi stack init dev –secrets-provider=”awskms://alias/my-pulumi-encryption-key?region=ap-northeast-1″
すでに存在するスタックのプロバイダを移行したい場合は、以下のコマンド一発です。
pulumi stack change-secrets-provider “awskms://alias/my-pulumi-encryption-key?region=ap-northeast-1”
これを行うと、Pulumiが保持するシークレットの暗号化・復号のすべての処理が、AWS KMSのハードウェアセキュリティモジュール(HSM)の背後で行われるようになります。PulumiのSaaS側には平文のデータはもちろん、復号の鍵すらいかないため、セキュリティ監査も一発でクリアできるようになります。
(※GCPの場合は `gcpkms://projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key` のように指定します)
—
4. コードレビュー時にsecretsが漏洩しないための対策
最後に、SREとして最も強調したい「人間のうっかり」を防ぐためのディフェンスラインについてです。どんなに優れたツールを使っても、人間がコードに直接シークレットを書いてしまっては意味がありません。
チーム開発でシークレット漏洩を100%防ぐための3つの鉄則です。
① GitHooks(Lefthook や Husky)によるプッシュ前スキャン
コードをcommit/pushする前に、ローカル環境でシークレットが含まれていないかを自動検知させます。
代表的なツールとして gitleaks や trufflehog があります。
例えば、`gitleaks` をプロジェクトに組み込んでおけば、うっかり `const apiKey = “AKIAIOSFODNN7EXAMPLE”` のようなコードをコミットしようとした瞬間、Gitがエラーを出してコミットを強制阻止してくれます。
② Pulumi Policy (CrossGuard) によるガバナンス
Pulumiには、インフラストラクチャのコードが組織のポリシーに違反していないかをチェックする Pulumi Policy (CrossGuard) という強力な機能があります。
「コード内にハードコードされた文字列がないか」「特定のaws_s3_bucketがパブリックになっていないか」などをPythonやTypeScriptでポリシーとして記述し、`pulumi up` の実行時に自動でブロックできます。
③ CI/CDパイプラインでの権限分離
GitHub ActionsやGitLab CIなどのパイプラインでPulumiを実行する際、AWSやGCPのクレデンシャル(認証情報)は、必ずOIDC(OpenID Connect)経由で一時的な権限(IAM Role)として取得するようにしてください。長期的なアクセスキーをCIのSecretsに保存するのをやめるだけで、漏洩リスクは劇的に低下します。
—
まとめ
今回は、Pulumiにおけるシークレット管理の基礎から、AWS KMSとの統合、そしてチーム開発での事故防止策までを駆け足で解説しました。
- Pulumi標準のSecrets機能を使えば、`–secret` や `config.requireSecret` によって、コードや設定ファイルを安全に保てる。
- AWS KMSやGCP KMSと統合することで、暗号化の鍵を自社インフラ側で完全にコントロールし、エンタープライズレベルのセキュリティを実現できる。
- LefthookやGitleaks、Pulumi Policyを組み合わせて、人間の「うっかり」をシステムでブロックする。
インフラのコード化において、セキュリティは後付けではなく最初に設計すべき土台です。Pulumiの強力なシークレット機能を使いこなし、安全で堅牢なクラウドインフラを自信を持って構築していきましょう!
あなたのSREライフが、より安全で快適なものになりますように。