尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

群晖NAS内网穿透:frpc容器化部署与安全实践

群晖NAS内网穿透:frpc容器化部署与安全实践 如果跟我一样是群晖用户大概率经历过这种时刻人在地铁上甲方临时要一份存在家里NAS上的图纸或者想在公司连回自家服务器改个配置。眼下没有公网IPDSM自带的QuickConnect用起来又慢又绕托管型的内网穿透工具虽然省事但数据链路握在别人手里总归不踏实。我的方案很简单——在群晖上跑一个frpc容器开机自启、配置随走日常基本不用碰它所有内网服务从外面就能直连。这篇文章就把这套方案从头到尾摊开讲清楚覆盖镜像与版本选型、配置文件写法、容器创建细节、多实例路由、完整排查链路以及安全加固。适合已经有一台群晖、并且手头有一台带公网IP服务器的朋友。如果你还在纠结要不要上容器看完第一章应该就有答案。1. 为什么我坚持用frpc容器四个真实场景里的选择逻辑1.1 场景一人在外面想直接连回家里的SSH有段时间我经常在外网环境维护家里的Linux小主机过去要到路由器上做端口映射还得赌运营商没有把公网IP吞掉。实际用下来大多数家庭宽带的IP要么是大内网要么动态变化端口映射根本无从谈起。frpc容器解决的是原点访问问题内网设备上运行一个轻量客户端主动向外网服务器发起连接再由外网服务器把流量转回内网整个过程不需要公网IP也不需要改路由器。这种模式对SSH尤其友好。我习惯把SSH映射到公网服务器的高位端口出门用手机Termius连一下就能进去改配置延迟体感跟在内网差不多。1.2 场景二不想把管理界面完全交给QuickConnect中转QuickConnect作为群晖官方功能胜在零配置但数据要经过官方中转服务器速度上限摆在那里。遇到上传下载大文件、看监控回放这类场景转一圈之后延迟和吞吐都不够看。frpc容器跑起来之后我直接在电脑浏览器里访问https://nas.example.com流量走自己服务器的带宽和线路速度明显更快而且链路是自己可控的。1.3 场景三多台内网机器服务要一并开放只看一台NAS的话单一穿透就够了但我的网络里还有Linux服务器、摄像头NVR、打印机后台每台设备都开一个穿透客户端太碎。frpc的配置里天然支持多条转发规则一个容器就能把所有服务集中管理。实际配置时你可以在同一份配置文件里把NAS的5000端口、SSH的22端口、摄像头后台的80端口全部映射出去统一用域名或端口区分。这也是容器方案比单个套件更灵活的地方。1.4 为什么必须是容器套件与手动进程的对比群晖上跑frpc有三条路找第三方套件、手动下载二进制文件、用容器。我三条路都试过说下真实体验。第三方套件的问题在于更新迟缓frp服务端一升级、协议一调整老版本客户端就可能握手失败。手动下载二进制文件最灵活但群晖重启后进程不会自动起来得靠计划任务硬拉状态管理也麻烦。容器在这两者之间的平衡最好镜像版本跟随上游、重启策略交给Container Manager、日志统一在Docker界面里看改配置也就是挂载目录里替换一个文件的事。顺带一提容器化还有一个隐形优势——不影响群晖系统本身。frpc需要使用的库和运行环境全部封装在镜像里卸载时只需要删容器和镜像文件不会在系统里留下残留依赖。对于我这种喜欢让DSM保持干净的人这一点很关键。2. 镜像与运行环境准备先看清群晖的底子2.1 DSM版本与Container Manager的坑当前主流群晖已经用上了DSM 7.2套件中心里负责管理容器的是Container Manager早期版本叫Docker套件。如果你还在DSM 6.x界面虽然还叫Docker但操作逻辑差不多。真正要留意的是权限Container Manager默认以管理员权限运行如果用的是非管理员账号拉镜像和启动容器都会被拦下来。还有个小细节是Container Manager的注册表搜索受网络环境影响很大国内网络环境下经常搜不到想要的镜像。我一般直接去镜像站把Tag复制下来再在注册表设置里添加镜像加速地址或者干脆用docker pull命令行方式拉取。2.2 确认CPU架构x86_64、armv7还是aarch64群晖机型从低端到高端用的处理器完全不同。老款J系列很多是ARM架构主流Plus系列是x86_64新款也有不少是ARM64。frpc镜像的Tag必须跟CPU架构匹配否则容器会直接报exec format error。在群晖上确认架构很简单控制面板-信息中心-硬件或者打开终端执行uname -m。x86_64对应amd64aarch64对应arm64armv7l对应armv7。选镜像Tag时优先挑带完整架构名或者multi-arch的镜像比如snowdreamtech/frpc就是多架构同步推送我实测在x86_64和arm64机型上都能直接跑。2.3 镜像Tag选择禁止latest的实战理由很多人拉镜像习惯直接latest但frpc这种客户端工具恰恰不能这么做。原因有两个一是latest跟随上游更新frp协议升级后新版客户端可能不兼容老版本服务端导致容器起来但握手失败二是latest镜像无法锁定配置格式frp从0.52.0开始从INI转向TOML配置如果你按老教程写了frpc.ini拉到新版镜像大概率读不到配置。我的习惯是锁定一个具体的、经过验证的版本号。比如当前我在用的Tag是snowdreamtech/frpc:0.53.2服务端frps也是同一个版本两边配置格式都是TOML互不踩坑。2.4 拉取镜像与本地文件准备镜像拉取阶段打开Container Manager的注册表搜snowdreamtech/frpc选择对应Tag双击下载。如果界面搜索有问题可以开启SSH后用docker pull snowdreamtech/frpc:0.53.2拉取。拉镜像的同时建议在群晖File Station里建立一个专门的配置目录比如/docker/frpc后续把frpc.toml文件放在这里。为什么强调目录而不是散着放因为创建容器时挂载的是整个目录以后任何配置修改都只动这个目录下的文件重启容器即可生效清晰又省事。3. frpc配置文件把impossible变成可能的语法细节3.1 先厘清角色frps负责收frpc负责交frp是典型的C/S架构公网服务器上跑的是服务端frps内网群晖里跑的是客户端frpc。frpc启动后会主动连上frps告诉它我这有哪些端口可以对外开放frps收到请求后把这些端口绑定到自己的公网地址上。用户访问公网服务器的某个端口时frps把流量沿着既有隧道转给frpcfrpc再转发给内网的目标服务。所以配置文件的本质只有两件事告诉frpc服务端地址和认证信息以及定义要转发哪些内网服务。3.2 一份可用的frpc.toml配置拆解frp 0.53.0之后的配置推荐使用TOML格式基础配置长这样serverAddr your.public.server.com serverPort 7000 auth.method token auth.token replace-with-a-strong-random-token [[proxies]] name nas-ssh type tcp localIP 127.0.0.1 localPort 22 remotePort 6022 [[proxies]] name nas-web type tcp localIP 127.0.0.1 localPort 5000 remotePort 5000serverAddr和serverPort指向你公网服务器的域名和frps监听端口auth.token必须与服务端一致这是第一道安全关口。[[proxies]]是方括号嵌套的数组语法每个[[proxies]]块代表一条转发规则。name是规则名字在日志里会按这个名字区分type是转发类型localIP和localPort指定流量转发到内网哪个地址remotePort是公网服务器上对外开放的端口。3.3 从INI迁移到TOML的关键变化如果你以前用的是frp 0.51及更早版本可能写过这样的frpc.ini[common] server_addr 1.2.3.4 server_port 7000 token your-token [nas-web] type tcp local_ip 127.0.0.1 local_port 5000 remote_port 5000新版TOML的差异要特别注意下划线全部变成驼峰命名server_addr变成了serverAddrlocal_ip变成了localIP[common]段落被去掉了服务端信息直接写在顶层认证信息从token变成了auth.method加auth.token的嵌套结构。还有一个容易被忽略的点是老版的token字段在common下新版如果还写在顶层服务端会提示token缺失。我建议所有新部署直接使用TOML。网上大量教程还在贴INI格式按图索骥很容易在新版本上翻车。3.4 常见转发类型tcp与http的取舍frpc支持tcp、udp、http、https、stcp等多种类型。最常用的是tcp和http。tcp类型最简单任何基于TCP的协议都能映射比如SSH、RDP、群晖的文件传输端口。remotePort直接对应公网服务器上的一个端口访问公网IP:6022就能连回内网SSH。http类型则适合暴露Web页面。和tcp需要额外端口不同http类型可以通过域名区分多套Web服务配置里用customDomains指定域名[[proxies]] name nas-photo type http localIP 127.0.0.1 localPort 8080 customDomains [photo.example.com]这样photo.example.com会被frps识别并转发到内网的8080端口同一台公网服务器可以挂多个不同域名不需要为每个Web服务开一个新端口。需要说明的是http类型要求frps所在的公网服务器上对应域名的DNS已经解析到这台服务器并且在frps配置里允许http映射。4. 容器启动与网络模式一键背后的三处关键参数4.1 网络模式选择host还是bridge创建frpc容器时最重要的决定就是网络模式。群晖Container Manager提供bridge、host两个常用选项。bridge模式是默认的容器隔离网络容器有自己独立的虚拟网卡通过NAT访问外网。但这个模式下容器去访问宿主机局域网里的设备时需要额外配置网络或指定宿主机IP而且端口映射也要做一套比较绕。host模式则直接让容器共享宿主机的网络栈frpc看到的网络环境就是群晖本身的网络环境。这意味着配置里的localIP写成127.0.0.1就能访问群晖本机服务写成192.168.1.5就能访问局域网内其他设备无脑方便。frpc作为纯客户端没有隔离需求我直接选了host模式省掉所有端口映射的麻烦。这也是这类转发工具的普遍做法。4.2 用Container Manager创建容器的完整步骤以DSM 7.2的Container Manager为例完整步骤整理如下打开Container Manager进入镜像页面找到已拉取的snowdreamtech/frpc:0.53.2点击运行。容器名称填frpc勾选启用自动重新启动。点击高级设置在网络选项卡里勾选使用与Docker主机相同的网络。切到存储空间选项卡点击添加文件夹把File Station里准备好的/docker/frpc目录映射到容器内的/etc/frp目录。其他选项保持默认点击应用并确认。回到容器列表选中frpc容器进入日志页面看到类似login to server success的日志就说明链路通了。需要说明的是我选的这个镜像默认启动命令就是读取/etc/frp/frpc.toml所以不需要在创建时额外配置命令和参数。如果你用的镜像入口不是这样的需要在命令里覆盖为frpc -c /etc/frp/frpc.toml。这里强烈建议优先选择明确说明默认配置路径的镜像能省下不少试错时间。4.3 为什么重启策略要选容器停止时自动重启群晖上容器崩溃或意外停止的情况并不少见系统更新后重启、内存不足触发OOM、手动误停。Container Manager的启用自动重新启动本质上是把Docker的--restart策略设为unless-stopped容器只要不是被手动明确停止都会在退出后自动拉起来。我特意强调这个参数是因为frpc是远程访问的入口如果它挂了而你人在外面你连进群晖把它拉起来的机会都没有。自动重启是这条方案里最基础的保障绝对不能省。4.4 定时检测与自动恢复的补充方案自动重启覆盖的是容器退出后重新拉起但还有一种情况容器活着frpc进程内部的网络连接断了。这时候单靠Docker的重启策略是没用的。我的做法是在群晖计划任务里加一条每分钟执行的检测命令#!/bin/bash if ! docker ps --format {{.Names}} | grep -q ^frpc$; then docker start frpc fi每分钟检查一次容器是否存在如果没了就直接docker start。另外也可以配合服务端的keepalive配置让frpc与frps之间有心跳检测断线后由frpc自己重连。我实测下来frpc本身的断线重连机制已经比较可靠计划任务只是双保险但有了它人在外面能安心很多。5. 多frpc实例与多服务路由一套容器方案管住全部内网服务5.1 什么时候需要多个frpc实例大部分情况下一个frpc容器加多条[[proxies]]规则就够了。但遇到下面几种情况单一实例反而别扭内网有多个设备各设备上都有要开放的服务但不想把所有配置堆在同一份文件里。需要同时连接两个不同的frps服务端比如一台是自己的服务器一台是朋友的服务器。某条转发规则频繁出问题想单独重启它而不影响其他规则。多实例并不复杂本质就是创建多个容器每个容器挂载各自的配置目录命名区分开即可。我实际部署时用了一个frpc-home容器管理NAS本机服务一个frpc-camera容器单独管理摄像头后台重启哪个都不影响另一个。5.2 配置多proxy的聚合方式即使只有一个实例多个服务也可以集中管理。同样是[[proxies]]数组按顺序往后追加就行。我常用的一套完整配置大概长这样serverAddr your.public.server.com serverPort 7000 auth.method token auth.token your-strong-token [[proxies]] name dsm-ssh type tcp localIP 127.0.0.1 localPort 22 remotePort 6022 [[proxies]] name dsm-web type tcp localIP 127.0.0.1 localPort 5000 remotePort 5000 [[proxies]] name surveillance-rtsp type tcp localIP 192.168.1.10 localPort 554 remotePort 8554 [[proxies]] name video-station type http localIP 127.0.0.1 localPort 5000 customDomains [video.example.com]注意surveillance-rtsp这条规则的localIP写的是局域网内另一台设备的IP这体现了host模式的好处——frpc可以直接把流量转发到内网任意一台设备的任意端口不只是群晖自己。5.3 多实例的资源占用与控制有人可能担心跑多个frpc容器会把群晖资源吃满。这个担心可以放下frpc本身是一个非常轻量的Go二进制程序我在DS920上跑两个frpc实例内存占用加起来不到40MBCPU日常基本是0%。真正占资源的是那些被转发出去的业务服务frpc只做流量搬运不会额外增加太多负载。5.4 端口冲突排查群晖套件抢端口问题多实例、多规则带来的典型问题是端口冲突。群晖自带套件占用了大量端口最常见的是5000DSM管理、5001HTTPS管理、80Web Station、443Web Station HTTPS。如果你把remotePort设成和群晖本机一样的端口frpc启动时会报port already used因为host模式下frpc看到的端口空间和群晖完全一样。解决方式也很简单remotePort尽量映射到公网服务器上的非常见端口比如DSM管理端口5000在公网侧映射到6000本地SSH映射到6022。这样既避免群晖本地冲突也少一些被扫描器盯上的概率。如果你在公网服务器端已经用Nginx或Caddy托管了几套Web服务那http类型的转发就要注意customDomains不要和服务端已有站点域名重叠。6. 排错实录从连不上到服务异常的完整排查链路6.1 排查链路第一步容器是否在运行frpc出问题时最常见的情况是容器没起来或者起来了连不上服务端。我的排查顺序是从底层往外层推先看容器状态docker ps -a | grep frpc如果状态是Exited说明容器启动失败打开日志看原因docker logs frpc --tail 100如果状态是Up但服务不可用多半是配置问题或者frps服务端问题继续往下查。6.2 认证失败与连接拒绝的日志特征frpc的日志信息非常直白关键日志我已经整理成了对照表日志关键字含义常见原因处理方式login to server success登录成功无链路正常connection refused连接被拒绝localIP/localPort指向的本机或内网服务没起来检查内网目标服务是否在运行no route to host无法路由到目标localIP写错网段或防火墙拦截改用127.0.0.1或真实内网IPauth token error认证失败frpc与frps的token不一致两端配置统一后重启frpcport already used端口被占用remotePort与公网服务器已有端口冲突更换remotePort[xxx] start error代理启动失败协议类型配置错误或域名冲突检查type与customDomains6.3 穿透后端口不通的典型排查最让人头疼的是日志显示一切正常、login to server success也出来了但外部访问还是不通。这种情况我遇到过三次最终定位到的原因各不相同按出现频率排序如下。第一次是云服务商的安全组。公网服务器的防火墙规则里只放行了22、80、443这些常规端口frpc用的7000端口和映射出来的6022端口根本没放行。在云控制台安全组里补上端口规则后立刻通了。第二次是frps服务端配置文件问题。服务端绑定的bindPort虽然是7000但默认只监听0.0.0.0如果被改成了127.0.0.1外部就连不上。检查frps.toml里的bindAddr确认没有被错误设置。第三次是公网服务器的系统防火墙。有些Linux发行版默认开着firewalld或ufw即使云安全组放行了系统防火墙还是会拦。需要执行sudo firewall-cmd --add-port7000/tcp --permanent sudo firewall-cmd --add-port6022/tcp --permanent sudo firewall-cmd --reload整个排查链路走下来百分之九十的问题都出在这几层。我的建议是每次改完配置后先在公网服务器上本地测试端口是否监听再回到内网检查frpc日志最后才去考虑外部访问。6.4 配置改了不生效镜像缓存与TOML语法的隐形坑还有一类问题是改配置没用日志还是老样子的报错。这通常不是配置文件没保存而是没有重启容器——frpc只在启动时读取配置文件修改后必须docker restart frpc。另一个坑来自TOML语法本身。TOML对字符串要用引号包裹数字端口不能加引号数组里的每一项要用逗号分隔。很多人把remotePort 6022写成remotePort 6022别的工具可能无所谓frp会直接报类型错误。修改配置后我习惯先在本地用frpc verify -c frpc.toml这类命令做语法校验如果镜像里有frpc命令或者直接通过容器执行校验docker exec -it frpc frpc verify -c /etc/frp/frpc.toml校验通过再重启容器能省掉大量反复重启的无用功。7. 安全加固与自我修养容器跑起来之后还要做这些7.1 Token安全与白名单frps服务端侧的配合frpc容器本身是安全的它只是与frps建立一条加密通道真正的安全边界在frps。frps侧除了设置强token还应该在配置里加上允许映射的端口白名单避免每个拿到frpc配置的人都往公网服务器上开启任意端口。frps.toml中可以用allowPorts限定客户端能映射的端口范围bindPort 7000 auth.method token auth.token replace-with-a-strong-random-token allowPorts [ { start 6000, end 6100 }, { start 8554, end 8554 } ]这样即使frpc配置里写了其他remotePortfrps也会拒绝。生成强token的命令我常用openssl rand -base64 24输出的一长串字符直接填到两端的auth.token里手工输入的复杂密码没必要机器随机生成的才是真的好记又难猜。7.2 只暴露必要端口别把整个NAS塞进公网内网穿透最大的风险不是frp本身而是人。把DSM管理端口、SSH、数据库端口全部映射到公网上等于把自己的大门钥匙挂在门口。我的原则是能不映射就不映射。SSH端口能映射但公网侧要用高位端口并且frps侧的allowPorts必须限制群晖管理界面如果一定要远程访问建议只映射给固定IP白名单或者配合frps的客户端IP白名单相册、Drive这类大流量服务优先考虑只暴露给少数可信域名。另一个容易被忽略的点是frpc配置里那些只在本机使用的服务比如127.0.0.1:5000如果并不需要远程访问就应该从配置里删掉而不是图方便全部映射出去。每多一条映射就多一个暴露面。7.3 日志观察与镜像更新frpc跑起来之后日常并不需要频繁干预但日志值得定期扫一眼。我一般每周登录群晖看一眼docker logs frpc --since 168h重点看有没有异常IP尝试连接、有没有大量重连记录。frpc日志本身很安静除了启动和停止正常情况下就是定期的心跳日志如果发现异常刷屏多半是网络质量差或被人扫描。镜像更新方面建议跟随frp上游的小版本更新。更新流程不复杂拉新Tag镜像创建新容器挂载同一份配置目录确认日志正常后删除旧容器。因为有配置目录独立挂载这个前置设计升级过程几乎无感。7.4 长期运行观察一个反面教训最后分享一个真实反面教训。有段时间我发现frpc容器每天凌晨固定时间重启一次查日志没看到异常后来定位到是群晖的计划任务里有一个系统备份脚本内存占用过高触发OOM把frpc连带杀了。Docker的重启策略把它拉起来但导致当天早些时候的远程连接全部中断。这个案例给我的启发是容器跑得稳不稳不只看容器本身还要看宿主机上其他服务的资源占用。群晖上跑的套件越多、计划任务越重这类连带问题就越可能出现。如果你的frpc容器也出现规律性重启先检查宿主机内存和那段时间在跑的任务别一头扎进容器日志里找原因。整个方案用下来frpc容器在群晖上属于存在感很低但非常顶用的一类组件。它不会让NAS变慢配置文件清晰多服务扩展也方便只要按这套流程走一遍后续基本就是无感运行。如果你打算上手我建议第一次只穿透一个SSH服务验证链路通了之后再加其他服务从简单到复杂踩坑成本会低很多。
返回列表