Fastly と S3 で静的コンテンツの配信と HTTPS 化をしてみた

2023.11.15

こんにちは、AWS事業本部の平木です!

今回は、 Fastly と S3 で静的コンテンツの配信と HTTPS 化のため Fastly TLS での TLS の有効化を行ってみました。

構成図

独自ドメインのサブドメイン (例)fastly.example.com を使用し、Fastly と S3 を使用した静的コンテンツの配信を行えるようにします。

前提

例で提示しているような example.com については、Route53 のパブリックホストゾーンで管理しているものとします。

Fastly のアカウント作成

下記ページより必要情報を入力することで簡単にサインアップ可能です。
サインアップすると自動的にスタンダードサポートのプランにてアカウントを使用できます。

アクセスキーの払い出し

S3 のプライベートバケットを Fastly で使用するには、後述するカスタム VCL を使用して認証を行う必要があります。
このコードの中でアクセスキー、シークレットアクセスキーの指定が必要なため払い出す必要があります。

このアクセスキーを持つ IAM ユーザーの権限は、当該バケットに対しての読み取り権限( AmazonS3ReadOnlyAccess など)を付与してください。

S3 バケットの作成

オリジンとなるプライベートバケットを作成します。
また、静的コンテンツとなる index.html をアップロードしました。

バケットポリシーには下記のように記載します。(あくまで一例です。)

