Use MacOS logic for LinuxFirewall

This commit is contained in:
Mykola Baibuz 2024-01-24 17:20:50 -05:00
parent 874de74ac8
commit 5c9d45a8a8
6 changed files with 104 additions and 66 deletions

View file

@ -168,7 +168,7 @@ QStringList LinuxFirewall::getDNSRules(const QStringList& servers)
return result; return result;
} }
QStringList LinuxFirewall::getExcludeRule(const QStringList& servers) QStringList LinuxFirewall::getAllowRule(const QStringList& servers)
{ {
QStringList result; QStringList result;
for (const QString& server : servers) for (const QString& server : servers)
@ -178,6 +178,16 @@ QStringList LinuxFirewall::getExcludeRule(const QStringList& servers)
return result; return result;
} }
QStringList LinuxFirewall::getBlockRule(const QStringList& servers)
{
QStringList result;
for (const QString& server : servers)
{
result << QStringLiteral("-d %1 -j REJECT").arg(server);
}
return result;
}
void LinuxFirewall::install() void LinuxFirewall::install()
{ {
@ -237,10 +247,13 @@ void LinuxFirewall::install()
QStringLiteral("-o tun0+ -j ACCEPT"), QStringLiteral("-o tun0+ -j ACCEPT"),
}); });
installAnchor(IPv4, QStringLiteral("120.blockNets"), {});
installAnchor(IPv4, QStringLiteral("110.allowNets"), {});
installAnchor(Both, QStringLiteral("100.blockAll"), { installAnchor(Both, QStringLiteral("100.blockAll"), {
QStringLiteral("-j REJECT"), QStringLiteral("-j REJECT"),
}); });
// NAT rules // NAT rules
installAnchor(Both, QStringLiteral("100.transIp"), { installAnchor(Both, QStringLiteral("100.transIp"), {
@ -309,6 +322,8 @@ void LinuxFirewall::uninstall()
uninstallAnchor(Both, QStringLiteral("290.allowDHCP")); uninstallAnchor(Both, QStringLiteral("290.allowDHCP"));
uninstallAnchor(IPv6, QStringLiteral("250.blockIPv6")); uninstallAnchor(IPv6, QStringLiteral("250.blockIPv6"));
uninstallAnchor(Both, QStringLiteral("200.allowVPN")); uninstallAnchor(Both, QStringLiteral("200.allowVPN"));
uninstallAnchor(IPv4, QStringLiteral("120.blockNets"));
uninstallAnchor(IPv4, QStringLiteral("110.allowNets"));
uninstallAnchor(Both, QStringLiteral("100.blockAll")); uninstallAnchor(Both, QStringLiteral("100.blockAll"));
// Remove Nat anchors // Remove Nat anchors
@ -403,16 +418,25 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers)
execute(QStringLiteral("iptables -A %1.320.allowDNS %2").arg(kAnchorName, rule)); execute(QStringLiteral("iptables -A %1.320.allowDNS %2").arg(kAnchorName, rule));
} }
void LinuxFirewall::updateExcludeAddrs(const QStringList& servers) void LinuxFirewall::updateAllowNets(const QStringList& servers)
{ {
static QStringList existingServers {}; static QStringList existingServers {};
existingServers = servers; existingServers = servers;
execute(QStringLiteral("iptables -F %1.100.blockAll").arg(kAnchorName)); execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName));
for (const QString& rule : getExcludeRule(servers)) for (const QString& rule : getAllowRule(servers))
execute(QStringLiteral("iptables -A %1.100.blockAll %2").arg(kAnchorName, rule)); execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule));
} }
void LinuxFirewall::updateBlockNets(const QStringList& servers)
{
static QStringList existingServers {};
existingServers = servers;
execute(QStringLiteral("iptables -F %1.120.blockNets").arg(kAnchorName));
for (const QString& rule : getBlockRule(servers))
execute(QStringLiteral("iptables -A %1.120.blockNets %2").arg(kAnchorName, rule));
}
int waitForExitCode(QProcess& process) int waitForExitCode(QProcess& process)
{ {

View file

@ -12,8 +12,8 @@ struct FirewallParams
QStringList dnsServers; QStringList dnsServers;
// QSharedPointer<NetworkAdapter> adapter; // QSharedPointer<NetworkAdapter> adapter;
QVector<QString> excludeApps; // Apps to exclude if VPN exemptions are enabled QVector<QString> excludeApps; // Apps to exclude if VPN exemptions are enabled
QStringList allowAddrs;
QStringList excludeAddrs; QStringList blockAddrs;
// The follow flags indicate which general rulesets are needed. Note that // The follow flags indicate which general rulesets are needed. Note that
// this is after some sanity filtering, i.e. an allow rule may be listed // this is after some sanity filtering, i.e. an allow rule may be listed
// as not needed if there were no block rules preceding it. The rulesets // as not needed if there were no block rules preceding it. The rulesets
@ -29,6 +29,8 @@ struct FirewallParams
bool allowLoopback; // Exempt loopback traffic bool allowLoopback; // Exempt loopback traffic
bool allowHnsd; // Exempt Handshake DNS traffic bool allowHnsd; // Exempt Handshake DNS traffic
bool allowVpnExemptions; // Exempt specified traffic from the tunnel (route it over the physical uplink instead) bool allowVpnExemptions; // Exempt specified traffic from the tunnel (route it over the physical uplink instead)
bool allowNets;
bool blockNets;
}; };
class LinuxFirewall class LinuxFirewall
@ -47,7 +49,8 @@ private:
static void installAnchor(IPVersion ip, const QString& anchor, const QStringList& rules, const QString& tableName = kFilterTable, const FilterCallbackFunc& enableFunc = {}, const FilterCallbackFunc& disableFunc = {}); static void installAnchor(IPVersion ip, const QString& anchor, const QStringList& rules, const QString& tableName = kFilterTable, const FilterCallbackFunc& enableFunc = {}, const FilterCallbackFunc& disableFunc = {});
static void uninstallAnchor(IPVersion ip, const QString& anchor, const QString& tableName = kFilterTable); static void uninstallAnchor(IPVersion ip, const QString& anchor, const QString& tableName = kFilterTable);
static QStringList getDNSRules(const QStringList& servers); static QStringList getDNSRules(const QStringList& servers);
static QStringList getExcludeRule(const QStringList& servers); static QStringList getAllowRule(const QStringList& servers);
static QStringList getBlockRule(const QStringList& servers);
static void setupTrafficSplitting(); static void setupTrafficSplitting();
static void teardownTrafficSplitting(); static void teardownTrafficSplitting();
static int execute(const QString& command, bool ignoreErrors = false); static int execute(const QString& command, bool ignoreErrors = false);
@ -66,7 +69,8 @@ public:
static void setAnchorEnabled(IPVersion ip, const QString& anchor, bool enabled, const QString& tableName = kFilterTable); static void setAnchorEnabled(IPVersion ip, const QString& anchor, bool enabled, const QString& tableName = kFilterTable);
static void replaceAnchor(LinuxFirewall::IPVersion ip, const QString &anchor, const QString &newRule, const QString& tableName); static void replaceAnchor(LinuxFirewall::IPVersion ip, const QString &anchor, const QString &newRule, const QString& tableName);
static void updateDNSServers(const QStringList& servers); static void updateDNSServers(const QStringList& servers);
static void updateExcludeAddrs(const QStringList& servers); static void updateAllowNets(const QStringList& servers);
static void updateBlockNets(const QStringList& servers);
}; };

