ngrokを使ってKonnectの監査ログを取得する

Webhookの受信確認に使われるサービスとして有名なものは以下辺りがある。
 
- [ngrok](https://ngrok.com/)
- [Webhook.site](https://webhook.site/)
- [Beeceptor](https://beeceptor.com/)

と言いつつ、ngrokは使ったことがなかったので、Konnectの監査ログ（Audit Log）を題材としてngrokと連携する方法を確認する。

## ngrokの利用（ローカル環境）
ngrokは元々はローカルのWebサーバなどをインターネットなどの外部に公開するためのトンネリング的な機能を提供するツールである。
ローカル環境で利用する場合、基本的には以下のような感じで動く模様。
![20250502114912.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f6f64aba-8e7b-4c26-9442-b385a4b79642.png)

ポイントとしては、エンドポイントを提供するのではなくAPI Gatewayのように機能し、Webhookで受け取ったデータを受け取るWebサーバを用意する必要がある点である。
ということで、ChatGPTにちゃちゃっと作ってもらった。
```python:myhttpserver.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import gzip
from io import BytesIO

class SimpleHandler(BaseHTTPRequestHandler):
    def _set_headers(self, code=200):
        self.send_response(code)
        self.send_header('Content-type', 'application/json')
        self.end_headers()

    def do_GET(self):
        self._set_headers()
        response = {'message': 'Hello from GET'}
        self.wfile.write(json.dumps(response).encode())

    def do_POST(self):
        content_length = int(self.headers.get('Content-Length', 0))
        post_data = self.rfile.read(content_length)

        print("===== POSTリクエスト受信 =====")
        if self.headers.get('Content-Encoding') == 'gzip':
            try:
                buf = BytesIO(post_data)
                f = gzip.GzipFile(fileobj=buf)
                decompressed = f.read().decode('utf-8')
                print(decompressed)
            except Exception as e:
                print("[gzipの展開に失敗]")
                print(e)
        else:
            try:
                print(post_data.decode('utf-8'))
            except UnicodeDecodeError:
                print("[非UTF-8データ]")
                print(post_data)
        print("=============================")

        self._set_headers()
        response = {'message': 'POST received'}
        self.wfile.write(json.dumps(response).encode())

def run(server_class=HTTPServer, handler_class=SimpleHandler, port=8080):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    print(f'Serving HTTP on port {port} ...')
    httpd.serve_forever()

if __name__ == '__main__':
    run()
```
なお、上記のコードでは受け取ったデータを表示するようにしているが、リクエストの中身はngrokからも確認できるので実装は必須ではない。
これを`http://localhost:8080`で起動する。
```bash
python3 myhttpserver.py
```

次にngrokを導入する。
「[【ngrok】Webhookの通知先をローカル環境にできませんか？](https://qiita.com/suzuko24/items/704c3b135a11aa4b8371)」というngrokの紹介記事によるとローカル環境に構築する場合でもサインアップは必須のようなので、サインアップを最初に行う。
今回はGoogleのアカウントを使ってサインアップした。
サインアップ後、以下のようなアンケート画面が出てくるので、適当に選択して進める。
![20250502105428.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/890a40d6-95dc-457c-bc7b-ec117cc06990.png)

ホーム画面でngrokのCLIのインストール、トークン設定、コマンドの起動が出てくるのでそれぞれ実行する。
起動時は引数なしだと誰でもリクエストを投げれてしまうので、念の為ここではBasic認証を利用して起動する。
```bash
ngrok http http://localhost:8080 --basic-auth "myuser:mypassword"
```
実行すると以下のような画面が表示される。
```
ngrok                                                                                                                       (Ctrl+C to quit)

🫶 Using ngrok for OSS? Request a community license: https://ngrok.com/r/oss

Session Status                online
Account                       hoge@fuga.com (Plan: Free)
Version                       3.22.1
Region                        Japan (jp)
Latency                       11ms
Web Interface                 http://127.0.0.1:4040
Forwarding                    https://https://7ad3-240f-7b-402d-1-189c-6c00-6681-57ea.ngrok-free.app -> http://localhost:8080

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
```
Web InterfaceのURL(`http://127.0.0.1:4040`)を開くと、ngrokの管理画面が表示される。
![20250502115558.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/83eb5a6a-9cb6-4b1b-808a-467a39a8563a.png)

トラフィックが流れた後はここから中身を確認できる。
Fowardingの `https://https://7ad3-240f-7b-402d-1-189c-6c00-6681-57ea.ngrok-free.app` の部分がWebhookのエンドポイントになるもので、これをKonnectのWebhookに設定する。

次にKonnectを設定する。
Konnectで設定する際にAuthorizationヘッダを指定することが出来るが、ここに起動時に指定したBasic認証のユーザ名とパスワードをBase64でエンコードしたものを指定する。
まずBase64でエンコードする。
```bash
echo -n 'myuser:mypassword' | base64
```
KonnectのWebhookを以下のように設定する。
![20250502115929.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/8a8b564a-d2d0-4904-a4f1-d2f8bb7af679.png)

設定後に何かしらKonnectで操作を行うと、ngrokの管理画面にリクエストが流れてくる。
```
Connections                   ttl     opn     rt1     rt5     p50     p90
                              60      0       0.11    0.10    0.01    0.01

HTTP Requests
-------------

11:59:29.031 JST POST /                         200 OK
11:59:29.075 JST POST /                         200 OK
11:59:28.576 JST POST /                         200 OK
```
WebUIでの管理画面では以下のような形でリクエストの詳細が確認できる。
![20250502120125.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/fdbc8f8a-63e1-463c-a756-7d44a093c44c.png)

問題なく転送できているようだ。


## ngrokの利用（SaaS利用）

ローカルにWebサーバを立てるのが面倒な場合は、SaaSでWebhookを受け取ることもできる。
受け取ったリクエストはダッシュボードにある[`Traffic Inspector`](https://dashboard.ngrok.com/traffic-inspector)から確認できる。
この`Traffic Inspector`はデフォルトではメタデータのみ表示するようになっており、ヘッダやボディを完全に表示するにはダッシュボードの`Account`から`Full capture`を`Enabled`にする必要がある。
![20250502130332.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/80b6d937-a255-44da-8aa9-0420793b776a.png)

これはどうもエンドポイントの作成の前に設定する必要があるようで、後から変更しても作成後のエンドポイントには反映されない模様なので注意。

`Full capture`設定後、アクセス先となるSaaS上のエンドポイントのドメインを払い出す。
sngrokの[ダッシュボード](https://dashboard.ngrok.com/)から`Domains`を選択し、``Create Domain`を選択する。
![20250502125158.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f5c9493a-5699-4b35-bf07-0daf046e891a.png)

すると`Start a Tunnel`と書いたウィンドウが出るのでこれを閉じると自動で生成されたドメインが表示される。
![20250502125335.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/8f1189fc-28d3-4af5-9b61-a6b35fc77ff9.png)

このドメイン名をコピーし、次にダッシュボードの`Endpoint`から`New Endpoint`を選択する。
![20250502120547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/9ac4af3a-4dc5-4297-a553-b422a568dc77.png)

選択後、`Cloud Endpoint`を選択するとエンドポイントの種類が表示されるので、ここではPublicとしてURLに先ほどコピーしたドメイン名を貼り付け、`https://`を追加し`Create Cloud Endpoint`をクリックする。
![20250502125517.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/9170d37b-cb7c-432e-a5fa-f98d3b7ed560.png)

エンドポイント作成後、エンドポイントに対するポリシーがYAMLで表示されるが、ここにBasic認証の設定を追加する。
Basic認証の設定方法は[こちら](https://ngrok.com/docs/traffic-policy/actions/basic-auth/)にあるので、これを参考にして`actions`以下に次のように追加で設定する。
```yaml
on_http_request:
  - name: DefaultCloudEndpointPolicy
    actions:
      - type: basic-auth
        config:
          credentials:
            - myuser:mypassword
        ：（以降は元あった設定、記載は省略）
```
この設定を追加したら、`Save`をクリックして保存する。
次にKonnectのWebhookを設定する。
設定手順はエンドポイントのURLを変えるだけで後はローカル環境の時と同じなので割愛する。
問題なければ以下のような感じでリクエストが表示される。
![20250502132408.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/b1294264-022c-44e1-8c2c-0be423cdb32c.png)

なおgzipで圧縮されているリクエストのボディは展開して見せてはくれない模様（どこかを設定したら上手くいくのかもしれないが、ドキュメントや設定項目を見てもそれらしきものが見当たらず。。。）
![20250502132452.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/1fdfdb02-5a37-4433-a0b4-e9d15306df2e.png)
ただ、リクエストが来ているかの確認くらいには使えるので、ちょっとした動作確認ではこれでもよさそうだ。