🙈

OpenTelemetry CollectorでSpan内のPIIを削除

2023/05/09に公開

はじめに

OpenTelemetryでコードに計装したところうっかりSpanのメタデータに個人情報を含めてしまった!・・・あまり無いと思いますが、そんなときに何ができるかです。
以下の三つが考えられます。

  1. コードを修正する
    すぐに対応できるならベストです。

  2. OpenTelemetry CollectorのAttributes Processorを使う
    今日のお話です。一旦これで対応してしまって後でゆっくりコード修正します。

  3. バックエンド(APM)側で対応する
    バックエンド側で個人情報を削除する機能があればそれを使うのもよいでしょう。一旦これで対応してしまって後でゆっくりコード修正します。

Attributes Processorとは?

https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/attributesprocessor

SpanのAttributeを操作できるProcessorです。以下の操作が可能です。

insert:入力データのうち、キーがまだ存在しないものに新しい属性を挿入する。
update:入力データのうち、キーが存在する属性を更新する。
upsert:insertまたはupdateを実行する。キーがまだ存在しない入力データに新しい属性を挿入し、キーが存在する入力データ内の属性を更新する。
delete:入力データから属性を削除する。
hash:属性値をハッシュ化(SHA1)する。
extract: 正規表現規則を用いて、入力キーから規則で指定されたターゲットキーに値を抽出する。ターゲットキーがすでに存在する場合は、上書きされる。注:既存の属性をソースとするSpan Processorのto_attributes設定と同様の動作をする。
convert: 既存の属性を指定された型に変換する。

insertまたはupsertを使いアプリのバージョンや担当チームなどを一律で設定する使い方をよくしますね。
今回は値を削除またはマスキングしたいのでupdatedeletehashを試してみます。

テスト環境

今回はPythonに対して計装したいと思います。
Webページから個人情報を入力し、その値をSpanのAttributeとしてセットします。
テストのためのフロントエンドとバックエンドを作ってみました(ChatGPTが)。

フロントエンド

個人情報を入力しSubmitするだけの画面です。

static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PI Data Submission</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</head>
<body>
    <div class="container">
        <h1>Submit PI Data</h1>
        <form id="pi-data-form">
            <div class="form-group">
                <label for="username">Username:</label>
                <input type="text" class="form-control" id="username" required>
            </div>
            <div class="form-group">
                <label for="email">Email:</label>
                <input type="email" class="form-control" id="email" required>
            </div>
            <div class="form-group">
                <label for="password">Password:</label>
                <input type="password" class="form-control" id="password" required>
            </div>
            <div class="form-group">
                <label for="credit-card">Credit Card:</label>
                <input type="text" class="form-control" id="credit-card" required>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
        <div id="result" class="mt-3"></div>
    </div>
    <script>
        $("#pi-data-form").on("submit", function(event) {
            event.preventDefault();
            const data = {
                username: $("#username").val(),
                email: $("#email").val(),
                password: $("#password").val(),
                credit_card: $("#credit-card").val()
            };
            $.ajax({
                type: "POST",
                url: "/submit",
                contentType: "application/json",
                data: JSON.stringify(data),
                success: function(response) {
                    if (response.status === 1) {
                        $("#result").html("<div class='alert alert-success'>PI Data has been submitted!</div>");
                    } else {
                        $("#result").html("<div class='alert alert-danger'>Something went wrong.</div>");
                    }
                }
            });
        });
    </script>
</body>
</html>

バックエンド

TraceとSpanの作成は自動計装(Auto Instrumentation)に任せ、SpanへのAttribute追加だけを手動計装(Manual Instrumentation)します。
詳細はこちらをご参照ください。
https://opentelemetry.io/docs/instrumentation/python/getting-started/

app.py
from flask import Flask, request, jsonify, send_from_directory
from opentelemetry import trace

tracer = trace.get_tracer("pidata.tracer")

app = Flask(__name__)

@app.route('/attributesprocessor/')
def serve_index():
    return send_from_directory('static', 'index.html')

@app.route('/submit', methods=['POST'])
def submit():
    data = request.json
    with tracer.start_as_current_span("submit") as submit_span:
        submit_span.set_attribute("username", data["username"])
        submit_span.set_attribute("email", data["email"])
        submit_span.set_attribute("password", data["password"])
        submit_span.set_attribute("credit_card", data["credit_card"])

    return jsonify(status=1)

if __name__ == '__main__':
    app.run(debug=True)

OpenTelemetry Collector

APMに送信します。
今回はsapm(Splunk Observability Cloud)に送っていますがJeagerでもなんでもいいです。

/etc/otelcol-contrib/config.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:
      
processors:
  batch:

exporters:
  sapm:
    access_token: "<アクセストークン>"
    endpoint: "https://ingest.<REALM>.signalfx.com/v2/trace"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [sapm]

試してみる

自動計装しつつPython(Flask)を起動します。

opentelemetry-instrument flask run

無事立ち上がりました。個人情報をモリモリ入れ込んでSubmitします。

Before

APMで拾いました。個人情報が盛り沢山です。けしからんですね。

Otelコンフィグ変更

Attributes Processorを定義します。
processors.attributes/removepiiの定義およびPipelineに追加しています。
Emailはハッシュ化(hash)、パスワードは「obfuscated」に値変更(update)、クレジットカード番号は削除(delete)してみます。

/etc/otelcol-contrib/config.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:
  
  #追加
  attributes/removepii:
    actions:
      - key: email
        action: hash
      - key: password
        action: update
        value: "obfuscated"
      - key: credit_card
        action: delete

exporters:
  sapm:
    access_token: "<アクセストークン>"
    endpoint: "https://ingest.<REALM>.signalfx.com/v2/trace"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, attributes/removepii] #追加
      exporters: [sapm]

After

意図した結果になりました。やったね。

ところでログは・・・?

残念ながら現在のところ(2023年5月)、ログの内容を変更するためのProcessorはないように見えます。ロギングツールの方に任せましょう。
もしOtelに追加されたら更新します。

まとめ

Attributes Processorの一つの使い方としてSpan内に埋め込まれた個人情報を削除、マスキングするということをしてみました。コードに手をいれずともコントロールできちゃいますのでOpenTelemetry Collectorは強力ですね。

設定例はこちらにもあります。

https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/attributesprocessor/testdata/config.yaml

Discussion