安全、稳定、高性能的内网穿透工具,用 Rust 语言编写
rathole,类似于 frp 和 ngrok,可以让 NAT 后的设备上的服务通过具有公网 IP 的服务器暴露在公网上。
- 高性能 具有更高的吞吐量,高并发下更稳定。见Benchmark
- 低资源消耗 内存占用远低于同类工具。见Benchmark。二进制文件最小可以到 ~500KiB,可以部署在嵌入式设备如路由器上。
- 安全性 每个服务单独强制鉴权。Server 和 Client 负责各自的配置。使用 Noise Protocol 可以简单地配置传输加密,而不需要自签证书。同时也支持 TLS。
- 热重载 支持配置文件热重载,动态修改端口转发服务。HTTP API 正在开发中。
一个全功能的 rathole
可以从 release 页面下载。或者 从源码编译 获取其他平台和最小化的二进制文件。
rathole
的使用和 frp 非常类似,如果你有后者的使用经验,那配置对你来说非常简单,区别只是转发服务的配置分离到了服务端和客户端,并且必须要设置 token。
使用 rathole 需要一个有公网 IP 的服务器,和一个在 NAT 或防火墙后的设备,其中有些服务需要暴露在互联网上。
假设你在家里的 NAT 后面有一个 NAS,并且想把它的 ssh 服务暴露在公网上:
- 在有一个公网 IP 的服务器上
创建 server.toml
,内容如下,并根据你的需要调整。
# server.toml
[server]
bind_addr = "0.0.0.0:2333" # `2333` 配置了服务端监听客户端连接的端口
[server.services.my_nas_ssh]
token = "use_a_secret_that_only_you_know" # 用于验证的 token
bind_addr = "0.0.0.0:5202" # `5202` 配置了将 `my_nas_ssh` 暴露给互联网的端口
然后运行:
./rathole server.toml
- 在 NAT 后面的主机(你的 NAS)上
创建 client.toml
,内容如下,并根据你的需要进行调整。
# client.toml
[client]
remote_addr = "myserver.com:2333" # 服务器的地址。端口必须与 `server.bind_addr` 中的端口相同。
[client.services.my_nas_ssh]
token = "use_a_secret_that_only_you_know" # 必须与服务器相同以通过验证
local_addr = "127.0.0.1:22" # 需要被转发的服务的地址
然后运行:
./rathole client.toml
- 现在
rathole
客户端会连接运行在myserver.com:2333
的rathole
服务器,任何到myserver.com:5202
的流量将被转发到客户端所在主机的22
端口。
所以你可以 ssh myserver.com:5202
来 ssh 到你的 NAS。
Systemd examples 中提供了一些让 rathole
在 Linux 上作为后台服务运行的配置示例。
如果只有一个 [server]
和 [client]
块存在的话,rathole
可以根据配置文件的内容自动决定在服务器模式或客户端模式下运行,就像 Quickstart 中的例子。
但 [client]
和 [server]
块也可以放在一个文件中。然后在服务器端,运行 rathole --server config.toml
。在客户端,运行 rathole --client config.toml
来明确告诉 rathole
运行模式。
推荐首先查看 examples 中的配置示例来快速理解配置格式,如果有不清楚的地方再查阅完整配置格式。
关于如何配置 Noise Protocol 和 TLS 来进行加密传输,参见 Transport。
下面是完整的配置格式。
[client]
remote_addr = "example.com:2333" # Necessary. The address of the server
default_token = "default_token_if_not_specify" # Optional. The default token of services, if they don't define their own ones
heartbeat_timeout = 40 # Optional. Set to 0 to disable the application-layer heartbeat test. The value must be greater than `server.heartbeat_interval`. Default: 40 seconds
retry_interval = 1 # Optional. The interval between retry to connect to the server. Default: 1 second
[client.transport] # The whole block is optional. Specify which transport to use
type = "tcp" # Optional. Possible values: ["tcp", "tls", "noise"]. Default: "tcp"
[client.transport.tcp] # Optional. Also affects `noise` and `tls`
proxy = "socks5://user:passwd@127.0.0.1:1080" # Optional. The proxy used to connect to the server. `http` and `socks5` is supported.
nodelay = true # Optional. Override the `client.transport.nodelay` per service
keepalive_secs = 20 # Optional. Specify `tcp_keepalive_time` in `tcp(7)`, if applicable. Default: 20 seconds
keepalive_interval = 8 # Optional. Specify `tcp_keepalive_intvl` in `tcp(7)`, if applicable. Default: 8 seconds
[client.transport.tls] # Necessary if `type` is "tls"
trusted_root = "ca.pem" # Necessary. The certificate of CA that signed the server's certificate
hostname = "example.com" # Optional. The hostname that the client uses to validate the certificate. If not set, fallback to `client.remote_addr`
[client.transport.noise] # Noise protocol. See `docs/transport.md` for further explanation
pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s" # Optional. Default value as shown
local_private_key = "key_encoded_in_base64" # Optional
remote_public_key = "key_encoded_in_base64" # Optional
[client.transport.websocket] # Necessary if `type` is "websocket"
tls = true # If `true` then it will use settings in `client.transport.tls`
[client.services.service1] # A service that needs forwarding. The name `service1` can change arbitrarily, as long as identical to the name in the server's configuration
type = "tcp" # Optional. The protocol that needs forwarding. Possible values: ["tcp", "udp"]. Default: "tcp"
token = "whatever" # Necessary if `client.default_token` not set
local_addr = "127.0.0.1:1081" # Necessary. The address of the service that needs to be forwarded
nodelay = true # Optional. Determine whether to enable TCP_NODELAY for data transmission, if applicable, to improve the latency but decrease the bandwidth. Default: true
retry_interval = 1 # Optional. The interval between retry to connect to the server. Default: inherits the global config
[client.services.service2] # Multiple services can be defined
local_addr = "127.0.0.1:1082"
[server]
bind_addr = "0.0.0.0:2333" # Necessary. The address that the server listens for clients. Generally only the port needs to be change.
default_token = "default_token_if_not_specify" # Optional
heartbeat_interval = 30 # Optional. The interval between two application-layer heartbeat. Set to 0 to disable sending heartbeat. Default: 30 seconds
[server.transport] # Same as `[client.transport]`
type = "tcp"
[server.transport.tcp] # Same as the client
nodelay = true
keepalive_secs = 20
keepalive_interval = 8
[server.transport.tls] # Necessary if `type` is "tls"
pkcs12 = "identify.pfx" # Necessary. pkcs12 file of server's certificate and private key
pkcs12_password = "password" # Necessary. Password of the pkcs12 file
[server.transport.noise] # Same as `[client.transport.noise]`
pattern = "Noise_NK_25519_ChaChaPoly_BLAKE2s"
local_private_key = "key_encoded_in_base64"
remote_public_key = "key_encoded_in_base64"
[server.transport.websocket] # Necessary if `type` is "websocket"
tls = true # If `true` then it will use settings in `server.transport.tls`
[server.services.service1] # The service name must be identical to the client side
type = "tcp" # Optional. Same as the client `[client.services.X.type]
token = "whatever" # Necessary if `server.default_token` not set
bind_addr = "0.0.0.0:8081" # Necessary. The address of the service is exposed at. Generally only the port needs to be change.
nodelay = true # Optional. Same as the client
[server.services.service2]
bind_addr = "0.0.0.1:8082"
rathole
,像许多其他 Rust 程序一样,使用环境变量来控制日志级别。
支持的 Logging Level 有 info
, warn
, error
, debug
, trace
比如将日志级别设置为 error
:
RUST_LOG=error ./rathole config.toml
如果 RUST_LOG
不存在,默认的日志级别是 info
。
从 v0.4.7 开始, rathole 默认启用 TCP_NODELAY。这能够减少延迟并使交互式应用受益,比如 RDP,Minecraft 服务器。但它会减少一些带宽。
如果带宽更重要,比如网盘类应用,TCP_NODELAY 仍然可以通过配置 nodelay = false
关闭。
rathole 的延迟与 frp 相近,在高并发情况下表现更好,能提供更大的带宽,内存占用更少。
关于测试进行的更多细节,参见单独页面 Benchmark。
但是,不要从这里得出结论,rathole
能让内网转发出来的服务快上数倍。 Benchmark 是在本地回环上进行的,其结果说明了任务受 CPU 限制时的结果。当用户的网络不是瓶颈时,用户能得到很大的提升。但是,对很多用户来说并不是这样。在这种情况下,rathole
能带来的主要好处是更少的资源占用,而带宽和延迟不一定有显著的改善。
rathole
正在积极开发中
- 支持 TLS
- 支持 UDP
- 热重载
- 用于配置的 HTTP APIs
Out of Scope 列举了没有计划开发的特性并说明了原因。