前言
Ahri 这个实验性的项目我已经停止维护了。
科学上网需求,继续阅读下文。
我已经独立完成了另一个专门用于科学上网的工具,我称呼ta为 SpreadX。
要我评价 SpreadX,我只会告诉你挺不错的。我用了很久,还开发了 macOS 的 GUI 客户端,但是这个客户端我不会提供给大家。
我国(中国
所以,我已将 SpreadX (的执行文件及用法)直接 release 到 Ahri 的 release 页面。 但是,为了这个工具能存活下去,我不会提供源码。
sha-256 cb65b0f58cc113cd8d897a4eb8fa532d8ccc2a56b7c7c0bbddb9df558fe2155b
如果有人下载了它,并担心使用时有安全问题,请自行搭建测试环境验证,或者反编译解析。 本人承诺,该工具源码仅包含该工具自身的能力,不含有任何恶意代码。
时间(2022-07-05 13:26:00)
有各类内网穿透需求的人,看这里frp。
Ahri
Ahri 是一个高效且易用的网络环境共享工具。
你只需要有一个拥有公网 IP 的 VPS 便可以使用 Ahri 同时访问多个内网环境,并且支持使用公网服务器的网络环境。
Ahri 只想做好三件事。
- 核心:一台电脑同时使用多个内网环境,且不干预其他正常的网络访问。
- 辅助:允许使用公网服务器的网络环境。
- 易用:暴露出尽可能少的配置参数,避免给使用者带来困扰,降低学习使用成本。同时,尽可能的为每一个配置参数给出通用的默认值,将复杂与技术细节留给开发者。
请不要将 Ahri 与 frp,SSR,V2Ray 等项目做比较,因为 Ahri 对使用场景的定位与它们不同。
当然,如果你有优化 Ahri 的意见和建议,我很乐意接受。
Ahri 的特性
特性 | 详情 |
---|---|
通用性 | Ahri 基于 TCP,所以不关心转发的数据包是基于何种应用层协议的,也就是说 Ahri 一定支持 http。理论上讲,ta 支持所有基于 TCP 的上层协议。 |
帧化数据协议 | Ahri 拥有自己的应用层协议(Ahri protocol)来经行流量转发。Ahri 对流量的处理方式类似于 http 2.0,所有的数据均是 帧化 的。 |
安全 | Ahri 采用了 RSA, AES-256-CFB 加密算法保证数据的安全性。 |
高效 | Ahri 由 golang 编写,本身非常底层,执行效率极高。同时,Ahri 采用 多路复用 模式,在 client 与 server 之间仅建立一个 TCP 连接,避免不必要的协议握手。 |
低内存, 低 CPU 使用率 | 采用 sync.pool 降低 GC 压力与内存占用量。* ahri-client 活跃时,MEM < 7.0MB,%CPU ~= 10%,%CPU max < 15.0% 。* ahri-server 活跃时,MEM < 15.0MB,%CPU ~ 1.5%,%CPU max < 2.1%。 |
跨平台 | Ahri 由 golang 编写,轻松跨平台; |
*最简模型下的内存测试环境与情景:
- 在中国大陆连续访问数个 YouTube 1080p 视频播放页(即,当视频开始播放时,马上点击下一个视频地址,使机器保持数个持续的大流量的网络访问)。
- ahri-client 运行在 MacBook Pro <Retina, 13-inch, Early 2015> 乞丐版上。
- ahri-server 运行在 Vultr 的 LA 节点的 VPS 上 <CentOS 7 x64 5.1.14-1.el7.elrepo.x86_64, CPU 1 vCore, RAM 1GB,> 。
编写 Ahri 的初衷
在工作中,因商务合作需要使用几家合作公司的内网环境,导致我需要频繁切换 VPN 配置。 而回到家中,如果偶遇突发情况又无法使用自己公司的内网(因为运维不给配)。 TeamViewer 个人版虽然免费,但时不时会出现服务器宕机的情况。 女友公司的运维配置的 VPN 不稳定。
一系列的恼人事件发生后,我决定编写 Ahri 来改善我的工作与生活质量。同时,希望 Ahri 可以帮助那些遇到类似情况的人。
注意: 请严格遵守你所在地区的相关法律法规, 不要将 Ahri 用于违法犯罪行为(尤其是科学上网); 否则后果自负, 毕竟技术无罪, 最坏的情况我会清除源码.
Ahri 的使用场景
Ahri 适用于但不局限于以下场景,ta 可以解决这些问题。
场景一
你的工作内容需要使用到公司内网,但由于外因,没有 VPN 或根本无法使用。
TeamViewer 无法连接到公司内网的电脑或使用时及其卡顿。
作为开发,需要使用到几个中间件,而它们都在公司内网。
场景二
因商务合作需要使用几家合作公司的内网环境,频繁切换 VPN 配置非常麻烦。
场景三
你所在的网络环境对一些公网域名或 IP 进行了拦截,导致你无法使用它们。
Ahri 的工作原理
Ahri 需要解决的问题其实是流量转发。然而并不是所有的流量都需要转发,或多次转发。 所以,就请求的发起来说,流量的目的地有三种类型。
- 本地:在本机直接拨号(dial)TCP 请求。
- ahri-server: 使用 ahri-server 作为代理,在 ahri-server 上 dial TCP 请求。
- 另一个 ahri-client: 使用另一个 ahri-client 作为代理,在另一个 ahri-client 上 dial TCP 请求。
ahri-server & ahri-client
Ahri 服务由两个二进制程序来提供,它们是 ahri-server,ahri-client。
-
ahri-server 负责响应来自 ahri-client 的请求,或者转发一个 ahri-client 的请求给另一个 ahri-client。
-
ahri-client 负责发起请求,或者响应另一个 ahri-client 的请求。ta 有三个模式。
- take:启动一个 socks5 服务来接受本地的所有 TCP 请求;再按配置好的映射文件(ahri.hosts)决定采用上面的三种流量目的地中的哪一个。
- give:仅负责响应来自其他 ahri-client 的请求。
- trade:同时支持上面两种的模式。
ahri-client 采用主动注册到 ahri-server 的方式来进行连接,而 ahri-server 会对数个 ahri-client 进行管理。 ahri-client 注册到 ahri-server 后,它们之间会有一个内容加密的 TCP 连接。在这个连接中会传递心跳包与各类数据包。
然后再解释上面挖的一个坑,对一个 TCP 连接经行多路复用。
Ahri Protocal
基于 TCP ,Ahri 自行定义了一个应用层协议 Ahri Protocol。 ta 由 Ahri Registe Protocol(ARP)与 Ahri Frame Protocol(AFP)组成。 ARP 定义了 ahri-client 应该怎样注册到 ahri-server。 AFP 定义了 ahri-client 与 ahri-server 应该怎样交互数据包。 详细的内容请查看Ahri Protocol。
在这里我仅想说明,协议中出现了 Frame,表明 Ahri 将数据做了帧化,对数据包的管理方式类似于 HTTP 2.0。在这样的前提下,仅使用一个 TCP 连接便可以使 ahri-client 与 ahri-server 沟通顺畅。
为了让 ahri-server 管理数个 ahri-client,ARP 规定每一个 ahri-client 均有一个名字(最大长度为 2 的ACSII字符)。 每一个 ahri-client 均有一个 ahri.hosts 文件,可以控制本地请求转发的目的地;也可控制是否对他人提供某些域名或 IP 的转发服务。 其中 'S', 'L', '-', '|' 为系统保留名, 禁止使用它们对 ahri-client 命名.
ahri.hosts 示例
我们假设自己的客户端名为 'A', 另一个客户端名为 'B', 且均注册至服务端 S, 以下就是 ahri.hosts 文件的示例.
# 转发本地请求至服务端
youtube.com S
# 转发本地请求至另一个客户端 B
mary-live.com B
# 当前 ahri-client 为 give 或 trade 模式时, 禁止其他客户端访问本地网络环境中的这个域名
tom-live.com -
# 当前 ahri-client 遇到该域名时, 拦截本地请求, 一般用于广告屏蔽
ad-live.com |
# 显式表明请求应该在本地处理, 当然所有请求的默认目的地就是 L, 所以可以不写下面这一行.
baidu.com L
场景解析
我们回顾一下上面的使用场景。 假设我本机 ahri-client 是 A, 我使用的 ahri-server 是 S, 我公司内网(LAN 1)中有一个 ahri-client 是 B, 别人公司内网(LAN 2)中有一个 ahri-client 是 C。
场景一 & 场景二
这两个场景是最常见的 VPN 使用场景,只不过场景二略复杂一点。
当我在自己家时。 我使用自己公司的内网时,流量走向是 A -> S -> B -> LAN 1, 然后原路返回。 我也可以使用对方公司的内网,流量走向是 A -> S -> C -> LAN 2, 然后原路返回。 我也可以正常访问任何公网资源,A -> Internet。
当我在自己公司,使用自己公司的内网时,流量走向是 A -> LAN 1,然后原路返回。 我也可以使用对方公司的内网,流量走向是 A -> S -> C -> LAN 2, 然后原路返回。 我也可以正常访问任何公网资源,A -> Internet。
场景三
对这个场景的处理其实是我在实现场景一、二的过程中顺带写出来的衍生物。 主要就是你所在的网络环境会对一些公网资源的请求进行拦截。 A -> Internet 这条路不通了。所以换线为 A -> S -> Internet。
希望我对上面的场景的解决描述的足够清楚。
Ahri 的用法
我已经对常用的系统完成了源码编译的工作, 你应该可以在 releases 中找到可运行在你系统上的版本. 如果没有, 请自行从源码编译(go1.12.1+).
详细参数与解释仅需要在命令行下执行对应的帮助程序(因为 windows 的限制,需要将 ahri-client 与 ahri-server 先重命名为 ahri-client.exe 与 ahri-server.exe)
客户端
ahri-client -h
服务端
ahri-server -h
# windows 下
ahri-client.exe -h
ahri-server.exe -h
方法一
直接运行 ahri-client / ahri-server, 参数如下.
Usage: ./ahri-client <server-info> <client-info> [socks5-cfg] [global-cfg]
server-info: -sip serverIp, -sp serverPort, -k serverPassword
client-info: -n clientName, -m clientMode
socks5-cfg: -s5ip socks5Ip, -s5p socks5Port, -f ahriHostsFile
global-cfg: -L logLevel, -T timeoutUnitSec
Parameters:
-L int
the log level, 0 ~ 3 ==> debug, info, warn, error (default 3)
-T int
the timeout of one-way communication time interval between an AhriClient and an AhriServer;
Special: AhriClient Dial timeout = 3T, heartbeat timeout = 2T (default 5)
-f string
the ahri hosts file of this ahri client (default "ahri.hosts")
-k string
the password of an ahri server
-m int
the work mode of this ahri client, 0: Take, 1: Give, 2: Trade
-n string
the name of this ahri client
-s5ip string
the socks5 IP of this ahri client (default "127.0.0.1")
-s5p string
the socks5 port of this ahri client (default "23456")
-sip string
the IP of an ahri server
-sp string
the port of an ahri server
Usage: ./ahri-server <server-info> [global-cfg]
server-info: -ip serverIp, -p serverPort, -k serverPassword, -a rsaPrivateKey, -b rsaPublicKey
global-cfg: -L logLevel, -T timeoutUnitSec
Parameters:
-L int
the log level, 0 ~ 3 ==> debug, info, warn, error (default 3)
-T int
the timeout of one-way communication time interval between an AhriClient and an AhriServer;
Special: AhriClient Dial timeout = 3T, heartbeat timeout = 2T (default 5)
-a string
the private rsa key file of this ahri server (default "rsa_private_key.pem")
-b string
the public rsa key file of this ahri server (default "rsa_public_key.pem")
-ip string
the IP of an ahri server
-k string
the password of an ahri server
-p string
the port of an ahri server
使用 ahri-client, 你几乎仅配置 ahri.hosts 即可使用.
使用 ahri-server, 你需要 openssl 帮你生成 RSA 公私钥对, 如果你是 *nix 环境, 可以直接调用 bash gen_rsa_keys.sh
.
方法二
为了降低配置难度,压缩包中已包含 start.sh,stop.sh 。 仅需要修改必要的参数即可在 *nix 环境中使用。 至于 Windows,参考 sh 脚本即可编写 bat 即可。
提示:如果 ahri-client 与 ahri-server 之间的网络环境是 低带宽 或 高延迟,请适当增大 -T
参数的值。
Ahri http(s)代理实践
Ahri 其实很简单,但对于不熟悉终端的童鞋还是太难了,那么跟着这个【实践教程】,你可以学习到基础用法。
Q & A
我已测试并正常使用该工具多时,有任何意见与建议或者问题,请 open 一个 issues。
ahri-client 的命名
ahri-client 可以使用最长 2 个 ASCII 字符(你就当做两个英文字母好了)来命名自身。同时 'S', 'L', '-', '|' 均是系统保留名,禁止使用。 为什么最长 2 个字符?由 ARP 决定的。如此,一台服务器已经可以选 (256^2 - 4) 个客户端名,足够使用。
windows 的命令行中无法运行 Ahri 的二进制文件
因为 Ahri 的交叉编译脚本中,默认使用 ahri-client 与 ahri-server 这两个名字命名二进制文件,而 windows 要求可执行文件以 exe
作为后缀名。
所以,将 ta 们重命名加上 .exe
即可。
关于其他常用工具的对接 Ahri
一般来说,至少在 *nix 环境下绝大多数工具的对接都可以使用 nc 来解决。
下面是 ssh 使用 nc 来对接 Ahri 的例子。
现在本地 socks5 监听代理是 socks5://127.0.0.1:23456
自己服务器是server.test.com
修改~/.ssh/config 文件
添加下面配置
host server.test.com
HostName server.test.com
ProxyCommand nc -X 5 -x 127.0.0.1:23456 %h %p
ServerAliveInterval 30
然后通过
ssh [email protected] 就可以愉快的使用 Ahri 代理登录了。