【阅读原文】戳:使用阿里云服务网格 ASM LLMProxy 插件保障大模型用户数据安全
随着LLM(Large Language Model)的快速发展,各个行业逐渐看到了AI大规模落地的曙光。自MaaS(模型即服务)被提出以来,国内外厂商相继推出了自己的模型服务,进一步加速了大模型在实际场景中的落地进展。LLM正在成为各个企业所依赖的一项基础服务。
站在大模型使用方的角度,大模型引入的安全风险是一个无法回避的问题。比如API_KEY泄漏给调用方,可能会造成API滥用,使用成本增加;再比如企业敏感信息被无意间发送给大模型服务,由于大模型服务的控制权属于外部厂商,这些数据将变得不再安全。鉴于此,我们迫切的需要从平台层面提供全局的安全保护,以避免不必要的损失。
服务网格作为云原生环境下的网络基础设施,提供了丰富的可扩展能力。通过一些自定义的插件,用户可以在网格级别限制精准限制每一个应用(包括网关以及普通业务Pod)调用大模型的行为,防止敏感信息泄漏。
本文将演示如何利用Wasm插件在网格全局强制保护对于LLM的调用行为,主要演示能力如下:
• Sidecar或网关为LLM请求动态添加API_KEY,业务应用无需自行维护API_KEY。
• 在Sidecar或网关中配置自定义判别规则,禁止携带敏感信息的LLM请求离开Pod被发往外部LLM服务。
• 调用私有模型对LLM请求进行识别,更精准的判断请求是否携带敏感信息,以此决定是否放行请求。私有模型只用来判别请求是否包含敏感信息,在保证准确率的情况下,可以选择尽可能小的模型。
本文所涉及的插件代码已开源,用户可以自行下载使用或定制自己的LLM插件,您可以参考:
asm-labs/wasm-llm-proxy at main · AliyunContainerService/asm-labs
相关背景
ASM支持用户使用Wasm来扩展网格代理的功能,用户可以使用Go、Rust、C++等语言开发并编译成Wasm的二进制文件,之后打包成镜像上传至镜像仓库中,可以动态下发至网格代理(网关、Sidecar)中对请求进行操作。Wasm插件是完全热插拔的,不需要重新部署应用,也不会影响已有请求。并且Wasm插件运行在沙箱之中,具有良好的隔离性,不会影响代理本身。加上Wasm较低的开发门槛(相较于开发原生Envoy HTTP Filter),这里优先选用基于Go语言开发LLMProxy插件。
示例概述
本文面向的场景主要是用户通过ASM网关或者在业务Pod中访问第三方LLM服务的场景。主要解决的问题是:动态配置API_KEY,防止API_KEY泄漏;自定义判别规则,防止信息泄漏给第三方LLM;调用私有LLM,动态判定请求是否放行。得益于服务网格的统一架构,在ASM中并不需要特殊区分网关和普通业务Pod。所以本文将会以在一个普通业务Pod为例,在这个Pod中向外部LLM发起请求,来演示ASM LLMProxy插件的能力。
没有接入ASM之前如果用户要访问外部的HTTPS服务,需要直接发起HTTPS请求,并且在应用中维护与LLM服务的TCP长连接,如果维护不当,可能会导致频繁建连影响性能。
接入网格后,用户可以在应用中直接使用HTTP协议发起请求,网格代理可以将HTTP请求升级至HTTPS。由Envoy维护HTTPS连接,能够有效减少TLS握手次数从而提升性能。
本文将演示的最终效果是:业务容器使用HTTP协议发起请求,请求中无需携带LLM的API_KEY。之后这个请求进入Sidecar,Sidecar为这个请求添加API_KEY,然后进行敏感信息校验,根据校验结果允许或者拒绝请求通过,之后将HTTP协议升级为HTTPS协议发往外部LLM服务。
本文演示的LLM服务基于阿里云模型服务灵积DashScope[1] ,我们将使用标准的HTTP接口来调用DashScope,相关文档请参考:如何通过OpenAI接口调用通义千问模型_模型服务灵积(DashScope)-阿里云帮助中心[2] 。
演示
使用前提
• 已添加集群到ASM实例[3] ,且ASM实例版本为1.18及以上。
• 已启用Sidecar注入。具体操作,请参见配置Sidecar注入策略[4] 。
• 已经开通模型服务灵积,并且获取了可用的API_KEY。具体操作,请参见如何开通DashScope并创建API-KEY_模型服务灵积(DashScope)-阿里云帮助中心[5] 。
1. 部署客户端应用
本文使用的客户端应用是Sleep,在Sleep直接执行curl命令请求外部大模型,模拟用户应用或网关请求。
请使用ACK的kubeconfig创建,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 ---
2. 创建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,这里需要再配置一个对应的DestinationRule,对应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协议升级:
kubectl exec ${sleep pod name} -- 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": "你是谁"} ], "stream": false }'
如果没有Sidecar进行HTTPS升级,直接用HTTP访问dashscope会返回308重定向。Sidecar执行了HTTPS升级后,可以看到如下响应:
{"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"}
3. 配置LLMProxy插件
我们创建一个WasmPlugin资源,将LLM插件应用至Sleep Pod上。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: ${私有LLM服务的API_KEY} host: dashscope.aliyuncs.com model: qwen-turbo path: /compatible-mode/v1/chat/completions port: 80 # serviceentry中的HTTP端口
插件配置(pluginConfig)主要分为三部分,具体说明如下:
1.api_key:dashscope的API_KEY。此处配置后,应用发起HTTP请求时无需携带API_KEY,插件可以根据这里的配置动态添加,这样可以降低API_KEY泄漏的风险。如果API_KEY需要轮转,直接修改YAML中的配置即可,无需修改应用。
2.deny_patterns:正则表达式列表,用于匹配LLM请求中的用户message。匹配到的请求将会被拒绝。还支持配置allow_patterns,只有被匹配到的请求才会被放行。
3.hosts:host列表,只有发往这些host的请求才会被LLMProxy处理。避免其他普通请求被误处理。
4.intelligent_guard:使用OpenAI的标准接口调用私有LLM模型,用于判定该请求是否包含敏感信息,如果被私有大模型判定为包含敏感信息,请求将会被拒绝,并返回具体原因。本示例中为了演示方便,仍旧调用了模型服务灵积,下面主要是调用灵积的一些参数。
a.api_key:指定调用灵积时的API_KEY。
b.host:指定灵积服务的host。这里的host需要提前配置在ServiceEntry中,前面我们已经配置过了,直接使用即可。
c.model:指定要调用的大模型种类,比如qwen-turbo、qwen-max、baichuan2-7b-chat-v1等。这里可以根据需求进行定制,确保判别准确率的同时,尽量选择延迟低的大模型。
d.path:LLM请求的path。
e.port:私有LLM服务的端口,需要和ServiceEntry中的HTTP端口一致。
海外镜像地址请使用:
registry-cn-hongkong.ack.aliyuncs.com/test/asm-llm-proxy:v0.2
4.测试
1.测试不携带API_KEY的请求可以正常访问LLM服务:
kubectl exec ${sleep pod name} -- 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"}
2.测试携带敏感词“账号”的请求会被拒绝。
kubectl exec ${sleep pod name} -- 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
3.测试携带了敏感信息,但是不在deny_patterns中。观察intelligent_guard的能力:
kubectl exec ${sleep pod name} -- curl '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服务时更好的保障企业的数据安全,主要有两方面:
1.如何保障调用大模型时的API_KEY的安全?
2.如何确保调用大模型时,不会发生数据泄漏?
通过ASM的LLMProxy插件,用户可以更加优雅的实现API_KEY的轮转,并且能够精准、智能的限制敏感信息的外流。这一切都得益于ASM通过Wasm提供的可扩展能力。目前我们已经将这一插件的代码开源(asm-labs/wasm-llm-proxy at main · AliyunContainerService/asm-labs),欢迎大家体验。如果您有其他的通用需求,可以向我们提出issue,我们会持续更新。
相关链接:
[1] 模型服务灵积DashScope
[2] 如何通过OpenAI接口调用通义千问模型_模型服务灵积(DashScope)-阿里云帮助中心
[3] 已添加集群到ASM实例
https://help.aliyun.com/zh/asm/getting-started/add-a-cluster-to-an-asm-instance-1
[4] 配置Sidecar注入策略
https://help.aliyun.com/zh/asm/user-guide/configuring-a-sidecar-injection-policy
[5] 如何开通DashScope并创建API-KEY_模型服务灵积(DashScope)-阿里云帮助中心
我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。
获取关于我们的更多信息~