Amazon Web Services ブログ

Amazon Location と Edge Services を利用したジオブロックコンテンツ

企業は、コンプライアンスや規制要件、制裁措置、プライバシー法、領土所有権、セキュリティ管理などを遵守するために、コンテンツへのアクセスを制限する方法を必要としています。企業がアクセスを制限する方法のひとつに、ジオブロッキング(ユーザーの位置情報に基づいてウェブサイトやその他のコンテンツへのアクセスを制限すること)があります。一般的なジオブロッキングの方法は、IP アドレスからユーザーの所在地を特定する方法です。しかし、この方法は仮想プライベートネットワーク(VPN)などの技術を使用して回避することが非常に簡単です。
このチュートリアルでは、IP ベースの位置情報ではなく、ユーザーの物理的な位置情報に基づいて Web アプリケーションへのアクセスをジオブロッキングする方法を紹介します。この方法では、VPN だけでは回避できないジオブロッキングの制限をサーバー側で追加することができます。

ソリューション概要

免責事項:サンプルコード、ソフトウェアライブラリ、コマンドラインツール、概念実証、テンプレート、またはその他の関連技術(当社の担当者から提供される前述のものを含む)は、AWS Customer Agreement、またはお客様と AWS 間の関連書面契約(どちらかが適用)に基づき、AWS コンテンツとしてお客様に提供されるものです。お客様は、この AWS コンテンツを、お客様の本番アカウント、本番環境、またはその他の重要なデータで使用しないでください。お客様は、サンプルコードなどのAWS コンテンツを、お客様固有の品質管理手法および基準に基づき、本番環境での使用に適したテスト、セキュリティ確保、および最適化を行う責任を負います。AWS コンテンツをデプロイすると、Amazon EC2インスタンスの実行や Amazon S3 ストレージの使用など、AWS の課金対象リソースを作成または使用するための AWS 料金が発生する場合があります。

このチュートリアルでは、Amazon Simple Storage Service (Amazon S3)上の Web ページをデプロイして、位置データ(緯度と経度)を AWS Lambda 関数に送信しています。この Lambda 関数は、Amazon Location Serviceを利用して、ユーザーの位置情報をリバースジオコーディングし、リダイレクト用の URL を返します。ユーザーの位置情報に応じて、エラーページまたは Amazon Elastic Compute Cloud (Amazon EC2) 上で動作するウェブページに転送されます。

チュートリアル

この図には、ユーザーフローの各ステップを強調するための数字が書かれています。

  1. ユーザーは、ブラウザであなたのドメインに移動します。
  2. ドメインは、Amazon CloudFront によって紐付けられたエイリアスレコードを指します。
  3. CloudFront ディストリビューションは S3 バケットを指します。
  4. S3 バケットには HTML と JavaScript のファイルが含まれており、ユーザーの Web ブラウザに返されます。
  5. HTML Geolocation APIを活用して、ユーザーの物理的な座標を取得します。これらは Lambda 関数に送信されます。
  6. Lambda 関数は Amazon Location を呼び出し、提供された座標を使用してユーザーの位置をリバースジオコーディングします。
  7. HTML ページへ URLが返されます。
  8. ユーザーが「承認」されたジオロケーションにいる場合、AWS Web Application Firewall(WAF)の背後にある Amazon Application Balancer(ALB)へリダイレクトされます。
  9. ユーザーがサブドメインからリクエストしている場合、AWS WAF は ALB へのリクエストの通過を許可します。しかし、ALB へのリクエスト元がサブドメインでない場合、AWS WAF はリクエストを拒否してアクセスを拒否します。
  10. ALB は EC2 インスタンスにリクエストを配信し、ユーザーにウェブページが返されます。

前提条件

このチュートリアルを進めるために必要なものは以下のとおりです。

クライアントアプリケーション

ユーザーの物理的な位置を取得するためには、クライアントアプリケーションが必要です。このチュートリアルでは、Amazon S3 上でホストされた HTML ウェブページを使用できます。また、ドメインネームシステム(DNS)には Route 53 を使用します。次に、ACM を活用して、ドメインの新しいパブリックな SSL/TLS(Secure Sockets Layer/Transport Layer Security)証明書を発行します。最後に、CloudFront を使用して、静的な Web ページをユーザーに配布します。

