ヘヴィメタル・エンジニアリング

AWS特化型エンジニアのほのぼのヘヴィメタルブログ

ヘヴィメタル・エンジニアリング

クラウド特化型ヘヴィメタルエンジニアのほのぼのブログ

Azure Pipeline と Terraformを使った 環境構築 - Azure OpenAI を利用している方は必読

はじめに

こんにちは。

普段は新規開発メインの会社で SRE やデータエンジニアをしているものです。

クラウドプラットフォームとしては AWSGCP をメインで利用していますが、最近 Azure OpenAI の API が利用可能になったことで Azure も使い始めました。

そこで様々なエンジニアが Azure 上でオペレーションを行うことが想定されるため、Resource Groupや IAM、OpenAI などを包括的に管理したいです。

そのための設計と、上記リソースなどを Terraformで IaC する方法を今回ご紹介します。

ちなみに、ワークロードには Azure Pipeline を採用しました。理由は後述します。

それでは参りましょう。

前提

Azure DevOps

Azure DevOps とはアプリケーション開発を円滑に行うためのサービスの集合体です。具体的には Git リポジトリの Azure Repos や、CI/CD サービスの Azure Pipeline などです。

初学者で間違えがちですが、Azure DevOps は Azure とは別プラットフォームです。

ですので、ユーザーの権限管理の考え方や、管理単位までほとんどが異なりますので注意しましょう。
ただ Azure Active Directory (以後 AD) と接続して、ユーザー管理を簡素化することはできます。これについては別記事にしようと思います。

今回 ワークロードに Azure Pipeline を採用した理由としては、最もセキュアな環境が構築できるからです。
私の環境では、Github Actions、Cloud Build、AWS CodePipeline など様々な環境が存在しますが、環境構築を行う際の認証資格はほぼなんでも作成、削除できる権限を持つため、サービスプリンシパルを作成して、そのシークレットを他の環境へ保存することはセキュリティリスクが高いです。

Azure Pipeline ではサービス接続という機能でサービスプリンシパルの自動作成ができることから、シークレット自体は発行されますが、その値をクライアント側で確認することはできず、特に持ち出す必要もありません。

ちなみにここで、Managed ID を使えばいいのでは?と思った方がいるかと思いますが、Azure Pipeline ではユーザー管理のVMでエージェントを動かす場合を除いて、Managed ID は使えません。

ですので、上記の方法を取ります。

具体的には後述します。

Terraform

ここで特筆することは無いかもしれません。

Azure リソースを作成するためのプロバイダーが用意されており、ほぼすべてのリソース作成が可能だと思います。 https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs

IAM

Azure では Subscription 以下のリソースにも IAM で権限を付与することができます。
この際、上層のIAMを引き継ぐ形で、それに追加して下層で権限を付与できます。

ここでほかのクラウドプラットフォームと異なるのは

「大きく権限を付与して、拒否で権限を絞っていくのではなく、各レイヤーで細かく権限を付与していく」

ということです。 つまり、Azure での IAM の考え方は引き算です。

実際に現時点でも IAM で拒否ルールは作成できますが、Blueprints 経由で作成されたもののみが対象です。つまり今回のように Terraform で作成した場合対象外です。

以下の様に、Resource Group でもIAMの設定ができることがわかります。
上層で定義されているものは (継承済み) と表示されます。この Resource Group は属する Subscription の権限を継承していることがわかります。

そして、もう一点重要なのが、上層でつけられた権限を下層で拒否することはできないことです。
例えば、Subscription で所有者の権限を付与して、Resource Group で閲覧者の権限を付与した場合、Subscription に属するリソースの範囲全てで所有者の権限が適用されます。
つまりロール付与は上書き拒否できないのです。

例えば以下のようなロールの jsonを仮定した場合、

{
    "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/providers/Microsoft.Authorization/roleDefinitions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
    "properties": {
        "roleName": "test-role",
        "description": "test権限",
        "assignableScopes": [
            "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourceGroups/test-resource-group"
        ],
        "permissions": [
            {
                "actions": [
                    "*"
                ],
                "notActions": [
                    "Microsoft.Resources/subscriptions/resourceGroups/delete",
                    "Microsoft.Storage/storageAccounts/delete"
                ],
                "dataActions": [],
                "notDataActions": []
            }
        ]
    }
}

Subscription で所有者の権限を付与している場合、上記 json の scope では Resource Group を指定していて、notActions で Resource Group、Storage Account の削除を指定してるのでそのアクションができないと思いがちですが、それは間違いです。

