Linux内网穿透实战:frp自建隧道从原理到安全加固
2026/9/13 14:57:50 网站建设 项目流程

先把话放前面:这个标题里的“liunx”,我猜是想写Linux,这种拼写错误在搜索引擎里其实很常见,我也经常看到有人这么敲,所以这篇直接按Linux来写。内网穿透这活儿,说起来不复杂,但真要在Linux系统上把它跑通、跑稳、跑得让人放心,里面还是有不少门道的。这篇文章我不会只丢一个工具链接给你,而是把原理、选型、搭建、踩坑、加固整个链路都过一遍,适合正在折腾VPS和家里/公司内网机器的朋友,也适合那些 SSH 都连不上、但急需远程操作内网服务器的运维新手。

先说清楚它到底解决了什么问题:你有一台Linux机器,放在公司机房或者家里路由器后面,没有公网IP,外面的人访问不到。内网穿透就是在你有一台公网服务器(一般叫VPS)的前提下,让公网用户通过VPS的中转,反过来连到内网里的这台Linux机器。用一句话概括:外网访问内网,靠的不是“打洞”,而是“拉一根反向的线”。

1. 先弄清楚一个前提:你的服务为什么不能被公网访问

很多人一上来就急着装工具,结果连“访问不了”这件事的根本原因都没搞清楚。我建议先花几分钟把网络模型理顺,后面排查问题会快得多。

1.1 内网的隔离逻辑:NAT与私有IP

绝大多数家庭宽带和公司网络用的是私有IP段,典型的就是192.168.x.x、10.x.x.x、172.16.x.x。这套地址只在内网有效,出了路由器就被丢弃了,公网上的设备根本没法直接寻址到你的机器。原因很简单,IPv4地址资源早就枯竭了,运营商会给每个家庭/企业只分配一个公网IP,甚至很多地方分配给你的还是个大内网IP,也就是运营商层面的NAT,连公网IP都不是。

在这种结构下,你内网机器上的SSH服务(22端口)、Web服务(80/8080端口),默认只监听在内网接口上,公网用户发起请求时,数据包到了你家路由器的WAN口就被拦下来了。路由器不知道这个数据包该转发给哪台内网机器,除非你手动做端口映射(Port Forwarding),但即便做了映射,运营商不给你公网IP,映射也是白搭。

1.2 穿透的本质:拉一根反向连接

既然外网主动连不进来,那就换个思路,让内网机器主动往外连。这就是所有内网穿透工具的底层逻辑:内网机器上的客户端进程,主动和公网VPS上的服务端进程建立一个长连接。这个连接建立之后,公网用户访问VPS的某个端口,VPS就把这个流量顺着这条已有的长连接“塞”给内网客户端,内网客户端再把流量转发给你本地的SSH、Web或者其他服务。

打个比方,你家住在一个没有门牌号的小区(内网),外人找不到你。但你有一部能打给物业总机的电话(长连接),你先拨通了物业(VPS),说“我要找张三”。之后外面的人想找你,就打电话给物业,物业再通过这通已经接通的电话转告你。整个过程里,你不需要有固定门牌号,只需要保证那通电话(长连接)不断。

理解了这个模型,后面配置FRP时你就会知道,为什么服务端要有个bindPort(控制端口),为什么客户端要配置serverAddr和serverPort,为什么隧道建立后外部流量走的是remotePort。本质上就是两条通道:一条是控制信令通道(长连接),一条是数据转发通道(公网端口到内网端口)。

2. 内网穿透工具怎么选:ngrok、cpolar、frp、樱花Frp的取舍

市面上的内网穿透方案不少,我先列一下主流几个,再讲我怎么选。懒人可以直接跳到结论。

维度ngrokcpolar樱花Frpfrp自建
是否需要公网服务器不需要不需要不需要需要
自定义域名/端口免费版受限部分付费部分受限完全可控
数据经过第三方
免费带宽/速度有限制有限制看节点线路取决于VPS带宽
长期稳定性一般一般一般取决于自己服务器
上手难度

2.1 托管类工具:ngrok、cpolar

ngrok是老牌选手,注册账号、下载客户端、运行命令就能拿到一个公网地址。它最大的优势是零门槛,内网机器不需要有公网IP,也不用自己买VPS。缺点也很明显:免费版域名随机,每次启动可能变,带宽被限制,传输敏感数据时你完全不知道数据经过哪些节点。国内直连ngrok的服务器还经常抽风,体验不稳定。