Amazon S3 のセットアップ

Step 1: S3 バケットの作成 | ドキュメント

Step 2: HTML と JavaScript ファイルの作成

好きなエディタを使用し、以下の 2 つのファイルを作成します。

Filename: geolocation.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Geolocation Request Page</title>
  </head>
  <body>
    <h1>Click the button below to provide your geolocation</h1>
    <p id="location"></p>
    <button onclick="getLocation()">Get Geolocation</button>
    <script src="/geolocation.js"></script>
  </body>
</html>

Filename: error.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Error</title>
  </head>
  <body>
    <h1>You are not in an allowed location. Access Denied</h1>
  </body>
</html>

Step 3: ファイルを S3 バケットへアップロード | ドキュメント

Route 53 の設定

Step 1: Apex ドメインのパブリックホストゾーンの作成|ドキュメント

これは、あなたが所有するドメインである必要があります。まだドメインをお持ちでない場合は、Route 53 を使用してドメインを登録することができます。

Step 2: Route 53でサブドメインを作成する|ドキュメント

サブドメインを使用して、ユーザーをクライアントアプリケーションにルーティングします。

ACM の設定

Step 1: ドメイン用 ACM 証明書の作成|ドキュメント

サブドメインがある場合は、* シンタックスを使用して保護することを確認してください(画像参照)。それ以外の場合はすべてデフォルトのままにしておきます。

Step 2: ドメインの所有権の確認

ドメインを検証するには、そのドメインにクリックし、ドメインパネルに移動します。右側の「Create records in Route 53」ボタンを選択し、ドメインのホストゾーンに CNAME レコードを自動作成します。数分後、ステータスが発行に変わります。次のセクションで確認できます。

CloudFront のセットアップ

Step 1: ディストリビューションの作成|ドキュメント

Originドメインを前回作成した S3 バケットに設定します。以下の設定を使用します。

  • S3 バケットアクセス:Legacy Access Identities
  • Legacy Access Identities > Origin access identity (OAI):新しい OAI を作成します。フィールドに自動入力されます。されない場合は、ドロップダウンから手動で選択します。
  • バケットポリシー:「はい、バケットポリシーを自動更新します」

他のすべてをデフォルトのままにして、保存します。プロビジョニングに 15 分ほどかかります。

Step 2: 一般的なディストリビューション設定の編集

