open-kritt 自托管部署与安全配置指南
了解如何在 VPS 上用 Docker Compose 部署 open-kritt、固定版本,并通过 SSH 隧道访问 5173 端口 UI;首次扫描前还需设置 AI 服务商预算。
为什么要在 VPS 上自托管 open-kritt,而不是在笔记本电脑上
在可以销毁并重建的服务器上自托管 open-kritt。该工具会在一次性任务容器中以 root 身份运行分析代理,为每个代理提供代码的可写副本和直接的 Internet 访问权限,并将主机的 Docker socket 挂载到引擎服务中。对于专门用于此任务的主机,这是合理的取舍。但对于存放 SSH 密钥的机器,这种做法风险很高。
默认配置中的4个特性决定了这一建议。这4点都来自项目自己的 README 和 compose 文件。
这些代理的权限本来就很高。 README 说明,启用工具的代理会在一次性任务容器中以 root 身份运行,并拥有可写的代码仓库副本和直接的 Internet 访问权限。因此,它们可以安装工具、编译目标、运行测试和构建概念验证。扫描不是读取文件的代码检查器,而是您主动要求执行的任意代码。Internet 访问也会带来双向风险:代理在研究目标时获取的任何内容,都是进入其提示词的不可信文本;这与您 让代理自行进行 Web 搜索 时承担的风险相同。
引擎持有 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 agent 中并行执行这些任务,然后对返回结果去重并排序。您可以将工作流定义为一系列专注的提示词,每个步骤都会接收前序步骤提供的结构化上下文。扫描目标可以是远程或本地 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 在版本低于 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,发布于 4 August 2026;git tag --list 可显示克隆当天已有的内容。检出标签后,仓库会处于 detached HEAD 状态。这在此处是正确的,因为您将此克隆作为固定版本部署,而不是作为要提交的分支。以后升级时,先阅读发行说明,然后运行 git fetch --tags,检出新标签,再次运行 ./kritt start,因为 start 会重新构建镜像。
不要将 ./kritt 与 sudo 一起运行。文档对此有明确说明。CLI 会管理 .data/ 下的项目本地凭据目录,因此以 root 身份运行会使这些目录归 root 所有,后续以普通用户运行时将无法写入这些目录。
使用 ./kritt setup 配置模型访问
./kritt setup如果 .env.example 不存在,该命令会根据 .env.example 创建 .env,显示每个凭据的状态,并允许您设置或取消设置这些凭据。它不会将凭据值打印到终端。.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 上提高 worker 数量后,同时进行的模型调用数也会随之增加。
代码仓库不会限制您的支出。.env.example 中没有预算设置。引擎自身的停止条件只有这些 worker 限制和 ENGINE_HARNESS_TIMEOUT_SECONDS;后者默认为每次 harness 运行 7200 秒。因此,上限必须在提供商侧设置。首次扫描前打开提供商控制台并设置每月硬性限额,不要等到扫描完成后再设置。控制 VPS 上 AI 代理的费用介绍了各提供商的相关设置。
本地也有一个停止机制。设置 ENGINE_WORKER_COUNT=0 后,系统会暂停获取新任务;堆栈运行后,也可以在 Settings 页面中修改相同的 worker 值。
本指南不列出每次扫描的价格,因为费用取决于仓库大小、您构建的工作流以及所使用的模型。先对一个小型仓库运行一次扫描,然后查看提供商的用量页面,再将其用于大型仓库。
启动堆栈并检查其健康状态
./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,从而跳过隧道。不要这样做。后端没有登录页面,因此任何访问该页面的人都可以启动扫描并消耗您的服务商额度。这里还有另一个容易忽略的问题:已发布的容器端口会在 ufw 的默认策略应用前被处理,因此 ufw deny 5173 规则看似正确,实际上不会阻止任何连接。绕过 ufw 的 Docker 端口介绍了导致这一结果的规则链。
VPS 规格
ENGINE_MIN_FREE_STORAGE_GB默认为 20。当可用存储空间低于该值时,引擎会拒绝启动新的按任务创建的扫描容器。构建镜像、检出缓存、Postgres 数据和任务工作区都位于同一磁盘上,因此 20 GB 的 VPS 根本无法启动扫描。将 40 GB 视为最低配置;如果需要扫描大型代码仓库,请提供更多存储空间。
内存需求可以直接计算。ENGINE_MEMORY_RESERVE_GB=2会为引擎、数据库、API 和短时开销预留内存,每个扫描运行器还会设置为 ENGINE_SCAN_RUNNER_MEMORY_MB=1536 的预留值和硬上限。因此,2 个工作进程在其他服务运行前就需要约 5 GB 内存。引擎只会启动剩余内存预算能够容纳的运行器,因此在小型主机上扫描会排队,而不是失败。这比触发 out-of-memory killer 更容易处理。
有两个清理设置默认为 true:ENGINE_AUTO_PRUNE_DOCKER_BUILD_CACHE 和 ENGINE_AUTO_PRUNE_UNUSED_DOCKER_IMAGES。任务完成后,引擎会删除未使用的构建缓存、未使用的镜像和已停止的扫描容器。正在运行的容器所引用的镜像、bind mount、数据库数据、凭据和卷都会保留。不要与其他服务共享主机,这也是一个原因:一个并非由您配置的清理器正在操作该 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,并以绑定挂载方式映射到后端和引擎容器中的/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 会暂停接收新任务,是最快的本地停止方式。
应该检出哪个版本?
应检出标签,而不是 main。运行 git fetch --tags,然后运行 git tag --list,即可查看可用版本;截至本文撰写时,v1.3.0(发布于 4 August 2026)是最新版本。固定版本意味着数月后重新构建时仍会得到相同的软件栈,也意味着升级应在阅读发布说明后主动决定,而不是因为某天克隆代码时机不同而被动发生。
扫描始终无法启动。应该检查什么?
首先检查可用磁盘空间,因为当可用存储空间低于 ENGINE_MIN_FREE_STORAGE_GB 时,引擎不会为任务启动扫描容器;该值默认为 20 GB。然后检查 ENGINE_WORKER_COUNT 是否为 0,因为该值会暂停接收新任务。接着运行 ./kritt setup,确认确实配置了模型凭据,因为单独存在 GITHUB_TOKEN 时无法运行扫描。docker compose logs engine 会说明跳过该任务的原因。