# easytier-pro-synology **Repository Path**: EasyTier-Pro/easytier-pro-synology ## Basic Information - **Project Name**: easytier-pro-synology - **Description**: EasyTier Pro client for Synology DSM - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-15 - **Last Updated**: 2026-10-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # easytier-pro-synology 面向群晖 DSM 与 fnOS 的原生 EasyTier Pro 客户端:一个仓库同时产出群晖 SPK 套件与 fnOS FPK 应用,两端均不使用 Docker。 它把 NAS 作为一台设备接入 EasyTier Console:登录 Console、取得设备注册密钥、在 NAS 上运行 `easytier-core`,并在系统里提供本机管理界面与 Console 网页入口。**套件不包含 Console 服务端**,也不管理其他设备。 两端共享同一份 Go 守护进程与 Vue 界面源码;平台差异收敛在 `internal/platform/`、 `internal/{dsmenv,fnosenv}/`、`internal/config/` 的路径根与 `fnos` 构建标签中。 ## 功能 - 支持 EasyTier Console 设备码登录(在浏览器完成),也支持直接粘贴设备注册令牌。 - 优先复用本机已保存的密钥,也可恢复当前设备密钥、使用已有共享密钥,或创建共享/专用密钥。 - 自动从 Console 稳定通道获取版本,优先 Gitee、回退 GitHub,只下载 Console 指定的精确 tag。 - 运行时下载带校验与事务化安装:校验压缩包成员、版本号与(若 Console 提供)摘要, 替换过程中断或新版本不可用时会恢复旧版本。 - 连接切换使用持久事务:掉电或并发操作不会留下令牌与设置不一致的状态。 - 在界面里查看本机节点、Peer 摘要、TUN 设备和脱敏日志,并按 Console 网络列表加入/退出网络。 - 可一键打开 Console 网页控制台。 - 支持 `x86_64`、`armv8`、`armv7` 三种 DSM 架构,支持 DSM 6.2 与 DSM 7.x(`os_min_ver="6.2-23739"`)。 - fnOS 版支持 `x86_64` 与 `arm64`,要求 fnOS ≥ 1.2.0401,以 root 运行,界面经 fnOS 统一网关提供。 ## 安装 ### 群晖 DSM 1. 在 `dist/` 中取得与 NAS 架构匹配的 `.spk`(`x86_64`、`armv8`、`armv7`)。 2. 打开 DSM「套件中心 → 手动安装」,选择该 `.spk`。 3. 安装后打开 DSM 主菜单中的「EasyTier Pro」。 ### fnOS 1. 在 `dist/` 中取得与设备架构匹配的 `.fpk`(`x86_64` 或 `arm64`)。 2. 打开 fnOS「应用中心 → 手动安装」,上传该 `.fpk`。 3. 安装后打开桌面的「EasyTier Pro」图标。 首次进入页面时(两端相同)按引导登录 Console(或粘贴设备令牌)、选择工作空间与注册密钥; 套件会自动下载并安装 EasyTier 运行时,随后在概览页选择要加入的网络。 ### 界面地址 DSM: - 页面:`/3rdparty/easytier-pro/index.html`(DSM 主菜单入口,套件注入的 nginx 配置提供), `/webman/3rdparty/easytier-pro/index.html` 同样可访问。 - 本机 API:`127.0.0.1:15890`,由 DSM nginx 反向代理到 `/3rdparty/easytier-pro/api/` 与 `/webman/3rdparty/easytier-pro/api/`,浏览器与 API 同源。 fnOS: - 页面:桌面图标经统一网关前缀 `/app/easytier-pro` 打开(iframe)。 - 本机 API:Unix socket `$TRIM_APPDEST/app.sock`,仅经统一网关访问,由网关注入管理员身份。 ## 安全模型 (以下路径与校验细节以群晖端为例;fnOS 端的状态根为 `$TRIM_PKGVAR`,会话校验改为由统一网关 注入的管理员身份完成。) - 所有状态位于套件数据目录 `/volume*/@appdata/easytier-pro`,权限 `0700`, 升级套件不会丢失,卸载时由 `postuninst` 清理。其中 `cache/` 暂存最近一次下载的运行时 压缩包(约 25 MB):更新在下载之后失败时,重试直接复用它而不重新下载;安装成功后立即 删除,所以它只在"下载完成"到"更新结束"之间占用空间。 - `state/secrets/bootstrap-token`(设备注册令牌)、`state/secrets/console-session.json` (Console access/refresh token)、`state/machine-id` 权限均为 `0600`。 - 令牌只保存在 NAS 上,不返回浏览器;界面只读取白名单字段。 - `easytier-core` 的输出被丢弃,避免配置服务器 URL 中的注册令牌进入日志。 - 日志返回浏览器前会移除注册令牌、`Authorization` 头与 JWT。 - 本机 API 只监听回环地址,且每个请求都必须通过 DSM 会话校验:只有已登录的 DSM 管理员 (`administrators` 组)可以调用。 - Console 会话刷新与退出在进程内串行执行;只有明确的 `invalid_grant` 才会清理会话。 ## 范围限制 - 不包含 Console 服务端、relay、access、可观测性等云侧组件。 - 不管理 DSM 防火墙:虚拟网卡对局域网或反向的访问,请在 「控制面板 → 安全性 → 防火墙」中自行放行(OpenWrt 版插件的 zone 逻辑没有移植)。 - DSM 7 强制第三方套件降权运行:`conf/privilege` 只允许 `defaults.run-as = "package"`, 且不接受 `ctrl-script`、`executable` 与 `tool.capabilities`,因此守护进程以套件专用用户 `easytier-pro` 运行、不带任何 capability;本机 API 只监听回环,并且每个请求都要先通过 DSM 会话校验——守护进程携带调用方 Cookie、CSRF 令牌和 nginx 提供的来源 IP,通过回环询问 DSM 自身的 Web API,由 DSM 判定 该会话是否为管理员(详见 `docs/dsm-verification-checklist.md`)。 - DSM 6 通过 `conf/privilege` 让正式启动、停止和状态检查以 root 执行,守护进程及 core 因此可直接使用 TUN;预检查不启动进程。nginx 路由在 DSM 6 上由 `postinst`/`postuninst` 直接拷入/清理 `/usr/syno/share/nginx/conf.d/`,而非 DSM 7 的 `web-config` worker。 - TUN 设备需要 `CAP_NET_ADMIN`,而运行时二进制是首次连接时下载到套件数据目录的,无法由 DSM 提前授予。没有该能力时守护进程会自动把本机节点切换为**无 TUN 模式**(在 EasyTier Console 上 设置该节点的 `no_tun`,使下发的实例配置与本机权限一致)。无 TUN 模式并不等于退出网络: 本机仍会获得虚拟 IP,其它节点可以主动访问本机(也能经本机虚拟 IP 访问本机上的服务), 子网路由与中继照常可用;唯一的区别是本机自身没有虚拟网卡,NAS 上的程序无法主动访问网络中的 其它节点。概览页会显示「无 TUN 模式(用户态转发)」并说明原因。 - 连接同样不需要能力:core 默认会把出站 socket 绑定到网卡(需要 `CAP_NET_RAW`),守护进程会 在 Console 上为本机节点关闭该绑定(`bind_device = false`)。因此**在无 TUN 模式下本套件不需要 任何 capability,也不需要任何 root 操作**。若希望 NAS 本身拥有虚拟网卡(即让 NAS 上的程序能 主动访问其它节点),可由管理员可选地授予 `CAP_NET_ADMIN`(`setcap` 命令见概览页)。 - 需要本机拥有虚拟网卡时,由管理员一次性授予该文件能力,然后重新启动套件: `sudo setcap cap_net_admin,cap_net_raw+ep /volume*/@appdata/easytier-pro/runtime/easytier-core` (每次重新下载运行时后需要再执行一次;概览页的「复制命令」按钮直接给出当前路径的完整命令)。 守护进程每次启动都会重新检查该能力:检测到后会自动取消 Console 上的 `no_tun` 设置,恢复完整模式。 - 界面只覆盖本机相关操作,完整的 Console 管理能力请打开 Console 网页。 ## 开发 ```sh go vet ./... && go test ./... # 静态检查与单元测试(默认标签,群晖) go vet -tags fnos ./... && go test -tags fnos ./... # fnOS 构建标签下的静态检查与单元测试 scripts/build-spk.sh --arch all # 构建三个架构的 SPK(会先构建界面) scripts/check-spk.sh dist/*.spk # 校验 SPK 结构 scripts/build-fpk.sh --arch all # 构建 FPK(x86_64 与 arm64,会先构建界面) scripts/check-fpk.sh dist/*.fpk # 校验 FPK 结构 sh scripts/test-lifecycle.sh # fnOS 生命周期模拟(安装/启动/停止/卸载,无需真机) sh tests/smoke-lifecycle.sh # DSM 启停脚本冒烟测试(无需 DSM) ``` 界面是构建产物,守护进程用 `go:embed` 内嵌 `ui/dist-dsm/`(fnOS 构建内嵌 `ui/dist-fnos/`), 所以改界面后必须重新构建它,否则 Go 侧编译或运行到的仍是上一次的产物: ```sh cd ui npm ci npm test # 组件与工具函数测试(vitest) npm run typecheck # vue-tsc 类型检查(build:dsm / build:fnos 自身不做类型检查) npm run dev # 本地开发,接口默认指向同源 npm run build:dsm # 产出 ui/dist-dsm/,提交进仓库供 go:embed 使用 npm run build:fnos # 产出 ui/dist-fnos/,同样提交进仓库 ``` `ui/dist-dsm/` 与 `ui/dist-fnos/` 之所以提交,是因为内嵌发生在编译期:只装 Go 工具链的人 也要能构建出可用的守护进程。改动界面源码后请一并提交重新构建的产物。 守护进程每 30 分钟向 Console 查一次当前稳定版本。装好的核心比它旧时,界面会提示并 提供一键升级;升级由用户确认后执行(会重启核心、短暂断网),不会自动进行。同一份版本 判定逻辑既用于校验下载到的二进制,也用于判断是否有新版本,见 `versionReports`。 界面默认跟随系统明暗,页头可切换为固定浅色或深色,选择保存在浏览器里。首屏背景由 `ui/index.html` 的内联样式按同一选择直接画出,不等 JS 加载,因此打开时不会先白一下; 该文件的 `data-theme` 属性由 `ui/src/theme.ts` 维护,两处必须保持一致。 `ui/index.html` 里的内联样式是刻意为之,不要挪进打包产物。 在没有 DSM 的机器上调试(仅当设置了 `ETP_DEV_ROOT` 时下列变量才会生效): ```sh ETP_DEV_ROOT=/tmp/etp ETP_DEV_NO_DSM_AUTH=1 ETP_DEV_LISTEN=0.0.0.0:15890 \ ./easytier-pro-dsm serve ``` `ETP_DEV_ROOT` 会把套件目录重定位到 `/target` 与 `/var`,此时 `ETP_DEV_NO_DSM_AUTH=1` 跳过 DSM 会话校验,`ETP_DEV_LISTEN` 改写监听地址(fnOS 构建下改写 Unix socket 路径)。fnOS 构建对应 `ETP_DEV_NO_FNOS_AUTH=1`,跳过网关身份校验; `sh scripts/test-lifecycle.sh` 走的就是这条路径。这些变量都不会在真实设备上生效。 图标由 Console 的品牌图形生成(一次性提交,不参与构建): ```sh for n in 16 24 32 48 64 72 256; do rsvg-convert -w $n -h $n -o ui/public/images/app_$n.png ../easytier-console/web/public/favicon.svg done rsvg-convert -w 64 -h 64 -o spk/icons/PACKAGE_ICON.PNG ../easytier-console/web/public/favicon.svg rsvg-convert -w 256 -h 256 -o spk/icons/PACKAGE_ICON_256.PNG ../easytier-console/web/public/favicon.svg ``` ## 端到端验证 - CI(`.github/workflows/checks.yml`)对每个提交做双平台检查:默认与 `fnos` 标签的 go vet / go test、界面类型检查与 vitest、Shell 语法检查、SPK 与 FPK 的构建和结构校验、 fnOS 生命周期模拟,以及 fnOS 侧源码/产物的防泄露检查。main 分支额外产出两个平台的安装包 artifact(`.github/workflows/build.yml`)。 - DSM 真机验证(全新安装、设备登录、入网、TUN 与重启恢复)的证据归档在 `docs/e2e-20260915/`、 `docs/e2e-20260915-fix/` 与 `docs/e2e-20260915-tun/`。 - fnOS 版的设计文档见 `docs/superpowers/specs/2026-09-16-easytier-pro-fnos-design.md`; fnOS 真机 E2E 的证据将按 monorepo 计划(`docs/superpowers/plans/`)归档在 `docs/e2e-20260916-fnos/`。 ## 目录结构 |路径|说明| |---|---| |`cmd/easytier-pro-dsm`|群晖入口:`supervise`(套件启停)、`serve`(守护进程)、`version`| |`cmd/easytier-pro-fnos`|fnOS 入口(`fnos` 构建标签),与群晖入口共用 `internal/daemon` 脚手架| |`internal/config`|包路径、设置、密钥、机器标识、原子写与日志(路径根按平台拆分在 `paths_dsm.go` / `paths_fnos.go`)| |`internal/console`|EasyTier Console 客户端(设备码登录、会话、注册密钥、网络与节点)| |`internal/daemon`|supervise/serve 脚手架(两端共享)| |`internal/platform`|平台适配:监听器、鉴权器、品牌名(`dsm.go` / `fnos.go` 按构建标签选择)| |`internal/runtime`|`easytier-core` 监督、运行时下载与事务化更新、连接切换| |`internal/httpserver`|本机 REST API 与静态界面| |`internal/dsmenv`|DSM 会话校验| |`internal/fnosenv`|fnOS 网关身份校验| |`ui/`|管理界面(Vue 3 + TypeScript + Vite;`npm run build:dsm` / `build:fnos` 产物分别提交在 `ui/dist-dsm/` 与 `ui/dist-fnos/`,因为守护进程用 `go:embed` 内嵌它们)| |`spk/`|群晖套件元数据、脚本与 nginx 注入配置| |`fpk/`|fnOS 应用元数据、生命周期脚本与桌面入口| |`scripts/`|双平台构建与校验脚本、fnOS 生命周期模拟| |`tests/`|DSM 生命周期冒烟测试|