该存储库包含客户端和服务器,允许您通过其他网络协议传输 TCP 和 UDP 流量。
目前,支持的隧道有:
主要工具是用 Rust 编写的,端到端测试是用 Python 编写的。
docker pull ghcr.io/dlemel8/tunneler-server:latest
docker pull ghcr.io/dlemel8/tunneler-client:latest
cargo --version || curl --proto ' =https ' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo build --release
如果您想构建本地 docker 镜像,还有一个 docker 文件
docker run -e LOCAL_PORT=45301
-e REMOTE_PORT=5201
-e REMOTE_ADDRESS=localhost
-e TUNNELED_TYPE=udp
--rm -p 45301:45301 ghcr.io/dlemel8/tunneler-server:latest tcp
./target/release/client
--tunneled-type tcp
--remote-address 1.1.1.1
--remote-port 53
--log-level debug
dns
--read-timeout-in-milliseconds 100
--idle-client-timeout-in-milliseconds 30000
使用--help
运行 docker 映像或编译的二进制文件以获取更多信息
cargo test --all-targets
python3 -m pip install -r e2e_tests/requirements.txt
PYTHONPATH=. python3 -m pytest -v
此存储库包含一些使用 Docker Compose 的服务器部署示例:
您可以在本地运行每个示例,也可以使用 Terraform 和 Ansible 进行部署。请在此处查看更多信息。
每个可执行文件包含 2 个通过客户端流通道进行通信的组件(字节读取器和写入器的元组):
基于 TCP 的流量可以简单地转换为流。基于 UDP 的流量转换取决于隧道协议。
基于 UDP 的流量还需要一种方法来识别现有客户端以继续其会话。该解决方案是内存中的客户端缓存,它将客户端的标识符映射到其流。
为了将 UDP 流量转换为流,每个数据包有效负载之前有一个 2 字节大小的标头(采用大端字节序)。
UDP 侦听器使用传入数据包对等地址作为其客户端缓存的密钥。
我们在这里面临一些挑战:
为了解决这些挑战,每个客户端会话首先生成一个随机客户端 ID(4 个字母数字字符)。客户端读取数据到隧道并通过编码器管道运行它:
然后,编码数据将用作 TXT DNS 查询的名称。
如果您拥有权威 DNS 服务器,客户端可以将请求发送到递归 DNS 解析器。解析器将从您的域名注册商处获取您的 IP,并将请求转发到您的 IP。另一种选择(更快但对任何流量分析器来说更明显)是配置客户端将请求直接发送到您的 IP(在端口 UDP/53 或服务器正在侦听的任何其他端口上)。
服务器解码数据(忽略任何非客户端流量)并使用客户端 ID 作为其客户端缓存的密钥。
为了处理大型服务器响应和空 TCP ACK,客户端和服务器都使用读取超时。如果读取超时已过,将发送空消息。客户端和服务器端都使用空闲超时来停止转发并清理本地资源。
为了实现相互身份验证,我们使用私有证书颁发机构:
客户端和服务器都配置为在 TLS 握手中使用其密钥和证书。 CA 证书用作根证书。
由于使用了服务器名称指示扩展,因此客户端请求特定的服务器名称,并且服务器仅在请求该名称时才提供其证书。服务器名称也必须是证书的一部分,例如作为主题备用名称。