search
LoginSignup
3

posted at

updated at

CloudFront Functions の活用パターン集

はじめに

CloudFront Functions(以後CF2)は、L@Eと異なり、軽量のため、L@Eより優先して使用を検討する必要がようなので、CF2について使用パターンをまとめました。

ビューアーレスポンスは使用しなかったため、ビューアーリクエストのみのパターンになります。

CF2とL@Eの比較

1 2 3
CloudFront Functions Lambda@Edge
ランタイムサポート JavaScript(ECMAScript 5.1準拠) Node.js、Python
実行場所 218 以上の CloudFrontエッジロケーション 13 の CloudFrontリージョンのエッジキャッシュ
CloudFront トリガー ビューアリクエスト / ビューアレスポンス ビューアリクエスト / ビューアレスポンス / オリジンリクエスト / オリジンレスポンス
最大実行時間 1 ミリ秒未満 5 秒 (ビューアトリガー) / 30 秒 (オリジントリガー)
最大メモリ 2 MB 128 MB (ビューアトリガー) / 10 GB (オリジントリガー)
合計パッケージサイズ 10 KB 1 MB (ビューアトリガー) / 50 MB (オリジントリガー)
ネットワークアクセス なし あり
ファイルシステムアクセス なし あり
リクエスト本文へのアクセス なし あり
料金 無料利用枠あり。リクエストごとに課金。 無料利用枠なし。リクエストと関数の実行時間ごとに課金。
デプロイ反映時間 10秒以下 5分程度
デプロイ方法 複数を一度にデプロイ可能 1つずつデプロイ可能

CF2は、JavaScript(ECMAScript 5.1準拠)のため、constなど使用できない制限があります。下記で使用できる機能が挙げられています。

CF2の方が、軽量、デプロイ反映が早い、という点で始めに検討するとよいかと思います。

IP制限

許可したIPの場合、リクエストが通り、NGの場合403をレスポンスします

function handler(event) {
  var request = event.request;
  var clientIP = event.viewer.ip;
  // 許可するIPを指定
  var IP_WHITE_LIST = ["xxx.xxx.xxx.xxx", "xxx.xxx.xxx.xxx"];
  var allowIP = IP_WHITE_LIST.includes(clientIP);

  if (allowIP) {
    return request;
  } else {
    var response = {
      statusCode: 403,
      statusDescription: "Forbidden",
    };
    return response;
  }
}

アクセス承認

Bearer認証やBasic認証でCF2は使用できます。

Bearer認証

Bearerのトークンパラメータが成功した場合、リクエストが通り、失敗した場合、401エラーをレスポンスという処理になっています。

function handler(event) {

  var request = event.request;
  var headers = request.headers;

  var authString = "Bearer <token値>";

  if (
    typeof headers.authorization === "undefined" ||
    headers.authorization.value !== authString
  ) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: { "www-authenticate": { value: "Bearer" } },
    };
  }
  console.log("request-success");
  return request;
}

ローカルで認証できているか確認する場合、curlを使いましょう。

$ curl https://www.hoge.com/test/ \
  -H "Authorization:Bearer token値"

Basic認証

Basic認証のトークンパラメータが成功した場合、リクエストが通り、失敗した場合、401エラーを返すという処理になっています。

function handler(event) {
  var request = event.request;
  var headers = request.headers;

  // echo -n user:pass | base64
  var authString = "Basic <token値>";

  if (
    typeof headers.authorization === "undefined" ||
    headers.authorization.value !== authString
  ) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: { "www-authenticate": { value: "Basic" } }
    };
  }
  console.log("request-success");
  return request;
}

URLの書き換え

/test/のパスを/fuga/に書き換える

function handler(event) {
  var request = event.request;
  var uri = request.uri;

  if (uri.startsWith("/test/")) {
    request.uri = "/fuga/";
  }
  return request;
}

