本記事はAsiaQuest Advent Calendarの3日目です。
暗号化されたAuroraのスナップショットをAWS Backupで他リージョンにコピーする機会がありました。
その際、権限周りで躓く事がありましたので備忘録として残しておこうと思い、当記事を執筆しました。
補足:RDSにはクロスリージョン自動バックアップ機能があります。
しかし、当記事執筆時点(2023年2月)では、Auroraは非対応です。
クロスリージョン自動バックアップ
今回の構成のポイントは以下の通りです。
次にCloudFormationのテンプレート例です。
AWS Backup、KMS以外のリソースに関しては割愛させていただきます。ご了承ください。
AWSTemplateFormatVersion: '2010-09-09'
Description: Ap-northeast-1 Source AWS Backup
Resources:
BackupVault:
Type: AWS::Backup::BackupVault
Properties:
BackupVaultName: source-backup-vault
EncryptionKeyArn:
Fn::ImportValue: primary-kms
BackupPlan:
Type: AWS::Backup::BackupPlan
Properties:
BackupPlan:
BackupPlanName: source-backup-plan
BackupPlanRule:
- RuleName: source-backup-rule
TargetBackupVault: !Ref BackupVault
ScheduleExpression: cron(0 16 * * ? *) # JST 1:00
StartWindowMinutes: 60
CompletionWindowMinutes: 720
Lifecycle:
DeleteAfterDays: 1
CopyActions:
- DestinationBackupVaultArn: !Sub arn:aws:backup:ap-northeast-3:${AWS::AccountId}:backup-vault:destination-backup-vault
Lifecycle:
DeleteAfterDays: 1
BackupSelection:
Type: AWS::Backup::BackupSelection
Properties:
BackupPlanId: !Ref BackupPlan
BackupSelection:
SelectionName: source-backup-selection
IamRoleArn: !GetAtt BackupIamRole.Arn
Resources:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster:クラスター名
BackupIamRole:
Type: AWS::IAM::Role
Properties:
RoleName: source-backup-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: backup.amazonaws.com
BackupIamPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: source-backup-policy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- rds:CreateDBClusterSnapshot
- rds:DescribeDBClusters
- rds:DescribeDBClusterSnapshots
- rds:AddTagsToResource
- rds:ListTagsForResource
- rds:CopyDBClusterSnapshot
- rds:ModifyDBClusterSnapshotAttribute
- rds:RestoreDBClusterFromSnapshot
Resource:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster:クラスター名*
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:subgrp:サブネットグループ名
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-pg:クラスターパラメータグループ名
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- !Sub arn:aws:rds:ap-northeast-3:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- Effect: Allow
Action:
- rds:DeleteDBClusterSnapshot
Resource:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- !Sub arn:aws:rds:ap-northeast-3:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- Effect: Allow
Action:
- backup:DescribeBackupVault
- backup:CopyIntoBackupVault
Resource:
- !Sub arn:aws:backup:ap-northeast-1:${AWS::AccountId}:backup-vault:source-backup-vault
- !Sub arn:aws:backup:ap-northeast-3:${AWS::AccountId}:backup-vault:destination-backup-vault
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
- kms:GenerateDataKey
- kms:ReEncrypt*
- kms:CreateGrant
- kms:DescribeKey
Resource:
- Fn::ImportValue: primary-kms
- 大阪リージョンのレプリカキーのArn
- Effect: Allow
Action:
- kms:DescribeKey
Resource:
- !Sub arn:aws:kms:ap-northeast-1:${AWS::AccountId}:key/*
- !Sub arn:aws:kms:ap-northeast-3:${AWS::AccountId}:key/*
Condition:
ForAnyValue:StringEquals:
kms:ResourceAliases:
- alias/aws/rds
- alias/aws/backup
Roles:
- !Ref BackupIamRole
primary-kms.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: Ap-northeast-1 Primary Kms
Resources:
KmsKey:
Type: AWS::KMS::Key
Properties:
MultiRegion: true
KeyPolicy:
Version: 2012-10-17
Id: key-policy
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: "*"
Resource: "*"
- Sid: Allow administration of the key # 適宜ユーザかロールに権限を付与してください
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:role/xxxxx
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: "*"
KmsKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/primary-kms
TargetKeyId: !Ref KmsKey
Outputs:
KmsKey:
Value: !GetAtt KmsKey.Arn
Export:
Name: primary-kms
AWSTemplateFormatVersion: '2010-09-09'
Description: Ap-northeast-3 Destination AWS Backup
Resources:
BackupVault:
Type: AWS::Backup::BackupVault
Properties:
BackupVaultName: destination-backup-vault
EncryptionKeyArn:
Fn::ImportValue: replica-kms
replica-kms.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: Ap-northeast-3 Replica Kms
Resources:
KmsKey:
Type: AWS::KMS::ReplicaKey
Properties:
PrimaryKeyArn: 東京リージョンのプライマリキーのArn
KeyPolicy:
Version: 2012-10-17
Id: key-policy
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action: "*"
Resource: "*"
- Sid: Allow administration of the key # 適宜ユーザかロールに権限を付与してください
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:role/xxxxx
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: "*"
KmsKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/replica-kms
TargetKeyId: !Ref KmsKey
Outputs:
KmsKey:
Value: !GetAtt KmsKey.Arn
Export:
Name: replica-kms
上記のCloudFormationのテンプレートを元に、設定値をParametersで指定するようにしたり、Mappingsで設定値をまとめたりしてアレンジしてください。
以下の手順でデプロイしてください。
上記のCloudFormationのテンプレートからいくつかのポイントをピックアップして解説します。
まずはKMSから解説します。
Resources:
KmsKey:
Type: AWS::KMS::Key
Properties:
MultiRegion: true
今回、大阪リージョンにレプリカキーを作成するため、「MultiRegion」を「true」にします。
この設定は鍵作成時にしか設定できないため、要注意です。
キーポリシーは公式ドキュメントのポリシーをほぼそのまま利用しています。
適宜アクションやリソースを変更してください。
Create a symmetric encryption KMS key
Outputs:
KmsKey:
Value: !GetAtt KmsKey.Arn
Export:
Name: primary-kms
東京リージョンのAWS Backupデプロイ時にプライマリーキーのArnが必要なため、Outputsを利用しエクスポートします。
Resources:
KmsKey:
Type: AWS::KMS::ReplicaKey
Properties:
PrimaryKeyArn: 東京リージョンのプライマリキーのArn
レプリカキーを作成するには「AWS::KMS::ReplicaKey」Typeを利用します。
その際、「PrimaryKeyArn」で東京リージョンのプライマリキーを指定するのですが、
基本的にリージョン間でクロススタック参照を行うことが出来ないためArnを直接入力しています。
しかし、SSMパラメータストアを利用したり、AWS CDKを利用することでリージョン間でクロススタック参照する方法があります。
その方法を解説した記事を紹介しておきます。
リージョン間のCloudFormationクロススタック参照を実現する
AWS CDKでリージョン間のクロススタック参照を簡単に実現!cdk-remote-stackを試してみた
Outputs:
KmsKey:
Value: !GetAtt KmsKey.Arn
Export:
Name: replica-kms
プライマリキー同様にレプリカキーのArnを、Outputsを利用してエクスポートしております。
次にAWS Backupを解説します。大阪リージョンのAWS Backupから解説します。
Resources:
BackupVault:
Type: AWS::Backup::BackupVault
Properties:
BackupVaultName: destination-backup-vault
EncryptionKeyArn:
Fn::ImportValue: replica-kms
スナップショットのコピー先としてBackup Vaultを利用しますので、
東京リージョンより先に大阪リージョンにBackup Vaultを作成します。
「EncryptionKeyArn」では、Fn::ImportValueを利用してレプリカキーのArnを参照しています。
Resources:
BackupVault:
Type: AWS::Backup::BackupVault
Properties:
BackupVaultName: source-backup-vault
EncryptionKeyArn:
Fn::ImportValue: primary-kms
大阪リージョンのBackup Vault同様に、
「EncryptionKeyArn」において、Fn::ImportValueを利用してプライマリーキーのArnを参照しています。
BackupPlan:
Type: AWS::Backup::BackupPlan
Properties:
BackupPlan:
BackupPlanName: source-backup-plan
BackupPlanRule:
- RuleName: source-backup-rule
TargetBackupVault: !Ref BackupVault
ScheduleExpression: cron(0 16 * * ? *) # JST 1:00
StartWindowMinutes: 60
CompletionWindowMinutes: 720
Lifecycle:
DeleteAfterDays: 1
CopyActions:
- DestinationBackupVaultArn: !Sub arn:aws:backup:ap-northeast-3:${AWS::AccountId}:backup-vault:destination-backup-vault
Lifecycle:
DeleteAfterDays: 1
次にBackup Planです。
「BackupPlanRule」で、詳細なBackup Planのルールを定義します。
「TargetBackupVault」では、このBackup Planのルールを適用するBackup Vaultを指定します。
つまり、東京リージョンに作成する送信元Backup Vaultを指定することになります。
「ScheduleExpression」、「StartWindowMinutes」、「CompletionWindowMinutes」は、AWS Backupのジョブのスケジュールに関するプロパティです。
「ScheduleExpression」はcron式でジョブ開始のスケジュールを指定します。
今回の例では毎日午前1時(JST)にジョブを開始するように指定しています。(UTCで指定する点、ご注意ください。)
「ScheduleExpression」で指定したジョブ開始時刻ちょうどにジョブは開始されません。
ジョブ開始時刻をコントロールしたい場合、「StartWindowMinutes」で何分以内にジョブを開始させるか指定する必要があります。(デフォルトは8時間です。)
この「StartWindowMinutes」は、最低60分以上である必要があります。
また、指定した期間内にジョブが開始されなかった場合、ジョブはエラーとなります。
ジョブの完了時刻もコントロールしたい場合、「CompletionWindowMinutes」で何分以内にジョブを完了させるか指定する必要があります。(デフォルトは7日です。)
指定した期間内にジョブが完了されなかった場合、ジョブはエラーとなります。
AWS::Backup::BackupPlan BackupRuleResourceType
「Lifecycle」では、AWS Backupによって取得されたスナップショットをいつコールドストレージに移行するか、いつ削除するか指定する事が出来ます。
今回の例では、1日後に削除するように指定しています。
AWS::Backup::BackupPlan LifecycleResourceType
「CopyActions」で、AWS Backupによって取得されたスナップショットを他のBackup Vaultにコピーすることが出来ます。
「DestinationBackupVaultArn」では、送信先Backup VaultのArnを指定します。
つまり、大阪リージョンに作成する送信先Backup Vaultを指定することになります。
また、「Lifecycle」でコピーされたスナップショットのライフサイクルも指定する事が出来ます。
東京リージョンのスナップショット同様に、1日後に削除するように指定しています。
AWS::Backup::BackupPlan CopyActionResourceType
BackupSelection:
Type: AWS::Backup::BackupSelection
Properties:
BackupPlanId: !Ref BackupPlan
BackupSelection:
SelectionName: source-backup-selection
IamRoleArn: !GetAtt BackupIamRole.Arn
Resources:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster:クラスター名
次にBackupSelectionです。
Backup Planを元にバックアップを取得するリソースを指定します。
リソースにアタッチされているタグを元にバックアップを取得するリソースを指定する方法もありますが、
今回の例では「BackupSelection」の「Resources」で直接AuroraのクラスターのArnを指定しています。
また「IamRoleArn」で、バックアップ取得の際に利用するIAMロールを指定します。
AWS::Backup::BackupSelection BackupSelectionResourceType
BackupIamRole:
Type: AWS::IAM::Role
Properties:
RoleName: source-backup-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: backup.amazonaws.com
AssumeRolePolicyDocumentに関しては、 AWS BackupのデフォルトロールであるAWSBackupDefaultServiceRoleを元にしています。
BackupIamPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: source-backup-policy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- rds:CreateDBClusterSnapshot
- rds:DescribeDBClusters
- rds:DescribeDBClusterSnapshots
- rds:AddTagsToResource
- rds:ListTagsForResource
- rds:CopyDBClusterSnapshot
- rds:ModifyDBClusterSnapshotAttribute
- rds:RestoreDBClusterFromSnapshot
Resource:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster:クラスター名*
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:subgrp:サブネットグループ名
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-pg:クラスターパラメータグループ名
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- !Sub arn:aws:rds:ap-northeast-3:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- Effect: Allow
Action:
- rds:DeleteDBClusterSnapshot
Resource:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- !Sub arn:aws:rds:ap-northeast-3:${AWS::AccountId}:cluster-snapshot:awsbackup:*
最後にIAMポリシーです。
大きく分けて「Aurora」、「AWS Backup」、「KMS」でステートメントを分けています。
それぞれの一部アクションに関してはステートメントをさらに分割しております。
まず、Auroraに関するステートメントを解説します。
大元は下記公式ドキュメントの「カスタマー管理ポリシー」(「Amazon Aurora バックアップポリシー」、「Amazon Aurora のリストアポリシー」)を参考にしております。
「AWS Backup」、「KMS」のステートメントも同じく下記公式ドキュメントを参考にしております。
AWS Backup 用の管理ポリシー
しかし、この公式ドキュメントのポリシーでは、Resourceに「"*"」を利用しております。
セキュリティ要件でResourceに「"*"」が利用出来ない場合、Resourceは具体的に指定する必要があります。
その際、参考になるのがサービス認証リファレンスです。RDSのドキュメントは下記の通りです。
Amazon RDS のアクション、リソース、および条件キー
いくつかポイントをピックアップします。
Resource:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster:クラスター名*
Auroraのクラスターを指定しているのは、もちろんAuroraからスナップショットを取得するためです。
しかし、なぜクラスター名の最後に*をつけているかと言いますと、リストアを考慮したためです。
リストアの際に元のクラスターが残っていた場合、別名でクラスターをリストアする必要があります。
例えば、{元のクラスター名}-backupといった具合です。
ここはリストアの際の運用方針にもよりますので、方針に合わせて変更してください。
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:subgrp:サブネットグループ名
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-pg:クラスターパラメータグループ名
サブネットグループ、クラスターパラメーターグループを指定しているのも、リストアのためです。
ここでリストアについて補足しますと、RDSコンソールからリストアする時とAWS Backupからリストアする時の挙動が異なります。
簡単にまとめますと、以下の点が異なります。
解説した記事を紹介しますので、どちらの方法でリストアするか検討してください。
RDSコンソールからとAWS backupからのAuroraクラスターリストア時の挙動の違い
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- !Sub arn:aws:rds:ap-northeast-3:${AWS::AccountId}:cluster-snapshot:awsbackup:*
スナップショット取得、コピーのためにスナップショットを指定します。
今回はAWS Backupによって取得されるスナップショットのため、普通のAuroraのスナップショットのArnにawsbackupが追加されています。
また最後のIDはランダム値(job-ランダム値)となるため、*で指定する必要があります。
arn:aws:rds:region:account-id:cluster-snapshot:awsbackup:*
アクセスコントロール
今回は、大阪リージョンにスナップショットをコピーしますので、大阪リージョンのスナップショットも指定しています。
- Effect: Allow
Action:
- rds:DeleteDBClusterSnapshot
Resource:
- !Sub arn:aws:rds:ap-northeast-1:${AWS::AccountId}:cluster-snapshot:awsbackup:*
- !Sub arn:aws:rds:ap-northeast-3:${AWS::AccountId}:cluster-snapshot:awsbackup:*
事故防止のため「rds:DeleteDBClusterSnapshot」アクションに関しては別ステートメントに分けています。
- Effect: Allow
Action:
- backup:DescribeBackupVault
- backup:CopyIntoBackupVault
Resource:
- !Sub arn:aws:backup:ap-northeast-1:${AWS::AccountId}:backup-vault:source-backup-vault
- !Sub arn:aws:backup:ap-northeast-3:${AWS::AccountId}:backup-vault:destination-backup-vault
AWS Backupに関するステートメントです。
大阪リージョンの送信先Backup Vaultに対して「backup:CopyIntoBackupVault」アクションを許可しています。
「災害復旧後、大阪リージョンにコピーされたスナップショットを再度東京リージョンにコピーし、東京リージョンでリストアする」という想定のため、東京リージョンの送信元Backup Vaultも指定しています。 下記のサービス認証リファレンスによると、「backup:CopyIntoBackupVault」はリソースレベルのアクセス制御が出来ません。
AWS Backup のアクション、リソース、および条件キー
しかし、「backup:DescribeBackupVault」が許可されていないBackup Vaultに対してスナップショットをコピーすることは出来ませんので、アクセス制御としては上記のステートメントでも問題ないと考えております。
厳密な制御をしたい場合は、ドキュメント通り、条件キー「aws:RequestTag/${TagKey}」で制御するようにしてください。
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
- kms:GenerateDataKey
- kms:ReEncrypt*
- kms:CreateGrant
- kms:DescribeKey
Resource:
- Fn::ImportValue: primary-kms
- 大阪リージョンのレプリカキーのArn
- Effect: Allow
Action:
- kms:DescribeKey
Resource:
- !Sub arn:aws:kms:ap-northeast-1:${AWS::AccountId}:key/*
- !Sub arn:aws:kms:ap-northeast-3:${AWS::AccountId}:key/*
Condition:
ForAnyValue:StringEquals:
kms:ResourceAliases:
- alias/aws/rds
- alias/aws/backup
KMSに関するステートメントです。
AWS Backup 用のカスタマー管理ポリシーを参考にしております。
「kms:DescribeKey」に関しては、更に別ステートメントに分けています。
スナップショットを別リージョンにコピーする際、コピー先リージョンのAWSマネージド型キーである「aws/rds」「aws/backup」を参照する必要があります。
「alias/aws/rds」「alias/aws/backup」が既にコピー先リージョンにある場合、そのArnを直接指定しても問題ありません。
ただ、AWSマネージド型キーは必要となった時点でAWSが自動作成されますので、必要最低限のリソースしかないDR環境では「aws/rds」「aws/backup」が存在せず、Arnを事前に知ることが出来ない可能性もあります。
そのため、今回の例では、ResourceではKeyIDを*で指定するが、条件キー「kms:ResourceAliases」を利用することで、「aws/rds」「aws/backup」を指定するようにしています。
もし、「aws/rds」「aws/backup」が存在しない、かつ、Arnを直接指定したい場合は、CLIでdescribe-keyコマンドを実行し、「aws/rds」「aws/backup」を作成してください。
KMS AWSマネージド型キーを作成する方法を教えてください
上手くいくとバックアップジョブが成功しています。
コピージョブも同じく成功しております。
大阪リージョンの送信先Backup Vaultを確認すると、スナップショットがコピーされている事が分かります。
大阪リージョンにコピーされたスナップショットを、東京リージョンにコピーしてみようと思います。
コピージョブが成功しました。
東京リージョンの送信元Backup Vaultを確認すると、スナップショットがコピーされている事が分かります。
(コピーされたスナップショットのIDは(copyjob-ランダム値)となります。)
このスナップショットからリストアしてみようと思います。
復元ジョブが成功しました。
RDSのコンソールを確認しますと、リストアによって新規作成されたクラスターがあります。
繰り返しとなりますが、AWS Backupからのリストアではクラスターのみの作成となる点、ご注意ください。
暗号化されたAuroraのスナップショットをAWS Backupで他リージョンにコピーするCloudFormationについて解説しました。
細かい部分は、運用方針によって変わると思いますので、運用方針に合わせて設計してください。
その際、当記事が参考になりましたら幸いです。