1个不稳定版本

0.1.0 2021年4月11日

#2144 in 网络编程

MIT许可证

30KB
491

tokio-sando

这是一个基于tokio的简单代理服务器实现。

代理服务器位于客户端和客户端目标之间(日语中的"sando"意为三明治)。它将代表其客户端从目标请求数据。换句话说,代理本质上充当VPN。请参阅工作原理以了解更多信息。

如何运行

打开两个终端。一个用于服务器,一个用于客户端。

  1. 127.0.0.1 proxy.tokio.sando 添加到您的 /etc/hosts
    • proxy.tokio.sando 是在 domain.crt 中注册的主机名。
  2. 在服务器端,运行 ./server
  3. 在客户端,运行 ./client
    • ./client 基于 curl。请确保其版本高于 7.68.0。否则,我们无法并行发送HTTP请求。

高级设置

您可以使用正则表达式设置目标URL的模式。例如,您可以运行以下命令

服务器端

cargo run -- 127.0.0.1:7878 --pkcs12 domain.p12 --password "^G#=QVbVhh7Bt8t9L" --destination-pattern "([a-z]).(mozilla|rust-lang).org"

客户端

# Work
curl -vp --proxy "https://proxy.tokio.sando:7878" --proxy-cacert domain.crt "https://rust-lang.net.cn/"
# Work
curl -vp --proxy "https://proxy.tokio.sando:7878" --proxy-cacert domain.crt "https://www.mozilla.org/en-US/"
# Won't work. Doesn't match "([a-z]).(mozilla|rust-lang).org" pattern
curl -vp --proxy "https://proxy.tokio.sando:7878" --proxy-cacert domain.crt "https://en.wikipedia.org/"

生成证书

此仓库包含一个内置证书。如果证书已过期或您希望生成自己的证书,请运行以下命令

  1. 生成自签名证书

    openssl req \
        -newkey rsa:2048 -nodes -keyout domain.key \
        -x509 -days 365 -out domain.crt
    
    • proxy.tokio.sando 替换为 Common Name,在 client.sh 中已注册的 domain.crt
    • 如果您更改了 domain.crt 的文件名,请也在 client.sh 中进行更新
  2. 将证书和私钥打包成一个 PKCS12 文件

    openssl pkcs12 \
        -inkey domain.key \
        -in domain.crt \
        -export -out domain.p12
    
    • server.sh 中的密码 ^G#=QVbVhh7Bt8t9L 替换为您的新密码
    • 如果您更改了 domain.p12 的文件名,请也在 server.sh 中进行更新
  3. 现在 server.shclient.sh 应该可以使用您自己的证书工作了

以下是一个 服务器-客户端示例。

工作原理

  client        proxy       destination
    |             |             |
    * <--- 1 ---> *             |
    |             |             |
    * ---- 2 ---> * ---- 3 ---> *
    |             |             |
    * <--- 5 ---- * <--- 4 ---- *
    |             |             |
    * ----------> * ----------> * relay data: client -> destination
    |             |             |
    * <---------- * <---------- * relay data: destination -> client
    |             |             |
  1. 客户端和代理之间的 TCP 和 TLS 握手(现在为 HTTPS)
  2. 客户端向代理发送 HTTP CONNECT 请求
  3. 代理建立到客户端 目的 的 TCP 通道
  4. 一旦建立上述 TCP 通道
  5. 通知客户端 HTTP 隧道 已建立
  6. 现在代理可以通过隧道在客户端和 目的 之间中继数据

用例

代理的一个用例是保护隐私的服务。一旦代理建立了隧道,如果通过隧道中继的数据是 HTTPS,客户端可以匿名地与 目的 通信。目的知道客户端的请求,但不知道客户端是谁。代理知道客户端和目的都是谁,但不知道他们在谈论什么。

这里有一个演示是 giphy 搜索。打开两个终端。一个是服务器,一个是客户端。

  1. 如果 127.0.0.1 proxy.tokio.sando 还不在,请将其添加到您的 /etc/hosts

  2. 在服务器端,运行 ./server,如果服务器仅用于 giphy 服务,请运行以下命令

    cargo run -- 127.0.0.1:7878 \
      --pkcs12 domain.p12 \                 # or your own PKCS12 file
      --password "^G#=QVbVhh7Bt8t9L" \      # or your new password if using your own PKCS12 file
      --destination-pattern "api.giphy.com" # Server only accept HTTP CONNECT to "api.giphy.com"
    
  3. 在客户端,运行 ./giphy_search.sh 以匿名搜索 gif。

请确保 HTTP CONNECT 请求(要求在客户端和目的之间中继数据)使用 HTTPS 协议。否则,如果隧道通信在 HTTP 中,代理将知道客户端和目的之间传输的内容,因为 HTTP 以纯文本形式传输数据。

待办事项

  • 隐私感知
    • 找到一种方法强制客户端使用 HTTPS 在隧道中传输数据
      • 至少可以通过嗅探中继的数据来说明这一点
  • 更灵活的设置
    • 添加超时设置
      • 如果连接挂起一段时间,则关闭连接。这应该有助于结束具有未知错误的连接
    • HTTP 版本检查
      • 代理应该能够要求最低的 HTTP 版本
    • 最大并行连接数
      • 代理应该能够限制并行连接的数量,以控制资源
  • 更好的错误/响应处理
    • 原因:目前,如果抛出 std::io::Error,则连接会被终止。代理应该向客户端发送响应,指示遇到的问题,而不是静默地断开连接。一旦客户端和代理之间的TLS建立,代理应该能够向客户端发送消息。
    • 如果客户端请求不完整或无效,则向客户端发送 400 Bad Request
      • 使用特定的错误枚举进行请求解析应该是有帮助的。
    • 如果代理无法连接到客户端的目的地,则向客户端返回错误。
    • 如果转发数据有问题,则向客户端返回错误。
      • 在隧道中转发数据时应该有一个超时机制。
      • 对于隧道模块使用特定的错误枚举应该是有帮助的。
    • 在代理服务器响应中添加更多详细的错误消息。现在响应只包含状态码。
  • 测试
    • 添加性能基准。需要与并行请求一起工作。
    • 为请求解析测试添加更多边缘情况。
    • 对不同类型的错误添加测试。
  • 流量控制/统计
    • 实时流量统计监控和控制
    • 即使出错,也要收集传输的字节数,只要代理已经转发了数据。
      • 现在,代理仅在连接成功完成后才显示统计信息。即使隧道转发或其他失败有错误,也应该知道已传输了多少数据。
  • 内存
    • 更好的缓冲区大小控制
      • 现在解析HTTP请求和转发数据的缓冲区大小是固定的。在服务器上有大量并行HTTP请求且内存有限时,需要更复杂的控制。
  • 日志系统
    • 将日志消息写入文件,而不是仅打印到屏幕上。
    • 为日志设置不同的级别。一些日志仅用于调试。
  • 构建时间/代码大小
    • 评估是否需要从tokio中获取 full 功能。

依赖关系

~5–17MB
~218K SLoC