コンテンツにスキップ

VPC Block Public Access

検証日: 2026-04-30 / リージョン: ap-northeast-1
本ページの情報は 2026 年 4 月時点のものです。

概要

VPC Block Public Access(BPA)は EC2 ポリシー(宣言型) の属性の一つで、VPC / サブネットの IGW 経由のインターネットアクセスを組織全体でブロックする。

Control Tower 予防コントロールとの関係

CT.EC2.PV.8 は内部的に本属性の宣言型ポリシーを使用している。Organizations で直接適用する場合は、modeexclusions_allowed をより柔軟に設定できる。Control Tower を使わない環境では本ページの手順で Organizations ポリシーを直接設定する。

ポリシー形式

{
    "ec2_attributes": {
        "vpc_block_public_access": {
            "internet_gateway_block": {
                "mode": {
                    "@@assign": "block_bidirectional"
                },
                "exclusions_allowed": {
                    "@@assign": "disabled"
                }
            }
        }
    }
}

上記の例は、modeblock_bidirectional(IGW 経由の双方向通信をブロック)、exclusions_alloweddisabled(除外の作成を禁止)に設定した形である。

@@assign は宣言型ポリシー全般で使われる operator(詳細は 宣言型ポリシー: ポリシー構文の基礎 を参照)。VPC BPA では vpc_block_public_access.internet_gateway_block 配下に 2 つの属性(modeexclusions_allowed)を設定する。

属性取り得る値意味
mode"off" / "block_ingress" / "block_bidirectional"VPC BPA の動作モード(詳細
exclusions_allowed"enabled" / "disabled"アカウント側で除外(特定 VPC / サブネットの例外)を作成できるか(詳細
値の表記揺れ: ポリシーの content 値はアンダースコア区切り(block_bidirectional / block_ingress)で指定するが、実機で API から取得する際のレスポンスはハイフン区切り(block-bidirectional)で返る。本記事の実行例では両方が混在する。

mode

mode は VPC BPA の動作モードを指定する。取り得る値と効果は以下。

mode効果
offVPC BPA 無効
block_ingressVPC 外部から IGW 経由で入ってくるインバウンドトラフィックをブロック。アウトバウンドは NAT Gateway / Egress-only IGW 経由のみ許可される(これらは outbound のみ確立可能なため。戻りトラフィックはステートフルに自動許可)。パブリック IP インスタンス → IGW 直接のアウトバウンドは、戻りパケットが IGW のインバウンドとしてブロックされるため実質的に双方向通信が成立しない。本記事では NAT Gateway 経由について補足 2で検証済み(Egress-only IGW 経由は公式ドキュメントの記述に基づく、未検証)
block_bidirectionalIGW / Egress-only IGW 経由の全トラフィック(双方向)をブロック。IGW 直接のアウトバウンドブロックは CT.EC2.PV.8 の検証、NAT Gateway 経由のアウトバウンドブロックは補足 2で検証済み(Egress-only IGW 経由は公式ドキュメントの記述に基づく、未検証)

exclusions_allowed(除外)

exclusions_allowed は、VPC BPA が有効な状態で特定の VPC / サブネットを除外できるかを制御する。

  • enabled: 除外の作成を許可する。ただし除外の実体(どの VPC / サブネットを除外するか)は本ポリシーでは指定できない。アカウント側で CreateVpcBlockPublicAccessExclusion API を使って個別に作成する
  • disabled: 除外の作成を許可しない。すべての VPC / サブネットが VPC BPA の影響を受ける

つまりポリシーは「例外を作ることを許すか」だけを制御し、実際にどこを例外にするかはアカウント管理者の裁量で決まる。

VPC BPA はインターネットアクセスを広範にブロックするため、既存環境ではパブリック IP の付与や IGW 経由の通信が意図した設計に組み込まれている可能性がある。実環境への投入時には影響評価が必要である。

本記事の検証は、検証専用の OU(サンドボックス OU、または SCP テスト用 OU) で実施する。本番ワークロードを含む OU にアタッチすると、配下のすべてのアカウントで意図せず IGW 経由の通信が遮断される。特に補足 2 の検証 C(block-bidirectional + NAT Gateway)は、運用中の NAT Gateway 経由のアウトバウンドも止めるため影響が大きい。

デフォルト VPC を検証に使うため、他のワークロードが稼働していない検証専用アカウントで実施することを推奨する。

本記事の読み方

本記事は本編と 2 つの補足で構成されている。関心に応じて読む順序を変えられる。

  • Security Hub finding への影響: 本編(ステップ 1 〜 5)で全 13 コントロールについて扱う
  • VPC BPA 有効状態で起動できないサービス(EMR / Redshift / RedshiftServerless): 補足 1 で扱う
  • VPC BPA の各モード(block-ingress / block-bidirectional)の挙動検証: 補足 2 で扱う

Security Hub finding への影響

VPC BPA はインターネット通信をブロックするが、パブリック IP の付与自体や各サービスの設定値は変更しない。そのため、VPC BPA を有効化しても以下のコントロールの finding は変化しない(検証可能な範囲で確認済み)。EMR.1 / Redshift.1 / RedshiftServerless.3 は VPC BPA 有効状態で起動できないため、補足 1では先に起動してから VPC BPA を有効化する手順で検証した。finding は変化しなくても、実際のインターネットアクセスは VPC BPA によりブロックされる(CT.EC2.PV.8 の検証、および本記事の補足 2で確認済み)。

コントロール評価対象VPC BPA 有効化後の finding備考
EC2.9パブリック IP の有無FAILED のまま変化しない(検証済み)
EC2.25起動テンプレートの設定値FAILED のまま変化しない(検証済み)
Autoscaling.5起動設定の設定値検証不可起動設定の作成が不可(2024 年 10 月以降のアカウント)
ECS.2サービスの assignPublicIpFAILED のまま変化しない(検証済み)
ECS.16TaskSet の assignPublicIpFAILED のまま変化しない(検証済み)
EMR.1プライマリノードのパブリック IPFAILED のまま変化しない(補足 1VPC BPA 有効状態ではブートストラップが完了しない
RDS.2PubliclyAccessible フラグFAILED のまま変化しない(検証済み)
RDS.46サブネットのルートテーブルFAILED のまま変化しない(検証済み)
DMS.1PubliclyAccessible フラグFAILED のまま変化しない(検証済み)
Redshift.1PubliclyAccessible フラグFAILED のまま変化しない(補足 1VPC BPA 有効状態では PubliclyAccessible=true での起動がブロックされる
RedshiftServerless.3PubliclyAccessible フラグFAILED のまま変化しない(補足 1VPC BPA 有効状態では PubliclyAccessible=true での起動がブロックされる
SageMaker.1DirectInternetAccess の設定値FAILED のまま変化しない(検証済み)
EC2.19SG のインバウンドルールFAILED のまま変化しない(検証済み)
「FAILED のまま変化しない」の確認は、VPC BPA 有効化後に Config の再評価(NON_COMPLIANT、ResultRecordedTime がアタッチ時刻より後であることを確認)と Security Hub の FAILED を確認したものである。ただし、一部のコントロールでは Security Hub finding の UpdatedAt が VPC BPA 有効化後に更新されないケースがあった。詳細は補足 1 の Redshift.1 / RedshiftServerless.3 セクションを参照。

検証環境

検証環境の構成図

検証の流れ

    flowchart LR
    A[1. ポリシー作成・アタッチ] --> B[2. 各コントロールの<br>FAILED 状態を作成]
    B --> C[3. FAILED 確認]
    C --> D[4. デタッチ・削除]
    D --> E[5. クリーンアップ]
  

--profile 指定がない場合は Workload アカウントで実行する。export AWS_PROFILE=Workload でデフォルトを設定しておくと便利。ポリシーの作成・アタッチ・デタッチ・削除は --profile Master(Organizations 管理アカウント)で実行する。

1. ポリシー作成・アタッチ

宣言型ポリシーの作成(block-bidirectional)

aws organizations create-policy \
  --name vpc-bpa-test \
  --type DECLARATIVE_POLICY_EC2 \
  --content '{
    "ec2_attributes": {
      "vpc_block_public_access": {
        "internet_gateway_block": {
          "mode": {
            "@@assign": "block_bidirectional"
          },
          "exclusions_allowed": {
            "@@assign": "disabled"
          }
        }
      }
    }
  }' \
  --description "VPC BPA test" \
  --query 'Policy.PolicySummary.{Id:Id,Name:Name,Type:Type}' \
  --region ap-northeast-1 --profile Master
{
    "Id": "<ポリシー ID>",
    "Name": "vpc-bpa-test",
    "Type": "DECLARATIVE_POLICY_EC2"
}

OU にアタッチ

aws organizations attach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

VPC BPA が有効になったことを確認

反映には約 2 分かかる。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{InternetGatewayBlockMode:InternetGatewayBlockMode,State:State,ManagedBy:ManagedBy}' \
  --region ap-northeast-1
{
    "InternetGatewayBlockMode": "block-bidirectional",
    "State": "update-complete",
    "ManagedBy": "declarative-policy"
}

2. 各コントロールの FAILED 状態を作成

VPC BPA が有効な状態でも、パブリック IP 付きインスタンスの起動や各種設定は可能である(VPC BPA はインターネット通信をブロックするだけで、設定自体は止めない)。

本検証で使用する共通リソースの確認

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' \
  --output text \
  --region ap-northeast-1
<AMI ID>
aws ec2 describe-subnets \
  --filters Name=default-for-az,Values=true \
  --query 'Subnets[].SubnetId' \
  --region ap-northeast-1
[
    "<サブネット ID 1>",
    "<サブネット ID 2>",
    "<サブネット ID 3>"
]

EC2 / ECS / SageMaker では 1 つ目のサブネットを使用する。DMS のレプリケーションサブネットグループには複数の AZ のサブネットが必要なため、全サブネットを使用する。

DB サブネットグループの確認

RDS で使用する DB サブネットグループが存在することを確認する。デフォルト VPC では default という名前で自動作成されるが、環境によっては存在しない場合がある。

aws rds describe-db-subnet-groups \
  --db-subnet-group-name default \
  --query 'DBSubnetGroups[].{Name:DBSubnetGroupName,VpcId:VpcId}' \
  --region ap-northeast-1
[
    {
        "Name": "default",
        "VpcId": "<デフォルト VPC ID>"
    }
]

存在しない場合(DBSubnetGroupNotFoundFault エラー)は作成する。

aws rds create-db-subnet-group \
  --db-subnet-group-name default \
  --db-subnet-group-description "default" \
  --subnet-ids <サブネット ID 1> <サブネット ID 2> <サブネット ID 3> \
  --region ap-northeast-1

EC2.9: パブリック IP 付きインスタンスの起動

aws ec2 run-instances \
  --image-id <AMI ID> \
  --instance-type t3.micro \
  --subnet-id <サブネット ID> \
  --associate-public-ip-address \
  --query 'Instances[].{InstanceId:InstanceId,State:State.Name}' \
  --region ap-northeast-1
[
    {
        "InstanceId": "<インスタンス ID>",
        "State": "pending"
    }
]

EC2.25: パブリック IP 割り当て設定の起動テンプレート作成

aws ec2 create-launch-template \
  --launch-template-name vpc-bpa-test \
  --launch-template-data '{
    "NetworkInterfaces": [{
      "DeviceIndex": 0,
      "AssociatePublicIpAddress": true,
      "SubnetId": "<サブネット ID>"
    }],
    "ImageId": "<AMI ID>",
    "InstanceType": "t3.micro"
  }' \
  --query 'LaunchTemplate.{Id:LaunchTemplateId,Name:LaunchTemplateName}' \
  --region ap-northeast-1
{
    "Id": "<起動テンプレート ID>",
    "Name": "vpc-bpa-test"
}

ECS.2: assignPublicIp=ENABLED の Fargate サービス作成

aws ecs create-cluster \
  --cluster-name vpc-bpa-test-cluster \
  --query 'cluster.{clusterName:clusterName,status:status}' \
  --region ap-northeast-1
{
    "clusterName": "vpc-bpa-test-cluster",
    "status": "ACTIVE"
}
aws ecs register-task-definition \
  --family vpc-bpa-test-task \
  --network-mode awsvpc \
  --requires-compatibilities FARGATE \
  --cpu "256" \
  --memory "512" \
  --container-definitions '[{"name":"test","image":"public.ecr.aws/amazonlinux/amazonlinux:latest","essential":true}]' \
  --query 'taskDefinition.{family:family,revision:revision,status:status}' \
  --region ap-northeast-1
{
    "family": "vpc-bpa-test-task",
    "revision": 1,
    "status": "ACTIVE"
}
aws ecs create-service \
  --cluster vpc-bpa-test-cluster \
  --service-name vpc-bpa-test-svc \
  --task-definition vpc-bpa-test-task \
  --desired-count 0 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<サブネット ID>],assignPublicIp=ENABLED}" \
  --query 'service.{serviceName:serviceName,status:status}' \
  --region ap-northeast-1
{
    "serviceName": "vpc-bpa-test-svc",
    "status": "ACTIVE"
}

ECS.16: assignPublicIp=ENABLED の TaskSet 作成

EXTERNAL デプロイメントコントローラーのサービスを作成し、TaskSet を追加する。

aws ecs create-service \
  --cluster vpc-bpa-test-cluster \
  --service-name vpc-bpa-test-svc-external \
  --desired-count 0 \
  --deployment-controller type=EXTERNAL \
  --query 'service.{serviceName:serviceName,status:status}' \
  --region ap-northeast-1
{
    "serviceName": "vpc-bpa-test-svc-external",
    "status": "ACTIVE"
}
aws ecs create-task-set \
  --cluster vpc-bpa-test-cluster \
  --service vpc-bpa-test-svc-external \
  --task-definition vpc-bpa-test-task \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<サブネット ID>],assignPublicIp=ENABLED}" \
  --query 'taskSet.{id:id,status:status,assignPublicIp:networkConfiguration.awsvpcConfiguration.assignPublicIp}' \
  --region ap-northeast-1
{
    "id": "ecs-svc/<タスクセット ID>",
    "status": "ACTIVE",
    "assignPublicIp": "ENABLED"
}

