大規模モデルでユーザーデータのセキュリティを確保するために、アリババクラウドASM LLMProxyプラグインを使用する

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/use-alibaba-cloud-asm-llmproxy-plug-in-to-ensure-user-data-security-for-large-models_601805?spm=a2c65.11461433.0.0.2d0e5355vuRMKs)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

## 大規模言語モデル(LLM)の呼び出しにおけるグローバルセキュリティ保護 - Alibaba Cloud Service Mesh(Wasmプラグインを使用)

著者: Ma Yuanyuan

大規模言語モデル(LLM)の急速な発展により、様々な業界で大規模なAI実装が見込まれています。モデル・アズ・ア・サービス(MaaS)が提唱されて以降、国内外のメーカーは自身のモデルサービスを立ち上げ、大規模モデルの実用化をさらに加速させています。LLMは企業が依存する基盤的なサービスとなることが期待されています。大規模モデル導入によるセキュリティリスクは、ユーザーにとって避けられない課題となります。例えば、API_KEYが呼び出し元に漏れると、APIの不正利用や使用コスト増加につながる可能性があります。また、企業の機密情報が不注意で大規模モデルサービスに送られ、そのサービスの制御が外部ベンダーに属するため、データの安全性が確保できなくなることがあります。これらの事例から、プラットフォームレベルでのグローバルセキュリティ保護を提供することが急務となっています。 ![1](https://yqintl.alicdn.com/b17c92db81c32b63693748d29271391241cf77f3.jpeg)

クラウドネイティブ環境のネットワーク基盤として、[Alibaba Cloud Service Mesh](https://www.alibabacloud.com/product/servicemesh?spm=a2c63.p38356.6791778070.128.67cf4f47a2x8g0) (ASM)は優れた拡張性を提供しています。カスタムプラグインを使用することで、ユーザーはメッシュレベルで各アプリケーション（ゲートウェイや通常のビジネスPodを含む）が大規模モデルを呼び出すことを精密に制限し、機密情報漏洩を防止できます。本記事では、Wasmプラグインを活用して、メッシュ内でのLLM呼び出しのグローバル保護を実現する方法を紹介します。主な機能は以下の通りです：

- Sidecarまたはゲートウェイが、LLMリクエストに対してAPI_KEYを動的に追加する。アプリケーション側でAPI_KEYを管理する必要はありません。
- Sidecarやゲートウェイでカスタム識別規則を設定し、機密情報を含むLLMリクエストがPod外に出ることを防ぐ。
- 専用モデルを呼び出して、LLMリクエストを識別し、より正確にリクエストが機密情報を含んでいるかどうかを判断し、許可するかどうかを決定する。専用モデルは、リクエストに機密情報が含まれているかどうかを判定するためにのみ使用され、精度を確保するために可能な限り小さなモデルを選択することが望ましい。

本記事に関連するプラグインコードはオープンソース化されており、ユーザーはダウンロードして使用したり、独自のLLMプラグインをカスタマイズすることも可能です。詳細は[asm-labs/wasm-llm-proxy at main · AliyunContainerService/asm-labs](https://github.com/AliyunContainerService/asm-labs/tree/main/wasm-llm-proxy)をご覧ください。

### 背景

[ASM](https://www.alibabacloud.com/product/servicemesh?spm=a2c63.p38356.6791778070.128.67cf4f47a2x8g0)は、Wasmを使用してメッシュプロキシの機能を拡張することをサポートしています。ユーザーはGo、Rust、C++などの言語でWasmバイナリファイルを開発・コンパイルし、それをイメージとしてパッケージングしてイメージリポジトリにアップロードできます。これらのイメージはメッシュプロキシ（ゲートウェイ、Sidecars）に動的に配信され、リクエストに作用するようになります。Wasmプラグインは、既存のリクエストに影響を与えることなくホットスワップ可能であり、アプリケーションの再デプロイも不要です。また、Wasmプラグインはサンドボックス内で実行されるため、プロキシ自体に影響を与えない良質な分離性を提供します。Wasmの開発門檻がEnvoy HTTP Filtersのネイティブ開発よりも低いことから、当社はGo言語に基づくLLMProxyプラグインの開発を最優先しました。

### 概要

本記事で紹介するシナリオでは、ユーザーはASMゲートウェイ経由やビジネスPod内でサードパーティのLLMサービスにアクセスします。解決すべき主要な問題は、API_KEYの動的構成と漏洩防止、機密情報漏洩を防ぐカスタム識別規則の設定、およびリクエストが機密情報を含むかどうかを動的に判断して許可するかどうかを決めるために専用モデルの呼び出しだ。サービスメッシュの一元化アーキテクチャのおかげで、ASMはゲートウェイと通常のビジネスPodを区別する必要はありません。そのため、本記事では、外部LLMへのリクエストを開始する通常のビジネスPodの例を使って、ASMのLLMProxyプラグインの能力をデモンストレーションします。

ASMに接続する前に、ユーザーが外部HTTPSサービスにアクセスするには、直接HTTPSリクエストを発行し、アプリケーション内でLLMサービスとの長期間TCP接続を維持する必要があります。これらの接続を適切に維持しないと、接続確立が頻発し、パフォーマンスに影響を与える可能性があります。メッシュに接続された後、ユーザーはアプリケーション内で直接HTTPプロトコルを使用してリクエストを発行できるようになります。メッシュプロキシはHTTPリクエストをHTTPSにアップグレードし、EnvoyがHTTPS接続を維持することでTLSハンドシェイク回数を減らし、パフォーマンスを向上させます。 ![2](https://yqintl.alicdn.com/43a7c301a6997332a718b9987dde22281002ea7a.jpeg)

本記事でデモンストレーションする最終結果として、サービスコンテナがHTTPプロトコルを使用して
アプリケーションのYAMLファイルは次の通りです：
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sleep
---
apiVersion: v1
kind: Service
metadata:
  name: sleep
  labels:
    app: sleep
    service: sleep
spec:
  ports:
  - port: 80
    name: http
  selector:
    app: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      terminationGracePeriodSeconds: 0
      serviceAccountName: sleep
      containers:
      - name: sleep
        image: registry.cn-hangzhou.aliyuncs.com/acs/curl:8.1.2
        command: [/bin/sleep, infinity]
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - mountPath: /etc/sleep/tls
          name: secret-volume
      volumes:
      - name: secret-volume
        secret:
          secretName: sleep-secret
          optional: true
```
2. ServiceEntryとDestinationRuleを作成する
LLMサービスがメッシュ外にあるため、外部サービスをメッシュで管理するには、ユーザーがServiceEntryを作成してサービスをメッシュに登録する必要があります。ここでは、ServiceEntryを使用してDashScopeをASMに登録します。対応するYAMLファイルは次の通りです：
```yaml
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: dashscope
  namespace: default
spec:
  hosts:
    - dashscope.aliyuncs.com
  ports:
    - name: http-port
      number: 80
      protocol: HTTP
      targetPort: 443  # HTTPをHTTPSにアップグレードするために使用される
    - name: https-port
      number: 443
      protocol: HTTPS
  resolution: DNS
```
ポート80経由でDashScopeサービスにアクセスするためにSidecarがHTTPプロトコルをHTTPSにアップグレードできるようにするには、追加のDestinationRuleを構成する必要があります。対応するYAMLファイルは次の通りです：
```yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: dashscope
  namespace: default
spec:
  host: dashscope.aliyuncs.com
  trafficPolicy:
    portLevelSettings:
    - port:
        number: 80
      tls:
        mode: SIMPLE
```
構成が完了したら、次のコマンドを使用してテストし、SidecarがHTTPプロトコルをHTTPSにアップグレードできることを確認できます：
```sh
kubectl exec ${sleepポッド名} -- curl -v http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
--header Authorization: Bearer ${dashscope API_KEY} \
--header Content-Type: application/json \
--header user: test \
--data '{model: qwen-turbo,messages: [{role: user, content: Who are you}],stream: false}'
```
SidecarによるHTTPSアップグレードがない場合、HTTP経由で直接DashScopeにアクセスすると308リダイレクトが返されます。SidecarがHTTPSアップグレードを行った後、次のようなレスポンスが見られます：
```json
{choices:[{message:{role:assistant,content:I am a large-scale language model from Alibaba Cloud. My name is Tongyi Qianwen. },finish_reason:stop,index:0,logprobs:null}],object:chat.completion,usage:{prompt_tokens:10,completion_tokens:16,total_tokens:26},created:xxxxxxxx,system_fingerprint:null,model:qwen-turbo,id:xxxxxxxxxxxxxxxxxx}
```
3. LLMProxyプラグインの構成
Sleep PodにLLMプラグインを適用するため、WasmPluginリソースを作成します。WasmPluginのYAMLリソースは次の通りです：
```yaml
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  name: asm-llm-proxy
  namespace: default
spec:
  imagePullPolicy: Always
  phase: AUTHN
  selector:
    matchLabels:
      app: sleep
  url: registry-cn-hangzhou.ack.aliyuncs.com/test/asm-llm-proxy:v0.2
  pluginConfig:
    api_key: ${dashscopeのAPI_KEY}
    deny_patterns:
    - .*Account.*     # 「Account」を含むメッセージを外部モデルに送信することを許可しない
    hosts:
    - dashscope.aliyuncs.com    # ホストがdashscope.aliyuncs.comである要求のみプラグインが適用される
intelligent_guard:   # 要求の機密情報を検証するプライベートLLMサービスを構成する。 # 検証の容易さのために、この記事では依然としてDashScopeサービスを呼び出して要求を検証しています。
    api_key: ${プライベートLLMサービスのAPI_KEY}
    host: dashscope.aliyuncs.com
    model: qwen-turbo
    path: /compatible-mode/v1/chat/completions
    port: 80  # serviceentryのHTTPポート
```
プラグインの構成（pluginConfigは主に3つの部分に分けられ、以下のように説明されます：
1. api_key: DashscopeのAPI_KEY。この構成により、アプリケーションがHTTPリクエストを開始する際にAPI_KEYを含める必要がなくなります。プラグインはこの構成に基づいて動的に追加することで、API_KEYの漏洩リスクを低減します。API_KEYのローテーションが必要な場合は、YAMLファイル内の構成を直接変更するだけで、アプリケーションを変更する必要はありません。
2. deny_patterns: LLMリクエスト中のユーザーのメッセージと一致する正規表現のリスト。一致したリクエストは拒否されます。また、allow_patternsの構成もサポートされており、一致したリクエストのみが通過することになります。
3. hosts: ホストのリスト。これらのホスト宛ての要求のみがLLMProxyによって処理され、他の一般的な要求が誤って処理されることを防ぎます。
4. intelligent_guard: OpenAIの標準的なインターフェイスを使用してプライベートLLMモデルを呼び出し、要求に機密情報が含まれているかどうかを判断します。プライベートの大規模モデルが要求に機密情報を含
d.path: LLMリクエストのパスです。  
e.port: プライベートLLMサービスのポートであり、ServiceEntry内のHTTPポートと同一である必要があります。海外のイメージアドレスを使用してください: registry-cn-hongkong.ack.aliyuncs.com/test/asm-llm-proxy:v0.2

## 4. テスト

### 4.1. API_KEYなしのテストリクエストがLLMサービスへのアクセスを成功させる
```shell
kubectl exec ${スリープポッド名} -- curl http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
--header 'Content-Type: application/json' \
--data-raw '{
    "model": "qwen-turbo",
    "messages": [
        {"role": "user", "content": "Who are you"}
    ],
    "stream": false
}'
```
出力は次のようになります:
```json
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "私はアリババクラウドの大規模な言語モデルです。私の名前は統一千問です。"
      },
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null
    }
  ],
  "object": "chat.completion",
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 16,
    "total_tokens": 26
  },
  "created": "xxxxxxx",
  "system_fingerprint": null,
  "model": "qwen-turbo",
  "id": "xxxxxxxxx"
}
```

### 4.2. センシティブワード「アカウント」を含むテストリクエストは拒否される
```shell
kubectl exec ${スリープポッド名} -- curl http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
--header 'Content-Type: application/json' \
--data-raw '{
    "model": "qwen-turbo",
    "messages": [
        {"role": "user", "content": "私は小豆餡の粽が好きです。私のQQアカウント番号は1111111です。"}
    ],
    "stream": false
}'
```
サンプル出力:
```
request was denied by asm llm proxy
```

### 4.3. センシティブ情報が含まれているがdeny_patterns外のテスト
```shell
kubectl exec ${スリープポッド名} -- curl http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
--header 'Content-Type: application/json' \
--data-raw '{
    "model": "qwen-turbo",
    "messages": [
        {"role": "user", "content": "当社は9月10日に内部の高階層会議を開催します。会議のテーマは顧客をよりよくサービスする方法です。オープニングステートメントを私に与えてください。"}
    ],
    "stream": false
}'
```
この記事のデモンストレーション結果は以下の通りです:
![3](https://yqintl.alicdn.com/689f88f2c6e2ee20a461f7b6e3c660aa48db3083.jpeg)

ご覧のように、LLMモデルは現在のリクエストがおそらくセンシティブ情報を含んでいることを正しく識別しています。LLMProxyプラグインはリクエストを拒否し、外部のLLMサービスに送信することを続けます。本番環境では、リクエストがセンシティブ情報を含んでいるかどうかを判定するモデルはプライベートにデプロイする必要があります。これにより、センシティブ情報の漏洩を防ぐことができます。

## 総括
この記事は、ユーザーが外部のLLMサービスを使用する際に企業のデータセキュリティをどのように効果的に確保するかに焦点を当てています。二つの主要な側面があります:

1. 大型モデル呼び出し時のAPI_KEYのセキュリティをどのように確保するか？
2. 大型モデル呼び出し時にデータ漏洩をどのように防ぐか？

ASMのLLMProxyプラグインを通じて、ユーザーはAPI_KEYのローテーションをスムーズに実装し、センシティブ情報の漏洩を精密かつ知能的に制限できます。これは、ASMがWasmを通じて提供する拡張性の機能のおかげで可能となっています。現在、このプラグインのコードはオープンソース化されています([asm-labs/wasm-llm-proxy at main · AliyunContainerService/asm-labs](https://github.com/AliyunContainerService/asm-labs/tree/main/wasm-llm-proxy))、皆様のご体験をお待ちしております。その他の汎用的な要望があれば、問題を立てていただければ、継続的に更新していきます。