EC2.9
--profile 指定がない場合は Workload アカウントで実行する。export AWS_PROFILE=Workload でデフォルトを設定しておくと便利。Control Tower の有効化・無効化操作(ステップ 6・8)は --profile Master(Organizations 管理アカウント)で実行する。
コントロールの説明
EC2 インスタンスにパブリック IPv4 アドレスが割り当てられていないかをチェックする。インスタンスの設定に publicIp フィールドが存在する場合に FAILED となる。IPv4 のみが対象(IPv6 アドレスのみを持つインスタンスは本コントロールの対象外)。
対応する Config ルール: EC2_INSTANCE_NO_PUBLIC_IP(評価頻度: 変更トリガー)
結果
- パブリック IP を持つ EC2 インスタンスが存在しない場合は PASSED。ResourceId は
AWS::::Account(評価対象リソースなし) - パブリック IP 付きインスタンスを起動すると FAILED。ResourceId はインスタンス ARN
- インスタンスを終了すると PASSED に復帰。ResourceId は再び
AWS::::Accountに戻る - CT.EC2.PV.8(VPC BPA、Declarative Policy 型)を有効化しても finding は FAILED のまま変化しない。VPC BPA はインターネットアクセスをブロックするが、パブリック IP の付与自体は止めないため
検証の流れ
flowchart LR
A[1. デフォルト状態の確認<br>PASSED] --> B[2. パブリック IP 付き<br>インスタンス起動]
B --> C[3. FAILED 確認]
C --> D[4. インスタンス終了]
D --> E[5. PASSED 復帰確認]
E --> F[6. CT.EC2.PV.8 有効化]
F --> G[7. FAILED のまま変化しないことを確認]
G --> H[8. クリーンアップ]
1. デフォルト状態の確認
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.9"}],
"AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
"WorkflowStatus": [{"Comparison": "NOT_EQUALS", "Value": "SUPPRESSED"}],
"RecordState": [{"Comparison": "EQUALS", "Value": "ACTIVE"}]
}' \
--query 'Findings[].{Status:Compliance.Status,ResourceId:Resources[0].Id}' \
--region ap-northeast-1[
{
"Status": "PASSED",
"ResourceId": "AWS::::Account:<アカウント ID>"
}
]パブリック IP を持つインスタンスが存在しないため PASSED。評価対象リソース(インスタンス)が存在しない場合、Config の評価結果は空となり、Security Hub はアカウントレベルの PASSED(AWS::::Account)を返す。インスタンスを起動すると、インスタンス単位の finding が生成される。
2. パブリック IP 付きインスタンス起動
デフォルト VPC のサブネットはパブリック IP の自動割り当てが有効なため、デフォルト VPC で起動する。
デフォルト VPC の確認
aws ec2 describe-vpcs \
--filters Name=isDefault,Values=true \
--query 'Vpcs[].{VpcId:VpcId,CidrBlock:CidrBlock}' \
--region ap-northeast-1[
{
"VpcId": "<デフォルト VPC ID>",
"CidrBlock": "172.31.0.0/16"
}
]デフォルト VPC のサブネット確認
aws ec2 describe-subnets \
--filters Name=vpc-id,Values=<デフォルト VPC ID> Name=availabilityZone,Values=ap-northeast-1a \
--query 'Subnets[].{SubnetId:SubnetId,MapPublicIpOnLaunch:MapPublicIpOnLaunch}' \
--region ap-northeast-1[
{
"SubnetId": "<デフォルトサブネット ID 1>",
"MapPublicIpOnLaunch": false
},
{
"SubnetId": "<デフォルトサブネット ID 2>",
"MapPublicIpOnLaunch": true
}
]MapPublicIpOnLaunch: true のサブネット(<デフォルトサブネット ID 2>)を使用する。
最新の Amazon Linux 2023 AMI を取得
aws ec2 describe-images \
--owners amazon \
--filters Name=name,Values=al2023-ami-2023*-x86_64 Name=state,Values=available \
--query 'sort_by(Images,&CreationDate)[-1].ImageId' \
--region ap-northeast-1"<AMI ID>"インスタンス起動
aws ec2 run-instances \
--image-id <AMI ID> \
--instance-type t3.micro \
--subnet-id <デフォルトサブネット ID 2> \
--associate-public-ip-address \
--query 'Instances[].{InstanceId:InstanceId,State:State.Name,PublicIpAddress:PublicIpAddress}' \
--region ap-northeast-1[
{
"InstanceId": "<インスタンス ID>",
"State": "pending",
"PublicIpAddress": null
}
]インスタンスの running 確認
aws ec2 describe-instances \
--instance-ids <インスタンス ID> \
--query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,PublicIpAddress:PublicIpAddress}' \
--region ap-northeast-1[
{
"InstanceId": "<インスタンス ID>",
"State": "running",
"PublicIpAddress": "<パブリック IP アドレス>"
}
]3. FAILED 確認
Config ルールの手動トリガー
aws configservice start-config-rules-evaluation \
--config-rule-names securityhub-ec2-instance-no-public-ip-<サフィックス> \
--region ap-northeast-1
(出力なし)Config 評価結果の確認
aws configservice get-compliance-details-by-config-rule \
--config-rule-name securityhub-ec2-instance-no-public-ip-<サフィックス> \
--query 'EvaluationResults[?EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId==`<インスタンス ID>`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType}' \
--region ap-northeast-1[
{
"ResourceId": "<インスタンス ID>",
"ComplianceType": "NON_COMPLIANT"
}
]Security Hub finding の FAILED 確認
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.9"}],
"AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
"WorkflowStatus": [{"Comparison": "NOT_EQUALS", "Value": "SUPPRESSED"}],
"RecordState": [{"Comparison": "EQUALS", "Value": "ACTIVE"}]
}' \
--query 'Findings[].{Status:Compliance.Status,ResourceId:Resources[0].Id}' \
--region ap-northeast-1[
{
"Status": "FAILED",
"ResourceId": "arn:aws:ec2:ap-northeast-1:<アカウント ID>:instance/<インスタンス ID>"
},
{
"Status": "PASSED",
"ResourceId": "AWS::::Account:<アカウント ID>"
}
]インスタンス単位で FAILED が生成される。ResourceId はインスタンス ARN。AWS::::Account の PASSED finding はインスタンスが存在しない状態の finding が別 entity として並存しているもの。
4. インスタンス終了
aws ec2 terminate-instances \
--instance-ids <インスタンス ID> \
--query 'TerminatingInstances[].{InstanceId:InstanceId,CurrentState:CurrentState.Name}' \
--region ap-northeast-1[
{
"InstanceId": "<インスタンス ID>",
"CurrentState": "shutting-down"
}
]terminated 確認
aws ec2 describe-instances \
--instance-ids <インスタンス ID> \
--query 'Reservations[].Instances[].{State:State.Name}' \
--region ap-northeast-1[
{
"State": "terminated"
}
]5. PASSED 復帰確認
Config ルールの手動トリガー
aws configservice start-config-rules-evaluation \
--config-rule-names securityhub-ec2-instance-no-public-ip-<サフィックス> \
--region ap-northeast-1
(出力なし)Security Hub finding の PASSED 確認
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.9"}],
"AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
"WorkflowStatus": [{"Comparison": "NOT_EQUALS", "Value": "SUPPRESSED"}],
"RecordState": [{"Comparison": "EQUALS", "Value": "ACTIVE"}]
}' \
--query 'Findings[].{Status:Compliance.Status,ResourceId:Resources[0].Id}' \
--region ap-northeast-1[
{
"Status": "PASSED",
"ResourceId": "AWS::::Account:<アカウント ID>"
}
]インスタンスを終了すると、インスタンス ARN の FAILED finding は消え、AWS::::Account の PASSED finding のみ残る。これはインスタンスが存在しない状態(評価対象リソースなし)を表すアカウントレベルの finding が別 entity として維持されているものである。
6. CT.EC2.PV.8 有効化
CT.EC2.PV.8 は Declarative Policy 型の予防コントロールであり、VPC BPA を強制する。VPC BPA はインターネットアクセスをブロックするが、パブリック IP の付与自体は止めない。そのため EC2.9 の finding は変化しないはずである。
ステップ 4 でインスタンスを終了して PASSED 復帰を確認した後、改めてインスタンスを起動して FAILED 状態を作成する。「PASSED 復帰確認」と「CT.EC2.PV.8 の影響確認」を独立した検証として切り分けるためである。その後、Security Hub finding が FAILED であることを確認してから CT.EC2.PV.8 を有効化する。
run-instances に --security-group-ids を指定しない場合、デフォルト VPC のデフォルトセキュリティグループが使用される。デフォルト SG にはインバウンドルールがないため、パブリック IP が付与されても外部からのアクセスは受けない。インスタンス起動
aws ec2 run-instances \
--image-id <AMI ID> \
--instance-type t3.micro \
--subnet-id <デフォルトサブネット ID 2> \
--associate-public-ip-address \
--query 'Instances[].{InstanceId:InstanceId,State:State.Name,PublicIpAddress:PublicIpAddress}' \
--region ap-northeast-1[
{
"InstanceId": "<インスタンス ID 2>",
"State": "pending",
"PublicIpAddress": null
}
]インスタンスの running 確認
aws ec2 describe-instances \
--instance-ids <インスタンス ID 2> \
--query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,PublicIpAddress:PublicIpAddress}' \
--region ap-northeast-1[
{
"InstanceId": "<インスタンス ID 2>",
"State": "running",
"PublicIpAddress": "<パブリック IP アドレス>"
}
]Config ルールの手動トリガーと FAILED 確認
aws configservice start-config-rules-evaluation \
--config-rule-names securityhub-ec2-instance-no-public-ip-<サフィックス> \
--region ap-northeast-1
(出力なし)aws configservice get-compliance-details-by-config-rule \
--config-rule-name securityhub-ec2-instance-no-public-ip-<サフィックス> \
--query 'EvaluationResults[?EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId==`<インスタンス ID 2>`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType}' \
--region ap-northeast-1[
{
"ResourceId": "<インスタンス ID 2>",
"ComplianceType": "NON_COMPLIANT"
}
]Security Hub finding の FAILED 確認
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.9"}],
"AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
"WorkflowStatus": [{"Comparison": "NOT_EQUALS", "Value": "SUPPRESSED"}],
"RecordState": [{"Comparison": "EQUALS", "Value": "ACTIVE"}]
}' \
--query 'Findings[].{Status:Compliance.Status,ResourceId:Resources[0].Id}' \
--region ap-northeast-1[
{
"Status": "FAILED",
"ResourceId": "arn:aws:ec2:ap-northeast-1:<アカウント ID>:instance/<インスタンス ID 2>"
},
{
"Status": "PASSED",
"ResourceId": "AWS::::Account:<アカウント ID>"
}
]CT.EC2.PV.8 有効化
aws controltower enable-control \
--control-identifier arn:aws:controlcatalog:::control/acfp8qjz7eggnursu5pfw7q2w \
--target-identifier <OU の ARN> \
--region ap-northeast-1 --profile Master{
"arn": "<有効化 ARN>",
"operationIdentifier": "<オペレーション ID>"
}aws controltower get-control-operation \
--operation-identifier <オペレーション ID> \
--query 'controlOperation.{operationType:operationType,status:status}' \
--region ap-northeast-1 --profile Master{
"operationType": "ENABLE_CONTROL",
"status": "SUCCEEDED"
}7. FAILED のまま変化しないことを確認
Config ルールの手動トリガー
aws configservice start-config-rules-evaluation \
--config-rule-names securityhub-ec2-instance-no-public-ip-<サフィックス> \
--region ap-northeast-1
(出力なし)Config 評価結果の確認(NON_COMPLIANT のまま)
aws configservice get-compliance-details-by-config-rule \
--config-rule-name securityhub-ec2-instance-no-public-ip-<サフィックス> \
--query 'EvaluationResults[?EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId==`<インスタンス ID 2>`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType}' \
--region ap-northeast-1[
{
"ResourceId": "<インスタンス ID 2>",
"ComplianceType": "NON_COMPLIANT"
}
]VPC BPA はパブリック IP の付与自体を止めないため、インスタンスにはパブリック IP が割り当てられたままとなり、finding は NON_COMPLIANT のまま変化しない。
Security Hub finding の確認(FAILED のまま)
aws securityhub get-findings \
--filters '{
"ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.9"}],
"AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
"WorkflowStatus": [{"Comparison": "NOT_EQUALS", "Value": "SUPPRESSED"}],
"RecordState": [{"Comparison": "EQUALS", "Value": "ACTIVE"}]
}' \
--query 'Findings[].{Status:Compliance.Status,ResourceId:Resources[0].Id}' \
--region ap-northeast-1[
{
"Status": "FAILED",
"ResourceId": "arn:aws:ec2:ap-northeast-1:<アカウント ID>:instance/<インスタンス ID 2>"
},
{
"Status": "PASSED",
"ResourceId": "AWS::::Account:<アカウント ID>"
}
]8. クリーンアップ
CT.EC2.PV.8 の無効化
aws controltower disable-control \
--control-identifier arn:aws:controlcatalog:::control/acfp8qjz7eggnursu5pfw7q2w \
--target-identifier <OU の ARN> \
--region ap-northeast-1 --profile Master{
"operationIdentifier": "<オペレーション ID>"
}aws controltower get-control-operation \
--operation-identifier <オペレーション ID> \
--query 'controlOperation.{operationType:operationType,status:status}' \
--region ap-northeast-1 --profile Master{
"operationType": "DISABLE_CONTROL",
"status": "SUCCEEDED"
}インスタンス終了
aws ec2 terminate-instances \
--instance-ids <インスタンス ID 2> \
--query 'TerminatingInstances[].{InstanceId:InstanceId,CurrentState:CurrentState.Name}' \
--region ap-northeast-1[
{
"InstanceId": "<インスタンス ID 2>",
"CurrentState": "shutting-down"
}
]補足:Config ルール名の確認方法
Config ルール名のサフィックスは環境によって異なる。以下のコマンドで確認できる。
aws configservice describe-config-rules \
--query 'ConfigRules[?contains(Source.SourceIdentifier,`EC2_INSTANCE_NO_PUBLIC_IP`)].ConfigRuleName' \
--region ap-northeast-1補足:VPC BPA と EC2.9 の関係
CT.EC2.PV.8(VPC BPA)は IGW 経由のインターネット通信をブロックするが、インスタンスへのパブリック IP の付与自体は止めない。そのため、VPC BPA が有効でもインスタンスの publicIp フィールドは存在し続け、EC2.9 の finding は FAILED のままとなる。
VPC BPA は「通信の遮断」、EC2.9 は「IP アドレスの付与」という異なるレイヤーをチェックしている。パブリック IP を持たないインスタンスを確実に運用するには、VPC BPA に加えてサブネットの MapPublicIpOnLaunch を無効化するか、EC2.25 で起動テンプレートの設定を制御する必要がある。