118 lines
3.4 KiB
C++
118 lines
3.4 KiB
C++
/*
|
|
* BastionGuard™
|
|
* Copyright (C) 2025–2026 Calogero Scarnà
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* BastionGuard™ is a trademark of Calogero Scarnà.
|
|
* The BastionGuard™ name and branding are not licensed under the GPL.
|
|
*/
|
|
|
|
#include "FirewallRuleManager.hpp"
|
|
#include <iostream>
|
|
#include <regex>
|
|
#include <unordered_set>
|
|
#include <sstream>
|
|
#include <glib/gi18n.h>
|
|
|
|
static bool is_valid_ip(const std::string& ip) {
|
|
static const std::regex ipv4(
|
|
R"(^(25[0-5]|2[0-4]\d|1?\d{1,2})(\.(25[0-5]|2[0-4]\d|1?\d{1,2})){3}$)"
|
|
);
|
|
|
|
static const std::regex ipv6(
|
|
R"(^(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}$)"
|
|
);
|
|
|
|
return std::regex_match(ip, ipv4) || std::regex_match(ip, ipv6);
|
|
}
|
|
|
|
bool FirewallRuleManager::run(const std::string& cmd) {
|
|
int r = std::system(cmd.c_str());
|
|
if (r != 0)
|
|
std::cerr << _("[Firewall] Command failed: ") << cmd << "\n";
|
|
return r == 0;
|
|
}
|
|
|
|
void FirewallRuleManager::applyWithFirewalld(const std::vector<std::string>& ips) {
|
|
|
|
std::unordered_set<std::string> filtered;
|
|
|
|
for (const auto& ip : ips) {
|
|
if (is_valid_ip(ip))
|
|
filtered.insert(ip);
|
|
else
|
|
std::cerr << _("[Firewall] SKIP non-IP: ") << ip << "\n";
|
|
}
|
|
|
|
if (filtered.empty()) {
|
|
std::cerr << _("[Firewall] Nessun IP valido da applicare.\n");
|
|
return;
|
|
}
|
|
|
|
run("pkexec firewall-cmd --permanent --new-ipset=bastionguard-blocklist "
|
|
"--type=hash:ip --option=family=inet");
|
|
|
|
for (const auto& ip : filtered) {
|
|
std::string cmd =
|
|
"pkexec firewall-cmd --permanent "
|
|
"--ipset=bastionguard-blocklist --add-entry=" + ip;
|
|
run(cmd);
|
|
}
|
|
|
|
run("pkexec firewall-cmd --permanent "
|
|
"--add-rich-rule='rule source ipset=\"bastionguard-blocklist\" reject'");
|
|
|
|
run("pkexec firewall-cmd --reload");
|
|
}
|
|
|
|
void FirewallRuleManager::applyWithUfw(const std::vector<std::string>& ips) {
|
|
|
|
std::unordered_set<std::string> filtered;
|
|
|
|
for (const auto& ip : ips) {
|
|
if (is_valid_ip(ip))
|
|
filtered.insert(ip);
|
|
else
|
|
std::cerr << _("[Firewall] SKIP non-IP: ") << ip << "\n";
|
|
}
|
|
|
|
if (filtered.empty()) {
|
|
std::cerr << _("[Firewall] Nessun IP valido da applicare.\n");
|
|
return;
|
|
}
|
|
|
|
for (const auto& ip : filtered) {
|
|
run("pkexec ufw deny from " + ip);
|
|
}
|
|
|
|
run("pkexec ufw reload");
|
|
}
|
|
|
|
void FirewallRuleManager::apply(FirewallType fw, const std::vector<std::string>& ips) {
|
|
switch (fw) {
|
|
case FirewallType::FIREWALLD:
|
|
std::cout << _("[Firewall] Applying rules with firewalld (ipset)\n");
|
|
applyWithFirewalld(ips);
|
|
break;
|
|
|
|
case FirewallType::UFW:
|
|
std::cout << _("[Firewall] Applying rules with UFW\n");
|
|
applyWithUfw(ips);
|
|
break;
|
|
|
|
default:
|
|
std::cerr << _("[Firewall] Nessun firewall selezionato.\n");
|
|
break;
|
|
}
|
|
}
|