wmproxy

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

项目地址

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

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

设计目标

需要能对针对性的IP地址进行放行或者禁止,从而达到网络限制或者安全等目的,保护系统的整体稳定性。

IP的作用

IP地址的作用是标示一台在互联网上的主机,就像每个人的住宅地址一样,邮寄东西需要住宅地址,而互联网上一台电脑对另一台电脑发送数据也需要一个可以识别的地址。

早期的IP地址由32位(即IPv4)的数据表示,也就是大概有42亿的地址,以现如今的网络拥有量已经将公有IP即将耗尽的情形。即使有大量的地址并不会占用公网地址,如公司内部的电脑,家庭内部的电脑,手机等并未占用公网地址,可能很多的设备占用了同一个公网出口。所以我们做了IPv4的限定的时候,也有可能将正常的用户做了相关限定。

现在正在推行的IP地址是128位(即IPv6)的数据将可以表示很大的IP数,将可以每个物联网的设备都拥有独立的IP,但是由于IP的升级涉及到大量的基础设备,大量的旧软件,所以当下基本上两种IP都必须支持的阶段。但是国内相对IPv4占了大部分。

IP中子网掩码

子网掩码就是为了划分同网段的主机数量。每类网段默认是254个。

我们在现实的生活中经常看到路由上255.255.255.0/24也经常在云的白名单上看到了0.0.0.0/0,那么他们表示的含义又是什么呢?

通常我们可以把IPv4看成一个无符号的32位整型,那么255.255.255.0/24后面的24就表示0xffffff00,那么我们将某个IP与该值进行按位与运算,得到相同的值将表示归属于同一个IP段。

举例:内网常用的ip如192.168.0.100按位与后将变成192.168.0.0,及192.168.0.1-254这254个ip按位与得到的地址均为

192.168.0.0,所以他们归属于同一个IP段,也就是表示在他们在同一个路由器下面。

0.0.0.0/0中最后的0就是表示0x00000000,那么我们将任意的IP与该值做按位与得到的值均为0.0.0.0,则表示所有的IP都归属于同一个类,也就是通常配置的白名单对所有的都生效。

IPv6与IPv4类似,只是IPv6的长度更长,子网的长度可以由0-127,而IPv6不叫子网掩码,通常称其为前缀,但其原理都是用位表示,前n位为网络位,则说明IP只要前n位一样,则子网一样,IP的机制通了,涉及IP的问题也就好解决了。

源码的实现

Rust中并不支持子网掩码等,那么我们将在其基础上增加一个8位的无符号型:

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IpGate {
pub ip: IpAddr,
pub gate: u8,
}

序列化我们通过serde_with中的DisplayForStr实现,如果存在/则将其切割,如果不存在那么子网掩码位数为0,兼容两种模式:


impl FromStr for IpGate {
type Err = io::Error; fn from_str(s: &str) -> Result<Self, Self::Err> {
let vals = s.split("/").collect::<Vec<&str>>();
let ip = vals[0].parse::<IpAddr>().map_err(|_| io::Error::new(io::ErrorKind::Other, "parse ip error"))?;
let mut gate = 0;
if vals.len() > 1 {
gate = vals[1].parse::<u8>().map_err(|_| io::Error::new(io::ErrorKind::Other, "parse ip error"))?;
if ip.is_ipv4() && gate > 32 {
return Err(io::Error::new(io::ErrorKind::Other, "too big gate"));
} else if ip.is_ipv6() && gate > 128 {
return Err(io::Error::new(io::ErrorKind::Other, "too big gate"));
}
}
Ok(IpGate {
ip, gate
})
}
} impl Display for IpGate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.gate > 0 {
f.write_fmt(format_args!("{}/{}", self.ip, self.gate))
} else {
f.write_fmt(format_args!("{}", self.ip))
}
}
}

查看是否包含:

pub fn contains(&self, ip: &IpAddr) -> bool {
if self.gate == 0 {
ip == &self.ip
} else {
match (&ip, &self.ip) {
(IpAddr::V4(other), IpAddr::V4(my)) => {
let other = u32::from_be_bytes(other.octets()) >> (32u8 - self.gate);
let my = u32::from_be_bytes(my.octets()) >> (32u8 - self.gate);
other == my
}
_ => {
ip == &self.ip
}
}
}
}

在负载均衡中的通行及禁止

我们将配置信息转化成可通行的IP段数组或者禁止的IP段数组

  • 如果存在可通行的配置那么必须在配置中才可通行
  • 如果存在禁止的IP,那么在配置中的将会被禁止

我们在配置的时候,就可以进行如下的配置:

[[http.server.location]]
rule = "/try"
# 只允许本地网络通行
allow_ip = "127.0.0.1 192.168.0.0/24" [[http.server.location]]
rule = "/"
reverse_proxy = "http://server"
# 全面禁止10开头的IP段
deny_ip = "10.0.0.0/8"

源码示例:


if l.comm.deny_ip.is_some() || l.comm.allow_ip.is_some() {
if let Some(ip) = req.headers().system_get("{client_ip}") {
let ip = ip
.parse::<IpAddr>()
.map_err(|_| ProtError::Extension("client ip error"))?;
if let Some(allow) = &l.comm.allow_ip {
if !allow.contains(&ip) {
return Ok(Response::status503()
.body("now allow ip")
.unwrap()
.into_type());
}
}
if let Some(deny) = &l.comm.deny_ip {
if deny.contains(&ip) {
return Ok(Response::status503().body("deny ip").unwrap().into_type());
}
}
}
}

