mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-21 02:01:03 +07:00
feat: per-tunnel Windows firewall for seamless WG switch
This commit is contained in:
@@ -79,7 +79,7 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const
|
||||
QString ip = NetworkUtilities::getIPAddress(host);
|
||||
if (!ip.isEmpty()) {
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
QRemoteObjectPendingReply<bool> reply = iface->addKillSwitchAllowedRange(QStringList { ip });
|
||||
QRemoteObjectPendingReply<bool> reply = iface->addKillSwitchAllowedRange(QString(), QStringList { ip });
|
||||
if (!reply.waitForFinished(1000) || !reply.returnValue())
|
||||
qWarning() << "GatewayController::prepareRequest(): Failed to execute remote addKillSwitchAllowedRange call";
|
||||
});
|
||||
|
||||
@@ -41,6 +41,10 @@ public:
|
||||
State state() const { return m_state; }
|
||||
QSharedPointer<VpnProtocol> protocol() const { return m_protocol; }
|
||||
|
||||
const QString& handoverIfname() const { return m_handoverIfname; }
|
||||
void setHandoverIfname(const QString& ifname) { m_handoverIfname = ifname; }
|
||||
void clearHandoverIfname() { m_handoverIfname.clear(); }
|
||||
|
||||
virtual void prepare();
|
||||
virtual void commit();
|
||||
virtual void deactivate();
|
||||
@@ -61,6 +65,7 @@ protected:
|
||||
|
||||
QString m_ifname;
|
||||
QString m_remoteAddress;
|
||||
QString m_handoverIfname;
|
||||
amnezia::DockerContainer m_container;
|
||||
QJsonObject m_config;
|
||||
QSharedPointer<VpnProtocol> m_protocol;
|
||||
|
||||
@@ -31,37 +31,27 @@ void VpnTrafficGuard::setConfig(const QJsonObject &config)
|
||||
m_config = config;
|
||||
}
|
||||
|
||||
bool VpnTrafficGuard::allowEndpoint(const QString &remoteAddress)
|
||||
bool VpnTrafficGuard::allowEndpoint(const QString &remoteAddress, const QString &ifname)
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (remoteAddress.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (!m_allowedEndpoints.contains(remoteAddress)) {
|
||||
m_allowedEndpoints.append(remoteAddress);
|
||||
if (m_allowedEndpoints.contains(remoteAddress)) {
|
||||
return true;
|
||||
}
|
||||
m_allowedEndpoints.append(remoteAddress);
|
||||
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
QRemoteObjectPendingReply<bool> reply = iface->addKillSwitchAllowedRange(QStringList(remoteAddress));
|
||||
QRemoteObjectPendingReply<bool> reply = iface->addKillSwitchAllowedRange(ifname, QStringList(remoteAddress));
|
||||
return reply.waitForFinished(1000) && reply.returnValue();
|
||||
});
|
||||
#else
|
||||
Q_UNUSED(remoteAddress)
|
||||
Q_UNUSED(ifname)
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::revokeEndpoint(const QString &remoteAddress)
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
m_allowedEndpoints.removeAll(remoteAddress);
|
||||
IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
iface->resetKillSwitchAllowedRange(m_allowedEndpoints);
|
||||
});
|
||||
#else
|
||||
Q_UNUSED(remoteAddress)
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSharedPointer<VpnProtocol> &protocol, const QString &remoteAddress)
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
@@ -175,9 +165,29 @@ void VpnTrafficGuard::addSplitTunnelRoutes(const QString &gw, amnezia::RouteMode
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::applyFirewall(const QString &gateway, const QString &localAddress)
|
||||
void VpnTrafficGuard::finishFirewallHandover(Tunnel* tunnel)
|
||||
{
|
||||
#if defined(AMNEZIA_DESKTOP) && defined(Q_OS_WIN)
|
||||
if (!tunnel) return;
|
||||
const QString handoverIfname = tunnel->handoverIfname();
|
||||
if (handoverIfname.isEmpty() || handoverIfname == tunnel->ifname()) {
|
||||
tunnel->clearHandoverIfname();
|
||||
return;
|
||||
}
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
iface->disableKillSwitchForTunnel(handoverIfname, QStringList());
|
||||
});
|
||||
tunnel->clearHandoverIfname();
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void VpnTrafficGuard::applyKillSwitch(Tunnel* tunnel, const QString &gateway, const QString &localAddress)
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
finishFirewallHandover(tunnel);
|
||||
|
||||
QJsonObject updatedConfig = m_config;
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -213,10 +223,10 @@ void VpnTrafficGuard::applyFirewall(const QString &gateway, const QString &local
|
||||
NetworkUtilities::getIPAddress(updatedConfig.value(amnezia::configKey::hostName).toString()));
|
||||
QRemoteObjectPendingReply<bool> reply = iface->enableKillSwitch(updatedConfig, 0);
|
||||
//TODO: why it takes so long?
|
||||
if (!reply.waitForFinished(5000) || !reply.returnValue()) {
|
||||
qWarning() << "VpnTrafficGuard::applyFirewall: Failed to enable killswitch";
|
||||
if (!reply.waitForFinished(1000) || !reply.returnValue()) {
|
||||
qWarning() << "VpnTrafficGuard::applyKillSwitch: Failed to enable killswitch";
|
||||
} else {
|
||||
qDebug() << "VpnTrafficGuard::applyFirewall: Successfully enabled killswitch";
|
||||
qDebug() << "VpnTrafficGuard::applyKillSwitch: Successfully enabled killswitch";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -250,7 +260,7 @@ void VpnTrafficGuard::flushAll()
|
||||
QRemoteObjectPendingReply<bool> reply = iface->disableKillSwitch();
|
||||
m_allowedEndpoints.clear();
|
||||
//TODO: why it takes so long?
|
||||
if (!reply.waitForFinished(5000) || !reply.returnValue()) {
|
||||
if (!reply.waitForFinished(1000) || !reply.returnValue()) {
|
||||
qWarning() << "VpnTrafficGuard::flushAll: Failed to disable killswitch";
|
||||
} else {
|
||||
qDebug() << "VpnTrafficGuard::flushAll: Successfully disabled killswitch";
|
||||
@@ -318,7 +328,7 @@ void VpnTrafficGuard::reserve(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
allowEndpoint(tunnel->remoteAddress());
|
||||
allowEndpoint(tunnel->remoteAddress(), tunnel->ifname());
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
@@ -327,8 +337,12 @@ void VpnTrafficGuard::reserve(Tunnel* tunnel)
|
||||
void VpnTrafficGuard::release(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
disconnect(tunnel, nullptr, this, nullptr);
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
revokeEndpoint(tunnel->remoteAddress());
|
||||
m_allowedEndpoints.removeAll(tunnel->remoteAddress());
|
||||
IpcClient::withInterface([this, &tunnel](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
iface->disableKillSwitchForTunnel(tunnel->ifname(), m_allowedEndpoints);
|
||||
});
|
||||
#else
|
||||
Q_UNUSED(tunnel)
|
||||
#endif
|
||||
@@ -396,6 +410,17 @@ void VpnTrafficGuard::commit(Tunnel* tunnel)
|
||||
{
|
||||
if (!tunnel) return;
|
||||
applyPolicy(tunnel);
|
||||
connect(tunnel, &Tunnel::activated, this, [this, tunnel] {
|
||||
if (auto p = tunnel->protocol()) {
|
||||
applyKillSwitch(tunnel, p->vpnGateway(), p->vpnLocalAddress());
|
||||
}
|
||||
});
|
||||
#ifdef Q_OS_WIN
|
||||
connect(tunnel, &Tunnel::addressesUpdated, this,
|
||||
[this, tunnel](const QString& gw, const QString& la) {
|
||||
applyKillSwitch(tunnel, gw, la);
|
||||
});
|
||||
#endif
|
||||
tunnel->commit();
|
||||
}
|
||||
|
||||
@@ -410,11 +435,18 @@ void VpnTrafficGuard::tearDown(Tunnel* tunnel)
|
||||
void VpnTrafficGuard::swap(Tunnel* from, Tunnel* to)
|
||||
{
|
||||
if (!to) return;
|
||||
applyPolicy(to);
|
||||
to->commit();
|
||||
if (from) {
|
||||
to->setHandoverIfname(from->ifname());
|
||||
}
|
||||
commit(to);
|
||||
if (from) {
|
||||
m_allowedEndpoints.removeAll(from->remoteAddress());
|
||||
#ifndef Q_OS_WIN
|
||||
IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
iface->resetKillSwitchAllowedRange(m_allowedEndpoints);
|
||||
});
|
||||
#endif
|
||||
revokePolicy(from);
|
||||
release(from);
|
||||
from->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,8 @@ public:
|
||||
const QString &remoteAddress);
|
||||
|
||||
void flushAll();
|
||||
bool allowEndpoint(const QString &remoteAddress);
|
||||
void revokeEndpoint(const QString &remoteAddress);
|
||||
void applyFirewall(const QString &vpnGateway, const QString &vpnLocalAddress);
|
||||
bool allowEndpoint(const QString &remoteAddress, const QString &ifname = QString());
|
||||
void applyKillSwitch(Tunnel* tunnel, const QString &vpnGateway, const QString &vpnLocalAddress);
|
||||
|
||||
void reserve(Tunnel* tunnel);
|
||||
void release(Tunnel* tunnel);
|
||||
@@ -36,6 +35,7 @@ public:
|
||||
|
||||
private:
|
||||
void addSplitTunnelRoutes(const QString &gateway, amnezia::RouteMode mode);
|
||||
void finishFirewallHandover(Tunnel* tunnel);
|
||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||
QJsonObject m_config;
|
||||
bool m_ipv6RoutingStopped = false;
|
||||
|
||||
@@ -159,7 +159,7 @@ bool WindowsFirewall::initSublayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||
bool WindowsFirewall::enableInterface(int vpnAdapterIndex, const QString& ifname) {
|
||||
// Checks if the FW_Rule was enabled succesfully,
|
||||
// disables the whole killswitch and returns false if not.
|
||||
#define FW_OK(rule) \
|
||||
@@ -182,31 +182,39 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||
} \
|
||||
}
|
||||
|
||||
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
||||
if (vpnAdapterIndex < 0)
|
||||
{
|
||||
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex
|
||||
<< "ifname:" << ifname;
|
||||
|
||||
QList<uint64_t>& perTunnel = ifname.isEmpty() ? m_globalRules
|
||||
: m_tunnelRules[ifname];
|
||||
if (vpnAdapterIndex < 0) {
|
||||
IPAddress allv4("0.0.0.0/0");
|
||||
if (!blockTrafficTo(allv4, MED_WEIGHT,
|
||||
"Block Internet", "killswitch")) {
|
||||
return false;
|
||||
}
|
||||
IPAddress allv6("::/0");
|
||||
if (!blockTrafficTo(allv6, MED_WEIGHT,
|
||||
"Block Internet", "killswitch")) {
|
||||
if (!blockTrafficTo(allv4, MED_WEIGHT, "Block Internet", perTunnel)) {
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||
"Allow usage of VPN Adapter"));
|
||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
|
||||
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||
"Allow all for AmneziaVPN.exe"));
|
||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||
FW_OK(allowLoopbackTraffic(MED_WEIGHT,
|
||||
"Allow Loopback traffic on device %1"));
|
||||
IPAddress allv6("::/0");
|
||||
if (!blockTrafficTo(allv6, MED_WEIGHT, "Block Internet", perTunnel)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||
"Allow usage of VPN Adapter", perTunnel));
|
||||
}
|
||||
|
||||
logger.debug() << "Killswitch on! Rules:" << m_activeRules.length();
|
||||
if (m_globalRules.isEmpty()) {
|
||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic", m_globalRules));
|
||||
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic", m_globalRules));
|
||||
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||
"Allow all for AmneziaVPN.exe", m_globalRules));
|
||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS", m_globalRules));
|
||||
FW_OK(allowLoopbackTraffic(MED_WEIGHT,
|
||||
"Allow Loopback traffic on device %1",
|
||||
m_globalRules));
|
||||
}
|
||||
|
||||
logger.debug() << "Killswitch on! Globals:" << m_globalRules.length()
|
||||
<< "Tunnel[" << ifname
|
||||
<< "]:" << m_tunnelRules.value(ifname).length();
|
||||
return true;
|
||||
#undef FW_OK
|
||||
}
|
||||
@@ -226,7 +234,8 @@ bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
|
||||
|
||||
// Blocking unprotected traffic
|
||||
for (const IPAddress& prefix : ranges) {
|
||||
if (!allowTrafficTo(prefix, LOW_WEIGHT + 1, "Allow LAN bypass traffic")) {
|
||||
if (!allowTrafficTo(prefix, LOW_WEIGHT + 1, "Allow LAN bypass traffic",
|
||||
m_globalRules)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -242,7 +251,10 @@ bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
|
||||
}
|
||||
|
||||
// Allow unprotected traffic sent to the following address ranges.
|
||||
bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||
bool WindowsFirewall::allowTrafficRange(const QStringList& ranges, const QString& ifname) {
|
||||
QList<uint64_t>& target = ifname.isEmpty() ? m_globalRules
|
||||
: m_tunnelRules[ifname];
|
||||
|
||||
// Start the firewall transaction
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
@@ -255,8 +267,9 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||
});
|
||||
|
||||
for (const QString& addr : ranges) {
|
||||
logger.debug() << "Allow killswitch exclude: " << addr;
|
||||
if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, "Allow killswitch bypass traffic")) {
|
||||
logger.debug() << "Allow killswitch exclude: " << addr << "ifname:" << ifname;
|
||||
if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT,
|
||||
"Allow killswitch bypass traffic", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -273,6 +286,10 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||
|
||||
|
||||
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
QList<uint64_t>& target = config.m_ifname.isEmpty()
|
||||
? m_globalRules
|
||||
: m_tunnelRules[config.m_ifname];
|
||||
|
||||
// Start the firewall transaction
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
@@ -288,12 +305,12 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
logger.info() << "Enabling traffic for peer"
|
||||
<< config.m_serverPublicKey;
|
||||
if (!blockTrafficTo(config.m_allowedIPAddressRanges, LOW_WEIGHT,
|
||||
"Block Internet", config.m_serverPublicKey)) {
|
||||
"Block Internet", target)) {
|
||||
return false;
|
||||
}
|
||||
if (!config.m_primaryDnsServer.isEmpty()) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
"Allow DNS-Server", target)) {
|
||||
return false;
|
||||
}
|
||||
// In some cases, we might configure a 2nd DNS server for IPv6, however
|
||||
@@ -302,7 +319,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
|
||||
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
|
||||
config.m_serverPublicKey)) {
|
||||
target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -310,7 +327,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
|
||||
if (!config.m_secondaryDnsServer.isEmpty()) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_secondaryDnsServer), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
"Allow DNS-Server", target)) {
|
||||
return false;
|
||||
}
|
||||
// In some cases, we might configure a 2nd DNS server for IPv6, however
|
||||
@@ -319,7 +336,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
if (config.m_secondaryDnsServer == config.m_serverIpv4Gateway) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
|
||||
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
|
||||
config.m_serverPublicKey)) {
|
||||
target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -328,7 +345,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
for (const QString& dns : config.m_allowedDnsServers) {
|
||||
logger.debug() << "Allow DNS: " << dns;
|
||||
if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT,
|
||||
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||
"Allow DNS-Server", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -338,7 +355,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
logger.debug() << "excludedAddresses range: " << i;
|
||||
|
||||
if (!allowTrafficTo(i, HIGH_WEIGHT,
|
||||
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
||||
"Allow Ecxlude route", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -354,35 +371,6 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
auto cleanup = qScopeGuard([&] {
|
||||
if (result != ERROR_SUCCESS) {
|
||||
FwpmTransactionAbort0(m_sessionHandle);
|
||||
}
|
||||
});
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info() << "Disabling traffic for peer" << pubkey;
|
||||
for (const auto& filterID : m_peerRules.values(pubkey)) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
m_peerRules.remove(pubkey, filterID);
|
||||
}
|
||||
|
||||
// Commit!
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::disableKillSwitch() {
|
||||
return KillSwitch::instance()->disableKillSwitch();
|
||||
}
|
||||
@@ -400,11 +388,13 @@ bool WindowsFirewall::allowAllTraffic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& filterID : m_peerRules.values()) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
for (const auto& bucket : qAsConst(m_tunnelRules)) {
|
||||
for (const auto& filterID : bucket) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& filterID : qAsConst(m_activeRules)) {
|
||||
for (const auto& filterID : qAsConst(m_globalRules)) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
|
||||
@@ -415,15 +405,42 @@ bool WindowsFirewall::allowAllTraffic() {
|
||||
<< result;
|
||||
return false;
|
||||
}
|
||||
m_peerRules.clear();
|
||||
m_activeRules.clear();
|
||||
m_tunnelRules.clear();
|
||||
m_globalRules.clear();
|
||||
logger.debug() << "Firewall Disabled!";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::disableKillSwitchForTunnel(const QString& ifname) {
|
||||
if (ifname.isEmpty() || !m_tunnelRules.contains(ifname)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionBegin0 failed. Return value:" << result;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QList<uint64_t> filters = m_tunnelRules.take(ifname);
|
||||
logger.info() << "Disabling killswitch filters for tunnel" << ifname
|
||||
<< "count:" << filters.length();
|
||||
for (const auto& filterID : filters) {
|
||||
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||
}
|
||||
|
||||
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
logger.error() << "FwpmTransactionCommit0 failed. Return value:" << result;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||
int weight,
|
||||
const QString& title) {
|
||||
const QString& title,
|
||||
QList<uint64_t>& target) {
|
||||
DWORD result = ERROR_SUCCESS;
|
||||
Q_ASSERT(weight <= 15);
|
||||
|
||||
@@ -460,7 +477,7 @@ bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||
{
|
||||
QString desc("Permit (out) IPv4 Traffic of: " + appName);
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
if (!enableFilter(&filter, title, desc)) {
|
||||
if (!enableFilter(&filter, title, desc, target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -468,7 +485,7 @@ bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||
{
|
||||
QString desc("Permit (in) IPv4 Traffic of: " + appName);
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||
if (!enableFilter(&filter, title, desc)) {
|
||||
if (!enableFilter(&filter, title, desc, target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -476,7 +493,8 @@ bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||
const QString& title) {
|
||||
const QString& title,
|
||||
QList<uint64_t>& target) {
|
||||
FWPM_FILTER_CONDITION0 conds;
|
||||
// Condition: Request must be targeting the TUN interface
|
||||
conds.fieldKey = FWPM_CONDITION_INTERFACE_INDEX;
|
||||
@@ -498,25 +516,25 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||
// #1 Permit outbound IPv4 traffic.
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("out").arg(networkAdapter))) {
|
||||
description.arg("out").arg(networkAdapter), target)) {
|
||||
return false;
|
||||
}
|
||||
// #2 Permit inbound IPv4 traffic.
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("in").arg(networkAdapter))) {
|
||||
description.arg("in").arg(networkAdapter), target)) {
|
||||
return false;
|
||||
}
|
||||
// #3 Permit outbound IPv6 traffic.
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("out").arg(networkAdapter))) {
|
||||
description.arg("out").arg(networkAdapter), target)) {
|
||||
return false;
|
||||
}
|
||||
// #4 Permit inbound IPv6 traffic.
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("in").arg(networkAdapter))) {
|
||||
description.arg("in").arg(networkAdapter), target)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -524,7 +542,7 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||
|
||||
bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight,
|
||||
const QString& title,
|
||||
const QString& peer) {
|
||||
QList<uint64_t>& target) {
|
||||
GUID layerKeyOut;
|
||||
GUID layerKeyIn;
|
||||
if (addr.type() == QAbstractSocket::IPv4Protocol) {
|
||||
@@ -562,11 +580,11 @@ bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight,
|
||||
// Send the filters down to the firewall.
|
||||
QString description = "Permit traffic %1 " + addr.toString();
|
||||
filter.layerKey = layerKeyOut;
|
||||
if (!enableFilter(&filter, title, description.arg("to"), peer)) {
|
||||
if (!enableFilter(&filter, title, description.arg("to"), target)) {
|
||||
return false;
|
||||
}
|
||||
filter.layerKey = layerKeyIn;
|
||||
if (!enableFilter(&filter, title, description.arg("from"), peer)) {
|
||||
if (!enableFilter(&filter, title, description.arg("from"), target)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -574,7 +592,7 @@ bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight,
|
||||
|
||||
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
||||
int weight, const QString& title,
|
||||
const QString& peer) {
|
||||
QList<uint64_t>& target) {
|
||||
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
|
||||
GUID layerOut =
|
||||
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
@@ -623,19 +641,20 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
||||
filter.layerKey = layerOut;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("to").arg(targetIP.toString()).arg(port),
|
||||
peer)) {
|
||||
target)) {
|
||||
return false;
|
||||
}
|
||||
filter.layerKey = layerIn;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("from").arg(targetIP.toString()).arg(port),
|
||||
peer)) {
|
||||
target)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target) {
|
||||
// Allow outbound DHCPv4
|
||||
{
|
||||
FWPM_FILTER_CONDITION0 conds[4];
|
||||
@@ -672,7 +691,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
|
||||
if (!enableFilter(&filter, title, "Allow Outbound DHCP")) {
|
||||
if (!enableFilter(&filter, title, "Allow Outbound DHCP", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -705,7 +724,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||
|
||||
if (!enableFilter(&filter, title, "Allow inbound DHCP")) {
|
||||
if (!enableFilter(&filter, title, "Allow inbound DHCP", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -740,7 +759,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
if (!enableFilter(&filter, title, "Allow outbound DHCPv6")) {
|
||||
if (!enableFilter(&filter, title, "Allow outbound DHCPv6", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -773,7 +792,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||
filter.weight.uint8 = weight;
|
||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||
if (!enableFilter(&filter, title, "Allow inbound DHCPv6")) {
|
||||
if (!enableFilter(&filter, title, "Allow inbound DHCPv6", target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -781,7 +800,8 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||
}
|
||||
|
||||
// Allows the internal Hyper-V Switches to work.
|
||||
bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) {
|
||||
bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target) {
|
||||
FWPM_FILTER_CONDITION0 cond;
|
||||
// Condition: Request must be targeting the TUN interface
|
||||
cond.fieldKey = FWPM_CONDITION_L2_FLAGS;
|
||||
@@ -801,12 +821,12 @@ bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) {
|
||||
|
||||
// #1 Permit Hyper-V => Hyper-V outbound.
|
||||
filter.layerKey = FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE;
|
||||
if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V outbound")) {
|
||||
if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V outbound", target)) {
|
||||
return false;
|
||||
}
|
||||
// #2 Permit Hyper-V => Hyper-V inbound.
|
||||
filter.layerKey = FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE;
|
||||
if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V inbound")) {
|
||||
if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V inbound", target)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -814,7 +834,7 @@ bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) {
|
||||
|
||||
bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||
const QString& title,
|
||||
const QString& peer) {
|
||||
QList<uint64_t>& target) {
|
||||
QString description("Block traffic %1 %2 ");
|
||||
|
||||
auto lower = addr.address();
|
||||
@@ -852,12 +872,12 @@ bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||
|
||||
filter.layerKey = layerKeyOut;
|
||||
if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()),
|
||||
peer)) {
|
||||
target)) {
|
||||
return false;
|
||||
}
|
||||
filter.layerKey = layerKeyIn;
|
||||
if (!enableFilter(&filter, title,
|
||||
description.arg("from").arg(addr.toString()), peer)) {
|
||||
description.arg("from").arg(addr.toString()), target)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -865,9 +885,9 @@ bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||
|
||||
bool WindowsFirewall::blockTrafficTo(const QList<IPAddress>& rangeList,
|
||||
uint8_t weight, const QString& title,
|
||||
const QString& peer) {
|
||||
QList<uint64_t>& target) {
|
||||
for (auto range : rangeList) {
|
||||
if (!blockTrafficTo(range, weight, title, peer)) {
|
||||
if (!blockTrafficTo(range, weight, title, target)) {
|
||||
logger.info() << "Setting Range of" << range.toString() << "failed";
|
||||
return false;
|
||||
}
|
||||
@@ -923,7 +943,8 @@ void WindowsFirewall::importAddress(const QHostAddress& addr,
|
||||
}
|
||||
|
||||
bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight,
|
||||
const QString& title) {
|
||||
const QString& title,
|
||||
QList<uint64_t>& target) {
|
||||
// Allow Traffic to IP with PORT using any protocol
|
||||
FWPM_FILTER_CONDITION0 conds[3];
|
||||
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||
@@ -953,20 +974,20 @@ bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight,
|
||||
|
||||
QString description("Block %1 on Port %2");
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
if (!enableFilter(&filter, title, description.arg("outgoing v6").arg(port))) {
|
||||
if (!enableFilter(&filter, title, description.arg("outgoing v6").arg(port), target)) {
|
||||
return false;
|
||||
}
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
if (!enableFilter(&filter, title, description.arg("outgoing v4").arg(port))) {
|
||||
if (!enableFilter(&filter, title, description.arg("outgoing v4").arg(port), target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||
if (!enableFilter(&filter, title, description.arg("incoming v4").arg(port))) {
|
||||
if (!enableFilter(&filter, title, description.arg("incoming v4").arg(port), target)) {
|
||||
return false;
|
||||
}
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||
if (!enableFilter(&filter, title, description.arg("incoming v6").arg(port))) {
|
||||
if (!enableFilter(&filter, title, description.arg("incoming v6").arg(port), target)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -974,7 +995,7 @@ bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight,
|
||||
|
||||
bool WindowsFirewall::enableFilter(FWPM_FILTER0* filter, const QString& title,
|
||||
const QString& description,
|
||||
const QString& peer) {
|
||||
QList<uint64_t>& target) {
|
||||
uint64_t filterID = 0;
|
||||
auto name = title.toStdWString();
|
||||
auto desc = description.toStdWString();
|
||||
@@ -987,16 +1008,12 @@ bool WindowsFirewall::enableFilter(FWPM_FILTER0* filter, const QString& title,
|
||||
return false;
|
||||
}
|
||||
logger.info() << "Filter added: " << title << ":" << description;
|
||||
if (peer.isEmpty()) {
|
||||
m_activeRules.append(filterID);
|
||||
} else {
|
||||
m_peerRules.insert(peer, filterID);
|
||||
}
|
||||
target.append(filterID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight,
|
||||
const QString& title) {
|
||||
bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target) {
|
||||
QList<QNetworkInterface> networkInterfaces =
|
||||
QNetworkInterface::allInterfaces();
|
||||
for (const auto& iface : networkInterfaces) {
|
||||
@@ -1004,7 +1021,7 @@ bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight,
|
||||
continue;
|
||||
}
|
||||
if (!allowTrafficOfAdapter(iface.index(), weight,
|
||||
title.arg(iface.name()))) {
|
||||
title.arg(iface.name()), target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHostAddress>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
@@ -38,38 +39,42 @@ class WindowsFirewall final : public QObject {
|
||||
static WindowsFirewall* create(QObject* parent);
|
||||
~WindowsFirewall() override;
|
||||
|
||||
bool enableInterface(int vpnAdapterIndex);
|
||||
bool enableInterface(int vpnAdapterIndex, const QString& ifname = QString());
|
||||
bool enableLanBypass(const QList<IPAddress>& ranges);
|
||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||
bool disablePeerTraffic(const QString& pubkey);
|
||||
bool disableKillSwitch();
|
||||
bool disableKillSwitchForTunnel(const QString& ifname);
|
||||
bool allowAllTraffic();
|
||||
bool allowTrafficRange(const QStringList& ranges);
|
||||
bool allowTrafficRange(const QStringList& ranges, const QString& ifname = QString());
|
||||
|
||||
private:
|
||||
static bool initSublayer();
|
||||
WindowsFirewall(HANDLE session, QObject* parent);
|
||||
HANDLE m_sessionHandle;
|
||||
bool m_init = false;
|
||||
QList<uint64_t> m_activeRules;
|
||||
QMultiMap<QString, uint64_t> m_peerRules;
|
||||
QList<uint64_t> m_globalRules;
|
||||
QMap<QString, QList<uint64_t>> m_tunnelRules;
|
||||
|
||||
bool allowTrafficForAppOnAll(const QString& exePath, int weight,
|
||||
const QString& title);
|
||||
const QString& title, QList<uint64_t>& target);
|
||||
bool blockTrafficTo(const QList<IPAddress>& range, uint8_t weight,
|
||||
const QString& title, const QString& peer = QString());
|
||||
const QString& title, QList<uint64_t>& target);
|
||||
bool blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||
const QString& title, const QString& peer = QString());
|
||||
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
||||
const QString& title, QList<uint64_t>& target);
|
||||
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target);
|
||||
bool allowTrafficTo(const IPAddress& addr, int weight, const QString& title,
|
||||
const QString& peer = QString());
|
||||
QList<uint64_t>& target);
|
||||
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
|
||||
const QString& title, const QString& peer = QString());
|
||||
const QString& title, QList<uint64_t>& target);
|
||||
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||
const QString& title);
|
||||
bool allowDHCPTraffic(uint8_t weight, const QString& title);
|
||||
bool allowHyperVTraffic(uint8_t weight, const QString& title);
|
||||
bool allowLoopbackTraffic(uint8_t weight, const QString& title);
|
||||
const QString& title, QList<uint64_t>& target);
|
||||
bool allowDHCPTraffic(uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target);
|
||||
bool allowHyperVTraffic(uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target);
|
||||
bool allowLoopbackTraffic(uint8_t weight, const QString& title,
|
||||
QList<uint64_t>& target);
|
||||
|
||||
// Utils
|
||||
QString getCurrentPath();
|
||||
@@ -78,8 +83,7 @@ class WindowsFirewall final : public QObject {
|
||||
void importAddress(const QHostAddress& addr, OUT FWP_CONDITION_VALUE0_& value,
|
||||
OUT QByteArray* v6DataBuffer);
|
||||
bool enableFilter(FWPM_FILTER0* filter, const QString& title,
|
||||
const QString& description,
|
||||
const QString& peer = QString());
|
||||
const QString& description, QList<uint64_t>& target);
|
||||
};
|
||||
|
||||
#endif // WINDOWSFIREWALL_H
|
||||
|
||||
+34
-18
@@ -166,20 +166,37 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
NetworkUtilities::getIPAddress(vpnConfiguration.value(configKey::hostName).toString());
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
// Seamless WG -> WG switch path: already connected via Tunnel, new container is also WG.
|
||||
if (m_active
|
||||
&& m_connectionState == Vpn::ConnectionState::Connected
|
||||
&& VpnProtocol::isWireGuardBased(container)) {
|
||||
if (!m_trafficGuard->allowEndpoint(resolvedRemote)) {
|
||||
const bool isWg = VpnProtocol::isWireGuardBased(container);
|
||||
const QString preAllocatedIfname = isWg ? allocateIfname() : QString();
|
||||
|
||||
bool seamlessSwitch = m_active
|
||||
&& m_connectionState == Vpn::ConnectionState::Connected
|
||||
&& isWg;
|
||||
#ifdef Q_OS_WIN
|
||||
if (seamlessSwitch) {
|
||||
auto clientIpFor = [](const QJsonObject& cfg) {
|
||||
const QString proto = cfg.value("protocol").toString();
|
||||
return cfg.value(proto + "_config_data").toObject().value(configKey::clientIp).toString();
|
||||
};
|
||||
if (clientIpFor(vpnConfiguration) == clientIpFor(m_active->config())) {
|
||||
seamlessSwitch = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (seamlessSwitch) {
|
||||
if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) {
|
||||
releaseIfname(preAllocatedIfname);
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed);
|
||||
return;
|
||||
}
|
||||
startTunnelSwitch(container, vpnConfiguration, resolvedRemote);
|
||||
startTunnelSwitch(container, vpnConfiguration, resolvedRemote, preAllocatedIfname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_trafficGuard->allowEndpoint(resolvedRemote)) {
|
||||
if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) {
|
||||
if (isWg) releaseIfname(preAllocatedIfname);
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed);
|
||||
return;
|
||||
@@ -212,10 +229,9 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
|
||||
m_remoteAddress = resolvedRemote;
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (VpnProtocol::isWireGuardBased(container)) {
|
||||
const QString ifname = allocateIfname();
|
||||
config.insert("ifname", ifname);
|
||||
m_active = new Tunnel(ifname, container, config, resolvedRemote, this);
|
||||
if (isWg) {
|
||||
config.insert("ifname", preAllocatedIfname);
|
||||
m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this);
|
||||
wireTunnelSignals(m_active, /*isActive=*/true);
|
||||
wireDaemonReconnectSignals();
|
||||
m_trafficGuard->setConfig(config);
|
||||
@@ -255,7 +271,10 @@ void VpnConnection::createProtocolConnections()
|
||||
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||
connect(m_vpnProtocol.data(), &VpnProtocol::connectionStateChanged, this, &VpnConnection::setConnectionState);
|
||||
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
|
||||
connect(m_vpnProtocol.data(), &VpnProtocol::tunnelAddressesUpdated, m_trafficGuard.data(), &VpnTrafficGuard::applyFirewall);
|
||||
connect(m_vpnProtocol.data(), &VpnProtocol::tunnelAddressesUpdated, this,
|
||||
[this](const QString& gateway, const QString& localAddress) {
|
||||
m_trafficGuard->applyKillSwitch(nullptr, gateway, localAddress);
|
||||
});
|
||||
|
||||
wireDaemonReconnectSignals();
|
||||
}
|
||||
@@ -532,13 +551,14 @@ void VpnConnection::setConnectionState(Vpn::ConnectionState state) {
|
||||
|
||||
void VpnConnection::startTunnelSwitch(DockerContainer container,
|
||||
const QJsonObject &vpnConfiguration,
|
||||
const QString &resolvedRemote)
|
||||
const QString &resolvedRemote,
|
||||
const QString &stagingIfname)
|
||||
{
|
||||
QJsonObject config = vpnConfiguration;
|
||||
config.insert("ifname", stagingIfname);
|
||||
appendKillSwitchConfig(config);
|
||||
appendSplitTunnelingConfig(config);
|
||||
|
||||
const QString stagingIfname = allocateIfname();
|
||||
m_staging = new Tunnel(stagingIfname, container, config, resolvedRemote, this);
|
||||
wireTunnelSignals(m_staging, /*isActive=*/false);
|
||||
|
||||
@@ -576,10 +596,6 @@ void VpnConnection::onTunnelActivated()
|
||||
|
||||
if (tunnel == m_active) {
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
if (auto proto = m_active->protocol()) {
|
||||
m_trafficGuard->applyFirewall(proto->vpnGateway(),
|
||||
proto->vpnLocalAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,8 @@ private:
|
||||
|
||||
void startTunnelSwitch(DockerContainer container,
|
||||
const QJsonObject &vpnConfiguration,
|
||||
const QString &resolvedRemote);
|
||||
const QString &resolvedRemote,
|
||||
const QString &stagingIfname);
|
||||
|
||||
private slots:
|
||||
void onTunnelPrepared();
|
||||
|
||||
@@ -35,9 +35,10 @@ class IpcInterface
|
||||
SLOT( bool StopRoutingIpv6() );
|
||||
|
||||
SLOT( bool disableKillSwitch() );
|
||||
SLOT( bool disableKillSwitchForTunnel( const QString &ifname, const QStringList &remainingRanges ) );
|
||||
SLOT( bool disableAllTraffic() );
|
||||
SLOT( bool refreshKillSwitch( bool enabled ) );
|
||||
SLOT( bool addKillSwitchAllowedRange( const QStringList ranges ) );
|
||||
SLOT( bool addKillSwitchAllowedRange( const QString &ifname, const QStringList ranges ) );
|
||||
SLOT( bool resetKillSwitchAllowedRange( const QStringList ranges ) );
|
||||
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
|
||||
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
|
||||
|
||||
+12
-3
@@ -282,13 +282,13 @@ bool IpcServer::resetKillSwitchAllowedRange(QStringList ranges)
|
||||
return KillSwitch::instance()->resetAllowedRange(ranges);
|
||||
}
|
||||
|
||||
bool IpcServer::addKillSwitchAllowedRange(QStringList ranges)
|
||||
bool IpcServer::addKillSwitchAllowedRange(const QString &ifname, QStringList ranges)
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug() << "IpcServer::addKillSwitchAllowedRange";
|
||||
qDebug() << "IpcServer::addKillSwitchAllowedRange" << ifname;
|
||||
#endif
|
||||
|
||||
return KillSwitch::instance()->addAllowedRange(ranges);
|
||||
return KillSwitch::instance()->addAllowedRange(ifname, ranges);
|
||||
}
|
||||
|
||||
bool IpcServer::disableAllTraffic()
|
||||
@@ -318,6 +318,15 @@ bool IpcServer::disableKillSwitch()
|
||||
return KillSwitch::instance()->disableKillSwitch();
|
||||
}
|
||||
|
||||
bool IpcServer::disableKillSwitchForTunnel(const QString &ifname, const QStringList &remainingRanges)
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug() << "IpcServer::disableKillSwitchForTunnel" << ifname;
|
||||
#endif
|
||||
|
||||
return KillSwitch::instance()->disableKillSwitchForTunnel(ifname, remainingRanges);
|
||||
}
|
||||
|
||||
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
|
||||
+2
-1
@@ -41,11 +41,12 @@ public:
|
||||
virtual bool StartRoutingIpv6() override;
|
||||
virtual bool StopRoutingIpv6() override;
|
||||
virtual bool disableAllTraffic() override;
|
||||
virtual bool addKillSwitchAllowedRange(QStringList ranges) override;
|
||||
virtual bool addKillSwitchAllowedRange(const QString &ifname, QStringList ranges) override;
|
||||
virtual bool resetKillSwitchAllowedRange(QStringList ranges) override;
|
||||
virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
|
||||
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
|
||||
virtual bool disableKillSwitch() override;
|
||||
virtual bool disableKillSwitchForTunnel(const QString &ifname, const QStringList &remainingRanges) override;
|
||||
virtual bool refreshKillSwitch( bool enabled ) override;
|
||||
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
||||
virtual bool restoreResolvers() override;
|
||||
|
||||
@@ -159,6 +159,16 @@ bool KillSwitch::disableKillSwitch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KillSwitch::disableKillSwitchForTunnel(const QString& ifname, const QStringList& remainingRanges) {
|
||||
#ifdef Q_OS_WIN
|
||||
Q_UNUSED(remainingRanges)
|
||||
return WindowsFirewall::create(this)->disableKillSwitchForTunnel(ifname);
|
||||
#else
|
||||
Q_UNUSED(ifname)
|
||||
return resetAllowedRange(remainingRanges);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KillSwitch::disableAllTraffic() {
|
||||
#ifdef Q_OS_WIN
|
||||
WindowsFirewall::create(this)->enableInterface(-1);
|
||||
@@ -221,7 +231,15 @@ bool KillSwitch::resetAllowedRange(const QStringList &ranges) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KillSwitch::addAllowedRange(const QStringList &ranges) {
|
||||
bool KillSwitch::addAllowedRange(const QString &ifname, const QStringList &ranges) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (!ifname.isEmpty()) {
|
||||
return WindowsFirewall::create(this)->allowTrafficRange(ranges, ifname);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(ifname)
|
||||
#endif
|
||||
|
||||
for (const QString &range : ranges) {
|
||||
if (!range.isEmpty() && !m_allowedRanges.contains(range)) {
|
||||
m_allowedRanges.append(range);
|
||||
@@ -242,7 +260,11 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||
config.m_secondaryDnsServer = configStr.value(amnezia::configKey::dns2).toString();
|
||||
}
|
||||
|
||||
config.m_serverPublicKey = "openvpn";
|
||||
config.m_ifname = configStr.value("ifname").toString();
|
||||
const QString protocolName = configStr.value(amnezia::configKey::vpnProto).toString();
|
||||
const QString pubkey = configStr.value(protocolName + "_config_data").toObject()
|
||||
.value(amnezia::configKey::serverPubKey).toString();
|
||||
config.m_serverPublicKey = pubkey.isEmpty() ? QStringLiteral("openvpn") : pubkey;
|
||||
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
|
||||
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
|
||||
int vpnAdapterIndex = resolveVpnAdapterIndex(configStr);
|
||||
@@ -306,10 +328,11 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
||||
#ifdef Q_OS_WIN
|
||||
Q_UNUSED(vpnAdapterIndex)
|
||||
const int resolvedIndex = resolveVpnAdapterIndex(configStr);
|
||||
const QString ifname = configStr.value("ifname").toString();
|
||||
if (configStr.value("splitTunnelType").toInt() != 0) {
|
||||
WindowsFirewall::create(this)->allowAllTraffic();
|
||||
}
|
||||
return WindowsFirewall::create(this)->enableInterface(resolvedIndex);
|
||||
return WindowsFirewall::create(this)->enableInterface(resolvedIndex, ifname);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
|
||||
@@ -14,11 +14,12 @@ public:
|
||||
bool init();
|
||||
bool refresh(bool enabled);
|
||||
bool disableKillSwitch();
|
||||
bool disableKillSwitchForTunnel(const QString& ifname, const QStringList& remainingRanges);
|
||||
bool disableAllTraffic();
|
||||
bool enablePeerTraffic(const QJsonObject &configStr);
|
||||
bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex);
|
||||
bool resetAllowedRange(const QStringList &ranges);
|
||||
bool addAllowedRange(const QStringList &ranges);
|
||||
bool addAllowedRange(const QString &ifname, const QStringList &ranges);
|
||||
bool isStrictKillSwitchEnabled();
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user