CFの代替ドメイン名 (CNAME)に記載がないホスト名は、ビューアーリクエストで書き変えることは、基本的にできません。
CNAME に複数指定するよう *.hoge.com等の対応するとできますが、それ以外の方法でホストを書き換えたい場合、L@Eのオリジンリクエストで実現できます。

サブディレクトリのドキュメントルートを指定

静的サイトをS3に構築した場合、サブドメイン名と同じS3のディレクトリをルーティングできます。

fuga.hoge.comの場合、S3のあるバケットのfugaディレクトリを指定する。

function handler(event) {
  var request = event.request;
  var uri = request.uri;
  var host = request.headers.host.value;

  var prefix = host.split(".", 1)[0];
  if (uri.startsWith("/")) {
    request.uri = "/" + prefix + uri;
  } else {
    request.uri = "/" + prefix + "/" + uri;
  }
  return request;
}

リダイレクト

URLのリダイレクト

特定のパスのみリダイレクト

/test/配下のパスは、/test/403.htmlにリダイレクトさせる

function handler(event) {
  var request = event.request;
  var host = request.headers.host.value;
  var uri = request.uri;

  if (uri.startsWith("/test/") && uri != "/test/403.html") {
    return {
      statusCode: 301,
      statusDescription: "Moved Permanently",
      headers: { location: { value: `https://${host}/test/403.html` } },
    };
  }
  return request;
}

必ずリダイレクトさせる

必ず/test/403.htmlにリダイレクトさせる

function handler(event) {
  var request = event.request;
  var host = request.headers.host.value;
  var uri = request.uri;

  if (uri != "/test/403.html") {
    return {
      statusCode: 301,
      statusDescription: "Moved Permanently",
      headers: { location: { value: `https://${host}/test/403.html` } },
    };
  }
  return request;
}

リダイレクトの場合、ホストを変えることは、もちろん可能です。

403を返す

403を返す

function handler(event) {
  var response = {
    statusCode: 403,
    statusDescription: "Forbidden",
  };
  return response;
}

wwwなしのhostをwwwにリダイレクトさせる

hoge.comをリクエストした場合、www.hoge.comにリダイレクトさせます。
CloudFrontには、元々xxx.cloudfront.netというドメインが使用されているため、そのドメインでないことを判定に含めます。

function handler(event) {
  var request = event.request;
  var host = request.headers.host.value;
  var uri = request.uri;

  if (!host.includes('cloudfront.net') && !host.startsWith('www')) {
    return {
      statusCode: 301,
      statusDescription: 'Moved Permanently',
      headers: { location: { value: `https://www.${host}${uri}` } },
    };
  }

  return request;
}

ルートディレクトリをindex.phpに指定

fuga.com/test/やfuga.com/test`の場合、index.htmlがデフォルトで表示されるようにします。

function handler(event) {
  var request = event.request;
  var uri = request.uri;

  if (uri.endsWith("/")) {
    request.uri += "index.html";
  } else if (!uri.includes(".")) {
    request.uri += "/index.html";
  }

  return request;
}

他のCF2の使用パターン

ドキュメントでは、使用例が豊富でした。

  • レスポンスに Cache-Control ヘッダーを追加する
  • Cross-Origin Resource Sharing (CORS) ヘッダーをレスポンスに追加
  • Cross-Origin Resource Sharing (CORS) ヘッダーをリクエストに追加
  • レスポンスにセキュリティヘッダーを追加する
  • リクエストに True-Client-IP ヘッダーを追加する
  • ビューワーを新しい URL にリダイレクトさせる
  • index.html を追加してファイル名を含まない URL をリクエストする
  • リクエストの単純なトークンを検証する

CF2のログ出力方法

ログの出力方法は、下記のドキュメントにも記載がありますが、console.log()を記述し、CloudFrontに設置し、実際に起動させる必要があります。
L@Eと異なり、ログは、us-east-1のみで出力・記録されます。

eventの構造

下記で、event構造が記載されています。
例えば、user-agentヘッダーは、request.headers["user-agent"].valueで取得できます。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3