wmproxy

wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子

项目地址

国内: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

项目设计目标

  • HTTP转发
  • HTTPS转发(证书在服务器,内网为HTTP)
  • TCP转发(纯粹的TCP转发,保持原样的协议)
  • PROXY转发(服务端接收数据,内网的客户端当成PROXY客户端,相当于逆向访问内网服务器,[新增])

实现方案

服务端提供客户端的连接端口,可加密Tls,可双向加密mTls,可账号密码认证,客户端连接服务端的端口等待数据的处理。主要有两个类服务端CenterServer客户端CenterClient

一些细节可以参考第5篇,第6篇,第10篇,第12篇,有相关的内网穿透的细节。

内网代理的实现

  1. 首先添加一种模式
#[serde_as]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MappingConfig {
/// 其它字段....
// 添加模块proxy
pub mode: String,
}
  1. 添加内网代理监听端口
#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProxyConfig {
/// 其它字段....
pub(crate) map_http_bind: Option<SocketAddr>,
pub(crate) map_https_bind: Option<SocketAddr>,
pub(crate) map_tcp_bind: Option<SocketAddr>,
// 新加代理接口监听字段
pub(crate) map_proxy_bind: Option<SocketAddr>,
-
}

目前端口做唯一绑定,后续可根据配置动态响应相应的数据。

  1. 做映射

由于代理和tcp类似,服务端均不做任务处理,只需将数据完全转发给客户端处理即可

pub async fn server_new_prxoy(&mut self, stream: TcpStream) -> ProxyResult<()> {
let trans = TransTcp::new(
self.sender(),
self.sender_work(),
self.calc_next_id(),
self.mappings.clone(),
);
tokio::spawn(async move {
if let Err(e) = trans.process(stream, "proxy").await {
log::warn!("内网穿透:转发Proxy转发时发生错误:{:?}", e);
}
});
return Ok(());
}
  1. 客户端处理

    客户端将映射流转化成VirtualStream,把它当成一个虚拟流,然后逻辑均用代理的来处理
let (virtual_sender, virtual_receiver) = channel::<ProtFrame>(10);
map.insert(p.sock_map(), virtual_sender); if mapping.as_ref().unwrap().is_proxy() {
let stream = VirtualStream::new(
p.sock_map(),
sender.clone(),
virtual_receiver,
); let (flag, username, password, udp_bind) = (
option.flag,
option.username.clone(),
option.password.clone(),
option.udp_bind.clone(),
);
tokio::spawn(async move {
// 处理代理的能力
let _ = WMCore::deal_proxy(
stream, flag, username, password, udp_bind,
)
.await;
});
}

VirtualStream是一个虚拟出一个流连接,并实现AsyncRead及AsyncRead,可以和流一样正常操作,这也是Trait而不是继承的好处之一,定义就可以比较简单:

pub struct VirtualStream
{
// sock绑定的句柄
id: u32,
// 收到数据通过sender发送给中心端
sender: PollSender<ProtFrame>,
// 收到中心端的写入请求,转成write
receiver: Receiver<ProtFrame>,
// 读取的数据缓存,将转发成ProtFrame
read: BinaryMut,
// 写的数据缓存,直接写入到stream下,从ProtFrame转化而来
write: BinaryMut,
}
  1. 设计ProxyServer

统一的代理服务类,剥离相关代码,使代码更清晰

/// 代理服务器类, 提供代理服务
pub struct ProxyServer {
flag: Flag,
username: Option<String>,
password: Option<String>,
udp_bind: Option<IpAddr>,
headers: Vec<ConfigHeader>,
}
  1. 代理HTTP头信息的重写

    HTTP中添加相关代码以支持头信息重写
impl Operate {
fn deal_request(&self, req: &mut RecvRequest) -> ProtResult<()> {
if let Some(headers) = &self.headers {
// 复写Request的头文件信息
Helper::rewrite_request(req, headers);
}
Ok(())
} fn deal_response(&self, res: &mut RecvResponse) -> ProtResult<()> {
if let Some(headers) = &self.headers {
// 复写Request的头文件信息
Helper::rewrite_response(res, headers);
}
Ok(())
}
}

内网代理流程图:

flowchart TD
A[外部客户端] -->|以代理方式访问|B
B[服务端监听Proxy] <-->|数据转发| C[中心服务端CenterServer]
C <-->|协议传输|D[中心客户端CenterClient]
D <-->|虚拟数据流|E[虚拟客户端]
E <-->|处理数据|F[内网代理服务,可完全访问内网]