先程述べた通り、上層の権限を上書き拒否するのはできないのと、notActions の本来の意味は、actions で許可したアクションを拒否することなので、他のロールと関係を持ちません。

以上のことに注意しましょう。

手順

それでは実際に Terraformの環境を構築して、Azure リソースを作成していきましょう。

今回作成するリソースはこちらです

・Resource Group
・Storage Account (Terraform Backend 用)
・Blob コンテナ
・IAM カスタムロール
・IAM ロールアサインメント
・Azure Open AI

Terraform ファイル

手元では Terraform の環境が整っている前提で話を進めます。 今回は Backend に Storage Blob を採用するので、はじめは local でリソースを作成しましょう。

以下のようなディレクトリ構成でファイルを用意します。

/terraform/test
/terraform/test/resource_group.tf
/terraform/test/storage.tf
/terraform/test/iam.tf
/terraform/test/cognitive_service.tf
/terraform/test/provider.tf
/terraform/test/terraform.tf

resource_group.tf

resource "azurerm_resource_group" "tf" {
  name     = "tf-resource-group"
  location = "Japan East"
}

resource "azurerm_resource_group" "this" {
  name     = "resource-group"
  location = "Japan East"
}

storage.tf

resource "azurerm_storage_account" "this" {
  name                     = "tfenv"
  resource_group_name      = azurerm_resource_group.tf.name
  location                 = azurerm_resource_group.tf.location
  account_tier             = "Standard"
  account_replication_type = "GRS"
  account_kind             = "StorageV2"

  ## tfstateを保存するのでバージョニングは必須
  blob_properties {
    versioning_enabled = "true"
  }
}

resource "azurerm_storage_container" "tfstate" {
  name                  = "tfstate"
  storage_account_name  = azurerm_storage_account.this.name
  container_access_type = "private"
}

iam.tf
前提で書いたことに注意しましょう。
ここでは Subscription レベルに設定します。

resource "azurerm_role_definition" "this" {
  name        = "test-role"
  scope       = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
  description = "test権限"

  permissions {
    actions = [
      "*"
    ]
    not_actions = [
      "Microsoft.Authorization/*/Delete",
      "Microsoft.Authorization/*/Write",
      "Microsoft.Authorization/elevateAccess/Action",
      "Microsoft.Blueprint/blueprintAssignments/write",
      "Microsoft.Blueprint/blueprintAssignments/delete",
      "Microsoft.Compute/galleries/share/action",
      # リソースグループ削除
      "Microsoft.Resources/subscriptions/resourceGroups/delete",
      # リソースグループ作成
      "Microsoft.Resources/subscriptions/resourceGroups/write",
    ]
  }
}

resource "azurerm_role_assignment" "this" {
  principal_id       = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx
  role_definition_id = azurerm_role_definition.this.role_definition_resource_id
  scope              = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
}

cognitive_service.tf

resource "azurerm_cognitive_account" "openai" {
  custom_subdomain_name = "test"
  kind                  = "OpenAI"
  location              = "eastus"
  name                  = "test"
  resource_group_name   = azurerm_resource_group.this.name
  sku_name              = "S0"
  network_acls {
    default_action = "Allow"
  }
  depends_on = [
    azurerm_resource_group.this,
  ]
}

resource "azurerm_cognitive_deployment" "gpt_35_turbo" {
  cognitive_account_id = azurerm_cognitive_account.openai.id
  name                 = "gpt-35-turbo"
  model {
    format  = "OpenAI"
    name    = "gpt-35-turbo"
    version = "0301"
  }
  scale {
    type = "Standard"
  }
  depends_on = [
    azurerm_cognitive_account.openai,
  ]
}

provider.tf

provider "azurerm" {
  features {}
}

terraform.tf

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.62.1"
    }
  }

  backend "local" {
    path = "test.tfstate"
  }
}

Terraform 実行

実際にリソースを作成していきましょう。

はじめローカルで作成するのは、tfstate 用の Resource Group と Storage Account、Blob コンテナのみで大丈夫です。

手元の環境では az cli が使える前提です。

ローカルとazureの認証
$ az login

向いているサブスクリプション、資格情報を取得
$ az account show

対象のサブスクリプションを向いていない場合は切り替える
$ az account set --subscription "サブスクリプションIDまたはサブスクリプション名"

terraform/test ディレクトリに移動し、以下を実行

