当前位置:网站首页>#gStore-weekly | gStore源码解析(四):安全机制之黑白名单配置解析
#gStore-weekly | gStore源码解析(四):安全机制之黑白名单配置解析
2022-07-02 17:41:00 【PKUMOD】
上一章我们介绍了安全机制的用户权限管理,接下来将对安全机制中黑白名单配置的源码进行解析。
1.1 简介
在gstore安全机制中的黑白名单是指访问IP的黑名单库和白名单库;黑名单规则为:黑名单库中的IP将被禁止访问,白名单规则为:仅允许白名单库中的IP访问;原则上黑白名单的配置为二选一,若二者都开启则是白名单优先。
1.2 配置黑白名单
首先通过gstore根目录的init.conf文件进行配置:启动黑名单或者白名单模式;默认为黑名单模式,如下配置文件所示:
[ghttp]
......
# 白名单库文件路径,为空表示不启用
ip_allow_path=""
# 黑名单库文件路径,为空表示不启用
ip_deny_path="ipDeny.config"
......
然后根据配置的模式,可以复制对应的黑白名单库示例文件进行配置,配置支持单个IP和某一范围的IP组,如下所示:
#ipDeny.config.example
#two kinds of ip format
#1. a single ip address
1.2.2.1
#2. a closed intervel represent by "-", to allow for a segment of ips or deny a segment of ips
1.1.1.1-1.1.1.2
###########################################################
# Notice!!!! #
# 1. use "#" to comment out a Whole line #
# 2. ip address write in one line #
# 3. do not include extra space or other char #
# 4. keep it only numbers or with one '-' #
# 5. to enable black list, use "--ipDeny=<filename>" #
###########################################################
1.3 黑白名单管理
在完成黑白名单的配置后,我们还可以通过接口进行动态的配置,ghttp网络服务提供了黑白名单的查询和更新接口。
当ghttp网络服务启动时,将会对黑白名单进行初始化,读取配置文件init.conf配置的黑白名单库路径,并读取解析黑白名单的库数据,部分关键代码如下:
// 与黑白名单相关的全局参数
// 是否启用黑名单
int blackList = 0;
// 是否启用白名单
int whiteList = 0;
// 白名单库路径
string ipBlackFile = "ipDeny.config";
// 黑名单库路径
string ipWhiteFile = "ipAllow.config";
// 白名单库
IPWhiteList* ipWhiteList;
// 黑名单库
IPBlackList* ipBlackList;
ghttp服务器启动时将调用函数ghttp::initialize进行初始化:
int initialize(int argc, char *argv[])
{
cout << "ghttp begin initialize..." << endl;
cout << "init param..." << endl;
Util util;
// 读取init.conf文件中的配置参数
Util::configure_new();
......
else
{
......
ipWhiteFile = Util::getConfigureValue("ip_allow_path");
ipBlackFile = Util::getConfigureValue("ip_deny_path");
cout<<"ipWhiteFile:"<<ipWhiteFile<<endl;
cout<<"ipBlackFile:"<<ipBlackFile<<endl;
// 判断是否配置白名单库
if (ipWhiteFile.empty()) {
whiteList = 0;
} else {
whiteList = 1;
}
// 判断是否配置黑名单库
if (ipBlackFile.empty()) {
blackList = 0;
} else {
blackList = 1;
}
......
}
// 判断黑白名单的启用规则,在二者同时启用的情况下,白名单优先
if (whiteList) {
cout << "IP white List enabled." << endl;
ipWhiteList = new IPWhiteList();
ipWhiteList->Load(ipWhiteFile);
} else if (blackList) {
cout << "IP black list enabled." << endl;
ipBlackList = new IPBlackList();
ipBlackList->Load(ipBlackFile);
}
}
在初始化白名单的库时将调用IPWhiteList::Init和IPWhiteList::Load函数进行解析(黑名单库也类似):
// IPWhiteList中的参数ipList用于保存IP库
std::set<std::string> ipList;
// IPWhiteList构造函数
IPWhiteList::IPWhiteList(){
this->Init();
}
// 初始化参数ipList
void IPWhiteList::Init(){
ipList.erase(ipList.begin(), ipList.end());
}
// 通过文件记载IP库
void IPWhiteList::Load(std::string file){
this->Init();
this->ReadIPFromFile(file);
}
// 解析IP库
void IPWhiteList::ReadIPFromFile(std::string file){
ifstream infile(file.c_str());
string line;
if (!infile.is_open())
{
cout << "open white list file failed." << endl;
return;
}
while(getline(infile, line)) {
if (line.length() >= 7) {
int pos = line.find("#");
if(pos != -1)
continue;
this->ipList.insert(line);
}
}
infile.close();
}
在完成初始化后,如果需要实时的更新黑白名单库数据,可以通过调用api接口来修改,将会调用ghttp::IPManager_thread_new(说明:黑白名单的启用规则不支持动态切换,只能通过init.conf进行修改,修改后需要重启ghttp服务才生效),代码如下:
/**
* @brief IP manager
*
* @param response
* @param ips ip string
* @param ip_type 1-black ip 2-white ip
* @param type 1-query 2-save
*/
void IPManager_thread_new(const shared_ptr<HttpServer::Response>& response, string ips, string ip_type, string type) {
Document all;
Document::AllocatorType& allocator = all.GetAllocator();
all.SetObject();
// 查询接口将返回当前生效的规则是白名单还是黑名单,以及生效规则对应的IP库列表
if ( type == "1") { // query
Document responseBody;
Document listDoc;
responseBody.SetObject();
listDoc.SetArray();
// 在初始化时通过是否配置白名单路径来设置改参数值
if (whiteList) // white IP
{
cout << "IP white List enabled." << endl;
responseBody.AddMember("ip_type", "2", allocator);
for (std::set<std::string>::iterator it = ipWhiteList->ipList.begin(); it!=ipWhiteList->ipList.end();it++)
{
Value item(kStringType);
item.SetString((*it).c_str(), allocator);
listDoc.PushBack(item, allocator);
}
}
// 在初始化时通过是否配置黑名单路径来设置改参数值
// (若白名单已生效,则优先使用白名单,即便配置了黑名单库路径)
else if (blackList) // black IP
{
cout << "IP black List enabled." << endl;
responseBody.AddMember("ip_type", "1", allocator);
for (std::set<std::string>::iterator it = ipBlackList->ipList.begin(); it!=ipBlackList->ipList.end();it++)
{
Value item(kStringType);
item.SetString((*it).c_str(), allocator);
listDoc.PushBack(item, allocator);
}
}
......
responseBody.AddMember("ips", listDoc, allocator);
......
}
else if (type == "2") { // save
......
vector<string> ipVector;
Util::split(ips,",", ipVector);
if (ip_type == "1") { // black IP
// 更新黑名单库并重新加载
if (blackList) {
ipBlackList->UpdateIPToFile(ipBlackFile, ipVector, "update by wrokbanch");
// realod ip list
ipBlackList->Load(ipBlackFile);
}
......
}
else if (ip_type == "2") { // white IP
// 更新白名单库并重新加载
if (whiteList) {
ipWhiteList->UpdateIPToFile(ipWhiteFile, ipVector, "update by wrokbanch");
// realod ip list
ipWhiteList->Load(ipWhiteFile);
}
......
}
......
}
}
在更新黑白名单库时将会调用IPWhiteList::UpdateIPToFile和IPBlackList::UpdateIPToFile函数,将最新的配置数据更新到文件中,部分代码片段如下:
/**
* @brief
*
* @param file
* @param ips
* @param reason
*/
void IPWhiteList::UpdateIPToFile(std::string file, vector<std::string>& ips, std::string reason)
{
ofstream outfile;
outfile.open(file.c_str());
if (!outfile)
{
cout << "open white list file failed." << endl;
return;
}
// 记录每次变更的原因
outfile << "#" << reason << "\n";
for(vector<std::string>::iterator it = ips.begin(); it != ips.end(); it++)
{
outfile << (*it) << "\n";
}
outfile.close();
}
1.4 黑白名单校验
黑白名单校验一般是在开启gstore网络服务(如ghttp服务)后,外部通过接口对数据库进行操作时,会对发起请求的客户端IP进行验证,是否满足黑白名单规则要求,部分代码片段如下:
// 在请求处理的子线程中会对请求的接口进行IP验证
void request_thread(const shared_ptr<HttpServer::Response>& response,
const shared_ptr<HttpServer::Request>& request, string RequestType)
{
// 验证IP是否符合黑白名单的访问规则
if (!ipCheck(request)) {
string error = "IP Blocked!";
sendResponseMsg(1101, error, response,remote_ip,"ipCheck");
return;
}
......
}
在函数ghttp::ipCheck中,会判断当前黑白名单生效的规则,再根据生效规则进行校验,代码如下:
bool ghttp::ipCheck(const shared_ptr<HttpServer::Request>& request){
// 获取请求的IP地址
string remote_ip;
unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> m=request->header;
string map_key = "X-Real-IP";
pair<std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals>::iterator,std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals>::iterator> lu = m.equal_range(map_key);
if(lu.first != lu.second)
remote_ip = lu.first->second;
else
remote_ip = request->remote_endpoint_address;
// 执行白名单规则
if(whiteList == 1) {
return ipWhiteList->Check(remote_ip);
}
// 执行黑名单规则
else if(blackList == 1){
return ipBlackList->Check(remote_ip);
}
return true;
}
// 白名单规则匹配:命中规则返回true,否则返回false
bool IPWhiteList::Check(std::string ip){
if(this->ipList.empty())
return false;
else if(this->ipList.find(ip) != this->ipList.end())
return true;
for(std::set<string>::iterator it=this->ipList.begin(); it!=this->ipList.end(); ++it) {
// case for xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx
string test = *it;
if(test.find("-") != -1){
std::vector<std::string> fields;
std::vector<std::string> start;
std::vector<std::string> end;
std::vector<std::string> test_ip;
boost::split( fields, test, boost::is_any_of( "-" ) );
boost::split( start, fields[0], boost::is_any_of( "\\." ) );
boost::split( end, fields[1], boost::is_any_of( "\\." ) );
boost::split( test_ip, ip, boost::is_any_of( "\\." ) );
bool res = true;
for(int i = 0; i < 4; i++){
int s = std::stoi(start[i]);
int e = std::stoi(end[i]);
int candidate = std::stoi(test_ip[i]);
if(!(s <= candidate && candidate <= e)){
res = false;
break;
}
}
if(res)
return true;
}
}
return false;
}
// 黑名单规则匹配:命中规则返回false,否则返回true
bool IPBlackList::Check(std::string ip){
if(this->ipList.empty())
return true;
else if(this->ipList.find(ip) != this->ipList.end())
return false;
for(std::set<string>::iterator it=this->ipList.begin(); it!=this->ipList.end(); ++it){
// case for xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx
string test = *it;
if(test.find("-") != -1){
std::vector<std::string> fields;
std::vector<std::string> start;
std::vector<std::string> end;
std::vector<std::string> test_ip;
boost::split( fields, test, boost::is_any_of( "-" ) );
boost::split( start, fields[0], boost::is_any_of( "\\." ) );
boost::split( end, fields[1], boost::is_any_of( "\\." ) );
boost::split( test_ip, ip, boost::is_any_of( "\\." ) );
bool res = false;
for(int i = 0; i < 4; i++){
int s = std::stoi(start[i]);
int e = std::stoi(end[i]);
int candidate = std::stoi(test_ip[i]);
if(!(s <= candidate && candidate <= e)){
res = true;
break;
}
}
if(!res)
return false;
}
}
return true;
}
1.6 小结
本章节介绍了安全机制的黑白名单模块,分析了如何配置黑白名单、如何管理黑白名单以及如何使用黑白名单规则来校验请求,建议在阅读的同时结合源码Main/ghttp.cpp、Util/IPWhiteList.cpp、Util/IPBlackList.cpp一起分析,会更容易理解。下一章将解析gstore安全机制中的日志追踪模块。
边栏推荐
- 27: Chapter 3: develop Passport Service: 10: [registration / login] interface: after the registration / login is OK, save the user session information (uid, utoken) to redis and cookies; (one main poi
- M2DGR:多源多场景 地面机器人SLAM数据集(ICRA 2022 )
- Deep neural network Summary
- R语言ggplot2可视化:gganimate包创建动态柱状图动画(gif)、使用transition_states函数在动画中沿给定维度逐步显示柱状图
- How to use PS to extract image color and analyze color matching
- MySQL about only_ full_ group_ By limit
- MySQL advanced learning summary 7: MySQL data structure - Comparison of hash index, AVL tree, B tree and b+ tree
- 元宇宙链游系统开发(逻辑开发)丨链游系统开发(详细分析)
- Websocket of Web real-time communication technology
- STM32G0 USB DFU 升级校验出错-2
猜你喜欢

Looking for innocence in New York -- a beautiful day at the discovery center of Legoland, New Jersey

How to play when you travel to Bangkok for the first time? Please keep this money saving strategy

【JVM调优实战100例】01——JVM的介绍与程序计数器

Stm32g0 USB DFU upgrade verification error -2

Industrial software lecture - core technology analysis of 3D CAD design software - the second lecture of the Forum

Mini Golf Course: a good place for leisure and tourism in London

谷歌官方回应:我们没有放弃TensorFlow,未来与JAX并肩发展

How to clean up discarded PVs and their corresponding folders

医院在线问诊源码 医院视频问诊源码 医院小程序源码

Leetcode(81)——搜索旋转排序数组 II
随机推荐
Hongmeng's fourth learning
[100 cases of JVM tuning practice] 01 - introduction of JVM and program counter
How can retail enterprises open the second growth curve under the full link digital transformation
[100 cases of JVM tuning practice] 03 -- four cases of JVM heap tuning
Eliminate the yellow alarm light on IBM p750 small computer [easy to understand]
Radian to angle, angle to radian in MATLAB
Mysql高级篇学习总结8:InnoDB数据存储结构页的概述、页的内部结构、行格式
R language dplyr package Na_ The if function converts the control in the vector value into the missing value Na, and converts the specified content into the missing value Na according to the mapping r
M2DGR:多源多场景 地面机器人SLAM数据集(ICRA 2022 )
The difference between SLC, MLC, TLC and QLC NAND SSD: which is better?
材质UV遮罩的技巧
彻底搞懂基于Open3D的点云处理教程!
How to play when you travel to Bangkok for the first time? Please keep this money saving strategy
Meta universe chain game system development (logic development) - chain game system development (detailed analysis)
第一次去曼谷旅游怎么玩?这份省钱攻略请收好
R语言dplyr包rowwise函数、mutate函数计算dataframe数据中多个数据列在每行的最大值、并生成行最大值对应的数据列(row maximum)
Ali was wildly asked by the interviewer on three sides. Redis dared not write 'proficient' on his resume anymore
How to use PS to extract image color and analyze color matching
Tips for material UV masking
R language ggplot2 visualization: visualize the line chart and add customized X-axis label information to the line chart using labs function