LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

クロスアカウントでの暗号化したRDSのスナップショット利用には気をつけようという話

これは Livesense Advent Calendar 2022 DAY 4 の記事です。

こんにちは。アルバイト事業部エンジニアの@mnmandahalfです。

今日は先日開催された社内LT大会で話したネタを記事にしてみたいと思います。

VPoEだけが追い出された?!エンジニアLT大会を開催した話 - LIVESENSE ENGINEER BLOG

TL;DR

  • クロスアカウントで暗号化したRDSスナップショットを共有するときはCMKで暗号化した方がベター
  • CMKを作るときのキーポリシーに注意しよう

やりたかったこと

ざっくり説明すると、以下の通りです。

  • 本番環境(以下、AWSのproductionアカウント)のDBデータをステージング環境(以下、AWSのstagingアカウント)に日次で同期して利用したい
  • その際、個人情報等にアクセスできないようにマスキング処理(例:データの削除、改変等)を行いたい

上記をAWSのproductionアカウントにあるRDSのスナップショットをstagingアカウントに共有し、リストアするという方法で実現しようとしました。 この際、productionのアカウントにあるRDSはKMSのAWSマネージドキー(AWSが管理するキー)を使って暗号化されていました。

ハマったこと

stagingアカウントはproductionアカウントのAWSマネージドキーにアクセスできない

stagingアカウントに共有されたスナップショットをリストアしようとして、KMSキーへのアクセス権限がなくエラーになってしまいました。

知識不足による勘違いポイントなのですが、KMSキー関連のIAMポリシーを設定しているproductionのIAMロールにstagingアカウントでAssumeRoleしてもKMSを操作できません。

An error occurred (KMSKeyNotAccessibleFault) when calling the RestoreDBClusterFromSnapshot operation: The specified KMS key [arn:aws:kms:ap-northeast-1:1122334455:key/11111-22222-3333-44444-55555] does not exist, is not enabled or you do not have permissions to access it.
Error: Process completed with exit code 254.

KMSキーを操作するためにはIAMポリシーだけでなく、キーポリシーでもstagingアカウントへの許可が必要でした。

そして元々AWSのマネージドキーでRDSを暗号化していたものの、AWSのマネージドキーはキーポリシーを変更できないのでカスタマーマネージドキー(以下、CMK)を作る必要がありました。

ちなみにRDSのAWSマネージドキーのキーポリシーは以下のようになっているので、productionアカウント内のIAMユーザーやロールにアタッチしているIAMポリシーの許可があればキーの利用が可能になっています。

{
    "Version": "2012-10-17",
    "Id": "auto-rds-2",
    "Statement": [
        {
            "Sid": "Allow access through RDS for all principals in the account that are authorized to use RDS",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:CreateGrant",
                "kms:ListGrants",
                "kms:DescribeKey"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "kms:CallerAccount": "${production_account_id}",
                    "kms:ViaService": "rds.ap-northeast-1.amazonaws.com"
                }
            }
        },
        {
            "Sid": "Allow direct access to key metadata to the account",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${production_account_id}:root"
            },
            "Action": [
                "kms:Describe*",
                "kms:Get*",
                "kms:List*",
                "kms:RevokeGrant"
            ],
            "Resource": "*"
        }
    ]
}

RDSの暗号化キーの変更はできないため、変更のためにはRDSの再作成が必要になります。今回は本番稼働前だったのでインスタンスを作り直すことにしました。

CMKのキーポリシーの設定

DB クラスターのスナップショットの共有 - Amazon Aurora

その後、基本はこのユーザーガイドを見ながらCMKのキーポリシーを設定していったのですが、ポリシーを適切に設定しないと以下のようなエラーが出てしまいました。

Error: error creating KMS Key: MalformedPolicyDocumentException: The new key policy will not allow you to update the key policy in the future.

このエラーは、キーの使用者に必要な許可がない時に発生するようでした。

キーポリシーが作成されると、AWS KMS はセーフティチェックを実行します。セーフティチェックの 1 つが、キーポリシーのプリンシパルに CreateKey API と PutKeyPolicy API を実行するために必要な許可があることを確認します。このチェックは、KMS キーが管理不能になる可能性を排除します。つまり、キーポリシーを変更したりキーを削除したりすることができないという意味です。
「The new key policy will not allow you to update the key policy in the future」エラーを解決する