EMR.1: EMR クラスターの起動

aws emr create-default-roles --region ap-northeast-1

デフォルトロール(EMR_DefaultRole、EMR_EC2_DefaultRole、EMR_AutoScaling_DefaultRole)が作成される。既に存在する場合はスキップされる。

aws emr create-cluster \
  --name vpc-bpa-test-emr \
  --release-label emr-7.8.0 \
  --applications Name=Spark \
  --instance-type m5.xlarge \
  --instance-count 1 \
  --use-default-roles \
  --query '{ClusterId:ClusterId}' \
  --region ap-northeast-1
{
    "ClusterId": "<クラスター ID>"
}

VPC BPA block-bidirectional が有効な状態では、クラスターは WAITING に到達しない。インスタンスが BOOTSTRAPPING フェーズで失敗し、EMR が自動でリトライを繰り返す。

aws emr list-instances \
  --cluster-id <クラスター ID> \
  --query 'Instances[].{Id:Ec2InstanceId,State:Status.State}' \
  --region ap-northeast-1

数分後:

[
    {
        "Id": "<インスタンス ID 1>",
        "State": "TERMINATED"
    },
    {
        "Id": "<インスタンス ID 2>",
        "State": "BOOTSTRAPPING"
    }
]

さらに数分後:

[
    {
        "Id": "<インスタンス ID 1>",
        "State": "TERMINATED"
    },
    {
        "Id": "<インスタンス ID 2>",
        "State": "TERMINATED"
    },
    {
        "Id": "<インスタンス ID 3>",
        "State": "BOOTSTRAPPING"
    }
]

インスタンスが BOOTSTRAPPING → TERMINATED を繰り返し、新しいインスタンスが自動で起動される。EC2 インスタンス自体は PROVISIONING → BOOTSTRAPPING まで進むことから、インスタンスの起動は成功しているが、ブートストラップ中のソフトウェアダウンロード等のインターネットアクセスが VPC BPA によりブロックされたことが原因と推察される。本検証はパブリック IP インスタンス + IGW 直接経由の構成であり、block-bidirectional 下でこの経路のアウトバウンドがブロックされることは補足 2で検証済み。

クラスターが起動しないため、EMR.1 の finding への影響は本手順では検証できない。クラスターを終了する。

aws emr terminate-clusters \
  --cluster-ids <クラスター ID> \
  --region ap-northeast-1
(出力なし)

RDS.46: パブリックサブネットに RDS インスタンス起動

aws rds create-db-instance \
  --db-instance-identifier vpc-bpa-test-rds \
  --db-instance-class db.t3.micro \
  --engine mysql \
  --master-username admin \
  --master-user-password '<パスワード>' \
  --allocated-storage 20 \
  --no-publicly-accessible \
  --db-subnet-group-name default \
  --no-multi-az \
  --query 'DBInstance.{DBInstanceIdentifier:DBInstanceIdentifier,DBInstanceStatus:DBInstanceStatus}' \
  --region ap-northeast-1
{
    "DBInstanceIdentifier": "vpc-bpa-test-rds",
    "DBInstanceStatus": "creating"
}

available になるまで数分待つ。

aws rds describe-db-instances \
  --db-instance-identifier vpc-bpa-test-rds \
  --query 'DBInstances[].{DBInstanceIdentifier:DBInstanceIdentifier,DBInstanceStatus:DBInstanceStatus}' \
  --region ap-northeast-1
