当前位置:网站首页>#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安全机制中的日志追踪模块。
边栏推荐
- Golang并发编程——goroutine、channel、sync
- Matlab中弧度转角度、角度转弧度
- 拦截器与过滤器的区别
- Typical application of "stack" - expression evaluation (implemented in C language)
- How to play when you travel to Bangkok for the first time? Please keep this money saving strategy
- M2dgr: slam data set of multi-source and multi scene ground robot (ICRA 2022)
- How to use PS to extract image color and analyze color matching
- 如何设置VSCode删除整行快捷键?
- What is cloud primordial? This time, I can finally understand!
- [100 cases of JVM tuning practice] 03 -- four cases of JVM heap tuning
猜你喜欢
【JVM调优实战100例】01——JVM的介绍与程序计数器
How to copy and paste interlaced in Excel
MySQL advanced learning summary 7: MySQL data structure - Comparison of hash index, AVL tree, B tree and b+ tree
MySQL advanced learning summary 8: overview of InnoDB data storage structure page, internal structure of page, row format
[daily question] first day
CDN acceleration and breaking J anti-theft chain function
Learning summary of MySQL advanced 6: concept and understanding of index, detailed explanation of b+ tree generation process, comparison between MyISAM and InnoDB
M2DGR:多源多场景 地面机器人SLAM数据集(ICRA 2022 )
[100 cases of JVM tuning practice] 01 - introduction of JVM and program counter
A simple PHP personal card issuing program v4.0
随机推荐
After 22 years in office, the father of PowerShell will leave Microsoft: he was demoted by Microsoft for developing PowerShell
科技公司不同人对Bug的反应 | 每日趣闻
Comprendre complètement le tutoriel de traitement de Point Cloud basé sur open3d!
元宇宙链游系统开发(逻辑开发)丨链游系统开发(详细分析)
How to enable the run dashboard function of idea
Eliminate the yellow alarm light on IBM p750 small computer [easy to understand]
MySQL advanced learning summary 8: overview of InnoDB data storage structure page, internal structure of page, row format
使用 Cheat Engine 修改 Kingdom Rush 中的金钱、生命、星
A simple PHP personal card issuing program v4.0
迷你高尔夫球场:伦敦休闲旅游好去处
Singapore summer tourism strategy: play Singapore Sentosa Island in one day
How to set vscode to delete the whole line shortcut key?
Looking for innocence in New York -- a beautiful day at the discovery center of Legoland, New Jersey
从list转化成map的时候,如果根据某一属性可能会导致key重复而异常,可以设置处理这种重复的方式
Introduction to sap s/4hana OData mock service
R language uses the lsnofunction function function of epidisplay package to list all objects in the current space, except user-defined function objects
Meal card hdu2546
Thoroughly understand the point cloud processing tutorial based on open3d!
Stretchdibits function
【JVM调优实战100例】02——虚拟机栈与本地方法栈调优五例