セキュリティグループでアプリケーションへのアクセスを制御する

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

はじめに

配送アプリケーションのパブリックアクセス経路はすでに動作していますが、用意済みのアクセスルールでは、すべての IPv4 送信元から HTTP を許可しています。アプリケーションに専用のセキュリティグループを割り当て、想定するクライアントとポートだけを許可します。実際のリクエストで、送信元・ポートの制限とステートフルな応答がアクセスに与える影響を確認します。

先に「パブリックサブネットをインターネットに接続する」を完了してください。この新しい環境には専用のネットワーク、パブリックアドレス、ルート、アプリケーションが用意され、以前の VM は再利用しません。CLI は設定済みです。用意済みのリソースと、演習に無関係な参照ネットワークを維持してください。削除する練習用リソースは、新しく作るセキュリティグループだけです。

認定試験との関連

この実験では、次の試験項目を実践します。

空の練習用セキュリティグループを関連付ける

このステップでは、動作中のアプリケーションを確認し、広いアクセスを許可するグループを自分の空のグループに置き換えます。

コマンドは Terminal で実行し、その隣の AWS View をクリックします。ビューは CLI と同じリソース状態を読み取ります。application-network、そのパブリック・プライベートサブネット、プライベートアドレス 10.20.1.10 のアプリケーションが表示されます。パブリックアドレスとインターネットゲートウェイへのルートは用意済みなので、変更しないでください。

cd /home/labex/project

Name タグで VPC を選択します。--filters はサーバーの結果を絞り、--query は ID を選び、$(...) は結果をシェル変数に保存します。

VPC_ID=$(aws ec2 describe-vpcs \
  --filters Name=tag:Name,Values=application-network \
  --query 'Vpcs[0].VpcId' \
  --output text)

その VPC 内の用意済みアプリケーションインターフェイスを選びます。

ENI_ID=$(aws ec2 describe-network-interfaces \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=application-interface \
  --query 'NetworkInterfaces[0].NetworkInterfaceId' \
  --output text)

セキュリティグループは、関連付けたリソースのネットワークインターフェイスで許可するトラフィックを制御します。インバウンドルールはアプリケーションに到着する通信、アウトバウンドルールはアプリケーションが開始する通信を許可します。後で元に戻せるよう、唯一の用意済みグループの ID を保存します。[0] は、この1グループだけのリストの最初の項目を選びます。

SUPPLIED_GROUP_ID=$(aws ec2 describe-network-interfaces \
  --network-interface-ids "$ENI_ID" \
  --query 'NetworkInterfaces[0].Groups[0].GroupId' \
  --output text)

ルールを変更せずに読み取ります。

aws ec2 describe-security-groups \
  --group-ids "$SUPPLIED_GROUP_ID" \
  --query 'SecurityGroups[].{ID:GroupId,Inbound:IpPermissions,Outbound:IpPermissionsEgress}' \
  --output json

用意済みグループは 0.0.0.0/0、つまりすべての IPv4 送信元からのインバウンド TCP ポート 80 と、すべてのアウトバウンド通信を許可します。HTTP はリクエストとレスポンスを使い、ポートは受信サービスを識別します。このアプリケーションは TCP ポート 80 と 8081 で HTTP を提供しますが、用意済みルールが許可するのは 80 だけです。

AWS View で Request application · client A、続けて Request application · client B をクリックします。どちらもポート 80 で Success と Application online を返します。クライアント A は 198.51.100.10、B は 198.51.100.20 を使います。Request port 8081 をクリックすると、このポートは許可されていないため、クライアント A のリクエストは失敗します。

同じ VPC に別のグループを作ります。--group-name は名前、--description は用途を指定します。引用符で囲んだタグ指定は、所有を識別するタグを1つの引数で追加します。新しい ID を保存します。

GROUP_ID=$(aws ec2 create-security-group \
  --group-name parcel-web \
  --description "Parcel HTTP access" \
  --vpc-id "$VPC_ID" \
  --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=parcel-web},{Key=Project,Value=parcel}]' \
  --query 'GroupId' \
  --output text)

新しいグループにはインバウンドの許可がなく、すべてのアウトバウンド通信が許可されます。セキュリティグループにあるのは許可ルールで、明示的な拒否ルールではありません。許可ルールに一致しない通信はブロックされます。