[
    {
        "DBInstanceIdentifier": "vpc-bpa-test-rds",
        "DBInstanceStatus": "available"
    }
]

SageMaker.1: DirectInternetAccess=Enabled のノートブックインスタンス起動

VPC BPA の対象となるよう、デフォルト VPC のパブリックサブネットに配置する。VPC 配置時はセキュリティグループの指定が必須である。

cat > /tmp/sagemaker-trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "sagemaker.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
aws iam create-role \
  --role-name vpc-bpa-test-sagemaker-role \
  --assume-role-policy-document file:///tmp/sagemaker-trust-policy.json \
  --query 'Role.Arn' --output text
arn:aws:iam::<アカウント ID>:role/vpc-bpa-test-sagemaker-role
aws ec2 describe-security-groups \
  --filters Name=vpc-id,Values=<デフォルト VPC ID> Name=group-name,Values=default \
  --query 'SecurityGroups[0].GroupId' --output text \
  --region ap-northeast-1
<セキュリティグループ ID>
aws sagemaker create-notebook-instance \
  --notebook-instance-name vpc-bpa-test-notebook \
  --instance-type ml.t3.medium \
  --role-arn arn:aws:iam::<アカウント ID>:role/vpc-bpa-test-sagemaker-role \
  --direct-internet-access Enabled \
  --subnet-id <サブネット ID> \
  --security-group-ids <セキュリティグループ ID> \
  --query '{NotebookInstanceArn:NotebookInstanceArn}' \
  --region ap-northeast-1
{
    "NotebookInstanceArn": "arn:aws:sagemaker:ap-northeast-1:<アカウント ID>:notebook-instance/vpc-bpa-test-notebook"
}

InService になるまで数分待つ。

aws sagemaker describe-notebook-instance \
  --notebook-instance-name vpc-bpa-test-notebook \
  --query '{NotebookInstanceStatus:NotebookInstanceStatus,DirectInternetAccess:DirectInternetAccess,SubnetId:SubnetId}' \
  --region ap-northeast-1
{
    "NotebookInstanceStatus": "InService",
    "DirectInternetAccess": "Enabled",
    "SubnetId": "<サブネット ID>"
}
EMR や Redshift は VPC BPA 有効状態で起動できなかったが、SageMaker は InService になる。DirectInternetAccess=Enabled を指定しているにもかかわらず、SageMaker の VPC 配置 ENI にはパブリック IP が付与されていなかった(以下のコマンドで ENI の状態を確認した)。公式ドキュメントによると、VPC 配置時の Direct Internet Access は、ユーザーの VPC の IGW ではなく SageMaker が管理する別の VPC のネットワークインターフェース経由で提供される。そのため、ユーザー VPC の ENI にはパブリック IP が不要であり、VPC BPA のブロック対象にならない。一方、EMR はインスタンスにパブリック IP が付与され IGW 経由でブートストラップのダウンロードを試みるため、VPC BPA にブロックされて失敗する。なお、VPC BPA はアカウント・リージョン単位で適用されるが、SageMaker 管理 VPC は AWS 側のアカウントに存在するため、ユーザーアカウントの VPC BPA の影響を受けないと考えられる(未検証)。
aws ec2 describe-network-interfaces \
  --filters Name=subnet-id,Values=<サブネット ID> Name=description,Values='*SageMaker*' \
  --query 'NetworkInterfaces[].{Id:NetworkInterfaceId,Description:Description,PrivateIp:PrivateIpAddress,PublicIp:Association.PublicIp}' \
  --region ap-northeast-1
[
    {
        "Id": "<ENI ID>",
        "Description": "[Do not delete] Network Interface created to access resources in your VPC for SageMaker Notebook Instance [...]",
        "PrivateIp": "<プライベート IP>",
        "PublicIp": null
    }
]

RDS.2: PubliclyAccessible=true で RDS インスタンス起動

aws rds create-db-instance \
  --db-instance-identifier vpc-bpa-test-rds2 \
  --db-instance-class db.t3.micro \
  --engine mysql \
  --master-username admin \
  --master-user-password '<パスワード>' \
  --allocated-storage 20 \
  --publicly-accessible \
  --db-subnet-group-name default \
  --no-multi-az \
  --query 'DBInstance.{DBInstanceIdentifier:DBInstanceIdentifier,DBInstanceStatus:DBInstanceStatus,PubliclyAccessible:PubliclyAccessible}' \
  --region ap-northeast-1
{
    "DBInstanceIdentifier": "vpc-bpa-test-rds2",
    "DBInstanceStatus": "creating",
    "PubliclyAccessible": true
}

available になるまで数分待つ。

DMS.1: PubliclyAccessible=true で DMS レプリケーションインスタンス起動

DMS VPC ロールとレプリケーションサブネットグループが必要。既に存在する場合はスキップ。

aws dms create-replication-subnet-group \
  --replication-subnet-group-identifier vpc-bpa-test-dms-subnet-group \
  --replication-subnet-group-description "VPC BPA test" \
  --subnet-ids <サブネット ID 1> <サブネット ID 2> <サブネット ID 3> \
  --query 'ReplicationSubnetGroup.{Id:ReplicationSubnetGroupIdentifier}' \
  --region ap-northeast-1
{
    "Id": "vpc-bpa-test-dms-subnet-group"
}
aws dms create-replication-instance \
  --replication-instance-identifier vpc-bpa-test-dms \
  --replication-instance-class dms.t3.small \
  --allocated-storage 5 \
  --publicly-accessible \
  --replication-subnet-group-identifier vpc-bpa-test-dms-subnet-group \
  --no-multi-az \
  --query 'ReplicationInstance.{Id:ReplicationInstanceIdentifier,Status:ReplicationInstanceStatus,PubliclyAccessible:PubliclyAccessible}' \
  --region ap-northeast-1
{
    "Id": "vpc-bpa-test-dms",
    "Status": "creating",
    "PubliclyAccessible": true
}

available になるまで数分待つ。

Redshift.1 / RedshiftServerless.3(VPC BPA 有効状態での起動失敗)

VPC BPA 有効状態では PubliclyAccessible=true での起動が API レベルでブロックされる。

aws redshift create-cluster \
  --cluster-identifier vpc-bpa-test-redshift \
  --node-type ra3.large \
  --cluster-type single-node \
  --master-username admin \
  --master-user-password '<パスワード>' \
  --publicly-accessible \
  --region ap-northeast-1
An error occurred (InvalidParameterValue) when calling the CreateCluster operation: Subnets [...] have Block Public Access Enabled. Disable Block Public Access for the subnets and retry the operation

RedshiftServerless も同様にエラーとなる。

aws redshift-serverless create-workgroup \
  --workgroup-name vpc-bpa-test-rs-wg \
  --namespace-name vpc-bpa-test-rs-ns \
  --publicly-accessible \
  --base-capacity 8 \
  --region ap-northeast-1
An error occurred (ValidationException) when calling the CreateWorkgroup operation: Subnets [...] have VPC Block Public Access enabled. Disable Block Public Access for the subnets and retry the operation

これらのコントロールの VPC BPA 有効状態での finding 確認は、先にリソースを起動してから VPC BPA を有効化する手順で補足 1にて実施する。

EC2.19: ハイリスクポートを開放した SG を作成

aws ec2 create-security-group \
  --group-name vpc-bpa-test-sg \
  --description "VPC BPA test - SSH 0.0.0.0/0" \
  --vpc-id <デフォルト VPC ID> \
  --query '{GroupId:GroupId}' \
  --region ap-northeast-1
{
    "GroupId": "<SG ID>"
}
aws ec2 authorize-security-group-ingress \
  --group-id <SG ID> \
  --protocol tcp \
  --port 22 \
  --cidr 0.0.0.0/0 \
  --query 'SecurityGroupRules[].{RuleId:SecurityGroupRuleId,Port:FromPort,Cidr:CidrIpv4}' \
  --region ap-northeast-1
[
    {
        "RuleId": "<ルール ID>",
        "Port": 22,
        "Cidr": "0.0.0.0/0"
    }
]

3. FAILED 確認

各コントロールの Config ルールをトリガーし、VPC BPA 有効状態で NON_COMPLIANT / FAILED であることを確認する。

VPC BPA は既にステップ 1 で有効化済みであり、ステップ 2 で起動したリソースは VPC BPA 有効状態で作成されている。そのため、Config が NON_COMPLIANT を返せば「VPC BPA が有効でも finding は変化しない」ことの確認となる。ただし、Security Hub finding の UpdatedAt は Config の評価結果が変わらない場合に更新されないケースがあるため(詳細は補足 1 の Redshift.1 / RedshiftServerless.3 セクションを参照)、本ステップでは Config の ResultRecordedTime が VPC BPA 有効化後であることをもって、再評価されたことを確認する。

レート制限があるため Config トリガーは 30〜60 秒の間隔を空ける。

