本文最后更新于:2024年5月7日 下午
为了让家中的NAS 可以为不在局域网范围内的我提供服务,而家里又没有公网IP,需要进行内网穿透,frp是一款优秀好用的工具,本文介绍 ssh, http 内网穿透方法与示例。
frp 简介
-
frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。
-
frp目前最新版本为0.38.0,frp目前仍然处于前期开发阶段,未经充分测试与验证,不推荐用于生产环境。
-
本文记录快速搭建 frp 并使用的方法,更多细节请参考官方文档。
-
官方 github: https://github.com/fatedier/frp
背景
frp 适用于有公网IP需求的用户,如果有需要在互联网中找到自己的电脑,但是电脑又没有公网IPv4地址,又不想用IPv6地址,恰好还有一个空闲的拥有IPv4公网地址的云服务器,那么frp 是使用你需要的工具。
使用条件
- 内网计算机有在公网 IP 提供服务的需求
- 有拥有公网 IP 的计算机(可以是云服务器,别人家的电脑等)
- 如果有http / https 服务需求还要有备案的域名
- 没有域名也可以配置 tcp 映射完成网站穿透
使用教程
安装
以我的安装了Ubuntu 20.04 的 x86_64 cpu 主机为例,我需要下载 frp版本
_ Linux
_ amd64
的安装包:
1
| wget https://github.91chi.fun//https://github.com//fatedier/frp/releases/download/v0.38.0/frp_0.38.0_linux_amd64.tar.gz
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $ tar -axvf frp_0.38.0_linux_amd64.tar.gz
frp_0.38.0_linux_amd64/ frp_0.38.0_linux_amd64/frpc_full.ini frp_0.38.0_linux_amd64/frps.ini frp_0.38.0_linux_amd64/frps_full.ini frp_0.38.0_linux_amd64/LICENSE frp_0.38.0_linux_amd64/frpc frp_0.38.0_linux_amd64/systemd/ frp_0.38.0_linux_amd64/systemd/frps@.service frp_0.38.0_linux_amd64/systemd/frpc.service frp_0.38.0_linux_amd64/systemd/frps.service frp_0.38.0_linux_amd64/systemd/frpc@.service frp_0.38.0_linux_amd64/frpc.ini frp_0.38.0_linux_amd64/frps
|
1 2
| frp_0.38.0_linux_amd64$ ls frpc frpc_full.ini frpc.ini frps frps_full.ini frps.ini LICENSE systemd
|
其中 frpc
开头的是客户端(client) 的核心文件
其中 frps
开头的是服务器端(server) 的核心文件
systemd
文件夹是建立 systemctl
的示例文件
服务器端
- 服务器可以购买服务商的云服务主机,最便宜的就行,但是必须有公网IP
配置
- 此处关心的核心文件为
frps
和 frps.ini
- 将
frps
复制到 /usr/bin
目录下(也可以按照个人习惯放置)
- 将
frps.ini
放到 /etc/frp
目录下
1 2
| sudo mkdir /etc/frp sudo cp frps.ini /etc/frp
|
1
| sudo vim /etc/frp/frps.ini
|
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
| [common] bind_port = 7000
vhost_http_port = 6988 vhost_https_port = 6989
token=yourtoken
dashboard_port = 6999 dashboard_user = admin dashboard_pwd = admin
log_file = ./frps.log log_level = info log_max_days = 3
heartbeat_timeout = 90
privilege_allow_ports = 6000-7000, 3001
max_pool_count = 100
max_ports_per_client = 0
authentication_timeout = 900
tcp_mux = true
|
其中:
参数 |
含义 |
bind_port |
表示内网计算机和服务器计算机的通信端口,需要打开外网计算机的端口防火墙限制 |
vhost_http_port |
http 服务映射端口,需要打开外网计算机的端口防火墙限制 |
vhost_https_port |
https 服务映射端口,需要打开外网计算机的端口防火墙限制 |
token |
协商令牌,客户端和服务器需要一致才可以生效 |
dashboard_port |
frp 管理端口,需要打开外网计算机的端口防火墙限制 |
dashboard_user |
管理端口用户名 |
dashboard_pwd |
管理端口密码 |
log_file |
日志文件位置 |
log_level |
日志级别,分为debug, info, warn, error四级 |
log_max_days |
日志保存的天数 |
heartbeat_timeout |
心跳配置 |
privilege_allow_ports |
frp内网穿透服务端监听的端口,如果不设置的话,所有端口都可以连接使用,但为为了不占用系统使用的端口号,建议设置允许的监听端口,比如www.chuantou.org提供的内网穿透服务器就是开放50000-60000端口 |
max_pool_count |
连接池的数量,如果frp内网穿透客户端设置的连接池的数量大于下面的数值,就会修改frp客户端的连接池为下面的数值 |
max_ports_per_client |
每个客户端最大可以使用的端口,0表示无限制 |
authentication_timeout |
frp内网穿透服务端frps和frp内网穿透的客户端frpc两台电脑的时间差,如果设置为0的话,不校验时间差异,默认校验时间差为900秒。 |
tcp_mux |
是否使用tcp复用,默认为true, frp只对同意客户端的连接进行复用 |
开启服务
1
| /usr/bin/frps -c /etc/frp/frps.ini
|
便可以开启服务了
测试
- 开启后可以访问
公网IP:6999
查看 frp 的工作状态,也说明我们成功开启了服务器端 frp 服务
开机启动
- 接下来关闭服务,我们设置服务器端 frps 服务开机启动
- 将解压的压缩包中
systemd
文件夹中的 frps.service
文件复制到 /usr/lib/systemd/system
文件夹中
- 如果你的 frps 文件和配置文件都按照我上述步骤完成的不需要修改
frps.service
文件,否则需要将文件中 ExecStart
值更改为你执行命令时的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [Unit] Description=Frp Server Service After=network.target
[Service] Type=simple User=nobody Restart=on-failure RestartSec=5s ExecStart=/usr/bin/frps -c /etc/frp/frps.ini LimitNOFILE=1048576
[Install] WantedBy=multi-user.target
|
- 重新加载 systemd 并开启服务,设置开机启动:
相关 systemd 知识可以参考 Linux Systemd 实战
1 2 3 4 5 6
| sudo systemctl daemon-reload
sudo systemctl start frps
sudo systemctl enable frps
|
客户端
配置
- 将
frpc.ini
文件放到 /etc/frp
文件夹中
1 2
| sudo mkdir /etc/frp sudo cp frpc.ini /etc/frp
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| [common] server_addr=23.23.23.23 server_port=7000 token=yourtoken
[ssh] type = tcp local_ip =127.0.0.1 local_port = 22 remote_port = 6987
[http] type=http local_port=4000 custom_domains=test.zywvvd.com
[web] type = tcp local_ip = 127.0.0.1 local_port = 80 remote_port = 80
|
该配置文件主要绑定了两个业务——ssh 和网站服务,其中http为http模式的网页,需要域名,如果没有域名可以直接使用80端口tcp映射完成无域名网页穿透
|
|
server_addr |
公网服务器的公网IP |
server_port |
和服务器端配置一致 |
token |
和服务器端配置一致 |
[ssh] / [http] |
服务名称,根据个人情况设置 |
type |
服务类型,包含 tcp, udp, http, https 等,ssh 使用 tcp即可 |
local_ip |
本机IP,建议设置127.0.0.1 ,就不用来回改了 |
local_port |
本地映射端口,其实本质为本地端口数据映射到服务器端端口 |
remote_port |
服务器端端口 |
custom_domains |
http 类型必填,需要域名解析到该公网IP上(http 类型服务需要) |
域名解析
- 对于http服务需要域名解析
- 以我的百度智能云域名为例
开启服务
1
| /usr/bin/frpc -c /etc/frp/frpc.ini
|
即可开启客户端服务
ssh 测试
1 2 3 4
| ssh user_name@公网IP:6987
ssh user_name@公网IP -p 6987
|
输入密码即可远程公网ssh访问自己的电脑
http 测试
- 此时我把我的个人网站挂载在我本机的 4000 端口上
- 公网访问
test.zywvvd.com:6988
即可看到我本地的网站,说明我们的http穿透成功
web 测试
- 公网访问公网IP,可以直接访问本地映射到80端口的网页
注意:一个 frp 服务器只可以同一时间有一个 http 服务,有一个绑定 http 服务的客户机后,其余http服务不会被支持
开机启动
- 接下来关闭服务,我们设置服务器端 frpc 服务开机启动
- 将解压的压缩包中
systemd
文件夹中的 frpc.service
文件复制到 /usr/lib/systemd/system
文件夹中
- 如果你的 frpc 文件和配置文件都按照我上述步骤完成的不需要修改
frpc.service
文件,否则需要将文件中 ExecStart
和 ExecReload
值更改为你执行命令时的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| [Unit] Description=Frp Client Service After=network.target
[Service] Type=simple User=nobody Restart=on-failure RestartSec=5s ExecStart=/etc/frp/frpc -c /etc/frp/frpc.ini ExecReload=/etc/frp/frpc reload -c /etc/frp/frpc.ini LimitNOFILE=1048576
[Install] WantedBy=multi-user.target
|
- 重新加载 systemd 并开启服务,设置开机启动:
1 2 3 4 5 6
| sudo systemctl daemon-reload
sudo systemctl start frpc
sudo systemctl enable frpc
|
- 至此我们的客户端彻底设置完成,也就完成了 ssh 和 http 的内网穿透
服务端配置详细信息
frp 服务端详细配置说明,官方的极简设置只要设置红色 bind_port即可。
稍微复杂点可以选择紫色的条目
基础配置
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
bind_addr |
string |
服务端监听地址 |
0.0.0.0 |
|
|
bind_port |
int |
服务端监听端口 |
7000 |
|
接收 frpc 的连接 |
bind_udp_port |
int |
服务端监听 UDP 端口 |
0 |
|
用于辅助创建 P2P 连接 |
kcp_bind_port |
int |
服务端监听 KCP 协议端口 |
0 |
|
用于接收采用 KCP 连接的 frpc |
proxy_bind_addr |
string |
代理监听地址 |
同 bind_addr |
|
可以使代理监听在不同的网卡地址 |
log_file |
string |
日志文件地址 |
./frps.log |
|
如果设置为 console,会将日志打印在标准输出中 |
log_level |
string |
日志等级 |
info |
trace, debug, info, warn, error |
|
log_max_days |
int |
日志文件保留天数 |
3 |
|
|
disable_log_color |
bool |
禁用标准输出中的日志颜色 |
false |
|
|
detailed_errors_to_client |
bool |
禁用服务端返回详细错误信息给客户端 |
true |
|
|
heart_beat_timeout |
int |
服务端和客户端心跳连接的超时时间 |
90 |
|
单位:秒 |
user_conn_timeout |
int |
用户建立连接后等待客户端响应的超时时间 |
10 |
|
单位:秒 |
权限验证
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
authentication_method |
string |
鉴权方式 |
token |
token, oidc |
|
authenticate_heartbeats |
bool |
开启心跳消息鉴权 |
false |
|
|
authenticate_new_work_conns |
bool |
开启建立工作连接的鉴权 |
false |
|
|
token |
string |
鉴权使用的 token 值 |
|
|
客户端需要设置一样的值才能鉴权通过 |
oidc_issuer |
string |
oidc_issuer |
|
|
|
oidc_audience |
string |
oidc_audience |
|
|
|
oidc_skip_expiry_check |
bool |
oidc_skip_expiry_check |
|
|
|
oidc_skip_issuer_check |
bool |
oidc_skip_issuer_check |
|
|
|
管理配置
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
allow_ports |
string |
允许代理绑定的服务端端口 |
|
|
格式为 1000-2000,2001,3000-4000 |
max_pool_count |
int |
最大连接池大小 |
5 |
|
|
max_ports_per_client |
int |
限制单个客户端最大同时存在的代理数 |
0 |
|
0 表示没有限制 |
tls_only |
bool |
只接受启用了 TLS 的客户端连接 |
false |
|
|
Dashboard, 监控
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
dashboard_addr |
string |
启用 Dashboard 监听的本地地址 |
0.0.0.0 |
|
|
dashboard_port |
int |
启用 Dashboard 监听的本地端口 |
0 |
|
|
dashboard_user |
string |
HTTP BasicAuth 用户名 |
admin |
|
|
dashboard_pwd |
string |
HTTP BasicAuth 密码 |
admin |
|
|
enable_prometheus |
bool |
是否提供 Prometheus 监控接口 |
false |
|
需要同时启用了 Dashboard 才会生效 |
asserts_dir |
string |
静态资源目录 |
|
|
Dashboard 使用的资源默认打包在二进制文件中,通过指定此参数使用自定义的静态资源 |
HTTP & HTTPS
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
vhost_http_port |
int |
为 HTTP 类型代理监听的端口 |
0 |
|
启用后才支持 HTTP 类型的代理,默认不启用 |
vhost_https_port |
int |
为 HTTPS 类型代理监听的端口 |
0 |
|
启用后才支持 HTTPS 类型的代理,默认不启用 |
vhost_http_timeout |
int |
HTTP 类型代理在服务端的 ResponseHeader 超时时间 |
60 |
|
|
subdomain_host |
string |
二级域名后缀 |
|
|
|
custom_404_page |
string |
自定义 404 错误页面地址 |
|
|
|
TCPMUX
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
tcpmux_httpconnect_port |
int |
为 TCPMUX 类型代理监听的端口 |
0 |
|
启用后才支持 TCPMUX 类型的代理,默认不启用 |
客户端配置详细信息
基础配置
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
server_addr |
string |
连接服务端的地址 |
0.0.0.0 |
|
|
server_port |
int |
连接服务端的端口 |
7000 |
|
|
http_proxy |
string |
连接服务端使用的代理地址 |
|
|
格式为 {protocol}😕/user:passwd@192.168.1.128:8080 protocol 目前支持 http 和 socks5 |
log_file |
string |
日志文件地址 |
./frpc.log |
|
如果设置为 console,会将日志打印在标准输出中 |
log_level |
string |
日志等级 |
info |
trace, debug, info, warn, error |
|
log_max_days |
int |
日志文件保留天数 |
3 |
|
|
disable_log_color |
bool |
禁用标准输出中的日志颜色 |
false |
|
|
pool_count |
int |
连接池大小 |
0 |
|
|
user |
string |
用户名 |
|
|
设置此参数后,代理名称会被修改为 {user}.{proxyName},避免代理名称和其他用户冲突 |
dns_server |
string |
使用 DNS 服务器地址 |
|
|
默认使用系统配置的 DNS 服务器,指定此参数可以强制替换为自定义的 DNS 服务器地址 |
login_fail_exit |
bool |
第一次登陆失败后是否退出 |
true |
|
|
protocol |
string |
连接服务端的通信协议 |
tcp |
tcp, kcp, websocket |
|
tls_enable |
bool |
启用 TLS 协议加密连接 |
false |
|
|
heartbeat_interval |
int |
向服务端发送心跳包的间隔时间 |
30 |
|
|
heartbeat_timeout |
int |
和服务端心跳的超时时间 |
90 |
|
|
start |
string |
指定启用部分代理 |
|
|
当配置了较多代理,但是只希望启用其中部分时可以通过此参数指定,默认为全部启用 |
权限验证
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
authentication_method |
string |
鉴权方式 |
token |
token, oidc |
需要和服务端一致 |
authenticate_heartbeats |
bool |
开启心跳消息鉴权 |
false |
|
需要和服务端一致 |
authenticate_new_work_conns |
bool |
开启建立工作连接的鉴权 |
false |
|
需要和服务端一致 |
token |
string |
鉴权使用的 token 值 |
|
|
需要和服务端设置一样的值才能鉴权通过 |
oidc_client_id |
string |
oidc_client_id |
|
|
|
oidc_client_secret |
string |
oidc_client_secret |
|
|
|
oidc_audience |
string |
oidc_audience |
|
|
|
oidc_token_endpoint_url |
string |
oidc_token_endpoint_url |
|
|
|
UI
参数 |
类型 |
说明 |
默认值 |
可选值 |
备注 |
admin_addr |
string |
启用 AdminUI 监听的本地地址 |
0.0.0.0 |
|
|
admin_port |
int |
启用 AdminUI 监听的本地端口 |
0 |
|
|
admin_user |
string |
HTTP BasicAuth 用户名 |
admin |
|
|
admin_pwd |
string |
HTTP BasicAuth 密码 |
admin |
|
|
asserts_dir |
string |
静态资源目录 |
|
|
AdminUI 使用的资源默认打包在二进制文件中,通过指定此参数使用自定义的静态资源 |
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
type |
string |
代理类型 |
是 |
tcp |
tcp, udp, http, https, stcp, sudp, xtcp, tcpmux |
|
use_encryption |
bool |
是否启用加密功能 |
否 |
false |
|
启用后该代理和服务端之间的通信内容都会被加密传输 |
use_compression |
bool |
是否启用压缩功能 |
否 |
false |
|
启用后该代理和服务端之间的通信内容都会被压缩传输 |
proxy_protocol_version |
string |
启用 proxy protocol 协议的版本 |
否 |
|
v1, v2 |
如果启用,则 frpc 和本地服务建立连接后会发送 proxy protocol 的协议,包含了原请求的 IP 地址和端口等内容 |
bandwidth_limit |
string |
设置单个 proxy 的带宽限流 |
否 |
|
|
单位为 MB 或 KB,0 表示不限制,如果启用,会作用于对应的 frpc |
本地服务配置
local_ip
和 plugin
的配置必须配置一个,且只能生效一个,如果配置了 plugin
,则 local_ip
配置无效。
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
local_ip |
string |
本地服务 IP |
是 |
127.0.0.1 |
|
需要被代理的本地服务的 IP 地址,可以为所在 frpc 能访问到的任意 IP 地址 |
local_port |
int |
本地服务端口 |
是 |
xxxx |
|
配合 local_ip |
plugin |
string |
客户端插件名称 |
否 |
|
见客户端插件的功能说明 |
用于扩展 frpc 的能力,能够提供一些简单的本地服务,如果配置了 plugin,则 local_ip 和 local_port 无效,两者只能配置一个 |
plugin_params |
map |
客户端插件参数 |
否 |
|
|
map 结构,key 需要都以 “plugin_” 开头,每一个 plugin 需要的参数也不一样,具体见客户端插件参数中的内容 |
负载均衡和健康检查
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
group |
string |
负载均衡分组名称 |
否 |
|
|
用户请求会以轮询的方式发送给同一个 group 中的代理 |
group_key |
string |
负载均衡分组密钥 |
否 |
|
|
用于对负载均衡分组进行鉴权,group_key 相同的代理才会被加入到同一个分组中 |
health_check_type |
string |
健康检查类型 |
否 |
|
tcp,http |
配置后启用健康检查功能,tcp 是连接成功则认为服务健康,http 要求接口返回 2xx 的状态码则认为服务健康 |
health_check_timeout_s |
int |
健康检查超时时间(秒) |
否 |
3 |
|
执行检查任务的超时时间 |
health_check_max_failed |
int |
健康检查连续错误次数 |
否 |
1 |
|
连续检查错误多少次认为服务不健康 |
health_check_interval_s |
int |
健康检查周期(秒) |
否 |
10 |
|
每隔多长时间进行一次健康检查 |
health_check_url |
string |
健康检查的 HTTP 接口 |
否 |
|
|
如果 health_check_type 类型是 http,则需要配置此参数,指定发送 http 请求的 url,例如 “/health” |
TCP
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
remote_port |
int |
服务端绑定的端口 |
是 |
|
|
用户访问此端口的请求会被转发到 local_ip:local_port 当type=tcp时,此项必选 |
UDP
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
remote_port |
int |
服务端绑定的端口 |
是 |
|
|
用户访问此端口的请求会被转发到 local_ip:local_port |
HTTP
custom_domains
和 subdomain
必须要配置其中一个,两者可以同时生效。
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
custom_domains |
[]string |
服务器绑定自定义域名 |
是(和 sub_domain 两者必须配置一个) |
|
|
用户通过 vhost_http_port 访问的 HTTP 请求如果 Host 在 custom_domains 配置的域名中,则会被路由到此代理配置的本地服务 |
subdomain |
string |
自定义子域名 |
是(和 custom_domains 两者必须配置一个) |
|
|
和 custom_domains 作用相同,但是只需要指定子域名前缀,会结合服务端的 subdomain_host 生成最终绑定的域名 |
locations |
[]string |
URL 路由配置 |
否 |
|
|
采用最大前缀匹配的规则,用户请求匹配响应的 location 配置,则会被路由到此代理 |
http_user |
string |
用户名 |
否 |
|
|
如果配置此参数,暴露出去的 HTTP 服务需要采用 Basic Auth 的鉴权才能访问 |
http_pwd |
string |
密码 |
否 |
|
|
结合 http_user 使用 |
host_header_rewrite |
string |
替换 Host header |
否 |
|
|
替换发送到本地服务 HTTP 请求中的 Host 字段 |
headers |
map |
替换 header |
否 |
|
|
map 中的 key 是要替换的 header 的 key,value 是替换后的内容 |
HTTPS
custom_domains
和 sub_domain
必须要配置其中一个,两者可以同时生效。
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
custom_domains |
[]string |
服务器绑定自定义域名 |
是(和 sub_domain 两者必须配置一个) |
|
|
用户通过 vhost_http_port 访问的 HTTP 请求如果 Host 在 custom_domains 配置的域名中,则会被路由到此代理配置的本地服务 |
sub_domain |
string |
自定义子域名 |
是(和 custom_domains 两者必须配置一个) |
|
|
和 custom_domains 作用相同,但是只需要指定子域名前缀,会结合服务端的 subdomain_host 生成最终绑定的域名 |
STCP
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
role |
string |
角色 |
是 |
server |
server,visitor |
server 表示服务端,visitor 表示访问端 |
sk |
string |
密钥 |
是 |
|
|
服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
SUDP
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
role |
string |
角色 |
是 |
server |
server,visitor |
server 表示服务端,visitor 表示访问端 |
sk |
string |
密钥 |
是 |
|
|
服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
XTCP
参数 |
类型 |
说明 |
是否必须 |
默认值 |
可选值 |
备注 |
role |
string |
角色 |
是 |
server |
server,visitor |
server 表示服务端,visitor 表示访问端 |
sk |
string |
密钥 |
是 |
|
|
服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
参考资料
文章链接:
https://www.zywvvd.com/notes/environment/nas/frp-usage/frp-usage/