Terraformでキーポリシーを明示せずにCMKを作成するとデフォルトでrootユーザーの CreateKey API と PutKeyPolicy API を含む全アクションを許可したものが作成されるのですが、誤ってキーの管理者ユーザーを削除してもキーの操作ができるようになるためのようです。

AWS KMS API を使用して KMS キーをプログラムで作成し (AWS SDK、AWS Command Line Interface、または AWS Tools for PowerShell の使用を含む)、キーポリシーを指定しなかった場合、AWS KMS は非常に単純なデフォルトキーポリシーを適用します。このデフォルトのキーポリシーには、KMS キー許可を有する AWS アカウント に対して、IAM ポリシーを使用して KMS キー上のすべての AWS KMS オペレーションに対するアクセスを許可する権限を付与するポリシーステートメントが 1 つ含まれます。
デフォルトのキーポリシー - AWS Key Management Service
デフォルトで作成されたCMKのキーポリシー
{
    "Sid": "Enable IAM policies",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::${prod_account_id}:root"
    },
    "Action": "kms:*",
    "Resource": "*"
}

上記を受けて、最終的なキーポリシーではproductionアカウントのrootユーザーで管理できるように許可を与えています。

最終的なキーポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${prod_account_id}:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "AllowUseOfTheKey",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${stg_account_id}:role/staging-dbsync-iam-role"
            },
            "Action": [
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:Encrypt",
                "kms:DescribeKey",
                "kms:Decrypt",
                "kms:CreateGrant"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowAttachmentOfPersistentResources",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${stg_account_id}:role/staging-dbsync-iam-role"
            },
            "Action": [
                "kms:RevokeGrant",
                "kms:ListGrants",
                "kms:CreateGrant"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "kms:GrantIsForAWSResource": "true"
                }
            }
        }
    ]
}
CMK利用者(stagingアカウント)のIAMポリシー
{
    "Statement": [
        // (略)RDS関連のAction...
        {
            "Action": [
                "kms:RetireGrant",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:Encrypt",
                "kms:DescribeKey",
                "kms:Decrypt",
                "kms:CreateGrant"
            ],
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "AllowUseOfTheKey"
        },
        {
            "Action": [
                "kms:RevokeGrant",
                "kms:ListGrants",
                "kms:CreateGrant"
            ],
            "Condition": {
                "Bool": {
                    "kms:GrantIsForAWSResource": "true"
                }
            },
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "AllowAttachmentOfPersistentResources"
        },
        {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Resource": "arn:aws:iam::${prod_account_id}:role/production-dbsync",
            "Sid": ""
        }
    ],
    "Version": "2012-10-17"
}

RDSの削除に手間取った

もう一つ手間取ったのが、Terraformで作成していたRDSのdestroyです。

もともと削除することになるとは思っていなかったので削除保護のオプションを入れていました。そして、Terraformの削除保護を解除した後も、skip_final_snapshot = falseを指定しているのにfinal_snapshot_identifierを指定しておらず、destroyに失敗してしまいました。

skip_final_snapshotとfinal_snapshot_identifierはニコイチのオプションなので、設定するときは要注意です。

運用中のDBで起きてしまったらどうするか

運用中のDBで同様の事象が発生した場合は、スナップショットを別のCMK(上述のポリシー設定が必要)で暗号化し直し、そのキーをstagingアカウントで操作できるようにすればよいようです。

Aurora Serverless v1 のデフォルトKMSキーで暗号化したスナップショットを他アカウントへ共有するために必要なこと | DevelopersIO

最後に

AWSを利用している多くのチームでは本番アカウントのRDSスナップショットを開発/ステージング等の別アカウントに共有する処理を行なっていると思うので、実はハマりがちなポイントなのではないかと思って記事にしてみました。

AWS環境に限らず、皆様のチームではどのように本番 - 開発/ステージング環境間のDBデータ同期を行っていますでしょうか?もしよろしければブコメ等でぜひ教えてください。