こんにちは。AWS CLIが好きな福島です。
はじめに
今回は、内部ALBとS3を利用し、プライベートなWebサイトを構築したいと思います。
参考情報
構成図
実際には、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サイトを構築する方法をご紹介いたしました。 どなたかのお役に立てれば幸いです。