ネットワークを利用者に接続する

AWSBeginner
オンラインで実践に進む

はじめに

アプリケーションには、稼働中のインスタンスとパブリックアドレスがあっても、接続できない場合があります。Terraform で不足しているルートと、範囲を絞ったセキュリティグループルールを定義し、許可される接続とブロックされる接続の両方をテストします。

Terraform のリソース参照、VPC、サブネット、インターネットへのルート、セキュリティグループを理解していることを前提とします。この独立した環境には、対象アプリケーション、別の正常に動作する参照用アプリケーション、準備済みの Terraform 入力値が用意されています。管理するのは、新しく追加する接続用リソースだけです。

認定試験との関連

認定資格 試験のタスク 演習内容
Solutions Architect – Associate (SAA-C03) Task 1.2 インターネットゲートウェイを通じて VPC サブネットを接続し、送信元とポートでアプリケーションへのネットワークアクセスを制限します。

実験の概要

概念の概要:許可されたクライアントはインターネットゲートウェイとデフォルトルートを通じてアプリケーションのサブネットに到達し、セキュリティグループがアプリケーションのポートへのアクセスを制限します。

インターネットへのルートを構築する

このステップでは、アプリケーションの VPC にインターネットゲートウェイをアタッチし、そのサブネットにルートテーブルを明示的に関連付けます。

Terminal と AWS View を開きます。アプリケーション用ネットワークと参照用ネットワークは独立しています。application-server のサブネットは起動時にパブリック IP を割り当てるため、このサーバーにはすでにパブリックアドレスがあります。パブリックアドレスがあるだけでは、インターネットへのルートやインバウンドの許可は作成されません。

AWS View で application-server、User · 198.51.100.10、Application · 8081 を選択し、Send request をクリックします。Blocked と表示されるはずです。同じクライアントとアプリケーションポートで reference-server を選択し、別のリクエストを送信して、参照用アプリケーションが応答することを確認します。この実験を通して、参照用アプリケーションが動作する状態を維持してください。

準備済みの作業ディレクトリに移動します。

cd /home/labex/project/application-access

用意された terraform.tfvars.json には、この新しい環境の VPC、サブネット、セキュリティグループの ID が含まれています。Terraform はこれらの入力値を自動的に読み込みます。対応する変数を使う前に、内容を確認します。

cat terraform.tfvars.json

準備済みのプロバイダー設定は、このコースの環境を選択します。以前の VM から ID をコピーしたり、個人の認証情報を入力したりする必要はありません。後で確認できるよう、用意された対象インスタンスの ID を保存して表示します。$(...) はコマンドの出力を取り込み、--output text は ID をプレーンテキストとして返します。

TARGET_ID=$(aws ec2 \
  describe-instances \
  --filters Name=tag:Name,Values=application-server Name=instance-state-name,Values=running \
  --query 'Reservations[0].Instances[0].InstanceId' \
  --output text)
echo "$TARGET_ID"

インターネットゲートウェイは VPC にアタッチします。ルートテーブルは、宛先に向かうトラフィックをゲートウェイへ送り、サブネットの関連付けによって、どのテーブルをそのサブネットに適用するかを選択します。明示的な関連付けがなければ、サブネットは VPC のメインルートテーブルを使用します。用意されたメインテーブルは変更しないでください。宛先 0.0.0.0/0 は、より具体的なルートのない IPv4 アドレスに一致しますが、セキュリティグループを通過する許可を与えるものではありません。

routing.tf を作成します。引用符付きの区切り語を使ったヒアドキュメントは、HCL をそのまま書き込みます。リソース参照によって、ルートをテーブルとゲートウェイに接続し、サブネットの関連付けも同じテーブルに接続します。

cat > routing.tf <<'HCL'
resource "aws_internet_gateway" "application" {
  vpc_id = var.vpc_id
}

resource "aws_route_table" "application" {
  vpc_id = var.vpc_id
}

resource "aws_route" "internet" {
  route_table_id         = aws_route_table.application.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.application.id
}

resource "aws_route_table_association" "application" {
  subnet_id      = var.subnet_id
  route_table_id = aws_route_table.application.id
}

output "route_table_id" {
  value = aws_route_table.application.id
}

output "subnet_id" {
  value = var.subnet_id
}
HCL

設定を初期化し、整形して検証します。

terraform init
terraform fmt
terraform validate

init が Incomplete lock file information for providers と表示した場合は、他のプラットフォーム向けのチェックサムについての説明です。この準備済み VM は linux_amd64 を使用します。初期化と検証が成功したら続行してください。

ゲートウェイ、テーブル、ルート、関連付けの 4 つの追加を確認します。用意されたインスタンスと参照用ネットワークが、置き換えや削除の対象として表示されてはいけません。

terraform plan

適用し、確認を求められたら yes を入力します。

terraform apply

新しいテーブル ID と、用意されたサブネット ID を保存して表示します。

TABLE_ID=$(terraform output -raw route_table_id)
echo "$TABLE_ID"
SUBNET_ID=$(terraform output -raw subnet_id)
echo "$SUBNET_ID"

ルートテーブルを独立して照会します。デフォルトの宛先、igw-... のゲートウェイ、サブネット ID との関連付けを確認してください。VPC のローカルルートも残っています。

aws ec2 \
  describe-route-tables \
  --route-table-ids "$TABLE_ID" \
  --query 'RouteTables[].{Routes:Routes[].{Destination:DestinationCidrBlock,Gateway:GatewayId},Subnets:Associations[].SubnetId}'

用意されたサブネットで、パブリック IP の割り当てを確認します。

