ブログ

Shifter WebhookとAWS CodeDeploy、Auth0 を利用して、Amazon S3 上にプライベートなウェブサイトを構築する

Shifter では、簡易的な Basic 認証を利用して公開中のウェブサイトへのアクセスを制限することができます。ですが社内メンバー向けの情報サイトや、会員限定のコンテンツを配信したいといった場合には 別のインテグレーションが必要になります。

このブログでは、Shifter で Generate した WordPress サイトに対して、AWS リソースと Auth0 を活用してログイン済みのユーザーのみアクセスできるようにする方法を紹介します。

このアーキテクチャでは、Shifter / Auth0 / AWS の3サービスを利用しています。


Shifter : ウェブサイトの更新・デプロイ Webhook の呼び出し

すべてのコンテンツは WordPress で管理されています。WordPress は Shifter Static 上でホストされており、Generate によって静的なファイルに変換することができます。

Basic 認証または完全な公開サイトであれば、Shifter Static で全て完結することができます。しかし今回のようなログイン機能を提供したい場合には、静的化されたコンテンツを Shifter Static の外でホストする必要があります。

Shifter Static には、Generate が完了したタイミングで任意の API を実行する Webhook が用意されています。この Webhook を利用し、静的化されたコンテンツを自身の持つ AWS アカウントにアップロードするワークフローを実行します。

Shifter Outgoing Webhook at Generate の詳しい利用方法についてはサポートドキュメントをご参照ください。

サポートドキュメントでは、Netlify との連携例も紹介していますのでぜひお試しください。


AWS : デプロイワークフロー・公開サイトのホスティング

静的なコンテンツのホスティングは Shifter Static や Netlify などでも可能です。しかしより柔軟にカスタマイズできるホスティング環境を必要とする場合には AWS が最適な選択肢です。

AWS 上に Amazon CloudFront と Amazon S3 を用意して静的サイトを公開するための環境を用意し、Amazon CloudFront に Lambda@edge を設定することでユーザー認証のためのスクリプトを実行できるようにします。

また、Shifter Static にて静的化されたコンテンツを任意の Amazon S3 へアップロードするため、この例では AWS CodeBuild を利用しています。そして AWS CodeBuild を Shifter Webhook からのリクエストを受けて実行するために、Amazon API Gateway と AWS Lambda も1つづつ用意しました。

Auth0 : ユーザー認証

サイトへのアクセスは、Amazon CloudFront に設定された Lambda@edge を利用してログイン状況などの確認を行います。もしログインしていないと判定された場合には、Lambda@edge は Auth0 が持つログイン URL へリダイレクトします。

ユーザー認証を Auth0 に移譲することで、ユーザー情報などの管理を自前で行う必要がなくなり、またより柔軟な認証フローやユーザー管理なども実現しています。


公開サイトのホスティング環境を作る

公開サイトは Lambda@edge を利用するため Amazon CloudFront + Amazon S3 を自前でセットアップします。

まず Shifter で生成されたコンテンツを配置するための Amazon S3 バケットを作成します。ここでのポイントは「静的ウェブサイトホスティング」オプションを有効化しないことです。このオプションを有効化すると、Amazon S3 から直接 Web に公開するための URL が作成されます。この URL に直接アクセスされてしまうと、Amazon CloudFront 側で認証をかけても Amazon S3 の URL から誰でもアクセスできてしまいます。

Amazon S3 に配置された静的コンテンツは、Amazon CloudFront の OAI(オリジンアクセスアイデンティティ)を利用してアクセスできるようにします。

オリジンアクセスアイデンティティ (OAI) と呼ばれる特別な Amazon CloudFront ユーザーを作成し、ディストリビューションに関連付けます。

<中略>

これらのステップを実行すると、ユーザーは S3 バケットから直接ではなく、Amazon CloudFront 経由でのみファイルにアクセスできます。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html

その後、Amazon CloudFront の viewer-request に Lambda@edge を設定します。

