☰
frp内网穿透全攻略:原理、配置与安全加固实践
2026/10/2 22:33:27 网站建设 项目流程

做项目最怕的不是需求复杂,而是人坐在办公室,服务器却在客户机房,生产环境出问题只能干瞪眼。这种时候,frp这种内网穿透工具就成了我工具箱里出场率最高的东西。frp全称Fast Reverse Proxy,是一个开源的反向代理工具,核心功能是把内网机器的端口“搬运”到一台有公网IP的服务器上,让外部流量能够绕过NAT和防火墙策略,通过中转服务器访问内网服务。

这篇东西主要写给两类人:一类是经常要出差连公司电脑、远程设置家里NAS、临时给客户演示内网页面的运维和开发者;另一类是做安全方向、需要在拿到授权后做内网环境测试的同学。frp的优点很直接:部署简单、一个二进制文件就能跑,性能也不错,还支持TCP、UDP、HTTP、HTTPS、STCP等多种代理类型,基本覆盖了日常会碰到的远程访问场景。接下来我把从服务端到客户端、从基础配置到安全加固的完整过程拆开讲清楚。

1. 先用大白话讲清楚frp到底解决了什么问题

1.1 为什么你会遇到“内网穿透”这个需求

先看一个最常见的场景。我在办公室连着一台内网Linux服务器,公司的出口是一个公网IP,路由器做了NAT。出差住酒店时想SSH登录这台机器,直接连是连不上的,因为你根本不知道流量该往哪儿走:内网服务器没有公网IP,路由器也不会把外部连接转给这台不相关的机器。防火墙策略在这里扮演的角色也一样,本质是“默认拒绝来自不信任区域的连接”。

frp的解决思路并不神秘:既然外网没法直接访问内网机器,那就找一台两边都能访问的公网服务器当中转。公网服务器上跑frps(frp服务端),内网机器上跑frpc(frp客户端),frpc主动向frps发起连接,建立起一条长连接的加密隧道。外部用户去连公网服务器的某个端口时,frps把流量从这条隧道原样转发给内网机器的frpc,frpc再把流量交给本地目标端口。整个过程对流经的服务来说是透明的,SSH、RDP、数据库客户端都无感知。

这里有个关键点:frpc发起的是“出站连接”,方向是从内网到公网。大多数防火墙策略主要拦截外网主动进来的流量,对出站连接的限制相对宽松,这就是frp能打通访问通道的原理。有些环境出站限制也很严格,那就只能配合代理或者白名单手段,后面我会单独讲。

1.2 frp和同类工具怎么选

市面上做内网穿透的工具有不少,每一类都有各自的适用场景。我把常用的列个表对比一下,方便你根据自己情况选。

工具原理优点缺点适合场景
frp自建服务端+客户端反向代理功能全、协议多、可深度定制需要一台公网服务器长期稳定的远程访问、自建穿透节点
ngrok类似frp,官方有SaaS服务零部署,一条命令就能用免费版域名随机、速度不稳定临时演示、联调
ZeroTier/TailscaleP2P虚拟组网不依赖中心服务器就能直连公司网络策略严时可能连不上多台设备组成私有网络
SSH反向隧道利用OpenSSH转发功能系统自带,无需额外装客户端隧道断了不会自动重连应急登录、临时开个端口

我的习惯是:正式环境一律用frp,因为它功能最完整——支持TCP/UDP/HTTP/STCP多种模式,有Dashboard面板可以看连接状态,还能做端口白名单限制。ngrok适合现场给客户演示的时候临时开一个隧道,演示完就关,省事。如果是自己家里的几台设备要组网,ZeroTier更省心,但到了公司这种出口对UDP 限制比较严格的环境,反而可能不如frp稳。

2. 架构与核心原理:一条隧道是怎么打通的

2.1 frp的整体流程拆解

frp的工作流程可以分成四步,理解了这个流程,后面排错会快很多。

