frp 可以让没有公网 IP 的内网机器,通过一台公网服务器对外提供 SSH、Web 等服务。本文用 Compose 部署:公网机跑 frps,内网机跑 frpc,鉴权用 token,管理面板加访问限制。示例版本锁定 v0.61.1(0.52+ 统一使用 TOML 配置)。

服务端与客户端版本必须一致,token 必须一致。

一、服务端 frps(公网服务器)

目录:

/opt/frps/
├── docker-compose.yml
└── frps/
    └── frps.toml

docker-compose.yml(host 网络,后续每个代理端口无需逐一发布):

services:
  frps:
    image: fatedier/frps:v0.61.1
    container_name: frps
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./frps/frps.toml:/etc/frp/frps.toml:ro
    command:
      - -c
      - /etc/frp/frps.toml
    security_opt:
      - no-new-privileges:true
    mem_limit: 128m
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:7500/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

frps/frps.toml:

# 客户端连接端口
bindPort = 7000

# 管理面板:建议只绑回环,用 SSH 隧道访问
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "替换为强密码"

# token 鉴权(openssl rand -hex 32 生成,64 位)
auth.method = "token"
auth.token = "替换为与客户端一致的64位随机串"

# 如果要按域名转发 HTTP 服务,再打开:
# vhostHTTPPort = 8080

面板确实需要公网访问时,把 addr 改成 0.0.0.0 并务必用强密码,或干脆只通过 Nginx 反代 + HTTPS + IP 白名单暴露。

云安全组放行:

  • 7000/tcp(frpc 回连)
  • 每个代理声明的 remotePort,例如 6000/tcp(SSH 穿透);规划一批就放一段,如 6000-6010
  • 7500 不要对公网开放(回环 + 隧道方案下)

启动:

cd /opt/frps
docker compose up -d
docker compose ps
docker compose logs -f
ss -ltnp | grep -E ':7000|:7500'

二、客户端 frpc(内网机器)

内网机同样要装好 Docker。目录:

/opt/frpc/
├── docker-compose.yml
└── frpc/
    └── frpc.toml

docker-compose.yml(需要穿透本机 SSH 时用 host 网络最直接):

services:
  frpc:
    image: fatedier/frpc:v0.61.1
    container_name: frpc
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./frpc/frpc.toml:/etc/frp/frpc.toml:ro
    command:
      - -c
      - /etc/frp/frpc.toml
    security_opt:
      - no-new-privileges:true
    mem_limit: 64m
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }

frpc/frpc.toml(示例:把本机 SSH 暴露到服务端 6000 端口):

serverAddr = "203.0.113.10"     # 替换成你的公网服务器 IP(或域名)
serverPort = 7000

auth.method = "token"
auth.token = "替换为与服务端一致的64位随机串"

# 穿透本机 SSH:外网通过 ssh -p 6000 user@203.0.113.10 连入
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000

# 穿透内网另一台机器的 Web 服务(bridge 网络时 localIP 写内网可达地址即可)
# [[proxies]]
# name = "intranet-web"
# type = "tcp"
# localIP = "192.168.1.20"
# localPort = 80
# remotePort = 6001

# 按域名转发 HTTP(需在服务端配置 vhostHTTPPort,并把域名解析到服务器)
# [[proxies]]
# name = "web-by-domain"
# type = "http"
# localIP = "127.0.0.1"
# localPort = 8080
# customDomains = ["app.example.com"]

启动并观察注册是否成功:

cd /opt/frpc
docker compose up -d
docker compose logs -f
# 出现 "start proxy success" 即成功

三、验证

# 在任意外部机器上,经公网服务器跳回内网 SSH
ssh -p 6000 内网用户名@203.0.113.10

# 服务端面板(先在本机建隧道)
ssh -L 7500:127.0.0.1:7500 user@203.0.113.10
# 浏览器开 http://127.0.0.1:7500,用 frps.toml 里的账号密码登录

面板可以看到每个 proxy 的在线状态、进出流量;proxy 显示 offline 时先查客户端日志。

四、加固建议

  1. token 用 openssl rand -hex 32,两端配置文件 chmod 600;token 就是全部准入凭据,泄漏等于任何人能往你的服务器上打洞。
  2. 服务端安全组只放行 7000 和实际使用的 remotePort 段,面板 7500 不对公网开。
  3. 可以在 frps.toml 加 allowPorts 限制客户端只允许映射指定端口段:
    [[allowPorts]]
    start = 6000
    end = 6010
  4. 被穿透的 SSH 本身改用密钥登录、禁用密码登录、fail2ban 照常开。
  5. 主机名/IP、密码、token 提交到任何公开仓库前一律打码,compose 与 toml 不进公开 git。

五、常见问题

现象 原因与处理
客户端日志 authorization failed token 不一致或有空格/引号问题,逐字核对两端 auth.token
dial tcp ...:7000 i/o timeout 安全组没放 7000,或服务端没起来;ss -ltnp 在服务器上确认
proxy 注册成功但连 6000 超时 安全组没放 remotePort;或该端口被其他进程占用(ss -ltnp \| grep 6000)
SSH 能连但很卡/频繁断 中间链路问题可在 proxy 下加 transport.proxyProtocolVersion/心跳设置;默认 heartbeatInterval = 30、heartbeatTimeout = 90 一般不用动
HTTP 域名代理 404 服务端要配 vhostHTTPPort,域名 DNS 要指向服务器,访问端口与 vhost 端口一致
客户端时间错误导致鉴权异常 校准时间:chronyd/ntpdate,容器用 host 网络会直接用宿主时间
升级后客户端起不来 0.52 起是 TOML 配置,老的 INI 写法已废弃;两端保持同一大版本
最后修改:2026 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