背景
家里人在外地,想通过飞牛(fnOS)的 fnConnect 中转远程看 Emby。问题在于:fnConnect 只代理"应用商店安装的应用",我自己 docker 跑的 Emby(18096 端口,挂着刮削代理和番剧库)没有桌面图标、也没有中转通道。目标很明确:不重新部署、保留全部 docker 环境变量,让家人登录飞牛 Web 桌面就能点开 Emby。
一、fnConnect 是怎么代理应用流量的(第一手逆向)
全部结论来自对自建 NAS(fnOS 1.2.0701)的实查,非二手资料:
- 进程三件套:
trim-connect(主服务)+pxy(常驻出站隧道,连中转服务器 43.226.56.35:7202)+trim_open_gateway。开启 fnConnect 后系统自动签发 Let's Encrypt 证书(CN=用户名.5ddd.com)并配置泛解析。
- 中转层没有通用端口转发:中转服务器上一票端口 TCP 握手都通、但 TLS/HTTP 全部无响应——它们只讲 pxy 的私有隧道协议。浏览器唯一入口是 443,按 SNI 分流。
- 端口型应用走三级子域名:nginx 访问日志里抓到远程用户实际访问的是
openlist.xueayi.5ddd.com(<应用名小写>.<FN ID>.<中转域名>)。OpenList 自己的日志证实中转链路把远程用户真实 IP 透传了进来,头一两次 403(未授权提示页/trimfn/apperror)之后就正常了。
- 路径型应用走主域:影视是
https://xueayi.5ddd.com/v/...,由 nginx 静态 location 反代(配置里只有一个 server 块、零动态上游,vhost 分流完全不经过 nginx)。
- 映射的源头是应用注册:
trim-connect与系统服务总线(/run/trim_app_cgi/netsvr)之间有常驻 RPC,"哪个子域名转到哪个端口"完全由应用中心注册数据决定。随机编一个子域名,403 页面与 emby 的逐字节相同——查不到注册就没有拨号目标。
二、注册体系的三张表
appcenter库(PostgreSQL):app(身份/service_url 模板)、app_service(http://${host}:${port}/这类入口)、app_open(桌面打开方式)。
trim_sac库entry表:桌面图标注册,is_admin字段就是 GUI 里那个"仅管理员/所有用户"开关。
- 有趣的发现:OpenList 的端口存在
app_env表里且是混淆值(env.obfuscate=true),而 qBittorrent 等是直接把端口写死在 url 模板里。
- 死胡同:
entry.fn_connect_allow_proxy全系统只有 trim_sac 引用,trim-connect 根本不读它——网上猜的"翻这个开关就能让 docker 走中转"是错的。
三、失败尝试一:直接插库伪造注册
模仿 OpenList 往三张表插行指向 18096,外加图标文件和
/var/apps/Emby 目录。结果:桌面图标出现了,但 emby.xueayi.5ddd.com 依旧 403。结论:子域映射由应用中心服务运行时经 RPC 下发并带包校验,裸 DB 行骗得过桌面、骗不过 trim-connect。已完整回滚(顺带踩了 PostgreSQL desc 保留字和 heredoc 吃掉 ${host} 两个小坑)。四、失败尝试二:官方 fnpack 打"壳应用"
按官方开发者文档用 fnpack 1.2.3 打了一个不含服务程序的壳包(manifest 声明
service_port=18096,生命周期脚本全是 no-op,status 只探测 127.0.0.1:18096)。打包成功,但安装后桌面无图标——安装链上的初始化校验没有走通。这条路理论上是正路(不重部署、纯注册),但调试成本超出收益,先放弃。五、成功方案:应用中心原生 Emby + 数据移植
应用商店里的 Emby(EmbyServer4-9,v4.9.42)是原生包(
is_docker=f,直接跑二进制),没有 compose 也就没有环境变量——但数据完全可以搬过来:- 迁移:
cmd/main stop后,把 docker 的/config(722MB,剔除可再生的 cache 和转码临时目录)整体复制到原生应用的 programdata(/var/apps/EmbyServer4-9/var),chown给应用用户EmbyServer(uid 980)。全新初始化的空库改名var.fresh-bak留底。
- 单独代理:原生应用没有独立 systemd 单元(挂在
trim_app_center.service下),注入点是它自己的cmd/service-setup——在SERVICE_COMMAND=前插入三行export HTTP_PROXY/HTTPS_PROXY/NO_PROXY(从 docker 容器原样照抄)。Emby 是 .NET 程序,认这套变量,日志里 mb3admin/GitHub 请求 200 实锤生效。只作用于这一个进程。
- 媒体授权:原生应用跑在 uid 980 下,而媒体目录是
911:911的 700 权限、/vol1/1000整个是0000——docker 时代靠 root 才没事。必须在 GUI 的"授权目录"里把媒体目录授权给应用(fnOS 会沿路径打 ACL)。另外在根目录建了/anime → /vol1/1000/anime_HC550软链,库里存的原容器路径原样可用。
- 一个假故障:迁移后 GUI 报"启动失败",实际服务一直活着——是进程号文件在迁移中变成空文件,导致状态检查误判"未运行",再点启动就撞上 8096 端口占用。清干净重启一次即愈。
六、收尾
- 旧 docker 容器已删(镜像保留 760MB),compose 和完整
docker inspect快照留在/vol1/1000/docker_HC550/emby/,随时docker compose up -d回滚;
- frpc 隧道
emby目标从 18096 改 8096(frpc.sh off + add),emby.cn.xueayi.site 验证 302 全链路通;
- Sun-Panel 两张导航卡(公网直连域和局域网地址)同步切到 8096。
七、备忘
- 应用升级会重写
cmd/service-setup,代理三行会被冲掉——需要在SERVICE_COMMAND=前重新注入(脚本里留了fnconnect-migration注释标记方便检索)。
- fnConnect 中转按会员带宽走(40Mbps),家人日常看片没问题;自己的重度使用仍然走 frp 直连线路更宽。
- "只有 Emby 走代理"是个误会:fnConnect 本来就是按需的入站通道,没人远程访问就是零流量,NAS 的出站流量(刮削/下载/局域网播放)从不经过它;再把不想暴露的应用设成"仅管理员"即可。
脚本与中间产物见:fnos-emby-migration(Gitea)。