第一步是frpc主动连接frps。frpc启动后,会根据配置文件里的serverAddr和serverPort去连接公网服务器的frps,完成握手和认证。注意这里是frpc主动出站,不是frps去连内网机器,这是整个方案能成立的前提。

第二步是frps验证身份。双方配置里的token必须一致,否则连接直接被拒绝。从新版开始frp还支持TLS加密传输,可以防止token和业务流量在网络传输过程中被截获。

第三步是端口映射注册。frpc在配置里声明要建立哪些代理,比如“把本地22端口映射到公网服务器的6222端口”,frps收到请求后会在公网服务器上监听6222端口。

第四步是流量转发。外部用户访问公网服务器的6222端口时,frps通过已经建立的隧道,把流量转发给内网机器的frpc,frpc再把流量送给本机22端口。返回路径是反过来的。整个过程中,frps和frpc之间始终只有一条长连接,多路业务流量在这条连接上复用传输。

2.2 新版TOML配置格式和旧版INI的区别

如果你以前用过frp,可能记得老版本用的是ini格式的配置文件,大概长这样:

[common] server_addr = 1.2.3.4 server_port = 7000

从frp 0.52版本开始,官方把配置格式迁移到了TOML。新格式和旧格式差异很大,有两个变化最需要关注:一是配置项名字从下划线风格改成了驼峰风格,比如server_addr变成了serverAddr;二是客户端里的代理定义从多个[proxy-xxx]小节,改成了在proxies数组里用列表形式统一定义。

我见过不少同学在网上找教程,复制了一堆旧版配置,结果新版frpc启动直接报错,多半就是格式问题。现在官方release页面下载的稳定版都是TOML格式,建议直接用新版。如果实在有老项目还跑在ini格式的上,可以看frp官方文档里提到的自动转换逻辑,但没必要纠结兼容旧版。

3. 服务端frps部署与配置全流程

3.1 准备工作:一台有公网IP的服务器

frps需要跑在一台能被公网访问的服务器上,这就意味着这台服务器必须有公网IP,且端口不能被云平台的“安全组策略”挡住。云服务器的安全组和系统内防火墙是两个层面的东西,安全组相当于云厂商在机房网络设备上的访问控制策略,系统防火墙则是操作系统层面的策略,两者同时生效,有一个没放行就连不上。

服务器配置不用太高,1核1G的入门款就能扛住不少并发连接,因为frp本身只是做转发,计算开销很小。带宽倒是要留意,如果穿透的流量是远程桌面或者文件传输,带宽决定了体验。操作系统我推荐Debian或者Ubuntu。原因很简单:系统干净、默认防火墙规则少、遇到问题网上资料多。

3.2 下载安装与服务端完整配置

去GitHub的frp release页面找最新的linux-amd64版本,注意服务器架构。绝大多数云服务器是x86架构,用amd64版本没问题;如果是ARM架构的服务器,比如某些国产芯片或者树莓派,要下arm64版本,下错了会报exec format error。

下载解压的命令大致如下:

wget https://github.com/fatedier/frp/releases/download/v0.61.0/frp_0.61.0_linux_amd64.tar.gz tar -zxvf frp_0.61.0_linux_amd64.tar.gz mv frp_0.61.0_linux_amd64 /usr/local/frp

解压完的目录里有frps和frpc两个二进制文件,还有对应的toml配置示例文件。服务端只用到frps和frps.toml,客户端的可以不管。

下面是一个生产环境可用的frps.toml完整配置:

bindPort = 7000 auth.method = "token" auth.token = "Kj8mN2pQx7Lv9Tr5Wd4Yh6Ue1Ba3Cs0" webServer.addr = "0.0.0.0" webServer.port = 7500 webServer.user = "admin" webServer.password = "改成你自己的强密码" transport.tls.force = true allowPorts = [ { start = 6222, end = 6299 }, { start = 6338, end = 6339 } ]

逐项解释一下作用。bindPort是frps监听frpc连接的端口,相当于frp自己的大门,默认7000,这个端口可以自己改,但改了之后客户端配置里的serverPort要同步改。auth.token是客户端连上来时的通行证,一定要设置成长随机字符串。我遇到不少部署,token还是默认的12345678,这种节点等于裸奔,非常危险。