作成したディストリビューションをクリックし、「一般」タブで「設定」パネルに移動し、「編集」ボタンを選択します。以下のフィールドに変更を適用します。

  • 代替ドメイン名(CNAME:[あなたのサブドメイン]。
  • カスタム SSL 証明書:[ACM で取得した SSL 証明書]。
  • デフォルトのルートオブジェクト:[ geolocation.html ]。

これらの変更がデプロイされるまでには、5 分ほどかかります。

Step 3: サブドメインを CloudFront ディストリビューションに指定

サブドメインの Route 53 Hosted Zone に戻り、Alias toggle を有効にして新しい「A」レコードを作成します。次のオプションを使用します。

  • トラフィックのルーティング先:CloudFrontディストリビューションへのエイリアス
  • ドロップダウンから CloudFront ディストリビューション ID を選択します。

この変更が反映されるまでには、5 分ほどかかります。この変更が伝わると、ウェブブラウザでサブドメインに移動して、HTML ページを見ることができます。

バックエンド

次に、ジオロケーション座標を受け取り、それをリバースジオコーディングするためのバックエンドをセットアップします。

このチュートリアルでは、座標をリバースジオコーディングするために、Amazon Location で Place Index を作成します。Amazon Location は完全に管理されたサービスで、開発者はデータセキュリティ、ユーザープライバシー、データ品質、コストを犠牲にすることなく、マップ、ポイントオブインタレスト、ジオコーディング、ルーティング、トラッキング、ジオフェンシングなどのロケーション機能をアプリケーションに簡単に追加することができる。

Lambda 関数が座標を受け取り、Amazon Location へ API コールを行います。ユーザーが正しい場所にいる場合、そのリクエストはサブドメインのウェブページから Apex ドメインのウェブページに転送されます。リクエストは AWS WAF を通過する必要があり、サブドメインから発信されないリクエストはブロックされます。リクエストがブロックされなければ、ALB に到達し、ALB は EC2 インスタンスにリクエストを転送する。

Amazon Location の設定

Step 1: プレイスインデックスの作成|ドキュメント

デフォルトの設定を使用します。プレースインデックス名とARNは後で必要になるので、必ず控えておいてください。

Lambdaのセットアップ

Step 1:Lambda 関数の作成|ドキュメント

ランタイムが NodeJS 16に設定されていることを確認します。その他のオプションはすべてデフォルトのままにしておきます。

Step 2:Amazon Location を許可するためのパーミッションの編集

「設定」タブを選択し、左のパネルで「アクセス権限」を選択します。デフォルトでは、Lambda 実行ロールは Place Index にアクセスすることができません。そのため、適切なパーミッションを追加する必要があります。ロール名を選択して、AWS Identity and Access Management (IAM) を開きます。ポリシー名の横にある「+」記号を押して、「編集」を選択します。すると、このポリシーから IAM 権限を追加/削除するためのビジュアルエディターが表示されます。「さらにアクセス許可を追加する」をクリックします。サービスとして「Location」を選択します。アクションで「Read > SearchPlaceIndexForPosition」を選択します。リソースで、「place-index」の横にある「編集」を選択し、「place index」の ARN を付与します。その後、ポリシーを確認し、変更を保存します。

Step 3:Lambda 関数 URL を作成

Lambda 関数 URL は、Lambda 関数の専用の HTTP(S) エンドポイントです。フロントエンドが Lambda 関数に座標を送信できるようにするために、1 つ作成します。必要であれば、Lambda 関数に戻り、「設定」タブを選択します。左側のパネルで「関数 URL」を選択し、「関数 URL を作成」を選択します。以下の設定を使用します。

  • 認証タイプ:NONE(この方法に関する考慮事項はこちらをご覧ください)
  • オリジン間リソース共有(CORS)を設定:チェック
  • 許可オリジン:https://[あなたのサブドメイン]
  • 許可メソッド:PUT

その他はデフォルトのまま、「保存」を選択します。Lambda 関数 URL が取得できたので、以下の JavaScript ファイルを作成し、S3 バケットにアップロードします。

Filename: geolocation.js

let x = document.getElementById("location");

function getLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showPosition, showError);
  } else {
    x.textContent = "Geolocation is not supported by this browser.";
  }
}

function showPosition(position) {
  let lat = position.coords.latitude;
  let long = position.coords.longitude;
  let coordinates = {};

  console.log(lat);
  console.log(long);

  x.textContent = `Latitude: ${lat} Longitude: ${long}`;

  coordinates.latitude = lat;

  coordinates.longitude = long;

  console.log("coordinates: ", coordinates);

  //Send coordinates to AWS Lambda
  fetch(
    "[insert lambda function URL]",
    {
      method: "PUT",
      mode: "cors",
      body: JSON.stringify(coordinates),
    }
  )
    .then((response) => response.json())
    .then(
      (data) => (
        (x.textContent = `Latitude: ${lat} Longitude: ${long}`),
        console.log(data),
        window.location.assign(data)
      )
    );
}

function showError(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED:
      x.textContent = "User denied the request for Geolocation.";
      break;
    case error.POSITION_UNAVAILABLE:
      x.textContent = "Location information is unavailable.";
      break;
    case error.TIMEOUT:
      x.textContent = "The request to get user location timed out.";
      break;
    case error.UNKNOWN_ERROR:
      x.textContent = "An unknown error occurred.";
      break;
  }
}

必ず [insert lambda function URL] を実際の Lambda 関数 URL に置き換えてください。

Step 4: Lambda 関数の編集

