Azure Static Web Apps でプレビューサイトのみアクセスを制限する

Web アプリの開発中に実際にデプロイして動作確認する際、何らかのアクセス制限をかけたいというニーズはよくあると思います。Basic 認証を使う例をよく見ますが、パブリッククラウド系のホスティングではその仕組み上使えないことが多いです。

Azure Static Web Apps でも Basic 認証は使えず、GitHub や Twitter などの認証プロバイダーを使ったアクセス制御を利用する形になります。ただし、ある特定の環境のみ認証を有効にする方法が標準では用意されていないので、config ファイルと CI/CD ワークフローをカスタマイズして実現する方法を紹介します。

開発環境用の config ファイルを作成する

Azure Static Web Apps(以下 SWA と記載する場合あり) ではアプリケーションの構成は基本的に staticwebapp.config.json で設定します。ここでは、まず開発環境で有効にしたい設定を追加していくことにします。

上記公式ドキュメントの通り、設定ファイルでできることはたくさんありますが、アクセス制御に使うのは以下の設定項目になると思います。

ロールによるルートのセキュリティ保護

あるルート以下を特定のロールに紐づけられた認証が必須となるようにしたい場合に設定します。今回は開発環境の場合にサイト全体にアクセス制限をかけたいので、以下のように制限対象のルートを /* で設定します。ロール名は後でポータルで紐づけるので適当な文字列を入れておきます。

{
"route": "/*",
"allowedRoles": ["developer"]
}

ログインのルート設定

SWA では認証プロバイダーごとに .auth/login/<プロバイダー名> といったログイン用のルートがあらかじめ定義されています。今回は開発時のみの認証を想定しているので GitHub 認証を使うことにします。その際、必須ではないのですが以下のように /login などのルートに特定の認証用ルートをマッピングする定義を入れておくと便利です。

{
"route": "/login",
"redirect": "/.auth/login/github"
}

なお、他の認証プロバイダーを使わせたくない場合は以下のように設定すると無効になります(Twitter 認証を無効にする例)。

{
"route": "/.auth/login/twitter",
"statusCode": "404"
}

組み込みの認証を使う場合は、Azure Portal でアクセスを許可するアカウントを招待しておき、ロールを割り当てておく必要があります。

IP アドレス制限

最近、ネットーワーク設定の項目が新たに追加になり、 IP アドレスの制限を設定することができるようになりました(Standard プランのみ)。開発者が特定の IP 以下に特定できる場合はこの設定を入れるだけで、認証設定は使わなくても良いというケースもあると思います。認証と併用することも可能です。

{
"networking": {
"allowedIpRanges": ["xxx.xxx.xxx.0/24"]
}
}

CI/CD ワークフローで config ファイルを選択する

これらの設定を staticwebapp.config.json に追加した状態で、SWA 標準の CI/CD ワークフローでデプロイすると、本番環境も含めてアクセス制限が効いてしまいます。そこで、GitHub Actions のワークフローで、開発時のみにこの設定ファイルが適用されるようにしていきます。

ワークフローを以下のようにカスタマイズして実現しています。

  • 開発環境専用の設定を組み込んだ config ファイルを作成する(ここまでの作業で実施済み)
  • ビルドとデプロイの Job を分割する( Azure/static-web-apps-deploy ではビルドしない)
  • main ブランチへのプッシュは PR 時以外はされないように branch protection を設定しておく
  • PR 時のビルドでは開発環境の config が選択されるようにする
  • main へのプッシュ時(PR マージ時)は本番用の config が使われるようにする

これらを実現すると以下のような yaml になります(一部抜粋、アプリケーションは Nuxt.js で開発)。

name: Azure Static Web Apps CI/CD

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build: # ビルド用の Job
runs-on: ubuntu-latest
name: Build Job
steps:
- uses: actions/checkout@v2

# Setup Node
# (省略)

# Restore Pacakges
- run: npm ci

# Build & Generate
- run: npm run generate

# Use SWA config for dev/staging
- name: move swa config
run: mv staticwebapp.config.json ./dist
if: github.event_name == 'pull_request' # PR 時は開発環境用の config を利用する

# Use SWA config for production
- name: move swa config
run: mv staticwebapp.config.prod.json ./dist/staticwebapp.config.json
if: github.event_name == 'push'

# Upload Nuxt artifact
# (省略)

deploy: # デプロイ用の Job
runs-on: ubuntu-latest
needs: build
name: Deploy Job
steps:
# Download Nuxt artifact
# (省略)

# Deploy to SWA
- name: Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_xxx }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: 'upload'
app_location: '/dist'
skip_app_build: true # アプリのビルドはスキップする

この設定にした場合、Pull Request を発行すると以下のようなワークフローが実現します。

  1. PR を発行する
  2. 開発環境用の config が使われてアプリがビルドされる
  3. プレビューサイト (pre-production environments) にアプリがデプロイされる
  4. アクセス制限が必要なプレビューサイトが公開される

なお、本番環境でも何らかの設定を入れたい場合は、 staticwebapp.config.prod.json のように別名で config ファイルを設定しておき、CI/CD 時にファイル名を変更するようにしています。この点は、Node.js の env ファイルのようにファイル名から自動で環境に紐づくような仕組みが欲しいところです。Issue をあげておいたので、いつか改善されることを期待したいです。