webServer这一段是frp自带的Dashboard监控面板,浏览器打开http://服务器IP:7500就能看到当前在线客户端、代理列表和流量统计。addr设置为0.0.0.0表示所有网卡都可以访问,如果你只希望自己电脑能打开面板,可以改成你的办公网IP。

transport.tls.force为true表示强制TLS加密,所有客户端必须开启TLS才能连上,防止token和业务流量被中间人抓包。allowPorts段是服务端对客户端远程端口白名单的限制——客户端想占用公网服务器的哪个端口,必须在这个范围内,不在范围内的映射请求直接拒绝。这是个很实用的安全措施,比如我把6222-6299和6338-6339放开了,客户端就只能用这些端口段,避免有人乱开端口把服务器搞成公网代理。

3.3 用systemd把frps变成常驻服务

直接运行./frps -c frps.toml可以手动启动,但是关掉终端服务就停了,生产环境肯定不行。Linux上把frps做成systemd服务是最标准的做法。

创建服务文件/etc/systemd/system/frps.service:

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

启动并设置开机自启:

systemctl daemon-reload systemctl enable frps systemctl start frps systemctl status frps

注意几个细节。ExecStart里的路径必须是绝对路径,写成相对路径service会启动失败。Restart=on-failure表示服务异常退出时自动拉起,RestartSec=5s指定5秒后重试,这两个参数能保证frps在服务器重启或者进程崩溃后自动恢复。查看日志用journalctl -u frps -f,排查问题就靠它了。

3.4 服务端防火墙和云安全组放行

frps配置完成只是第一步,90%的“部署完连不上”问题都出在端口没有放行。这台公网服务器上需要放行的端口有三类:bindPort(7000)、allowPorts里定义的远程端口、Dashboard端口(7500)。

如果你用的是CentOS或者安装了firewalld的系统,放行命令是这样的:

firewall-cmd --permanent --add-port=7000/tcp firewall-cmd --permanent --add-port=6222-6299/tcp firewall-cmd --permanent --add-port=6338-6339/tcp firewall-cmd --permanent --add-port=7500/tcp firewall-cmd --reload

跑在Debian/Ubuntu系统上如果没有另外装防火墙,默认就不拦端口,但要确认一下云厂商控制台里的安全组策略。以阿里云为例,在ECS实例的安全组里添加入方向规则,放行相应TCP端口。很多朋友本地测frps是通的,换到云服务器就不行,十有八九就是安全组忘放行了。

这里我要特别说明一下:标题里经常出现的“绕过防火墙”,在真实运维场景里并不是要绕过云平台或者系统防火墙的安全策略,而是通过frp隧道,在不改变外网对内部网络访问策略的情况下,建立一条受控的访问通道。云平台安全组和系统防火墙该放行的端口还是要放行,这是正规操作,和“绕过”是两个概念。我见过一些安全演练场景里,团队会特意不开放相关端口,用frp的隐藏端口来做隐蔽通道,但那属于攻防对抗专题,而且需要完整的授权,不是日常运维该模仿的做法,这里就不展开了。

4. 客户端frpc配置与三种最常用场景

4.1 基础配置:认识frpc.toml的结构

内网机器上下载frp的方法和服务端一样,只是运行的是frpc。客户端配置文件frpc.toml的结构分为两大部分:连接服务器的参数和代理列表。

serverAddr = "你的服务器公网IP或域名" serverPort = 7000 auth.method = "token" auth.token = "Kj8mN2pQx7Lv9Tr5Wd4Yh6Ue1Ba3Cs0" transport.tls.enable = true proxies = [ { name = "ssh", type = "tcp", localIP = "127.0.0.1", localPort = 22, remotePort = 6222 } ]

