为了让家中的NAS 可以为不在局域网范围内的我提供服务,而家里又没有公网IP,需要进行内网穿透,frp是一款优秀好用的工具,本文介绍 ssh, http 内网穿透方法与示例。
frp 简介 {#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
的安装包:
- 下载安装包:
- 解压
- 得到这样的文件结构
其中 frpc
开头的是客户端(client) 的核心文件
其中 frps
开头的是服务器端(server) 的核心文件
systemd
文件夹是建立 systemctl
的示例文件
服务器端 {#服务器端}
- 服务器可以购买服务商的云服务主机,最便宜的就行,但是必须有公网IP
配置 {#配置}
- 此处关心的核心文件为
frps
和frps.ini
- 将
frps
复制到/usr/bin
目录下(也可以按照个人习惯放置)
- 将
frps.ini
放到/etc/frp
目录下
- 修改
frps.ini
文件 (核心步骤)
其中:
| 参数 | 含义 | |------------------------|---------------------------------------------------------------------------------------------------------------| | 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只对同意客户端的连接进行复用 |
开启服务 {#开启服务}
- 之后我们执行命令:
便可以开启服务了
测试 {#测试}
- 开启后可以访问
公网IP:6999
查看 frp 的工作状态,也说明我们成功开启了服务器端 frp 服务
开机启动 {#开机启动}
- 接下来关闭服务,我们设置服务器端 frps 服务开机启动
- 将解压的压缩包中
systemd
文件夹中的frps.service
文件复制到/usr/lib/systemd/system
文件夹中 - 如果你的 frps 文件和配置文件都按照我上述步骤完成的不需要修改
frps.service
文件,否则需要将文件中ExecStart
值更改为你执行命令时的指令
- 重新加载 systemd 并开启服务,设置开机启动:
相关 systemd 知识可以参考 Linux Systemd 实战
- 至此我们的服务端彻底设置完成
客户端 {#客户端}
- 客户端也需要下载 frp 安装包并解压
配置 {#配置-2}
- 将 frpc 文件复制到
/usr/bin
中
- 将
frpc.ini
文件放到/etc/frp
文件夹中
- 修改
frpc.ini
文件 (核心步骤)
该配置文件主要绑定了两个业务------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服务需要域名解析
- 以我的百度智能云域名为例
- 将自己的公网 IP 解析到一个 A 记录上即可
开启服务 {#开启服务-2}
- 执行命令
即可开启客户端服务
ssh 测试 {#ssh-测试}
- 此时 ssh 服务已经可用了
输入密码即可远程公网ssh访问自己的电脑
http 测试 {#http-测试}
- 此时我把我的个人网站挂载在我本机的 4000 端口上
- 公网访问
test.zywvvd.com:6988
即可看到我本地的网站,说明我们的http穿透成功
web 测试 {#web-测试}
- 公网访问公网IP,可以直接访问本地映射到80端口的网页
注意:一个 frp 服务器只可以同一时间有一个 http 服务,有一个绑定 http 服务的客户机后,其余http服务不会被支持
开机启动 {#开机启动-2}
- 接下来关闭服务,我们设置服务器端 frpc 服务开机启动
- 将解压的压缩包中
systemd
文件夹中的frpc.service
文件复制到/usr/lib/systemd/system
文件夹中 - 如果你的 frpc 文件和配置文件都按照我上述步骤完成的不需要修改
frpc.service
文件,否则需要将文件中ExecStart
和ExecReload
值更改为你执行命令时的指令
- 重新加载 systemd 并开启服务,设置开机启动:
- 至此我们的客户端彻底设置完成,也就完成了 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-监控}
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 | |--------------------|--------|----------------------|---------|-----|----------------------------------------------| | 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 {#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}
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 | |-------------------------|-----|--------------------|-----|-----|---------------------------| | tcpmux_httpconnect_port | int | 为 TCPMUX 类型代理监听的端口 | 0 | | 启用后才支持 TCPMUX 类型的代理,默认不启用 |
客户端配置详细信息 {#客户端配置详细信息}
基础配置 {#基础配置-2}
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 | |--------------------|--------|----------------|------------|---------------------------------|-----------------------------------------------------------------------------| | 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 | 指定启用部分代理 | | | 当配置了较多代理,但是只希望启用其中部分时可以通过此参数指定,默认为全部启用 |
权限验证 {#权限验证-2}
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 | |-----------------------------|--------|-------------------------|-------|-------------|--------------------| | 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 {#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 {#TCP}
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 | |-------------|-----|----------|------|-----|-----|-----------------------------------------------------| | remote_port | int | 服务端绑定的端口 | 是 | | | 用户访问此端口的请求会被转发到 local_ip:local_port 当type=tcp时,此项必选 |
UDP {#UDP}
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 | |-------------|-----|----------|------|-----|-----|-------------------------------------| | remote_port | int | 服务端绑定的端口 | 是 | | | 用户访问此端口的请求会被转发到 local_ip:local_port |
HTTP {#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 {#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 {#STCP}
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 | |------|--------|----|------|--------|----------------|----------------------------| | role | string | 角色 | 是 | server | server,visitor | server 表示服务端,visitor 表示访问端 | | sk | string | 密钥 | 是 | | | 服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
SUDP {#SUDP}
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 | |------|--------|----|------|--------|----------------|----------------------------| | role | string | 角色 | 是 | server | server,visitor | server 表示服务端,visitor 表示访问端 | | sk | string | 密钥 | 是 | | | 服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
XTCP {#XTCP}
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 | |------|--------|----|------|--------|----------------|----------------------------| | role | string | 角色 | 是 | server | server,visitor | server 表示服务端,visitor 表示访问端 | | sk | string | 密钥 | 是 | | | 服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
参考资料 {#参考资料}
- https://blog.csdn.net/u012198893/article/details/82348605
- https://github.com/fatedier/frp
- https://www.cnblogs.com/srczhang/p/12698685.html
- https://www.jianshu.com/p/166b1c59dc3e
- https://www.cryogeny.cn/352.html
- https://www.bilibili.com/video/BV175411J7W6?zw
- https://www.cryogeny.cn/346.html
文章链接:
https://www.zywvvd.com/notes/environment/nas/frp-usage/frp-usage/