
シークレットは多くの本番システムの運用に欠かせない要素です。意図しないシークレット漏洩は、最優先で対処すべきリスクの一つです。開発者は、アプリケーションのシークレットを守るために最善を尽くす必要があります。
マイクロサービスアーキテクチャに移行し、複数のサービスがそれぞれ異なるシークレットへのアクセスを必要とするようになると、この問題はさらに複雑になります。そこで新たな課題が浮上します。意図しない漏洩を防ぎつつ、アプリケーションのシークレットをどう配布・管理・監視・ローテーションするかという問題です。
Kubernetes Secrets
KubernetesにはSecretというオブジェクトが用意されており、パスワード、SSHキー、APIキー、トークンなど、アプリケーションの機密データを保存できます。Kubernetes Secretは、環境変数として、あるいはファイルとしてマウントする形でPodコンテナに渡せます。Kubernetes Secretsを使えば、機密データや設定をアプリケーションのデプロイから切り離して扱えます。
たとえば、Kubernetes Secretは kubectl create secret コマンドで作成できます:
https://gist.github.com/02c582490747681776f1f480ec2f4165
あるいは、同じシークレットを定義するKubernetesの db-credentials.yaml ファイルでも作成できます:
ここで注意したいのは、機密データをKubernetes Secretに保存しただけでは安全とは言えないという点です。デフォルトでは、Kubernetes Secrets内のデータはすべて base64 でエンコードされた平文として保存されます。
バージョン1.13以降、Kubernetesは EncryptionConfiguration オブジェクトと組み込みまたは外部の暗号化プロバイダーを使った保存時のSecretsデータ暗号化に対応しています。
現在サポートされている暗号化プロバイダー:
組み込みプロバイダー: aescbc、aesgsm、secretbox
とはいえ、保存時のSecrets暗号化はデフォルトで強制されているわけではありません。さらに、有効化したとしてもそれだけでは不十分で、本格的なシークレット管理ソリューションとは呼べません。
本格的なシークレット管理ソリューションには、シークレットの配布、ローテーション、きめ細かなアクセス制御、監査ログ、利用状況のモニタリング、バージョン管理、強固に暗号化されたストレージ、扱いやすいAPIとクライアントSDK、その他便利な機能が求められます。
クラウドのシークレット管理
多くのクラウドベンダーは、自社プラットフォームの一部としてシークレット管理サービスを提供しており、アプリケーション、サービス、APIへのアクセスに必要なシークレットの保護に役立ちます。これらのサービスを活用すれば、機密情報を平文でハードコードしたり、独自のシークレット管理ライフサイクルを自前で構築したりする必要はありません。きめ細かな権限設定と監査機能を備えており、アプリケーションのシークレットへのアクセスを的確に制御できます。
Kubernetesとシークレット管理サービスの統合
基本的に、どのアプリケーションもベンダー固有のSDK/APIを使えば、シークレット管理サービスに保存されたシークレットにアクセスできます。ただし通常は、アプリケーションコードの変更や、ブートストラップスクリプト、あるいはInit containerを介したクライアントCLIツールやWeb APIの利用が必要になります。
secrets-init ツールでシンプルに
本日、doitintl/secrets-init をリリースしました。クラウドマネージド/セルフマネージドのKubernetesクラスター上で動作するコンテナ化されたworkloadと、cloud-nativeなシークレット管理サービスとの統合をシンプルにする、オープンソースツール(Apache License 2.0)です。
secrets-init の本質は、コンテナ環境内で PID 1 として動作するように設計されたミニマルな init システムである点にあります。dumb-init と同様の役割を果たしながら、複数のcloud-nativeなシークレット管理サービスとスムーズに連携します:
Dockerコンテナにinitシステムが必要な理由
詳しくはYelpの dumb-init リポジトリの解説をご覧ください。
要点は次のとおりです:
- シグナルの適切な転送
- 孤児ゾンビプロセスの回収
secrets-init の動作
secrets-init は PID 1 として実行され、シンプルなinitシステムとして振る舞います。ENTRYPOINT または最初のコンテナコマンドの位置に入り、子プロセスを起動して、その子プロセスを起点とするセッションへすべてのシステムシグナルを中継します。これがinitプロセスの本質です。一方で secrets-init は、環境変数の_ほぼ_すべてをそのまま引き渡しつつ、特殊な_シークレット変数_だけをシークレット管理サービスから取得した値に置き換えます。
Dockerとの統合
secrets-init は静的にコンパイルされたバイナリ(外部依存なし)で、どんなDockerイメージにも簡単に組み込めます。secrets-init バイナリをダウンロードし、Dockerコンテナの ENTRYPOINT として指定してください。
例:
https://gist.github.com/7efe25155c034ee1b978a2477b3224e4
Kubernetesとの統合
Dockerイメージに手を加えずにKubernetesオブジェクト(Pod/Deployment/Jobなど)で secrets-init を使うには、initContainer 経由で対象のPodに secrets-init を注入する方法がおすすめです。doitint/secrets-init のDockerイメージを利用してもよいですし、独自のイメージを作成しても構いません。initコンテナから共有ボリュームに secrets-init バイナリをコピーし、Podの command を変更して secrets-init が最初に実行されるようにします。
AWS Secrets Managerとの統合
secrets-init をAWS Secrets Managerと組み合わせて使うには、AWSシークレットのARNを環境変数の値として設定します。secrets-init は、指定されたARNをもとに、その環境変数の値を参照先のシークレット値に置き換えます。
Kubernetes Jobで secrets-init を使う例:
https://gist.github.com/6ae6d57ed08b26c64f534f87fc1872df
AWS Systems Manager Parameter Storeとの統合
AWS Systems Manager Parameter Storeは、アプリケーションのパラメータを平文または暗号化(シークレットに近い形)で保存するのにも使えます。
先ほどの例と同様に、AWS Parameter StoreのARNを環境変数の値として設定すれば、secrets-init が指定のARNをもとにその値を参照先のパラメータ値へと置き換えます。
AWS Systems Manager Parameter Storeのフォーマット例:
https://gist.github.com/9a7a83ec1659c391412557e5af340f42
AWS Secrets ManagerおよびParameter StoreからAWSシークレットを解決するには、secrets-init を対象シークレットへのアクセス権限を持つIAMロール配下で実行する必要があります。
これは、Kubernetes PodまたはECS TaskにIAMロールを割り当てることで実現できます。詳しくはEKSブログの記事 Introducing fine-grained IAM roles for service accounts をご覧ください。
コンテナが動作するEC2インスタンス自体にIAMロールを割り当てる方法もありますが、セキュリティ面で大きく劣るため推奨しません。
Google Secret Managerとの統合
Google Cloudは先ごろ、クラウド上でシークレットを管理する新サービス Google Secret Manager をリリースしました。
secrets-init が認識するプレフィックス(gcp:secretmanager:)に続けてシークレット名(projects/{PROJECT_ID}/secrets/{SECRET_NAME} または projects/{PROJECT_ID}/secrets/{SECRET_NAME}/versions/{VERSION})を環境変数の値として指定すれば、secrets-init がその名前をもとに値を参照先のシークレット値へと置き換えます。
https://gist.github.com/65d46eb2b1fe44417a9d91828c94712d
Google Secret ManagerからGoogleシークレットを解決するには、secrets-init を対象シークレットへのアクセス権限を持つIAMロール配下で実行する必要があります。
これは、Workload Identity を使ってKubernetes PodにIAMロールを割り当てることで実現できます。コンテナが動作するGCEインスタンスにIAMロールを割り当てることもできますが、こちらはセキュリティ面で劣ります。
(2020年3月15日)アップデート: Kubernetes Admission Webhook
kube-secrets-init プロジェクトは、Kubernetesの admission webhook を実装したもので、クラウドシークレットへの明示的(環境変数)または暗黙的(Kubernetesの Secrets や ConfigMaps)な参照を持つPodに対して initContainer を自動で注入します。
関連リンク
- Kubernetes GKE Workload Identity DoiTブログ記事
- doitintl/secrets-init GitHubリポジトリ
- kube-secrets-init Kubernetes admission webhook GitHubリポジトリ
まとめ
本記事がお役に立てば幸いです。ご意見やご質問をお待ちしています。
他の記事もお読みになりたい方は、ブログをチェックするか、TwitterでAlexeiをフォローしてみてください。