Skip to content

データベースのスナップショットを作成したアカウントから別アカウントにコピーする

はじめに

データベースのスナップショットを作成した AWS アカウントから別の AWS アカウントにコピーしたい場合、スナップショットがデフォルトの KMS キー(alias: aws/rds)で暗号化されていると共有できません。

2つの記事を読むと、別のアカウントにスナップショットを共有するためには、スナップショットをカスタマーマネージドキー(以下:CMK)で暗号化する必要があります。

  1. Amazon RDS の手動 DB スナップショットまたは Aurora DB クラスタースナップショットを他の AWS アカウントと共有するにはどうすればよいですか? - AWS re:Post
  2. 暗号化された Amazon Aurora スナップショットを別のアカウントと共有するにはどうすればよいですか? - AWS re:Post

私自身スナップショットの暗号化に使用する KMS キーが共有元、共有先、どちらのアカウントに作成するのか最初わかりませんでした。

aws-kms-key.drawio

調べてみると、AWS のドキュメントには以下のようなことが書いてあります。

スナップショットの暗号化に使用された AWS KMS key を、スナップショットにアクセスできるようにするすべてのアカウントと共有します。 KMS キーポリシーに他のアカウントを追加することで、KMS キーを別の AWS アカウントと共有できます。

引用:暗号化されたスナップショットの共有 - AWS Documentation

さらに AWSの情報センターでは、ターゲットアカウントに、ソースアカウント内のカスタム AWS KMS キーへのアクセスを許可する方法について記述があります。

つまりKMS のキーは共有元の AWS アカウントにあり、それを別アカウント(共有先)に共有することで暗号化されたスナップショットを別アカウントに共有することができるそうです。

今回は、AWS のアーキテクチャ図と一緒に説明している記事が少なく、イメージしづらかったことから自ら検証してみようと思います。

成果物

https://github.com/kntks/blog-code/tree/main/2024/02/copy-rds-snapshot

アーキテクチャ

AWS アカウントを2つ用意します。

Sandbox-1 がスナップショット共有元アカウントで、Sandbox-2 が共有先アカウントです。

sso-login

以下の画像にあるアーキテクチャを Terraform で作成します。
copy-snapshot-customer-managed-key

手順

これから検証する内容の簡単な手順を示します。

  1. Terraform で環境構築、共有元アカウントに Aurora クラスターと DB インスタンスを作成。
  2. 踏み台サーバから DB に簡単なデータを Insert する
  3. デフォルトキーでスナップショットを作成する
  4. CMK でスナップショットのコピーを作成する
  5. 共有先アカウントにスナップショットを共有する
  6. 共有先アカウントでスナップショットから DB を復元する

検証

1. 環境構築

Terraform は 2024 年 1 月現在最新の 1.7.0 を使用します。

Terminal window
mise local terraform 1.7.0
terraform init
terraform plan
terraform apply -auto-approve

RDS のデフォルトキー

Aurora MySQL クラスターと DB インスタンスの作成が完了すると、AWS マネージド型キーに RDS のデフォルトキー(alias: aws/rds)が作成されます。

rds-default-key

AWS マネージドキー は、お客様のアカウントにある KMS キーであり、AWS KMS と統合されている AWS のサービスがお客様に代わって作成し、管理し、使用します。デフォルトでは、RDS AWS マネージドキー (aws/rds) が暗号化に使用されます。

引用:AWS KMS key 管理 - AWS Documentation

RDS のデフォルトキーのキーポリシー
{
"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:ViaService": "rds.ap-northeast-1.amazonaws.com",
"kms:CallerAccount": "891377404752"
}
}
},
{
"Sid": "Allow direct access to key metadata to the account",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::891377404752:root"
},
"Action": [
"kms:Describe*",
"kms:Get*",
"kms:List*",
"kms:RevokeGrant"
],
"Resource": "*"
}
]
}

2. 踏み台サーバから DB に簡単なデータを Insert する

※ 共有元アカウントでの作業

まず、Aurora MySQL のエンドポイントを確認します。