$ terraform init
$ terraform plan
$ terraform apply -auto-approve

これにより、ローカルからオペレーションを実行し、Azure リソースを作成するのに成功しました。
実際に作成されているかを確認してください。

Azure Pipeline 環境構築

Azure Pipeline 環境を構築するにあたって、Azure DevOps Organization と Azure DevOps Project を作成しましょう。
Azure DevOps 自体が Azure とは別プラットフォームになるため、ユーザーの権限管理も独特です。
そちらについては別記事にします。

作成した Project の画面からPipelineを作成します。

コードは GitHub を指定して、適宜 GitHub 認証をしてください。 途中、pipeline の yaml テンプレートを求められますが、一旦無視をして保存してください。

作成できたら細かい設定をしていきます。

作成された pipeline を選択し、Edit を選択

右上から、Trigger を選択

まずは pipeline の yaml からです。

今回対象のリポジトリの任意の場所に以下のような yaml ファイルを配置し、YAML file path にその場所を指定します。 詳細については Azure DevOps 公式ドキュメントを御覧ください。

pipeline 内で使用する環境変数などは variables で設定しましょう。variables group を設定した場合はデフォルトで 実行環境 にexport されています。

jobs:
- job: terraform
  pool:
    vmImage: ubuntu-latest

  workspace:
    clean: all

  variables:
  - group: test-variables

  steps:
  - task: ms-devlabs.custom-terraform-tasks.custom-terraform-installer-task.TerraformInstaller@1
    displayName: install terraform
    inputs:
      terraformVersion: latest

  - task: TerraformCLI@0
    displayName: terraform init
    inputs:
      command: init
      # サービス接続名
      backendServiceArm: test connection
      workingDirectory: /terraform/test
      backendType: azurerm

  - task: TerraformCLI@0
    displayName: terraform format
    inputs:
      command: fmt
      commandOptions: -recursive
      workingDirectory: /terraform/test

  - task: TerraformCLI@0
    displayName: terraform validate
    inputs:
      command: validate
      # サービス接続名
      environmentServiceName: test connection
      commandOptions: -no-color
      workingDirectory: /terraform/test

  - task: TerraformCLI@0
    displayName: terraform plan
    continueOnError: true
    inputs:
      command: plan
      # サービス接続名
      environmentServiceName: test connection
      commandOptions: -no-color -out=tfplan"
      workingDirectory: /terraform/test
      runAzLogin: true

  - task: TerraformCLI@0
    displayName: terraform apply
    inputs:
      command: apply
      # サービス接続名
      environmentServiceName: test connection
      commandOptions: -no-color -auto-approve"
      workingDirectory: /terraform/test
      runAzLogin: true

task には自身で script を定義することもできますし、market place で定義されている task を利用することもできます。

今回は便利ですので、以下のような extensions を利用しています。

https://marketplace.visualstudio.com/items?itemName=charleszipp.azure-pipelines-tasks-terraform

https://marketplace.visualstudio.com/items?itemName=ms-devlabs.custom-terraform-tasks

extensions の設定は Organization Settings の以下の箇所から行います。

話は pipeline の設定に戻って、次は Get Sourcesです。

Authorized using connection で設定した方法で pipeline と GitHub が接続されます。

接続方法には、GitHub Apps か OAuth の大きく2つの認証方式がありますが、GitHub Apps を利用する際は注意が必要です。

公式ドキュメントにも書いてありますが、1リポジトリにつき、1 Organization でしか利用できません。
ですので、他の Organization でも同じリポジトリに対して pipeline を作成する際は OAuth を利用して認証しましょう。

設定の詳細は Project Settings の Service connections から確認しましょう。

OAuthの場合、GitHub ユーザーは対象のリポジトリに対して「Write」権限を持っていれば十分です。

次に Variables です。 yaml では定義していますが、別途定義が必要なものはここで定義しましょう。

もし Variables group を設定したい場合は、Pipelines の Library から設定してください。
ここでは Pipeline permissons が設定でき、この Variables Group へのアクセス許可をしないと参照でエラーが出ることに注意してください。

最後に Triggers です。

Trigger は2種類設定ができ、
・CI (ブランチへのコミットにより発火)
・PR (プルリクにより発火)

どちらとも ON/OFF、yaml で設定した場合は Override できます。

最後に Save を押し、保存を忘れずにしてください。

次に先程 yaml で設定したサービス接続の設定をしましょう。
これにより、pipeline から Azure にアクセスでき、リソースを作成することができます。

