码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 36. 干货系列从零用Rust编写负载均衡及代理,内网穿透中内网代理的实现


    合集 - wmproxy(44)
    1.用Rust手把手编写一个Proxy(代理), 动工2023-09-192.用Rust手把手编写一个Proxy(代理), UDP绑定篇2023-09-213.5. 用Rust手把手编写一个Proxy(代理), 通讯协议建立, 为内网穿透做准备2023-09-284.6. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 通讯协议源码解读篇2023-09-305.7. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP及TCP内网穿透原理及运行篇2023-10-046.8. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP改造篇之HPACK原理2023-10-087. 用Rust手把手编写一个Proxy(代理), 准备篇, 动手造轮子2023-09-168.9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理2023-10-099.10. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP内网穿透支持修改头信息2023-10-1010.11. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 实现健康检查2023-10-1211.12. 用Rust手把手编写一个wmproxy(代理,内网穿透等), TLS的双向认证信息及token验证2023-10-1412.13. 从零开始编写一个类nginx工具, HTTP中的压缩gzip,deflate,brotli算法2023-10-1713.14. 从零开始编写一个类nginx工具, HTTP文件服务器的实现过程及参数2023-10-1914.15. 从零开始编写一个类nginx工具, 如果将nginx.conf转成yaml,toml,json会怎么样2023-10-2015.16. 从零开始编写一个类nginx工具, 反向代理upstream源码实现2023-10-2316.17. 从零开始编写一个类nginx工具, Rust中一些功能的实现2023-10-2417.18. 从零开始编写一个类nginx工具, 主动式健康检查源码实现2023-10-2618.19. 从零开始编写一个类nginx工具, 配置数据的热更新原理及实现2023-10-2719.25. 干货系列从零用Rust编写正反向代理,序列化之serde是如何工作的2023-11-1020.27. 干货系列从零用Rust编写正反向代理,Rust中日志库的应用基础准备2023-11-1521.28. 干货系列从零用Rust编写正反向代理,项目日志的源码实现2023-11-1722.30. 干货系列从零用Rust编写正反向代理,HTTP的组装之旅(中间件)2023-11-2423.29. 干货系列从零用Rust编写正反向代理,异步回调(async trait)的使用2023-11-2124.26. 干货系列从零用Rust编写正反向代理,如何发布Rust项目到Docker2023-11-1325.32. 干货系列从零用Rust编写正反向代理,关于堆和栈以及如何解决stack overflow2023-12-0426.33. 干货系列从零用Rust编写正反向代理,关于HTTP客户端代理的源码实现2023-12-1227.34. 干货系列从零用Rust编写负载均衡及代理,异步测试在Rust中的实现2023-12-1628.35. 干货系列从零用Rust编写负载均衡及代理,代理服务器的源码升级改造2023-12-19
    29.36. 干货系列从零用Rust编写负载均衡及代理,内网穿透中内网代理的实现2023-12-22
    30.37. 干货系列从零用Rust编写负载均衡及代理,负载均衡中try_files实现2023-12-2731.38. 干货系列从零用Rust编写负载均衡及代理,负载均衡中ip通行与禁止01-0332.39. 干货系列从零用Rust编写负载均衡及代理,正则及格式替换01-0933.40. 干货系列从零用Rust编写负载均衡及代理,websocket的实现01-1134.41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转01-1635.42 干货系列从零用Rust编写负载均衡及代理,wmproxy中配置tcp转websocket01-1836.43 干货系列从零用Rust编写负载均衡及代理,内网穿透方案完整部署01-2237.44从零开始用Rust编写nginx,命令行参数的设计与解析及说明01-2438.45从零开始用Rust编写nginx,静态文件服务器竟然还有这些细节01-2639.46从零开始用Rust编写nginx,数据还能这么传,多层代理(IP多级代理)搭建01-3040.47从零开始用Rust编写nginx,配对还有这么多要求!负载均衡中的路径匹配02-0141.48从零开始用Rust编写nginx,搭建一个简单又好看官方网站02-0642.49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP02-1943.50从零开始用Rust编写nginx,原来TLS证书还可以这么申请02-2644.51从零开始用Rust编写nginx,江湖救急,TLS证书快过期了02-29
    收起

    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,
    pub(crate) map_https_bind: Option,
    pub(crate) map_tcp_bind: Option,
    // 新加代理接口监听字段
    pub(crate) map_proxy_bind: Option,
    -
    }

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

    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::(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,
    // 收到中心端的写入请求,转成write
    receiver: Receiver,
    // 读取的数据缓存,将转发成ProtFrame
    read: BinaryMut,
    // 写的数据缓存,直接写入到stream下,从ProtFrame转化而来
    write: BinaryMut,
    }
    1. 设计ProxyServer类

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

    /// 代理服务器类, 提供代理服务
    pub struct ProxyServer {
    flag: Flag,
    username: Option<String>,
    password: Option<String>,
    udp_bind: Option,
    headers: Vec,
    }
    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(())
    }
    }

    内网代理流程图:

    以代理方式访问
    数据转发
    协议传输
    虚拟数据流
    处理数据
    外部客户端
    服务端监听Proxy
    中心服务端CenterServer
    中心客户端CenterClient
    虚拟客户端
    内网代理服务,可完全访问内网

    这样子我们就以代理的方式拥有了所有的内网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 {
    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通道。可以在有这方面需求的人优化网络结构。

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

  • 相关阅读:
    c# 实验七 图像列表框及树形视图控件的使用
    合作式智能运输系统-车用通信系统应用层及应用数据交互标准 第一阶段2020
    上传文件报错:The temporary upload location [/tmp/tomcat/xxx] is not valid
    【前端实例代码】使用 HTML CSS 和 JavaScript 制作一个响应式搜索栏
    bug是什么意思详细介绍
    多线程详解(三)
    Linux 进程控制
    【Python&GIS】基于Python批量合并矢量数据
    临沂大学张继群-智慧农业项目招募
    LeetCode-219. 存在重复元素 II.(java)
  • 原文地址:https://www.cnblogs.com/wmproxy/p/wmproxy36.html
  • 最新文章
  • 【FHE】我们如何实现同态加密推理(八):SiLU 的密文化——两条路径,和一个 8 字节的开关
    SQLcl 中 c/old/new 替换变量为何失效?先定位再替换
    不让模型写作文,直接从它脑子里读答案:Jev 决策在 .NET 的两条路线
    Halo插件:支持GitHub的图床附件策略
    电视投屏界的“一股清流”!SimpleTVCast 6.0.0-beta 抢先体验:无需TV端App,界面大换血!✨
    华三VXLAN集中式网关实战指南:Spine多活网关 + 双活接入(M-LAG)
    软件更好写之后口碑会更重要
    大 O、大 Omega、大 Theta 的区别,O(n) 复杂度是什么意思
    Java标签跳转:一个鲜为人知但却很实用的小技巧
    多智能体系统论文速读
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号