OpenTelemetry CollectorでSpan内のPIIを削除
はじめに
OpenTelemetryでコードに計装したところうっかりSpanのメタデータに個人情報を含めてしまった!・・・あまり無いと思いますが、そんなときに何ができるかです。
以下の三つが考えられます。
-
コードを修正する
すぐに対応できるならベストです。 -
OpenTelemetry CollectorのAttributes Processorを使う
今日のお話です。一旦これで対応してしまって後でゆっくりコード修正します。 -
バックエンド(APM)側で対応する
バックエンド側で個人情報を削除する機能があればそれを使うのもよいでしょう。一旦これで対応してしまって後でゆっくりコード修正します。
Attributes Processorとは?
SpanのAttributeを操作できるProcessorです。以下の操作が可能です。
insert:入力データのうち、キーがまだ存在しないものに新しい属性を挿入する。
update:入力データのうち、キーが存在する属性を更新する。
upsert:insertまたはupdateを実行する。キーがまだ存在しない入力データに新しい属性を挿入し、キーが存在する入力データ内の属性を更新する。
delete:入力データから属性を削除する。
hash:属性値をハッシュ化(SHA1)する。
extract: 正規表現規則を用いて、入力キーから規則で指定されたターゲットキーに値を抽出する。ターゲットキーがすでに存在する場合は、上書きされる。注:既存の属性をソースとするSpan Processorのto_attributes設定と同様の動作をする。
convert: 既存の属性を指定された型に変換する。
insert
またはupsert
を使いアプリのバージョンや担当チームなどを一律で設定する使い方をよくしますね。
今回は値を削除またはマスキングしたいのでupdate
、delete
、hash
を試してみます。
テスト環境
今回はPythonに対して計装したいと思います。
Webページから個人情報を入力し、その値をSpanのAttributeとしてセットします。
テストのためのフロントエンドとバックエンドを作ってみました(ChatGPTが)。
フロントエンド
個人情報を入力しSubmitするだけの画面です。
<!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)します。
詳細はこちらをご参照ください。
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でもなんでもいいです。
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
)してみます。
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は強力ですね。
設定例はこちらにもあります。
Discussion