ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

Orca Security と Backlog の自動連携方法

こんにちは、Orca Security 担当の尾谷です。

Orca Security と Backlog の自動連携方法ということで、Orca Cloud Security Platform でアラートを検知したら、自動で Backlog に課題を起票する仕組みと、Backlog で課題を完了ステータスに変更したら、関連する Orca アラートを自動でクローズする仕組みを作りましたので、ご紹介させていただきます。

構成図

本ブログでご紹介するのは、以下のような構成イメージです。

項番の 1 と 2 が、Orca Cloud Security Platform をトリガーに Backlog チケットを起票するまでのフローで、3 と 4 が、Backlog をトリガーに Orca のチケットを Closed にするまでのフローです。

  1. Orca Automations
    Orca で High 以上のアラートを検知したら、Automations の AWS SQS でキューを送信します。
  2. Backlog API
    Amazon Simple Queue Service のキューをトリガーに、AWS Lambda 関数が呼び出され、Backlog API を用いて課題を起票します。
    この際、アラート ID を Backlog 課題のカスタム属性フィールドに登録します。
  3. Backlog Webhook
    Backlog の課題を完了したら、Webhook で API Gateway のエンドポイントをコールします。
    この際、課題のカスタム属性フィールドに登録された アラート ID を API コール時のパラメーターに指定します。
  4. Orca API
    API Gateway に紐付けた統合 Lambda が、Orca アラート ID をキーにチケットを Orca のアラートのステータスを Closed にします。

Orca Automations

Orca で High アラート以上を検知したら、AWS SQS を実行する Automations を作成しました。

Orca Automations - AWS SQS の使い方は以下ブログ記事でご紹介しています。

techblog.forgevision.com

Webhook での連携も検討しましたが、万が一、大量にアラートを検知した際に、通知対象を取りこぼさないよう、Amazon SQS のキューに入れることにしました。

Amazon Simple Queue Service

SQS キューに Lambda トリガーを設定しました。

Lambda 関数には以下の SQS を操作する IAM ポリシーを追加しました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ReceiveMessage",
            "Effect": "Allow",
            "Action": [
                "sqs:ReceiveMessage",
                "sqs:DeleteMessage",
                "sqs:GetQueueAttributes"
            ],
            "Resource": "arn:aws:sqs:ap-northeast-1:123456789012:amazon-sqs-queue"
        }
    ]
}

Lambda 関数には、SQS から以下のようなイベントが届きます。body の中に Orca のアラート情報が含まれます。
Value を削除しているので、シンプルに見えますが、body は非常に多くのデータが含まれます。

{
    "Records": [
        {
            "messageId": "",
            "receiptHandle": "",
            "body": "",
            "attributes": {
                "ApproximateReceiveCount": "",
                "SentTimestamp": "",
                "SenderId": "",
                "ApproximateFirstReceiveTimestamp": ""
            },
            "messageAttributes": {},
            "md5OfBody": "",
            "eventSource": "aws:sqs",
            "eventSourceARN": "",
            "awsRegion": ""
        }
    ]
}

※ Value は削除して Key のみ記載しています。今後、Orca Security の改修作業によって仕様が変更される可能性があります。

body の中に含まれる Orca アラートの情報は、以下のような形で取り出せます。

import os
import json
import requests

def lambda_handler(event, context):

    # event から Orca のデータを取得
    arr_records = event.get('Records')
    str_body = arr_records[0].get('body')
    json_body = json.loads(str_body)
    json_data = json_body.get('data')
    # アラートタイトル
    str_title = json_data.get('title')
    # アラートの概要
    str_details = json_data.get('details')
    # アラート ID
    str_alert_id = json_body.get('state').get('alert_id')

※ 見やすいようにコメントを入れて、エラー分岐は省略しています。

Lambda レイヤー

Requests は、Lambda レイヤーで Klayers-p312-arm64-requests を指定しました。
以下、ARN です。

arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p312-arm64-requests:7

Backlog API

