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共用共同的 ...
随机推荐
- WebStrom提交代码到GitLab报错Error: Cannot find any-observable implementation nor global.Observable.
项目场景: 前端代码完成后,提交代码 问题描述 提交代码到GitLab时,因自动检测机制导致项目提交失败 C:\D\insper\inspur_works\custom-manage-front\no ...
- 小札 Combinatorics 2
对于 Newton Expansion,式子本身的证明其实无甚可翻新的花样,但是题还是很有意思的.比如 codeforces - 1332E Height All the Same 这个. 首先给出几 ...
- Springboot项目使用Undertow替换内置Tomcat服务器,实现RESTFUL接口web应用
Maven实例:pom.xml文件中添加更换依赖 <dependency> <groupId>org.springframework.boot</groupId> ...
- element ui的多个表格复选框,展开列显示错误
今天在公司写页面的时候碰到一个bug,我们的那个页面上有多个表格. 用v-if来判断显示,然后再使用复选框和展开列的时候出了问题.先是复选框,第二个表格的复选框下一列不显示,我试了试,在下面的一列都会 ...
- Avalonia 实现视频聊天、远程桌面(源码,支持Windows、Linux、国产OS)
现在最火的.NET跨平台UI框架莫过于Avalonia了.Avalonia 基于.NET Core,因此它可以运行在任何支持.NET Core的平台上.之前基于CPF跨平台UI框架写过一个视频聊天的d ...
- NPOI在EXCEL中插入图片和超链接
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Kubernetes 迁移节点 Kubelet 数据存储目录
1.概述 默认Kubernetes节点Kubelet数据目录在/var/lib/kubelet,如果在部署前没有做好规划,其实默认就存储在系统盘/分区下了,这可能会引发一些问题: 磁盘空间限制: 系统 ...
- gcd|最大公约数|欧几里得算法|欧几里得算法证明 一文说明白
gcd 最大公因数,也称最大公约数.最大公因子,指两个或多个整数共有约数中最大的一个.a,b的最大公约数记为 $ gcd(a,b) $ ,同样的,a,b,c的最大公约数记为 $ gcd(a,b,c) ...
- docker 安装、升级、修改数据目录
1.查看系统要求 Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看CentOS的内核版本. uname -a 2.删除旧版本 yum remove docker docker-c ...
- DNS 服务 docker-bind 的部署使用
前言 前面使用 nginx 代理转发了几个域名到服务器,但是每次添加一个域名都需要在客户端添加一行 hosts 文件,无疑是繁琐的,其中也提到可以使用 DNS 来实现自动解析域名 到指定服务器的功能, ...