服務網格ASM作為云原生環境下的網絡基礎設施,提供了豐富的可擴展能力。通過一些自定義的插件,您可以在網格級別精準限制每一個應用(包括網關以及普通業務Pod)調用大模型的行為,防止敏感信息泄漏。本文將演示如何利用Wasm插件在網格全局強制保護對LLM的調用行為。
背景信息
隨著LLM(Large Language Model)的快速發展,各個行業逐漸看到了AI大規模落地的曙光。自MaaS(模型即服務)被提出以來,國內外廠商相繼推出了自己的模型服務,進一步加速了大模型在實際場景中的落地進展。LLM正在成為各個企業所依賴的一項基礎服務。
站在大模型使用方的角度,大模型引入的安全風險是一個無法回避的問題。例如API_KEY泄漏給調用方,可能會造成API濫用,使用成本增加;再比如企業敏感信息被無意間發送給大模型服務,由于大模型服務的控制權屬于外部廠商,這些數據將變得不再安全。鑒于此,我們迫切需要從平臺層面提供全局的安全保護,以避免不必要的損失。
ASM支持使用Wasm來擴展網格代理的功能,您可以使用Go、Rust、C++等語言開發并編譯成Wasm的二進制文件,之后打包成鏡像上傳至鏡像倉庫中,可以動態下發至網格代理(網關、Sidecar)中對請求進行操作。Wasm插件是完全熱插拔的,不需要重新部署應用,也不會影響已有請求。并且Wasm插件運行在沙箱之中,具有良好的隔離性,不會影響代理本身。加上Wasm較低的開發門檻(相較于開發原生Envoy HTTP Filter),ASM優先選用基于Go語言開發LLMProxy插件。
本文所涉及的插件代碼已開源,您可以自行下載使用或定制自己的LLM插件,具體信息,請參見asm-labs/wasm-llm-proxy at main · AliyunContainerService/asm-labs。
前提條件
添加集群到ASM實例,且ASM實例版本為1.18及以上。
已啟用Sidecar注入。具體操作,請參見配置Sidecar注入策略。
已開通百煉,并且獲取了可用的API_KEY。具體操作,請參見獲取API Key。
示例概述
本文主要演示能力如下:
Sidecar或網關為LLM請求動態添加API_KEY,業務應用無需自行維護API_KEY。動態配置API_KEY,防止API_KEY泄漏。
在Sidecar或網關中配置自定義判別規則,禁止攜帶敏感信息的LLM請求離開Pod被發往外部LLM服務。
調用私有模型對LLM請求進行識別,更精準地判斷請求是否攜帶敏感信息,以此決定是否放行請求。私有模型只用來判別請求是否包含敏感信息,在保證準確率的情況下,可以選擇盡可能小的模型。
沒有接入ASM之前如果需要訪問外部的HTTPS服務,需要直接發起HTTPS請求,并且在應用中維護與LLM服務的TCP長連接,如果維護不當,可能會導致頻繁建連影響性能。
接入網格后,您可以在應用中直接使用HTTP協議發起請求,網格代理可以將HTTP請求升級至HTTPS。由Envoy維護HTTPS連接,能夠有效減少TLS握手次數從而提升性能。
演示的最終效果是:業務容器使用HTTP協議發起請求,請求中無需攜帶LLM的API_KEY。之后這個請求進入Sidecar,Sidecar為這個請求添加API_KEY,然后進行敏感信息校驗,根據校驗結果允許或者拒絕請求通過,之后將HTTP協議升級為HTTPS協議發往外部LLM服務。
演示的LLM服務基于阿里云百煉大模型服務平臺,我們將使用標準的HTTP接口來調用大模型,相關文檔請參見文本生成。
示例演示
步驟一:部署客戶端應用
通過kubectl連接到ASM實例添加的Kubernetes集群,使用以下內容創建sleep.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
---
執行以下命令部署Sleep應用。
kubectl apply -f sleep.yaml
步驟二:創建ServiceEntry以及DestinationRule
由于LLM服務在網格外部,想要外部服務可以被網格管理,您需要手動創建一個集群外服務(ServiceEntry)將服務注冊到網格中。具體操作,請參見創建集群外服務。
在這里,我們使用ServiceEntry將百煉服務注冊至ASM中。對應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 # 和Destination配合使用,用于將HTTP協議升級為HTTPS
- name: https-port
number: 443
protocol: HTTPS
resolution: DNS
為了讓Sidecar能夠將訪問百煉服務80端口的HTTP協議升級為HTTPS,這里需要再配置一個對應的目標規則,對應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的協議升級。
export API_KEY=${dashscope的API_KEY} # 請替換為實際的API KEY
kubectl exec deploy/sleep -- curl -v 'http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions' \
--header "Authorization: Bearer ${API_KEY}" \
--header 'Content-Type: application/json' \
--header 'user: test' \
--data '{
"model": "qwen-turbo",
"messages": [
{"role": "user", "content": "你是誰"}
],
"stream": false
}'
預期輸出:
{"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":xxxxxxxx,"system_fingerprint":null,"model":"qwen-turbo","id":"xxxxxxxxxxxxxxxxxx"}
步驟三:配置LLMProxy插件
使用以下內容創建WasmPlugin.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:
- .*賬號.* # 禁止包含“賬號”兩個字的message被發往外部大模型
hosts:
- dashscope.aliyuncs.com # 該插件只對host為dashscope.aliyuncs.com的請求生效
intelligent_guard: # 配置一個私有LLM服務,對請求進行敏感信息驗證。
# 本文為了方便驗證,依然調用百煉服務來對請求進行驗證。
api_key: ${dashscope的API_KEY}
host: dashscope.aliyuncs.com
model: qwen-turbo
path: /compatible-mode/v1/chat/completions
port: 80 # serviceentry中的HTTP端口
海外鏡像地址請使用:registry-cn-hongkong.ack.aliyuncs.com/test/asm-llm-proxy:v0.2。
pluginConfig
配置說明。
參數 | 子參數 | 說明 |
api_key | \ | dashscope的API_KEY。配置后,應用發起HTTP請求時無需攜帶API_KEY,可以根據這里的配置動態添加,降低API_KEY泄漏的風險。如果API_KEY需要輪轉,直接修改YAML中的配置即可,無需修改應用。 |
deny_patterns | \ | 正則表達式列表,用于匹配LLM請求中的message。匹配到的請求將會被拒絕。還支持配置 |
hosts | \ | host列表,只有發往這些host的請求才會被LLMProxy處理。避免其他普通請求被誤處理。 |
intelligent_guard | api_key | dashscope的API_KEY。 |
host | 百煉服務的host。 | |
model | 要調用的大模型種類,比如qwen-turbo、qwen-max、baichuan2-7b-chat-v1等。這里可以根據需求進行定制,確保判別準確率的同時,盡量選擇延遲低的大模型。 | |
path | LLM請求的path。 | |
port | 私有LLM服務的端口,需要和ServiceEntry中的HTTP端口一致。 |
intelligent_guard
是OpenAI的標準接口,用于判定發往LLM模型的請求是否包含敏感信息。如果被私有大模型判定為包含敏感信息,請求將會被拒絕,并返回具體原因。本示例中為了演示方便,仍舊調用了百煉服務。
執行以下命令,創建WasmPlugin。
kubectl apply -f WasmPlugin.yaml
示例測試
執行以下命令,測試不攜帶API_KEY的請求可以正常訪問LLM服務。
kubectl exec deploy/sleep -- curl 'http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions' \ --header 'Content-Type: application/json' \ --data '{ "model": "qwen-turbo", "messages": [ {"role": "user", "content": "你是誰"} ], "stream": false }'
預期輸出:
{"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"}
執行以下命令,測試攜帶敏感詞“賬號”的請求會被拒絕。
kubectl exec deploy/sleep -- curl 'http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions' \ --header 'Content-Type: application/json' \ --data '{ "model": "qwen-turbo", "messages": [ {"role": "user", "content": "我喜歡吃豆沙粽子,我的QQ賬號是1111111"} ], "stream": false }'
預期輸出:
request was denied by asm llm proxy
測試攜帶了敏感信息,但是不在deny_patterns中。觀察
intelligent_guard
能發識別敏感信息并攔截請求。kubectl exec deploy/sleep -- curl -s 'http://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions' \ --header 'Content-Type: application/json' \ --data '{ "model": "qwen-turbo", "messages": [ {"role": "user", "content": "我們公司將會在9月10日舉行內部高級別會議,會議主題是如何更好的服務客戶,請給我一份會議開場白。"} ], "stream": false }'
輸出如下:
可以看到,LLM模型成功識別到當前請求中可能存在敏感信息,LLMProxy插件會拒絕該請求繼續發往外部LLM服務。在生產環境中,判定請求是否有敏感信息的模型需要私有化部署,這樣可以確保敏感信息不泄漏。
總結
本文圍繞的主要問題是:如何在使用外部LLM服務時更好地保障企業的數據安全,主要有兩方面:
如何保障調用大模型時的API_KEY的安全?
如何確保調用大模型時,不會發生數據泄漏?
通過ASM的LLMProxy插件,您可以更加優雅地實現API_KEY的輪轉,并且能夠精準、智能地限制敏感信息的外流。這一切都得益于ASM通過Wasm提供的可擴展能力。目前我們已經將這一插件的代碼開源(asm-labs/wasm-llm-proxy at main · AliyunContainerService/asm-labs),歡迎大家體驗。如果您有其他的通用需求,可以向我們提出issue,我們會持續更新。