aws ec2 \
  describe-subnets \
  --subnet-ids "$SUBNET_ID" \
  --query 'Subnets[].{CIDR:CidrBlock,PublicIPOnLaunch:MapPublicIpOnLaunch}'

PublicIPOnLaunch は true です。この設定は新しく起動するインスタンスを制御するもので、変更しても既存のインスタンスに後からアドレスを割り当てることはできません。AWS View には、実際のゲートウェイと有効なルートテーブルが表示されます。アプリケーションに User/8081 のリクエストを再送信してください。まだ許可するインバウンドルールがないため、引き続き Blocked になります。

対象クライアントを許可して境界をテストする

このステップでは、User クライアントからアプリケーションポートへの接続を許可し、別のクライアントと別のポートが引き続きブロックされることを検証します。

セキュリティグループは、インスタンスのネットワークインターフェースに適用されるステートフルなネットワーク境界です。ルーティングによって経路が利用できるようになっても、どの着信接続を許可するかはグループが判断します。用意されたアプリケーションのグループには、現時点ではインバウンドルールがありません。

このグループ用に、独立したインバウンドルールのリソースを 1 つ作成します。cidr_ipv4 = "198.51.100.10/32" は、User クライアントの単一の IPv4 アドレスを選択します。両方のポートを 8081 に設定することで、アプリケーションのリスナーだけを許可します。参照用アプリケーションは独自のセキュリティグループを使用します。

cat > access.tf <<'HCL'
resource "aws_vpc_security_group_ingress_rule" "application" {
  security_group_id = var.security_group_id
  ip_protocol       = "tcp"
  from_port         = 8081
  to_port           = 8081
  cidr_ipv4         = "198.51.100.10/32"
}

output "security_group_id" {
  value = var.security_group_id
}
HCL

整形して検証し、プランを確認します。インバウンドルールが 1 つ追加され、インスタンスの置き換えはないはずです。

terraform fmt
terraform validate
terraform plan

ルールを適用し、yes で確認します。

terraform apply

既存のグループ ID を保存して表示し、実際のインバウンド許可を読み取ります。

GROUP_ID=$(terraform output -raw security_group_id)
echo "$GROUP_ID"
aws ec2 \
  describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].{Group:GroupId,Ingress:IpPermissions}'

許可の内容は、198.51.100.10/32 から TCP ポート 8081 への接続でなければなりません。Terraform が収束していることを確認します。

terraform plan

No changes. と表示されるはずです。次に、AWS View で毎回 application-server を選択し、3 つのテストをすべて行います。

User · 198.51.100.10 と Application · 8081 を選択し、Send request をクリックします。Connected · 200 とメッセージ Application network connected が表示されるはずです。

実行時の確認画面:実際の対象ネットワーク、選択した送信元、アプリケーションポートで正常なレスポンスが得られています。リソース ID は例です。 送信元を Unrelated · 198.51.100.20 に変更し、ポートは 8081 のままで別のリクエストを送信します。Blocked と表示されるはずです。 送信元を User に戻し、SSH · 22 を選択して、別のリクエストを送信します。Blocked と表示されるはずです。アプリケーションへのリクエストが成功しても、ゲストの別の SSH リスナーへのアクセスが許可されるわけではありません。

結果には、実際の送信元、対象インスタンス、ポートが示されます。Request service unavailable は環境のエラーであり、意図したネットワーク拒否の証拠ではありません。ルールを 0.0.0.0/0 に広げないでください。広げると、同じアプリケーションリスナーが無関係のクライアントにも公開されます。

リソースのフィールドについては、AWS プロバイダーのインバウンドルールのドキュメントを参照してください。CLI、Terraform の対応付け、実際のリクエスト結果は、一致している必要があります。

接続用リソースを削除してアプリケーションを保持する

このステップでは、Terraform が管理する接続用リソースだけを削除し、用意されたアプリケーションがそのまま残ることを確認します。

この作業ディレクトリが所有するのは、インターネットゲートウェイ、ルートテーブル、デフォルトルート、サブネットの関連付け、インバウンドルールの 5 つのリソースです。VPC、サブネット、メインルートテーブル、対象インスタンス、参照用アプリケーションは独立して用意されたものです。これらはこの状態の管理対象外であり、保持しなければなりません。

削除プランを確認します。

terraform plan -destroy

削除対象は 5 つのリソースのはずです。次に削除を実行し、yes を入力します。

terraform destroy

状態に管理対象のリソースアドレスがないことを確認します。

terraform state list

用意された対象インスタンスは、同じ ID で引き続き稼働しているはずです。

aws ec2 \
  describe-instances \
  --instance-ids "$TARGET_ID" \
  --query 'Reservations[].Instances[].{ID:InstanceId,State:State.Name,Subnet:SubnetId}'

AWS View で、対象に User/8081 のリクエストを再送信します。接続用リソースを削除したため、Blocked になるはずです。次に reference-server、User、ポート 8081 を選択し、リクエストを送信します。参照用アプリケーションは、引き続き Connected · 200 と Reference application を返す必要があります。

HCL ファイルは確認用に残っています。後で通常のプランを実行すると、削除したリソースの再作成が提案されます。terraform destroy 後にその設定が残るのは正常です。

まとめ

Terraform の参照を使ってインターネットゲートウェイをアタッチし、デフォルトルートを定義して、アプリケーションのサブネットにルートテーブルを関連付けました。その後、範囲を絞ったインバウンドルールを 1 つ追加し、実際の送信元とポートの境界をテストして収束を確認し、用意されたアプリケーションを保持しながら接続用リソースだけを削除しました。