Lambda 関数の「コード」タブに移動します。現在はデフォルトのコードを使用しています。以下は、座標のリバースジオコードに使用できる Lambda 関数コードです。

Filename: index.js

//Define Libraries
const AWS = require("aws-sdk");
const location = new AWS.Location();

//Begin Function
exports.handler = async (event) => {
  //Set Variables
  let coordinates = JSON.parse(event.body);
  let latitude = coordinates.latitude;
  let longitude = coordinates.longitude;

  //Parameters for Location Service
  let params = {
    IndexName: "[your place index]" /* required */,
    Position: [longitude, latitude],
    MaxResults: 1,
  };

  //Reverse geocodes a given coordinate and returns a legible address. Logs to CloudWatch Logs
  location.searchPlaceIndexForPosition(params, function (err, data) {
    if (err) console.log(err, err.stack); // an error occurred
    else console.log(data.Results); // successful response
  });

  //Store legible address
  let locationData = await location
    .searchPlaceIndexForPosition(params)
    .promise()
    .then((data, err) => {
      return data;
    });

  //Filter for state. See CloudWatch logs for other JSON values
  let state = locationData["Results"][0]["Place"]["Region"];

  if (state === "New York") {
    let redirectURL = "https://[your apex domain]"; // Change the redirect URL to your apex domain

    const response = {
      statusCode: 302,
      statusDescription: "Found",
      body: JSON.stringify(redirectURL),
    };
    return response;
  }

  if (state !== "New York") {
    let redirectURL = "https://[your subdomain]/error.html"; // Change the redirect URL to your subdomain's error page
    const response = {
      statusCode: 302,
      statusDescription: "Found",
      body: JSON.stringify(redirectURL),
    };
    return response;
  }
};

place-index と両方の redirectURL にそれぞれ値を挿入することを確認してください。

ALB と EC2 のセットアップ

Step 1: セキュリティグループの作成|ドキュメント

ALB セキュリティグループ(SG)を以下の設定で作成します。

  • セキュリティグループ名:任意
  • VPC:適切な VPC を選択
  • インバウンドルール:
Type Source Source Value
HTTP Custom 0.0.0.0/0
HTTPS Custom 0.0.0.0/0

Amazon EC2 SG を以下の設定で作成します。

  • セキュリティグループ名:任意
  • VPC: ALB SG と同じ VPC
  • インバウンドルール:
Type Source Source Value
HTTP Custom ALB SG ID
HTTPS Custom ALB SG ID

Step 2: 新しい Amazon EC2 インスタンスの起動

Amazon EC2 コンソールに移動し、Launch an instanceを選択します。以下のパラメータを使用します。

  • 名前:任意
  • アプリケーションとOSのイメージ(Amazon Machine Image (AMI)):Amazon Linux
  • キーペア名 – 必須:キーペアを指定せずに進む
  • VPC: セキュリティグループと同じ VPC
  • ファイアウォール(セキュリティグループ):既存のセキュリティグループを選択する
  • セキュリティグループ:以前に作成したEC2 SG
  • 高度な詳細: ユーザーデータ

このスクリプトは、インスタンスを更新し、apache ウェブサーバーをインストールし、いくつかの基本的なコンテンツを含む index.html ファイルを作成します。他のオプションはすべてデフォルトのままにして、インスタンスを起動します。

Step 3: 新しいターゲットグループと ALB の作成

必要に応じて、Amazon EC2 コンソールに移動し、左側のパネルで「ロードバランサー」を選択します。ドキュメントに従って、ALB とターゲットグループを作成します。ALB については、以下のパラメータを変更します。

  • ALB 名:[関連する名前を選択してください。]
  • VPC: [EC2インスタンスと同じVPCを選択]。
  • マッピング [すべて選択する]
  • セキュリティグループ
    • デフォルトのSGを削除する
    • 前項で作成したALB SGを追加します。
    • リスナー、ルーティング
