TREK 插件管理面板实战:从零装好一个第三方插件并守住安全底线
TREK 插件管理面板实战从零装好一个第三方插件并守住安全底线【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREKTREK 是一款自托管的旅行规划应用插件系统让它可以在不改一行源码的前提下长出新的能力仪表盘 widget、独立页面、行程页标签还有照片、日历、通知渠道这类集成。本文带你在Admin → Plugins面板里走完一遍真实运维动作安装、审查、启用、更新、卸载以及每一步背后的安全边界。看完你可以直接接手一个生产实例的插件治理。装之前先确认两个开关状态打开面板前有两个东西要先弄清楚。第一层运行时总开关。环境变量TREK_PLUGINS_ENABLED控制整个插件系统默认是开的。判定逻辑写得很直白见 kill-switch.ts值只要不是false、0、off、no大小写不敏感就算开启而且每次调用时实时读取环境变量改完重启立即生效。第二层逐个激活。总开关打开只代表电闸合上了但每个插座插件默认还是断电的——所有插件必须逐个手动激活激活之前没有任何第三方代码执行。两层关系记成一句话开关管系统开关不管具体谁在跑。如果总开关被关掉面板会提示Plugins are disabled大意是运行时已关闭任何插件都无法运行需要管理员在服务端配置里重新打开。总开关关闭时已安装插件不会从磁盘消失只是处于停用状态重新打开后随时可恢复。插件相关的环境变量一共四个先列在这里后文用到时会展开环境变量作用默认TREK_PLUGINS_ENABLED插件系统总开关开启TREK_PLUGINS_DEV_LINK允许从本地构建目录注册插件并热重载值必须恰好是1关闭TREK_PLUGIN_ALLOW_PRIVATE_EGRESS设为on后允许插件出口访问私网/内网地址关闭默认拒绝私网TREK_PLUGIN_REGISTRY_URL覆盖 Discover 页浏览的注册表索引地址可指向自己的镜像官方注册表面板长什么样两个视图加一个容易忽略的按钮面板顶部是分段切换器两个视图Installed已安装当前实例装了什么带数量统计多一个Status状态筛选Active / Off / Update available / Error。Discover发现社区注册表卡片式浏览。两边共享一套工具栏Search plugins…搜索框、Type类型筛选Widget / Page / Integration / Trip page、Sort排序、Upload plugin上传按钮、Rescan重扫描按钮。Rescan 值得单独说。很多人以为它只是刷一下列表其实它做两件事重新发现磁盘上本地安装的插件同时强制拉取远程注册表绕过服务器 30 分钟的缓存CACHE_TTL 30 * 60 * 1000见 registry.service.ts和 GitHub 的 CDN。为什么非要绕缓存因为不绕的话一个刚发布的插件最长要等约 35 分钟才看得见。点一次 Rescan新插件立刻出现。拉取的是聚合的dist/index.json而不是逐个插件调 GitHub API避免速率限制即使注册表请求失败也只是软降级面板本身照常能用。从注册表装一个插件三步以及版本不兼容时按钮会变的逻辑安装流程就三步切到 Discover 找到卡片 → 点开卡片进入预安装审查对话框→ 点Install。这里有一个容易被忽略的细节你看到的按钮不总是Install。如果最新版要求的 TREK 版本比当前实例新按钮会变形——还有兼容的旧版本时按钮变成Install {version}装那个旧版本如果所有版本都不兼容按钮变成Incompatible并禁用。无论哪种情况对话框里都会有一条琥珀色提示条把原因讲清楚不会藏进 tooltip。背后是 host-compat.ts 的assertHostCompatible/hostSatisfies在比对插件声明的trek版本范围与宿主版本。装完的插件默认是关闭状态。安装流程只做下载、校验、安全解压、重新校验 manifest、注册为 inactive不执行任何内容。预安装审查对话框四块信息数据来源是审查时点的 manifest点卡片后你会看到四个区块What it can access——它请求的权限逐条用大白话渲染读取行程……、创建和编辑地点……。未知权限代码会原样显示一条不请求就显示Needs no special access.Connects to——manifest 声明的所有可访问主机等宽字体 chip。Setup——插件要求填写的设置项标注Instance-wide或Per user必填项带Required。Details——版本、体积、所需 TREK 版本范围、审查时间、总下载量。对话框底部还有Source repository、Report an issue、Homepage三个链接。关键在数据来源这些内容不是作者写的介绍文案而是服务端拉取插件审查提交点的实时 manifest 生成的预览ManifestPreviewregistry.service.ts包含权限列表、出口主机列表、operatorEgress标记、设置字段、许可证、图标、所需 addon 与插件依赖。也就是说你审查的是被钉在某个提交上、经过维护者扫描的那个字节的声明而不是作者事后想说什么就说什么。只有我知道地址的插件允许主机白名单怎么管有一类插件要访问只有你自己知道地址的服务比如自托管的 Gotify、ntfy。manifest 没法预知主机名所以这类插件会声明operatorEgress审查对话框里就多出一个 hosts you addchip 和一条提示装完后去 ⋯ → Allowed hosts 添加它允许到达的主机其它主机一律到不了。装完之后你会在插件行看到一个琥珀色的Add allowed hostchip——这是有意的因为此时它一个主机都访问不到不提示的话看起来就像静默故障。往 ⋯ → Allowed hosts 对话框里逐个添加主机名后chip 变蓝并显示数量。三个机制必须知道保存即重启。运行中插件的出口白名单不能在原进程内热扩容——出口守卫在子进程初始化时安装一次且拒绝二次 initplugin-runtime.service.ts 中有明确注释所以唯一让新列表生效的方式就是重新拉起子进程。删除某个主机同理立即失去出口并重启。校验规则与 manifest 一致不允许裸*、不允许整 TLD 通配、不允许带协议前缀。权限边界是死的。没声明operatorEgress的插件永远无法被授予主机安装时同意的范围就是硬边界只有管理员能添加主机普通用户永远不能扩大插件出口哪怕凭据是用户自己填的。❗ 如果目标服务跑在 TREK 同一台机器或同一局域网localhost、192.168.x.x还要设TREK_PLUGIN_ALLOW_PRIVATE_EGRESSon——默认插件不得访问私网。但这个变量会放宽所有已安装插件的私网出口策略只有你信任全部插件时才该开。启用失败三种被拒原因和它们的补救路径每个插件行上有一个Enable plugin开关旁边是Active或Off状态。图标块上的彩色圆点反映运行时健康active 绿、starting 蓝闪、error 红、inactive 淡、disabled/incompatible 琥珀。启用被拒时会给出结构化错误码plugin-runtime.service.ts 的assertActivatable检查链从最严重到最次依次检查宿主版本不兼容TREK_VERSION_INCOMPATIBLE/TREK_VERSION_UNKNOWN——插件声明的 TREK 支持范围与当前实例对不上。权限再同意——新版本请求了尚未授予的权限见下文更新章节。必需 addon 未启用ADDON_DISABLED——toast 会报出 addon 名称去 Admin → Addons 开启后重试。插件依赖缺失或过旧DEPENDENCY_MISSING——对话框逐条列出依赖提供一键Download / Update装好最新兼容版本后自动重试启用。整个预检是只读的任何一项不满足都不会留下半激活状态。级联语义双向存在。启用时服务端先按依赖图算出enableOrder拉起所有依赖再拉起目标已安装但停用的依赖会被自动级联启用toast 会告诉你哪些被顺带打开。反过来如果某插件依赖的 addon 被关闭它会被自动停用deactivateForDisabledAddon会顺带停用所有传递依赖它的插件同理你手动停用一个被依赖的插件所有依赖它的插件也会一起停deactivateWithDependents——一个插件不能在依赖缺失的情况下继续跑。⋯ 菜单里还有Restart仅活动插件、View error log、Allowed hosts、Source repository与Report an issue仅注册表插件、Delete。更新绝不静默扩权新权限必须二次点头有新版本时插件行出现Update → v{version}按钮列表上方多一条{count} updates available for your plugins.提示和Update all批量按钮。如果新版本请求了尚未授予的权限TREK 的做法是新代码装上去但插件保持关闭并提示大意是{name} v{version} 在请求你还没批准的权限新版本已安装但在你批准前保持关闭。对话框分两列Newly requested permissions与New outbound connections你选Approve turn on或Keep off for now。批量更新多个插件时这些同意提示会排队依次出现一个都不跳。若被同意的新版本未签名对话框还会补一句没有任何机制把这个版本和作者绑定。服务端保证更新永不会静默扩大权限update()对比新版本声明权限与已授予权限的差集newGrants无新增就透明重启到新代码有任何新增权限或出口主机就保持 inactive 并返回差集。同时resolveUpdateTarget挑的是当前 TREK 能运行的最新版本而非简单最新版——避免新版本放弃对旧宿主的支持时把本来好好的插件更新坏。签名密钥变更唯一可以覆盖的情形且必须回显完整公钥作者的签名密钥和安装时钉住的密钥对不上时更新被拒插件行显示Update blocked — {reason}和Review链接。对话框并排展示钉住的密钥指纹与当前提供的密钥指纹并提醒TREK 分不清合法的密钥轮换和账号接管两者在这里长得一样请先通过你信任的渠道向作者确认新密钥。覆盖范围有严格限定只有密钥变更SIGNATURE_KEY_CHANGED可以给Trust the new key update按钮。签名无效、缺失或半声明half-declared只有解释没有任何覆盖按钮服务端同样拒绝这些情形。服务端的/retrust端点强制了这一点plugins.controller.ts 与assertRetrustable调用方必须回显对话框中展示的完整公钥防止对话框渲染之后注册表条目又被换钥。重信任与更新在同一调用内完成——要么新密钥通过校验、插件落到新版本并钉住新密钥要么什么都不变不存在钉住未验证密钥的中间窗口。卸载它会清掉哪些东西⋯ → Delete弹确认框Uninstall plugin?——大意这会停止插件、移除代码、删掉它的全部数据不可撤销。uninstall执行的动作plugin-runtime.service.ts停止插件进程、移除代码目录、删除plugins注册表行与设置字段。选deleteData时再清插件自己的数据目录、错误日志、实体元数据、每用户配置含加密密钥、OAuth 令牌与状态、迁移台账、能力审计日志、待处理的 GDPR 擦除队列。两条无条件规则要记牢出口主机与定时任务无条件删除——否则后续复用同一个 id 的插件会悄悄继承前人的权限。若选择保留数据待处理的用户数据擦除义务也会保留等同 id 插件重装后继续兑现。⚠️ 卸载前想留资料就提前导出这个对话框里没有先备份再删的选项。徽章说明什么Reviewed、Signed、Unsigned 的保证与不保证Reviewed已审查TREK 维护者对每个版本做过恶意软件扫描。但注意这不承诺质量、不承诺能用更不是无害担保。Signed已签名安装时文件已对照作者签名密钥校验且密钥被钉住TOFU信任首见。校验和证明字节是注册表担保的字节签名再往前一步证明这些字节来自作者。Unsigned未签名字节和注册表担保的一致但没有任何东西把它和作者绑定。目前注册表里大多数插件未签名所以这是琥珀色提示而非警报。两个徽章都不告诉你代码到底做了什么。这也是下面这节要回答的问题如果它恶意最坏会发生什么最坏情况推演隔离模型与权限边界每个活动插件跑在独立 OS 子进程里用 Node 权限模型--permission启动文件系统读取限定在它自己的代码目录内。插件摸不到JWT_SECRET、数据库连接或任何 TREK 机密——这些对它的进程物理不可达它不能打开trek.db、不能写文件、不能派生子进程、不能用 worker 线程、不能加载原生模块它自己的数据放在独立 SQLite 文件里且只能经由 TREK 访问。插件与 TREK 只通过内部 RPC 通道通信TREK 只应答 manifest声明且你批准的能力未授予的调用是被拒绝而非忽略。RPC 通道本身对插件代码是封死的即使插件跑在 fork 进程里其原始 IPC 原语process.send、process.on(message)在代码加载前就被吊销——它既伪造不了宿主消息也窃听不了在途请求一切交互都被迫经过能力校验的 SDK实现见 runtime/plugin-sdk.ts 与 supervisor/plugin-supervisor.ts。界面侧插件的页面/组件跑在密封的浏览器 frame中读不到会话 cookie碰不到外围 TREK 页面。进程侧插件崩溃、挂起或内存耗尽时死掉的只有它自己的进程TREK 继续运行你可以随时重启或停用它。把上面串起来最坏情况的边界就清楚了你批准的权限列表是真实边界而非标签。它界定插件能触及什么但管不了它在授权范围内的意图——一个被允许读行程、且允许连某主机的插件完全可能把行程数据发过去。所以装之前读权限清单和出口主机是这道防线里唯一由你拍板的一环。两条旁路上传插件与开发链接绕过注册表有两条路各有适用场景和风险。Upload plugin旁路上传。工具栏按钮或直接拖.zip到面板。上传的归档保持 inactive激活时照样要同意权限。该行标记Sideloaded含义是手动上传非来自注册表无签名无审查。服务端sideload()先解压到 staging做和注册表安装相同的硬性防护防 zip-slip/zip 炸弹的安全解压、严格 manifest 校验、拒绝原生二进制只有 SHA-256/签名校验不适用因为没有注册表条目可比对。上传上限 50 MBplugins.controller.ts 中50 * 1024 * 1024 4096。用相同 id 覆盖上传时旧代码会先被强制停止并停用再替换不会出现未经重新激活仍在跑的旧状态。Link a local plugin开发链接。一个路径输入框从本地构建目录注册插件并针对真实数据热重载。该入口仅在TREK_PLUGINS_DEV_LINK1值必须恰好是1见 dev-link.ts时出现面向开发环境。该行标记Dev-Link。实现是link()对代码目录建符号链接而非复制校验 manifest、拒绝原生二进制注册为 inactive再用fs.watch监听构建输出重建后防抖 400ms 自动重新 fork。对自己诚实一点这两个徽章都只是卡片上的标签。它们记录代码来源但不会触发额外检查、不同沙箱或额外限制——旁路插件以它声明的权限原样运行和注册表插件待遇完全相同只是没人替这份代码做过恶意扫描和签名背书。source 徽章会替换这些行上的 Signed/Unsigned 徽章因为它本身就是更强的声明。审计插件替你做了什么谁来查面板上的每个端点都要求管理员账户且叠加TREK_PLUGINS_ENABLED总开关dev-link 还额外要求TREK_PLUGINS_DEV_LINK。服务端路由统一挂在Controller(api/admin/plugins)同时用JwtAuthGuard与AdminGuardplugins.controller.ts运行时关闭时install、upload、activate、update、rescan等操作统一返回 503。除了管理端点插件的每次行动本身也是可审计的。每个用户都能在Settings → Plugins的活动日志里查看插件以自己名义执行的全部操作读了哪些行程和费用、写了哪些地点、TREK 代发的每一次出站调用。这个视图不设管理员门槛——这是 TREK 基于哈希链的防篡改插件审计的用户侧管理员则在Admin → Plugins看到按插件维度的聚合视图。每条权限的确切授予范围——从只读的db:read:trips每次调用对操作者做 membership 校验到写入类的db:write:places叠加place_edit权限与写入审计再到宿主中介的oauth:client宿主持令牌插件只拿短期 access token和notify:send收件人被限定为操作者本人或其所属行程——完整记录在 wiki/Plugin-Permissions.md。延伸阅读wiki/Plugins.md插件系统全貌类型、隔离模型、依赖与活动日志wiki/Plugin-Permissions.md每条权限的授予范围与http:outbound细节wiki/Plugin-Development.mdSDK 与 manifest 编写wiki/Plugin-Publishing.md注册表提交流程与trek-pluginCLIwiki/Admin-Addons.md插件可能依赖的 addon 管理wiki/Admin-Panel-Overview.md管理面板总览wiki/Environment-Variables.md插件相关环境变量完整参考wiki/Security-Hardening.md安全加固建议插件运行时源码server/src/nest/plugins/【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考