cpolar是国内团队做的,中文文档友好,命令行交互清晰,适合快速做演示、调试Webhook回调这类场景。它也支持创建TCP隧道,临时把内网的SSH暴露出去。同样的问题:免费额度有限,如果要稳定的域名和更大的带宽,得付费。作为临时调试工具,cpolar和ngrok都不错,但作为生产级长期方案,我不太推荐。

2.2 半托管类:樱花Frp

樱花Frp严格来说是一个frp的公益托管平台,你在它的网站上创建隧道,下载它的客户端(也是基于frp改的),就能把内网服务穿透出去。它对个人用户很友好,甚至提供了免费线路,适合个人博客、游戏联机、临时演示这类场景。但因为节点资源是共享的,高峰期速度不保证,而且你依赖第三方的稳定性。它适合不折腾、不想买VPS的人,不适合对数据链路有掌控要求的场景。

2.3 自建首选:frp(我最终的选择)

frp是开源项目,GitHub上星标极高,Go语言写的,单文件部署,跨平台。它支持TCP、UDP、HTTP、HTTPS,还能做STCP、XTCP这种点对点加密代理,能力非常全面。自建frp需要一台有公网IP的VPS,但好处是数据路径完全由自己掌控,端口、域名、带宽、鉴权全都自己说了算。

我最终选择frp,原因有三点。第一,VPS本来就常年开着,闲置带宽不用白不用。第二,frp的服务端frps和客户端frpc都是单二进制文件,部署就是复制文件的事,没有任何依赖。第三,社区活跃,遇到问题搜一搜基本都有答案。如果你手里已经有了一台公网Linux服务器,不管配置多低(1核512M就够跑frps了),自建frp都是长期最稳的方案。

3. 基于frp自建穿透服务:从VPS准备到客户端配置的完整搭建

下面进入正题,我用最典型的场景来演示:内网一台Linux主机跑着SSH(22端口)和一个Web服务(8080端口),我希望能从公网SSH登录到这台机器,也能通过VPS访问到内网的Web服务。

3.1 准备阶段:VPS、域名、端口规划

你需要准备的东西:

  • 一台有公网IP的Linux VPS(我以Ubuntu 22.04为例,CentOS也同理)
  • 一台内网Linux主机(目标机器,可以是Ubuntu、Debian、CentOS)
  • 一个域名(可选。如果你只想穿透SSH/TCP,不需要域名;如果要穿透HTTP,有个域名体验会舒服很多,免去访问时需要带端口的麻烦)

端口规划是很多人忽略的一步。我建议按下面的原则分配:

  • frps控制端口:不使用默认的7000,改成比如17000,减少被扫描器盯上的概率
  • SSH代理remotePort:比如16022,避免和VPS自身的22冲突
  • HTTP代理vhost端口:比如18080,避免和VPS上已有的Nginx/Web服务占用的80端口冲突
  • frp Dashboard端口:比如17500,管理页面用

表格整理一下我用的端口分配:

用途端口说明
frps控制通道17000frpc连接frps用
SSH远程端口16022公网 ssh -p 16022 访问内网机器
HTTP虚拟主机端口18080配合域名做Web穿透
Dashboard管理页17500frp自带的监控面板

3.2 服务端部署frps

去GitHub的frp发布页下载最新版,注意选对架构。x86_64的机器选linux_amd64,ARM64的选linux_arm64。用uname -m确认一下,树莓派或者部分国产ARM服务器经常在这里翻车。

# 以frp 0.61.1版本为例 wget https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz tar -zxvf frp_0.61.1_linux_amd64.tar.gz cd frp_0.61.1_linux_amd64 sudo cp frps /usr/local/bin/ sudo mkdir -p /etc/frp sudo cp frps.toml /etc/frp/

然后编辑服务端配置文件 /etc/frp/frps.toml:

bindAddr = "0.0.0.0" bindPort = 17000 # 认证令牌,frpc连接时必须携带,相当于通信密码 auth.method = "token" auth.token = "请改成足够长的随机字符串" # 管理面板,方便看连接状态和流量 webServer.addr = "127.0.0.1" webServer.port = 17500 webServer.user = "admin" webServer.password = "改成强密码" # HTTP穿透的虚拟主机端口 vhostHTTPPort = 18080

