frp简单部署
Easul Lv6

相关文档

服务端的配置

脚本能力

以下均在 deepin + x86_64 环境下测试

  • 配置必备变量后自动下载并部署frp服务端
  • 进行服务端的服务注册及自启动相关操作
  • 提供服务异常崩溃退出后的自动重连
  • 自动清理处理过程中的无用文件
  • 部署后提供frp客户端的连接
  • 为不同frp客户端传输数据。
  • 提供frp服务端对客户端的监控
  • 提供frp版本设置,便于固定与修改版本

一键脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 脚本请设置如下值
#
# 服务端的frps暴露端口,用于数据传输
FRP_SERVER_PORT="27000"
# frp服务端的监控端口
FRPS_MONITOR_SERVER_PORT="27500"
# frp服务端的监控账号名称
FRPS_MONITOR_USER="xxx"
# frp服务端的监控账号密码
FRPS_MONITOR_PASSWORD="xxxxx"
# frp服务端设置的子域名,用于大量客户端的自动泛域名注册
FRPS_SUB_DOMAIN_HOST="xxx.xxxx.xx"
# frp服务端tcpmux的端口
FRPS_TCPMUX_PORT=21337

# 软件下载
mkdir -p /opt/frp
cd /opt/frp
release_latest_version=$(curl -s "https://github.com/fatedier/frp/releases" | grep 'data-view-component="true" class="Link--primary' -m 1 | sed -r 's:.*tag/(.*?)" data-view-component="true".*:\1:g' | cut -c2-)
wget "https://github.com/fatedier/frp/releases/download/v${release_latest_version}/frp_${release_latest_version}_linux_amd64.tar.gz"
tar -zxvf "frp_${release_latest_version}_linux_amd64.tar.gz"
mv "frp_${release_latest_version}_linux_amd64/frp"* .
rm -rf "frp_${release_latest_version}_linux_amd64"
rm -rf "frp_${release_latest_version}_linux_amd64.tar.gz"

# 设置服务端配置文件,相关参数解释如下
#
# bindPort: 服务端的用于进行数据传输的接口,不填默认为7000
# webServer: 用于开启服务端监控页面。由于服务端的监控页面未做过多优化,会整个页面直接加载。连接设备过多的时候,页面会很长
# addr: 监控页面托管地址,填入 0.0.0.0 即可
# port: 监控页面托管端口,可以自定义
# user: 监控页面登录用户
# password: 监控页面登录密码
# subdomainHost: 当客户端特别多的时候,设置该子域名可以自动使用泛域名
# vhostHTTPPort: HTTP类型的客户端服务可访问的端口,可以和 bindPort 一致,实现端口复用
# tcpmuxHTTPConnectPort: tcpmux 类型的客户端服务访问服务时借用的端口。
cat > frps.toml <<EOF
bindPort = $FRP_SERVER_PORT
webServer.addr = "0.0.0.0"
webServer.port = $FRPS_MONITOR_SERVER_PORT
webServer.user = "$FRPS_MONITOR_USER"
webServer.password = "$FRPS_MONITOR_PASSWORD"
subdomainHost="$FRPS_SUB_DOMAIN_HOST"
vhostHTTPPort=$FRP_SERVER_PORT
tcpmuxHTTPConnectPort=$FRPS_TCPMUX_PORT
EOF

# 设置自启动服务
cat > /etc/systemd/system/frps.service <<-'EOF'
[Unit]
Description=frps
After=network.target

[Service]
Type=simple
ExecStart=/opt/frp/frps -c /opt/frp/frps.toml
Restart=always
RestartSec=10
NoNewPrivileges=yes
ProtectSystem=strict

[Install]
WantedBy=default.target
EOF

# 设置自启动
systemctl enable frps
# 启动服务端
systemctl start frps

设置后,服务端可以用 http://服务端IP:27500 来访问监控页面。

客户端的配置

脚本能力

以下均在 ubuntu + armv7 环境下测试

  • 配置必备变量后自动下载并部署frp客户端
  • 自动下载缺失必备命令行工具
  • 进行客户端的服务注册及自启动相关操作
  • 提供服务异常崩溃退出后的自动重连
  • 自动清理处理过程中的无用文件
  • 部署后frp客户端可自动连接到服务端(网络通畅的情况下)
  • 提供frp客户端的监控查看
  • 提供frp版本设置,便于固定与修改版本
  • 提供相关环境的预检操作
  • 对旧环境中 /etc/rc.local 启动项的清理
  • 设置客户端的心跳配置与连接池,以保证客户端实时更新状态与长期可用