前提でも書きましたが、今回はマネージドな環境を利用しているため、サービスプリンシパルを作成し、その資格情報を用いて認証します。 Managed Identity はユーザー管理のVM で pipeline agent を動かす場合に限り設定できるため、今回は対象外です。

ここでは Auto を選択することをおすすめします。
Auto では自動でサービスプリンシパルが作成され、シークレットをユーザー側で確認、持ち出すことはできません。
特にここでのサービスプリンシパルには「共同作成者」の権限を付与するため、漏れた際のリスクが大きすぎます。
これにより、よりセキュアな環境が実現されます。

作成されたサービスプリンシパルを選択し、「Managed Service Principal」を押すと、Azure AD のアプリ登録の画面に遷移するので、このサービスプリンシパルのIDを確認しましょう。

そして、今回リソースを作成する対象の Subscription の IAM でこのサービスプリンシパルに「共同作成者」の権限を付与します。

これで設定は完了です。 先程の terraform テンプレートをコミットなり PR なり作成して、pipeline を動かしましょう。

おわりに

同じベンダーが開発したサービスを利用すると、よりセキュアな環境で開発ができます。
パフォーマンスや使い勝手などはまだわかっていない部分ですが、今のところ不満は無いです。

OpenAI や Face API など Azure には魅力的な AI サービスが豊富にありますので、この際利用してみてはいかがでしょうか。
その際、環境構築に本記事が役立てば幸いです。

Azure での証跡ログはこれでサクッと解析する! Activity Log と Synapse Analytics を使った便利な方法

はじめに

こんにちは。

普段は新規開発メインの会社で SRE やデータエンジニアをしているものです。

クラウドプラットフォームとしては AWSGCP をメインで利用していますが、最近 Azure OpenAI の API が利用可能になったことで Azure も使い始めました。

マルチプラットフォームアーキテクチャを取っていますが、今後 Azure 内での様々なサービスの利用、リソースの作成が予想されるため、セキュリティや障害対応を考えて証跡ログの調査方法を確立させます。

みなさんがよく利用されている AWS で例えて言うと、CloudTrail の証跡ログを S3 にエクスポートし、そのバケット内のオブジェクトを Athena で分析するイメージです。

Azure の基礎知識

以降で出てくる内容、用語をスムーズに理解するために、まず基礎知識を説明します。
ChatGPT のほうが私よりも賢いので、聞いてみることもおすすめします。

・Azure AD

Microsoft Azureを利用するための管理単位です。
会社単位で作ることをおすすめします。

Microsoft アカウントを使って作成し、Azure AD 内で組織ユーザを作り、その組織ユーザーを使って Azure 内にリソースを作成していきます。
Microsoft アカウントを使ってあれこれすることはおすすめしません。

サブスクリプション

課金の単位です。
Azure AD 内で言うと最上位概念です。

すべてのリソースは後述するリソースグループに含まれ、そのリソースグループは1つのサブスクリプションと結び付けられている必要があります。
サブスクリプションの IAM で組織ユーザーに権限を付与することで、リソースグループ以降は権限を継承することができます。

サブスクリプションには以降の Activity Log はすべて表示されるため、本記事ではこちらを対象にします。

・リソースグループ

Azure 内でのリソースを管理する単位です。
できることは主に以下です。

・統一管理: リソースグループ内のリソースに対して一元的にアクセス制御、監視、およびアラート設定を行える。
・共通タグ付け
・リソースの展開: Azure Resource Manager テンプレートを使用して、リソースグループ内に複数のリソースを一度に展開できる。
・リソースの移動: リソースを他のリソースグループやサブスクリプションに移動することができる。
コンプライアンスとポリシー: Azure Policy を使用して、リソースグループ内のリソースに対してポリシーを適用し、コンプライアンスを確保できる。
・リソースの構成管理: リソースグループを使用して、アプリケーションの構成情報や設定を一元管理でる。
・セキュリティ: リソースグループを使用して、関連するリソースに対して共通のセキュリティ設定を適用できる。
・バックアップと復元: リソースグループを使用して、アプリケーションのリソースを定期的にバックアップし、必要に応じて復元できる。
・コスト共通可視化
・リソースの履歴追跡共通化

リソースグループの単位は諸説ありますが、ライフサイクルが共通するリソースを同じリソースグループに含めることがベストのようです。

Activity Log

リソースに対して、どのようなアクションがあったかを記録するログです。
最上位はサブスクリプションで、リソースグループ以降の単位でも参照できます。

