Webアプリケーションの配信と保護​

[更新:2025年12月11日]

delivery_and_protection

グローバルな配信基盤を通じてWebアプリケーションを高速に配信しつつ、WAFなどのセキュリティ機能で攻撃から保護する仕組みを構築します。これにより、遅延の少ない快適なユーザー体験と高い可用性を確保しながら、不正アクセス対策やトラフィック制御を一元化し、運用効率と安全性を同時に向上できます。

各機能のサービス対応

機能

対応サービス

コードリポジトリ

Azure Front Door

システム構成要素

  • さくらのクラウド

    • エンハンスドロードバランサ

    • Nginxサーバ

  • Addon

    • Azure Front Door

構築手順

さくらのクラウドの準備

サンプルファイル内に、エンハンスドロードバランサとNginxサーバを作成するTerraformとAnsibleのサンプルコードを用意しています。

Let’s Encryptの設定

エンハンスドロードバランサーにLet’s Encryptを設定します。

SSL証明書のCommon Nameには、エンハンスドロードバランサーのFQDNを設定します。

オリジンガードの設定

エンハンスドロードバランサーのオリジンガードを有効にします。

オリジンガードトークンには、このサンプルではエンハンスドロードバランサーのリソースIDを設定します。

Addonの「ネットワークとCDN」機能 の準備

Azure Front Doorの作成

Addonの「CDNサーバ機能作成」APIを利用してAzure Front Doorを作成します。

endpoint.originGroup.origin.hostNameendpoint.originGroup.origin.hostHeaderには、先に作成したエンハンスドロードバランサーのFQDNを設定します。

# APIキーを使用した認証トークン
TOKEN='<your-api-token>'

# POST
curl -v \
  --location 'https://secure.sakura.ad.jp/cloud-test/zone/is1y/api/addon/1.0/cdn' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header "Authorization: Basic $TOKEN" \
  --data '{
    "location": "japaneast",
    "profile": {
      "level": 1
    },
    "endpoint": {
      "route":{
        "patterns": ["/*"],
        "originGroup": {
          "origin": {
            "hostName": "example-lb.proxylb2.sakura.ne.jp",
            "hostHeader": "example-lb.proxylb2.sakura.ne.jp"
          }
        }
      }
    }
  }'

Azure Front Doorの診断ログ設定にAzure Data Lake Storage Gen2を利用するため、Addonの「データレイク機能作成」APIを利用してAzure Data Lake Storage Gen2を作成します。

#TOKEN='<your-api-token>'

#POST
curl -v \
  --location 'https://secure.sakura.ad.jp/cloud-test/zone/is1y/api/addon/1.0/analytics/datalake' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header "Authorization: Basic $TOKEN" \
  --data '{
    "location": "japaneast",
    "performance": 1,
    "redundancy": 1
  }'

Azure Front Doorの情報を取得

Addonの「CDNサーバ機能の個別取得」APIを利用して、作成したAzure Front Doorの情報を取得します。

properties.frontDoorIdの値はエンハンスドロードバランサ側の設定を行う際に利用するため、メモしておきます。

# APIキーを使用した認証トークン
TOKEN='<your-api-token>'