注意我特意把webServer.addr设成了127.0.0.1,而不是0.0.0.0。原因是Dashboard管理页面不应该直接暴露到公网,如果你想看面板,用SSH隧道或者下面提到的安全组策略做限制,而不是裸奔。

启动服务端验证一下:

frps -c /etc/frp/frps.toml

看到类似“frps started successfully”的日志就说明服务端起来了。这时候VPS的17000端口应该处于监听状态,用ss -lntp | grep 17000确认。

3.3 客户端部署frpc

在内网Linux机器上执行同样的下载和解压,注意这次我们只需要frpc和frpc.toml:

wget https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz tar -zxvf frp_0.61.1_linux_amd64.tar.gz cd frp_0.61.1_linux_amd64 sudo cp frpc /usr/local/bin/ sudo mkdir -p /etc/frp sudo cp frpc.toml /etc/frp/

编辑客户端配置 /etc/frp/frpc.toml:

serverAddr = "你的VPS公网IP" serverPort = 17000 auth.method = "token" auth.token = "和frps.toml里保持一致" # 启动时如果连不上服务端不要立即退出,而是继续等。 # 这个参数对开机自启非常关键,避免网络还没就绪时服务直接挂掉。 loginFailExit = false # 第一个隧道:SSH [[proxies]] name = "ssh-internal" type = "tcp" localIP = "127.0.0.1" localPort = 22 remotePort = 16022 # 第二个隧道:内网Web服务 [[proxies]] name = "web-internal" type = "http" localIP = "127.0.0.1" localPort = 8080 customDomains = ["blog.example.com"]

启动客户端:

frpc -c /etc/frp/frpc.toml

3.4 连通性验证

验证SSH穿透是否生效,在任意一台公网机器上执行:

ssh -p 16022 root@你的VPS公网IP

如果能登录到内网机器,说明TCP穿透链路已经通了。验证Web穿透,需要先确保域名解析到了VPS的IP,然后访问 http://blog.example.com:18080,正常情况下应该能看到内网Web服务返回的页面。

这里我解释一下Web穿透的流量路径,很多人第一次看会绕晕:

  • 用户访问 blog.example.com:18080
  • DNS解析blog.example.com,得到VPS的IP
  • 请求到达VPS的18080端口,frps收到
  • frps根据HTTP请求里的Host字段(blog.example.com)匹配到名为web-internal的隧道
  • frps把这个请求打包,通过17000的长连接发给内网机器的frpc
  • frpc将请求转发给本机127.0.0.1:8080的Web服务
  • Web服务的响应再原路返回

整个过程对用户来说是透明的,他只会觉得这个网站就在VPS上。

3.5 为什么remotePort不能随意乱选

很多人随手把remotePort配成80、443这种常用端口,然后在VPS上发现起不来。原因很直接:VPS本身的Nginx、Apache或者系统服务已经占用了这些端口。既然frp的端口规划是“公网侧端口”,你就要把VPS看成一个独立的公网主机,它的端口资源和内网机器完全隔离。所以配置remotePort之前,先用ss -lntp看看目标端口在VPS上是否已被占用。

同理,customDomains指向的域名,如果你想用80端口访问Web服务,那你的VPS上必须能腾出80端口给frps的vhostHTTPPort。如果80被Nginx占了,要么改Nginx做反向代理到vhostHTTPPort,要么就别用80端口,访问时带着端口号。

4. 踩坑实录:穿透服务跑不起来时,我是怎么一步步定位的

以下是真实遇到过的高频问题,我按排查链路写出来。这几个坑,我敢保证你大概率会踩到至少一个。

4.1 第一坑:云安全组没有放行端口,导致“明明配置全对,就是连不上”

这是最隐蔽也最常见的问题。你的frps起来了,ss也能看到端口监听,但外部就是连不上。原因是阿里云、腾讯云、AWS这些云厂商的安全组策略默认只放行22、80、443等少量端口,其他端口一律丢弃。你敲了ss -lntp看到端口在监听,那是在服务器内部视角,而安全组是在云端网络层面拦截的。

排查思路:先别急着怀疑frp配置。从你的本地机器上用telnet或者nc测试一下VPS端口通不通:

nc -vz 你的VPS公网IP 17000 nc -vz 你的VPS公网IP 16022

