RDS スナップショット
概要
IAM Access Analyzer が RDS スナップショットの外部アクセスをどのように検出するかを検証する。本記事では DB スナップショット(AWS::RDS::DBSnapshot)とクラスタースナップショット(AWS::RDS::DBClusterSnapshot)の両方を検証する。
RDS スナップショットは modify-db-snapshot-attribute(DB スナップショット)または modify-db-cluster-snapshot-attribute(クラスタースナップショット)により、restore 属性に all を指定するとパブリック共有、特定のアカウント ID を指定するとクロスアカウント共有となる。S3 や EBS のような Block Public Access の仕組みは存在しない。デフォルトでは共有設定がなく、明示的に設定しない限り外部アクセスは発生しない。
RDS スナップショットの外部アクセス制御レイヤー
| レイヤー | RDS スナップショットの状況 |
|---|---|
| デフォルト保護 | なし(Block Public Access なし) |
| 予防(SCP / RCP) | Control Tower 予防コントロールなし |
| Security Hub CSPM | RDS.1(パブリック共有のチェック) |
| IAM Access Analyzer | パブリック + クロスアカウントの到達可能性を分析 |
結果
- パブリックアクセスの検出: DB スナップショット・クラスタースナップショットともに、
restore属性にallを設定するとisPublic: trueの finding が生成された。組織外アカウントからの復元(restore-db-instance-from-db-snapshot/restore-db-cluster-from-snapshot)も成功した - クロスアカウントアクセスの検出:
restore属性に組織外アカウント ID を設定するとisPublic: falseの finding が生成された。principalに組織外アカウント ID が特定された。組織外アカウントからの復元も成功した - DB スナップショットとクラスタースナップショットの違い:
resourceType(AWS::RDS::DBSnapshot/AWS::RDS::DBClusterSnapshot)とaction(rds:RestoreDBInstanceFromDBSnapshot/rds:RestoreDBClusterFromSnapshot等)が異なる。検出の仕組みは同一 - resourceControlPolicyRestriction: RDS スナップショットに対応する RCP 型予防コントロールは存在しないため、すべて
NOT_APPLICABLE - 共有解除による finding の解消:
restore属性から共有先を削除すると finding は RESOLVED になった
検証環境
本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。
検証の流れ
flowchart LR
A[1. 既存 finding の<br>確認] --> B[2. テスト用<br>スナップショット作成]
B --> C[3. DB スナップショット<br>の検出]
C --> D[4. クラスター<br>スナップショットの検出]
D --> E[5. クリーンアップ]
1. 既存 finding の確認
テスト用スナップショットを作成する前に、既存の RDS スナップショット finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::RDS::DBSnapshot"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::RDS::DBClusterSnapshot"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]2. テスト用スナップショットの作成
DB インスタンスと DB スナップショット
テスト用 DB インスタンスを作成する。
aws rds create-db-instance \
--db-instance-identifier iaa-rds-test \
--db-instance-class db.t4g.micro \
--engine mysql \
--master-username admin \
--master-user-password <パスワード> \
--allocated-storage 20 \
--no-multi-az \
--no-publicly-accessibleDB インスタンスが available になったら、2 つの DB スナップショットを作成する。
aws rds create-db-snapshot \
--db-instance-identifier iaa-rds-test \
--db-snapshot-identifier iaa-rds-public-test \
--query '{DBSnapshotIdentifier:DBSnapshot.DBSnapshotIdentifier,Status:DBSnapshot.Status}'{
"DBSnapshotIdentifier": "iaa-rds-public-test",
"Status": "creating"
}aws rds create-db-snapshot \
--db-instance-identifier iaa-rds-test \
--db-snapshot-identifier iaa-rds-crossaccount-test \
--query '{DBSnapshotIdentifier:DBSnapshot.DBSnapshotIdentifier,Status:DBSnapshot.Status}'{
"DBSnapshotIdentifier": "iaa-rds-crossaccount-test",
"Status": "creating"
}スナップショットが available になったら、共有を設定する。
aws rds modify-db-snapshot-attribute \
--db-snapshot-identifier iaa-rds-public-test \
--attribute-name restore \
--values-to-add all
(出力なし)aws rds modify-db-snapshot-attribute \
--db-snapshot-identifier iaa-rds-crossaccount-test \
--attribute-name restore \
--values-to-add <組織外アカウント ID>
(出力なし)Aurora クラスターとクラスタースナップショット
テスト用 Aurora クラスターを作成する。
aws rds create-db-cluster \
--db-cluster-identifier iaa-aurora-test \
--engine aurora-mysql \
--master-username admin \
--master-user-password <パスワード>クラスターが available になったら、2 つのクラスタースナップショットを作成する。
aws rds create-db-cluster-snapshot \
--db-cluster-identifier iaa-aurora-test \
--db-cluster-snapshot-identifier iaa-aurora-public-test \
--query '{DBClusterSnapshotIdentifier:DBClusterSnapshot.DBClusterSnapshotIdentifier,Status:DBClusterSnapshot.Status}'{
"DBClusterSnapshotIdentifier": "iaa-aurora-public-test",
"Status": "creating"
}aws rds create-db-cluster-snapshot \
--db-cluster-identifier iaa-aurora-test \
--db-cluster-snapshot-identifier iaa-aurora-crossaccount-test \
--query '{DBClusterSnapshotIdentifier:DBClusterSnapshot.DBClusterSnapshotIdentifier,Status:DBClusterSnapshot.Status}'{
"DBClusterSnapshotIdentifier": "iaa-aurora-crossaccount-test",
"Status": "creating"
}スナップショットが available になったら、共有を設定する。
aws rds modify-db-cluster-snapshot-attribute \
--db-cluster-snapshot-identifier iaa-aurora-public-test \
--attribute-name restore \
--values-to-add all
(出力なし)aws rds modify-db-cluster-snapshot-attribute \
--db-cluster-snapshot-identifier iaa-aurora-crossaccount-test \
--attribute-name restore \
--values-to-add <組織外アカウント ID>
(出力なし)3. DB スナップショットの検出
パブリックアクセスの検出
パブリック共有 DB スナップショットの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-rds-public-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<DB パブリックスナップショット finding ID>",
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:snapshot:iaa-rds-public-test",
"resourceType": "AWS::RDS::DBSnapshot",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <DB パブリックスナップショット finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"rds:CopyDBSnapshot",
"rds:DescribeDBSnapshots",
"rds:RestoreDBInstanceFromDBSnapshot"
],
"condition": {},
"isPublic": true,
"principal": {"AWS": "*"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:snapshot:iaa-rds-public-test",
"status": "ACTIVE",
"resourceType": "AWS::RDS::DBSnapshot",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<DB パブリックスナップショット finding ID>"
}以下を確認する。
isPublicがtrueprincipalが{"AWS": "*"}actionにrds:RestoreDBInstanceFromDBSnapshot等が含まれるresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからパブリック DB スナップショットを使って DB インスタンスを復元できることを確認する。
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier iaa-rds-restore-public \
--db-snapshot-identifier arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:snapshot:iaa-rds-public-test \
--db-instance-class db.t4g.micro \
--query '{DBInstanceIdentifier:DBInstance.DBInstanceIdentifier,DBInstanceStatus:DBInstance.DBInstanceStatus}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"DBInstanceIdentifier": "iaa-rds-restore-public",
"DBInstanceStatus": "creating"
}確認後、組織外アカウントで復元した DB インスタンスを削除する。
aws rds delete-db-instance \
--db-instance-identifier iaa-rds-restore-public \
--skip-final-snapshot \
--profile <組織外プロファイル> \
--region ap-northeast-1
(出力なし)クロスアカウントアクセスの検出
クロスアカウント共有 DB スナップショットの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-rds-crossaccount-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<DB クロスアカウントスナップショット finding ID>",
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:snapshot:iaa-rds-crossaccount-test",
"resourceType": "AWS::RDS::DBSnapshot",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <DB クロスアカウントスナップショット finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"rds:CopyDBSnapshot",
"rds:DescribeDBSnapshots",
"rds:RestoreDBInstanceFromDBSnapshot"
],
"condition": {},
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:snapshot:iaa-rds-crossaccount-test",
"status": "ACTIVE",
"resourceType": "AWS::RDS::DBSnapshot",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<DB クロスアカウントスナップショット finding ID>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionにrds:RestoreDBInstanceFromDBSnapshot等が含まれるresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからクロスアカウント DB スナップショットを使って DB インスタンスを復元できることを確認する。
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier iaa-rds-restore-crossaccount \
--db-snapshot-identifier arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:snapshot:iaa-rds-crossaccount-test \
--db-instance-class db.t4g.micro \
--query '{DBInstanceIdentifier:DBInstance.DBInstanceIdentifier,DBInstanceStatus:DBInstance.DBInstanceStatus}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"DBInstanceIdentifier": "iaa-rds-restore-crossaccount",
"DBInstanceStatus": "creating"
}確認後、組織外アカウントで復元した DB インスタンスを削除する。
aws rds delete-db-instance \
--db-instance-identifier iaa-rds-restore-crossaccount \
--skip-final-snapshot \
--profile <組織外プロファイル> \
--region ap-northeast-1
(出力なし)4. クラスタースナップショットの検出
パブリックアクセスの検出
パブリック共有クラスタースナップショットの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-aurora-public-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<クラスターパブリックスナップショット finding ID>",
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:cluster-snapshot:iaa-aurora-public-test",
"resourceType": "AWS::RDS::DBClusterSnapshot",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <クラスターパブリックスナップショット finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"rds:CopyDBClusterSnapshot",
"rds:DescribeDBClusterSnapshots",
"rds:RestoreDBClusterFromSnapshot"
],
"condition": {},
"isPublic": true,
"principal": {"AWS": "*"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:cluster-snapshot:iaa-aurora-public-test",
"status": "ACTIVE",
"resourceType": "AWS::RDS::DBClusterSnapshot",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<クラスターパブリックスナップショット finding ID>"
}DB スナップショットとの違い:
resourceTypeがAWS::RDS::DBClusterSnapshotactionがrds:CopyDBClusterSnapshot,rds:DescribeDBClusterSnapshots,rds:RestoreDBClusterFromSnapshot(DB スナップショットとは異なるアクション名)
以下を確認する。
isPublicがtrueprincipalが{"AWS": "*"}actionにrds:RestoreDBClusterFromSnapshot等が含まれるresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからパブリッククラスタースナップショットを使ってクラスターを復元できることを確認する。
aws rds restore-db-cluster-from-snapshot \
--db-cluster-identifier iaa-aurora-restore-public \
--snapshot-identifier arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:cluster-snapshot:iaa-aurora-public-test \
--engine aurora-mysql \
--query '{DBClusterIdentifier:DBCluster.DBClusterIdentifier,Status:DBCluster.Status}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"DBClusterIdentifier": "iaa-aurora-restore-public",
"Status": "creating"
}確認後、組織外アカウントで復元したクラスターを削除する。
aws rds delete-db-cluster \
--db-cluster-identifier iaa-aurora-restore-public \
--skip-final-snapshot \
--profile <組織外プロファイル> \
--region ap-northeast-1
(出力なし)クロスアカウントアクセスの検出
クロスアカウント共有クラスタースナップショットの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-aurora-crossaccount-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<クラスタークロスアカウントスナップショット finding ID>",
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:cluster-snapshot:iaa-aurora-crossaccount-test",
"resourceType": "AWS::RDS::DBClusterSnapshot",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <クラスタークロスアカウントスナップショット finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"rds:CopyDBClusterSnapshot",
"rds:DescribeDBClusterSnapshots",
"rds:RestoreDBClusterFromSnapshot"
],
"condition": {},
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:cluster-snapshot:iaa-aurora-crossaccount-test",
"status": "ACTIVE",
"resourceType": "AWS::RDS::DBClusterSnapshot",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<クラスタークロスアカウントスナップショット finding ID>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionにrds:RestoreDBClusterFromSnapshot等が含まれるresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからクロスアカウントクラスタースナップショットを使ってクラスターを復元できることを確認する。
aws rds restore-db-cluster-from-snapshot \
--db-cluster-identifier iaa-aurora-restore-crossaccount \
--snapshot-identifier arn:aws:rds:ap-northeast-1:<Workload アカウント ID>:cluster-snapshot:iaa-aurora-crossaccount-test \
--engine aurora-mysql \
--query '{DBClusterIdentifier:DBCluster.DBClusterIdentifier,Status:DBCluster.Status}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"DBClusterIdentifier": "iaa-aurora-restore-crossaccount",
"Status": "creating"
}確認後、組織外アカウントで復元したクラスターを削除する。
aws rds delete-db-cluster \
--db-cluster-identifier iaa-aurora-restore-crossaccount \
--skip-final-snapshot \
--profile <組織外プロファイル> \
--region ap-northeast-1
(出力なし)5. クリーンアップ
共有の解除
DB スナップショットの共有を解除する。
aws rds modify-db-snapshot-attribute \
--db-snapshot-identifier iaa-rds-public-test \
--attribute-name restore \
--values-to-remove all
(出力なし)aws rds modify-db-snapshot-attribute \
--db-snapshot-identifier iaa-rds-crossaccount-test \
--attribute-name restore \
--values-to-remove <組織外アカウント ID>
(出力なし)クラスタースナップショットの共有を解除する。
aws rds modify-db-cluster-snapshot-attribute \
--db-cluster-snapshot-identifier iaa-aurora-public-test \
--attribute-name restore \
--values-to-remove all
(出力なし)aws rds modify-db-cluster-snapshot-attribute \
--db-cluster-snapshot-identifier iaa-aurora-crossaccount-test \
--attribute-name restore \
--values-to-remove <組織外アカウント ID>
(出力なし)finding の確認
共有解除後、finding が RESOLVED になることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::RDS::DBSnapshot"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::RDS::DBClusterSnapshot"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]テスト用リソースの削除
aws rds delete-db-snapshot \
--db-snapshot-identifier iaa-rds-public-test
(出力なし)aws rds delete-db-snapshot \
--db-snapshot-identifier iaa-rds-crossaccount-test
(出力なし)aws rds delete-db-cluster-snapshot \
--db-cluster-snapshot-identifier iaa-aurora-public-test
(出力なし)aws rds delete-db-cluster-snapshot \
--db-cluster-snapshot-identifier iaa-aurora-crossaccount-test
(出力なし)aws rds delete-db-instance \
--db-instance-identifier iaa-rds-test \
--skip-final-snapshot
(出力なし)aws rds delete-db-cluster \
--db-cluster-identifier iaa-aurora-test \
--skip-final-snapshot
(出力なし)