added killSwitch switcher (#746)

* added killSwitch switcher
* KillSwitch toggle for OpenVPN and XRay
* killSwitch toggle for AWG/WG protocol
* Some fixes for killSwitch
This commit is contained in:
Nethius 2024-04-25 20:01:00 +07:00 committed by GitHub
parent 477d7214c5
commit 87b738ef16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 144 additions and 58 deletions

View file

@ -116,8 +116,8 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
if (!isApiConfig) {
QRegularExpression regex("redirect-gateway.*");
config.replace(regex, "");
if (!m_settings->getSitesSplitTunnelingEnabled()) {
if (!m_settings->isSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");

View file

@ -374,6 +374,8 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
return false;
}
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
if (!obj.value("Jc").isNull()) {
config.m_junkPacketCount = obj.value("Jc").toString();
}

View file

@ -37,6 +37,7 @@ class InterfaceConfig {
QList<IPAddress> m_allowedIPAddressRanges;
QStringList m_excludedAddresses;
QStringList m_vpnDisabledApps;
bool m_killSwitchEnabled;
#if defined(MZ_ANDROID) || defined(MZ_IOS)
QString m_installationId;
#endif

View file

@ -221,7 +221,9 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert("excludedAddresses", jsExcludedAddresses);
json.insert("vpnDisabledApps", splitTunnelApps);
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
if (protocolName == amnezia::config_key::awg) {
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));

View file

@ -136,24 +136,25 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
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());
if (config.m_killSwitchEnabled) {
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());
}
}
} else {
params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) {
params.blockAddrs.append(net.toString());
}
applyFirewallRules(params);
}
applyFirewallRules(params);
}
return (err == 0);

View file

@ -134,26 +134,26 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
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()) {
if (config.m_killSwitchEnabled) {
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());
params.allowAddrs.append(net.toUtf8());
}
}
} else {
params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) {
}
} else {
params.blockNets = true;
foreach (auto net, config.m_allowedIPAddressRanges) {
params.blockAddrs.append(net.toString());
}
}
applyFirewallRules(params);
}
applyFirewallRules(params);
}
return (err == 0);
}

View file

@ -116,10 +116,12 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
m_luid = luid.Value;
m_routeMonitor.setLuid(luid.Value);
// Enable the windows firewall
NET_IFINDEX ifindex;
ConvertInterfaceLuidToIndex(&luid, &ifindex);
WindowsFirewall::instance()->enableKillSwitch(ifindex);
if (config.m_killSwitchEnabled) {
// Enable the windows firewall
NET_IFINDEX ifindex;
ConvertInterfaceLuidToIndex(&luid, &ifindex);
WindowsFirewall::instance()->enableKillSwitch(ifindex);
}
logger.debug() << "Registration completed";
return true;
@ -137,9 +139,10 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
QByteArray pskKey =
QByteArray::fromBase64(qPrintable(config.m_serverPskKey));
// Enable the windows firewall for this peer.
WindowsFirewall::instance()->enablePeerTraffic(config);
if (config.m_killSwitchEnabled) {
// Enable the windows firewall for this peer.
WindowsFirewall::instance()->enablePeerTraffic(config);
}
logger.debug() << "Configuring peer" << publicKey.toHex()
<< "via" << config.m_serverIpv4AddrIn;

View file

@ -336,8 +336,11 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
for (int i = 0; i < netInterfaces.size(); i++) {
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
{
// killSwitch toggle
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
}
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway);
m_configData.insert("vpnServer", m_configData.value(amnezia::config_key::hostName).toString());
@ -347,7 +350,10 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
}
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
// killSwitch toggle
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
}
#endif
qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway());
}

View file

@ -92,6 +92,8 @@ namespace amnezia
constexpr char splitTunnelApps[] = "splitTunnelApps";
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
constexpr char killSwitchOption[] = "killSwitchOption";
constexpr char crc[] = "crc";
constexpr char clientId[] = "clientId";

View file

