はじめに
この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2022 (1枚目) の 14日目として投稿しています。
2022年版 1/2枚目
過去のカレンダー
2017年版 2018年版 2019年版 2020年版1枚目 2020年版2枚目 2021年版1枚目 2021年版2枚目
SecureX Orchestrationについては、以下の記事にて紹介されていますので、これらを参照ください。
無料のクラウドPython環境としてのSecureX
Cisco SecureX を使って簡易 REST API モジュールを作る
サンプルのWorkflowがやってくれること
サンプルのWorkflowは、Webexに投稿されたBot宛のメッセージを処理するための枠組みを提供しています。
Pythonで処理をさせているので、その中身を拡張することで、コマンドに応じて何らかの処理をさせて、その結果をWebexにPostするというようなアプリが作成できます。
例えば、SecureXで連携しているセキュリティソリューションのステータスを確認したり、何らかの設定変更をする手段を提供する、というようなBOTアプリへの応用が考えられます。
サンプルの動作イメージ
SecureXでWorkflowを作成する
Step1: Webhookの作成
SecureX Orchestrationで、Webhookの受け口となるエントリを作成します。
Step2: Webexの準備
1. BOTの作成
以下のページの"Create a New App"から新規にBOTを作成し、Access Tokenとxxx@webex.bot
形式のUsernameを記録します。
https://developer.webex.com/my-apps
Access Tokenは再表示することができず、わからなくなると再生成となってしまうので、必ず何らかの方法で記録を残しましょう
2. BOTをメンバーに加えたWebexのスペースを作成
WebexのアプリケーションでBOTがメンバーとして含まれるスペースを作成します。
3. Room IDを調べる
Webex APIを使って、作成したスペースのRoom IDを調べます。
https://developer.webex.com/docs/api/v1/rooms/list-rooms
sortBy: createdとすると、新しい順にソートされますので、2で作ったスペースのRoom IDを記録します。
4. BOTのPerson IDを調べる
Webex APIを使って、作成したBOTのPerson IDを調べます。
https://developer.webex.com/docs/api/v1/people/list-people
email: BOTのUsernameでフィルタし、BOTのidを記録します。
(1のBOTの管理画面で確認できるIDと、ここで調べたPerson IDは異なるものです)
5. WebexのWebhookを作成
Webex APIを使って、WebexのWebhookを作成します。
https://developer.webex.com/docs/api/v1/webhooks/create-a-webhook
パラメータ | 内容 |
---|---|
name | 任意の名前 |
targetUrl | Step1で作成したSecureXのWebhook URL |
resource | messages |
event | created |
filter | roomId=<3のRoomID> &mentionedPeople=<4のBotのID> |
これらのパラメータを設定すると、BOT宛のメンション付きメッセージが投稿された場合に、SecureXに対してWebhookを通じてイベントの発生を通知するようになります。
今回の例ではsecretは設定しておらず、SecureX側でもメッセージの検証は実施していません。
セキュリティレベルを高めるためには、secretを設定してメッセージに対する署名の検証を実施することを推奨します。
Authenticating Requests
Step3: SecureX workflowのインポート
Workflow JSON
サンプルJSON (クリックして展開してください)
{
"workflow": {
"unique_name": "definition_workflow_021FSI6HRJKRJ2Z8DzviBz0NCS74TcZ3Hhn",
"name": "Webex Webhook Receiver",
"title": "Webex Webhook Receiver",
"type": "generic.workflow",
"base_type": "workflow",
"variables": null,
"properties": {
"atomic": {
"is_atomic": false
},
"delete_workflow_instance": false,
"display_name": "Webex Webhook Receiver",
"runtime_user": {
"target_default": true
},
"target": {
"target_type": "web-service.endpoint",
"target_id": "definition_target_01QBNQMLV0JZ60DEPeaVtzSjfEvrJE3QFeh",
"execute_on_workflow_target": true
}
},
"object_type": "definition_workflow",
"actions": [
{
"unique_name": "definition_activity_021FSI6NZ4XKZ4VFFRGHvZHDtKYWfsfZkrP",
"name": "JSONPath Query",
"title": "Get message/room IDs",
"type": "corejava.jsonpathquery",
"base_type": "activity",
"properties": {
"action_timeout": 180,
"continue_on_failure": false,
"description": "Get messageId from body of webhook",
"display_name": "Get message/room IDs",
"input_json": "$trigger.triggerevent_021FSI6N0ECFR40XDW449RAQnvNIUM0U9rW.output.request_body$",
"jsonpath_queries": [
{
"jsonpath_query": "$.data.id",
"jsonpath_query_name": "msgId",
"jsonpath_query_type": "string",
"zdate_type_format": "yyyy-MM-dd'T'HH:mm:ssZ"
},
{
"jsonpath_query": "$.data.roomId",
"jsonpath_query_name": "roomId",
"jsonpath_query_type": "string",
"zdate_type_format": "yyyy-MM-dd'T'HH:mm:ssZ"
}
],
"skip_execution": false
},
"object_type": "definition_activity"
},
{
"unique_name": "definition_activity_021FSI6P901D10XSWFKvlKiqdKUH5fNWRpy",
"name": "HTTP Request",
"title": "Webex - Get Message",
"type": "web-service.http_request",
"base_type": "activity",
"properties": {
"action_timeout": 180,
"allow_auto_redirect": true,
"allow_headers_redirect": false,
"content_type": "application/json",
"continue_on_error_status_code": false,
"continue_on_failure": false,
"custom_headers": [
{
"name": "Authorization",
"value": "Bearer $env.variable_021FSRR44GSHJ25067E6CfbyKEzPDoxbbVz.env.variable_021FSRR44GSHJ25067E6CfbyKEzPDoxbbVz$"
}
],
"description": "Get message content based on messageId which was informed on the body of webhook",
"display_name": "Webex - Get Message",
"method": "GET",
"relative_url": "/v1/messages/$activity.definition_activity_021FSI6NZ4XKZ4VFFRGHvZHDtKYWfsfZkrP.output.jsonpath_queries.msgId$",
"runtime_user": {
"target_default": true
},
"skip_execution": false,
"target": {
"use_workflow_target": true
}
},
"object_type": "definition_activity"
},
{
"unique_name": "definition_activity_021FSI6QU5ZOU1iNjO0616wCUNBw0fafahb",
"name": "Execute Python Script",
"title": "Parse command string",
"type": "python3.script",
"base_type": "activity",
"properties": {
"action_timeout": 180,
"continue_on_failure": false,
"display_name": "Parse command string",
"script": "import sys, json, re\n\nstrHtml = json.loads(sys.argv[1])['html']\nm = re.search('<\\/spark-mention>\\s*(.*)\\s*<\\/p>',strHtml)\nstrCmd = m.group(1)\n\nstrWebexMsg = strCmd",
"script_arguments": [
"$activity.definition_activity_021FSI6P901D10XSWFKvlKiqdKUH5fNWRpy.output.response_body$"
],
"script_queries": [
{
"script_query": "strWebexMsg",
"script_query_name": "strWebexMsg",
"script_query_type": "string"
}
],
"skip_execution": false
},
"object_type": "definition_activity"
},
{
"unique_name": "definition_activity_021FSSTV4PSJB09Q70ZYcJSUY88MDvlFBwh",
"name": "Webex - Post Message to Room",
"title": "Webex - Post Message to Room",
"type": "workflow.atomic_workflow",
"base_type": "subworkflow",
"properties": {
"continue_on_failure": false,
"display_name": "Webex - Post Message to Room",
"input": {
"variable_workflow_01PP78DJH1TI76BYfsu9g0Tqj2S6cUxjtu5": "$activity.definition_activity_021FSI6NZ4XKZ4VFFRGHvZHDtKYWfsfZkrP.output.jsonpath_queries.roomId$",
"variable_workflow_01PP78DJH1XNQ7gNQ5iZdperRHqrppzARXC": "$env.variable_021FSRR44GSHJ25067E6CfbyKEzPDoxbbVz.env.variable_021FSRR44GSHJ25067E6CfbyKEzPDoxbbVz$",
"variable_workflow_01PP78DJH1YWL3allalGQbg1VkgKwh9GvCi": "",
"variable_workflow_01PP78DJH22BB3Ej3I8tJ4OCQur0unYGjj9": "$activity.definition_activity_021FSI6QU5ZOU1iNjO0616wCUNBw0fafahb.output.script_queries.strWebexMsg$",
"variable_workflow_01SVERQNMKN8N6vqX2djMtAfshphGydGsH8": ""
},
"runtime_user": {
"target_default": true
},
"skip_execution": false,
"target": {
"target_type": "web-service.endpoint",
"use_workflow_target": true
},
"workflow_id": "definition_workflow_01PP78DJMXS415nTjonujf03ROkr6t2PNyw",
"workflow_name": "Webex - Post Message to Room"
},
"object_type": "definition_activity"
}
],
"categories": [
"category_1BMfMXSnJMyt5Ihqi7rWJr5N8cf"
]
},
"triggers": {
"triggerevent_021FSI6N0ECFR40XDW449RAQnvNIUM0U9rW": {
"workflow_id": "definition_workflow_021FSI6HRJKRJ2Z8DzviBz0NCS74TcZ3Hhn",
"name": "webex message created",
"title": "",
"lowercase_name": "event.webex_message_created",
"type": "event",
"base_type": "trigger",
"ref_id": "event_webhook_021C5Q1RM9I7I05ETt1iabQLBUwfAVsYHq9",
"version": "1.0.0",
"disabled": false,
"unique_name": "triggerevent_021FSI6N0ECFR40XDW449RAQnvNIUM0U9rW",
"object_type": "triggerevent"
}
},
"events": {
"event_webhook_021C5Q1RM9I7I05ETt1iabQLBUwfAVsYHq9": {
"name": "Webhook event - Webex BOT",
"title": "Webhook event - Webex BOT",
"type": "webhook.event",
"base_type": "event",
"object_type": "event_webhook",
"target_id": "",
"webhook_id": "webhook_021GC3O4FYSVO00JC8U4MGWwYR8lXllFODJ",
"version": "1.0.0",
"properties": {
"display_name": "Webex BOT",
"title": "Webhook event - Webex BOT",
"webhook_id": "webhook_021GC3O4FYSVO00JC8U4MGWwYR8lXllFODJ"
},
"unique_name": "event_webhook_021C5Q1RM9I7I05ETt1iabQLBUwfAVsYHq9"
}
},
"targets": {
"definition_target_01QBNQMLV0JZ60DEPeaVtzSjfEvrJE3QFeh": {
"unique_name": "definition_target_01QBNQMLV0JZ60DEPeaVtzSjfEvrJE3QFeh",
"name": "Webex Teams",
"title": "Webex Teams",
"type": "web-service.endpoint",
"base_type": "target",
"object_type": "definition_target",
"properties": {
"description": "Webex Teams",
"disable_certificate_validation": false,
"display_name": "Webex Teams",
"host": "webexapis.com",
"no_runtime_user": true,
"port": 443,
"protocol": "https"
}
}
},
"variables": {
"variable_021FSRR44GSHJ25067E6CfbyKEzPDoxbbVz": {
"unique_name": "variable_021FSRR44GSHJ25067E6CfbyKEzPDoxbbVz",
"properties": {
"value": "*****",
"scope": "env",
"name": "WebexBOT-Token",
"type": "datatype.secure_string",
"is_required": false,
"is_invisible": false
},
"object_type": "variable"
}
},
"atomic_workflows": [
"definition_workflow_01PP78DJMXS415nTjonujf03ROkr6t2PNyw"
],
"webhooks": {
"webhook_021GC3O4FYSVO00JC8U4MGWwYR8lXllFODJ": {
"name": "WebexBOT - post message",
"title": "WebexBOT - post message",
"type": "generic.webhook",
"base_type": "webhook",
"object_type": "webhook",
"version": "1.0.0",
"properties": {
"display_name": "WebexBOT - post message",
"request_content_type": "application/json; charset=utf-8"
},
"unique_name": "webhook_021GC3O4FYSVO00JC8U4MGWwYR8lXllFODJ"
}
},
"dependent_workflows": [
"definition_workflow_01PP78DJMXS415nTjonujf03ROkr6t2PNyw"
]
}
Step4: SecureX webhook eventの変更
インポートされたイベントにはStep1で作成したWebhookが関連づいていないため、内容を変更します。
Workflow 各ステップの処理内容概説
- CORE (JSONPath Query): Get message/room IDs
WebexのWebhookが送信してきたJSONから、イベントのもととなったメッセージ、ルームのIDを抽出。
階層としては'data' > 'id', 'data' > 'roomId'が該当するため、JSONPathの記述としては'\$.data.id', '\$.data.roomId'の表記になる。(https://github.com/json-path/JsonPath) - WEB SERVICE (HTTP Request): Webex – Get Message
1で取得したMessageIdの詳細情報をWebexAPIで取得する。
ATOMIC actionの定義がないため、HTTP Requestで実行。アクセス先のホストの定義は、Workflow全体のプロパティにあるTargetで"Webex Teams" (Default定義のTarget) を指定。 - PYTHON: Parse command string
2で取得したメッセージの詳細情報にあるHTMLコンテンツから、コマンド部分を抽出。
"SCRIPT OUTPUTVARIABLES"として定義した"strWebexMsg"に抽出した文字列をセットする。このPythonの内容を改変することで、コマンドの内容に従った動作をさせる事ができる。 - ATOMIC: Webex – Post Message to Room
3で生成したメッセージを、BOTを投稿者として、イベントのもととなったWebexのRoomへ投稿する。Room IDは1で抽出したもの。
テスト
ここまでで、SecureX Orchestration、Webexの設定作業は完了です。
最初に紹介した動作イメージのように、作成したスペースで、BOT宛のメンション付きメッセージを送ってみましょう。
SecureXにおけるWebhookの制限事項
本記事の作成時点で、以下のような制限事項があります。
- WebhookのPayloadサイズは1MBまで
- レートリミット
- 5回/1分 (イベント単位でカウント)
- 5000回/1日 (外部イベントをトリガとするすべてのイベントの合算)
このような制限が設けられているため、特にイベント数が多くなるような環境では利用に当あたり注意が必要です。
免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。