View file

@ -118,12 +118,26 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
int err = uapiErrno(uapiCommand(message)); int err = uapiErrno(uapiCommand(message));
if (err != 0) { if (err != 0) {
logger.error() << "Interface configuration failed:" << strerror(err); logger.error() << "Interface configuration failed:" << strerror(err);
} } else {
FirewallParams params { };
params.dnsServers.append(config.m_dnsServer);
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
params.blockAll = true;
if (config.m_excludedAddresses.size()) {
params.allowNets = true;
foreach (auto net, config.m_excludedAddresses) {
params.allowAddrs.append(net.toUtf8());
}
}
} else {
params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) {
params.blockAddrs.append(net.toString());
}
}
FirewallParams params {}; applyFirewallRules(params);
params.dnsServers.append(config.m_dnsServer); }
params.excludeAddrs.append(config.m_serverIpv4AddrIn);
applyFirewallRules(params);
return (err == 0); return (err == 0);
} }
@ -273,22 +287,19 @@ void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params)
LinuxFirewall::ensureRootAnchorPriority(); LinuxFirewall::ensureRootAnchorPriority();
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), params.blockAll);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("200.allowVPN"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), params.allowNets);
LinuxFirewall::updateAllowNets(params.allowAddrs);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), params.blockNets);
LinuxFirewall::updateBlockNets(params.blockAddrs);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("310.blockDNS"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
LinuxFirewall::updateDNSServers(params.dnsServers); LinuxFirewall::updateDNSServers(params.dnsServers);
LinuxFirewall::updateExcludeAddrs(params.excludeAddrs);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4,
QStringLiteral("100.vpnTunOnly"),
true,
LinuxFirewall::kRawTable);
} }
bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) { bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) {

View file

@ -144,12 +144,18 @@ uint OpenVpnProtocol::selectMgmtPort()
void OpenVpnProtocol::updateRouteGateway(QString line) void OpenVpnProtocol::updateRouteGateway(QString line)
{ {
// TODO: fix for macos if (line.contains("net_route_v4_best_gw")) {
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1); QStringList params = line.split(" ");
if (!line.contains("/")) if (params.size() == 6) {
return; m_routeGateway = params.at(3);
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first(); }
m_routeGateway.replace(" ", ""); } else {
line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1);
if (!line.contains("/"))
return;
m_routeGateway = line.split("/", Qt::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
}
qDebug() << "Set VPN route gateway" << m_routeGateway; qDebug() << "Set VPN route gateway" << m_routeGateway;
} }
@ -288,7 +294,7 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
} }
} }
if (line.contains("ROUTE_GATEWAY")) { if (line.contains("ROUTE_GATEWAY") || line.contains("net_route_v4_best_gw")) {
updateRouteGateway(line); updateRouteGateway(line);
} }