本記事で証跡ログと言っているのはこのActivity Logのことです。

証跡ログ調査手順

前提

まず Activity Log を調査する方法はいくつかあります。
その中で、なぜ今回の方法を取ったのかを説明します。

1. Activity Log の検索機能

Activity Log でも検索機能があるので、それを使って解析をすることはできます。
ですが、機能に限界があるのと、デフォルト保持期間は90日間なので、こちらは採用しません。

2. Log Analytics

Activity Log のエクスポートから診断設定を作成して、Log Analyticsへログをエクスポートします。
AWSで例えるなら、CloudWatch Logsで調査するイメージです。

Log Analytics ではKQLを使ってログを解析することができます。

Activity Logの量は膨大で、簡素な方法では Log Analytics へのエクスポートに料金が多くかかるので、こちらも採用しません。

ちなみに保持期間はデフォルトで2年間です。

3. [ Storage Blob 経由 ] Log Analytics

Activity Log のエクスポート設定で Blob コンテナにログを保管する設定をします。
Blob コンテナへのデータエクスポートは料金が安いです。

この Blob コンテナ内のログデータを任意で Data Factory などのサービスを使って Activity Log へデータを移すことができます。
任意の量のデータを Log Analytics で解析できますが、もっと簡素で良い方法をこの後紹介します。

4. [ Storage Blob 経由 ] Synapse Analytics

Synapse Analytics は Azure 上でビッグデータ分析を可能にするサービスです。
T-SQLを使ってデータ分析するサーバーは、サーバーレスを選択しましょう。

このサーバーレス SQL プール上で Blob コンテナ内のデータを参照できます。

参照するやり方は OPENROWSET 関数を使った参照と、外部データを定義するやり方の2種類ありますが、より簡素な前者を選択します。

手順

Synapse Analytics の サーバーレス SQL プール内で OPENROWSET 関数を使って、Blob コンテナに保管されたActivity Log を解析する方法を示します。

前提

もし、Blob コンテナのパブリックアクセスが拒否されている場合は注意点があります。
Synapse Analytics から Blob コンテナを参照するに当たって、実際に SQL を実行する組織ユーザーに対して、IAMで「ストレージ BLOB データ閲覧者」以上の権限が明示的についているかを確認してください。
「所有者」の権限がついていたとしても、ストレージ Blob 専用の権限がついている必要があり、そうでないと Blob を参照できません。

またストレージアカウントのネットワークで「すべてのネットワークから有効」が選択されているかも確認してください。

1. Synapse Analytics ワークスペースを作成

必要な情報を入力して作成しましょう。

その際、Synapse Analytics のログを出力する Data Lake Storage Gen2 を作成しましょう。

2. Synapse Studio にログイン

Synapse Analytics 専用のコンソールです。

概要メニューから、ワークスペースのWeb URL を選択します。

3. OPENROWSET 関数を使って Blob コンテナのデータを参照

Synapse Studio から、Develop メニューを選択し、SQL スクリプトを選択します。

接続は組み込み (サーバーレス SQL プール)、データベースの仕様は master が選択されていることを確認してください。

以下のような、SQLでBlobコンテナ内のActivity Logを検索することができます。

-- 5/18, 5/19のActivity Logを対象として、検索
WITH tmp_target_log
AS
(
    SELECT *
    FROM OPENROWSET(
            BULK (
                'https://xxx.blob.core.windows.net/insights-activity-logs/resourceId=/SUBSCRIPTIONS/AAAAAAA-2222-RRRR-9999-MMMMMMMMMMMM/y=2023/m=05/d=18/**',
                'https://xxx.blob.core.windows.net/insights-activity-logs/resourceId=/SUBSCRIPTIONS/AAAAAAA-2222-RRRR-9999-MMMMMMMMMMMM/y=2023/m=05/d=19/**'
            ),
            FORMAT = 'CSV',
            FIELDTERMINATOR = '\0',
            FIELDQUOTE = '0x0b',
            ROWTERMINATOR = '0x0A'
        ) WITH (doc NVARCHAR(MAX)) AS rows
)
, target_log
AS
(
    SELECT
        -- 操作の名前の一部 操作の種類の詳細内訳 - "Write"/"Delete"/"Action"
        JSON_VALUE(doc, '$.category') as 'category',
        -- ログレベル
        JSON_VALUE(doc, '$.level') as 'level',
        -- イベント発生時間
        JSON_VALUE(doc, '$.time') as 'time',
        -- リソースID
        JSON_VALUE(doc, '$.resourceId') as 'resourceId',
        -- どのサービスに、どんなアクションをしたか
        JSON_VALUE(doc, '$.operationName') as 'operationName',
        -- アクションをした結果ステータス
        JSON_VALUE(doc, '$.resultSignature') as 'resultSignature',
        -- どのIPから呼び出されたか
        JSON_VALUE(doc, '$.callerIpAddress') as 'callerIpAddress',
        -- アクションの具体
        JSON_VALUE(doc, '$.identity.authorization.action') as 'action',
        -- アクションを実行した人の名前
        -- Azureに組織メンバーとして登録されているユーザー名
        JSON_VALUE(doc, '$.identity.claims.name') as 'name'
    FROM tmp_target_log
)
------------------------------------
-- 以下で詳細な条件を絞ることもできます --
------------------------------------
-- , detail_log
-- AS
-- (
--     SELECT
--         *
--     FROM tmp_target_log
--     WHERE name = 'hogetaro'
-- )
------------------------------------
SELECT * FROM target_log;