--groups はインターフェイスのグループ一覧を置き換えます。新しいグループだけを関連付けます。広いアクセスを許可するグループも残すと、両方の許可が合算され、両クライアントのアクセスが引き続き許可されます。

aws ec2 modify-network-interface-attribute \
  --network-interface-id "$ENI_ID" \
  --groups "$GROUP_ID"

変更に成功すると出力はありません。自分のグループだけが関連付いていることを確認します。

aws ec2 describe-network-interfaces \
  --network-interface-ids "$ENI_ID" \
  --query 'NetworkInterfaces[].{ID:NetworkInterfaceId,Groups:Groups}' \
  --output json

AWS View に parcel-web と Inbound · none が表示されます。A と B から新しいリクエストを送ると、両方とも Connection failed になります。パブリックアドレスとルートは残っていますが、空のグループはインバウンドリクエストを許可しません。保存した ID を維持するため、この Terminal は開いたままにしてください。

想定する HTTP クライアントだけを許可する

このステップでは、クライアント A にポート 80 へのアクセスを許可し、もう一方の送信元とポートはブロックされることを確認します。

インバウンドルールはプロトコル、ポート範囲、送信元を指定します。--protocol tcp は TCP、--port 80 はこの1ポート、--cidr 198.51.100.10/32 はクライアント A の IPv4 アドレスだけを選びます。/32 は1つの IPv4 アドレスを含み、0.0.0.0/0 より限定的です。

用意済みグループではなく、自分のグループにルールを追加します。

aws ec2 authorize-security-group-ingress \
  --group-id "$GROUP_ID" \
  --protocol tcp \
  --port 80 \
  --cidr 198.51.100.10/32

応答は成功を示し、新しいルール ID が含まれる場合があります。すべてのインバウンドルールを読み取ります。

aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].IpPermissions' \
  --output json

TCP ルールが1つあり、FromPort と ToPort は両方 80、送信元は 198.51.100.10/32 です。AWS View も同じ送信元とポートを表示します。

Request application · client A をクリックします。結果は Success、送信元 198.51.100.10、宛先ポート 80、本文 Application online です。これは用意済みアプリケーションからの実際のレスポンスです。

次に Request application · client B と Request port 8081 をクリックします。両方とも Connection failed です。最初は送信元が不一致、次は A からでもポートが不一致です。パブリックアドレスとルートが経路を提供し、セキュリティグループがその経路を使える通信を決めます。

一時的なポート許可を試して削除する

このステップでは、2つ目の待受ポートが専用ルールのある場合だけ到達可能になることを示し、一時的な許可を削除します。

用意済みアプリケーションはポート 8081 でも HTTP を提供します。許可する送信元はそのままに、このポート用の一時的なルールを追加します。

aws ec2 authorize-security-group-ingress \
  --group-id "$GROUP_ID" \
  --protocol tcp \
  --port 8081 \
  --cidr 198.51.100.10/32

ルールをもう一度読み取ります。

aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].IpPermissions' \
  --output json

TCP ルールは 80 と 8081 の2つになり、どちらもクライアント A のみを許可します。AWS View に両方が表示されます。Request port 8081 をクリックすると、結果は Success、宛先ポート 8081、本文 Application online に変わります。2つ目のサービスは動作しており、以前の失敗はアクセスルールの制限だったと確認できます。

一時的な TCP 8081 ルールがクライアント A の実際のリクエストを許可する

AWS View の例:限定的なインバウンドルールが2つ存在し、A はポート 8081 でアプリケーションの応答を受け取ります。生成されるリソース ID は環境によって異なります。

この演習で必要なのはポート 80 だけです。ルールの取り消しは、その許可を削除します。同じプロトコル、ポート、送信元を指定し、一時的なルールだけを削除します。

aws ec2 revoke-security-group-ingress \
  --group-id "$GROUP_ID" \
  --protocol tcp \
  --port 8081 \
  --cidr 198.51.100.10/32

応答は成功を示します。変更後のルールを問い合わせます。

aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].IpPermissions' \
  --output json

A のポート 80 ルールだけが残ります。AWS View から 8081 ルールが消えたら、再度 Request port 8081 をクリックします。今度は失敗します。A の 80 は引き続き成功し、B の 80 は引き続き失敗します。アプリケーションやルートは置き換えず、1つのポート許可だけを変更しました。

ステートフルな HTTP 応答を観察する