# GET
curl -v \
  --location 'https://secure.sakura.ad.jp/cloud-test/zone/is1y/api/addon/1.0/cdn' \
  --header 'Accept: application/json' \
  --header "Authorization: Basic $TOKEN"
{
  "data": {
    "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myFrontDoor",
    "type": "Microsoft.Cdn/profiles",
    "name": "myFrontDoor",
    "location": "Global",
    "kind": "frontdoor",
    "tags": {},
    "sku": {
      "name": "Standard_AzureFrontDoor"
    },
    "properties": {
      "originResponseTimeoutSeconds": 30,
      "logScrubbing": null,
      "frontDoorId": "324e651f-1234-1234-1234-123456789abc",
      "extendedProperties": {},
      "resourceState": "Active",
      "provisioningState": "Succeeded"
    },
    "originGroups": [
      {
        "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myFrontDoor/origingroups/myOriginGroup",
        "type": "Microsoft.Cdn/profiles/origingroups",
        "name": "myOriginGroup",
        "properties": {
          "loadBalancingSettings": {
            "sampleSize": 4,
            "successfulSamplesRequired": 3,
            "additionalLatencyInMilliseconds": 50
          },
          "healthProbeSettings": null,
          "trafficRestorationTimeToHealedOrNewEndpointsInMinutes": null,
          "sessionAffinityState": "Disabled",
          "provisioningState": "Succeeded",
          "deploymentStatus": "NotStarted"
        },
        "origins": [
          {
            "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myFrontDoor/origingroups/myOriginGroup/origins/myOrigin",
            "type": "Microsoft.Cdn/profiles/origingroups/origins",
            "name": "myOrigin",
            "properties": {
              "originGroupName": "myOriginGroup",
              "hostName": "example-lb.proxylb2.sakura.ne.jp",
              "httpPort": 80,
              "httpsPort": 443,
              "originHostHeader": "example-lb.proxylb2.sakura.ne.jp",
              "priority": 1,
              "weight": 1000,
              "enabledState": "Enabled",
              "sharedPrivateLinkResource": null,
              "enforceCertificateNameCheck": true,
              "provisioningState": "Succeeded",
              "deploymentStatus": "NotStarted"
            }
          }
        ]
      }
    ],
    "endpoints": [
      {
        "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myFrontDoor/afdendpoints/myEndpoint",
        "type": "Microsoft.Cdn/profiles/afdendpoints",
        "name": "myEndpoint",
        "location": "Global",
        "tags": {},
        "properties": {
          "hostName": "myEndpoint-1234567890ab.a03.azurefd.net",
          "autoGeneratedDomainNameLabelScope": null,
          "enabledState": "Enabled",
          "provisioningState": "Succeeded",
          "deploymentStatus": "NotStarted"
        },
        "routes": [
          {
            "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myFrontDoor/afdendpoints/myEndpoint/routes/myRoute",
            "type": "Microsoft.Cdn/profiles/afdendpoints/routes",
            "name": "myRoute",
            "properties": {
              "cacheConfiguration": null,
              "customDomains": [],
              "originGroup": {
                "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myFrontDoor/originGroups/myOriginGroup"
              },
              "originPath": null,
              "ruleSets": [],
              "supportedProtocols": [
                "Http",
                "Https"
              ],
              "patternsToMatch": [
                "/*"
              ],
              "forwardingProtocol": "MatchRequest",
              "linkToDefaultDomain": "Enabled",
              "httpsRedirect": "Enabled",
              "enabledState": "Enabled",
              "provisioningState": "Succeeded",
              "deploymentStatus": "NotStarted"
            }
          }
        ]
      }
    ]
  },
  "url": "https://portal.azure.com/#@contoso.onmicrosoft.com/resource/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup"
}

エンハンスドロードバランサ側の追加設定

エンハンスドロードバランサ側でX-Azure-FDIDで該当する値を持つアクセスだけを許可するようにルールを設定します。X-Azure-FDIDの値は、先ほどメモしたproperties.frontDoorIdを設定します。

リクエストヘッダーを持つ場合はアクセスできるルールを追加します。

アクセスできるルールを追加

リクエストヘッダーを持たない場合は拒否するルールを追加します。

拒否するルールを追加

動作確認

次の項目を確認します。

リクエストヘッダーを持つAzure Front Door経由のアクセスではNginxサーバが表示されること

Nginxサーバが表示される

リクエストヘッダーを持たない場合は403と表示されステータス403が返ってくること

ステータス403が返ってくる

アクセスログの設定

Azure Front Doorのアクセスログを取得するよう設定します。

  1. 監視 > 診断設定を選択し、診断設定の追加を選択

addon-diagnosis-settings
  1. 以下の設定を行い、「保存」を選択

  • 診断設定名を入力

  • カテゴリグループ「allLogs」を選択

  • Addonのデータレイク機能APIで作成したストレージアカウントを選択

addon-diagnosis-setting-storage
  1. 設定したストレージアカウントに診断設定ログが保存されることを確認

  2. insights-logs-{診断設定名}のコンテナーにアクセスし、JSONファイル形式でアクセスログが保存されていることを確認する

addon-confirm-logs

アクセスログの例:

{
  "time": "2025-10-15T06:34:26.0000000Z",
  "resourceId": "/SUBSCRIPTIONS/{SUBSCRIPTION_ID}/RESOURCEGROUPS/RG-ALCCHPCLNEKY4/PROVIDERS/MICROSOFT.CDN/PROFILES/AFD-5BLH76CRKCEIM",
  "category": "FrontDoorAccessLog",
  "operationName": "Microsoft.Cdn/Profiles/AccessLog/Write",
  "properties": {
    "trackingReference": "20251015T063426Z-1758f994594pqswshC1OSAaw4n0000000b3g00000000mb3q",
    "httpMethod": "GET",
    "httpVersion": "2.0.0.0",
    "requestUri": "https://fde-5blh76crkceim-ekhwcwfbavg9e3cd.a03.azurefd.net:443/",
    "sni": "fde-5blh76crkceim-ekhwcwfbavg9e3cd.a03.azurefd.net",
    "requestBytes": "20",
    "responseBytes": "179",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0",
    "clientIp": "{CLIENT_IP_ADDRES}",
    "clientPort": "54944",
    "socketIp": "{SOCKET_IP_ADDRES}",
    "timeToFirstByte": "0.010",
    "timeTaken": "0.010",
    "requestProtocol": "HTTPS",
    "securityProtocol": "TLS 1.3",
    "rulesEngineMatchNames": [],
    "httpStatusCode": "304",
    "edgeActionsStatusCode": "0",
    "roxyConnectStatusCode": "",
    "httpStatusDetails": "304",
    "pop": "OSA",
    "cacheStatus": "CONFIG_NOCACHE",
    "errorInfo": "NoError",
    "ErrorInfo": "NoError",
    "result": "N/A",
    "endpoint": "fde-5blh76crkceim-ekhwcwfbavg9e3cd.a03.azurefd.net",
    "routingRuleName": "route-5blh76crkceim",
    "clientJA4FingerPrint": "t13d1517h2_8daaf6152771_b6f405a00624",
    "hostName": "fde-5blh76crkceim-ekhwcwfbavg9e3cd.a03.azurefd.net",
    "originUrl": "https://site-rvyb634.proxylb1.sakura.ne.jp:443/",
    "originIp": "{ORIGIN_IP_ADDRES}",
    "originName": "site-rvyb634.proxylb1.sakura.ne.jp:443",
    "originCryptProtocol": "N/A",
    "originCryptCipher": "N/A",
    "referer": "",
    "clientCountry": "Japan",
    "domain": "fde-5blh76crkceim-ekhwcwfbavg9e3cd.a03.azurefd.net:443",
    "securityCipher": "TLS_AES_256_GCM_SHA384",
    "securityCurves": "0x9a9a:0x11ec:X25519:prime256v1:secp384r1"
  }
}

Azure Front DoorのWAF設定

参考:Azure Front Door上のAzure Webアプリケーションファイアウォール

本ユースケースではWAFの利用例として、IP制限を行う手順について説明します。

  1. Azure Front Doorのセキュリティ > セキュリティポリシーの画面に遷移し、「追加」を選択

addon-create-waf
  1. 以下の設定を行い、セキュリティポリシーの「新規作成」を選択

  • 名前:任意のセキュリティポリシー名を入力

  • ドメイン:Addon APIで作成したAzure Front Doorに関連付けられているドメイン

addon-size-waf-option
  1. 任意の名前を入力し、「作成」を選択

addon-create-waf-policy
  1. セキュリティポリシーの「保存」を選択

addon-save-security-policy
  1. 上記で作成したWAFポリシーを選択

addon-choice-waf
  1. カスタムルール > カスタムルールの追加を選択

addon-waf-custom-rule
  1. カスタムルールタブで、次の項目で設定する

  • カスタムルール名:任意のカスタムルール名

  • 状態:有効

  • ルールの種類:一致

  • 優先度:任意の優先度(例では100を指定)

addon-waf-custom-rule-option
  1. カスタムルールの条件で次の項目を設定し、「追加」を選択する。

  • 一致の種類:IPアドレス

  • 一致変数:RemoteAddr

  • 演算:次の値を含まない

  • IP アドレス:IPv4またはIPv6の単一のIPアドレス、または範囲を入力

  • 結果:トラフィックを拒否する

addon-waf-custom-rule-option2
  1. 特定IPアドレス以外からのアクセスをブロックするために、WAFの動作モードの設定を「防止モードに切り替える」を選択する

addon-waf-block-mode

動作確認

Azure Front DoorのWAF設定について、次の動作を確認します。

  • 許可されたIPからアクセスした場合、Nginxのページを表示できること

addon-waf-ip-allow
  • 許可されていないIPからアクセスした場合、ブロックされること

addon-waf-ip-blocked

Azure Front Doorのメトリック

Azure Front Doorでは、トラフィックやパフォーマンスに関する様々なメトリックが使用できます。

addon-adf-metric

Azure Front Doorで確認できるメトリックの詳細については、以下のリンクの記事を参照ください。

Azure Front Door 監視データのリファレンス