EC2.9(Config トリガー → Config 評価結果 → Security Hub の例)

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[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<インスタンス ID>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

ResultRecordedTime がステップ 1 のポリシーアタッチ時刻より後であることを確認する。

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.9"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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>"
    }
]

残りのコントロールも同様に実施する。以下は各コントロールの Config トリガー、ResultRecordedTime 確認結果、Security Hub の確認結果である。

EC2.25

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-ec2-launch-template-public-ip-disabled-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-ec2-launch-template-public-ip-disabled-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<起動テンプレート ID>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.25"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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>:launch-template/<起動テンプレート ID>"
    }
]

ECS.2

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-ecs-service-assign-public-ip-disabled-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-ecs-service-assign-public-ip-disabled-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<サービス ARN>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "ECS.2"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:ecs:ap-northeast-1:<アカウント ID>:service/<クラスター名>/<サービス名>"
    }
]

ECS.16

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-ecs-taskset-assign-public-ip-disabled-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-ecs-taskset-assign-public-ip-disabled-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<タスクセット ARN>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "ECS.16"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:ecs:ap-northeast-1:<アカウント ID>:task-set/<クラスター名>/<サービス名>/<タスクセット ID>"
    }
]

RDS.46

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-rds-instance-subnet-igw-check-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-rds-instance-subnet-igw-check-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<Config リソース ID>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "RDS.46"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:rds:ap-northeast-1:<アカウント ID>:dbinstance/<Config リソース ID>"
    }
]

SageMaker.1

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-sagemaker-notebook-no-direct-internet-access-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-sagemaker-notebook-no-direct-internet-access-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<ノートブック名>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "SageMaker.1"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:sagemaker:ap-northeast-1:<アカウント ID>:notebook-instance/<ノートブック名>"
    }
]

RDS.2

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-rds-instance-public-access-check-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-rds-instance-public-access-check-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<Config リソース ID>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "RDS.2"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:rds:ap-northeast-1:<アカウント ID>:db:<インスタンス名>"
    }
]

DMS.1

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-dms-replication-not-public-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-dms-replication-not-public-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<インスタンス名>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "DMS.1"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:dms:ap-northeast-1:<アカウント ID>:rep:<インスタンス名>"
    }
]

EC2.19

Config:

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-vpc-sg-restricted-common-ports-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-vpc-sg-restricted-common-ports-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<SG ID>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub:

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EC2.19"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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>:security-group/<SG ID>"
    }
]

VPC BPA が有効な状態で、9 件全てが Config NON_COMPLIANT(ResultRecordedTime がアタッチ時刻より後)かつ Security Hub FAILED であることを確認した。

Redshift.1 と RedshiftServerless.3 は VPC BPA 有効状態では PubliclyAccessible=true での起動がブロックされるため、本ステップでは検証不可。先に起動してから VPC BPA を有効化した場合の検証は補足 1を参照。

4. デタッチ・削除

aws organizations detach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws organizations delete-policy \
  --policy-id <ポリシー ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

5. クリーンアップ

EC2 インスタンス終了

aws ec2 terminate-instances \
  --instance-ids <インスタンス ID> \
  --query 'TerminatingInstances[].{InstanceId:InstanceId,CurrentState:CurrentState.Name}' \
  --region ap-northeast-1
[
    {
        "InstanceId": "<インスタンス ID>",
        "CurrentState": "shutting-down"
    }
]

起動テンプレート削除

aws ec2 delete-launch-template \
  --launch-template-id <起動テンプレート ID> \
  --query 'LaunchTemplate.{Id:LaunchTemplateId,Name:LaunchTemplateName}' \
  --region ap-northeast-1
{
    "Id": "<起動テンプレート ID>",
    "Name": "vpc-bpa-test"
}

ECS リソース削除

aws ecs delete-task-set \
  --cluster vpc-bpa-test-cluster \
  --service vpc-bpa-test-svc-external \
  --task-set <タスクセット ID> \
  --region ap-northeast-1
(出力なし)
aws ecs delete-service \
  --cluster vpc-bpa-test-cluster \
  --service vpc-bpa-test-svc \
  --force \
  --region ap-northeast-1
(出力なし)
aws ecs delete-service \
  --cluster vpc-bpa-test-cluster \
  --service vpc-bpa-test-svc-external \
  --force \
  --region ap-northeast-1
(出力なし)
aws ecs deregister-task-definition \
  --task-definition vpc-bpa-test-task:1 \
  --region ap-northeast-1
(出力なし)
aws ecs delete-cluster \
  --cluster vpc-bpa-test-cluster \
  --region ap-northeast-1
(出力なし)

RDS インスタンス削除

aws rds delete-db-instance \
  --db-instance-identifier vpc-bpa-test-rds \
  --skip-final-snapshot \
  --query 'DBInstance.{DBInstanceIdentifier:DBInstanceIdentifier,DBInstanceStatus:DBInstanceStatus}' \
  --region ap-northeast-1
{
    "DBInstanceIdentifier": "vpc-bpa-test-rds",
    "DBInstanceStatus": "deleting"
}

SageMaker ノートブックインスタンス削除

aws sagemaker stop-notebook-instance \
  --notebook-instance-name vpc-bpa-test-notebook \
  --region ap-northeast-1
(出力なし)

Stopped 確認後:

aws sagemaker delete-notebook-instance \
  --notebook-instance-name vpc-bpa-test-notebook \
  --region ap-northeast-1
(出力なし)
aws iam delete-role \
  --role-name vpc-bpa-test-sagemaker-role
(出力なし)
rm -f /tmp/sagemaker-trust-policy.json

RDS.2 インスタンス削除

aws rds delete-db-instance \
  --db-instance-identifier vpc-bpa-test-rds2 \
  --skip-final-snapshot \
  --query 'DBInstance.{DBInstanceIdentifier:DBInstanceIdentifier,DBInstanceStatus:DBInstanceStatus}' \
  --region ap-northeast-1
{
    "DBInstanceIdentifier": "vpc-bpa-test-rds2",
    "DBInstanceStatus": "deleting"
}

DMS レプリケーションインスタンス削除

ARN を確認してから削除する。

aws dms describe-replication-instances \
  --filters Name=replication-instance-id,Values=vpc-bpa-test-dms \
  --query 'ReplicationInstances[].ReplicationInstanceArn' \
  --region ap-northeast-1
aws dms delete-replication-instance \
  --replication-instance-arn <レプリケーションインスタンス ARN> \
  --query 'ReplicationInstance.{Id:ReplicationInstanceIdentifier,Status:ReplicationInstanceStatus}' \
  --region ap-northeast-1
{
    "Id": "vpc-bpa-test-dms",
    "Status": "deleting"
}

削除完了後にサブネットグループを削除する。

aws dms delete-replication-subnet-group \
  --replication-subnet-group-identifier vpc-bpa-test-dms-subnet-group \
  --region ap-northeast-1
(出力なし)

EC2.19 セキュリティグループ削除

aws ec2 delete-security-group \
  --group-id <SG ID> \
  --region ap-northeast-1
(出力なし)

EMR デフォルトロールの削除(検証で初めて作成した場合)

EMR デフォルトロールは他の検証や運用で使用する可能性がある。不要な場合のみ削除する。
aws iam remove-role-from-instance-profile \
  --instance-profile-name EMR_EC2_DefaultRole \
  --role-name EMR_EC2_DefaultRole
aws iam delete-instance-profile \
  --instance-profile-name EMR_EC2_DefaultRole
aws iam detach-role-policy \
  --role-name EMR_EC2_DefaultRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role
aws iam delete-role --role-name EMR_EC2_DefaultRole
aws iam detach-role-policy \
  --role-name EMR_DefaultRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole
aws iam delete-role --role-name EMR_DefaultRole
aws iam detach-role-policy \
  --role-name EMR_AutoScaling_DefaultRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforAutoScalingRole
aws iam delete-role --role-name EMR_AutoScaling_DefaultRole
(各コマンド出力なし)

補足 1:VPC BPA 有効状態で起動できないサービスの検証

本編のステップ 2 で、EMR.1 / Redshift.1 / RedshiftServerless.3 は VPC BPA 有効状態で起動できなかった。ここでは、先にリソースを起動してから VPC BPA を有効化した場合の finding への影響を確認する。

EMR.1

本編では VPC BPA 有効状態で EMR クラスターを起動したところ、ブートストラップが完了しなかった。

EMR クラスターの起動(VPC BPA なし)

aws emr create-cluster \
  --name vpc-bpa-test-emr-2 \
  --release-label emr-7.8.0 \
  --applications Name=Spark \
  --instance-type m5.xlarge \
  --instance-count 1 \
  --use-default-roles \
  --query '{ClusterId:ClusterId}' \
  --region ap-northeast-1
{
    "ClusterId": "<クラスター ID>"
}

WAITING 状態になるまで待つ(5〜10 分)。