Protocol Port Default Action
HTTP 80 Forward to: [Select your Target Group]
HTTPS 443 Forward to: [Select your Target Group]
    • セキュアリスナーの設定
      • セキュリティ・ポリシー:デフォルトのまま
      • デフォルトの SSL/TLS 証明書:ACM よりドメイン証明書を選択

AWS WAF のセットアップ

Step 1:ウェブACLの作成

AWS WAF コンソールを開き、「Create web ACL 」をクリックします。以下の設定を使用します。

  • 名前(Name):任意
  • リソースタイプ(Resource type):Regional resources
  • AWS リソースの関連付け(Associated AWS resources)
    • Add AWS resources」をクリックします
    • リソースタイプ:Application Load Balancer
    • Add」をクリックします
    • Next」をクリックします
  • ルール(Rule):「Add my own rules and groups」をクリックします
  • 名前(Name):任意
  • インスペクト(Inspect):Single header
  • ヘッダーフィールド名(Header field name):referrer
  • マッチタイプ(Match tyoe):Start with string
  • 一致する文字列: https://[あなたのサブドメイン]
  • 文字列の変換(Text transformation):Lowercase
  • アクション(Action): Allow
  • Add rule」をクリック
  • どのルールにもマッチしないリクエストに対するデフォルト Web ACL アクション(Default web ACL action for requests that don’t match any rules):Block
  • その他はデフォルトのまま、「Create web ACL」をクリックします。

仕上げ

サブドメインにナビゲートし直します。これで初めてボタンを選択すると、ブラウザがジオロケーションにアクセスする許可を求めてきます。「許可する」を選択します。数秒後、あなたの座標が返され、あなたの位置に基づいてルートが変更されます。これには数秒かかる場合があります。Lambda 関数のコードを変更しなかったと仮定すると、もしあなたがニューヨークにいないのであれば、エラーページが表示されるはずです。

それ以外の場合は、問題なく apex のドメインにリダイレクトされます。

apexドメインに直接移動しようとすると、以下のように AWS WAF によってリクエストがブロックされたことを示すエラーが表示されるはずです。

エラーページが表示されなかった場合は、apex ドメインの Web ページがブラウザにキャッシュされている可能性があるため、シークレットモードで再試行してください。
Lambda 関数を編集して、state 以外の location パラメータを使って許可/拒否することができます。Lambda コンソールに戻り、コードの一部を見てみましょう。searchPlaceIndexForPosition 関数は、以下の JSON レスポンスボディを返します。

{
    Distance: double,
    Place: {
      AddressNumber: [string],
      Country: [string],
      Geometry: [Object],
      Interpolated: [true/false],
      Label: [address],
      Municipality: [string],
      PostalCode: [string],
      Region: [string],
      SubRegion: [string]
    }
  }

座標が米国のものである場合、地域は通常、米国の州と一致します。以下の手順に従って、JSON キーがどのようにあなたの場所にマッピングされるかを確認してください。

  • Lambda 関数に移動します
  • モニタリング」タブを選択し、「View CloudWatch Logs」を選択します。
  • ログストリーム」の最初の項目を選択します。
  • ログイベントの 1 つには、以下の画像のように Amazon Location からの位置情報が含まれています。

クリーンアップ

最後に不要になったリソースの削除を行います。各リソースに対して以下のアクションを行ってください。

まとめ

本投稿では、Amazon Location を利用して、クライアントアプリケーションから受け取った座標をリバースジオコーディングし、ユーザーの物理的な位置に基づいたコンテンツのジオブロッキングする方法を学びました。ここでは、リージョンキーに基づいてこれを行いましたが、もっと細かく特定の住所まで設定することも可能です。この機能を CloudFront のジオロケーションヘッダと組み合わせることで、ユーザーの物理的な場所と IP ベースの場所に基づいてコンテンツへのアクセスを制御することもできます。Amazon Locationの活用方法について詳しくは、こちらをご覧ください。他に質問がある場合は、私たちのフォーラム、AWS re:Post に投稿してください。

本ブログは、Geo-block Content Using Amazon Location and Edge Services を翻訳したものです。
翻訳はソリューションアーキテクトの稲田大陸が担当しました。