这样子我们就以代理的方式拥有了所有的内网HTTP相关服务的访问权限。可以简化我们网络的结构。

自动化测试

内网穿透的自动化测试在 tests/mapping

将自动构建内网客户端服务,外网服务端服务做测试,以下部分代码节选:

#[tokio::test]
async fn run_test() {
let local_server_addr = run_server().await.unwrap();
let addr = "127.0.0.1:0".parse().unwrap();
let proxy = ProxyConfig::builder()
.bind_addr(addr)
.map_http_bind(Some(addr))
.map_https_bind(Some(addr))
.map_tcp_bind(Some(addr))
.map_proxy_bind(Some(addr))
.center(true)
.mode("server".to_string())
.into_value()
.unwrap(); let (server_addr, http_addr, https_addr, tcp_addr, proxy_addr, _sender) =
run_mapping_server(proxy).await.unwrap();
let mut mapping = MappingConfig::new(
"test".to_string(),
"http".to_string(),
"soft.wm-proxy.com".to_string(),
vec![],
);
mapping.local_addr = Some(local_server_addr); let mut mapping_tcp = MappingConfig::new(
"tcp".to_string(),
"tcp".to_string(),
"soft.wm-proxy.com".to_string(),
vec![],
);
mapping_tcp.local_addr = Some(local_server_addr); let mut mapping_proxy = MappingConfig::new(
"proxy".to_string(),
"proxy".to_string(),
"soft.wm-proxy.com1".to_string(),
vec![
ConfigHeader::new(wmproxy::HeaderOper::Add, false, "from_proxy".to_string(), "mapping".to_string())
],
);
mapping_proxy.local_addr = Some(local_server_addr); let proxy = ProxyConfig::builder()
.bind_addr(addr)
.server(Some(server_addr))
.center(true)
.mode("client".to_string())
.mapping(mapping)
.mapping(mapping_tcp)
.mapping(mapping_proxy)
.into_value()
.unwrap();
let _client_sender = run_mapping_client(proxy).await.unwrap(); fn do_build_req(url: &str, method: &str, body: &Vec<u8>) -> Request<Body> {
let body = BinaryMut::from(body.clone());
Request::builder()
.method(method)
.url(&*url)
.body(Body::new_binary(body))
.unwrap()
} {
let url = &*format!("http://{}/", local_server_addr);
let client = Client::builder()
// .http2(false)
.http2_only(true)
.add_proxy(&*format!("http://{}", proxy_addr.unwrap())).unwrap()
.connect(&*url)
.await
.unwrap(); let mut res = client
.send_now(do_build_req(url, "GET", &vec![]))
.await
.unwrap();
let mut result = BinaryMut::new();
res.body_mut().read_all(&mut result).await; // 测试头信息来确认是否来源于代理
assert_eq!(res.headers().get_value(&"from_proxy"), &"mapping");
assert_eq!(result.remaining(), HELLO_WORLD.as_bytes().len());
assert_eq!(result.as_slice(), HELLO_WORLD.as_bytes());
assert_eq!(res.version(), Version::Http2);
}
}

小结

内网代理可以实现不想暴露太多信息给外部,但是又能提供内部的完整信息支持,相当于建立了一条可用的HTTP通道。可以在有这方面需求的人优化网络结构。

点击 [关注][在看][点赞] 是对作者最大的支持