@ -142,7 +142,6 @@ ErrorCode XrayProtocol::startTun2Sock()
QThread::msleep(5000);
IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
#endif
#ifdef Q_OS_WINDOWS
QThread::msleep(15000);
@ -151,7 +150,12 @@ ErrorCode XrayProtocol::startTun2Sock()
QThread::msleep(1000);
IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// killSwitch toggle
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
}
#endif
if (m_routeMode == 0) {
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
@ -165,8 +169,11 @@ ErrorCode XrayProtocol::startTun2Sock()
for (int i = 0; i < netInterfaces.size(); i++) {
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
{
// killSwitch toggle
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
}
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway);
m_configData.insert("vpnServer", m_remoteAddress);
@ -200,6 +207,7 @@ void XrayProtocol::stop()
{
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::Interface()->disableKillSwitch();
IpcClient::Interface()->StartRoutingIpv6();
#endif
qDebug() << "XrayProtocol::stop()";
m_xrayProcess.terminate();

View file

@ -256,7 +256,7 @@ Settings::RouteMode Settings::routeMode() const
return static_cast<RouteMode>(value("Conf/routeMode", 0).toInt());
}
bool Settings::getSitesSplitTunnelingEnabled() const
bool Settings::isSitesSplitTunnelingEnabled() const
{
return value("Conf/sitesSplitTunnelingEnabled", false).toBool();
}
@ -415,7 +415,7 @@ void Settings::setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &a
m_settings.sync();
}
bool Settings::getAppsSplitTunnelingEnabled() const
bool Settings::isAppsSplitTunnelingEnabled() const
{
return value("Conf/appsSplitTunnelingEnabled", false).toBool();
}
@ -425,6 +425,16 @@ void Settings::setAppsSplitTunnelingEnabled(bool enabled)
setValue("Conf/appsSplitTunnelingEnabled", enabled);
}
bool Settings::isKillSwitchEnabled() const
{
return value("Conf/killSwitchEnabled", true).toBool();
}
void Settings::setKillSwitchEnabled(bool enabled)
{
setValue("Conf/killSwitchEnabled", enabled);
}
QString Settings::getInstallationUuid(const bool needCreate)
{
auto uuid = value("Conf/installationUuid", "").toString();

View file

@ -115,7 +115,7 @@ public:
RouteMode routeMode() const;
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
bool getSitesSplitTunnelingEnabled() const;
bool isSitesSplitTunnelingEnabled() const;
void setSitesSplitTunnelingEnabled(bool enabled);
QVariantMap vpnSites(RouteMode mode) const
@ -211,9 +211,11 @@ public:
QVector<InstalledAppInfo> getVpnApps(AppsRouteMode mode) const;
void setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &apps);
bool getAppsSplitTunnelingEnabled() const;
bool isAppsSplitTunnelingEnabled() const;
void setAppsSplitTunnelingEnabled(bool enabled);
bool isKillSwitchEnabled() const;
void setKillSwitchEnabled(bool enabled);
QString getInstallationUuid(const bool needCreate);
signals:

View file

@ -223,3 +223,13 @@ void SettingsController::checkIfNeedDisableLogs()
}
}
}
bool SettingsController::isKillSwitchEnabled()
{
return m_settings->isKillSwitchEnabled();
}
void SettingsController::toggleKillSwitch(bool enable)
{
m_settings->setKillSwitchEnabled(enable);
}

View file

@ -63,6 +63,9 @@ public slots:
bool isCameraPresent();
bool isKillSwitchEnabled();
void toggleKillSwitch(bool enable);
signals:
void primaryDnsChanged();
void secondaryDnsChanged();

View file