Activity Log が Blob コンテナに保存されるときは json 形式で保存されます。
以下が json スキーマです。

learn.microsoft.com

以下に、クエリ内の中間テーブルである detail_log 内でのクエリ例を簡単に紹介します。

◇ 特定の誰かに絞る

SELECT
    *
FROM tmp_target_log
WHERE LOWER('name') = LOWER('hogetaro');

◇ 特定のIPからのアクセスに絞る

SELECT
    *
FROM tmp_target_log
WHERE callerIpAddress = 'xxx.xxx.xxx.xxx';

以上で手順は完了です。

まとめ

Azure OpenAI が利用開始されるようになってから、Azure 上でアプリケーションを開発する機会がより増えてくると思います。

少しでも参考になれば幸いです。

Log4J問題をAWS WAFで防ぐ手順

はじめに

よくAWSにふれるものです。

2021年12月10日、歴史的な日になるかもしれない、その日にJava ベースのロギングライブラリであるLog4J脆弱性が見つかりました。

詳しくは情報処理推進機構のページをご覧ください。

www.ipa.go.jp

簡単に言うと、Log4Jの一部機能を悪用して、任意のコードを実行できてしまいます。

www.jpcert.or.jp

今回はAWS上で動いているJavaアプリケーションでそれを防ぐために、AWS公式が用意したWAFのルールを適用する手順を説明しようと思います。

これでLog4Jの誤作動を引き起こす文字列を含むリクエストを防ぐことができます。

手順

Javaアプリケーションへは、ELBを通ってサーバーに達っする想定で説明します。

なのでWAFはELBに挟みます。

Create web ACLをクリック f:id:xkenshirou:20211215233834p:plain

Nameにルールの名前を適用し、Regionを選択します。

Add AWS resourcesを選択します。 f:id:xkenshirou:20211215234038p:plain

Application Load Balancerを選択、好きなALBを選択してAddします。 f:id:xkenshirou:20211215234408p:plain

Add rulesからAdd managed rule groupsを選択。 f:id:xkenshirou:20211215234548p:plain

AWS managed rule groupsを選択してから、Known bad inputsのAdd to web ACLをONにします。 その後、Editをクリック。 f:id:xkenshirou:20211215234703p:plain

f:id:xkenshirou:20211215234814p:plain

Log4JRCEのみcountをOFFにして、save ruleします。その後、Add rulesします。 f:id:xkenshirou:20211215235224p:plain

目的のルールが追加されているか確認します。 f:id:xkenshirou:20211215235338p:plain

もしも他にもルールがある場合は、目的のruleを選択してMove upをクリックすることで、順位が上に来ます。最上位にしましょう。 f:id:xkenshirou:20211215235459p:plain

確認 f:id:xkenshirou:20211215235632p:plain

これでCreate web ACLをすれば完了です!

確認

普段叩けるpingがOKで、悪意のあるリクエストが弾ければOKです。

通常のping

$ curl https://xxx.yyy.com/ping

悪意のあるリクエス

$ curl -H 'User-Agent: ${jndi:ldap://0.0.0.0:389/1111111}' https://xxx.yyy.com/ping

まとめ

今回の脆弱性に対しては、世界的に温度感が高いので、早急に対応して、思わぬ事故を防ぎましょう。

誰かの助けになれば幸いです。