如果显示Connection refused,并且你确认frps进程活着,那大概率是安全组的问题。登录云控制台,找到安全组规则,放行17000、16022、18080这三个入口方向端口。放行之后重新测试,这是99%的“服务端明明正常但客户端连不上”的答案。

4.2 第二坑:版本更新导致配置格式不兼容,老教程直接抄会报错

frp在0.52.0版本之后,配置格式从INI格式换成了TOML格式。网上大量老教程还在讲 [common] server_addr = xxxx,如果你直接用新版frp去跑老配置,一定会报错。新版应该是 serverAddr = xxxx 这种更简洁的风格。

而且代理配置的写法也变了:老版是用 [ssh] type = tcp 这种带中括号的段落独立定义,新版是用 [[proxies]] 数组形式,然后name字段做区分。我在这篇博文里写的就是新版格式,建议你下载最新版之后,直接参考官方release包里的frps.toml和frpc.toml模板,不要盲目复制网上三年前的配置。

排查思路:启动frpc时报类似“toml: line X: key 'server_addr' is not expected”的错误,基本就是配置格式问题。到下载的目录里看官方的frpc.toml示例,对着改字段名就行。

4.3 第三坑:token不一致,握手静默失败

frps和frpc都配置了auth.token,只要有一个字符不一致,连接就会握手失败,但诡异的是frps日志里可能只显示一条不太起眼的信息,而frpc一直报“login to server failed: EOF”或者“connection closed”。我第一次遇到这个坑时,反复检查了防火墙和端口,最后才发现是两个配置文件的token里有个空格没删干净。

排查思路:先去frps日志里看有没有token验证失败的记录,然后在frpc里执行frpc -c frpc.toml -v,用详细模式启动,它会打印握手过程,token校验失败时会有明确的提示。另外建议token直接用openssl rand -base64 24生成,不要手敲,避免复制粘贴时带上换行符或空格。

4.4 第四坑:内网服务的localIP写成了别的机器IP

localIP这字段,指的是你内网那台目标机器的IP。如果你frpc和SSH服务跑在同一台机器上,写127.0.0.1完全没问题。但如果你在内网一台跳板机上跑frpc,想穿透另外一台数据库服务器,那localIP就得写那台数据库服务器的内网IP,比如192.168.1.50,而且确保跳板机和数据库服务器之间网络通、防火墙放行。

这个坑的诡异之处在于,frpc启动时完全不校验localIP和localPort是否可达。配置里随便写一个不存在的内网地址,frpc照样显示代理启动成功,但外部访问就是不通。所以排查时要看frpc日志,如果出现“connect to local service error”之类的记录,赶紧检查localIP和localPort是不是写错了,或者服务本身没起来。

4.5 第五坑:systemd守护进程的管理细节

很多人直接nohup ./frps -c frps.toml & 就跑了,看起来没问题,但服务器一重启,服务就没了。内网穿透这种东西,连接断了就相当于远程失联,所以必须交给systemd托管,并且设置自动重启。

服务端和客户端的unit文件写法如下,先服务端 /etc/systemd/system/frps.service:

[Unit] Description=FRP Server After=network.target [Service] Type=simple ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target

客户端 /etc/systemd/system/frpc.service:

[Unit] Description=FRP Client After=network.target [Service] Type=simple ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml Restart=always RestartSec=5s [Install] WantedBy=multi-user.target

然后执行:

sudo systemctl daemon-reload sudo systemctl enable --now frps sudo systemctl enable --now frpc

注意客户端我用了Restart=always,服务端用Restart=on-failure。原因是客户端如果因为网络抖动退出,重启策略必须是无条件的,而服务端正常停止时没必要反复拉起。

5. 从“能跑”到“好用”:多客户端接入、系统守护与安全加固

穿透链路一旦通了,你会发现它的用处绝不只是SSH。但“能跑”和“生产环境能用”之间还有一段路要走,我给你列一下我实践后觉得必须做的几件事。

5.1 systemd守护不再赘述,但记得处理启动顺序

上面已经把unit文件写清楚了,这里补充一个容易忽略的点:如果你的内网机器设置了开机自启,但网络启动较慢,frpc可能在网络就绪前就开始尝试连接服务端,导致启动失败。除了配置loginFailExit = false之外,还可以在unit文件里给frpc加一句:

After=network-online.target Wants=network-online.target

这样systemd会等网络完全就绪后再拉起frpc,配合Restart=always,基本能做到开机即恢复。

