FRP内网穿透实战:从原理到配置,打通局域网服务访问
1. 项目概述:从“局域网孤岛”到“全球可达”的桥梁
搞开发、做运维,或者只是想在办公室访问家里NAS的朋友,估计都遇到过这个经典难题:服务明明在自己电脑或内网服务器上跑得好好的,但一离开这个局域网,就彻底失联了。这就是典型的“内网孤岛”困境。为了解决这个问题,内网穿透技术应运而生,而frp正是这个领域里经久不衰、口碑极佳的一款开源工具。它就像一个智能的通信中继,帮你把内网的服务“映射”到公网上,让全世界都能访问。
简单来说,frp的核心工作模式是“客户端-服务端”架构。你需要一台拥有公网IP的服务器(称为frps,服务端),以及运行在你内网环境里的机器(称为frpc,客户端)。服务端在公网监听,客户端主动连接到服务端并告知“我这里有这些服务(比如Web、SSH、远程桌面)”。之后,当外部用户想访问你的内网服务时,流量会先到达公网服务器,再由服务器转发给内网的客户端,最终抵达目标服务。整个过程对内外网的应用都是透明的。
这篇文章,我会结合自己多次搭建和运维的经验,把frp的配置、核心概念(尤其是让人头疼的端口问题)、以及整个通信过程掰开揉碎了讲清楚。无论你是想暴露一个开发中的Web应用进行测试,还是想安全地远程访问家里的设备,这篇指南都能让你避开我踩过的那些坑,一次配置成功。
2. 核心概念与工作原理解析
在动手配置之前,彻底理解frp的几个核心概念和工作流程至关重要。这能让你在遇到问题时,不再是盲目地修改配置,而是能清晰地知道问题出在链条的哪个环节。
2.1 核心组件角色定义
frps (Frp Server): 服务端这是整个穿透体系的核心枢纽,必须部署在一台拥有公网IP地址的机器上,例如云服务器(阿里云、腾讯云等)。它的核心职责是:
- 监听与管理:监听一个或多个特定的端口(如
7000),等待frpc客户端的连接。 - 流量转发:根据客户端注册的服务类型(如TCP、HTTP)和配置的端口,接收来自互联网的访问请求,并将其精准地转发给对应的frpc客户端。
- 控制通道:与客户端之间维持一个稳定的控制连接,用于传输配置、心跳和指令。
frpc (Frp Client): 客户端部署在你需要被访问的内网机器上。它的核心职责是:
- 主动连接:主动向指定的frps地址和端口发起连接,建立一条安全的通信隧道。
- 服务注册:告诉frps:“我本地(
127.0.0.1或内网IP)的哪个端口(如8080)上运行着什么服务(比如一个网站),希望你在公网用哪个端口(如8080)来暴露它。” - 流量代理:接收从frps转发过来的外部流量,并将其送达本地的目标服务;同时,也将本地服务的响应数据通过隧道送回frps,再传回给访问者。
2.2 通信流程全景图
理解数据是如何流转的,是调试一切问题的关键。我们以一个最常见的场景为例:你想通过公网访问内网机器A上的一个Web服务(运行在localhost:8080)。
隧道建立阶段:
- 你在云服务器B上启动
frps,它开始监听控制端口(例如7000)。 - 在内网机器A上启动
frpc,其配置文件中指定了服务器B的公网IP和端口7000。 frpc主动向frps的7000端口发起TCP连接,成功建立一条控制通道。这条通道主要用于传输配置信息、心跳包保持连接,后续具体业务数据的传输会建立新的连接。
- 你在云服务器B上启动
服务注册与暴露阶段:
frpc通过控制通道告诉frps:“我本地有一个HTTP服务在127.0.0.1:8080,请你在你的80端口上帮我监听HTTP请求。”frps收到指令后,在其公网IP的80端口上启动一个监听器。
外部访问与流量转发阶段:
- 互联网上的用户C在浏览器输入
http://服务器B的公网IP:80。 - 这个请求到达服务器B的
80端口,被frps的监听器捕获。 frps根据之前frpc的注册信息,知道这个80端口的请求应该转发给哪个frpc客户端(机器A)。frps通过已建立的控制通道,通知frpc:“有外部请求来了,准备接收数据。”随后,frps和frpc之间会为这个具体的HTTP请求建立一条新的数据通道(可能复用或新建TCP连接)。frpc通过这条数据通道收到原始的HTTP请求数据包,然后将其原封不动地发送给本地真正的Web服务127.0.0.1:8080。- 内网Web服务处理请求,生成响应,返回给
frpc。 frpc再将响应数据通过数据通道发回给frps。frps最后将响应数据发送回用户C的浏览器。
- 互联网上的用户C在浏览器输入
整个过程中,用户C完全感知不到内网机器A的存在,他以为就是在直接访问服务器B。而内网的Web服务也感知不到来自公网的复杂网络环境,它只看到来自127.0.0.1(即frpc)的本地连接。frp完美地扮演了“翻译”和“邮差”的角色。
2.3 端口:穿透过程中的“门牌号”与“交通枢纽”
端口是网络通信的端点,在内网穿透中扮演着不同角色,极易混淆。务必分清以下三种端口:
服务端监听端口 (
bind_port):- 作用:
frps用来接收frpc连接的控制端口。这是整个穿透隧道的“总开关”。 - 示例:通常设置为
7000。在配置中对应bind_port = 7000。 - 注意事项:此端口必须在云服务器的安全组/防火墙中放行。它是
frpc能找到frps的唯一入口。
- 作用:
服务端代理端口 (
remote_port):- 作用:
frps暴露给公网用户访问的端口。是外部流量进入穿透体系的“大门”。 - 示例:你想让用户通过服务器B的
8080端口访问内网服务,那么remote_port就设为8080。 - 注意事项:
- 此端口也必须在云服务器安全组/防火墙中放行。
- 确保该端口在服务器上没有其他进程占用,否则
frps会启动失败。错误信息常类似于“windows socket error: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。”或“端口被占”。 - 对于Web服务,常用
80(HTTP)或443(HTTPS)。使用80/443端口可能需要root权限启动frps,或通过setcap赋予普通程序监听特权端口的能力。
- 作用:
本地服务端口 (
local_port):- 作用:内网中实际运行服务的端口。是流量穿透的最终目的地。
- 示例:你的网站运行在内网机器的
localhost:3000上,那么local_port就是3000。 - 注意事项:确保
frpc所在机器能正常访问这个local_port。可以用curl http://127.0.0.1:3000或telnet 127.0.0.1 3000在本机测试。
关系梳理:用户访问公网IP:remote_port->frps在remote_port接收 ->frps通过隧道转发给frpc->frpc访问127.0.0.1:local_port-> 到达真实服务。
3. 实战配置:从零搭建一个可用的穿透服务
理论清晰后,我们进入实战。我会以最经典的暴露一个内网HTTP服务和SSH远程连接为例,展示完整配置。
3.1 服务端 (frps) 配置详解
首先在拥有公网IP的云服务器上操作。
下载与解压: 访问frp的GitHub Releases页面,根据服务器系统选择对应版本(如Linux amd64)。使用
wget下载并用tar解压。wget https://github.com/fatedier/frp/releases/download/v0.54.0/frp_0.54.0_linux_amd64.tar.gz tar -zxvf frp_0.54.0_linux_amd64.tar.gz cd frp_0.54.0_linux_amd64目录下你会看到
frps(服务端程序)和frps.ini(服务端配置文件)。编辑配置文件
frps.ini: 这是最核心的一步。一个基础且安全的配置如下:[common] # frps监听的端口,用于接收frpc的连接 bind_port = 7000 # 仪表盘端口,用于查看frp状态(可选但推荐) dashboard_port = 7500 # 仪表盘用户名和密码(强烈建议修改!) dashboard_user = admin dashboard_pwd = your_strong_password_here # 认证令牌,用于增强安全性,防止未经授权的客户端连接(强烈建议设置!) token = your_secret_token_here # 如果要用80/443等特权端口暴露HTTP/HTTPS服务,可能需要设置 # vhost_http_port = 80 # vhost_https_port = 443配置项解读与避坑指南:
bind_port:这是生命线,确保防火墙开放此端口(如7000)。dashboard_port:Web管理界面,能直观看到连接客户端和代理状态,排查问题时非常有用。token:这是服务端和客户端之间的共享密钥。务必设置一个强密码,并在客户端配置中填写相同的token。这是防止他人恶意连接你的frps服务器的关键。vhost_http_port:如果你需要用到HTTP域名访问(一个端口对应多个不同域名的网站),才需要配置此项。普通端口映射不需要。
启动frps: 直接运行
./frps -c ./frps.ini。为了长期运行,推荐使用系统服务管理。使用systemd(推荐): 创建服务文件/etc/systemd/system/frps.service:[Unit] Description=Frp Server Service After=network.target [Service] Type=simple User=nobody Restart=on-failure RestartSec=5s ExecStart=/path/to/your/frps -c /path/to/your/frps.ini [Install] WantedBy=multi-user.target然后执行:
sudo systemctl daemon-reload sudo systemctl enable frps sudo systemctl start frps sudo systemctl status frps # 检查状态服务器防火墙与安全组配置: 这是导致“连接失败”的最高频原因!你必须登录云服务商的控制台,配置安全组规则,入方向放行你配置中用到的所有端口:
7000(bind_port)7500(dashboard_port,如果启用)- 以及你计划暴露给公网的业务端口(如
6000,8080等)。
3.2 客户端 (frpc) 配置详解
在内网需要被访问的机器上操作。同样先下载对应版本的frp,我们关注frpc和frpc.ini。
编辑配置文件
frpc.ini: 假设你想暴露两个服务:一个本地Web服务(端口3000)和一个SSH服务(端口22)。[common] # frps服务器的公网IP地址或域名 server_addr = x.x.x.x # frps服务器的bind_port server_port = 7000 # 必须和服务端配置的token一致! token = your_secret_token_here # 第一个代理:暴露Web服务 [web] type = tcp # 本地服务IP和端口 local_ip = 127.0.0.1 local_port = 3000 # 在frps服务器上暴露的端口 remote_port = 8080 # 第二个代理:暴露SSH服务 [ssh] type = tcp local_ip = 127.0.0.1 local_port = 22 # 通过服务器B的6000端口访问内网A的SSH remote_port = 6000配置项解读:
[web]和[ssh]:这是代理规则的名称,可以自定义,但在一个配置文件中必须唯一。type:代理类型。tcp是最基础的TCP流量转发。对于HTTP/HTTPS服务,还有更智能的http和https类型,可以复用端口、按域名分发。local_ip:一般就是127.0.0.1。如果服务运行在同局域网另一台机器,可填其内网IP。remote_port:这是关键!这个端口必须在frps服务器上未被占用,且安全组已放行。
启动frpc:
./frpc -c ./frpc.ini。同样建议配置为系统服务或后台运行。Windows下作为服务运行: 可以使用nssm(Non-Sucking Service Manager)这个工具,将frpc安装为Windows服务,实现开机自启。Linux下使用systemd: 类似frps,创建frpc.service文件即可。
3.3 验证与访问
检查连接状态:
- 访问
http://你的服务器IP:7500,使用设置的dashboard账号密码登录。你应该能看到一个在线的客户端(frpc)以及它注册的两个代理(web和ssh),状态应为online。 - 在客户端机器上,查看
frpc的日志输出,应显示“start proxy success”之类的信息。
- 访问
测试穿透服务:
- Web服务:在任何能联网的电脑上,打开浏览器,访问
http://你的服务器IP:8080。你应该能看到运行在内网机器3000端口上的网站。 - SSH服务:在终端使用命令
ssh -p 6000 你的服务器IP。注意,这里连接的是服务器的IP和6000端口,但实际登录的会是内网那台机器的SSH。首次连接可能会提示主机密钥变更,这是正常的,因为流量经过了转发。
- Web服务:在任何能联网的电脑上,打开浏览器,访问
4. 高级配置与场景应用
基础打通后,可以探索更强大的功能来满足复杂需求。
4.1 使用域名访问内网Web服务(HTTP/HTTPS)
如果你有域名,并且希望用http://dev.yourdomain.com这样的方式访问内网开发环境,可以使用http代理类型。
服务端配置 (
frps.ini):[common] bind_port = 7000 token = your_token # 启用HTTP反向代理端口 vhost_http_port = 80 # 如果需要HTTPS,再启用这个 # vhost_https_port = 443客户端配置 (
frpc.ini):[common] server_addr = x.x.x.x server_port = 7000 token = your_token [web-dev] type = http local_ip = 127.0.0.1 local_port = 3000 # 关键配置:自定义域名 custom_domains = dev.yourdomain.com # 可以配置多个http代理,共用80端口 [web-test] type = http local_ip = 192.168.1.100 # 也可以是同局域网其他机器 local_port = 8080 custom_domains = test.yourdomain.comDNS解析: 将域名
dev.yourdomain.com和test.yourdomain.com的A记录指向你的frps服务器的公网IP。访问: 现在,访问
http://dev.yourdomain.com的流量会到达服务器80端口,frps根据Host头识别出这是dev.yourdomain.com的请求,然后转发给配置了对应custom_domains的frpc客户端。这样,一个公网端口(80)就可以服务多个不同域名的内网网站。
4.2 安全强化:TLS加密隧道
默认情况下,frp客户端与服务端之间的通信是明文的。虽然传输的是应用层数据(如加密的HTTPS或SSH),但隧道本身未加密。对于高敏感场景,可以启用TLS加密整个隧道。
生成证书(可选,生产环境建议使用正式证书):
# 生成CA根证书 openssl genrsa -out ca.key 2048 openssl req -new -x509 -days 3650 -key ca.key -out ca.crt # 生成服务器证书 openssl genrsa -out server.key 2048 openssl req -new -key server.key -out server.csr openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt # 生成客户端证书 openssl genrsa -out client.key 2048 openssl req -new -key client.key -out client.csr openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt服务端配置:
[common] bind_port = 7000 token = your_token # 启用TLS,并指定服务器证书 tls_only = true tls_cert_file = /path/to/server.crt tls_key_file = /path/to/server.key tls_trusted_ca_file = /path/to/ca.crt客户端配置:
[common] server_addr = x.x.x.x server_port = 7000 token = your_token # 指定客户端证书和信任的CA tls_enable = true tls_cert_file = /path/to/client.crt tls_key_file = /path/to/client.key tls_trusted_ca_file = /path/to/ca.crt启用后,所有通过
7000端口的数据都将被TLS加密,安全性极大提升。
4.3 其他实用代理类型
stcp(Secret TCP):一种安全的TCP代理,不需要在服务端暴露任何端口。访问者也需要运行一个特殊的frpc(visitor)来连接。适用于点对点的安全内网服务暴露,如数据库、远程桌面,避免服务被全网扫描。sudp:类似于stcp,但是用于UDP流量。- 文件访问插件:配合
http代理,可以快速将内网目录暴露为一个简单的文件服务器。
5. 故障排查与性能优化实录
即使配置正确,在实际运行中也可能遇到各种问题。这里记录了我遇到过的典型问题及解决方法。
5.1 连接类问题排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
frpc连接frps超时或失败 | 1. 服务器IP/端口错误 2. 服务器防火墙/安全组未放行 bind_port3. 服务器 frps未正常运行 | 1. 核对server_addr和server_port。2.重点!登录云平台控制台,确认安全组入规则已允许 bind_port(如7000)。可在服务器用sudo netstat -tlnp | grep :7000查看端口监听状态。3. 登录服务器,检查 frps进程状态和日志sudo systemctl status frps或./frps -c ./frps.ini直接运行看输出。 |
frpc显示“login to server failed” | 1. 认证失败(token不匹配) 2. 服务器 bind_port被占用 | 1. 仔细检查服务端和客户端frps.ini/frpc.ini中的token是否完全一致,包括空格和大小写。2. 在服务器检查端口占用: sudo lsof -i:7000,停止占用进程或为frps更换bind_port。 |
公网能访问dashboard,但无法访问代理服务(如8080) | 1. 代理的remote_port未在安全组放行2. remote_port在服务器上被其他进程占用3. 客户端本地服务未启动或无法访问 | 1.最常见原因!确保安全组放行了你配置的remote_port(如8080, 6000)。2. 在服务器上检查该端口占用情况。 3. 在客户端机器上,用 curl http://127.0.0.1:3000或telnet 127.0.0.1 22测试本地服务是否正常。检查frpc配置中的local_ip和local_port是否正确。 |
| 连接建立成功,但访问速度慢或不稳定 | 1. 服务器带宽或性能瓶颈 2. 客户端上行带宽不足(重要!) 3. 网络延迟高 | 1. 升级云服务器配置,特别是带宽。 2.内网穿透的典型瓶颈!客户端(你家宽带)的上行带宽决定了出口速度。用测速工具检查客户端上行带宽,如果太低,考虑升级宽带或优化传输内容(如压缩图片)。 3. 选择地理位置上更接近客户端或用户的云服务器区域。 |
5.2 性能优化与稳定性心得
选择合适的协议类型:
- 纯TCP转发(
type = tcp)是最通用但效率最低的,所有流量(包括HTTP头)都经过隧道。 - 对于Web服务,务必使用
type = http。frps可以解析HTTP协议,进行连接复用、头部优化等,能显著降低延迟和服务器资源消耗。https类型同理。
- 纯TCP转发(
启用压缩与加密: 在
[common]段可以配置:[common] # 启用传输压缩,对文本、代码类内容效果明显 use_compression = true # 启用加密,即使不用TLS,也能提供基础混淆加密 use_encryption = true注意:压缩会消耗CPU,在树莓派等低性能设备上需权衡。
调整连接池与超时:
[common] # 设置连接池大小,避免频繁建连 pool_count = 5 # 设置TCP保活参数 tcp_mux_keepalive_interval = 60对于需要大量并发短连接的服务,调整
pool_count可以提升性能。善用健康检查: 在代理配置中增加健康检查,确保后端服务不可用时,frp能及时感知。
[web] type = tcp local_ip = 127.0.0.1 local_port = 3000 remote_port = 8080 # 启用健康检查 health_check_type = tcp health_check_timeout_s = 3 health_check_max_failed = 3 health_check_interval_s = 10
5.3 安全配置黄金法则
内网穿透意味着将内网服务暴露在公网,安全是重中之重。
- 强密码与Token:
dashboard密码和token必须使用高强度、无规律的字符串,切勿使用默认值或简单密码。 - 最小化暴露原则:
- 只暴露必要的端口和服务。
- 使用
stcp代替tcp来暴露数据库、远程桌面等高危服务,实现“零端口暴露”。 - 为
dashboard(7500端口)设置复杂的访问密码,并考虑通过云防火墙限制其访问来源IP(如只允许自己的办公IP访问)。
- 定期更新:关注frp项目的安全更新,及时升级到最新版本。
- 防火墙配合:即使在云安全组放行了端口,在服务器操作系统内部也建议配置
iptables或firewalld,进行更细粒度的访问控制。
6. 进阶:在容器化与自动化环境中部署frp
现代基础设施中,服务常运行在Docker或Kubernetes中。frp同样可以很好地集成。
6.1 使用Docker运行frps
这是最简洁的部署方式之一。官方提供了Docker镜像。
# 创建一个目录存放配置和持久化数据 mkdir -p /opt/frp # 创建frps.ini配置文件 vim /opt/frp/frps.ini # 将你的配置写入,例如: # [common] # bind_port = 7000 # dashboard_port = 7500 # dashboard_user = admin # dashboard_pwd = strongpassword # token = your_token # 使用Docker运行 docker run -d \ --name frps \ --restart=always \ -p 7000:7000 \ -p 7500:7500 \ -p 80:80 \ -p 443:443 \ -v /opt/frp/frps.ini:/etc/frp/frps.ini \ snowdreamtech/frps注意:-p参数映射端口,需要映射所有你在配置文件中用到的端口(bind_port,dashboard_port,vhost_http_port等)。
6.2 在Kubernetes中部署frpc作为Sidecar
对于Kubernetes中的Pod,如果希望从集群外访问某个特定Pod的服务,可以将frpc作为Sidecar容器与业务容器部署在同一个Pod里。
apiVersion: v1 kind: Pod metadata: name: my-app-with-frpc spec: containers: - name: my-app image: my-web-app:latest ports: - containerPort: 3000 - name: frpc image: snowdreamtech/frpc volumeMounts: - name: frpc-config mountPath: /etc/frp # 从ConfigMap或Secret读取配置更安全 volumes: - name: frpc-config configMap: name: frpc-configfrpc-config这个ConfigMap包含你的frpc.ini配置,其中local_ip可以设置为127.0.0.1或Pod内网IP,local_port对应业务容器的端口(如3000)。
6.3 配置管理自动化
当有大量内网机器需要穿透时,手动管理frpc.ini是噩梦。可以考虑:
- 使用配置管理工具:如Ansible,编写Playbook来批量分发和更新
frpc二进制文件及配置文件。 - 结合服务发现:在微服务架构中,可以编写脚本,根据服务注册中心(如Consul, Nacos)的信息,动态生成
frpc.ini配置文件并重载frpc。
最后,关于端口冲突错误“通常每个套接字地址只允许使用一次”,除了之前提到的检查占用,在Windows上还有一个常见原因:如果frpc异常退出,可能没有立即释放端口,导致快速重启时失败。可以等待几十秒再重启,或者使用netstat -ano | findstr :PORT找到占用进程的PID,在任务管理器中强制结束。在Linux下,如果遇到TIME_WAIT状态导致端口无法立即重用,可以尝试在frps.ini中设置tcp_mux = true来启用连接复用,或者调整系统参数net.ipv4.tcp_tw_reuse。