一键脚本

可以把脚本在客户端保存为 frpc.sh ,然后 nohup bash frpc.sh & 即可。运行后,脚本会自动被清理掉。
另外这里下载的 frp包armv7 下的版本,其他情况下需根据实际情况修改下自动化脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/bin/bash
# 脚本请设置如下值
#
# 设置该设备的名称
DEVNAME="1122433"
# frp客户端部署的根目录
FRP_ROOT_PATH="/opt/frp"
# frp设置为systemd服务的服务配置存放路径
FRP_SERVICE_ROOT_PATH="/etc/systemd/system"
# 服务端的frps的公网IP或域名
FRP_SERVER_ADDRESS="1.2.3.4"
# 服务端的frps的公网暴露端口,需要为 frps 的 bindPort 的公网映射端口
FRP_SERVER_PORT="27000"
# frp客户端的监控账号名称
FRPC_MONITOR_USER="xxx"
# frp客户端的监控账号密码
FRPC_MONITOR_PASSWORD="xxxxx"

function print_log() {
echo "[$(date +%Y-%m-%dT%H:%M:%S)] $1"
}

# 进行预检操作
function pre_check() {
# pre-check command tools
local tools=(jq)
for tool in ${tools[*]};do
if ! command -v $tool >/dev/null 2>&1; then
apt-get install -y jq
fi
done

# pre-check frpc existence
if [ ! -f $FRP_ROOT_PATH/frpc ];then
print_log "$FRP_ROOT_PATH/frpc 不存在,将自动下载文件"
mkdir -p $FRP_ROOT_PATH
cd $FRP_ROOT_PATH
release_latest_version=$(curl -s "https://github.com/fatedier/frp/releases" | grep 'data-view-component="true" class="Link--primary' -m 1 | sed -r 's:.*tag/(.*?)" data-view-component="true".*:\1:g' | cut -c2-)
wget --no-check-certificate "https://github.com/fatedier/frp/releases/download/v${release_latest_version}/frp_${release_latest_version}_linux_arm_hf.tar.gz"
tar -zxvf "frp_${release_latest_version}_linux_arm_hf.tar.gz"
mv "frp_${release_latest_version}_linux_arm_hf/frpc"* .
rm -rf "frp_${release_latest_version}_linux_arm_hf.tar.gz"
rm -rf "frp_${release_latest_version}_linux_arm_hf"
rm -rf wget-log*
cd -
fi
}

# 初始化 frpc 的配置文件
function init_frpc_toml() {
if [ -z "${FRP_SERVER_ADDRESS:-}" ];then
print_log "FRP_SERVER_ADDRESS(FRP服务端地址)不可为空值"
return 1
fi
if [ -z "${FRP_SERVER_PORT:-}" ];then
print_log "FRP_SERVER_PORT(FRP服务端端口)不可为空值"
return 1
fi
if [ -z "${FRPC_MONITOR_USER:-}" ];then
print_log "FRPC_MONITOR_USER(FRP客户端用户名)不可为空值"
return 1
fi
if [ -z "${FRPC_MONITOR_PASSWORD:-}" ];then
print_log "FRPC_MONITOR_PASSWORD(FRP客户端密码)不可为空值"
return 1
fi
echo > "$FRP_ROOT_PATH/frpc.toml"
# 这里演示了 tcpmux 和 http 的配置方式,映射了本机的监控和ssh到外部
tee $FRP_ROOT_PATH/frpc.toml > /dev/null <<EOF
serverAddr = "$FRP_SERVER_ADDRESS"
serverPort = $FRP_SERVER_PORT
transport.heartbeatInterval = 30
transport.heartbeatTimeout = 90
transport.dialServerKeepalive = 60
transport.poolCount = 5
transport.tcpMux = true
transport.tcpMuxKeepaliveInterval = 60

webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "$FRPC_MONITOR_USER"
webServer.password = "$FRPC_MONITOR_PASSWORD"

[[proxies]]
name = "$DEVNAME-monitor"
type = "http"
localIP = "127.0.0.1"
localPort = 7500
transport.useCompression = true
subdomain = "$DEVNAME-monitor"

[[proxies]]
name = "$DEVNAME-ssh"
type = "tcpmux"
localIP = "127.0.0.1"
localPort = 22
multiplexer = "httpconnect"
transport.useCompression = true
subdomain = "$DEVNAME-ssh"
EOF
}