36. 干货系列从零用Rust编写负载均衡及代理,内网穿透中内网代理的实现的更多相关文章

  1. Docker系列-(3) Docker-compose使用与负载均衡

    上一篇文章介绍了docker镜像的制作与发布,本文主要介绍实际docker工程部署中经常用到的docker-compose工具,以及docker的网络配置和负载均衡. Docker-compose介绍 ...

  2. 死磕nginx系列--使用upsync模块实现负载均衡

    问题描述 nginx reload是有一定损耗的,如果你使用的是长连接的话,那么当reload nginx时长连接所有的worker进程会进行优雅退出,并当该worker进程上的所有连接都释放时,进程 ...

  3. hbase源码系列(一)Balancer 负载均衡

    看源码很久了,终于开始动手写博客了,为什么是先写负载均衡呢,因为一个室友入职新公司了,然后他们遇到这方面的问题,某些机器的硬盘使用明显比别的机器要多,每次用hadoop做完负载均衡,很快又变回来了. ...

  4. office web apps安装部署,配置https,负载均衡(七)配置过程中遇到的问题详细解答

    该篇文章,是这个系列文章的最后一篇文章,该篇文章将详细解答owa在安装过程中常见的问题. 如果您没有搭建好office web apps,您可以查看前面的一系列文章,查看具体步骤: office we ...

  5. 工具系列 | 如何在阿里云负载均衡上启用WS/WSS支持

    官方文档:https://help.aliyun.com/document_detail/63421.html?spm=5176.10695662.1996646101.searchclickresu ...

  6. 最简单的 nginx 负载均衡,只能演示,企业中最好不用

    修改nginx.conf 配置,重启nginx即可 upstream 包名{ ip_hash; #使用此功能,权重和备份都不能使用!一台机器永远只连同一台机子 server IP:端口 weight= ...

  7. 干货 | 亿级Web系统负载均衡几种实现方式

    一个执着于技术的公众号 负载均衡(Load Balance)是集群技术(Cluster)的一种应用技术.负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力.目前最常见的负载均衡应用是Web ...

  8. 干货 | Nginx负载均衡原理及配置实例

    一个执着于技术的公众号 Nginx系列导读 给小白的 Nginx 10分钟入门指南 Nginx编译安装及常用命令 完全卸载nginx的详细步骤 Nginx 配置文件详解 理解正向代理与反向代理的区别 ...

  9. SpringCloud系列五:Ribbon 负载均衡(Ribbon 基本使用、Ribbon 负载均衡、自定义 Ribbon 配置、禁用 Eureka 实现 Ribbon 调用)

    1.概念:Ribbon 负载均衡 2.具体内容 现在所有的服务已经通过了 Eureka 进行了注册,那么使用 Eureka 注册的目的是希望所有的服务都统一归属到 Eureka 之中进 行处理,但是现 ...

  10. 【SpringCloud微服务实战学习系列】客户端负载均衡Spring Cloud Ribbon

    Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.通过Spring Cloud的封装,可以让我们轻松地将面向服务的RES模板 ...

随机推荐

  1. CodeForces 1388D Captain Flint and Treasure

    题意 给长度为\(n\)的序列\(a[n]\)和\(b[n]\),初始时\(ans=0\),有以下操作: \(ans=ans+a[i]\) 如果\(b[i]\neq-1\),则\(a[b[i]]=a[ ...

  2. C#结合OpenCVSharp4使用直方图算法比较图片相似度

    C#结合OpenCVSharp4使用直方图算法比较图片相似度 直方图有灰度直方图.颜色直方图,如果是灰度图像,那么就用灰度直方图,这里使用颜色直方图来计算两个图片的相似度. 这里只记录如何使用,至于算 ...

  3. Jenkins 忘记密码|密码重置

    I. 当前环境 OS Version : AlmaLinux release 8.8 Jenkins Version : 2.414.1 II. 操作步骤 2.1 修改配置文件 1. SSH 登录服务 ...

  4. MySQL实战实战系列 03 事务隔离:为什么你改了我还看不见?

    提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务.最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱. 转账过程具体到程序里会有一系列的操作,比如 ...

  5. 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表TreeView的使用

    在我们展示一些参考信息的时候,有所会用树形列表来展示结构信息,如对于有父子关系的多层级部门机构,以及一些常用如字典大类节点,也都可以利用树形列表的方式进行展示,本篇随笔介绍基于WPF的方式,使用Tre ...

  6. Eclipse OSGI配置文件说明

  7. 探秘公有IP地址与私有IP地址的区别及其在路由控制中的作用

    引言 IP地址是互联网通信中至关重要的组成部分.虽然在前一章节我们讲解了IP一些基础知识,但在我们日常生活中,我们经常听到公有IP地址和私有IP地址这两个术语.那么,公有IP地址和私有IP地址有何区别 ...

  8. 基于react18+vite4+arco.design搭建极简版后台管理模板

    趁着国庆前夕整了一个vite4结合react18搭建后台管理模板,搭配上字节团队react组件库ArcoDesign,整体操作功能非常丝滑.目前功能支持多种模板布局.暗黑/亮色模式.国际化.权限验证. ...

  9. 分布式事务:XA和Seata的XA模式

    上一篇内容<从2PC和容错共识算法讨论zookeeper中的Create请求>介绍了保证分布式事务提交的两阶段提交协议,而XA是针对两阶段提交提出的接口实现标准,本文则对XA进行介绍. 1 ...

  10. python-微信

    wxpy/itchat已禁用 自从微信禁止网页版登陆之后,itchat 库实现的功能也就都不能用了: itchat现在叫wxpy 1.安装库wxpy: PS D:\01VSCodeScript\Pyt ...