CloudFrontのIP制限やBotアクセスの拒否にはCloudFront functionsかLambda@edgeを使います。
CloudFront functionsとLambda@edge
CloudFrontはユーザから最も近い位置で配信を行うため、オリジンやその間のロードバランサーなどではリダイレクトやその他の設定を適用することができません。
従って、CloudFrontへのアクセスに対する制御はCloudFront側で実装する必要があります。
CloudFrontでアクセス制御を行う方法としては2つあります。
- CloudFront functions
- Lambda@egde
両者の違いは、軽い処理はコストも低いCloudFront funciton (JSのみ)を使用し、複雑で重い処理はLambda@edgeを使うというように使い分けると良いです。
- functionsはjavascriptのみサポート(egdeはnode.js & python)
- funcitonsはオリジンリクエスト/レスポンスでは利用できない
- functionsは1 ミリ秒未満の実行時間
- functionsが使用できるメモリは最大2MB(edgeの1/1500)
リダイレクトやアクセス制限といった機能は軽い処理で実装できるのでコストが1/6のCloudFront functionsで十分です。
詳しいCloudFront FuncitonsとLambda@Edgeの比較はデベロッパーガイドにあります。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/edge-functions.html
デベロッパーガイドには以下の簡単な作業に向いているとあります。
- ヘッダー操作(追加、書き換え、判定)
- リダイレクト
- 簡単な検証
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html
CloudFront functionsでIP制限を行うjavascriptコードを作成する
まずはCloudFront functionsの関数を作成してみます。
awsのgithubコード例を元に作成していきましょう。
https://github.com/aws-samples/amazon-cloudfront-functions/blob/main/add-true-client-ip-header/index.js
コード例にもありますhttps://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-add-true-client-ip-header.html
オリジンリクエストヘッダーにクライアントのIPを押し込むコード例を見ると、クライアントのIPは
var clientIP = event.viewer.ip; で取れそうです。このIPを見て条件分岐して該当のIPのならリクエストをオリジンに渡す、該当のIPでない場合は403のレスポンスを返すというようにすればよさそうです。
function handler(event) {
var request = event.request;
var clientIP = event.viewer.ip;
//Add the true-client-ip header to the incoming request
request.headers['true-client-ip'] = {value: clientIP};
return request;
}
ステータスコードを返すコードはチュートリアルの簡単な関数の例が参考になりそうです。
function handler(event) {
// NOTE: This example function is for a viewer request event trigger.
// Choose viewer request for event trigger when you associate this function with a distribution.
var response = {
statusCode: 302,
statusDescription: 'Found',
headers: {
'cloudfront-functions': { value: 'generated-by-CloudFront-Functions' },
'location': { value: 'https://aws.amazon.com/cloudfront/' }
}
};
return response;
}
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-tutorial.html
allowIPに許可IPを記述し、includesメソッドでclientIPが配列に含まれていればtrueなければfalseで判定、if文で条件分岐してtrueならrequestをそのままオリジンに送信、falseなら変数resposeの内容(403のエラー)をクライアントに返します。
function handler(event) {
var request = event.request;
var clientIP = event.viewer.ip;
// 許可するIPを↓に記述
var allowIP = [
'192.0.2.11',
'192.0.2.30',
];
// allowIPに含まれるipならtrueでオリジンにそのままリクエストを渡す、falseなら403でresponseを返す
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
if (allowIP.includes(clientIP)) {
return request;
} else {
var response = {
statusCode: 403,
statusDescription: 'Forbidden, Your IP is restricted',
};
return response;
}
}
テスト実行してみてエラーがないか確認してください。コンピューティング使用率をみて50以下に抑えられているかを確認しましょう。それ以上の場合はタイムアウトして503になる可能性があるのでコードを見直します。
fucntion handlerのeventの構造は下記のURL中に記載があるのでコード作成の参考にしてください
user-agentをみてbotアクセスの拒否を行うjavascriptコードを作成する
botブロックもAWSWAFを使ってブロックできますが、同じくCloudFront Functionsでも可能です。
useragentはイベント中のrequest.headers中にあるのでこの値を取ってきて判定します。ロジックはIP制限と全く同じです。
function handler(event) {
var request = event.request;
var userAgent = event.request.headers['user-agent'].value;
// ブロックするBotを↓に記述
var denyBot = [
'MJ12bot',
'DotBot',
];
var testUserAgent = denyBot.includes(userAgent);
console.log(userAgent);
console.log(testUserAgent);
if (denyBot.includes(userAgent)) {
var response = {
statusCode: 403,
statusDescription: 'Forbidden',
};
return response;
} else {
return request;
}
}
CloudFront functionsの作成方法
CloudFront Functionsには2つのステージ(開発と本番[Development/Live])があり保存した関数(Development)を発行するとその内容が本番のステージに上がります。CloudFront Functionsの構築タブ中で編集・テストしている限りは本番に反映されている関数には影響を与えません。
1. CloudFrontの画面で関数の作成をクリックする
2.関数の名前などを入力
3. 関数を入力して必要に応じでテストする
4.関数を発行する
CloudFront functionsを対象のディストリビューションに紐づける。ビヘイビアの設定でビューワリクエスト(ユーザからのリクエスト)にCloudFront functionsを設定します。
あとは、ブラウザを通してCloudFrontのドメインにアクセスしてIP制限がかかっているかを確認しましょう。
設定できてるのにCloudFrontのIP制限が動作しない時
おそらくCloudFrontの「IPv6が有効化」が原因です。
以下の症状がある場合はIPv6をオフにしてみてください
- curlではIP制限がかかるがブラウザではかからない
- 同じネットワークでも機器によってかからない
CloudFrontにIPv6でアクセスしにいったため、IPv4の制限がかからなかったと思われます。