From 5c9d45a8a83f078a23193808b114850861dcbd95 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 24 Jan 2024 17:20:50 -0500 Subject: [PATCH] Use MacOS logic for LinuxFirewall --- .../platforms/linux/daemon/linuxfirewall.cpp | 36 +++++++++-- client/platforms/linux/daemon/linuxfirewall.h | 12 ++-- .../linux/daemon/wireguardutilslinux.cpp | 41 ++++++++----- client/protocols/openvpnprotocol.cpp | 20 ++++--- .../ui/models/protocols/ikev2ConfigModel.cpp | 2 +- ipc/ipcserver.cpp | 59 ++++++++----------- 6 files changed, 104 insertions(+), 66 deletions(-) diff --git a/client/platforms/linux/daemon/linuxfirewall.cpp b/client/platforms/linux/daemon/linuxfirewall.cpp index ab51ea740..0d32b56dd 100644 --- a/client/platforms/linux/daemon/linuxfirewall.cpp +++ b/client/platforms/linux/daemon/linuxfirewall.cpp @@ -168,7 +168,7 @@ QStringList LinuxFirewall::getDNSRules(const QStringList& servers) return result; } -QStringList LinuxFirewall::getExcludeRule(const QStringList& servers) +QStringList LinuxFirewall::getAllowRule(const QStringList& servers) { QStringList result; for (const QString& server : servers) @@ -178,6 +178,16 @@ QStringList LinuxFirewall::getExcludeRule(const QStringList& servers) 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() { @@ -237,10 +247,13 @@ void LinuxFirewall::install() QStringLiteral("-o tun0+ -j ACCEPT"), }); + installAnchor(IPv4, QStringLiteral("120.blockNets"), {}); + + installAnchor(IPv4, QStringLiteral("110.allowNets"), {}); + installAnchor(Both, QStringLiteral("100.blockAll"), { QStringLiteral("-j REJECT"), }); - // NAT rules installAnchor(Both, QStringLiteral("100.transIp"), { @@ -309,6 +322,8 @@ void LinuxFirewall::uninstall() uninstallAnchor(Both, QStringLiteral("290.allowDHCP")); uninstallAnchor(IPv6, QStringLiteral("250.blockIPv6")); uninstallAnchor(Both, QStringLiteral("200.allowVPN")); + uninstallAnchor(IPv4, QStringLiteral("120.blockNets")); + uninstallAnchor(IPv4, QStringLiteral("110.allowNets")); uninstallAnchor(Both, QStringLiteral("100.blockAll")); // Remove Nat anchors @@ -403,16 +418,25 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers) 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 {}; existingServers = servers; - execute(QStringLiteral("iptables -F %1.100.blockAll").arg(kAnchorName)); - for (const QString& rule : getExcludeRule(servers)) - execute(QStringLiteral("iptables -A %1.100.blockAll %2").arg(kAnchorName, rule)); + execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName)); + for (const QString& rule : getAllowRule(servers)) + 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) { diff --git a/client/platforms/linux/daemon/linuxfirewall.h b/client/platforms/linux/daemon/linuxfirewall.h index 486fafbcd..9e9412a47 100644 --- a/client/platforms/linux/daemon/linuxfirewall.h +++ b/client/platforms/linux/daemon/linuxfirewall.h @@ -12,8 +12,8 @@ struct FirewallParams QStringList dnsServers; // QSharedPointer adapter; QVector excludeApps; // Apps to exclude if VPN exemptions are enabled - - QStringList excludeAddrs; + QStringList allowAddrs; + QStringList blockAddrs; // 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 // 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 allowHnsd; // Exempt Handshake DNS traffic bool allowVpnExemptions; // Exempt specified traffic from the tunnel (route it over the physical uplink instead) + bool allowNets; + bool blockNets; }; 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 uninstallAnchor(IPVersion ip, const QString& anchor, const QString& tableName = kFilterTable); 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 teardownTrafficSplitting(); 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 replaceAnchor(LinuxFirewall::IPVersion ip, const QString &anchor, const QString &newRule, const QString& tableName); static void updateDNSServers(const QStringList& servers); - static void updateExcludeAddrs(const QStringList& servers); + static void updateAllowNets(const QStringList& servers); + static void updateBlockNets(const QStringList& servers); }; diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 79f7dd2dd..e5dce5249 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -118,12 +118,26 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { int err = uapiErrno(uapiCommand(message)); if (err != 0) { 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 {}; - params.dnsServers.append(config.m_dnsServer); - params.excludeAddrs.append(config.m_serverIpv4AddrIn); - applyFirewallRules(params); + applyFirewallRules(params); + } return (err == 0); } @@ -273,22 +287,19 @@ void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params) 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::Both, QStringLiteral("100.blockAll"), params.blockAll); + 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::Both, QStringLiteral("290.allowDHCP"), 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::updateExcludeAddrs(params.excludeAddrs); 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); - } bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) { diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index f9196fc36..5f8db6256 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -144,12 +144,18 @@ uint OpenVpnProtocol::selectMgmtPort() void OpenVpnProtocol::updateRouteGateway(QString line) { - // TODO: fix for macos - line = line.split("ROUTE_GATEWAY", Qt::SkipEmptyParts).at(1); - if (!line.contains("/")) - return; - m_routeGateway = line.split("/", Qt::SkipEmptyParts).first(); - m_routeGateway.replace(" ", ""); + if (line.contains("net_route_v4_best_gw")) { + QStringList params = line.split(" "); + if (params.size() == 6) { + m_routeGateway = params.at(3); + } + } 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; } @@ -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); } diff --git a/client/ui/models/protocols/ikev2ConfigModel.cpp b/client/ui/models/protocols/ikev2ConfigModel.cpp index f22b965c5..a11b66523 100644 --- a/client/ui/models/protocols/ikev2ConfigModel.cpp +++ b/client/ui/models/protocols/ikev2ConfigModel.cpp @@ -1,4 +1,4 @@ -#include "ikev2ConfigModel.h".h " +#include "ikev2ConfigModel.h" #include "protocols/protocols_defs.h" diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index a19039f6e..c0e87fc92 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -181,39 +181,7 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex); #endif -#ifdef Q_OS_LINUX - // 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 +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) int splitTunnelType = configStr.value("splitTunnelType").toInt(); QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); bool blockAll = 0; @@ -241,6 +209,31 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd 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 // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)