自托管Treg的密钥暗礁:Fernet加密、ACL与四级角色,配错一步照样裸奔
自托管Treg的密钥暗礁Fernet加密、ACL与四级角色配错一步照样裸奔【免费下载链接】tregOpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn项目地址: https://gitcode.com/GitHub_Trending/treg/treg把 3000 个外部 API 端点收敛到一个网关、把几十家供应商的密钥托管在服务器上——treg 这套OpenRouter for agent tools的设计听起来很美Agent 只需一个 token就能替你调用 SEO、社媒、广告、数据富集等真实世界的工具密钥永不出服务器。但密钥不出服务器有个前提服务器上那一坨加密落盘的密钥以及决定谁能用它们的那套权限必须配得对。社区里流传的各种 treg 教程大多停留在怎么把一个 token 调通的层面真正让自托管实例裸奔的往往是四件事Fernet 主密钥没设或丢了、角色权限给了宽、审计日志根本没被当成安全数据看、以及部署时踩了那些默认值暗坑。这篇基于 treg 仓库源码逐一拆解最后给一份可以直接照着执行的自检清单。一、Fernet 主密钥全库密钥的总开关treg 落盘加密选的是 Fernet——对称加密一个 32 字节 urlsafe base64 主密钥加密所有 Secret 值。看 crypto.py 的实现整个模块只有几十行def _fernet() - Fernet: key get_settings().secret_key return Fernet(key.encode() if key else _EPHEMERAL) def encrypt(plaintext: str) - str: return _fernet().encrypt(plaintext.encode()).decode() def new_key() - str: return Fernet.generate_key().decode()关键就在_fernet()里那个兜底secret_key为空时进程会现场铸一把临时密钥_EPHEMERAL。这意味着什么没配TREG_SECRET_KEY的服务照样能跑、密钥照样加密落盘——但重启后所有密文都无法解密。这不是悄悄丢失而是一个响亮的安全信号代码注释原话是secrets wont survive a restart, which is the intended loud signal to set TREG_SECRET_KEY。更狠的防线在 infra/db.py 的verify_db()if not settings.secret_key and sqlite not in settings.database_url: raise RuntimeError( TREG_SECRET_KEY is not set on a non-SQLite database - stored secrets would be lost on the next restart. Set a key (treg keygen) before starting. )也就是说SQLite 本地模式允许临时密钥反正数据在本机、丢了重连即可但只要你不是 SQLite启动直接拒绝。这个设计堵住了拿 Postgres 当生产库却不设密钥的最蠢路径。生成与保存密钥的正确姿势仓库自己给的 self-host 脚本写得很标准见 selfhost.shKEY$($VENV/bin/python -m treg keygen) cat $ENV_FILE EOF TREG_SECRET_KEY$KEY EOF chmod 600 $ENV_FILEpython -m treg keygen见main.py就是new_key()的 CLI 出口.env权限收成 600。这套流程值得照抄因为大部分自托管事故恰恰反着来密钥明文进 shell history、.env 权限 644、或者干脆没设。轮换主密钥轮换其实无解所以要在钥匙环里做文章必须直说一个残酷事实treg 的 Fernet 主密钥不支持原地轮换。encrypt()与decrypt()用的是同一个TREG_SECRET_KEY一旦换新密钥旧密文全部解不开唯一的补救是重新录入/重连所有供应商密钥。仓库文档auth-secrets.md和代码都没有提供双密钥滚动解密的能力。所以对自托管者而言密钥轮换的现实答案分两层主密钥层面能做的只有用 KMS/密钥管理服务注入环境变量 文件权限 600 备份到密码管理器把TREG_SECRET_KEY当根 CA 私钥一样对待。丢了它 丢了一库供应商凭据。真正的轮换粒度在 API Key 层面treg 把谁调用的凭据treg_xxx开头的 token和调用什么的供应商凭据分开管理。调用方 token 只存 SHA-256 哈希hash_token()见 crypto.py库中永远没有明文 token轮换走rotate_agent_key/rotate_human_keyapi_keys.py用条件 UPDATE 做跨进程原子占锁result await db.execute( update(ApiKey).where( ApiKey.id old.id, ApiKey.state.in_((ACTIVE, DISABLED)), ApiKey.replacement_key_id.is_(None), ).values(stateREVOKED, revoked_atnow, deleted_atnow) ) if result.rowcount ! 1: raise RotationConflict旧行置为REVOKED并挂replacement_key_id旧 token 当场失效同时保留审计痕迹。对Default key每名成员每团队一张的签名 token轮换走default_generation自增rotate_default_generationtoken 里签的kgclaim 对不上就直接 401revoked key。教训把轮换的预期放在调用方 token 上而不是主密钥上。主密钥设计成不可轮换意味着你必须保证它从一开始就不会泄露——这比能轮换更考验部署纪律。另外注意 config.py 里一个极易被忽略的细节session_secret签名会话 cookie 的 HMAC 密钥未设置时回退到secret_key。也就是说如果你只设了一个强secret_key而session_secret为空那么拿到secret_key的人同时能伪造会话 cookie——密钥隔离形同虚设。生产环境两个都应显式设置成不同的随机值。二、四级角色与 ACL最危险的权限不是给了 admintreg 的角色模型是四级owner admin member viewer定义在 models.pyROLE_RANK {viewer: 0, member: 1, admin: 2, owner: 3}角色的分界点在哪里看 access.py 的_require_can_registerdef _require_can_register(caller: Caller) - None: if not _role_at_least(caller.role, member): raise HTTPException(status_code403, detailviewers can call and read, but cannot register)一句话概括viewer 能调用、能读但什么都不能注册不能建 Secret、不能注册工具、不能发起 OAuth。routers/api_keys.py里 viewer 连创建附加 key都被拒绝viewers cannot create additional keys。invite 的--role可选值也只有viewer/member/admincli.pyowner 只能通过改角色接口授予。两个最容易被误解的坑坑一owner 不受任何 ACL 限制而 deny 规则连 owner 都拦。看 access.pydef _tool_allowed(caller: Caller, tool_name: str) - bool: if caller.role owner: return True access caller.membership.tool_access return access is None or tool_name in accessowner 在工具级 ACL 和项目级 ACL_project_allowed面前一律放行——这是刻意的owner is never restricted但意味着把一个人升成 owner就等于把团队所有密钥的使用权都交给他。反过来enforce_deny的注释写得明明白白deny 规则对包括 owner 在内的所有角色生效因为deny rule is a guardrail, not a permission tier。坑二四级角色只是注册权限的标尺真正的精细管控在 Membership 行上。每个成员有一行Membershipmodels.py上面可以单独钉三样东西tool_accessNULL 团队全部工具JSON 列表 只允许列出的工具project_accessNULL 整个 orgJSON 列表 只允许这些项目里的工具daily_call_cap每人每日调用上限-1 不限配合local_run_enabled控制能否走本地 run 授权。两把 ACL 锁的关系是AND 组合_tool_usable_tool_allowed且_project_allowed。换句话说最小权限的正确姿势不是给 viewer 还是 member而是给协作方 member 角色 tool_access钉到个别工具 project_access钉到单个项目 daily_call_cap设上限。四级角色管能不能注册ACL 管能用哪些缺一不可。还有一个隐蔽的坑invite 接口models.py 的Invite在tool_access字段上就能预置 ACL——发邀请时就钉好工具名单而不是等人进来再逐项改。自托管团队如果靠先发 member 邀请、事后收权邀请窗口期就是权限敞口。别忽视机器身份这条暗线treg 里 Agent 是不可登录的机器身份邮箱挂在agents.treg.local这种不可路由域上access.py任何登录门都解析不到它而它作为 User 却能继承 Membership 的所有能力。require_identity里专门防了一手if user is not None and _is_machine_email(user.email): raise HTTPException(status_code403, detail( this token belongs to a machine identity — it can call this teams tools, but cannot act as a user))机器 token 不能建 org、不能收邀请——因为机器能建 org 就自动成为 ownerowner 又豁免所有 ACL。这是仓库里自己写明的逃逸路径自托管时给 Agent 发 key 也请遵循同样的最小权限专用 token、钉死的tool_access、独立的daily_call_cap。三、审计日志的盲区它是尽力而为的不是证据链如果你把CallRecordmodels.py当谁在什么时候调了什么的完整账本那就踩进最大的盲区了。审计子系统audit.py的设计哲学模块 docstring 第一句就是Audit writes — deferred and fire-and-forget (rule #2: never block the proxied response).具体机制是单进程单 writer、批量落库、后台连接池以及背压丢行_MAX_PENDING 5000 # shed load past this: drop the audit row rather than grow unbounded _BATCH 200队列超过 5000 条就直接丢审计行只记一个递增计数器并打日志。这是刻意的取舍——audit is best-effort; never OOM or wedge the server for it。所以审计表可能缺行。峰值流量下丢的正是最需要查的那几笔。做安全复盘时不能把审计表里没有当成没发生。审计与计费分离。CallRecord注释明确钱在 ledger 里同步落地审计表是允许丢行的分析数据the money landed in the ledger synchronously... losing a row here costs analytics, not accounting。查成本走 ledger查行为走 audit两套数据源对不齐时别慌。失败静默但有 ERROR 日志。写库失败不会打断调用an audit hiccup must never break a real call但会打ERROR级日志。运维上必须把 treg 的 ERROR 日志当警报接住——正常流量下 audit writer 不该报错一旦出现说明审计链路已破。对安全审计而言更值得盯的三张表/字段ApiKeyEventappend-only 的 key 管理审计记录 created / rotated / revoked / membership_revoked且从不含完整密钥材料——轮换、吊销都有痕。CallRecord.refused_by凡是 treg 主动拒绝的调用ACL 拒绝、deny 规则、密钥失效都标refused_by。大量refused_by就是攻击探测的信号比正常调用日志值钱得多。refresh token 重用检测application/auth.py已轮换的 refresh token 被再次使用会吊销整条 grant family并记oauth.refresh_reuse审计行——这是仓库内置的凭据被复制检测器看到它基本等于有人在偷用你的授权。另一个盲区在本地 run 授权treg run --local的 grant 是唯一被允许把密钥值交给客户端的路径api.py但它有两道闸——local_run_enabled成员开关 共享密钥必须通过持有X-Treg-Run-Proof的隔离 runner。审计里对应GRANT/DENY事件。自托管时如果开了--server运行run_allowed_bins允许列表务必盯RunRecord的argv它是服务器以服务账号执行了哪些命令的唯一记录。四、自托管安全自检清单基于以上源码证据给一份能直接照着执行的清单。按会导致裸奔的严重程度排序第一优先密钥与加密用python -m treg keygen生成TREG_SECRET_KEY写入.env并chmod 600不要进 shell history、不要进代码仓库。非 SQLite 数据库未设密钥会拒绝启动——请把这条当成部署回归测试跑一遍故意去掉变量确认它拒绝。显式设置独立的session_secret不要让它回退到secret_keyconfig.py 第 601 行附近。主密钥不可轮换备份到密码管理器/KMS一旦疑似泄露按全量重新录入供应商凭据 轮换全部调用方 token处理。调用方 token 只存哈希看到treg_明文出现在任何日志/备份里即为事故。第二优先角色与 ACL按能注册才 member、只调用就 viewer分派角色invite 时就钉好tool_access/project_access不留先进来后收权的窗口。给协作方和 Agent 用member/专用 key 工具白名单 项目白名单 每日调用上限四件套而不是简单发 owner/admin。记住 owner 豁免所有 ACL_tool_allowed对 owner 恒真——owner 名额是团队内最敏感的权限。Agent 用机器身份agents.treg.local域 token永远别给机器身份发能建 org的权限。检查require_superadmin依赖的TREG_ADMIN_TOKENconfig.py 注释it sees ALL orgs——这个 env 兜底如果设得短或泄露等于跨租户 admin 全开。默认留空、靠is_superadmin用户是更稳的姿势。第三优先审计与运行审计是尽力而为且会在背压时丢行把 treg 进程的ERROR日志接进监控audit back-pressure出现即扩容或排查。定期扫CallRecord.refused_by非空的行、oauth.refresh_reuse审计事件、ApiKeyEvent的 revoked/rotated 记录。若启用treg run --server严格维护run_allowed_bins白名单默认不允许任意二进制注释明确a member cant namebash/pythonand run arbitrary code as the server user。检查.env里TREG_BLOCKED_EMAIL_DOMAINS、TREG_PAUSED_PROVIDERS、webhook 目标是否按需配置——这些默认都是不拦/不暂停/不告警的安全面配置。最后一条纪律treg 的 selfhost.sh 里那句话值得反复读——Never point TREG_PUBLIC_URL at a public domain while TREG_SINGLE_USER is true: the no-login dashboard refuses to run anywhere but loopback sqlite。单用户免登录模式是被代码强制锁在 loopback 的你只要把TREG_SINGLE_USERtrue的实例暴露到公网域名就等于亲手拆掉认证。配错一步照样裸奔的每一个字都对应上面某一条默认值或某个我以为没关系的开关。把这四组检查放进部署流水线treg 的密钥永不出服务器才真正成立。【免费下载链接】tregOpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn项目地址: https://gitcode.com/GitHub_Trending/treg/treg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Java+MySQL选课系统课设实战:表结构设计、JDBC连接与事务处理

