frp —— 内网穿透

运维工具手册 12-隧道与高可用 5.7 KB 原文

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. 安全要点(重要)

  1. 改默认 token:默认 token 等于没有认证,任何人都能挂到你的 frps 上。
  2. allowPorts 限制:不限制等于给攻击者一个公网跳板。
  3. 管理面板只监听 127.0.0.1,通过 SSH 隧道访问。
  4. 数据库/RDP 不要直接 tcp 暴露:用 stcp 或加防火墙白名单。
  5. 开启 TLS:transport.tls.force = true。
  6. 日志与监控:异常流量(大量连接、陌生 proxy 名)要及时发现。
  7. 合规:企业网络使用需报备,可能违反安全策略。
  8. 不要用公共 frp 服务:流量经过陌生人服务器,等于裸奔。

9. 常见坑

  1. 版本与配置格式:0.52 之前用 ini,之后用 toml,照抄老教程会报错。
  2. customDomains 与 subDomain 二选一:用 subDomain 需要 frps 配 subDomainHost。
  3. 端口被 allowPorts 拒绝:frpc 日志报 port not allowed。
  4. 防火墙没放行:7000(控制)、vhost 端口、remotePort 都要放行。
  5. remotePort 冲突:多个 frpc 抢同一端口会失败。
  6. HTTP 代理 404/502:customDomains 与请求的 Host 不一致,或本地服务没起。
  7. stcp 双方 secretKey 不一致:连不上且日志不直观。
  8. frps 被扫:暴露 7000 端口后会有大量爆破,配 fail2ban 或限制来源。
  9. vhostHTTPPort = 80 与已有 Nginx 冲突:改用其他端口,由 Nginx 反代。
  10. 日志暴涨:log.level = "debug" 长期开启会写满磁盘。
  11. transport.tls.force 与老版本不兼容:升级两端到同版本。
  12. 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