Linux 部署 FRP 反向代理

gofrp.org

你有一台内网主机,以及一台公网服务器。如何通过公网主机访问内网服务器呢?FRP 可以建立一条从公网服务器通向内网主机的通道,只要我们能够访问公网服务器,就能通过 FRP 连接到内网主机。

graph TD subgraph Internet A[Client] end subgraph Public Server B[FRP Server] end subgraph Local Network C[FRP Client] D[Local Service] end A -->|访问公网地址| B B -->|转发请求| C C -->|转发到本地| D D -->|返回结果| C C -->|返回给公网服务器| B B -->|返回给客户端| A

原生部署

服务端

  1. GitHub 下载二进制,如 frp_x.x.x_linux_amd64.tar.gz

    wget https://github.com/fatedier/frp/releases/download/v0.61.2/frp_0.61.2_linux_amd64.tar.gz
    
  2. 在服务器上解压并安装 frps:

    tar -xzf frp*.tar.gz
    rm frp*.tar.gz
    cd frp*
    sudo mkdir /etc/frp
    sudo cp frps.toml /etc/frp
    sudo cp frps /usr/local/bin
    
  3. 编辑配置文件 /etc/frp/frps.toml

    bindAddr = "0.0.0.0"
    bindPort = 7000
    
    [auth]
    method = "token"
    token = "your_token"
    
    [webServer]
    addr = "127.0.0.1"
    port = 7500
    user = "your_username"
    password = "your_password"
    

    需要服务器防火墙放行 TCP 7000 端口。

  4. 创建 systemd 单元文件 /etc/systemd/system/frps.service

    [Unit]
    Description = FRP Server
    After = network.target syslog.target
    
    [Service]
    Type = simple
    DynamicUser=yes
    Restart = on-failure
    RestartSec = 5s
    ExecStart = /usr/local/bin/frps -c /etc/frp/frps.toml
    LimitNOFILE = 1048576
    
    [Install]
    WantedBy = multi-user.target
    
  5. 启动 frps 服务:

    sudo systemctl enable frps
    sudo systemctl start frps
    
  6. 检查运行情况:

    sudo systemctl status frps  # 检查运行状态
    sudo journalctl -eu frps    # 查看日志
    

    如果启动失败,可能是端口被占用。

    如果启动成功,则可在 http://127.0.0.1:7500 看到 FRP Web 管理界面(需 SSH 本地转发)。

客户端

这里介绍在 Linux 上的部署方法。在 Windows 上的部署方法可以参考 Windows 设置 FRP 自动启动

  1. 前往 Releases 下载二进制,如 frp_0.61.1_linux_amd64.zip

    wget https://github.com/fatedier/frp/releases/download/v0.61.2/frp_0.61.2_linux_amd64.tar.gz
    
  2. 在本地机器上解压并安装 frpc:

    tar -xzf frp*.tar.gz
    rm frp*.tar.gz
    cd frp*
    sudo mkdir /etc/frp
    sudo cp frpc.toml /etc/frp
    sudo cp frpc /usr/local/bin
    
  3. 编辑配置文件 /etc/frp/frpc.toml

    serverAddr = "x.x.x.x"  # frps 服务器的公网 IP
    serverPort = 7000       # frps 用于接收客户端连接的端口
    
    [auth]
    method = "token"
    token = "your_token"
    
    [transport]
    tls.enable = true
    
    [[proxies]]
    name = "ssh"
    type = "tcp"
    localIP = "127.0.0.1"   # 需要从公网访问的内网服务的地址和端口
    localPort = 22
    remotePort = 6000       # 在 frp 服务端监听的端口,访问此端口的流量将被转发到本地服务的相应端口
    

    需要服务器防火墙放行 TCP 6000 端口。

  4. 创建 systemd 单元文件 /etc/systemd/system/frpc.service

    [Unit]
    Description = FRP Client
    After = network.target syslog.target
    
    [Service]
    Type = simple
    DynamicUser = yes
    Restart = on-failure
    RestartSec = 5s
    ExecStart = /usr/local/bin/frpc -c /etc/frp/frpc.toml
    LimitNOFILE = 1048576
    
    [Install]
    WantedBy = multi-user.target
    
  5. 启动 frpc 服务:

    sudo systemctl enable frpc
    sudo systemctl start frpc
    
  6. 检查运行情况

    sudo systemctl status frpc  # 检查运行状态
    sudo journalctl -eu frpc    # 查看日志
    

Docker 部署

服务端

snowdreamtech/frps

  1. 编写配置文件

    mkdir -p ~/.config/frp
    vim ~/.config/frp/frps.toml
    

    配置文件内容参考服务端

  2. 启动 Docker 容器

    • Linux

      docker run -d --restart always --network host --name frps -v ~/.config/frp/frps.toml:/etc/frp/frps.toml snowdreamtech/frps
      
    • Windows

      docker run -d --restart always --network host --name frps -v /c/Users/YourName/.config/frp/frps.toml:/etc/frp/frps.toml snowdreamtech/frps
      

      记得替换 YourName 为你的用户名。

客户端