5.2 多客户端接入:怎么在同一台VPS上区分多台内网机器

frp的设计天然支持多客户端,也就是说,一个frps可以同时服务多个内网机器。你只需要在每台内网机器上部署frpc,配置相同的serverAddr和token,但确保每台机器里的[[proxies]] name和remotePort不重复就行。

例如,一号内网机器用:

  • 代理名 ssh-host1
  • remotePort 16022

二号内网机器用:

  • 代理名 ssh-host2
  • remotePort 16023

外部访问时,ssh -p 16022和ssh -p 16023分别连到不同机器。如果代理名重复,frps会报错;如果remotePort重复,端口会冲突。这个在规划阶段就要做好记录,机器多了之后很考验命名规范。

5.3 安全加固:别把SSH裸奔到公网

穿透的本质是把内网端口暴露到公网,这意味着你的SSH直接面对着全网扫描器。如果不做防护,暴力破解是早晚的事。我的建议是至少做以下几件:

第一,frp的token一定要用高强度随机串,并且定期轮换。token一旦泄露,别人就能利用你的VPS做端口映射,这是很危险的事。

第二,frps面板不要暴露在公网。我已经把webServer.addr设成了127.0.0.1,如果你非要外部访问面板,建议先SSH登录VPS,再通过ssh -L 17500:127.0.0.1:17500做本地端口转发来看,而不是直接把17500端口放到公网。

第三,在frps.toml里限制能够射出的远端端口范围,防止某个frpc被攻破后,攻击者随意占用VPS端口:

allowPorts = [ { start = 16022, end = 16039 }, { start = 18080, end = 18080 } ]

第四,如果穿透的只是自己日常用的SSH,建议改成密钥登录,把密码登录关掉。这一步和frp无关,但它是你内网机器暴露到公网后的第一道防线。

第五,考虑用STCP类型替代TCP类型。STCP的逻辑是,服务端只做信令握手,流量走两个frpc之间的点对点加密通道。也就是说,SSH流量并不真正经过VPS中转,安全性更高,速度也可能更快。代价是,你的访问端(比如你自己的电脑)也得装frpc并配置visitor。如果你能接受这个成本,STCP是SSH穿透最好用的方式。

5.4 常见进阶玩法:把MySQL、RDP或者内网网站都穿透出去

frp不只是给SSH用的。我之前就把公司内网的MySQL(3306)穿透到了公网,方便在家用Navicat连数据库排查问题。配置方式和SSH几乎一样:

[[proxies]] name = "mysql-internal" type = "tcp" localIP = "192.168.1.100" localPort = 3306 remotePort = 13306

Windows远程桌面也同理,把RDP的3389端口映射到公网,remotePort设一个不常用的比如13389,本地mstsc连接时填VPS的IP加端口就能远程操作内网Windows机器。

如果你有多台内网机器提供不同Web服务,还可以结合Nginx做虚拟主机分发。比如frps的vhostHTTPPort设成18080,然后VPS上的Nginx监听80端口,把不同域名的请求反向代理到127.0.0.1:18080。这样外部访问就能用标准的80端口,省去带端口的麻烦,多个域名还能各自指向不同内网服务。

5.5 监控与日志:别等连不上才想起来看

frp自带的Dashboard我建议必须开,哪怕只是内网访问。它能显示当前有多少个frpc在线、每个代理的连接数、流量占用,方便你快速判断是不是有人占满了带宽。服务端日志默认会往标准输出写,用journalctl -u frps -f可以实时查看。客户端日志也一样,本地机器上journalctl -u frpc -f。遇到问题先看日志,远比瞎猜配置高效得多。

另外,顺手加个每日日志轮转或者定期清理journal保存大小,避免VPS的小硬盘被日志塞满。命令很简单:

sudo journalctl --vacuum-size=100M

这句可以写进crontab,每周跑一次。

我在实际部署中用这套方案已经稳定跑了一年多,最深的体会是:内网穿透并不是一个“装完就忘”的工具,它会持续暴露在外网环境下,所以从一开始就要把端口规划、安全加固和监控体系做好。最后再分享一个实用小技巧:每次改完frp配置,先只跑一个最简单的TCP隧道验证链路,成功之后再往上叠加其他代理。这样一旦出问题,你永远知道是链路的问题还是新加代理的问题,排查速度会快很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询