ChatGPT Web UI 部署的安全隔离机制

群里有人反映自己部署的某ChatGPT Web UI后,Key被盗用导致额度大大超出预期。我查了下https://github.com/mckaywrigley/chatbot-ui/issues,发现确实有不少人反应这个问题。

虽然我也部署了多个ChatGPT Web UI和兼容OpenAI API的兼容URL接口。但我的系统是自研的,利用docker实现了三层架构。下图就是基本架构,下面介绍下这个系统。

UI层

这一层就是安装的各种Web UI。这种UI在GitHub里有很多,当初我还准备自己用solid.js开发一个,后来发现我缺乏艺术气息,设计出来的UI太糟糕了,在figma搜了下,也没找到满意的。只能在GitHub里找一个直接安装。不过GitHub里的Web UI分为两类。

(1)带后端的Web UI。这类的UI会自带一个node后端,UI请求的这个node后端,由后端去调取OpenAI API。这种是泄露Key的重灾区。

(2)没有后端的Web UI。这种就是直接在页面填写KEY,然后请求OpenAI API。当然,因为众所周知的原因,正常情况下是无法访问OpenAI,所以你还需要额外提供一套代理。

上面的这两种我都有安装,但都无法直连OpenAI,也无法获取我的key,而是从API中间层获取数据。

API 层

这一层包含了两块: (1)OpenAI API兼容接口。其实就是OpenAI的一个代理,除了域名不同,其他的与OpenAI的URL完全一致,可以直接使用OpenAI的SDK调用这个兼容URL。同时,可以屏蔽OpenAI的Key,避免泄露。

(2)鉴权、审核、改写。

鉴权:作为平台,肯定不能直接将OpenAI的Key交给用户,给的是一个虚拟的key,不同的用户或不同的来源,有不同的虚拟Key。鉴权模块需要对这些虚拟Key进行权限判断,并将虚拟Key改写成OpenAI的真实的Key。

审核:这一块不是必须的,主要是针对恶意提问,或一些无意义的单字提问进行过滤。同时对返回的内容进行内容审核。

改写:这一块也不是必须的,主要是针对行业的一些默认设置。而且以前ChatGPT没有Function功能,只能通过改写模块实现Function功能。

Proxy层

因为众所周知的原因,国内的服务器无法访问OpenAI的URL。为此要么购买海外服务器,要么用科学方式。

海外的服务器虽然可以直连OpenAI,但是如果提供给用户端访问,则不理想。而且海外服务器价钱也不便宜,仅仅把它当做代理有些奢侈。

科学方式就容易多了,我们只要在proxy层支持trojan,vmess,shadowsocks,socks5等协议,即可接入任何科学方式。也可以随时更换。

部署方式

整个系统是用Docker部署,docker compose管理。docker-compose.yml如下:

version: '3'
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.app
    container_name: chatbot-api
    entrypoint: /app/api
    restart: on-failure
    depends_on:
      - proxy
    ports:
      - "127.0.0.1:7718:7718"
    expose:
      - "7718"
    volumes:
      - /data/logs/chatBot/:/app/logs/
  chatgpt-ui:
    image: ghcr.io/mckaywrigley/chatbot-ui:main
    container_name: chatgpt-ui
    restart: on-failure
    depends_on:
      - api
    ports:
      - "127.0.0.1:6718:3000"
    expose:
      - "3000"
    environment:
      OPENAI_API_KEY: test111111111111111111
      OPENAI_API_HOST: "http://api:7718"
    volumes:
      - /data/logs/chatBot/:/app/logs/
  proxy:
    image: 私有docker仓库地址/tiyee/proxy:latest
    container_name: proxy
    entrypoint: /app/proxy run -c /etc/proxy/config.json
    restart: on-failure
    expose:
      - "1080"
      - "1087"
    volumes:
      - /data/logs/chatBot/:/app/logs/
      - /root/chatBot/config.json:/etc/proxy/config.json
  next-ui:
    image: yidadaa/chatgpt-next-web:latest
    container_name: next-ui
    restart: on-failure
    depends_on:
      - api
    ports:
      - "127.0.0.1:8718:3000"
    environment:
      OPENAI_API_KEY: sk-111111111111111111
      BASE_URL: "http://api:7718"
      CODE: maooo,jiangt
      HIDE_USER_API_KEY: 1
    volumes:
      - /data/logs/chatBot/:/app/logs/