※補足
今回のケースでバケットポリシーは不要ですが、
Fastly の IP 以外の利用を拒否したい場合など特記してポリシーを記載したい場合に活用ください。
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "PublicReadGetObject",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::<作成したバケット名>/*",
			"Condition": {
				"IpAddress": {
					"aws:SourceIp": [
						<Fastly の Public IP list>
					]
				}
			}
		}
	]
}

Fastly の Public IP list は、こちらで確認できます。

Fastly CDN の作成

Fastly のアカウント作成後、Home から「Create a Delivery service」を押下します。

ドメインの追加

Domains の項目にて使用したいドメイン(以下、対象ドメイン名 と呼びます)を入力し「Add」を押下します。

Fastly では、Apex ドメインの使用は推奨されていないため、サブドメインを利用したほうが良いです。
また、ドメインは他のユーザーが使用している場合には下記エラーが表示され利用できないためご注意ください。

Domain [domain name] is taken by another customer. Domain [domain name] is owned by another customer.

オリジンの追加

①左の Origins をクリックし、②作成した S3 バケットのホスト名を入力し、
③「Add」を押下します。

ホスト名の入力形式は以下です。
<BUCKET>.s3.<REGION>.amazonaws.com

鉛筆マークを押すと設定を修正できます。
もし下記のようになっていなければ下記のように訂正します。

  • Enable TLS?: Yes, enable TLS and connect securely using port
  • Verify certificate?: Yes, verify the authenticity of the TLS certificate
  • Certificate hostname: (先ほど入力した S3 のホスト名)
  • SNI hostname: (先ほど入力した S3 のホスト名)

その他はデフォルトで大丈夫です。

修正した場合は、一番下の「Update」を押下します。

VCL スニペットの作成

S3 のプライベートバケットを Fastly で使用するには、Amazon のヘッダーベースの認証のバージョン4を実装する必要があり、カスタム VCL を使用して行うことができます

そのためレギュラー VCL スニペットを作成します。

①左の「VCL snippets」を選択し、②「Create your first VCL snippet」を押下します。

続いて Name には任意のもの (例)AWS protected origin を、
Type (placement of the snippet) には、within subroutineを選択し、プルダウンから miss (vcl_miss) を選択します。

VCL には下記変数置き換え入力します。

  • YOUR_AWS_ACCESS_KEY: アクセスキー
  • YOUR_AWS_SECRET_KEY: シークレットアクセスキー
  • YOUR_AWS_BUCKET_NAME: バケット名
  • YOUR_AWS_BUCKET_REGION: バケットを作成したリージョン名
declare local var.awsAccessKey STRING;
declare local var.awsSecretKey STRING;
declare local var.awsS3Bucket STRING;
declare local var.awsRegion STRING;
declare local var.canonicalHeaders STRING;
declare local var.signedHeaders STRING;
declare local var.canonicalRequest STRING;
declare local var.canonicalQuery STRING;
declare local var.stringToSign STRING;
declare local var.dateStamp STRING;
declare local var.signature STRING;
declare local var.scope STRING;
set var.awsAccessKey = "YOUR_AWS_ACCESS_KEY";   # Change this value to your own data
set var.awsSecretKey = "YOUR_AWS_SECRET_KEY";   # Change this value to your own data
set var.awsS3Bucket = "YOUR_AWS_BUCKET_NAME";   # Change this value to your own data
set var.awsRegion = "YOUR_AWS_BUCKET_REGION";   # Change this value to your own data
if (req.method == "GET" && !req.backend.is_shield) {
  set bereq.http.x-amz-content-sha256 = digest.hash_sha256("");
  set bereq.http.x-amz-date = strftime({"%Y%m%dT%H%M%SZ"}, now);
  set bereq.http.host = var.awsS3Bucket ".s3." var.awsRegion ".amazonaws.com";
  set bereq.url = querystring.remove(bereq.url);
  set bereq.url = regsuball(urlencode(urldecode(bereq.url.path)), {"%2F"}, "/");
  set var.dateStamp = strftime({"%Y%m%d"}, now);
  set var.canonicalHeaders = ""
    "host:" bereq.http.host LF
    "x-amz-content-sha256:" bereq.http.x-amz-content-sha256 LF
    "x-amz-date:" bereq.http.x-amz-date LF
  ;
  set var.canonicalQuery = "";
  set var.signedHeaders = "host;x-amz-content-sha256;x-amz-date";
  set var.canonicalRequest = ""
    "GET" LF
    bereq.url.path LF
    var.canonicalQuery LF
    var.canonicalHeaders LF
    var.signedHeaders LF
    digest.hash_sha256("")
  ;
  set var.scope = var.dateStamp "/" var.awsRegion "/s3/aws4_request";
  set var.stringToSign = ""
    "AWS4-HMAC-SHA256" LF
    bereq.http.x-amz-date LF
    var.scope LF
    regsub(digest.hash_sha256(var.canonicalRequest),"^0x", "")
  ;
  set var.signature = digest.awsv4_hmac(
    var.awsSecretKey,
    var.dateStamp,
    var.awsRegion,
    "s3",
    var.stringToSign
  );
  set bereq.http.Authorization = "AWS4-HMAC-SHA256 "
    "Credential=" var.awsAccessKey "/" var.scope ", "
    "SignedHeaders=" var.signedHeaders ", "
    "Signature=" + regsub(var.signature,"^0x", "")
  ;
  unset bereq.http.Accept;
  unset bereq.http.Accept-Language;
  unset bereq.http.User-Agent;
  unset bereq.http.Fastly-Client-IP;
}

入力できたら「Create」を押下します。

アクティベート

画面一番上の「Activate」を押下し、サービスをアクティブにします。

疎通確認(HTTP)

下記 URL でアクセスできるか確認します。

(対象ドメイン名).global.prod.fastly.net/index.html

curl でも確認してみました。

C:\Users\UserName>curl -I fastly.example.com.global.prod.fastly.net
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 579
x-amz-id-2: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: xxxxxxxxxxxxxxxx
x-amz-bucket-region: ap-northeast-1
Content-Type: application/xml
Server: AmazonS3
Accept-Ranges: bytes
Date: Tue, 14 Nov 2023 15:24:49 GMT
Via: 1.1 varnish
Age: 8
X-Served-By: cache-nrt-rjtf7700040-NRT
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1699975489.354171,VS0,VE135

ドメインの TLS の設定

ドメインの HTTPS を有効化させるため、Fastly TLS を使用して TLS の設定を行います。

①タブの「Secure」を押下し、②「Manage certificates」を押下します。

Domains に対象ドメイン名を入力後、「Add」を押下し、「Submit」を押下します。

この段階では、ドメイン検証待ちに入ります。

対象ドメインを Route53 で管理している場合は、下記 CNAME レコードを追加します。

レコード名 タイプ ルーティング
_acme-challenge.(対象ドメイン名) CNAME シンプル Verification の項目に記載されている DNS 名
(対象ドメイン名) CNAME シンプル t.sni.global.fastly.net

時間が経過すると、検証が完了し TLS が有効化されます。

疎通確認(HTTPS)

TLS の有効化が完了したため今度は HTTPS で疎通確認してみます。

HTTPS で対象ドメイン名でブラウザから確認すると無事疎通できていることが確認できました。

以上で無事、Fastly と S3 で静的コンテンツの配信を HTTPS で確認できました。

参考

おわりに

今回は、Fastly と S3 で配信する静的コンテンツの HTTPS 化してみました。

Fastly CDN を構築するのみであれば流れは非常にシンプルで分かりやすく感じました。 実際に触ってみることで各機能についての理解が深まってきたのでこれからも様々な検証を行いたいと思います。

この記事がどなたかの役に立てば嬉しいです。