Lambda@Edgeを使えば、CloudFrontでの配信にカスタムヘッダーを付与できる話

今までLambdaをさわってきたものの、未体験だったLambda@Edgeを使ってみた話です。

2016年のre:Inventで発表されたサービスであり、新しいものではありません。

記事中では、Labda@Edgeに関する情報と、S3においた静的ファイルをCloudFrontで配信している場合に、カスタムヘッダーを付与する方法を事例として紹介してます。

# Lambda@Edgeとは

クライアントからCloudFrontにアクセスしたときの、特定のタイミング（後述）でLambda関数を実行できるサービスです。

# 実行されるタイミング

Lambda関数が実行されるタイミングとしては、

以下の四つがあります。

|タイプ|実行のタイミング|ユースケース例|
|---|---|---|
|Viewer Request|CloudFront がEnd userからリクエストを受信した後|CloudFrontで配信しているサイトにBasic認証をかけたいとき|
|Viewer Response|CloudFront がEnd userにレスポンスを転送する前|CloudFrontで配信している場合に、カスタムヘッダーを付与したいとき|
|Origin Request|CloudFront がリクエストをOriginサーバーに転送する前|CloudFront の特定のパスでアクセスを受けた時に、そのURLを書き換えたいとき|
|Origin Response|CloudFront がOriginからレスポンスを受信した後|Originから返却されたレスポンスがエラーステータスコードで、 それを200-OK に更新したいとき|

![cloudfront-events-that-trigger-lambda-functions.png](https://qiita-image-store.s3.amazonaws.com/0/88357/b9b78732-8c35-ae68-850a-17819c59a756.png)
（引用 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-edge.html）

本記事の具体例においてはOrigin serverがS3となります。

# 制約

制約は結構あります（2018年3月現在）。目立ったところを記述すると

- **バージニア北部**をリージョンとして使う必要がある。
- 追加できるHTTPのヘッダーに制限がある。
- Lambda関数の実行時間、最大圧縮サイズに制限がある。
- 実行環境としては、Node.js 6.10しかない。
- Originイベント（CloudFrontとOriginサーバ間でのイベント）とViewイベント（End UserとCloudFront間でのイベント）で実行できるリソースの量が異なる。Originイベントのほうが制約が緩い。
- コードからのネットワーク通信不可（DB等にアクセスできない）

[詳細はこちら](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html)

# 使ってみた

## 状況

S3で静的ファイルを配信している。CloudFrontフロント経由で配信しているが、カスタムヘッダーを付与したい。

これを実現するには以下のステップを踏みます。

- Lambda@Edge用のIAMロールの作成
- Lambda@Edge用のLambda関数作成
- Lambda@EdgeをCloudFrontと紐付ける


作業の前提として

- S3のバケット
- CloudFrontでの配信

の作成/設定はできているものとします。

## IAMロールの作成

作成するIAMアクセス権限としては、Lambda 関数を CloudFront ディストリビューションに関連付けるために次の IAM アクセス権限が必要です。

詳細は、

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-edge.html

の**「Lambda@Edge 用の IAM アクセス権限とロールの設定」**のセクションを参考にください。

忘れやすい注意点としては、**サービスプリンシパル lambda.amazonaws.com と edgelambda.amazonaws.com が引き受けることができる IAM ロールを作成する必要があります。**

実作業しては、「Edit Trust Relationship」で`lambda.amazonaws.com`, `edgelambda.amazonaws.com`を追加します。


## Lambda関数作成

カスタムヘッダに、Content Security Policyに関する情報を追加したいとします。コードは以下のようになります。

```js
exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;
    
    headers['content-security-policy'] = [{
        key:   'Content-Security-Policy', 
        value: "default-src 'self'"
    }];

    callback(null, response);
};
```

コード記述したあとは、バージョニングをする必要があります。Lambda関数の画面上部の「Actions」より「Publish New Version」をクリックすることで可能です。バージョニングしたものに関しては、「Add Triggers」よりCloudFrontが選択可能になります。

## CloudFrontと紐付ける

CloudFrontを追加したあとは以下のような画面が出るので、そちらを記入していきます。DistributionにはCloudFront DistributionのIDを入れます。

<img width="1077" alt="スクリーンショット 2018-03-18 0.11.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/88357/1602e688-8d48-3b2f-8563-2e96ae6b36da.png">

記入したら、関数のSaveを忘れないようにしましょう。

CloudFrontの方の画面を確認すると、StatusがDeploy中になり、Deployされると、実際にHttp Headerに追加されていることが確認できます。




# 参考
[AWS Lambda@Edge](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-edge.html)
**[CloudFront と Lambda@Edge の使用 » 関数の例](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/Devel*operGuide/lambda-examples.html#lambda-examples-a-b-testing)** <- **自分がyく確認するURL**
[Amazon CloudFrontとAWS Lambda@EdgeでSPAのBasic認証をやってみる](https://dev.classmethod.jp/cloud/aws/cloudfront-lambdaedge-basic-spa/)
[Lambda@Edge で URLパスを書き換える](https://dev.classmethod.jp/cloud/aws/lambdaedge-rewrite-url-path/)
[Serving custom headers from static sites on CloudFront/S3 with Lambda@Edge](https://medium.com/@tom.cook/edge-lambda-cloudfront-custom-headers-3d134a2c18a2)