# 初始化 frpc 的服务
function init_frpc_service() {
if ! systemctl list-unit-files | grep -q '^frpc\.service'; then
tee $FRP_SERVICE_ROOT_PATH/frpc.service > /dev/null <<EOF
[Unit]
Description = frpc
After = network.target syslog.target
Wants = network.target

[Service]
Type = simple
ExecStart = $FRP_ROOT_PATH/frpc -c $FRP_ROOT_PATH/frpc.toml
Restart=always
RestartSec=60

[Install]
WantedBy = multi-user.target
EOF
chmod 754 $FRP_SERVICE_ROOT_PATH/frpc.service
systemctl enable frpc.service
print_log "frpc服务已初始化"
fi
}

# 启动frpc
function start_frpc() {
if $(ps aux | grep -v grep | grep -q './frpc -c') ;then
ps aux | grep -v grep | grep './frpc -c' | awk '{print $2;}' | xargs kill -9 > /dev/null 2>&1
fi
if systemctl is-active --quiet frpc.service; then
systemctl restart frpc.service
print_log "frpc已重启,可使用 systemctl status frpc.service 查看当前启动日志"
else
systemctl start frpc.service
print_log "frpc服务已启动"
fi
}

# 删除脚本文件与临时生成文件
function delete_useless_files() {
local current_directory=$(pwd $(dirname $0))
local current_script_name=$(echo $0)
rm -rf $current_directory/$current_script_name
rm -rf $current_directory/nohup.out
}

function main() {
pre_check || exit 1
init_frpc_toml || exit 1
init_frpc_service || exit 1
start_frpc || exit 1
delete_useless_files
}

main

配置之后,访问http服务可以使用 http://1122433-monitor.xxx.xxxx.xx:27500,即 设备名称服务端子域名服务端的vhostHTTPPort 拼起来即可。
SSH的连接,可以使用如下方式

1
2
3
4
5
6
7
# 指定要连接的SSH的 subdomain
device_name="1122433-ssh"
# 使用 socat 利用 frps 的http代理来进行访问
# 没有 socat 命令可以装一下
# 服务端IP 和 服务端tcpmuxHTTPConnectPort的端口 根据实际情况调整一下
# 这样就可以通过SSH命令直接访问客户端了
ssh -o 'proxycommand socat - PROXY:服务端IP:%h:%p,proxyport=服务端tcpmuxHTTPConnectPort的端口' root@$device_name.xxx.xxxx.xx

FRPC的核心配置解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 服务端的frps的公网IP或域名
serverAddr = "1.2.3.4"
# 服务端的frps的公网暴露端口,需要为 frps 的 bindPort 的公网映射端口
serverPort = 27000

# 传输层配置
#
# 向服务端发送心跳包的间隔时间,,避免长连接被误判断开
# 官方文档建议,当 tcpMuxKeepaliveInterval设置后, 此值为-1,
# https://gofrp.org/zh-cn/docs/reference/client-configures/#clienttransportconfig
transport.heartbeatInterval = -1
# 和服务端心跳超时时间
transport.heartbeatTimeout = 90
# 和服务端底层 TCP 连接的 keepalive 间隔时间,单位秒。用于保持活跃
transport.dialServerKeepalive = 60
# 建立连接池,提高稳定性
transport.poolCount = 5
# 启用 TCP 多路复用
transport.tcpMux = true
# tcpMux 的心跳检查间隔时间
transport.tcpMuxKeepaliveInterval = 60

# 客户端监控托管的IP,使用该值即可
webServer.addr = "127.0.0.1"
# 客户端监控托管的端口
webServer.port = 7500
# 客户端监控的用户名
webServer.user = "xxx"
# 客户端监控的密码
webServer.password = "xxxxx"