Terminal window
$ aws rds describe-db-cluster-endpoints --db-cluster-identifier example-db --profile sandbox-1
{
"DBClusterEndpoints": [
{
"DBClusterIdentifier": "example-db",
"Endpoint": "example-db.cluster-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com",
"Status": "available",
"EndpointType": "WRITER"
},
{
"DBClusterIdentifier": "example-db",
"Endpoint": "example-db.cluster-ro-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com",
"Status": "available",
"EndpointType": "READER"
}
]
}

データベースを作成すると AWS Secret Manager にシークレットが自動で作成されます。そこから root のパスワードを取得します rds-root-password

踏み台サーバのインスタンス ID を取得し、セッションを開始します。

Terminal window
aws ec2 describe-instances --filter "Name=tag:Name,Values=example-bastion" "Name=instance-state-name,Values=running" --query "Reservations[].Instances[].InstanceId" --output text --profile sandbox-1
aws ssm start-session --target i-064c501d998f283a6 --profile sandbox-1

踏み台サーバを立ち上げる際に user_data に mysql client をインストールするスクリプトを入れているので、mysql コマンドが使えるはずです。

Terminal window
sh-5.2$ which mysql
/usr/bin/mysql

踏み台サーバから DB にアクセスします。

Terminal window
$ mysql -h example-db.cluster-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com -P 3306 -u root -p
Enter password:

参考:Amazon Aurora DB クラスターへの接続 - AWS Documentation

共有先のアカウントでスナップショットから DB を作成したときにデータが入っているかを確認したいため、この作業では、データベースにデータを入れておきます。

Terraform では、database_nameexample にしたので、use example; でデータベースを変更できます。

Terminal window
mysql> use example;
Database changed

データを作成します。

