在 VPS 上自托管 open-kritt:Docker Compose 配置指南
了解如何在 VPS 上运行 open-kritt:使用 Docker Compose 固定版本,通过 SSH 隧道访问 5173 端口 UI,并在首次扫描前设置服务商预算。
为什么应在 VPS 上自托管 open-kritt,而不是在笔记本电脑上
应在一台可以销毁并重建的服务器上自托管 open-kritt。该工具会在一次性任务容器中以 root 身份运行分析代理,为每个代理提供代码的可写副本和直接的互联网访问权限,并将宿主机的 Docker socket 挂载到其引擎服务中。对于专用于此任务的主机,这种取舍是合理的。对于存放 SSH 密钥的机器,这种做法风险很高。
默认配置有 4 个属性决定了这一建议,而且这 4 点都来自项目自己的 README 和 compose 文件。
这些代理本来就具备很高权限。 README 说明,启用工具的代理会在一次性任务容器中以 root 身份运行,并获得可写的代码仓库副本和直接的互联网访问权限。因此,它们可以安装工具、编译目标、运行测试并构建概念验证。扫描不是读取文件的 linter,而是你主动要求执行的任意代码。互联网访问具有双重风险:代理在研究目标时获取的任何内容,都会以不可信文本的形式进入其提示词。这与 让代理自行执行网页搜索 时面临的风险相同。
引擎持有 Docker socket。 docker-compose.yml 会将宿主机的 Docker socket 挂载到引擎服务中,因为引擎需要为每个任务构建并启动一个扫描容器。任何能够访问该 socket 的进程,都可以启动一个挂载宿主机文件系统的容器。因此,在运行该服务的主机上,引擎实际上拥有 root 权限。
没有登录界面。 后端未提供应用身份验证。能够访问该端口,就能够访问你的扫描结果和服务商账户额度。
你扫描的代码通常不属于你。 将代理指向第三方代码仓库,意味着在你的机器上以 root 身份并通过网络访问,执行该仓库的构建过程。
如果你读过为什么编码代理应运行在一次性 VM 中,就会知道这属于相同的威胁模型,只是风险更高。为 open-kritt 提供一台不运行其他服务的 VPS,并通过单独的最小权限用户账户管理这台 VPS,而不要使用 root。
open-kritt 的实际工作方式
open-kritt(代码仓库为 Kritt-ai/open-kritt,采用 AGPL-3.0 许可证)会将漏洞研究拆分为多个小任务,在多个 AI 代理上并行执行这些任务,然后对返回结果去重并排序。您可以将工作流定义为一系列专注的提示词,每个步骤都会接收前置步骤传递的结构化上下文。扫描目标可以是远程或本地 git 仓库。分析引擎可以使用 Codex 或 Claude Code。发现候选结果后,可选的后置脚本可以尝试验证该结果或构建概念验证。设计这类任务链属于常规的代理工作,而不是安全工作。因此,如果您还不熟悉提示词、工具和上下文传递,了解代理的构成方式比本指南中的任何设置都更有助于改进结果。
最终得到的是按优先级排序的候选结果列表。请将其视为分类队列,而不是报告。
开始前的准备工作
- 一台运行 Ubuntu 24.04、Debian 12 或 Rocky Linux 9 的 VPS。安装文档将这些系统列为已测试发行版,支持 x86_64 和 ARM64。
- Docker Engine,以及 Compose 插件。
- 主机上安装 Node.js 20 或更高版本,因为
./krittCLI 在主机上运行,而不是在容器内运行。 - 一个模型提供商:Codex 登录,或
OPENAI_API_KEY、CODEX_API_KEY、ANTHROPIC_API_KEY或OPENROUTER_API_KEY。 - 仅当您计划扫描私有仓库时才需要
GITHUB_TOKEN。随附的.env.example已明确说明:仅凭 GitHub token 无法运行扫描。
先安装 Docker 和 Node 20
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER退出登录后重新登录,使新的组成员身份生效,然后确认 Compose 插件已安装。
docker compose version如果输出版本字符串,说明 Compose 已作为插件安装。docker: 'compose' is not a docker command 表示系统使用的是旧版独立 docker-compose 二进制文件,open-kritt 会调用 docker compose。加入 docker 组等同于获得主机上的 root 权限,因此只能将运行 open-kritt 的账户加入该组。有关此配置的详细步骤,请参阅在 VPS 上运行 Docker。
Ubuntu 24.04 的自有软件仓库提供 Node 18,而 CLI 在 Node 版本低于 20 时会退出。请使用 NodeSource。
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -vnode -v 必须输出 v20. 或更高版本。在 Rocky Linux 9 上,对应的命令是先运行 sudo dnf module enable nodejs:20 -y,再运行 sudo dnf install -y nodejs。
克隆 open-kritt 并固定到标记版本
git clone https://github.com/Kritt-ai/open-kritt
cd open-kritt
git fetch --tags
git tag --list
git checkout v1.3.0main 会随提交移动,标记不会移动。截至 2026 年 8 月,最新标记为 v1.3.0,于 2026 年 8 月 4 日发布;git tag --list 会显示您克隆当天已有的内容。检出标记后,仓库会处于分离的 HEAD 状态。在这里这是正确的做法:您将此克隆作为固定版本部署,而不是作为要提交更改的分支。以后升级时,先阅读发行说明,然后运行 git fetch --tags,检出新的标记,再次运行 ./kritt start,因为 start 会重新构建镜像。
不要将 ./kritt 与 sudo 一起运行。文档对此有明确说明。该 CLI 会在 .data/ 下管理项目本地的凭据目录,因此以 root 身份运行会使这些目录归 root 所有,下一次以普通用户运行时将无法写入这些目录。
使用 ./kritt setup 配置模型访问
./kritt setup如果 .env 不存在,该命令会根据 .env.example 创建它,显示每个凭据的状态,并允许您设置或取消设置凭据。它绝不会将凭据值回显到终端。.env 和引擎凭据文件的权限都设置为 0600。
如果您希望手动完成:
cp .env.example .env
chmod 600 .env
mkdir -p .data/codex
chmod 700 .data/codex然后将提供商密钥写入 .env,并将文件权限保持为 0600。无论采用哪种方式,该服务器现在都保存了一个有效的提供商凭据,因此更应确保这台服务器不存放其他内容。请仅为此项目创建一个密钥。这样,日后撤销该密钥时,不会影响其他重要服务。避免让 AI 代理接触机密介绍了这一更广泛的安全习惯。
首次扫描前设置提供商支出上限
open-kritt 设计为并行分发任务,而并行分发会产生费用。v1.3.0 中 .env.example 的默认值较为保守:ENGINE_WORKER_COUNT=2,文件将其描述为适用于小型 2-vCPU 机器的保守默认值;以及 ENGINE_MAX_CONCURRENT_SCANS=1。更高一层的是 ENGINE_WORKERS_PER_ACCOUNT=15,即单个提供商账户允许同时运行的根模型调用数;还有 ENGINE_CODEX_MAX_SUBAGENTS_PER_SESSION=5,因为一个 Codex 会话最多可以运行五个子代理。在更大的 VPS 上提高工作进程数后,同时运行的模型调用数也会随之增加。
仓库本身不会限制您的支出。.env.example 中没有预算设置。引擎自身的停止条件只有这些 worker 限制,以及 ENGINE_HARNESS_TIMEOUT_SECONDS;后者默认为每次 harness 运行 7200 秒。这里的 harness 是一个循环:它持续携带工具和上下文调用模型,直到某个条件结束本次运行。因此,该超时限制的是包裹模型运行的程序的实际墙钟时间,而不是模型在其中产生的支出上限。所以,费用上限必须由提供商设置。在第一次扫描前打开提供商控制台并设置每月硬性限额,不要等扫描结束后再设置。控制 AI agent 在 VPS 上的费用介绍了各提供商的具体设置。
本地也有一个暂停机制。设置 ENGINE_WORKER_COUNT=0 后,系统会暂停接收新任务。堆栈运行后,还可以在 Settings 页面中修改相同的工作进程参数。
本指南不列出每次扫描的价格,因为费用取决于代码库大小、构建的工作流以及所使用的模型。先对一个小型代码库运行一次扫描,然后查看提供商的用量页面,再将其用于大型代码库。
启动堆栈并检查其健康状态
./kritt start该命令会检查 .env 和至少一个凭据,然后运行 docker compose up --build。首次构建较慢,因为它会构建前端、后端、引擎、执行器视图和数据库镜像。命令还会以前台方式运行,因此关闭 SSH 会话会停止堆栈。请在 tmux 中启动它,或者在首次构建成功后以 detached 模式启动。以上两种方式都不会自行跨重启保留。如果希望服务器重启后堆栈自动恢复运行,让自托管代理跨重启持续运行中的 systemd 单元模式可以直接套用。
docker compose up -d --build
docker compose psdocker compose ps 应列出 open-kritt-frontend、open-kritt-backend、open-kritt-engine、open-kritt-executor-view 和 open-kritt-db。然后检查后端是否能在服务器本机响应。
curl -s http://127.0.0.1:3002/api/health如果返回 JSON 响应,说明后端已启动。Failed to connect to 127.0.0.1 port 3002: Connection refused 表示后端未启动,docker compose logs backend 会说明原因。请在仓库目录中使用 docker compose down 停止所有组件。
还有一个可选步骤:docker compose exec backend npm run seed 会加载演示数据。这样可以在为真实扫描付费前,以较低成本查看界面。
通过 SSH 隧道访问 5173 端口上的 UI
compose 文件中的每个服务默认都绑定到 127.0.0.1:前端使用 5173,后端使用 3002,执行器视图使用 8090,Postgres 使用 5432。不要修改这些绑定,而应从自己的计算机通过 SSH 转发端口。
ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip在该命令运行期间,在本地浏览器中打开 http://localhost:5173。-N 表示连接会执行端口转发,且不会打开 shell。需要同时访问执行器视图时,请在同一条命令中再添加一个 -L 8090:127.0.0.1:8090。
不要为了跳过隧道而设置 FRONTEND_BIND_ADDRESS=0.0.0.0。后端没有登录界面,因此任何能访问该页面的人都可以启动扫描并消耗您的服务商额度。相比之下,Vaultwarden 的设计目标是直接面向互联网,而 加固它仍取决于管理员令牌和备份文件;open-kritt 没有提供这类控制手段。这里还有另一个容易忽略的问题:发布的容器端口会在 ufw 默认策略生效前被处理,因此 ufw deny 5173 规则看起来正确,却不会阻止任何流量。绕过 ufw 的 Docker 端口说明了导致这一结果的规则链。
VPS 规格
ENGINE_MIN_FREE_STORAGE_GB 默认值为 20。当可用存储空间低于该值时,引擎不会启动新的作业专用扫描容器。构建的镜像、检出缓存、Postgres 数据和作业工作区都位于同一块磁盘上,因此 20 GB 的 VPS 根本无法启动扫描。将 40 GB 视为最低配置;如果要扫描大型代码仓库,还应提供更多空间。不要为了压榨这台主机的剩余资源,而在旁边部署占用大量存储空间的服务。这篇 PhotoPrism 与 Immich 的对比中测得的内存和磁盘最低要求表明,媒体库会很快耗尽扫描所需的余量。同样,不要把那些看似相对扫描器几乎不占资源的附加组件部署在这里。一个将 Jellyfin 媒体库重新设计成 90 年代租赁店风格的浏览器前端仍会在后端占用完整的媒体服务器及其转码文件,因此应将其部署到另一台主机。
内存需求可以直接计算。ENGINE_MEMORY_RESERVE_GB=2会为引擎、数据库、API 和短时开销预留内存,每个扫描运行器还会申请并设置 ENGINE_SCAN_RUNNER_MEMORY_MB=1536 的硬上限。因此,两个工作器在其他组件运行前就需要约 5 GB 内存。引擎只会启动剩余预算能够容纳的运行器,所以小型主机上的扫描会排队,而不是失败。这比触发 out-of-memory killer 更安全。对于任何为每个工作单元创建独立容器的工具,同样的计算方式也适用。这就是为什么OpenBot 为每个 AI 协作者运行一个容器和一个浏览器时,会先遇到内存上限,而不是 CPU 上限。
两个清理设置默认值为 true:ENGINE_AUTO_PRUNE_DOCKER_BUILD_CACHE和 ENGINE_AUTO_PRUNE_UNUSED_DOCKER_IMAGES。任务完成后,引擎会删除未使用的构建缓存、未使用的镜像和已停止的扫描容器。正在运行的容器所引用的镜像、绑定挂载、数据库数据、凭据和卷都会保留。这也是不应共享主机的又一个原因:一个未经你配置的清理器正在操作该 Docker daemon。
多数人最终会修改的引擎设置
ENGINE_WORKER_COUNT:扫描步骤和后处理共享的工作器槽位总数。将其设置为 0 可暂停接收新任务。ENGINE_MAX_CONCURRENT_SCANS:同时允许运行的扫描数量。排队的扫描会等待活动池清空。ENGINE_MAX_WORKERS_PER_SCAN:设置为 0 时,会在扫描之间平均分配总槽位。ENGINE_HARNESS_TIMEOUT_SECONDS:默认值为 7200。这是单个失控任务最长可以运行的时间。ENGINE_MIN_FREE_STORAGE_GB:存储空间下限。ENGINE_IGNORE_LOW_STORAGE=true会禁用该保护机制,文件同时警告这可能会占满主机磁盘。ENGINE_SCAN_RUNNER_MEMORY_MB:每个运行器的内存硬上限。设置为 0 可移除该上限。
扫描本地仓库且不泄露仓库内容
LOCAL_REPOS_PATH默认为./local_repos,并通过 bind mount 挂载到 backend 和 engine 容器中的/local_repos。因此,放入主机该目录的仓库会立即出现在容器内。请使用全新克隆的副本,不要使用工作树。作业容器内部的副本可写,容器内以 root 身份运行,并且可访问外网。这意味着该副本中的任何内容都可能被修改或发送到主机外部。将项目复制进去前,请删除.env文件和私钥。
您将获得什么,以及不会获得什么
您会获得按优先级排序的候选发现项,但不会获得已验证的漏洞。排序和去重决定分诊队列中的顺序,但不能证明某个条目真实存在。后置脚本可以尝试验证并构建概念验证,这是该工具提供的最强信号。但后置脚本执行失败,并不能证明该发现项是误报。仍需由人员逐一检查每个候选项。候选项与证据之间存在差距,因此值得借鉴要求代理提供可由您自行重新运行的证据:能够按需复现的发现项,比必须凭信任接受的排序结果更有价值。
本指南不声称 open-kritt 能发现多少真实漏洞,因为我们尚未进行测量。任何声称已经针对您的代码库给出检测率的人,都没有在您的代码库上运行该工具。请先扫描一个您已经熟悉的代码库:您可以自行判断的发现项,是成本最低的校准方式。
在这里,授权比使用大多数自托管工具时更重要。代理会编译并执行代码,还会访问网络,因此概念验证步骤可能接触生产系统。请仅将其指向您拥有或受托测试的代码,并在运行任何操作前记录目标范围。如果您配置了 ANTHROPIC_API_KEY 并使用 Claude Code 引擎,那么在 VPS 上安全运行 Claude Code中的沙箱使用习惯同样适用于这些代理。
FAQ
为什么 open-kritt 需要使用专用 VPS?
因为它的分析代理会在一次性任务容器中以 root 身份运行。这些容器包含代码的可写副本,并可直接访问互联网。引擎服务还会挂载宿主机的 Docker socket,因此每个任务都可以启动一个容器。任何能够访问该 socket 的进程,都可以启动一个挂载宿主机文件系统的容器。因此,应将整套服务视为拥有宿主机 root 权限。在专用 VPS 上,这种取舍是可以接受的;即使重装整台服务器,也不会造成损失。如果在日常工作站上运行,您扫描的代码与 SSH 密钥、浏览器配置文件就处于同一信任边界内。
可以直接暴露 5173 端口,而不使用 SSH 隧道吗?
不应这样做。后端发布时未启用应用层身份验证,因此该端口是互联网与扫描结果、供应商额度之间唯一的隔离措施。出于这一原因,compose 文件会将每项服务绑定到 127.0.0.1。请运行 ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip,然后在本地访问 http://localhost:5173。ufw 规则不能替代这一措施,因为 Docker 发布的端口会在 ufw 默认策略生效前处理。
如何避免 open-kritt 的支出超过计划?
请在首次扫描前,于模型供应商的控制台中设置硬性限额,因为 open-kritt 没有自己的预算设置。前几次运行时保留发布版本的并发默认值,即 ENGINE_WORKER_COUNT=2 和 ENGINE_MAX_CONCURRENT_SCANS=1。还要注意,一个供应商账户默认最多允许 15 个并发 root 模型调用,而一个 Codex 会话最多可以运行 5 个子代理。ENGINE_WORKER_COUNT=0 会暂停领取新任务,是本地最快的停止方式。
应该检出哪个版本?
应检出 tag,不能使用 main。运行 git fetch --tags,再运行 git tag --list,即可查看可用版本。v1.3.0 于 4 August 2026 发布,是本文撰写时的最新版本。固定版本后,数月后重新构建仍会得到相同的服务栈。升级也会变成阅读发布说明后作出的明确决定,而不是因在不同日期执行 clone 产生的副作用。
扫描始终没有启动。应检查什么?
先检查可用磁盘空间,因为当可用存储低于 ENGINE_MIN_FREE_STORAGE_GB 时,引擎不会为任务启动扫描容器;该值默认为 20 GB。然后检查 ENGINE_WORKER_COUNT 是否为 0,因为该值会暂停领取新任务。接着运行 ./kritt setup,确认确实配置了模型凭据,因为仅有 GITHUB_TOKEN 无法运行扫描。docker compose logs engine 会说明跳过任务的原因。