aws emr describe-cluster \
  --cluster-id <クラスター ID> \
  --query 'Cluster.{Id:Id,Status:Status.State}' \
  --region ap-northeast-1
{
    "Id": "<クラスター ID>",
    "Status": "WAITING"
}

FAILED 確認

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-emr-master-no-public-ip-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-emr-master-no-public-ip-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<クラスター ID>",
        "ComplianceType": "NON_COMPLIANT"
    }
]

VPC BPA を有効化

aws organizations create-policy \
  --name vpc-bpa-test-emr \
  --type DECLARATIVE_POLICY_EC2 \
  --content '{
    "ec2_attributes": {
      "vpc_block_public_access": {
        "internet_gateway_block": {
          "mode": {
            "@@assign": "block_bidirectional"
          },
          "exclusions_allowed": {
            "@@assign": "disabled"
          }
        }
      }
    }
  }' \
  --description "VPC BPA test for EMR" \
  --query 'Policy.PolicySummary.{Id:Id}' \
  --region ap-northeast-1 --profile Master
{
    "Id": "<ポリシー ID>"
}
aws organizations attach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

反映を待つ(約 2 分)。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{InternetGatewayBlockMode:InternetGatewayBlockMode,State:State,ManagedBy:ManagedBy}' \
  --region ap-northeast-1
{
    "InternetGatewayBlockMode": "block-bidirectional",
    "State": "update-complete",
    "ManagedBy": "declarative-policy"
}

FAILED のまま変化しないことを確認

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-emr-master-no-public-ip-<サフィックス> \
  --region ap-northeast-1
(出力なし)

ResultRecordedTime がポリシーアタッチ時刻より後であることを確認する。

aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-emr-master-no-public-ip-<サフィックス> \
  --query 'EvaluationResults[?ComplianceType==`NON_COMPLIANT`].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "<クラスター ID>",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<評価時刻>"
    }
]

Security Hub finding の UpdatedAt もアタッチ時刻より後に更新されていることを確認する。

aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "EMR.1"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "WorkflowStatus": [{"Comparison": "NOT_EQUALS", "Value": "SUPPRESSED"}],
    "RecordState": [{"Comparison": "EQUALS", "Value": "ACTIVE"}]
  }' \
  --query 'Findings[].{Status:Compliance.Status,ResourceId:Resources[0].Id,UpdatedAt:UpdatedAt}' \
  --region ap-northeast-1
[
    {
        "Status": "FAILED",
        "ResourceId": "arn:aws:elasticmapreduce:ap-northeast-1:<アカウント ID>:cluster/<クラスター ID>",
        "UpdatedAt": "<更新時刻>"
    }
]

クリーンアップ

aws organizations detach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws organizations delete-policy \
  --policy-id <ポリシー ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws emr terminate-clusters \
  --cluster-ids <クラスター ID> \
  --region ap-northeast-1
(出力なし)

Redshift.1 / RedshiftServerless.3

本編では VPC BPA 有効状態で PubliclyAccessible=true を指定して起動したところ、「Subnets have Block Public Access Enabled」エラーで起動がブロックされた。EMR のブートストラップ失敗とは異なり、API レベルでサブネットの BPA 状態をチェックして拒否している。

VPC BPA の有効化

aws organizations create-policy \
  --name vpc-bpa-test-redshift \
  --type DECLARATIVE_POLICY_EC2 \
  --content '{
    "ec2_attributes": {
      "vpc_block_public_access": {
        "internet_gateway_block": {
          "mode": {
            "@@assign": "block_bidirectional"
          },
          "exclusions_allowed": {
            "@@assign": "disabled"
          }
        }
      }
    }
  }' \
  --description "VPC BPA test for Redshift" \
  --query 'Policy.PolicySummary.{Id:Id}' \
  --region ap-northeast-1 --profile Master
{
    "Id": "<ポリシー ID>"
}
aws organizations attach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

反映を待つ(約 2 分)。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{InternetGatewayBlockMode:InternetGatewayBlockMode,State:State,ManagedBy:ManagedBy}' \
  --region ap-northeast-1
{
    "InternetGatewayBlockMode": "block-bidirectional",
    "State": "update-complete",
    "ManagedBy": "declarative-policy"
}

PubliclyAccessible=false での起動確認(VPC BPA 有効状態)

VPC BPA 有効状態でも PubliclyAccessible=false であれば起動できるかを確認する。

aws redshift create-cluster \
  --cluster-identifier vpc-bpa-test-redshift-priv \
  --node-type ra3.large \
  --cluster-type single-node \
  --master-username admin \
  --master-user-password '<パスワード>' \
  --no-publicly-accessible \
  --query 'Cluster.{ClusterIdentifier:ClusterIdentifier,ClusterStatus:ClusterStatus,PubliclyAccessible:PubliclyAccessible}' \
  --region ap-northeast-1
{
    "ClusterIdentifier": "vpc-bpa-test-redshift-priv",
    "ClusterStatus": "creating",
    "PubliclyAccessible": false
}

VPC BPA 有効状態でも PubliclyAccessible=false であれば起動できる。PubliclyAccessible=true の場合のみブロックされる。

起動確認後、クラスターを削除する。

aws redshift delete-cluster \
  --cluster-identifier vpc-bpa-test-redshift-priv \
  --skip-final-cluster-snapshot \
  --region ap-northeast-1
(出力なし)

RedshiftServerless も同様に確認する。

aws redshift-serverless create-namespace \
  --namespace-name vpc-bpa-test-rs-ns-priv \
  --admin-username admin \
  --admin-user-password '<パスワード>' \
  --query 'namespace.{namespaceName:namespaceName,status:status}' \
  --region ap-northeast-1
{
    "namespaceName": "vpc-bpa-test-rs-ns-priv",
    "status": "AVAILABLE"
}
aws redshift-serverless create-workgroup \
  --workgroup-name vpc-bpa-test-rs-wg-priv \
  --namespace-name vpc-bpa-test-rs-ns-priv \
  --no-publicly-accessible \
  --base-capacity 8 \
  --query 'workgroup.{workgroupName:workgroupName,status:status,publiclyAccessible:publiclyAccessible}' \
  --region ap-northeast-1
{
    "workgroupName": "vpc-bpa-test-rs-wg-priv",
    "status": "CREATING",
    "publiclyAccessible": false
}

VPC BPA 有効状態でも PubliclyAccessible=false であれば起動できる。

AVAILABLE になるまで待ってから、ワークグループと名前空間を削除する。

aws redshift-serverless get-workgroup \
  --workgroup-name vpc-bpa-test-rs-wg-priv \
  --query 'workgroup.status' --output text \
  --region ap-northeast-1
AVAILABLE
aws redshift-serverless delete-workgroup \
  --workgroup-name vpc-bpa-test-rs-wg-priv \
  --region ap-northeast-1
(出力なし)

ワークグループ削除完了を待ってから名前空間を削除する。

aws redshift-serverless delete-namespace \
  --namespace-name vpc-bpa-test-rs-ns-priv \
  --region ap-northeast-1
(出力なし)

VPC BPA のデタッチ

Redshift.1 / RedshiftServerless.3 を VPC BPA なしで起動して FAILED を確認するため、VPC BPA を一旦無効化する。

aws organizations detach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

反映を待つ(約 2 分)。

先に起動してから VPC BPA を有効化

VPC BPA なしの状態で PubliclyAccessible=true で起動し、FAILED を確認してから VPC BPA を有効化する。

aws redshift create-cluster \
  --cluster-identifier vpc-bpa-test-redshift \
  --node-type ra3.large \
  --cluster-type single-node \
  --master-username admin \
  --master-user-password '<パスワード>' \
  --publicly-accessible \
  --query 'Cluster.{ClusterIdentifier:ClusterIdentifier,ClusterStatus:ClusterStatus,PubliclyAccessible:PubliclyAccessible}' \
  --region ap-northeast-1
{
    "ClusterIdentifier": "vpc-bpa-test-redshift",
    "ClusterStatus": "creating",
    "PubliclyAccessible": true
}
aws redshift-serverless create-namespace \
  --namespace-name vpc-bpa-test-rs-ns \
  --admin-username admin \
  --admin-user-password '<パスワード>' \
  --query 'namespace.{namespaceName:namespaceName,status:status}' \
  --region ap-northeast-1
{
    "namespaceName": "vpc-bpa-test-rs-ns",
    "status": "AVAILABLE"
}
aws redshift-serverless create-workgroup \
  --workgroup-name vpc-bpa-test-rs-wg \
  --namespace-name vpc-bpa-test-rs-ns \
  --publicly-accessible \
  --base-capacity 8 \
  --query 'workgroup.{workgroupName:workgroupName,status:status,publiclyAccessible:publiclyAccessible}' \
  --region ap-northeast-1
{
    "workgroupName": "vpc-bpa-test-rs-wg",
    "status": "CREATING",
    "publiclyAccessible": true
}

