什么是内网穿透
在了解内网穿透原理之前,我们先说什么是内网穿透。内网,就是在公司或者家庭内部,建立的局域网络或者是办公网络,可以实现多台电脑之间的资源共享,包括设备、资料、数据等。而外网则是通过一个网关与其它的网络系统连接,相对于内网而言,这种网络系统称之为外部网络,常见的就是我们日常使用的互联网。
一般而言,在没有固定公网IP的情况下,外网设备无法直接访问内网设备。而内网穿透技术,顾名思义就是能让外网的设备找到处于内网的设备,从而实现数据通信。
内网穿透的原理
内网穿透,又称为NAT穿透。NAT背后的设备,它们的主要特点是 ,可以访问外网,但不能被外网设备有效访问。基于这一特点,NAT穿透技术是让NAT背后的设备,先访问指定的外网服务器,由指定的外网服务器搭建桥梁,打通内、外网设备的访问通道,实现外网设备访问到内网设备。
该技术除了可以访问隐藏在NAT后的设备,同样可以穿透防火墙。这是因为防火墙一般只拦截了入站没有拦截出站,所以也可以让防火墙内的设备对外提供服务。
由于内网设备并不是与外网设备直接相连,所以在安全性上是毋庸置疑的,内网穿透可以说是安全与效率兼得。
如何实现内网穿透
常见工具
| 名称 | 说明 | 复杂程度 | 下载地址 |
|---|---|---|---|
| 花生壳 | 免费的工具,安装就可以实现内网穿透功能,免费端口数量限制,不能使用80,所有数据都从他们服务端过 | 简单 | hsk.oray.com/ |
| nat123 | 免费的工具,安装就可以实现内网穿透功能,免费端口限制,所有数据都从他们服务端过 | 简单 | www.nat123.com/ |
| Frp | frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 | 复杂 | github.com/fatedier/fr… |
| Ngrok | ngrok是一个反向代理,它创建一个从公共端点到本地运行的web服务的安全隧道。ngrok捕获并分析隧道内的所有流量,以备日后检查和回放。注册后提供免费的公共服务器 | 复杂 | github.com/inconshreve… |
| lanproxy | lanproxy是一个将局域网个人电脑、服务器代理到公网的内网穿透工具,支持tcp流量转发,可支持任何tcp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面、http代理、https代理、socks5代理. | 复杂 | github.com/ffay/lanpro… |
| goproxy | Proxy是golang实现的高性能http,https,websocket,tcp,socks5代理服务器,支持内网穿透,链式代理,通讯加密,智能HTTP,SOCKS5代理,黑白名单,限速,限流量,限连接数,跨平台,KCP支持,认证API。 | 复杂 | github.com/snail007/go… |
| nps | 一款轻量级、高性能、功能强大的内网穿透代理服务器。支持tcp、udp、socks5、http等几乎所有流量转发,可用来访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析、内网socks5代理等等……,并带有功能强大的web管理端。 | 复杂 | github.com/cnlh/nps |
搭建示例
Frp的搭建(windows版本)
frp 主要由 客户端(frpc) 和 服务端(frps) 组成,服务端通常部署在具有公网 IP 的机器上,客户端通常部署在需要穿透的内网服务所在的机器上。
内网服务由于没有公网 IP,不能被非局域网内的其他用户访问。
用户通过访问服务端的 frps,由 frp 负责根据请求的端口或其他信息将请求路由到对应的内网机器,从而实现通信。
1.下载Frp的程序
可以在github上下载release版本:github.com/fatedier/fr… 或者在本文的附件中下载frp程序
2.服务端安装
需要在云服务上,有固定公网ip地址的服务器上安装服务端,并且设置的端口需要可以访问。 把安装包复制到云服务器上,修改frps.ini的配置。 服务端的全部配置的详细说明如下: 基础配置
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|
| 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 | ||
| tcp_mux_keepalive_interval | int | tcp_mux | 的心跳检查间隔时间 60 | 单位:秒 | |
| heartbeat_timeout | int | 服务端和客户端心跳连接的超时时间 | 90 | 单位:秒 | |
| user_conn_timeout | int | 用户建立连接后等待客户端响应的超时时间 | 10 | 单位:秒 | |
| udp_packet_size | int | 代理 UDP 服务时支持的最大包长度 | 1500 | 服务端和客户端的值需要一致 | |
| tls_cert_file | string | TLS 服务端证书文件路径 | |||
| tls_key_file | string | TLS 服务端密钥文件路径 | |||
| tls_trusted_ca_fil | e string | TLS CA 证书路径 | |||
| 权限验证 |
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|
| 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 用户名 | |||
| dashboard_pwd | string | HTTP BasicAuth 密码 | |||
| 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 类型的代理,默认不启用 |
我们这边只配置最简单的方式,修改frps.ini的配置文件为:
[common]
bind_port = 22354
token = woxuwireless
其中bind_port基于自己服务器的实际情况配置范围 1~65535,配置的端口服务器必须要要开放。
token就是自己设置的认证信息,所有的客户端都要保持一致,自己定义
打开 cmd 终端,在cmd终端中执行命令,终端必须在frp根目录, 在执行命令 ./frps -c ./frps.ini 启动服务端
3.客户端安装
把安装包复制到内网需要开放的服务器上(也可以复制到其他电脑上,其他电脑必须可以访问需要开发的服务器)。 修改frpc.ini配置,详细的frpc配置介绍如下:
基础配置
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|
| server_addr | string | 连接服务端的地址 | 0.0.0.0 | ||
| server_port | int | 连接服务端的端口 | 7000 | ||
| connect_server_local_ip | string | 连接服务端时所绑定的本地 IP | |||
| dial_server_timeout | int | 连接服务端的超时时间 | 10 | ||
| http_proxy | string | 连接服务端使用的代理地址 | 格式为 {protocol}://user:passwd@192.168.1.128:8080 protocol 目前支持 http、socks5、ntlm | ||
| 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 | ||
| tls_cert_file | string | TLS 客户端证书文件路径 | |||
| tls_key_file | string | TLS 客户端密钥文件路径 | |||
| tls_trusted_ca_file | string | TLS CA 证书路径 | |||
| tls_server_name | string | TLS Server 名称 | 为空则使用 server_addr | ||
| disable_custom_tls_first_byte | bool | TLS 不发送 0x17 | false | 当为 true 时,不能端口复用 | |
| tcp_mux_keepalive_interval | int | tcp_mux 的心跳检查间隔时间 | 60 | 单位:秒 | |
| heartbeat_interval | int | 向服务端发送心跳包的间隔时间 | 30 | 建议启用 tcp_mux_keepalive_interval,将此值设置为 -1 | |
| heartbeat_timeout | int | 和服务端心跳的超时时间 | 90 | ||
| udp_packet_size | int | 代理 UDP 服务时支持的最大包长度 | 1500 | 服务端和客户端的值需要一致 | |
| start | string | 指定启用部分代理 | 当配置了较多代理,但是只希望启用其中部分时可以通过此参数指定,默认为全部启用 | ||
| meta_xxx | map | 附加元数据 | 会传递给服务端插件,提供附加能力 |
权限验证
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|
| 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 | |||
| oidc_additional_xxx | map | OIDC 附加参数 | map 结构,key 需要以 oidc_additional_ 开头 | ||
| UI |
| 参数 | 类型 | 说明 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|
| admin_addr | string | 启用 AdminUI 监听的本地地址 | 0.0.0.0 | ||
| admin_port | int | 启用 AdminUI 监听的本地端口 | 0 | ||
| admin_user | string | HTTP BasicAuth 用户名 | |||
| admin_pwd | string | HTTP BasicAuth 密码 | |||
| 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 | 本地服务端口 | 是 | 配合 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 |
UDP
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|---|
| remote_port | int | 服务端绑定的端口 | 是 | 用户访问此端口的请求会被转发到 local_ip:local_port |
HTTP custom_domains 和 subdomain 必须要配置其中一个,两者可以同时生效。
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|---|
| custom_domains | []string | v服务器绑定自定义域名 | 是(和 subdomain 两者必须配置一个) | 用户通过 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 和 subdomain 必须要配置其中一个,两者可以同时生效。
| 参数 | 类型 | 说明 | 是否必须 | 默认值 | 可选值 | 备注 |
|---|---|---|---|---|---|---|
| custom_domains | []string | 服务器绑定自定义域名 | 是(和 subdomain 两者必须配置一个) | 用户通过 vhost_http_port 访问的 HTTP 请求如果 Host 在 custom_domains 配置的域名中,则会被路由到此代理配置的本地服务 | ||
| subdomain | 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 | 密钥 | 是 | 服务端和访问端的密钥需要一致,访问端才能访问到服务端 |
我们这边只配置最简单的方式,修改frpc.ini的配置文件为:
[common]
server_addr = 47.62.44.12
server_port = 22354
token = {{token}}
[nat1]
type = tcp
local_ip = 192.168.1.100
local_port = 8001
remote_port = 1801
[nat2]
type = tcp
local_ip = 192.168.1.101
local_port = 8080
remote_port = 1802
[common]节点下面为客户端配置
[nat1]和[nat2]为代理配置,详细的参数请参考上面的参数说明。
运行前请确保服务端已经启动成功,打开 cmd 终端,在cmd终端中执行命令,终端必须在frp根目录, 在执行命令 ./frpc -c ./frpc.ini 启动客户端
上面示例的效果是:
访问 47.62.44.12:1801就可以访问到 内网的192.168.1.100:8001
访问 47.62.44.12:1802就可以访问到 内网的192.168.1.102:8080
更多用法,请参考frp官方文档