serverAddr填服务器公网IP,如果服务器绑定了域名,建议填域名,这样服务器IP变更时客户端不用改配置。serverPort和token必须和服务端配置一致,一个字符都不能差。transport.tls.enable = true对应服务端的force true。proxies数组第一个元素就是一条SSH代理:name是代理名称,type是协议类型,localIP和localPort是内网目标服务地址,remotePort是公网服务器上要暴露的端口。

4.2 场景一:SSH远程管理内网Linux

最基础也最常用的就是SSH穿透。我在内网有一台开发机,IP是192.168.1.100,SSH端口是默认22。frpc配置如下:

proxies = [ { name = "ssh-dev", type = "tcp", localIP = "192.168.1.100", localPort = 22, remotePort = 6222 } ]

启动frpc后,公网服务器上的6222端口就指向了内网开发机的22端口。我在外面直接执行下面的命令就能登录:

ssh -p 6222 user@服务器公网IP

等一下,有个细节很多人会踩坑。如果你在内网开发机本机上跑frpc,并且SSH服务监听的是0.0.0.0,那么localIP写127.0.0.1就够了。但如果你和我的情况一样,内网有多台机器,frpc跑在一台单独的网关上,要代理其他机器的SSH,localIP必须写成目标机器的内网IP。这里的原则是:localIP填frpc能访问到目标的地址,能本地回环就不要写局域网IP,减少不必要的网络路径。

4.3 场景二:RDP远程桌面访问内网Windows

远程桌面是另一个高频需求。Windows的RDP服务端口是3389,穿透配置如下:

proxies = [ { name = "rdp-win", type = "tcp", localIP = "192.168.1.50", localPort = 3389, remotePort = 6338 } ]

外部Windows机器连接时,打开“远程桌面连接”,计算机名填公网IP:6338即可。

这里有一个Windows特有的坑:如果你在目标Windows机器上跑了frpc并且代理的是本机3389,localIP直接填127.0.0.1没问题,但如果Windows防火墙开启了“远程桌面服务”的入站限制,frpc转发到本机3389的流量也可能被拦。解决办法是在Windows防火墙里放行3389端口,或者放行frpc.exe程序的所有入站流量,二选一。

出站方向的限制也值得提一句。现在很多企业Windows防火墙的策略比较严,会阻止未签名程序对外连接,frpc.exe就经常被拦。现象是frpc启动后日志显示一直卡在连接服务器超时。排查方法是在防火墙的“出站规则”里新建一条允许frpc.exe连接网络放行,然后重试。这一点和很多安全软件阻止某些程序出站联网是同一个道理。

4.4 场景三:HTTP/HTTPS域名访问内网Web服务

TCP穿透解决的是SSH、RDP这类二进制协议,HTTP场景用TCP也完全能通,但域名访问和虚拟主机路由就搞不了。销售那边要给客户演示系统,不可能让客户去访问IP:8080这种地址,最好是一个干净的域名。

frp的HTTP代理可以解决这个问题。先看frps.toml里需要加一个配置:

vhostHTTPPort = 8080

这个端口是frps用来接收HTTP流量的统一入口。客户端配置如下:

proxies = [ { name = "web-demo", type = "http", localIP = "127.0.0.1", localPort = 8080, customDomains = ["demo.example.com"] } ]

external用户访问http://demo.example.com:8080,域名解析到frp服务器IP,frps根据请求的Host头找到customDomains匹配的代理,转发给内网机器的8080端口。一台frps可以承载无数个HTTP代理,域名不同互不干扰。

需要注意三个点:

  • 域名解析必须提前做好,否则外部用户找不到服务器。演示场景如果来不及申请域名,也可以用xxx.frp.你的域名这种泛解析方式。
  • 如果服务端还配了HTTPS的vhostHTTPSPort,需要同时准备证书和密钥文件,比HTTP多几步。没有HTTPS证书时建议先跑HTTP,演示归演示,别耽误正事。
  • customDomains不能随便填别人的域名,这个域名必须能解析到你的frp服务器。我见过直接把客户线上域名填进去的,那属于域名劫持了,别碰。

5. 安全加固:比“能通”更重要的是“安全”

5.1 默认配置下的安全隐患

说句不太好听的,我看到很多教程把frp配置出来能连通就结束了,没人管安全。默认配置直接跑,问题非常明显。

token弱口令。网上很多教程的token是12345678,如果你照着抄又没改,任何知道你服务器IP和端口的人都能连上来。token是frp的唯一口令,弱token相当于门锁是硬纸板做的。

远程端口范围不限。frps默认允许客户端映射除了bindPort以外的任何端口,意味着拿到token的人可以在你的公网服务器上随便开端口转发流量。轻则你的服务器变成别人的免费穿透节点,重则被用来跑违规流量,最后IP被封还是你背锅。

流量明文传输。frp老版本默认不加密,如果你的穿透链路经过运营商或者机房,中间任何环节都可以抓到流量内容。尤其是穿透的数据库、远程桌面这种敏感协议,明文就是裸奔。

5.2 我的安全加固清单

每次我在正式环境部署frp,都会强制执行下面这几条,缺一不可。

第一,token一定要用密码生成器生成至少32位的随机串,不要用生日、IP、英文单词。比如可以用openssl rand -hex 16生成一个十六进制串。

第二,用allowPorts限定远程端口范围。我在服务端配置里只放开需要用的端口段,至少避免被随意开端口中转流量。

第三,强制TLS加密。frps的transport.tls.force = true加上frpc的transport.tls.enable = true,让两端流量都走TLS。frp从0.51版本开始支持TLS,配置很简单,效果是实实在在的。

第四,Dashboard不要用默认账号密码,也不要暴露到公网。如果只是自己看,建议webServer.addr改成127.0.0.1,然后用SSH隧道访问Dashboard,比把admin密码暴露在公网安全得多:

ssh -L 7500:127.0.0.1:7500 user@公网服务器IP

然后在本地浏览器打开http://127.0.0.1:7500就能看到面板。

第五,能用STCP就不用TCP。STCP是frp提供的点对点安全隧道模式,原理是服务端只做协调和打洞,不直接转发业务流量,真正通信时两端用密钥做加密。这种模式下公网服务器上不需要开放远程端口,外部用户需要先在客户端本地运行一个frpc并配置visitor才能访问。STCP适合两个固定节点之间需要高安全通信的场景,配置稍微复杂一点,但安全级别高一个档次。

以SSH为例,STCP服务端(实际是穿透目标的机器)配置如下:

proxies = [ { name = "secret-ssh", type = "stcp", secretKey = "你自己的共享密钥", localIP = "127.0.0.1", localPort = 22 } ]

访问端的frpc配置则要声明一个visitor:

visitors = [ { name = "secret-ssh-visitor", type = "stcp", serverName = "secret-ssh", secretKey = "和上面的共享密钥一致", bindAddr = "127.0.0.1", bindPort = 6222 } ]

启动访问端frpc后,本机6222端口就对应了目标内网机器的22端口,然后执行ssh -p 6222 user@127.0.0.1即可。整个过程,公网服务器上看到的只是双方的连接握手,业务数据不经过frps中转,这是目前frp里安全等级最高的一种用法。

5.3 关于“绕过”的正反面:哪些事不能做

标题里“绕过防火墙”这个说法,加上“内网渗透”两个字,在一些人眼里会有攻击性的联想。我得把底线讲清楚:frp是工具,本身没有对错,关键看怎么用。有授权、有合同、有测试范围说明的渗透测试,用frp做通道完全合法合规;没有授权,哪怕只是登录一下陌生人的内网机器,也属于违法行为。

实务中,我这个角色最常碰到的合规场景是这些:客户买了一套内网系统,需要我们远程维护,但客户不愿开放太多公网端口,于是我们和客户约定好,用frp建立一条独立隧道,所有操作留日志,测试完撤销映射;或者出差时通过frp远程到公司电脑处理紧急工单。这类场景的核心特征是“被访问方知情且同意”。如果你发现自己所在的单位禁止使用任何穿透工具,那就别用,遵守单位网络管理规定同样是职业底线。

6. 常见问题排查与进阶技巧

6.1 外网连不上:按这个顺序排查

frp连不上的问题,我排查过太多次了,总结出一个固定顺序,按这个顺序走能最快定位问题。

先看frpc的日志。执行journalctl -u frpc -f看有没有报错。日志里如果出现dial tcp: connection refused,说明frpc连不上frps,重点检查frps是否启动、serverAddr和serverPort是否正确、服务器防火墙是否放行。如果出现login to server failed: token错误,说明token配置不一致,检查两端的token字符串。

再看frps的日志和服务状态。systemctl status frps看进程是否存活,journalctl -u frps -f看有没有accept连接记录。如果frps根本没有收到frpc的连接请求,那就是网络层面被拦了,多半是服务器防火墙或者云安全组没放行bindPort。

然后测端口连通性。在外部机器上执行telnet 公网IP 7000,能通说明网络通路没问题,不通就要倒回去检查防火墙。记住一个原则:从外网往内测,一步步缩小范围,不要上来就怀疑frp配置有问题。

最后测具体的代理端口。比如SSH穿透连不上,先确认frps上监听6222端口的是frps本身,而不是被其他程序占用了。用ss -lntp | grep 6222查看,监听的进程名应该是frps,如果不是,说明端口被占,改一个remotePort就行。

6.2 常见问题速查表

问题现象可能原因解决办法
frpc提示connection refusedfrps没启动或bindPort不对检查frps进程和配置
frpc提示token错误两端token不一致统一两端的token配置
外网能连frps但代理端口不通remotePort对应的防火墙/安全组未放行放行allowPorts对应端口
SSH/RDP连接时卡住或闪断中间网络MTU或延迟问题检查两边网络,TCPMux是否开启
frps Dashboard无法访问webServer.addr、端口未放行确认配置和防火墙规则
内网Windows上frpc不工作防火墙出站规则拦了frpc.exe给frpc.exe加出站放行规则
代理正常但访问极慢服务器带宽不足或两端带宽不匹配升级带宽或压缩流量

6.3 进阶:稳定性调优与多客户端管理

frp用了一段时间之后,稳定性会成为新的关注点。有几个参数值得调。

TCPMux默认是开启的,多个代理复用同一条TCP连接,能降低延迟、减少握手次数。但如果你的frp版本比较老,遇到过某些协议在mux下表现异常的情况,可以把frpc和frps都设置transport.tcpMux = false关闭,用独立连接传输。

心跳参数也值得设置一下。frpc默认每30秒向frps发送心跳包,如果在弱网环境或者经过严格防火墙的网络里,心跳间隔过长会被中间网络判定为闲置连接断掉。可以把心跳缩短到10秒,并开启transport.heartbeatKeepAlive相关选项(新版配置里对应的是transport.heartbeatInterval和transport.heartbeatTimeout)。这个参数不像其他配置那么常用,但我在穿透公司内网办公系统时实测下来确实能减少莫名其妙掉线的问题。

多客户端管理方面,frps的Dashboard其实已经能看所有客户端的在线状态和代理列表。如果客户端数量超过几十个,建议给每台机器配置不同的token,这样单个token泄露不会波及其他客户端。还可以在frps前边加一层Nginx或者Caddy做TLS终结和域名路由,让frps只监听内网端口,进一步缩小攻击面。说实话这个组合方案有点复杂,大多数场景用不到,但如果你的frps要面向多个项目组提供服务,建议尽早考虑隔离。

最后再分享一个小技巧。frpc的配置支持环境变量替换,格式是{{ .Envs.XXX }},这意味着你可以把token这类敏感信息放到环境变量里,配置文件中不出现明文秘钥。比如frpc.toml里写auth.token = "{{ .Envs.FRP_TOKEN }}",启动时先export FRP_TOKEN=xxxxx再运行frpc,这样即使配置文件被截图或者传到群里,token也不会泄露。我自己的生产环境一直用这个方案,属于性价比很高的一个安全习惯。

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

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

立即咨询