# 每一个块相当于客户端要在服务端映射的一个服务
#
# 这里映射了http服务
[[proxies]]
# 该代理服务的名称,可以为 `设备号-功能名称`,如下
name = "1122433-monitor"
# 指定映射类型为 http 类型
type = "http"
# 指定映射的本地服务IP
localIP = "127.0.0.1"
# 指定映射的本地服务端口
localPort = 7500
# 启动传输信息的压缩功能
transport.useCompression = true
# 设定为这个值后,若 frps 的 subdomainHost 为 xxx.xxxx.xx,即可在服务端用 1122433-monitor.xxx.xxxx.xx 来访问该http服务
subdomain = "1122433-monitor"

# 这里映射了SSH
[[proxies]]
# 该代理服务的名称,可以为 `设备号-功能名称`,如下
name = "1122433-ssh"
# 指定映射类型为 tcpmux
type = "tcpmux"
# 指定映射的本地服务IP
localIP = "127.0.0.1"
# 指定映射的本地服务端口
localPort = 22
# 设置SSH的多路复用器为 httpconnect
multiplexer = "httpconnect"
# 启动传输信息的压缩功能
transport.useCompression = true
# 设定为这个值后,若 frps 的 subdomainHost 为 xxx.xxxx.xx,即可在服务端用 1122433-ssh.xxx.xxxx.xx 来访问该ssh服务
subdomain = "1122433-ssh"

# 这里映射了纯tcp流量,用于无法进行多路复用的情况
# 例如转发websocket
[[proxies]]
# 该代理服务的名称,可以为 `设备号-功能名称`,如下
name = "1122433-stream"
# 指定映射类型为 tcp
type = "tcp"
# 指定映射的本地服务IP
localIP="192.168.0.1"
# 指定映射的本地服务端口
localPort=1254
# 指定远程映射的端口
remotePort=1254
# 启动传输信息的压缩功能
transport.useCompression = true

后续可尝试

目前只实现了 HTTPSSH 服务的多路复用,如果网页中需要映射另一个服务的 websocket 服务,则无法直接映射,可基于现有情况再分析下如何配置来实现效果。

废弃

简单手动测试部署流程

该部分操作比较简单,可不参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 在如下releases界面下载frp的二进制打包文件,压缩包内有客户端和服务端
# 需要根据自己设备的详情来下载对应架构的二进制包,这里服务端我先以最新版 linux 下的 amd64 架构的操作为例
# https://github.com/fatedier/frp/releases
mkdir -p ~/software/
cd ~/software/
wget https://github.com/fatedier/frp/releases/download/v0.64.0/frp_0.64.0_linux_amd64.tar.gz
tar -zxvf frp_0.64.0_linux_amd64.tar.gz
rm -rf frp_0.64.0_linux_amd64.tar.gz
mv frp_0.64.0_linux_amd64 frp
cd frp
# 先在服务端进行启动,这里默认服务端的端口是7000,如果需要改一下的话可以修改 frps.toml 中的端口号,然后进行启动
# 对应的公网要将 7000 端口给放行,这样的话客户端就可以通过 7000 来将端口映射到服务端了
./frps -c frps.toml
# 接着在客户端下载对应架构的二进制包
# 我这里是在 android 的 termux 上边测试的,所以我下载了 android 的 arm64 的包
mkdir -p ~/software/
cd ~/software/
wget https://github.com/fatedier/frp/releases/download/v0.64.0/frp_0.64.0_android_arm64.tar.gz
tar -zxvf frp_0.64.0_android_arm64.tar.gz
rm -rf frp_0.64.0_android_arm64.tar.gz
mv frp_0.64.0_android_arm64 frp
cd frp
# 这里需要对客户端的配置文件 frpc.toml 进行一些配置,主要如下
# serverAddr: 服务端的 ff
# serverPort: 刚才服务端托管的端口,如果刚刚改过,不是7000了,这里也要同步改一下
# name: 可以根据需求改一下客户端的名字
# localIP: 进行反向代理的设备的IP,如果为本机IP则不需要修改
# localPort: 进行反向代理的程序的端口,为需要暴露到公网的服务的端口
# remotePort: 需要在服务端进行外网访问的端口,默认为6000,可以自定义。
# 定义好之后,这个端口需要在服务端的公网放行,然后用下边的命令启动后,就可以把本地服务映射到公网了
./frpc -c frpc.toml
 评论