CloudFront Functionsによるアクセス制御

# はじめに
[以前](https://qiita.com/mitzi2funk/items/7dfd07ec00b916b8dfc6#-cloudfront-function)、AWS WAF によるアクセス制限(IP制限)に関して投稿した際、比較対象として2021年にリリースされた**CloudFront Functions**(以降 CF Functions)を取り上げました。
>・昨年リリースされた新機能。Lambdaに比べ簡易なjavascriptしか書けませんが、その分低コスト、高速処理が売り。そして、スクリプトを利用することで柔軟な対応が可能。
> 本件ですとRequest Headerより送信元IPを判別し、任意の宛先へリダイレクトさせることも出来るかと思います。
> (apacheのように時間設定も出来そうです。こちら近い内に試してみたく思います)

現在CF経由のアクセス制御にはWAFでもLambda@Edgeでもなく、全面的に**CF Functions**を利用しています。そこで今回、どのように利用しているかをご紹介致します。


# メリット
## 🔵 コストパフォーマンスがよい

### ◎ 引用
・前回の記事より
[5-1. 本WAFの月額利用料](https://qiita.com/mitzi2funk/items/7dfd07ec00b916b8dfc6#5-1-%E6%9C%ACwaf%E3%81%AE%E6%9C%88%E9%A1%8D%E5%88%A9%E7%94%A8%E6%96%99)
[5-2. CloudFront Functionsの料金と比較](https://qiita.com/mitzi2funk/items/7dfd07ec00b916b8dfc6#5-2-cloudfront-functions%E3%81%AE%E6%96%99%E9%87%91%E3%81%A8%E6%AF%94%E8%BC%83)


### ◎ CF Functionの利用料金
公式Docより[CF Functionsの料金](https://aws.amazon.com/jp/cloudfront/pricing/) の [エッジコンピューティング][無料利用枠]より

■ 無料利用枠
> 2,000,000 件の CloudFront Function 呼び出し

■ 利用料金
> 100 万件の呼び出しあたり 0.10 USD


### ◎ AWS WAFとの比較
**条件** : アクセス制御対象が 200万Req/mon だった場合
**比較結果** : CF Functionsならば無料枠だけで対処可能。WAFだとシンプルな設定内容(※)でも 9.20USD/mon 掛かる
(※) IPアクセス制限を適用した場合。詳細は上述した前回の投稿を参照ください


### ◎ Lambda@Edgeとの比較
**条件** : (同上)
**比較結果** : CF Functionsの場合、無料で利用可。Lambda@Edgeの場合、1.2 USD + メモリ使用量別の実行時間課金

▽ 補足
[Lambda@Edgeの料金](https://aws.amazon.com/jp/lambda/pricing/#Lambda.40Edge_Pricing)
 > リクエスト料金は、リクエスト 100万件あたり 0.60USD
 > 時間は、1 GB-秒の使用につき 0.00005001USD
 > [現在、Lambda@Edge に無料利用枠はありません。](https://aws.amazon.com/jp/cloudfront/pricing/)


## 🔵 機能が充実かつ使いやすい

・CloudFrontへのアクセス(HTTP Request)制御であれば充分な機能が備わっています

01) **Serverless**
-> Lambda@Edge そして AWS WAFも同様ですね

02) **簡易言語であるJavaScript対応** (正確には[ECMAScript 5.1準拠](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html))

03) **処理が早い**
[CloudFront Functions を使用したエッジでのカスタマイズ](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html)より
    > CloudFront Functions の runtime 環境は、起動時間が 1 ミリ秒未満、毎秒数百万のリクエストを処理するようにすぐにスケールでき、高い安全性を誇ります

04) **開発が容易**
・build/deploy不要。コンソールよりソースコード直接記入でもOK
※ AWS CLIによる CF Functions作成からソースコード・アップロードも可([参照](https://github.com/aws-samples/amazon-cloudfront-functions))

05) **CI/CDに関して、IaCとの統合管理も可能**
・ちなみにTerraformの一部として私は管理しています

06) **簡易テスト機能**
・直感的かつシンプルにテストが実施可能

07) **CWログ出力あり**
・標準で N.Virginia の CW logsに[**/aws/cloudfront/function/(Function名)**] としてログが出力される


# デメリット
## 🟣 機能面
01) 簡易スクリプトのみ対応のため、利用できる関数や定義に制限がある

02) 対象はCFへの Viewer Request/Viewer Responseに限られる(Origin Req/Resは対象外)



# 実装例
では実際にCFへのリクエスト(viewer request)に対し行っている制御を挙げます

## 機能一覧
1. **Basic Authentication**(ベーシック認証)
2. **Path Completion**(パス補完)
3. **Maintenance Mode with time control**(メンテナンスモード切り替え、時間指定付き)
4. **IP restriction**(アクセス元IPアドレスによる制御)
5. **referer restriction**(アクセス元refererによる制御)
6. **URI(path) check**(アクセス先による制御)