この Lambda@edge は cloudfront-auth という OSS のツールを利用して作成します。このツールを利用することで、Auth0 をはじめとする Idp を用いた認証を Lambda@edge にて実行するためのコードを作成することができます。

また、WordPress では、/blog/post/hello-world のように拡張子がつかない URL でページが作成されることがあります。Amazon S3 でホストしているコンテンツの場合、このリクエストを/blog/post/hello-world/index.html へ向くようにする必要がありますので、 origin-request にも以下のようなコードを実装した Lambda@edge を設定しましょう。

import { CloudFrontRequestHandler } from 'aws-lambda'

export const handler: CloudFrontRequestHandler = async (event, _context) => {
    // Extract the request from the CloudFront event that is sent to Lambda@Edge 
    const request = event.Records[0].cf.request;

    // Extract the URI from the request
    const olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    const newuri = olduri.replace(/\/$/, '\/index.html');
    
    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;
    return request
}

あとは Certificate Manager で証明書を取得し、独自ドメインを Amazon CloudFront に設定するだけで公開サイトの準備が整います。


Shifter のコンテンツを Amazon S3 へデプロイする

Shifter で生成されたコンテンツをS3にデプロイするには、以下のステップを踏む必要があります。

  • Shifter Outgoing Webhook at Generate のリクエストを受付ける
  • リクエストの body から静的コンテンツの tgz ファイルを DL する
  • tgz ファイルを解凍する
  • Amazon S3 バケットに解凍したファイルをアップロードする

このタスクを AWS のサービスに分担すると、このようになります。

  • [Amazon API Gateway / AWS Lambda] Shifter Webhook のリクエストを受付ける
  • [AWS CodeDeploy] リクエストの body から静的コンテンツのtgzファイルをダウンロードする
  • [AWS CodeDeploy] tgz ファイルを解凍する
  • [AWS CodeDeploy] Amazon S3 バケットに解凍したファイルをアップロードする

CodeBuild プロジェクトを作成する

CodeBuild プロジェクトでは、環境変数をビルド開始時に設定することができます。Shifter Outgoing Webhook at Generate から tgz ファイルの DL URL が送られてきますので、この値をビルド時の環境変数に設定します。

ビルドコマンドは、以下のように設定します。

$ DOWNLOAD_URL=$(echo "${INCOMING_HOOK_BODY}" | jq -r .download_url | base64 -d)
$ ARTIFACT_ID=$(echo "${INCOMING_HOOK_BODY}" | jq -r .artifact_id)

$ wget -O "${ARTIFACT_ID}.tgz" "${DOWNLOAD_URL}"
$ tar xvzf "${ARTIFACT_ID}".tgz
$ aws s3 sync "${ARTIFACT_ID}" "s3://TARGET_S3_BUCKET" --delete --exact-timestamps

プロジェクト作成時にソースの指定を求められますが、今回は AWS Lambda から直接実行ですので、「なし」を選びましょう。

また、Amazon S3にファイルをアップロードするには以下のアクションをAllowするロールを設定する必要があります。

  • “s3:ListBucket”
  • “s3:ListAllMyBuckets”
  • “s3:GetObject”
  • “s3:PutObject”
  • “s3:DeleteObject”

AWS CodeBuild でのビルドを開始する API を作成する

最後に AWS CodeBuild のビルドを実行する API を作成します。

export const handler = async (event) => {
    const client = new CodeBuild()
    const {build} = await client.startBuild({
        projectName: 'shifter-artifact',
        environmentVariablesOverride: [{
            name: 'INCOMING_HOOK_BODY',
            value: JSON.stringify(event.body)
        }]
    }).promise()
    if (!build) return
    const {
      id: buildId,
      startTime,
    } = build
    return {
      buildId,
      startTime
    }
}

API Gateway で POST の HTTP API を作成し、上記のコードを実行する Lambda 関数を呼び出すように設定しましょう。

Shifter Outgoing Webhook at Generate は独自に設定した Header をリクエスト時に送ることができます。ですので、API キーを Amazon API Gateway に設定するか、AWS Key Management Service (KMS) などを用いた独自の認証を AWS Lambda へ実装し、必要な値を Shifter Webhook から送信するようにしましょう。