Terminal window
CREATE TABLE users (
`user_id` INT NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`age` INT NOT NULL,
PRIMARY KEY(`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_bin;
INSERT INTO users (name, age) VALUES ("test1", 10), ("test2", 20);

データが入ったので、問題なさそうです。

Terminal window
mysql> select * from users;
+---------+-------+-----+
| user_id | name | age |
+---------+-------+-----+
| 1 | test1 | 10 |
| 2 | test2 | 20 |
+---------+-------+-----+
2 rows in set (0.00 sec)

3. デフォルトキーでスナップショットを作成する

※ 共有元アカウントでの作業

データベースからスナップショットを作成します。 action-snapshot

スナップショットの取得ボタンを押します。 click-snapshot-button

4. CMK でスナップショットのコピーを作成する

これで RDSのデフォルトキーで暗号化されたスナップショットが取得できます。 action-copy-snapshot

Terraform であらかじめ作成していた CMK を設定します。 copy-snapshot

スナップショットをコピーのボタンを押した後、example-db-copy-1 のスナップショットが作成されました。

snapshots-after-copied

5. 共有先アカウントにスナップショットを共有する

※ 共有元アカウントでの作業

example-db-copy-1 をクリックすると以下のページに遷移します。アクションからスナップショットの共有を選択してください。

action-share-snapshot

アクセス許可に Sandbox-2 の AWS アカウントID を入れ、追加ボタンを押します。
そのあと、保存ボタンを押してください。 snapshot-access-permission

6. 共有先アカウントでスナップショットから DB を復元する

ここからは Sandbox-2 のアカウントでの作業です。

Sandbox-2 のアカウントに移動します。RDS のページに行った後、自分と共有のタブを確認すると共有されたスナップショットが確認できました。 sandbox-2-snapshot

このスナップショットから復元してみます。 action-restore-snapshot

スナップショットを復元のページが長いため、変更点だけまとめました。

設定
DBインスタンス識別子example-db-2
インスタンスの設定
バースト可能クラス(ラジオボタンにチェックを入れる)db.t3.medium
接続補足
VPCexample-vpcTerraaform で作成済
パブリックアクセスなし
VPC セキュリティグループ既存の選択
既存の VPC セキュリティグループexample-db-2Terraform で作成済
アベイラビリティーゾーンap-northeast-1

Sandbox-1の時と同じ要領で DB に接続します。パスワードは Sandbox-1 で DB に接続した時と同じです。

Terminal window
aws rds describe-db-cluster-endpoints --db-cluster-identifier example-db-2-cluster --profile sandbox-2
aws ec2 describe-instances --filter "Name=tag:Name,Values=example-bastion" "Name=instance-state-name,Values=running" --query "Reservations[].Instances[].InstanceId" --output text --profile sandbox-2
aws ssm start-session --target i-006b7a2e209096de9 --profile sandbox-2
mysql -h example-db-2-cluster.cluster-c9ygy4kamm38.ap-northeast-1.rds.amazonaws.com -P 3306 -u root -p

Sandbox-2 のアカウントに作成した DB でもデータを復元することができました。

mysql> use example;
Database changed
mysql> select * from users;
+---------+-------+-----+
| user_id | name | age |
+---------+-------+-----+
| 1 | test1 | 10 |
| 2 | test2 | 20 |
+---------+-------+-----+
2 rows in set (0.00 sec)

片付け

Sandbox-2 では、DB を手動で作成したので、先に DB を AWS マネジメントコンソールから削除します。

そのあと、Terraform からすべてのリソースを削除します。

Terminal window
terraform destroy

さいごに

Sandbox-1 アカウントからスナップショットを取得し、共有することで Sandbox-2 アカウントで復元することができました。

Sandbox-2 でスナップショットを復元した後のデータベースパスワードは、Sandbox-1 のときと同じですが、Sandbox-2 側の AWS Secret Manager にシークレットが作成されるわけではありません。自分で設定する必要があります。

今回の検証では、操作する IAM が Administrator だったため、Sandbox-2 側の IAM について何もしませんでした。ポリシーや PermissionSet の設計をしている組織では、共有先の AWS アカウントでスナップショットを復元するときの権限には注意したほうがよさそうです。

トラブルシューティング

updating policy: MalformedPolicyDocumentException: The new key policy will not allow you to update the key policy in the future

とりあえず、KMS を作成しようと以下のような Terraform を作成したところ、 terraform apply のタイミングでエラーになりました。

resource "aws_kms_key" "snapshot" {
count = var.create_customer_managed_key ? 1 : 0
description = "to encrypt the database snapshot"
deletion_window_in_days = 10
}
resource "aws_kms_key_policy" "this" {
count = var.create_customer_managed_key ? 1 : 0
key_id = aws_kms_key.snapshot[0].id
policy = data.aws_iam_policy_document.this.json
}
data "aws_iam_policy_document" "this" {
statement {
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
]
resources = ["*"]
principals {
type = "AWS"
identifiers = [ "*" ]
}
}
}

エラー文を読んでみると、ポリシーの更新中: MalformedPolicyDocumentException: 新しいキーポリシーでは将来キーポリシーを更新できません。と書かれています。
以下の記事では、権限が足りないため発生している事象であることが説明されています。

セーフティチェックの 1 つが、キーポリシーのプリンシパルに CreateKey API と PutKeyPolicy API を実行するために必要な許可があることを確認します。このチェックにより、AWS KMS キーが管理不能になる可能性がなくなります。つまり、キーポリシーを変更したり、キーを削除したりできなくなります。

引用:AWS CloudFormation を使用して AWS KMS キーを作成しようとすると表示される「新しいキーポリシーでは今後キーポリシーを更新できません」エラーを解決する方法を教えてください。 - AWS rePost

エラーを解決するために強めの権限をつけてみると解消しました。

data "aws_iam_policy_document" "this" {
# 以下の権限を付与する
statement {
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
}
actions = [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
]
resources = ["*"]
}
省略
}

Aurora DBクラスターに接続できない

パスワードを入力したにもかかわらず、MySQL がスタートしない場合は、DB に関連している Security Group のルールが間違っている可能性があります。

Terminal window
sh-5.2$ mysql -h example-db.cluster-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com -P 3306 -u root -p
Enter password:

今回は、EC2 インスタンスに関連していたサブネットからの通信を Security Group が許可していませんでした。