View file

@ -1,4 +1,4 @@
#include "ikev2ConfigModel.h".h " #include "ikev2ConfigModel.h"
#include "protocols/protocols_defs.h" #include "protocols/protocols_defs.h"

View file

@ -181,39 +181,7 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd
return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex); return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex);
#endif #endif
#ifdef Q_OS_LINUX #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// double-check + ensure our firewall is installed and enabled
if (!LinuxFirewall::isInstalled()) LinuxFirewall::install();
// Note: rule precedence is handled inside IpTablesFirewall
LinuxFirewall::ensureRootAnchorPriority();
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("200.allowVPN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
// LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("310.blockDNS"), true);
QStringList serverAddr;
serverAddr.append(configStr.value(amnezia::config_key::hostName).toString());
LinuxFirewall::updateExcludeAddrs(serverAddr);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
dnsServers.append("127.0.0.1");
dnsServers.append("127.0.0.53");
LinuxFirewall::updateDNSServers(dnsServers);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
// LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4,
// QStringLiteral("100.vpnTunOnly"),
// true,
// LinuxFirewall::kRawTable);
#endif
#ifdef Q_OS_MACOS
int splitTunnelType = configStr.value("splitTunnelType").toInt(); int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
bool blockAll = 0; bool blockAll = 0;
@ -241,6 +209,31 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd
allownets.append(v.toString()); allownets.append(v.toString());
} }
} }
#endif
#ifdef Q_OS_LINUX
// double-check + ensure our firewall is installed and enabled
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
LinuxFirewall::updateAllowNets(allownets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
LinuxFirewall::updateBlockNets(blocknets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
dnsServers.append("127.0.0.1");
dnsServers.append("127.0.0.53");
LinuxFirewall::updateDNSServers(dnsServers);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
#endif
#ifdef Q_OS_MACOS
// double-check + ensure our firewall is installed and enabled. This is necessary as // double-check + ensure our firewall is installed and enabled. This is necessary as
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)