当前位置:网站首页>#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安全机制中的日志追踪模块。
边栏推荐
- 在Tensorflow2中使用mnist_784数据集进行手写数字识别
- 新加坡暑假旅遊攻略:一天玩轉新加坡聖淘沙島
- Kubernetes three open interfaces first sight
- Websocket of Web real-time communication technology
- R language ggplot2 visualization: gganimate package creates dynamic histogram animation (GIF) and uses transition_ The States function displays a histogram step by step along a given dimension in the
- R语言使用epiDisplay包的lrtest函数对多个glm模型(logisti回归)执行似然比检验(Likelihood ratio test)对比两个模型的性能是否有差异、广义线性模型的似然比检
- sql训练2
- 学生抖音宣传母校被吐槽“招生减章”,网友:哈哈哈哈哈哈
- Competence of product manager
- 使用CLion编译OGLPG-9th-Edition源码
猜你喜欢
UML 类图
使用 Cheat Engine 修改 Kingdom Rush 中的金钱、生命、星
距离度量 —— 杰卡德距离(Jaccard Distance)
LightGroupButton* sender = static_ cast<LightGroupButton*>(QObject::sender());
新加坡暑假旅遊攻略:一天玩轉新加坡聖淘沙島
Stratégie touristique d'été de Singapour: un jour pour visiter l'île de San taosha à Singapour
Excel查找一列中的相同值,删除该行或替换为空值
Responses of different people in technology companies to bugs | daily anecdotes
科技公司不同人对Bug的反应 | 每日趣闻
什么是云原生?这回终于能搞明白了!
随机推荐
After 22 years in office, the father of PowerShell will leave Microsoft: he was demoted by Microsoft for developing PowerShell
[100 cases of JVM tuning practice] 01 - introduction of JVM and program counter
How to copy and paste interlaced in Excel
阿里三面被面试官狂问Redis,简历上再也不敢写'精通'了
How to delete the border of links in IE? [repeat] - how to remove borders around links in IE? [duplicate]
STM32G0 USB DFU 升级校验出错-2
The text editor hopes to mark the wrong sentences in red, and the text editor uses markdown
Kubernetes three open interfaces first sight
[daily question] the next day
第一次去曼谷旅游怎么玩?这份省钱攻略请收好
Stm32g0 USB DFU upgrade verification error -2
How to enable the run dashboard function of idea
鸿蒙第四次学习
新加坡暑假旅游攻略:一天玩转新加坡圣淘沙岛
Meal card hdu2546
Redis (7) -- database and expiration key
【JVM调优实战100例】02——虚拟机栈与本地方法栈调优五例
Web实时通信技术之Websocket
Meta universe chain game system development (logic development) - chain game system development (detailed analysis)
Comprendre complètement le tutoriel de traitement de Point Cloud basé sur open3d!