snowdreamtech/frpc

  1. 编写配置文件

    vim ~/.config/frp/frpc.toml
    

    配置文件内容参考客户端

  2. 启动 Docker 容器

    • Linux

      docker run -d --restart always --network host --name frpc -v ~/.config/frp/frpc.toml:/etc/frp/frpc.toml snowdreamtech/frpc
      
    • Windows

      docker run -d --restart always --network host --name frpc -v /c/Users/YourName/.config/frp/frpc.toml:/etc/frp/frpc.toml snowdreamtech/frpc
      

      记得替换 YourName 为你的用户名。

  3. 查看日志:

    docker logs frpc
    

Troubleshooting

i/o deadline reached

frpc 连接不上服务器,提示 i/o deadline reached

2024-12-28 20:04:16.009 [I] [sub/root.go:142] start frpc service for config file [frpc.toml]
2024-12-28 20:04:16.015 [I] [client/service.go:295] try to connect to server...
2024-12-28 20:04:26.295 [W] [client/service.go:298] connect to server error: i/o deadline reached
2024-12-28 20:04:26.296 [I] [sub/root.go:160] frpc service for config file [frpc.toml] stopped
login to the server failed: i/o deadline reached. With loginFailExit enabled, no additional retries will be attempted

如果你的服务器在海外,放弃吧。你的流量被运营商过滤了。

参见:Frpc login to server failed: i/o deadline reached 的一种可能原因 | 哔哩哔哩

Docker 踩坑记录

我使用的配置文件是官方提供的示例配置文件 通过 SSH 访问内网机器,应该没有问题。

第一次我使用 Docker 镜像 snowdreamtech/frps 在服务器上部署 frps,发现始终连不上去。在内网机器的 frpc log 中显示如下错误:

2024-03-12 17:02:31 2024/03/12 09:02:31 [I] [root.go:142] start frpc service for config file [/etc/frp/frpc.toml]
2024-03-12 17:02:31 2024/03/12 09:02:31 [I] [service.go:287] try to connect to server...
2024-03-12 17:02:31 2024/03/12 09:02:31 [W] [service.go:290] connect to server error: dial tcp 8.134.175.243:7000: connect: connection refused
2024-03-12 17:02:31 2024/03/12 09:02:31 [I] [root.go:160] frpc service for config file [/etc/frp/frpc.toml] stopped
2024-03-12 17:02:31 login to the server failed: dial tcp 8.134.175.243:7000: connect: connection refused. With loginFailExit enabled, no additional retries will be attempted

可以看到连接失败的原因是连接 7000 端口时遇到了 connection refused 错误。这说明服务器的 7000 端口可能没有打开。

而在服务器的 frps log 中则一切正常:

2024/03/12 09:01:53 [I] [root.go:105] frps uses config file: /etc/frp/frps.toml
2024/03/12 09:01:54 [I] [service.go:225] frps tcp listen on 0.0.0.0:7000
2024/03/12 09:01:54 [I] [root.go:114] frps started successfully

可以看到 frps 正在监听 7000 端口。

一开始我以为是服务器的防火墙没开启 7000 端口,可是后来发现和防火墙设置没关系。后来我在服务器上查看 7000 端口的使用情况:

sudo lsof -i :7000

输出为空。

这说明没有服务在监听 7000 端口,那我的内网主机当然不可能连的上。

于是我在服务器上使用 brew 安装了一个 frps,并使用它部署了 frps 服务:

brew install frps
brew services start frps

最后内网 frpc 连接成功了:

2024-03-12 17:06:25 2024/03/12 09:06:25 [I] [root.go:142] start frpc service for config file [/etc/frp/frpc.toml]
2024-03-12 17:06:25 2024/03/12 09:06:25 [I] [service.go:287] try to connect to server...
2024-03-12 17:06:25 2024/03/12 09:06:25 [I] [service.go:279] [2d0f725bae7e4407] login to server success, get run id [2d0f725bae7e4407]
2024-03-12 17:06:25 2024/03/12 09:06:25 [I] [proxy_manager.go:173] [2d0f725bae7e4407] proxy added: [ssh]
2024-03-12 17:06:25 2024/03/12 09:06:25 [I] [control.go:170] [2d0f725bae7e4407] [ssh] start proxy success

可以看到内网 frpc 已经建立了 proxy。

然而这时又出了问题:我在外网使用 SSH 连接内网机器依旧不成功。

于是我在内网机器上也使用 brew 安装了 frpc,再次尝试连接。

这次连接成功了。

经过查阅 Docker host 网络模式参考文档,发现原因出在 host 网络模式上。我在服务器和内网机器上使用的都是 Docker Desktop 而不是 Docker CE,而此时的 Docker Desktop 版本还不支持 host 网络模式。因此我使用 Docker Desktop 部署在 host 网络模式下的容器是无法使用主机网络的。

2024.4.20 更新:目前 Docker Desktop 4.29 在 Windows 和 macOS 上的版本已经支持了 host 网络模式,而 Linux 版本则正处于实验阶段。所以现在使用 Docker Desktop 运行 host 网络模式的容器应该不会再出问题。

Host networking is also supported on Docker Desktop version 4.29 and later for Mac, Windows, and Linux as a beta feature. To enable this feature, navigate to the Features in development tab in Settings, and then select Enable host networking.

posted @ 2024-03-12 17:06  Undefined443  阅读(1741)  评论(0)    收藏  举报