Java+MySQL选课系统课设实战:表结构设计、JDBC连接与事务处理

简介:这份数据库课程设计资源面向高校计算机相关专业学生与课程设计指导教师,聚焦学生选课信息管理系统的完整实现,采用Java语言与MySQL数据库、C/S架构开发,可作为课程设计、期末大作业或数据库综合实践的参考方案。压缩包为rar格…

2026/10/10 18:30:17 阅读更多 →
开源在线客服系统源码部署实战:从WebSocket联调到二次开发避坑指南

开源在线客服系统源码部署实战:从WebSocket联调到二次开发避坑指南

简介:面向开发者、运维人员以及需要搭建在线客服体系的中小企业,提供一套开源在线客服管理系统的完整源码,可用于快速部署、二次开发与架构学习。系统能力覆盖网页端多渠道接入、客服工作台、机器人自动应答、工单处理、数据报表、多语言切换…

2026/10/10 18:30:17 阅读更多 →
知网查重与AI检测双重应对:论文从90%复制比降到10%免费攻略

知网查重与AI检测双重应对:论文从90%复制比降到10%免费攻略

如果你的知网报告刚刚出炉,满屏标红、复制比高到离谱,后面还跟着一个“疑似AI生成”的提示,那这篇文章就是给你写的。2026年毕业季,我前后实测了30多篇本硕论文,把市面上能用的降重手段摸了一遍,这里只讲免…

