【プライベート接続】内部ALB + S3で構築するWebサイト

記事タイトルとURLをコピーする

こんにちは。AWS CLIが好きな福島です。

はじめに

今回は、内部ALBとS3を利用し、プライベートなWebサイトを構築したいと思います。

参考情報

https://aws.amazon.com/jp/blogs/news/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/

構成図

実際には、Direct ConnectやSite to Site VPNで企業の拠点から内部のALBにプライベート接続することを想定していますが、 今回は検証目的でパブリックサブネットのEC2から内部のALBにアクセスできるかを確認します。

構成のポイント

  • S3バケット名は、WebサイトのFQDNと同一にする
    例えば、WebサイトのFQDNを test.example.com にする場合、S3バケット名も test.example.com にする必要があります。

  • S3のVPCエンドポイント(Interface型)を利用する
    ALBのターゲットとして、S3のVPCエンドポイントのIPを登録します。

  • ALBのヘルスチェックの成功コードには200に加えて、307,405を設定する
    ALBのターゲットにはS3のVPCエンドポイントをIPで登録するため、 ヘルスチェックのホストヘッダーにはドメイン(例えば、test.example.comなど)が含まれず、 200の成功コードが返ってきません。そのため、307,405の成功コードを追加します。

成功コードにおける補足

以下は、S3のVPCエンドポイントのIPに対して、GETとHEADメソッドでリクエストした際のログです。 GETメソッドの場合は、307、HEADメソッドの場合は、405のステータスコードが返ってきていることが分かります。

[ec2-user@ip-10-88-0-59 ~]$ curl -IX GET http://10.88.10.226
HTTP/1.1 307 Temporary Redirect
x-amz-id-2: 9jRaZuSddQUcR6BLnwDu129sv6heJEqy8IWa/stESeLJpN3m57rlmNPjxV/qyNt7aconrwXVDEM=
x-amz-request-id: KH5S2EFVFKNY2CX6
Date: Thu, 21 Mar 2024 00:34:04 GMT
Location: https://aws.amazon.com/s3/
Server: AmazonS3
Content-Length: 0

[ec2-user@ip-10-88-0-59 ~]$ curl -IX HEAD http://10.88.10.226
HTTP/1.1 405 Method Not Allowed
x-amz-request-id: 7V3Y1MW59P0NWMGM
x-amz-id-2: ShU6PXWLOrvNOLY+nMG1JgyDQONakfaM2MKBgo7V3IgVvFrQ+qkErZKM7jslQSqbawNEIcsfn84=
Allow: GET
Content-Type: application/xml
Date: Thu, 21 Mar 2024 00:34:05 GMT
Server: AmazonS3

[ec2-user@ip-10-88-0-59 ~]$

また、以下はELBのターゲットであるWebサーバ(EC2)のログになりますが、 見る限り、GETメソッドが使われているため、S3へのヘルスチェックは307だけでも良いのかも知れません。 (今回は参考情報に従い、307,405を成功コードに追加します。)

[root@ip-10-88-0-59 httpd]# tail -f access_log
10.88.1.178 - - [21/Mar/2024:09:39:38 +0900] "GET / HTTP/1.1" 200 13 "-" "ELB-HealthChecker/2.0"
10.88.0.219 - - [21/Mar/2024:09:39:49 +0900] "GET / HTTP/1.1" 200 13 "-" "ELB-HealthChecker/2.0"

検証におけるポイント

ALBにアクセスする際のFQDNおよびS3のバケット名は揃える必要があります。

本来は、オンプレミスのDNSサーバーやRoute53にALBのFQDNのCNAMEやエイリアスを設定することになりますが、 準備が大変のため、今回の検証では、ローカル端末のhostsファイルを活用して検証を行います。

動作確認のイメージは以下の通りです。

手順

手順の流れは、以下の通りです。

  • ①メインリソースのデプロイ
  • ②ALBへのターゲット(S3のVPCエンドポイントのIP)登録 ※1
  • ③S3バケットへのHTMLファイルアップロード
  • ④ローカル端末のhostsファイル更新
  • ⑤EC2を踏み台にALBへアクセスする
  • ⑥動作確認

※1 ALBのターゲットには、S3のVPCエンドポイントのIPを登録するのですが、 CloudFormationでは、S3のVPCエンドポイントのIPを取得できないため、手動で実施します。

①メインリソースのデプロイ

  • Git から Clone
git clone git@github.com:kazuya9831/blog-sample.git
  • ディレクトリの移動
cd internal-alb-and-s3
  • 変数の設定