available / AVAILABLE になるまで待つ。

FAILED 確認(VPC BPA なし)

Config ルールをトリガーし、Security Hub で FAILED であることを確認する。

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-redshift-cluster-public-access-check-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-redshift-serverless-workgroup-no-public-access-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "Redshift.1"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:redshift:ap-northeast-1:<アカウント ID>:cluster:vpc-bpa-test-redshift"
    }
]
aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "RedshiftServerless.3"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:redshift-serverless:ap-northeast-1:<アカウント ID>:workgroup/<ワークグループ UUID>"
    }
]

VPC BPA を再度有効化

aws organizations attach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

反映を待つ(約 2 分)。

FAILED のまま変化しないことを確認

aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-redshift-cluster-public-access-check-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws configservice start-config-rules-evaluation \
  --config-rule-names securityhub-redshift-serverless-workgroup-no-public-access-<サフィックス> \
  --region ap-northeast-1
(出力なし)
aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "Redshift.1"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:redshift:ap-northeast-1:<アカウント ID>:cluster:vpc-bpa-test-redshift"
    }
]
aws securityhub get-findings \
  --filters '{
    "ComplianceSecurityControlId": [{"Comparison": "EQUALS", "Value": "RedshiftServerless.3"}],
    "AwsAccountId": [{"Comparison": "EQUALS", "Value": "<アカウント ID>"}],
    "ComplianceStatus": [{"Comparison": "EQUALS", "Value": "FAILED"}],
    "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:redshift-serverless:ap-northeast-1:<アカウント ID>:workgroup/<ワークグループ UUID>"
    }
]

VPC BPA を有効化しても FAILED のまま変化しない。ただし、VPC BPA 有効化後に再評価されて FAILED が維持されたのか、有効化前の FAILED がそのまま残っているだけなのかは、この結果だけでは判断が付かない。

Security Hub finding の UpdatedAt を確認したところ、Redshift.1 は VPC BPA 有効化前の時刻(17:58 JST)のままであり、RedshiftServerless.3 は有効化後の時刻(18:03 JST)に更新されていた。Redshift.1 については UpdatedAt からは再評価の有無が確認できない。

そこで Config の ResultRecordedTime を確認すると、Redshift.1 も VPC BPA 有効化後(18:06 JST)に再評価されて NON_COMPLIANT が記録されていた。

aws configservice get-compliance-details-by-config-rule \
  --config-rule-name securityhub-redshift-cluster-public-access-check-<サフィックス> \
  --query 'EvaluationResults[].{ResourceId:EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType:ComplianceType,ResultRecordedTime:ResultRecordedTime}' \
  --region ap-northeast-1
[
    {
        "ResourceId": "vpc-bpa-test-redshift",
        "ComplianceType": "NON_COMPLIANT",
        "ResultRecordedTime": "<VPC BPA 有効化後の日時>"
    }
]

Security Hub の UpdatedAt は、RedshiftServerless.3 では VPC BPA 有効化後に更新されていたが、Redshift.1 では更新されなかった。理由は不明だが、Config の ResultRecordedTime が VPC BPA 有効化後であることから、VPC BPA が finding に影響しないことは確認できているため、ここではこれ以上追及しない。

クリーンアップ

aws organizations detach-policy \
  --policy-id <ポリシー ID> \
  --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws organizations delete-policy \
  --policy-id <ポリシー ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws redshift delete-cluster \
  --cluster-identifier vpc-bpa-test-redshift \
  --skip-final-cluster-snapshot \
  --region ap-northeast-1
(出力なし)
aws redshift-serverless delete-workgroup \
  --workgroup-name vpc-bpa-test-rs-wg \
  --region ap-northeast-1
(出力なし)

ワークグループ削除完了を待ってから名前空間を削除する。

aws redshift-serverless delete-namespace \
  --namespace-name vpc-bpa-test-rs-ns \
  --region ap-northeast-1
(出力なし)

補足 2:VPC BPA モード動作の検証

VPC BPA の各モードの挙動を実機検証で確認する。

モード観点検証セクション
block-ingressインバウンドブロック検証 A
block-ingressパブリック IP + IGW 直接のアウトバウンド検証 A
block-ingressNAT Gateway 経由のアウトバウンド(ステートフル)検証 B
block-bidirectionalNAT Gateway 経由のアウトバウンドブロック検証 C
block-bidirectionalIGW 直接のアウトバウンドブロックCT.EC2.PV.8

共通の前提

本補足の全検証で使用する SSM ロールを作成する。

cat > /tmp/ssm-trust.json << 'EOF'
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]}
EOF

aws iam create-role --role-name VPCBPATestSSMRole \
  --assume-role-policy-document file:///tmp/ssm-trust.json \
  --query 'Role.Arn' --output text
aws iam attach-role-policy --role-name VPCBPATestSSMRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam create-instance-profile --instance-profile-name VPCBPATestSSMRole
aws iam add-role-to-instance-profile --instance-profile-name VPCBPATestSSMRole \
  --role-name VPCBPATestSSMRole
arn:aws:iam::<アカウント ID>:role/VPCBPATestSSMRole
(以降のコマンドは出力なし)

cron で毎分 curl を実行するスクリプトを user-data に書く。