1 にて、SQS イベントからデータが抽出できました。
コードで用意した変数とその内容をご紹介します。

  • str_title:
    Orca アラートのタイトルです。Backlog のタイトルにします。
  • str_details:
    Orca アラートの詳細です。Backlog の本文に入力します。
  • str_alert_id:
    Orca アラート ID です。後ほど Backlog の課題を完了にした際にパラメーターとして利用します。

データが揃ったので、Backlog の課題を起票する部分をご紹介します。

Backlog 課題を追加する Lambda 関数

Backlog API は、以下のサイト で公開されています。
このリファレンスの中で利用したのは、課題一覧の取得課題の追加課題コメントの追加課題情報の更新 の 4 つです。

最初は、課題を追加するだけの Lambda 関数をコーディングしていましたが、アラートが重複して起票される不具合があったので改修しました。

既に起票していないかチェック

Orca は、アラートが Closed ステータスになると、そのリスクが本当に解消されたか検証をします。
リスクが解消されていない場合は、アラートを再度 Open ステータスや In Progress ステータスに変更します。 Lambda 関数をただ、課題を起票するだけの処理でコーディングしてしまうと、Orca アラートのステータスが Open に戻ったタイミングで Backlog 課題が二つ起票されてしまいます。

そこで Lambda 関数に、Backlog 課題の起票前にアラート ID を含む Backlog 課題が既に起票されていないか確認する処理を入れました。

    api_key = os.environ['BACKLOG_API']
    project_id = os.environ['PROJECT_ID']
    workgroup_name = os.environ['WORKGROUP_NAME']

    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    # アラート ID を含むアラートが存在するか確認する
    backlog_url = f"https://{workgroup_name}.backlog.jp/api/v2/issues?apiKey={api_key}&projectId[]={project_id}&customField_110282={alert_id}"
    r = requests.get(backlog_url, headers = headers)

以下、用意した変数と、環境変数などをご紹介します。

Backlog API Key

コード内の変数名: api_key

Backlog の API キーは個人設定から発行できます。

Project ID

コード内の変数名: project_id

Project ID は、Backlog プロジェクトを開き、プロジェクト設定をクリックすると見つかります。
URL に、?project.id=000000 と表記されます。

もし、課題が見つからない場合は、新規で課題を追加し、課題が既に登録されていた場合は、コメントを記載します。

Workgroup 名

コード内の変数名: workgroup_name

Workgroup 名は、Backlog のワークグループ名です。
これも、URL を参照します。
https://XXX.backlog.jp となっているかと思います。この XXX がワークグループ名です。

Backlog 課題の起票

    data = {
        'projectId': project_id,
        'summary': str_title,
        'description': str_details,
        'issueTypeId': 000000,
        'priorityId': 1,
        'customField_110282': alert_id
    }
    # 課題を起票する
    backlog_url = f"https://{workgroup_name}.backlog.jp/api/v2/issues?apiKey={api_key}"
    r = requests.post(backlog_url, data = data, headers = headers)

Backlog 課題コメントを追記

        data = {
            'content': f"アラート: {alert_id} がリオープンしました。'
        }
        # コメント追記
        backlog_url = f"https://{workgroup_name}.backlog.jp/api/v2/issues/{str_keyId}/comments?apiKey={api_key}"
        r = requests.post(backlog_url, data = data, headers = headers)

Backlog 課題のステータス変更 (完了から処理中に変更)

        # 課題のステータス変更
        data = {
            'statusId': '2'
        }
        
        # 課題の更新
        backlog_url = f"https://{workgroup_name}.backlog.jp/api/v2/issues/{str_keyId}?apiKey={api_key}"
        r = requests.patch(backlog_url, data = data, headers = headers)

※ ステータスの変更メソッドは、POST ではなく、PATCH でした。

カスタム属性フィールドの ID

コード内の変数名: custom_fid

Orca のアラートは、Close するとそのアラートのリスクが本当に解消しているか検証を行います。
以前はクローズしたアラートがゾンビのように戻ってくると、問い合わせをよくいただきましたが、現在は、Closed オプションを選択するとメッセージが表示されるように UI が改修されました。