STACK_NAME="private-web"
  • スタックのデプロイ
aws cloudformation deploy \
--stack-name ${STACK_NAME} \
--template-file private-web.yml \
--capabilities CAPABILITY_NAMED_IAM

※デプロイには4,5分程度かかります。

②ALBへのターゲット(S3のVPCエンドポイントのIP)登録

  • S3のVPCエンドポイントのARNをCloudFormationのOutputから取得
S3_VPC_ENDPOINT_ARN=$(aws cloudformation describe-stacks \
    --stack-name "${STACK_NAME}" \
    --query "Stacks[].Outputs[?OutputKey=='S3VpcEndpointId'].OutputValue" \
    --output text
)
  • S3のVPCエンドポイントのIPアドレスを取得
S3_VPC_ENDPOINT_IP=$(aws ec2 describe-network-interfaces \
--network-interface-ids "${S3_VPC_ENDPOINT_ARN}" \
--query "NetworkInterfaces[].PrivateIpAddresses[].PrivateIpAddress" \
--output text)
  • ELBのターゲットグループのARNをCloudFormationのOutputから取得
ELB_TARGET_GROUP_ARN=$(aws cloudformation describe-stacks \
    --stack-name "${STACK_NAME}" \
    --query "Stacks[].Outputs[?OutputKey=='ApplicationLoadBalancerTargetGroupArn'].OutputValue" \
    --output text
)
  • S3のVPCエンドポイントのIPアドレスをALBのターゲットに登録
aws elbv2 register-targets \
--target-group-arn "${ELB_TARGET_GROUP_ARN}" \
--targets Id="${S3_VPC_ENDPOINT_IP}",Port=443

③S3バケットへのHTMLファイルアップロード

  • S3のバケット名をCloudFormationのOutputから取得
S3_BUCKET=$(aws cloudformation describe-stacks \
    --stack-name "${STACK_NAME}" \
    --query "Stacks[].Outputs[?OutputKey=='S3Bucket'].OutputValue" \
    --output text
)
  • S3のバケットにHTMLファイルをアップロード
aws s3api put-object \
--bucket ${S3_BUCKET} \
--key index.html \
--body index.html \
--content-type text/html

④ローカル端末のhostsファイル更新

  • hostsに登録する情報を出力
echo "127.0.0.1 ${S3_BUCKET}"
  • 上記コマンドの実行結果をローカル端末のhostsファイルに追記する
Windows: C:\windows\system32\drivers\etc\hosts
Mac: /etc/hosts

⑤EC2を踏み台にALBへアクセスする

  • EC2のインスタンスIDをCloudFormationのOutputから取得
EC2_INSTANCE_ID=$(aws cloudformation describe-stacks \
    --stack-name "${STACK_NAME}" \
    --query "Stacks[].Outputs[?OutputKey=='Ec2InstanceInstanceId'].OutputValue" \
    --output text
)
  • 内部ALBのDNS名をCloudFormationのOutputから取得
INTERNAL_ALB_DNS_NAME=$(aws cloudformation describe-stacks \
    --stack-name "${STACK_NAME}" \
    --query "Stacks[].Outputs[?OutputKey=='ApplicationLoadBalancerDNSName'].OutputValue" \
    --output text
)
  • SSMのリモートホストポートフォワード(AWS-StartPortForwardingSessionToRemoteHost)を利用
aws ssm start-session \
--target ${EC2_INSTANCE_ID} \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"portNumber":["80"],"localPortNumber":["10080"],"host":["'"${INTERNAL_ALB_DNS_NAME}"'"]}'

⑥動作確認

  • HTMLファイルの確認
curl http://${S3_BUCKET}:10080/index.html
  • 実行結果例
$ curl http://${S3_BUCKET}:10080/index.html

Internal ALB and S3 test
$
  • リダイレクトされるかの確認
curl http://${S3_BUCKET}:10080
  • 実行結果例
$ curl -I http://${S3_BUCKET}:10080
HTTP/1.1 301 Moved Permanently
Server: awselb/2.0
Date: Thu, 21 Mar 2024 08:20:31 GMT
Content-Type: text/html
Content-Length: 134
Connection: keep-alive
Location: https://test-XXXXXXXXXXXX.example.com:80/index.html

$

終わりに

今回は、内部ALB+S3でプライベートなWebサイトを構築する方法をご紹介いたしました。 どなたかのお役に立てれば幸いです。

福島 和弥 (記事一覧)

2019/10 入社

AWS CLIが好きです。

AWS資格12冠。2023 Japan AWS Partner Ambassador/APN ALL AWS Certifications Engineer。