CT.EC2.PV.2
コントロールの説明
暗号化されていない EBS ボリュームの、EC2 インスタンスへのアタッチ(接続)を禁止します。 Security Hub CSPM の [EC2.3] は「アタッチされているボリュームが暗号化されているか」をチェックしますが、本コントロールを有効にすることで、そもそも未暗号化ボリュームを接続すること自体をブロックします。
検証環境
本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。事前に export AWS_PROFILE=Workload でデフォルトを設定しておくと便利。
検証の流れ
flowchart LR
A[1. 事前準備] --> B[2. 未暗号化EBS<br>アタッチ成功]
B --> C[3. Security Hub EC2.3<br>FAILED 検出]
C --> D[4. CT.EC2.PV.2<br>有効化]
D --> E[5. 既存アタッチ<br>維持を確認]
D --> F[6. Security Hub EC2.3<br>FAILED のまま]
D --> G[7. 未暗号化EBS<br>アタッチ拒否]
D --> H[7. 暗号化EBS<br>アタッチ成功]
E & F & G & H --> I[8. CloudTrail<br>で確認]
I --> J[9. CT.EC2.PV.2<br>無効化]
J --> K[10. 未暗号化EBS<br>アタッチ成功]
結果
- コントロールの有効化前、未暗号化 EBS ボリュームをインスタンスに正常にアタッチでき、Security Hub [EC2.3] で「FAILED」として検出されることを確認できた。
- コントロールの有効化後、未暗号化 EBS ボリュームのアタッチ操作が拒否されることを確認できた。
- 有効化前にアタッチ済みの未暗号化ボリュームについては、有効化後も接続が維持され、強制的なデタッチ(取り外し)は行われないことを確認できた。
- コントロール有効化後も Security Hub [EC2.3] は FAILED のまま変化しなかった。SCP は既存のアタッチメントを変更しないため、Config の評価結果も NON_COMPLIANT のままである。
- 暗号化済み EBS ボリュームのアタッチは制限されず、正常にアタッチできることを確認できた。
- アタッチ拒否時、「explicit deny in a service control policy」というメッセージが表示されるため、SCP による制御であることを確認できた。
- 拒否されたイベントが CloudTrail に記録されること、および管理アカウントの Organization Trail(CloudWatch Logs)からも確認できることを確認できた。
- コントロールの無効化後、再び未暗号化ボリュームのアタッチが可能になることを確認できた。
1. 事前準備
EBS のデフォルト暗号化設定がオフであることを確認する。
aws ec2 get-ebs-encryption-by-default{
"EbsEncryptionByDefault": false
}検証用の EC2 インスタンスを起動する。
aws ec2 run-instances \
--image-id <AMI ID> \
--instance-type t3.micro \
--subnet-id <プライベートサブネット ID> \
--no-associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ct-ec2-pv2-test}]' \
--query 'Instances[0].[InstanceId,State.Name,Placement.AvailabilityZone]' \
--output text<インスタンス ID> pending ap-northeast-1aインスタンスが起動したことを確認する。
aws ec2 describe-instances \
--instance-ids <インスタンス ID> \
--query 'Reservations[0].Instances[0].[InstanceId,State.Name]' \
--output text<インスタンス ID> running暗号化なしの EBS ボリュームを作成する(EC2 と同じ AZ に配置すること)。
aws ec2 create-volume \
--availability-zone ap-northeast-1a \
--size 1 \
--volume-type gp3 \
--no-encrypted \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ct-ec2-pv2-unencrypted}]' \
--query '[VolumeId,Encrypted,State]' \
--output text<未暗号化ボリューム 1 ID> False creatingボリュームが利用可能になったことを確認する。
aws ec2 describe-volumes \
--volume-ids <未暗号化ボリューム 1 ID> \
--query 'Volumes[0].[VolumeId,Encrypted,State]' \
--output text<未暗号化ボリューム 1 ID> False available2. コントロール有効化前の確認
コントロール有効化前の状態で、未暗号化 EBS ボリュームをインスタンスにアタッチする。
aws ec2 attach-volume \
--volume-id <未暗号化ボリューム 1 ID> \
--instance-id <インスタンス ID> \
--device /dev/sdf{
"VolumeId": "<未暗号化ボリューム 1 ID>",
"InstanceId": "<インスタンス ID>",
"Device": "/dev/sdf",
"State": "attaching",
"AttachTime": "<アタッチ日時>"
}アタッチが正常に完了したことを確認する。
ステップ 5 で「有効化後も既存のアタッチメントが維持されるか」を確認するため、このボリュームはアタッチしたままにしておく。
3. Security Hub CSPM での検出確認(EC2.3 FAILED)
Security Hub で [EC2.3] の検出結果を確認する。未暗号化ボリュームがアタッチされているため、「FAILED」として検出される。
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Value": "EC2.3", "Comparison": "EQUALS"}],
"ComplianceStatus": [{"Value": "FAILED", "Comparison": "EQUALS"}],
"AwsAccountId": [{"Value": "<Workload のアカウント ID>", "Comparison": "EQUALS"}]
}' \
--query 'Findings[*].{Status:Compliance.Status,ControlId:Compliance.SecurityControlId,ResourceId:Resources[0].Id,Description:Description}'[
{
"Status": "FAILED",
"ControlId": "EC2.3",
"ResourceId": "arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<未暗号化ボリューム 1 ID>",
"Description": "This AWS control checks whether the EBS volumes that are in an attached state are encrypted."
}
]4. 対象の予防コントロールを有効化(例外設定なし)
Control Tower の管理アカウントで、コントロール CT.EC2.PV.2 を有効化する。
まず、対象の OU にコントロールが有効になっていないことを確認する。
aws controltower list-enabled-controls \
--target-identifier <OU の ARN> \
--profile Master{
"enabledControls": []
}コントロールを有効化する。コントロールの ARN は Control Catalog から取得できる。
aws controltower enable-control \
--control-identifier arn:aws:controlcatalog:::control/dkjyeuczqj3rnyhn9116p16pw \
--target-identifier <OU の ARN> \
--profile Master{
"arn": "arn:aws:controltower:ap-northeast-1:<管理アカウント ID>:enabledcontrol/<enabledcontrol ID>",
"operationIdentifier": "<オペレーション ID>"
}有効化が完了するまで待機する。
aws controltower get-control-operation \
--operation-identifier <オペレーション ID> \
--query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
--profile Master{
"operationType": "ENABLE_CONTROL",
"status": "SUCCEEDED",
"statusMessage": "Operation was successful."
}5. 有効化後の挙動確認
有効化前にアタッチ済みの未暗号化ボリュームが、有効化後も接続が維持されていることを確認する。新規操作のみを制御し、既存のリソース状態には影響しない。
aws ec2 describe-volumes \
--volume-ids <未暗号化ボリューム 1 ID> \
--query 'Volumes[0].{VolumeId:VolumeId,Encrypted:Encrypted,State:State,Attachments:Attachments[0].{InstanceId:InstanceId,State:State,Device:Device}}'{
"VolumeId": "<未暗号化ボリューム 1 ID>",
"Encrypted": false,
"State": "in-use",
"Attachments": {
"InstanceId": "<インスタンス ID>",
"State": "attached",
"Device": "/dev/sdf"
}
}6. Security Hub EC2.3 が FAILED のまま変化しないことの確認
SCP は既存のアタッチメントを変更しないため、コントロール有効化後も Security Hub [EC2.3] は FAILED のまま変化しない。Config ルールの手動トリガー後、Config の評価結果と Security Hub の検出結果の両方で確認する。
まず Config ルールの評価を手動トリガーする。
aws configservice start-config-rules-evaluation \
--config-rule-names <Config ルール名>Config の評価結果を直接確認する。
aws configservice get-compliance-details-by-config-rule \
--config-rule-name <Config ルール名> \
--compliance-types NON_COMPLIANT \
--query 'EvaluationResults[*].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}'[
{
"ResourceId": "<未暗号化ボリューム 1 ID>",
"ComplianceType": "NON_COMPLIANT",
"ResultRecordedTime": "<評価完了時刻>"
}
]Config レベルで NON_COMPLIANT のままであることを確認できた。
さらに 5 分以上待機した後、Security Hub でも FAILED のまま変化しないことを確認する。
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Value": "EC2.3", "Comparison": "EQUALS"}],
"ComplianceStatus": [{"Value": "FAILED", "Comparison": "EQUALS"}],
"ResourceId": [{"Value": "arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<未暗号化ボリューム 1 ID>", "Comparison": "EQUALS"}]
}' \
--query 'Findings[0].{Status:Compliance.Status,ControlId:Compliance.SecurityControlId,ResourceId:Resources[0].Id}'{
"Status": "FAILED",
"ControlId": "EC2.3",
"ResourceId": "arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<未暗号化ボリューム 1 ID>"
}Config ルール手動トリガーから 5 分以上経過しても FAILED のまま変化しなかった。SCP は既存のアタッチメントを変更しないため、Config の評価結果も NON_COMPLIANT のままであり、反映ラグではなく本当に状態が変わらないことが確認できた。
7. 新規操作は制限(拒否)されることの確認
コントロール有効化後、新たに未暗号化 EBS ボリュームを作成し、アタッチを試みる。
aws ec2 create-volume \
--availability-zone ap-northeast-1a \
--size 1 \
--volume-type gp3 \
--no-encrypted \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ct-ec2-pv2-unencrypted-2}]' \
--query '[VolumeId,Encrypted,State]' \
--output text<未暗号化ボリューム 2 ID> False creatingボリュームが利用可能になったことを確認する。
aws ec2 describe-volumes \
--volume-ids <未暗号化ボリューム 2 ID> \
--query 'Volumes[0].[VolumeId,Encrypted,State]' \
--output text<未暗号化ボリューム 2 ID> False availableaws ec2 attach-volume \
--volume-id <未暗号化ボリューム 2 ID> \
--instance-id <インスタンス ID> \
--device /dev/sdg以下のようなエラーが返り、操作が拒否されることを確認する。
An error occurred (UnauthorizedOperation) when calling the AttachVolume operation:
You are not authorized to perform this operation.
User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<ユーザー名>
is not authorized to perform: ec2:AttachVolume on resource:
arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<未暗号化ボリューム 2 ID>
with an explicit deny in a service control policyなお、暗号化済みの EBS ボリュームであれば、問題なくアタッチできることも合わせて確認する。
aws ec2 create-volume \
--availability-zone ap-northeast-1a \
--size 1 \
--volume-type gp3 \
--encrypted \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ct-ec2-pv2-encrypted}]' \
--query '[VolumeId,Encrypted,State]' \
--output text<暗号化ボリューム ID> True creatingボリュームが利用可能になったことを確認する。
aws ec2 describe-volumes \
--volume-ids <暗号化ボリューム ID> \
--query 'Volumes[0].[VolumeId,Encrypted,State]' \
--output text<暗号化ボリューム ID> True available暗号化済みボリュームをアタッチする。
aws ec2 attach-volume \
--volume-id <暗号化ボリューム ID> \
--instance-id <インスタンス ID> \
--device /dev/sdg{
"VolumeId": "<暗号化ボリューム ID>",
"InstanceId": "<インスタンス ID>",
"Device": "/dev/sdg",
"State": "attaching",
"AttachTime": "<アタッチ日時>"
}8. CloudTrail での確認
拒否されたイベントが CloudTrail に記録されていることを確認する。
Workload アカウントの CloudTrail イベント履歴から確認する。
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=AttachVolume \
--start-time <検索開始時刻> \
--end-time <検索終了時刻> \
--query 'Events[].CloudTrailEvent' \
--output text | jq 'select(.errorCode != null) | {eventName, errorCode, errorMessage}'{
"eventName": "AttachVolume",
"errorCode": "Client.UnauthorizedOperation",
"errorMessage": "You are not authorized to perform this operation. User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名> is not authorized to perform: ec2:AttachVolume on resource: arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<未暗号化ボリューム 2 ID> with an explicit deny in a service control policy: arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/service_control_policy/<ポリシー ID>. Encoded authorization failure message: ..."
}errorMessage に explicit deny in a service control policy と記録されており、SCP による拒否であることがわかる。
管理アカウントの Organization Trail(CloudWatch Logs)からも同じイベントを確認できる。
aws logs start-query \
--log-group-name "aws-controltower/CloudTrailLogs" \
--start-time $(date -d '<検索開始時刻>' +%s) \
--end-time $(date -d '<検索終了時刻>' +%s) \
--query-string 'fields @timestamp, eventName, errorCode, errorMessage
| filter eventName = "AttachVolume" and errorCode like /UnauthorizedOperation|AccessDenied/
| sort @timestamp asc' \
--profile Master{
"queryId": "<クエリ ID>"
}上記で返された queryId を指定して結果を取得する。
aws logs get-query-results --query-id <クエリ ID> \
--query '{results:results,status:status}' \
--profile Master{
"results": [
[
{"field": "@timestamp", "value": "2026-03-14 11:44:08.265"},
{"field": "eventName", "value": "AttachVolume"},
{"field": "errorCode", "value": "Client.UnauthorizedOperation"},
{"field": "errorMessage", "value": "You are not authorized to perform this operation. User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名> is not authorized to perform: ec2:AttachVolume on resource: arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<未暗号化ボリューム 2 ID> with an explicit deny in a service control policy: arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/service_control_policy/<ポリシー ID>. Encoded authorization failure message: ..."},
{"field": "@ptr", "value": "..."}
]
],
"status": "Complete"
}管理アカウントの CloudWatch Logs にも同じ拒否イベントが記録されていることを確認できた。
9. 対象の予防コントロールを無効化
検証完了後、コントロールを無効化する。
aws controltower disable-control \
--control-identifier arn:aws:controlcatalog:::control/dkjyeuczqj3rnyhn9116p16pw \
--target-identifier <OU の ARN> \
--profile Master{
"operationIdentifier": "<オペレーション ID>"
}ステータスが無効になるまで待機する。
aws controltower get-control-operation \
--operation-identifier <オペレーション ID> \
--query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
--profile Master{
"operationType": "DISABLE_CONTROL",
"status": "SUCCEEDED",
"statusMessage": "Operation was successful."
}10. 制限解除の確認
コントロール無効化後、再び未暗号化 EBS ボリュームのアタッチを試みる。
aws ec2 attach-volume \
--volume-id <未暗号化ボリューム 2 ID> \
--instance-id <インスタンス ID> \
--device /dev/sdhアタッチが正常に完了し、制限が解除されたことを確認する。
{
"VolumeId": "<未暗号化ボリューム 2 ID>",
"InstanceId": "<インスタンス ID>",
"Device": "/dev/sdh",
"State": "attaching",
"AttachTime": "<アタッチ日時>"
}11. クリーンアップ
検証で作成したリソースをすべて削除する。
# EBS ボリュームをデタッチ
aws ec2 detach-volume --volume-id <未暗号化ボリューム 1 ID>
aws ec2 detach-volume --volume-id <未暗号化ボリューム 2 ID>
aws ec2 detach-volume --volume-id <暗号化ボリューム ID>
# EBS ボリュームを削除(デタッチ完了後)
aws ec2 delete-volume --volume-id <未暗号化ボリューム 1 ID>
aws ec2 delete-volume --volume-id <未暗号化ボリューム 2 ID>
aws ec2 delete-volume --volume-id <暗号化ボリューム ID>
# EC2 インスタンスを終了
aws ec2 terminate-instances --instance-ids <インスタンス ID>補足: Security Hub の検出を早める方法
Security Hub のコンプライアンスチェックはスケジュールベース(最大 12 時間間隔)で実行されるため、リソースの変更が即座に反映されない場合がある。検証時など、すぐに検出結果を確認したい場合は、対応する AWS Config ルールの評価を手動でトリガーできる。
Security Hub のコントロールは内部的に AWS Config ルールとして実装されている。EC2.3 に対応する Config ルールは SourceIdentifier が ENCRYPTED_VOLUMES なので、これを条件にルール名を取得する。
aws configservice describe-config-rules \
--query 'ConfigRules[?Source.SourceIdentifier==`ENCRYPTED_VOLUMES`].ConfigRuleName'出力例(末尾のハッシュ値は環境ごとに異なる):
[
"securityhub-encrypted-volumes-df779f3f"
]取得したルール名を指定して、評価を手動トリガーする。
aws configservice start-config-rules-evaluation \
--config-rule-names <上記で取得したルール名>数十秒〜数分程度で評価が完了し、結果が Security Hub に反映される。
なお、Security Hub への反映を待たずに Config ルールの評価結果を直接確認することもできる。
aws configservice get-compliance-details-by-config-rule \
--config-rule-name <上記で取得したルール名> \
--query 'EvaluationResults[*].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}'[
{
"ResourceId": "arn:aws:ec2:ap-northeast-1:<Workload のアカウント ID>:volume/<ボリューム ID>",
"ComplianceType": "NON_COMPLIANT",
"ResultRecordedTime": "<評価完了時刻>"
}
]Config ルールの評価は手動トリガー後すぐに完了するが、Config から Security Hub への反映にはさらに数分かかる。Security Hub が FAILED に変わらない場合は、まず Config の評価結果を確認することで、問題が Config 側か反映ラグかを切り分けられる。