38. 干货系列从零用Rust编写负载均衡及代理,负载均衡中ip通行与禁止
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通行与禁止的更多相关文章
- 【转】Spring Boot干货系列:(一)优雅的入门篇
转自Spring Boot干货系列:(一)优雅的入门篇 前言 Spring一直是很火的一个开源框架,在过去的一段时间里,Spring Boot在社区中热度一直很高,所以决定花时间来了解和学习,为自己做 ...
- Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用
Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用 原创 2017-04-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章介绍了一些基础,但都是静 ...
- Spring Boot干货系列:(七)默认日志框架配置
Spring Boot干货系列:(七)默认日志框架配置 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候, ...
- Spring Boot干货系列:(五)开发Web应用JSP篇
Spring Boot干货系列:(五)开发Web应用JSP篇 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 上一篇介绍了Spring Boot中使用Thymeleaf模板引擎,今天 ...
- Spring Boot干货系列:(四)Thymeleaf篇
Spring Boot干货系列:(四)Thymeleaf篇 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 Web开发是我们平时开发中至关重要的,这里就来介绍一下Spring Boo ...
- Spring Boot干货系列:(一)优雅的入门篇
Spring Boot干货系列:(一)优雅的入门篇 2017-02-26 嘟嘟MD 嘟爷java超神学堂 前言 Spring一直是很火的一个开源框架,在过去的一段时间里,Spring Boot在社 ...
- Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)
前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...
- (转)Spring Boot干货系列:(七)默认日志logback配置解析
转:http://tengj.top/2017/04/05/springboot7/ 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的, ...
- (转)Spring Boot干货系列:(四)开发Web应用之Thymeleaf篇
转:http://tengj.top/2017/03/13/springboot4/ 前言 Web开发是我们平时开发中至关重要的,这里就来介绍一下Spring Boot对Web开发的支持. 正文 Sp ...
- 【WEB API项目实战干货系列】- 导航篇(十足干货分享)
在今天移动互联网的时代,作为攻城师的我们,谁不想着只写一套API就可以让我们的Web, Android APP, IOS APP, iPad APP, Hybired APP, H5 Web共用共同的 ...
随机推荐
- ionic4请求skynet服务器的资源跨域问题
最近在做一个后台接口, 顺便用ionic4写了个简单的管理后台, 本来skynet管理后台监听的端口是6666, 但是发现chrome默认对一些接口不友善, 虽然可以通过设置启动参数来解决, 但是还是 ...
- Mysql忘记密码后如何重置密码
长时间不使用本机的Mysql后把密码忘记了咋整?直接上干货: 第一步(Mysql部署的位置,若自己能找到就忽略这一步):任务管理器中也可以找到 第二步:修改配置文件 在my.ini末尾加上 skip- ...
- Jenkins 命令执行 -- jetty 敏感信息泄露 --(CVE-2021-2816)&&(CVE-2017-1000353)&&(CVE-2018-1000861)
Jenkins 命令执行 -- jetty 敏感信息泄露 --(CVE-2021-2816)&&(CVE-2017-1000353)&&(CVE-2018-100086 ...
- 使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理
目录 1.前言 2.分析 3. 实现 4.踩坑 4.1.拖拽辅助线的坑 4.2.数据的坑 4.3.限制拖拽 4.4.样式调整 1.前言 最近在做一个文件夹管理的功能,要实现一个树状的文件夹面板.里面包 ...
- [C++]vector的基本的用法
[vector/容器/向量/动态数组]的基本的用法 容器的定义 向量/容器(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container). 跟任意其它类型容器一样,它能够存 ...
- 阿里云上的rds 的隔离级别read committed 而不是repeatable-read设置原因
阿里云上的rds 的隔离级别 是read committed ,而不是原生mysql的"可重复读(repeatable-read)",他们是基于什么原因这样设置的? show va ...
- 给STM32装点中国风——华为LiteOS移植
我都二手程序员好几个礼拜了!想给我的STM32来点"中国风",装个华为LiteOS操作系统. 在此之前,我也试过STM32CubeMX自带的FreeRTOS操作系统,不知是何缘故, ...
- JS逆向实战26——某店ua模拟登陆
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标 目标网站 aHR0c ...
- L2-028 秀恩爱分得快
90行,调了俩小时,大约有以下坑点. 1.每个数字都可能正负出现,比如-0 0,-1 1,一开始以为一个数的正负只会出现一个. 2.当俩人都不出现在照片中,那么输出俩人就行 3.当其中一个人不在照片里 ...
- 生命游戏(4.2leetcode每日打卡)
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞.每个细胞都具有一个初始状 ...