小结

后续可能需要在接受连接的时候就直接禁止掉IP,那么我们可以防止客户端持续的发送流量,即可能造成流量被耗尽。

IP的通行及禁止帮我们更好的保护系统的健壮性及私域的隐私性做保证。自动禁止IP的话,将是WAF等进阶能力的,更好的保护源站。

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

38. 干货系列从零用Rust编写负载均衡及代理,负载均衡中ip通行与禁止的更多相关文章

  1. 【转】Spring Boot干货系列:(一)优雅的入门篇

    转自Spring Boot干货系列:(一)优雅的入门篇 前言 Spring一直是很火的一个开源框架,在过去的一段时间里,Spring Boot在社区中热度一直很高,所以决定花时间来了解和学习,为自己做 ...

  2. Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用

    Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用 原创 2017-04-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章介绍了一些基础,但都是静 ...

  3. Spring Boot干货系列:(七)默认日志框架配置

    Spring Boot干货系列:(七)默认日志框架配置 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候, ...

  4. Spring Boot干货系列:(五)开发Web应用JSP篇

    Spring Boot干货系列:(五)开发Web应用JSP篇 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 上一篇介绍了Spring Boot中使用Thymeleaf模板引擎,今天 ...

  5. Spring Boot干货系列:(四)Thymeleaf篇

    Spring Boot干货系列:(四)Thymeleaf篇 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 Web开发是我们平时开发中至关重要的,这里就来介绍一下Spring Boo ...

  6. Spring Boot干货系列:(一)优雅的入门篇

    Spring Boot干货系列:(一)优雅的入门篇 2017-02-26 嘟嘟MD 嘟爷java超神学堂   前言 Spring一直是很火的一个开源框架,在过去的一段时间里,Spring Boot在社 ...

  7. Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)

    前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...

  8. (转)Spring Boot干货系列:(七)默认日志logback配置解析

    转:http://tengj.top/2017/04/05/springboot7/ 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的, ...

  9. (转)Spring Boot干货系列:(四)开发Web应用之Thymeleaf篇

    转:http://tengj.top/2017/03/13/springboot4/ 前言 Web开发是我们平时开发中至关重要的,这里就来介绍一下Spring Boot对Web开发的支持. 正文 Sp ...

  10. 【WEB API项目实战干货系列】- 导航篇(十足干货分享)

    在今天移动互联网的时代,作为攻城师的我们,谁不想着只写一套API就可以让我们的Web, Android APP, IOS APP, iPad APP, Hybired APP, H5 Web共用共同的 ...

随机推荐

  1. java获取服务器ip地址的工具类

    参考: https://www.cnblogs.com/raphael5200/p/5996464.html 代码实现 import lombok.extern.slf4j.Slf4j; import ...

  2. 使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理

    目录 1.前言 2.分析 3. 实现 4.踩坑 4.1.拖拽辅助线的坑 4.2.数据的坑 4.3.限制拖拽 4.4.样式调整 1.前言 最近在做一个文件夹管理的功能,要实现一个树状的文件夹面板.里面包 ...

  3. 数据结构与算法 | 数组(Array)

    数组(Array) 数组(Array)应该是最基础的数据结构之一,它由相同类型的元素组成的集合,并按照一定的顺序存储在内存中.每个元素都有一个唯一的索引,可以用于访问该元素. // java 数组示例 ...

  4. chatgpt镜像站汇总 - 聚合GPT【即时更新】

    自荐下由我开发的聚合GPT网站,这边的GPT镜像站均为免费.无登录.无次数限制的!会及时剔除失效.添加可用地址[欢迎STAR.PR] 地址:https://ele-cat.gitee.io/comp- ...

  5. js下IE和FF的一些兼容写法总结

    一.脚本差异: 1.事件绑定:addEventListener 与 attachEvent  事件处理函数中this指向不同, IE中指向window 2.获取事件对象 :事件处理函数     win ...

  6. Redis 6 学习笔记 2 —— 简单了解订阅和发布(Pub/Sub),JDK17环境下用Jedis 4.3.1连接Redis并模拟验证码发送

    REDIS pubsub -- Redis中国用户组(CRUG) 什么是发布和订阅 Redis发布订阅是一种通信模式:发送者(Pub)发送消息,订阅者(Sub)接收消息.Redis客户端可以订阅任意数 ...

  7. Educational Codeforces Round 148 [Rated for Div. 2]A~C

    A #include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 60; char c ...

  8. Acwing周赛102

    倍增 这是一道简单数论题 using namespace std; typedef long long LL; const int N = 1e5 + 10; int a[N], n; int div ...

  9. 【matplotlib 实战】--箱型图

    箱型图(Box Plot),也称为盒须图或盒式图,1977年由美国著名统计学家约翰·图基(John Tukey)发明.是一种用作显示一组数据分布情况的统计图,因型状如箱子而得名. 它能显示出一组数据的 ...

  10. Java开发中的工作流程和步骤

    前言 随着环境的变迁,大家总会更换工作,有裁员的,有跳槽的,除了进进出出的老人,还有源源不断入坑的新人. 很多人入职之后还不知道怎么快速适应工作,对我而言,除去寥寥可数的同事感情,对我而言,更换工作更 ...