このステップでは、自分のグループの既定アウトバウンドルールを削除し、許可されたインバウンド HTTP への応答が引き続き機能することを確認します。

セキュリティグループはステートフルです。許可されたインバウンドリクエストへの応答は、新しい接続を許可するアウトバウンドルールがなくてもアプリケーションから送信できます。アウトバウンドルールはアプリケーションが開始する通信を制御し、この HTTP 応答を許可するためには必要ありません。

現在のアウトバウンド許可を読み取ります。

aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].IpPermissionsEgress' \
  --output json

新しいグループには、すべての通信に対する既定の宛先 0.0.0.0/0 があります。次のコマンドの --ip-permissions は、ルール指定の JSON リストを引用符で囲んだ1つの引数として受け取ります。IpProtocol の -1 はすべてのプロトコル、IpRanges はアウトバウンドルールの宛先を表します。その既定ルールを正確に削除します。

aws ec2 revoke-security-group-egress \
  --group-id "$GROUP_ID" \
  --ip-permissions '[{"IpProtocol":"-1","IpRanges":[{"CidrIp":"0.0.0.0/0"}]}]'

応答は成功を示します。両方向のルールを読み取ります。

aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].{Inbound:IpPermissions,Outbound:IpPermissionsEgress}' \
  --output json

インバウンドは A のポート 80 だけを許可し、アウトバウンドは空です。AWS View は Outbound · none を表示します。再度 Request application · client A をクリックすると、Success と Application online が返ります。応答は許可済みのインバウンド接続に属するためです。

Request application · client B と Request port 8081 を再確認します。両方とも失敗します。ステートフルな応答は、別の送信元やポートへの新しいインバウンドアクセスを許可しません。このテストは応答の動作を確認し、アプリケーションが開始する新しいアウトバウンド接続はテストしません。

アウトバウンド許可がなくても、許可されたインバウンド HTTP リクエストに応答が返る

AWS View の例:インバウンドは A のポート 80 だけを許可し、アウトバウンドは空ですが、実際の HTTP 応答は成功します。生成されるリソース ID は環境によって異なります。

用意済みグループを復元し、自分のグループを削除する

このステップでは、アプリケーションの元の関連付けを復元し、自分の練習用セキュリティグループだけを削除します。

ネットワークインターフェイスに関連付けられたグループは削除できません。まず、保存した用意済みグループで自分のグループを置き換えます。用意済みグループを削除・編集しないでください。

aws ec2 modify-network-interface-attribute \
  --network-interface-id "$ENI_ID" \
  --groups "$SUPPLIED_GROUP_ID"

関連付けを確認します。

aws ec2 describe-network-interfaces \
  --network-interface-ids "$ENI_ID" \
  --query 'NetworkInterfaces[].{ID:NetworkInterfaceId,Groups:Groups}' \
  --output json

元の supplied-application だけが関連付いています。使わなくなった自分のグループを削除します。

aws ec2 delete-security-group --group-id "$GROUP_ID"

削除成功時に出力はありません。取り外せるタグに依存せず、不在を確認するためにグループの全一覧を問い合わせます。

aws ec2 describe-security-groups \
  --query 'SecurityGroups[].{ID:GroupId,Name:GroupName,VPC:VpcId}' \
  --output table

parcel-web は存在しません。用意済みグループと既定グループ、VPC、サブネット、アプリケーションインターフェイス、パブリックアドレス、ルートは残っています。一覧取得の失敗は、削除の証明になりません。

AWS View に再び supplied-application が表示されます。A と B のリクエストボタンをクリックすると、両方のポート 80 リクエストは初期状態と同じ Success を返します。変更していない用意済みグループは 80 だけを許可するため、Request port 8081 は失敗します。

このステップの完了チェックを実行してください。

まとめ

専用セキュリティグループを作成・関連付けし、A の TCP ポート 80 だけを許可しました。2つ目のポートの一時許可をテストして取り消し、アウトバウンド許可がなくてもステートフルな HTTP 応答が返ることを確認しました。実際のリクエストで許可・拒否通信を区別し、最後に用意済みグループを復元して自分の練習用グループだけを削除しました。

次の「プライベートサブネットにアウトバウンドアクセスを提供する」では、外部からの要求していないアクセスを許可せず、プライベートアプリケーションのアウトバウンド経路を構築します。