cat > /tmp/user-data-curl.sh << 'EOF'
#!/bin/bash
dnf install -y cronie
systemctl enable --now crond
cat > /usr/local/bin/vpc-bpa-check.sh << 'SCRIPT'
#!/bin/bash
LOG=/var/log/vpc-bpa-curl.log
OUT=$(curl -v -s -o /dev/null -w "HTTP=%{http_code}" --connect-timeout 10 https://checkip.amazonaws.com 2>&1)
EXIT=$?
SUMMARY=$(echo "$OUT" | grep -E "Connected|Could not resolve|Connection timed out|Connection refused|No route|timed out" | head -3 | tr "\n" "|")
HTTP=$(echo "$OUT" | grep -oE "HTTP=[0-9]+")
echo "$(date -u +%H:%M:%S) EXIT=$EXIT $HTTP MSG=$SUMMARY" >> $LOG
SCRIPT
chmod +x /usr/local/bin/vpc-bpa-check.sh
echo "* * * * * root /usr/local/bin/vpc-bpa-check.sh" > /etc/cron.d/vpc-bpa-test
chmod 644 /etc/cron.d/vpc-bpa-test
systemctl restart crond
EOF

検証 A: block-ingress(パブリック IP + IGW 直接経由)

検証用 EC2 の起動(パブリック IP 付き、nginx)

80 番ポートを許可する SG を作成する。

検証 A の VPC BPA off 期間中、nginx インスタンスは 0.0.0.0/0:80 で一時的にインターネット公開状態となる。コンテンツは VPC BPA mode test のみだが、検証完了後は速やかにクリーンアップを実施する。
aws ec2 create-security-group \
  --group-name vpc-bpa-mode-test-web \
  --description "VPC BPA mode test web" \
  --vpc-id <デフォルト VPC ID> \
  --query 'GroupId' --output text \
  --region ap-northeast-1
<SG ID>
aws ec2 authorize-security-group-ingress \
  --group-id <SG ID> \
  --protocol tcp --port 80 --cidr 0.0.0.0/0 \
  --region ap-northeast-1

検証 A 用の user-data を作成する。共通の前提のスクリプトに nginx の起動を追加した別ファイル /tmp/user-data-a.sh を作成する(cron 設定部分は共通の前提のスクリプトと同一)。

cat > /tmp/user-data-a.sh << 'EOF'
#!/bin/bash
dnf install -y nginx cronie
systemctl enable nginx
systemctl start nginx
echo "VPC BPA mode test" > /usr/share/nginx/html/index.html
systemctl enable --now crond
cat > /usr/local/bin/vpc-bpa-check.sh << 'SCRIPT'
#!/bin/bash
LOG=/var/log/vpc-bpa-curl.log
OUT=$(curl -v -s -o /dev/null -w "HTTP=%{http_code}" --connect-timeout 10 https://checkip.amazonaws.com 2>&1)
EXIT=$?
SUMMARY=$(echo "$OUT" | grep -E "Connected|Could not resolve|Connection timed out|Connection refused|No route|timed out" | head -3 | tr "\n" "|")
HTTP=$(echo "$OUT" | grep -oE "HTTP=[0-9]+")
echo "$(date -u +%H:%M:%S) EXIT=$EXIT $HTTP MSG=$SUMMARY" >> $LOG
SCRIPT
chmod +x /usr/local/bin/vpc-bpa-check.sh
echo "* * * * * root /usr/local/bin/vpc-bpa-check.sh" > /etc/cron.d/vpc-bpa-test
chmod 644 /etc/cron.d/vpc-bpa-test
systemctl restart crond
EOF

EC2 インスタンスを起動する。

aws ec2 run-instances \
  --image-id <Amazon Linux 2023 AMI ID> \
  --instance-type t3.micro \
  --subnet-id <パブリックサブネット ID> \
  --security-group-ids <SG ID> \
  --associate-public-ip-address \
  --iam-instance-profile Name=VPCBPATestSSMRole \
  --user-data file:///tmp/user-data-a.sh \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=vpc-bpa-mode-test-a}]' \
  --query 'Instances[0].{InstanceId:InstanceId,State:State.Name}' \
  --region ap-northeast-1
{
    "InstanceId": "<インスタンス ID>",
    "State": "pending"
}

running 状態になった後、パブリック IP を確認する。

aws ec2 describe-instances --instance-ids <インスタンス ID> \
  --query 'Reservations[0].Instances[0].{PublicIp:PublicIpAddress,PrivateIp:PrivateIpAddress,State:State.Name}' \
  --region ap-northeast-1
{
    "PublicIp": "<パブリック IP>",
    "PrivateIp": "<プライベート IP>",
    "State": "running"
}

SSM Online を待つ。

aws ssm describe-instance-information \
  --filters Key=InstanceIds,Values=<インスタンス ID> \
  --query 'InstanceInformationList[0].PingStatus' --output text \
  --region ap-northeast-1
Online

VPC BPA off 時の動作確認

ローカル端末から curl(インバウンド):

curl -s -o /dev/null -w "HTTP %{http_code}\n" --connect-timeout 10 http://<パブリック IP>
HTTP 200

EC2 からアウトバウンド(cron ログを SSM で取得):

aws ssm send-command \
  --instance-ids <インスタンス ID> \
  --document-name AWS-RunShellScript \
  --parameters 'commands=["cat /var/log/vpc-bpa-curl.log"]' \
  --region ap-northeast-1
<UTC 時刻> EXIT=0 HTTP=200 MSG=
<UTC 時刻> EXIT=0 HTTP=200 MSG=

block-ingress 適用

aws organizations create-policy \
  --name vpc-bpa-ingress-test \
  --type DECLARATIVE_POLICY_EC2 \
  --content '{"ec2_attributes":{"vpc_block_public_access":{"internet_gateway_block":{"mode":{"@@assign":"block_ingress"},"exclusions_allowed":{"@@assign":"disabled"}}}}}' \
  --description "VPC BPA block-ingress verification" \
  --query 'Policy.PolicySummary.Id' --output text \
  --region ap-northeast-1 --profile Master
<ポリシー ID>
aws organizations attach-policy --policy-id <ポリシー ID> --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

反映を確認する。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{Mode:InternetGatewayBlockMode,State:State}' \
  --region ap-northeast-1
{
    "Mode": "block-ingress",
    "State": "update-complete"
}

block-ingress 有効中の動作確認

ローカル端末から curl(インバウンド):

curl -s -o /dev/null -w "HTTP %{http_code} / time %{time_total}s\n" --connect-timeout 10 http://<パブリック IP>
HTTP 000 / time 10.002303s

接続タイムアウト。インバウンドトラフィックがブロックされている。

アウトバウンドは block-ingress 有効中に SSM Run Command が届かないため、5 分ほど待ってからデタッチし(cron で複数回ログを記録するため)、off 復帰後に cron ログを取得する(後述)。

block-ingress デタッチ

aws organizations detach-policy --policy-id <ポリシー ID> --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{Mode:InternetGatewayBlockMode,State:State}' \
  --region ap-northeast-1
{
    "Mode": "off",
    "State": "update-complete"
}

アウトバウンドログの取得

デタッチ後、SSM が復帰するまで 1 分ほど待ってから cron ログを取得する。

aws ssm send-command \
  --instance-ids <インスタンス ID> \
  --document-name AWS-RunShellScript \
  --parameters 'commands=["cat /var/log/vpc-bpa-curl.log"]' \
  --region ap-northeast-1
<off 時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<off 時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<block-ingress 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10002 milliseconds|
<block-ingress 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10002 milliseconds|
<block-ingress 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10001 milliseconds|
<block-ingress 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10001 milliseconds|
<off 復帰後の UTC 時刻> EXIT=0 HTTP=200 MSG=
<off 復帰後の UTC 時刻> EXIT=0 HTTP=200 MSG=

curl の終了コード 28 は Connection timeout で、TCP 3-way handshake の SYN に対する SYN-ACK が返ってこない状態を示す。

検証結果

  • インバウンド: block-ingress でブロックされる(想定通り)
  • アウトバウンド(パブリック IP + IGW 直接): block-ingress で Connection timed out となる(curl のリクエスト SYN は出るが、サーバーからの SYN-ACK が戻らない)

AWS 公式ドキュメントでは block-ingress について「Only traffic to and from NAT gateways and egress-only internet gateways is allowed」と記載されている。つまり block-ingress で許可されるアウトバウンドは NAT Gateway / Egress-only IGW 経由のみであり、パブリック IP インスタンス → IGW 直接のアウトバウンドは、戻りトラフィック(IGW からのインバウンド)がブロックされるため TCP 接続が成立しない。本検証で観測された Connection timed out は、この公式仕様通りの挙動である。

VPC BPA block-ingress は AWS 公式ブログでも「stateful when used in ingress-only mode」と明記されており、NAT Gateway / Egress-only IGW 経由では戻りトラフィックがステートフルに自動許可される(NAT Gateway 経由は検証 B で確認、Egress-only IGW 経由は未検証)。

検証 A のクリーンアップ

aws organizations delete-policy --policy-id <ポリシー ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws ec2 terminate-instances --instance-ids <インスタンス ID> \
  --region ap-northeast-1
(出力あり。省略)

インスタンスが完全に削除されるまで待つ(ENI が SG を参照している間は SG 削除が失敗する)。

aws ec2 wait instance-terminated --instance-ids <インスタンス ID> \
  --region ap-northeast-1
(出力なし)
aws ec2 delete-security-group --group-id <SG ID> \
  --region ap-northeast-1
(出力なし)
rm -f /tmp/user-data-a.sh

NAT Gateway 環境のセットアップ(検証 B・C 共通)

検証 B(block-ingress)と検証 C(block-bidirectional)は同一の NAT Gateway 環境を使用する。ここで一度セットアップし、両検証が終わったらまとめてクリーンアップする。

NAT Gateway は時間課金される(東京リージョンで約 $0.045/hour + データ処理料)。検証完了後は速やかに「NAT Gateway 環境のクリーンアップ」を実施する。

プライベートサブネット作成

aws ec2 create-subnet \
  --vpc-id <デフォルト VPC ID> \
  --cidr-block 172.31.128.0/24 \
  --availability-zone ap-northeast-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=vpc-bpa-nat-test-private}]' \
  --query 'Subnet.SubnetId' --output text \
  --region ap-northeast-1
<プライベートサブネット ID>

EIP 確保と NAT Gateway 作成

aws ec2 allocate-address --domain vpc \
  --query '{AllocationId:AllocationId,PublicIp:PublicIp}' \
  --region ap-northeast-1
{
    "AllocationId": "<EIP アロケーション ID>",
    "PublicIp": "<EIP アドレス>"
}
aws ec2 create-nat-gateway \
  --subnet-id <パブリックサブネット ID> \
  --allocation-id <EIP アロケーション ID> \
  --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=vpc-bpa-nat-test}]' \
  --query 'NatGateway.NatGatewayId' --output text \
  --region ap-northeast-1
<NAT Gateway ID>

available になるまで数分待つ。

aws ec2 describe-nat-gateways --nat-gateway-ids <NAT Gateway ID> \
  --query 'NatGateways[0].State' --output text \
  --region ap-northeast-1
available

ルートテーブル作成とサブネットへの関連付け

aws ec2 create-route-table --vpc-id <デフォルト VPC ID> \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=vpc-bpa-nat-test-rt}]' \
  --query 'RouteTable.RouteTableId' --output text \
  --region ap-northeast-1
<ルートテーブル ID>
aws ec2 create-route --route-table-id <ルートテーブル ID> \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id <NAT Gateway ID> \
  --region ap-northeast-1
{
    "Return": true
}
aws ec2 associate-route-table --route-table-id <ルートテーブル ID> \
  --subnet-id <プライベートサブネット ID> \
  --query 'AssociationId' --output text \
  --region ap-northeast-1
<関連付け ID>

プライベート EC2 の起動

共通の前提で作成した /tmp/user-data-curl.sh を使用する。

デフォルト SG ID は以下で取得できる。

aws ec2 describe-security-groups \
  --filters Name=vpc-id,Values=<デフォルト VPC ID> Name=group-name,Values=default \
  --query 'SecurityGroups[0].GroupId' --output text \
  --region ap-northeast-1