## 概要図
### 全体構成図
![CF Functionsによるアクセス制御-全体構成(一部).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50310/82df0145-ee3b-e05f-f61b-4e738cf5ae99.png)

### CF Functionsによるアクセス制御部
・上記全体構成よりCF Functionsの箇所をピックアウト
![CF Functionsによるアクセス制御-アクセス制御.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50310/ac8e44ae-4200-e689-742b-38eddebae428.png)


## コーディング
### 要点
・HTTP Request/Responseは、json形式の**event**ととしてCF Functionsで受け取ります。
そして、受け取ったオブジェクトを活用して制御していくことになります。

公式より[イベントオブジェクトの例](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-event-structure.html#functions-event-structure-example)
```json
{
  "version": "1.0",
  "context": {
    "distributionDomainName": "d111111abcdef8.cloudfront.net",
    "distributionId": "EDFDVBD6EXAMPLE",
    "eventType": "viewer-response",
    "requestId": "EXAMPLEntjQpEXAMPLE_SG5Z-EXAMPLEPmPfEXAMPLEu3EqEXAMPLE=="
  },
  "viewer": {
    "ip": "198.51.100.11"
  },
  "request": {
    "method": "GET",
    "uri": "/media/index.mpd",
    "querystring": {
      "ID": {
        "value": "42"
      },
      "TTL": {
        "value": "1440"
      },
      （省略)
    },
    "headers": {
      "host": {
        "value": "video.example.com"
      },
      "user-agent": {
        "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0"
      },
      "accept": {
        "value": "application/json",
        "multiValue": [
          {
            "value": "application/json"
          },
          (省略)
        ]
      },
      "accept-language": {
        "value": "en-GB,en;q=0.5"
      },
      "accept-encoding": {
        "value": "gzip, deflate, br"
      },
      "origin": {
        "value": "https://website.example.com"
      },
      "referer": {
        "value": "https://website.example.com/videos/12345678?action=play"
      },
      "cloudfront-viewer-country": {
        "value": "GB"
      }
    },
    "cookies": {
      "Cookie1": {
        "value": "value1"
      },
    (省略)
    }
  },
```

### サンプルコード
[サンプルコード](https://github.com/mitzi2funk/aws-cloudfront-functions)(私のGitHubより)

#### 補足説明
1. **Basic Authentication**(ベーシック認証)
・特定のRequestに対して、Basic認証をかけています

2. **Path Completion**(パス補完)
・ファイル指定のないRequest(/で終わる)の場合、index.htmlを参照させるようにしています

3. **Maintenance Mode with time control**(メンテナンスモード切り替え、時間指定付き)
・指定時間にメンテナンスモード(=メンテナンスページへリダイレクト)へ切り替え、指定時間に解除されるよう実装しています

4. **IP restriction**(アクセス元IPアドレスによる制御)
・よりセキュアなアクセス領域の制限として、アクセス元のIPアドレスを評価しています

5. **referer restriction**(アクセス元refererによる制御)
・図にありますとおり、メインサイトからのみアクセスを許可したいOriginがある場合に適用しています

6. **URI(path) check**(アクセス先による制御)
・特定のURI(パス)への制御になります


### リダイレクトにquerystringを含める場合
01) サンプルスクリプトには含めていませんが、**パス**および**query**をリダイレクト先に引き継ぎたい場合、URIおよびquerystringもリダイレクト先に含める必要があることご注意ください。
    ```http
    例)
    https://abc.example.com/index.html?region=ap-southeast-1
         ↓ (redirect)
    https://def.example.com/index.html?region=ap-southeast-1
    ```
02) またquerystringが複数ある場合、それを判定し処理する必要があることも合わせてご注意ください。

#### 参考
[[aws-samples/amazon-cloudfront-functions](https://github.com/aws-samples/amazon-cloudfront-functions)]の issuesより
- [Cut the .html from uri but remain querystring #7](https://github.com/aws-samples/amazon-cloudfront-functions/issues/7)
- [Redirect preserve query string #11](https://github.com/aws-samples/amazon-cloudfront-functions/issues/11)

## Tips
:::note info
リダイレクト処理の注意点
・メンテナンスやエラー画面へのリダイレクト処理に関しまして
解除後にCFおよびブラウザのキャッシュにより、想定通りの挙動とならないことがあります。その対処方法としまして、リダイレクト先のパスをCF Behaviorとして登録。そして、**object cachingをしない**様に設定しておくとスムースに切り替えることが出来るかと思います。
![behavior no caching.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50310/9816776a-42e8-1e0c-7528-f670ae35c9be.png)
図. behaviorにて特定パス(リダイレクト先)ではcacheしないよう設定
:::