@ -5,7 +5,7 @@
AppSplitTunnelingModel::AppSplitTunnelingModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings)
{
m_isSplitTunnelingEnabled = m_settings->getAppsSplitTunnelingEnabled();
m_isSplitTunnelingEnabled = m_settings->isAppsSplitTunnelingEnabled();
m_currentRouteMode = m_settings->getAppsRouteMode();
if (m_currentRouteMode == Settings::VpnAllApps) { // for old split tunneling configs
m_settings->setAppsRouteMode(static_cast<Settings::AppsRouteMode>(Settings::VpnAllExceptApps));

View file

@ -3,7 +3,7 @@
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings)
{
m_isSplitTunnelingEnabled = m_settings->getSitesSplitTunnelingEnabled();
m_isSplitTunnelingEnabled = m_settings->isSitesSplitTunnelingEnabled();
m_currentRouteMode = m_settings->routeMode();
if (m_currentRouteMode == Settings::VpnAllSites) { // for old split tunneling configs
m_settings->setRouteMode(static_cast<Settings::RouteMode>(Settings::VpnOnlyForwardSites));

View file

@ -149,10 +149,9 @@ PageType {
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot restore backup settings during active connection"))
} else
{
} else {
PageController.showBusyIndicator(true)
SettingsController.restoreAppConfig(filePath)
PageController.showBusyIndicator(false)

View file

@ -130,6 +130,31 @@ PageType {
DividerType {
visible: root.isAppSplitTinnelingEnabled
}
SwitcherType {
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("KillSwitch")
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.")
checked: SettingsController.isKillSwitchEnabled()
checkable: !ConnectionController.isConnected
onCheckedChanged: {
if (checked !== SettingsController.isKillSwitchEnabled()) {
SettingsController.toggleKillSwitch(checked)
}
}
onClicked: {
if (!checkable) {
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
}
}
}
DividerType {
visible: GC.isDesktop()
}
}
}
}

View file

@ -70,7 +70,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
if (m_settings->getSitesSplitTunnelingEnabled()) {
if (m_settings->isSitesSplitTunnelingEnabled()) {
IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
@ -89,7 +89,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
} else if (state == Vpn::ConnectionState::Error) {
IpcClient::Interface()->flushDns();
if (m_settings->getSitesSplitTunnelingEnabled()) {
if (m_settings->isSitesSplitTunnelingEnabled()) {
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
IpcClient::Interface()->clearSavedRoutes();
}
@ -237,15 +237,17 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
m_remoteAddress = credentials.hostName;
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
m_vpnConfiguration = vpnConfiguration;
#ifdef AMNEZIA_DESKTOP
if (m_vpnProtocol) {
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
m_vpnProtocol->stop();
m_vpnProtocol.reset();
}
appendKillSwitchConfig();
#endif
m_vpnConfiguration = vpnConfiguration;
appendSplitTunnelingConfig();
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
@ -282,6 +284,11 @@ void VpnConnection::createProtocolConnections()
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
}
void VpnConnection::appendKillSwitchConfig()
{
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString());
}
void VpnConnection::appendSplitTunnelingConfig()
{
if (m_vpnConfiguration.value(config_key::configVersion).toInt()) {
@ -305,7 +312,7 @@ void VpnConnection::appendSplitTunnelingConfig()
Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites;
QJsonArray sitesJsonArray;
if (m_settings->getSitesSplitTunnelingEnabled()) {
if (m_settings->isSitesSplitTunnelingEnabled()) {
routeMode = m_settings->routeMode();
auto sites = m_settings->getVpnIps(routeMode);
@ -325,7 +332,7 @@ void VpnConnection::appendSplitTunnelingConfig()
Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
QJsonArray appsJsonArray;
if (m_settings->getAppsSplitTunnelingEnabled()) {
if (m_settings->isAppsSplitTunnelingEnabled()) {
appsRouteMode = m_settings->getAppsRouteMode();
auto apps = m_settings->getVpnApps(appsRouteMode);

View file

@ -94,6 +94,7 @@ private:
void createProtocolConnections();
void appendSplitTunnelingConfig();
void appendKillSwitchConfig();
};
#endif // VPNCONNECTION_H

View file

@ -360,7 +360,11 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
config.m_vpnDisabledApps.append(i.toString());
}
WindowsFirewall::instance()->enablePeerTraffic(config);
// killSwitch toggle
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
WindowsFirewall::instance()->enablePeerTraffic(config);
}
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
return true;