<デフォルト SG ID>
aws ec2 run-instances \
  --image-id <Amazon Linux 2023 AMI ID> \
  --instance-type t3.micro \
  --subnet-id <プライベートサブネット ID> \
  --security-group-ids <デフォルト SG ID> \
  --iam-instance-profile Name=VPCBPATestSSMRole \
  --user-data file:///tmp/user-data-curl.sh \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=vpc-bpa-nat-test}]' \
  --query 'Instances[0].{InstanceId:InstanceId,State:State.Name,PublicIp:PublicIpAddress,PrivateIp:PrivateIpAddress}' \
  --region ap-northeast-1
{
    "InstanceId": "<NAT 検証用インスタンス ID>",
    "State": "pending",
    "PublicIp": null,
    "PrivateIp": "<プライベート IP>"
}

PublicIp: null であることを確認する(パブリック IP を持たず、アウトバウンドは NAT Gateway 経由でのみ可能)。

SSM Online を待つ。

aws ssm describe-instance-information \
  --filters Key=InstanceIds,Values=<NAT 検証用インスタンス ID> \
  --query 'InstanceInformationList[0].PingStatus' --output text \
  --region ap-northeast-1
Online

VPC BPA off 時のログ確認

2 分ほど待ってからログを確認する。

aws ssm send-command \
  --instance-ids <NAT 検証用インスタンス ID> \
  --document-name AWS-RunShellScript \
  --parameters 'commands=["cat /var/log/vpc-bpa-curl.log"]' \
  --region ap-northeast-1
<UTC 時刻> EXIT=0 HTTP=200 MSG=
<UTC 時刻> EXIT=0 HTTP=200 MSG=

NAT Gateway 経由で外部 API にアクセスできている。

検証 B: block-ingress + NAT Gateway

AWS 公式ブログによると、VPC BPA は block-ingress モードおよび egress-only exclusion 使用時にステートフルであり、許可された接続の戻りトラフィックは自動的に許可される。本セクションでは、block-ingress + NAT Gateway 経由でアウトバウンド通信が成立することを確認する。

ポリシー作成・アタッチ

aws organizations create-policy --name vpc-bpa-ingress-nat-test \
  --type DECLARATIVE_POLICY_EC2 \
  --content '{"ec2_attributes":{"vpc_block_public_access":{"internet_gateway_block":{"mode":{"@@assign":"block_ingress"},"exclusions_allowed":{"@@assign":"disabled"}}}}}' \
  --description "block-ingress + NAT Gateway test" \
  --query 'Policy.PolicySummary.Id' --output text \
  --region ap-northeast-1 --profile Master
<ポリシー ID>
aws organizations attach-policy --policy-id <ポリシー ID> --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)
aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{Mode:InternetGatewayBlockMode,State:State}' \
  --region ap-northeast-1
{
    "Mode": "block-ingress",
    "State": "update-complete"
}

アウトバウンドログの取得

block-ingress + NAT Gateway 経由では SSM Run Command が切断されない。4〜5 分待ってからログを取得する。

aws ssm send-command --instance-ids <NAT 検証用インスタンス ID> \
  --document-name AWS-RunShellScript \
  --parameters 'commands=["cat /var/log/vpc-bpa-curl.log"]' \
  --region ap-northeast-1
<off 時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<off 時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<block-ingress 有効時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<block-ingress 有効時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<block-ingress 有効時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<block-ingress 有効時の UTC 時刻> EXIT=0 HTTP=200 MSG=

block-ingress 有効中も HTTP=200 が継続する。NAT Gateway 経由のアウトバウンドは戻りトラフィックが自動許可され、双方向通信が成立する。

デタッチ

aws organizations detach-policy --policy-id <ポリシー ID> --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

off に戻ることを確認する。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{Mode:InternetGatewayBlockMode,State:State}' \
  --region ap-northeast-1
{
    "Mode": "off",
    "State": "update-complete"
}
aws organizations delete-policy --policy-id <ポリシー ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

検証結果

  • block-ingress + NAT Gateway 経由のアウトバウンド: 有効中も HTTP=200 で通信成立
  • VPC BPA block-ingress はステートフルであり、戻りトラフィックが自動許可されることを確認

検証 C: block-bidirectional + NAT Gateway

NAT Gateway は内部的に IGW を経由して外部と通信するため、公式ドキュメントによると block-bidirectional 有効状態では NAT Gateway 経由のアウトバウンドもブロックされる。これを検証する。

ポリシー作成・アタッチ

aws organizations create-policy \
  --name vpc-bpa-bidir-nat-test \
  --type DECLARATIVE_POLICY_EC2 \
  --content '{"ec2_attributes":{"vpc_block_public_access":{"internet_gateway_block":{"mode":{"@@assign":"block_bidirectional"},"exclusions_allowed":{"@@assign":"disabled"}}}}}' \
  --description "VPC BPA block-bidirectional NAT Gateway verification" \
  --query 'Policy.PolicySummary.Id' --output text \
  --region ap-northeast-1 --profile Master
<ポリシー ID>
aws organizations attach-policy --policy-id <ポリシー ID> --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

反映を確認する。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{Mode:InternetGatewayBlockMode,State:State}' \
  --region ap-northeast-1
{
    "Mode": "block-bidirectional",
    "State": "update-complete"
}

block-bidirectional 有効中は SSM が切断されるため、4〜5 分ほど待ってからデタッチする。

デタッチ

aws organizations detach-policy --policy-id <ポリシー ID> --target-id <OU ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

off に戻ることを確認する。

aws ec2 describe-vpc-block-public-access-options \
  --query 'VpcBlockPublicAccessOptions.{Mode:InternetGatewayBlockMode,State:State}' \
  --region ap-northeast-1
{
    "Mode": "off",
    "State": "update-complete"
}
aws organizations delete-policy --policy-id <ポリシー ID> \
  --region ap-northeast-1 --profile Master
(出力なし)

アウトバウンドログの取得

SSM が復帰するまで 1 分ほど待ってからログを取得する。

aws ssm send-command \
  --instance-ids <NAT 検証用インスタンス ID> \
  --document-name AWS-RunShellScript \
  --parameters 'commands=["cat /var/log/vpc-bpa-curl.log"]' \
  --region ap-northeast-1
<off 時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<off 時の UTC 時刻> EXIT=0 HTTP=200 MSG=
<block-bidirectional 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10001 milliseconds|
<block-bidirectional 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10002 milliseconds|
<block-bidirectional 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10002 milliseconds|
<block-bidirectional 有効時の UTC 時刻> EXIT=28 HTTP=000 MSG=* Connection timed out after 10001 milliseconds|
<off 復帰後の UTC 時刻> EXIT=0 HTTP=200 MSG=
<off 復帰後の UTC 時刻> EXIT=0 HTTP=200 MSG=

検証結果

  • block-bidirectional 有効中: NAT Gateway 経由のアウトバウンドも Connection timed out となり、公式ドキュメントの記載通りブロックされることを確認
  • off に戻ると NAT Gateway 経由の通信が復帰する

NAT Gateway 環境のクリーンアップ(検証 B・C 共通)

検証 B・C の両方が終わったら、NAT Gateway 環境を削除する。

aws ec2 terminate-instances --instance-ids <NAT 検証用インスタンス ID> \
  --region ap-northeast-1
(出力あり。省略)

インスタンスが完全に削除されるまで待つ(ENI がサブネット上に残っているとサブネット削除が失敗する)。

aws ec2 wait instance-terminated --instance-ids <NAT 検証用インスタンス ID> \
  --region ap-northeast-1
(出力なし)
aws ec2 delete-nat-gateway --nat-gateway-id <NAT Gateway ID> \
  --query 'NatGatewayId' --output text \
  --region ap-northeast-1
<NAT Gateway ID>

deleted まで数分待つ。

aws ec2 describe-nat-gateways --nat-gateway-ids <NAT Gateway ID> \
  --query 'NatGateways[0].State' --output text \
  --region ap-northeast-1
deleted
aws ec2 release-address --allocation-id <EIP アロケーション ID> \
  --region ap-northeast-1
(出力なし)
aws ec2 disassociate-route-table --association-id <関連付け ID> \
  --region ap-northeast-1
(出力なし)
aws ec2 delete-route-table --route-table-id <ルートテーブル ID> \
  --region ap-northeast-1
(出力なし)
aws ec2 delete-subnet --subnet-id <プライベートサブネット ID> \
  --region ap-northeast-1
(出力なし)

SSM ロールのクリーンアップ(補足 2 全体の完了後)

補足 2 の全検証が終わったら、共通で作成した SSM ロールと一時ファイルを削除する。

aws iam remove-role-from-instance-profile --instance-profile-name VPCBPATestSSMRole \
  --role-name VPCBPATestSSMRole
aws iam delete-instance-profile --instance-profile-name VPCBPATestSSMRole
aws iam detach-role-policy --role-name VPCBPATestSSMRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam delete-role --role-name VPCBPATestSSMRole
(各コマンド出力なし)
rm -f /tmp/ssm-trust.json /tmp/user-data-curl.sh

Amazonアソシエイトリンク