2026/10/10 18:30:17 阅读更多 →

最新新闻

Document:write() 方法(不推荐)

Document:write() 方法(不推荐)

document.write()将文本字符串写入由 document.open() 打开的文档流. 因为 document.write() 会向文档流中写入内容,所以在已关闭(已加载)的文档上调用 document.write() 会自动调用 document.open(),这将清空文档。 在未调用 d…

2026/10/11 5:06:32 阅读更多 →
二叉搜索树删除节点:递归五情况详解与Java实现

二叉搜索树删除节点:递归五情况详解与Java实现

1. 先把题目读懂:删除BST节点为什么是递归题的分水岭刷二叉树刷到一定阶段,大家都会卡在同一个地方:删除二叉搜索树中的节点。LC450这道题我第一遍做的时候,五个分类情况绕得我头晕,写出来的代码自己都害怕。后来把所有…

2026/10/11 5:06:31 阅读更多 →
MobaXterm高效运维指南:SSH、SFTP与远程图形化全掌握

MobaXterm高效运维指南:SSH、SFTP与远程图形化全掌握

1. 为什么我把终端全家桶换成了MobaXterm干开发这行,终端工具一直是个"看起来不重要、用起来天天烦"的东西。早些年我电脑上同时躺着好几个会话软件——一个负责SSH连服务器、一个负责传文件、偶尔还得开个浏览器去访问远程桌面。窗口切来切去&#xff0c…

