87 lines
3.4 KiB
C++

#include <iostream>
#include <thread>
#include <cstdlib>
#include <fstream>
#include <string>
#include <format>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
#include <chrono>
using namespace std::chrono_literals;
struct User{
std::vector<std::pair<std::string,int>>timeMap{};
uint32_t totalHits{0U};
};
int main(){
while(true){
uint32_t logEntries{5000};
uint32_t secondsSeparationOfAccess{120}; //In X seconds, the number of entries cannot be reached.
uint32_t tooFrequentAccess{600}; //Number of entries required in the seconds range specified to consider things spam.
std::unordered_map<std::string,User>userData;
std::unordered_set<std::string>ipBanList;
std::cout<<"Reading config file..."<<std::endl;
std::ifstream config{"/home/sigonasr2/.giteablocker-config"};
if(!config.good()){
std::cout<<"No config file found, using defaults."<<std::endl;
}else{
std::string line{};
std::getline(config,line);
logEntries=stoi(line);
std::getline(config,line);
secondsSeparationOfAccess=stoi(line);
std::getline(config,line);
tooFrequentAccess=stoi(line);
config.close();
}
std::cout<<std::format("\tLog Entries to Read: {} lines",logEntries)<<std::endl;
std::cout<<std::format("\tAccess Ban Limit: {} accesses / {} seconds",tooFrequentAccess,secondsSeparationOfAccess)<<std::endl;
std::cout<<"Reading journal..."<<std::endl;
std::system(std::format("journalctl -n {} -u gitea > /home/sigonasr2/gitea.access",logEntries).c_str());
std::cout<<"Beginning parsing..."<<std::endl;
std::ifstream file{"/home/sigonasr2/gitea.access"};
while(!file.eof()){
std::string line{};
std::getline(file,line);
if(line.length()>0){
size_t marker{};
if((marker=line.find(':'))==std::string::npos)continue;
if((marker=line.find(':',marker+1))==std::string::npos)continue;
if((marker=line.find(' ',marker+1))==std::string::npos)continue;
std::string time{line.substr(0,marker)};
size_t prevMarker=marker+1;
if((marker=line.find("for ",marker+1))==std::string::npos)continue;
prevMarker=(marker+=4);
if((marker=line.find(":",marker+1))==std::string::npos)continue;
std::string ipAddr{line.substr(prevMarker,marker-prevMarker)};
if(ipBanList.count(ipAddr)||ipAddr=="45.33.13.215")continue;
User&user{userData[ipAddr]};
const auto timeObjIt{std::find_if(user.timeMap.begin(),user.timeMap.end(),[&](const std::pair<std::string,int>&val){return val.first==time;})};
if(timeObjIt!=user.timeMap.end())(*timeObjIt).second++;
else user.timeMap.emplace_back(std::pair<std::string,int>{time,1});
user.totalHits++;
if(user.totalHits>tooFrequentAccess){
ipBanList.insert(ipAddr);
std::system(std::format("iptables -D INPUT -s {} -j DROP",ipAddr).c_str()); //In case the rule already existed, we are going to remove it first... No effect if it does not exist.
std::system(std::format("iptables -I INPUT 1 -s {} -j DROP",ipAddr).c_str());
std::cout<<std::format("New offender {} detected and added to banlist!",ipAddr)<<std::endl;
}
if(user.timeMap.size()>secondsSeparationOfAccess){
user.totalHits-=user.timeMap[0].second;
user.timeMap.erase(user.timeMap.begin());
}
}
}
std::cout<<std::format("{} offenders were banned!",ipBanList.size())<<std::endl;
std::system("service netfilter-persistent save");
std::cout<<"New Netfilter settings saved."<<std::endl;
std::this_thread::sleep_for(5min);
}
}