一度 Backlog 課題として起票したアラートが、Closed 処理後に検証され、リオープンした際に再度 Backlog 課題が起票されてしまうと課題が重複してしまいます。

そこで、Backlog にカスタム属性フィールドを追加しアラート ID をパラメーターとして渡せるようにしました。

カスタムフィールドの ID はプロジェクト設定のカスタム属性を開き、URL を見ると確認できます。
今回検証で作成したカスタムフィールド ID は、110282 でした。

そのため、API で指定するパラメーター名は、customField_110282 になります。

優先度

コード内の変数名: priorityId

優先度は、1: 高, 2: 中, 3: 低 から選択します。

種別の ID

コード内の変数名: issueTypeId

種別は Backlog 課題を起票する際に必須となるパラメーターです。
プロジェクト設定の種別を開き、URL を見ると確認できます。

チケットがリオープンしたときは、こんな感じで更新されます。

Backlog Webhook

Backlog 課題をクローズした際の処理です。
API Gateway をデプロイして、エンドポイントを払い出し、Backlog Webhook の WebHook URL に設定しました。

統合 Lambda のコードを紹介します。

event 処理

コードの冒頭です。
Backlog Webhook を用いて送信されたデータは、event で届きます。

import json
import requests

def lambda_handler(event, context):
    # event から body (文字列) を取得
    alert_id = ''
    str_body = event.get('body')
    if not str_body:
        print('This event is no body.')
        return
    json_body = json.loads(str_body)
    if json_body.get('content').get('status').get('name') != '完了':
        print('This event is not a closed ticket.')
        return
    
    # カスタム属性フィールドチェック
    arr_custom_field = json_body.get('content').get('customFields')
    for f in arr_custom_field:
        if f.get('field') == 'Alert-Id':
            alert_id = f.get('value')
    if not alert_id:
        print('This event do not has a Alert-id.')
        return

※ 見やすいようにコメントを入れて、エラー分岐は省略しています。

課題が完了ステータスに変更された場合のみ処理するようにしました。
Alert-id が含まれない課題がクローズされたときは、対象外にしました。

Backlog Webhook から届く課題の情報

Backlog Webhook で取得した event には、body がありまます。body には、文字列型で課題の情報が入っています。

以下、body に含まれるステータス部分です。
name に完了が記述されている場合は、後続の処理を行います。

        "status": {
            "id": 4,
            "projectId": 126588,
            "name": "完了",
            "color": "#b0be3c",
            "displayOrder": 4000
        },

Orca API

最後は、Orca API を使ってアラートを Close する処理について解説します。

Orca API でアラートを Closed にする

以下のような形でアラートのステータスを変更しました。

Orca API は Swagger が用意されていて、GUI で試せます。
以下、ブログ記事で藤原さんが詳しく紹介しています。

techblog.forgevision.com

API Token の払い出し方法は、こちらのブログ記事 でご紹介しています。

Orca API でアラートをクローズする方法

以下、コーディングしました。

    orca_api_url = f"https://{orca-tenant-url}/api/alerts/{alert_id}/status/close"
    data = {}
    headers = {
        'Content-Type': 'application/json',
        'accept': 'application/json',
        'Authorization': f"Token {api_token},
        'x-orca-ui-origin': 'true'
    }

    response = requests.put(orca_api_url, headers=headers, json=data)
    response.raise_for_status()

まとめ

如何でしたでしょうか。

かなり長くなりましたが、Orca で検知したアラートを自動で Backlog に課題を起票する仕組みと、Backlog で課題を完了ステータスに変更したら、関連する Orca アラートを自動でクローズする仕組みのご紹介でした。

Orca Security にはアラート管理機能も付与されており、PagerDuty や ServiceNow、Jira との連携機能は標準で用意されています。
一方で、日本のお客様ですと、Backlog で管理したいといったニーズも一定数あるかと思います。

エラー分岐など、かなり省略して記載しましたので、お分かりにならない点も多々あると思いますし、弊社にて組み込むことも可能ですので、是非、お声がけください。