2026/10/11 5:06:31 阅读更多 →
革命性AI科研智能体AutoSci:读论文、做实验、写论文的全生命周期自动化,一篇看懂

革命性AI科研智能体AutoSci:读论文、做实验、写论文的全生命周期自动化,一篇看懂

人工智能AI 技能/插件深度研究知识图谱MCP 服务 【免费下载链接】AutoSci Karpathys LLM-Wiki vision, fully realized — wiki-centric full-lifecycle AI research platform powered by Claude Code 项目地址: https://gitcode.com/gh_mirrors/om/AutoSci 点击查看…

2026/10/11 5:06:31 阅读更多 →
可靠性密码 | 高可靠性之光学镜架设计

可靠性密码 | 高可靠性之光学镜架设计

△ 高可靠性固体激光器激光器腔体内部光学镜架作为谐振腔镜片、反射镜、聚焦镜等核心光学元件的承载与调节部件,是决定激光器整机可靠性的关键子系统。传统镜架普遍存在热变形偏移、振动失准、螺纹蠕变、夹持应力不均等问题,极易引发光束漂移、功率衰减&…

2026/10/11 5:06:31 阅读更多 →
PyMuPDF + Qwen-VL:构建图文混排PDF的RAG检索方案

PyMuPDF + Qwen-VL:构建图文混排PDF的RAG检索方案

1. 为什么传统 PDF RAG 一到扫描件就“断片”1.1 文本提取式的 RAG 有多脆弱我最早做 PDF 知识库问答时,思路非常简单粗暴:用解析库把 PDF 里的文字抠出来,按段落切块,然后塞进向量库,查询时做相似度召回。这个方案跑纯…

2026/10/11 5:05:31 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →