frp —— 内网穿透
frp —— 内网穿透
分类:12 隧道与高可用 适用:frp 0.52+(配置从 ini 改为 toml),frps 服务端 + frpc 客户端 一句话:把没有公网 IP 的内网服务,通过一台有公网 IP 的机器暴露出去,比 SSH 反向隧道更稳、更易管。
1. 它解决什么问题
- 公司内网/家里的服务要让外部访问,但没有公网 IP。
- 需要长期稳定的映射(SSH
-R断线要重连、端口管理麻烦)。 - 多个内网服务要统一在一个公网入口上(按域名/端口区分)。
- 需要 Web 管理界面查看隧道状态。
2. 架构
公网服务器 (frps) ←── 长连接 ── 内网机器 (frpc) → 本地服务 127.0.0.1:8080
↑
外部用户访问 frps 的端口/域名
| 组件 | 部署位置 | 作用 |
|---|---|---|
frps |
有公网 IP 的服务器 | 接收连接、转发流量 |
frpc |
内网机器 | 主动连 frps,注册隧道 |
3. 安装
# 下载对应架构
VERSION=0.58.1
curl -LO https://github.com/fatedier/frp/releases/download/v${VERSION}/frp_${VERSION}_linux_amd64.tar.gz
tar xf frp_${VERSION}_linux_amd64.tar.gz
cp frp_${VERSION}_linux_amd64/frps /usr/local/bin/
cp frp_${VERSION}_linux_amd64/frpc /usr/local/bin/
mkdir -p /etc/frp
frps --version
4. 服务端配置 frps.toml
# /etc/frp/frps.toml
bindPort = 7000 # frpc 连接端口
# 认证(必须改,别用默认)
auth.method = "token"
auth.token = "替换成足够长的随机串"
# 只允许这些端口被映射,防止被当跳板
allowPorts = [
{ start = 6000, end = 6999 },
{ single = 8080 },
{ single = 8443 }
]
# HTTP/HTTPS 域名方式(可选)
vhostHTTPPort = 80
vhostHTTPSPort = 443
# 管理面板
webServer.addr = "127.0.0.1" # 只监听本地,用 SSH 隧道访问
webServer.port = 7500
webServer.user = "admin"
webServer.password = "强密码"
# 日志
log.to = "/var/log/frp/frps.log"
log.level = "info"
log.maxDays = 7
# TLS(frpc 与 frps 之间加密,0.50+ 默认开启)
transport.tls.force = true
启动:
frps -c /etc/frp/frps.toml
# systemd
cat > /etc/systemd/system/frps.service <<'EOF'
[Unit]
Description=frp server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now frps
5. 客户端配置 frpc.toml
# /etc/frp/frpc.toml
serverAddr = "1.2.3.4"
serverPort = 7000
auth.method = "token"
auth.token = "与服务端一致"
log.to = "/var/log/frp/frpc.log"
log.level = "info"
log.maxDays = 7
# 1. TCP:把本地 22 映射到公网 6001
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6001
# 2. HTTP:按域名访问
[[proxies]]
name = "web"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["app.example.com"]
# 3. 内网数据库(谨慎,强烈建议加 IP 白名单)
[[proxies]]
name = "mysql"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3306
remotePort = 63306
启动:
frpc -c /etc/frp/frpc.toml
systemctl enable --now frpc
热重载(不中断已建立连接):
frpc reload -c /etc/frp/frpc.toml
frpc status -c /etc/frp/frpc.toml
6. 代理类型
| 类型 | 说明 | 典型用途 |
|---|---|---|
tcp |
裸 TCP 转发 | SSH、MySQL、RDP |
udp |
UDP 转发 | DNS、游戏、WireGuard |
http |
按域名/路径路由 | Web 服务 |
https |
按 SNI 路由 | HTTPS 站点 |
stcp |
加密点对点,需双方 secret | 不给公网暴露的内部服务 |
xtcp |
P2P 直连(打洞) | 大流量点对点 |
tcpmux |
多路复用 | 单端口承载多服务 |
stcp(安全访问内网服务)
访问端也跑 frpc:
# 服务端(内网机器)
[[proxies]]
name = "secret-ssh"
type = "stcp"
secretKey = "只有双方知道的密钥"
localIP = "127.0.0.1"
localPort = 22
# 访问端
[[visitors]]
name = "secret-ssh-visitor"
type = "stcp"
serverName = "secret-ssh"
secretKey = "同上"
bindAddr = "127.0.0.1"
bindPort = 60022
之后 ssh -p 60022 user@127.0.0.1 即可,服务端不需要开放任何公网端口。
7. 实战场景
7.1 暴露内网 Web 给外网
# frps.toml
vhostHTTPPort = 80
# frpc.toml
[[proxies]]
name = "blog"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["blog.example.com"]
DNS 把 blog.example.com 解析到 frps 服务器,访问即通。
7.2 远程 SSH 到内网机器
[[proxies]]
name = "home-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6001
ssh -p 6001 user@frps-ip
建议再配 ~/.ssh/config 别名,并在 frps 上用防火墙限制来源 IP。
7.3 HTTPS 透传(保留原证书)
[[proxies]]
name = "https-app"
type = "https"
localIP = "127.0.0.1"
localPort = 443
customDomains = ["secure.example.com"]
frps.toml 配 vhostHTTPSPort = 443。frp 只按 SNI 转发,证书仍在你的应用侧。
7.4 用 systemd 保证常驻
客户端断线重连是 frp 内置能力,systemd 只需保证进程不退出:
[Service]
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
Restart=always
RestartSec=10
LimitNOFILE=65535
7.5 查看状态
frpc status -c /etc/frp/frpc.toml
# 或浏览器访问 frps 的 7500 面板(先 SSH 隧道)
ssh -L 7500:127.0.0.1:7500 ops@frps
# 打开 http://127.0.0.1:7500
8. 安全要点(重要)
- 改默认 token:默认 token 等于没有认证,任何人都能挂到你的 frps 上。
allowPorts限制:不限制等于给攻击者一个公网跳板。- 管理面板只监听 127.0.0.1,通过 SSH 隧道访问。
- 数据库/RDP 不要直接
tcp暴露:用stcp或加防火墙白名单。 - 开启 TLS:
transport.tls.force = true。 - 日志与监控:异常流量(大量连接、陌生 proxy 名)要及时发现。
- 合规:企业网络使用需报备,可能违反安全策略。
- 不要用公共 frp 服务:流量经过陌生人服务器,等于裸奔。
9. 常见坑
- 版本与配置格式:0.52 之前用
ini,之后用toml,照抄老教程会报错。 customDomains与subDomain二选一:用subDomain需要 frps 配subDomainHost。- 端口被
allowPorts拒绝:frpc 日志报port not allowed。 - 防火墙没放行:7000(控制)、vhost 端口、remotePort 都要放行。
remotePort冲突:多个 frpc 抢同一端口会失败。- HTTP 代理 404/502:
customDomains与请求的 Host 不一致,或本地服务没起。 stcp双方 secretKey 不一致:连不上且日志不直观。- frps 被扫:暴露 7000 端口后会有大量爆破,配 fail2ban 或限制来源。
vhostHTTPPort = 80与已有 Nginx 冲突:改用其他端口,由 Nginx 反代。- 日志暴涨:
log.level = "debug"长期开启会写满磁盘。 transport.tls.force与老版本不兼容:升级两端到同版本。- SELinux:非标准端口绑定可能被拒,
semanage port -a。
10. 速查表
# 服务端
frps -c /etc/frp/frps.toml
systemctl enable --now frps
# 客户端
frpc -c /etc/frp/frpc.toml
frpc reload -c /etc/frp/frpc.toml
frpc status -c /etc/frp/frpc.toml
systemctl enable --now frpc
# 最小 frps.toml
bindPort = 7000
auth.method = "token"
auth.token = "long-random-string"
allowPorts = [{ start = 6000, end = 6999 }]
# 最小 frpc.toml
serverAddr = "1.2.3.4"
serverPort = 7000
auth.method = "token"
auth.token = "same"
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6001
# 类型:tcp udp http https stcp xtcp tcpmux
# 安全:改 token、限 allowPorts、面板绑 127.0.0.1、开 TLS、敏感服务用 stcp