Shifter Outgoing Webhook at Generate を設定する

最後に作成したビルドを開始する APIを Shifter Webhook から呼び出すように設定します。

Shifter ダッシュボードの Sites > Webhooks タブから Amazon API Gateway の URL を入力し、API Key などの header の設定をした場合はその情報も追加します。


Generate!

設定が終われば、あとは Shifter Static でサイトを Generate するだけです。

Generate が完了すると、 Shifter Outgoing Webhook at Generate に設定された API に対して POST のリクエストが送られます。

その後、そのリクエストを受けた Amazon API Gateway が AWS Lambda を起動し、 POST リクエストの body にある tgz ファイルの ダウンロード URL を環境変数に設定した AWS CodeBuild のビルドを実行します。

AWS CodeBuild は環境変数に設定された URL から tgz ファイルを ダウンロード・解凍し、指定した Amazon S3 バケットにアップロードします。

ユーザーは Amazon CloudFront を経由して S3 に配置された静的ファイルにアクセスします。そして、Lambda@edge がユーザーのセッション情報を確認し、ログイン状態でない場合は Auth0 を用いたログイン画面を経由するようにリダイレクトを行います。


Next Step

今回は Shifter Static で管理している WordPress サイトを、Auth0 での認証を必須とした環境として公開する方法を紹介しました。

ここでは紹介していませんが、この他にも以下のようなカスタマイズを行うことも可能です。

ログアウトボタンの追加

今回のサンプルでは、Lambda@edge によるリダイレクトで認証を設定しています。が、認証後のサイトに Auth0 との連携は設定されていません。

そのため、ログアウトなど、ログイン後に操作したいユーザー管理機能がある場合は、Auth0 の JavaScript SDK を利用したウィジェットなどを WordPress サイト側に追加する必要があります。

一部のパスで認証不要な public コンテンツを配信する

認証用の Lambda@edge を設定しない Behavior を Amazon CloudFront に追加することで、特定のパスのみ認証なしでアクセスできるようにすることができます。

LP やトップページなど、一部コンテンツは public にしたい場合は、Amazon CloudFront の behavior を調整してみましょう。

CodeBuild のビルド完了通知を受け取る

AWS CodeBuild はビルドが完了したことを SNS などを経由してユーザーに通知することができます。

例えば AWS Chatbot と連携することで、ビルドの結果を Slack に pushす るということも可能です。

AWS コンソールで数回クリックするだけで、AWS CodeCommitAWS CodeBuildAWS CodeDeploy、および/または AWS CodePipeline の通知を Slack でご覧いただけるようになります

https://aws.amazon.com/jp/about-aws/whats-new/2020/04/receive-notifications-for-aws-codebuild-codecommit-codedeploy-codepipeline-in-slack/

AWS Chatbot を使用すると、リポジトリ、ビルドプロジェクト、デプロイアプリケーション、パイプラインといった開発者用ツールの通知を設定し、重要なイベントを自動的に Slack へ通知することができます。デプロイに失敗した時、ビルドが成功した時、プルリクエストが作成された時などに、開発者はもっとも気付きやすい形で通知を受け取ることができます。

https://aws.amazon.com/jp/blogs/news/receive-aws-developer-tools-notifications-over-slack-using-aws-chatbot/

Shifter Static での Generate 後、AWS CodeBuild でのビルドを実行してからページが更新されるという流れになるため、編集から公開まですこしタイムラグが発生します。

そのため、このような仕組みで通知できるようにすると、より効率的にサイトを運用できるようになります。

いかがでしたでしょうか。今回の連携方法は三菱重工様の社内向け情報サイトを Shifter で実現するためにご相談を受け、当社と一緒に設計・検証を行わせていただきました。
多くのユーザー様のためにこれらの情報の共有を快諾してくださった三菱重工のチームの皆様に深く感謝申し上げます。
プロジェクトの様子は別途ご紹介させていただく予定ですのでぜひお楽しみに!