#gStore-weekly | gStore源码解析(四):安全机制之黑白名单配置解析

​ 上一章我们介绍了安全机制的用户权限管理,接下来将对安全机制中黑白名单配置的源码进行解析。

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安全机制中的日志追踪模块。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值