【失敗談】KMSキーで「最小権限」に絞ろうと試みるも無惨に諦めた話
レバテック開発部の松浪です。
先日、EBSボリュームに使用する暗号化キーについて、AWSマネージドキーからカスタマーマネージドキー(KMSキー)への移行を試みました。
目的は権限を絞るためです。AWSマネージドキーは "Principal": {"AWS": "*"}
という汎用性が高い反面、AWSアカウント内の誰からでも利用可能な広い権限設定になっています。
KMSキーへ移行する過程で色々なエラーに遭遇しました。そして、移行を断念しました。
終
...で、終わったらブログにならないので、この記事では私が遭遇した3つのエラーと移行を断念した理由を解説します。
はじめに
- この記事は「キー移行の成功手順」を解説するものではありません 🙇
- 代わりにKMSキー作成の過程で遭遇した具体的なエラー、そのエラー理由と解消方法を解説します 🙇
- そして、最終的にKMSキーへ移行しなかった理由を共有する記事です 🙇
なお、インフラのコード管理(IaC)には terraform を利用しています。
KMSって何?という方は公式ドキュメントをご覧ください。
エラーその1: MalformedPolicyDocumentException
terraform plan
は正常に結果を出力してくれるのに、いざ terraform apply
を実行するとこのエラーに遭遇しました。
エラーログ
Error: creating KMS Key: operation error KMS: CreateKey, https response error StatusCode: 400, RequestID: f64ee3a1-1f12-43bb-8211-facd951d9ac9, MalformedPolicyDocumentException: The new key policy will not allow you to update the key policy in the future.
原因
kms:PutKeyPolicy
が不足していたため、発生していました。
KMSでは、新しいキーポリシーが作成された後も、将来にわたって更新できる必要があります。
これは自分自身がアクセスできないキーを作成してしまうのを防ぐためのAWSに備わっている対策(仕様)です。
The key policy must allow the calling principal to make a subsequent PutKeyPolicy request on the KMS key.
と記載があり、 PutKeyPolicy
が必要なのがわかります。
エラーログには Error: creating KMS Key: operation error KMS: CreateKey, 〜
とあるので、kms:CreateKey
が不足していると誤認しないように注意してください。
kms:CreateKey
をキーポリシーに含める必要はありません。
対処
キーポリシーの許可するActionに kms:PutKeyPolicy
を追加してエラーを回避できました。
resource "aws_kms_key_policy" "example" {
policy = jsonencode({
Statement = [
{
Action = [
...(略),
"kms:PutKeyPolicy" ## これが必要
],
:
エラーその2: no resource-based policy allows the kms:CreateAlias action
キーが無事に生成されたのに、そのキーに別名(エイリアス)を付けようとしたところでエラーが発生しました。
エラーログ
Error: creating KMS Alias (alias/ebs-default-encryption): operation error KMS: CreateAlias, https response error StatusCode: 400, RequestID: 12345-..., api error AccessDeniedException: User: arn:aws:sts::(account_id):assumed-role/GitHubActions is not authorized to perform: kms:CreateAlias on resource: arn:aws:kms:***:(account_id):key/abcde-... because no resource-based policy allows the kms:CreateAlias action
原因
エイリアスより前にキーポリシーを先に作成されていたため、発生していました。
KMSキー、キーポリシー、エイリアスの3つを同時に作成する際、terraformは次の順番でリソースを作成していました。
-
aws_kms_key
を作成 -
aws_kms_key_policy
を作成 -
aws_kms_alias
を作成 (← ここでエラー)
KMSはキーを作成した直後は、 非常に寛容な「デフォルトキーポリシー」 を適用します。このデフォルトキーポリシーは、KMSに対するさまざまなアクションが許可されている状態です。
It gives the AWS account that owns the KMS key full access to the KMS key.
と記載があり、KMSに対してあらゆるActionが許容されているのがわかります。
今回、terraformはキーを作成した後、エイリアスを作成するよりも先に、私の定義したカスタムキーポリシーが適用されていました。
結果としてデフォルトキーポリシーは上書きされ、 kms:CreateAlias
の許可がなくなってしまった、という訳です。
対処
depends_on
を使って、リソース間の依存関係を定義することでエラーを回避できました。
# 1. KMSキー
resource "aws_kms_key" "example" {
deletion_window_in_days = 30
}
# 2. キーポリシー
resource "aws_kms_key_policy" "example" {
key_id = aws_kms_key.example.id
policy = jsonencode({...})
depends_on = [aws_kms_alias.example] # 3. を作成後、2. を作成する
}
# 3. エイリアス
resource "aws_kms_alias" "example" {
name = "alias/ebs-default-encryption"
target_key_id = aws_kms_key.example.key_id
}
エラーその3: no resource-based policy allows the kms:GetKeyPolicy action
キーが無事に生成されて、エイリアスも付けて、いざAWSコンソール上で確認しようとするもキーにアクセスできず、次のエラーが発生しました。
エラーログ
Error: reading KMS Key (abcde-...) policy: operation error KMS: GetKeyPolicy, https response error StatusCode: 400, RequestID: 12345-..., api error AccessDeniedException: User: arn:aws:sts::(account_id):assumed-role/GitHubActions is not authorized to perform: kms:GetKeyPolicy on resource: arn:aws:kms:***:(account_id):key/abcde-... because no resource-based policy allows the kms:GetKeyPolicy action
原因
kms:GetKeyPolicy
が不足していたため、発生していました。
このエラーが厄介なのは、キーの作成やポリシーの適用の時点ではエラーを返さないことです。
「作成はできるけど、二度と中身を見ることができない」という状態に陥りました。
kms:PutKeyPolicy
が不足していると、AWSが親切にキーを生成する前にエラーを返してくれます。(エラー1のケース)
しかし、kms:GetKeyPolicy
にはそのような事前チェックがありません。
その結果、 terraform apply
は成功するが、applyしたキーにアクセスできなくて詰みます。
対処
一度、AWSアカウントのルートユーザでキーポリシーを初期化していただき、
キーポリシーの許可するActionに kms:GetKeyPolicy
を追加してエラーを回避できました。
resource "aws_kms_key_policy" "example" {
policy = jsonencode({
Statement = [
{
Action = [
...(略),
"kms:PutKeyPolicy",
"kms:GetKeyPolicy" ## これが必要
],
:
カスタマーマネージドキーに移行後...
3つのエラーを乗り越え、無事にEBSボリュームのデフォルト暗号化キーをカスタマーマネージドキーに移行しました。
めでたしめでたし。
...と思ったのも束の間、今度はEKSのバージョンアップがノード作成中のエラーで失敗してしまうのと報告を受けました。
原因
EKSに対して、KMSキーを使う権限が与えられていなかったため、発生していました。
これは完全に私の知識不足なのですが、EBSボリュームを作成する可能性があるケースは何も開発者からの操作だけではありません。
EC2のオートスケーリング、EKSのバージョンアップ、RDSをスナップショットから復元、AWS Backupでのバックアップからの復元、など。
つまり、権限を絞るということはどのAWSリソースに何のActionを許容するか事前にしっかり見極めないといけないということです。
戦略的(?)撤退
事前に影響範囲をすべて把握するのは困難で、障害が起きてから初めて気づくようでは運用面でもリスクが高すぎる、と判断してEBSボリュームに使用する暗号化キーの権限を絞ることは中断することしました。
触らぬ神に祟りなし。
では、どうすればよかったか?
AWSアカウント全体の1つの暗号化キーを使うのではなく、アプリケーションごとに暗号化キー(KMSキー)を用意して使うのが良いかなと思います。
そうすれば、権限を絞りやすくなり、万が一設定を誤っても影響はそのアプリケーション内に限定することができます。
おわりに
今回「最小権限の原則」をインフラに適用する際の難しさを改めて実感しました。
もし、KMSキーへ移行して適切な権限制御を実現したノウハウを持つ方がいらっしゃったら是非、弊社に来て助けてください。
Discussion