Merge branch 'feature/killswitch-strict-mode' into feature/allowed-dns-list

This commit is contained in:
aiamnezia 2025-03-08 17:58:29 +04:00
commit bb883b4880
73 changed files with 886 additions and 428 deletions

View file

@ -213,6 +213,7 @@ void CoreController::initSignalHandlers()
initAutoConnectHandler(); initAutoConnectHandler();
initAmneziaDnsToggledHandler(); initAmneziaDnsToggledHandler();
initPrepareConfigHandler(); initPrepareConfigHandler();
initStrictKillSwitchHandler();
} }
void CoreController::initNotificationHandler() void CoreController::initNotificationHandler()
@ -339,6 +340,12 @@ void CoreController::initPrepareConfigHandler()
}); });
} }
void CoreController::initStrictKillSwitchHandler()
{
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged,
m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged);
}
QSharedPointer<PageController> CoreController::pageController() const QSharedPointer<PageController> CoreController::pageController() const
{ {
return m_pageController; return m_pageController;

View file

@ -80,6 +80,7 @@ private:
void initAutoConnectHandler(); void initAutoConnectHandler();
void initAmneziaDnsToggledHandler(); void initAmneziaDnsToggledHandler();
void initPrepareConfigHandler(); void initPrepareConfigHandler();
void initStrictKillSwitchHandler();
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;

View file

@ -17,6 +17,8 @@
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "killswitch.h"
constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
@ -182,7 +184,7 @@ bool WireguardUtilsLinux::deleteInterface() {
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
// double-check + ensure our firewall is installed and enabled // double-check + ensure our firewall is installed and enabled
LinuxFirewall::uninstall(); KillSwitch::instance()->disableKillSwitch();
return true; return true;
} }

View file

@ -16,6 +16,8 @@
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "killswitch.h"
constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
@ -180,7 +182,7 @@ bool WireguardUtilsMacos::deleteInterface() {
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
// double-check + ensure our firewall is installed and enabled // double-check + ensure our firewall is installed and enabled
MacOSFirewall::uninstall(); KillSwitch::instance()->disableKillSwitch();
return true; return true;
} }

View file

@ -29,6 +29,8 @@
#include "logger.h" #include "logger.h"
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
#include "killswitch.h"
#define IPV6_ADDRESS_SIZE 16 #define IPV6_ADDRESS_SIZE 16
// ID for the Firewall Sublayer // ID for the Firewall Sublayer
@ -180,11 +182,24 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
} \ } \
} }
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex; logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
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")) {
return false;
}
} else
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT, FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
"Allow usage of VPN Adapter")); "Allow usage of VPN Adapter"));
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic")); FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic")); FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
"Allow all for AmneziaVPN.exe")); "Allow all for AmneziaVPN.exe"));
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
@ -321,37 +336,41 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
} }
bool WindowsFirewall::disableKillSwitch() { bool WindowsFirewall::disableKillSwitch() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL); return KillSwitch::instance()->disableKillSwitch();
auto cleanup = qScopeGuard([&] { }
bool WindowsFirewall::allowAllTraffic() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
auto cleanup = qScopeGuard([&] {
if (result != ERROR_SUCCESS) {
FwpmTransactionAbort0(m_sessionHandle);
}
});
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
FwpmTransactionAbort0(m_sessionHandle); logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
<< result;
return false;
} }
});
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
<< result;
return false;
}
for (const auto& filterID : m_peerRules.values()) { for (const auto& filterID : m_peerRules.values()) {
FwpmFilterDeleteById0(m_sessionHandle, filterID); FwpmFilterDeleteById0(m_sessionHandle, filterID);
} }
for (const auto& filterID : qAsConst(m_activeRules)) { for (const auto& filterID : qAsConst(m_activeRules)) {
FwpmFilterDeleteById0(m_sessionHandle, filterID); FwpmFilterDeleteById0(m_sessionHandle, filterID);
} }
// Commit! // Commit!
result = FwpmTransactionCommit0(m_sessionHandle); result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
<< result; << result;
return false; return false;
} }
m_peerRules.clear(); m_peerRules.clear();
m_activeRules.clear(); m_activeRules.clear();
logger.debug() << "Firewall Disabled!"; logger.debug() << "Firewall Disabled!";
return true; return true;
} }
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,

View file

@ -43,6 +43,7 @@ class WindowsFirewall final : public QObject {
bool enablePeerTraffic(const InterfaceConfig& config); bool enablePeerTraffic(const InterfaceConfig& config);
bool disablePeerTraffic(const QString& pubkey); bool disablePeerTraffic(const QString& pubkey);
bool disableKillSwitch(); bool disableKillSwitch();
bool allowAllTraffic();
private: private:
static bool initSublayer(); static bool initSublayer();

View file

@ -171,6 +171,11 @@ ErrorCode OpenVpnProtocol::start()
return lastError(); return lastError();
} }
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::Interface()->allowTrafficTo(QStringList(NetworkUtilities::getIPAddress(
m_configData.value(amnezia::config_key::hostName).toString())));
#endif
// Detect default gateway // Detect default gateway
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QProcess p; QProcess p;

View file

@ -143,7 +143,9 @@
<file>ui/qml/Controls2/DropDownType.qml</file> <file>ui/qml/Controls2/DropDownType.qml</file>
<file>ui/qml/Controls2/FlickableType.qml</file> <file>ui/qml/Controls2/FlickableType.qml</file>
<file>ui/qml/Controls2/Header2Type.qml</file> <file>ui/qml/Controls2/Header2Type.qml</file>
<file>ui/qml/Controls2/HeaderType.qml</file> <file>ui/qml/Controls2/BaseHeaderType.qml</file>
<file>ui/qml/Controls2/HeaderTypeWithButton.qml</file>
<file>ui/qml/Controls2/HeaderTypeWithSwitcher.qml</file>
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file> <file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
<file>ui/qml/Controls2/ImageButtonType.qml</file> <file>ui/qml/Controls2/ImageButtonType.qml</file>
<file>ui/qml/Controls2/LabelWithButtonType.qml</file> <file>ui/qml/Controls2/LabelWithButtonType.qml</file>
@ -199,6 +201,7 @@
<file>ui/qml/Pages2/PageSettingsBackup.qml</file> <file>ui/qml/Pages2/PageSettingsBackup.qml</file>
<file>ui/qml/Pages2/PageSettingsConnection.qml</file> <file>ui/qml/Pages2/PageSettingsConnection.qml</file>
<file>ui/qml/Pages2/PageSettingsDns.qml</file> <file>ui/qml/Pages2/PageSettingsDns.qml</file>
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file> <file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageSettingsServerData.qml</file> <file>ui/qml/Pages2/PageSettingsServerData.qml</file>
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file> <file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>

View file

@ -1,7 +1,7 @@
#include "secure_qsettings.h" #include "secure_qsettings.h"
#include "QAead.h" #include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
#include "QBlockCipher.h" #include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
#include "utilities.h" #include "utilities.h"
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>

View file

@ -6,7 +6,7 @@
#include <QObject> #include <QObject>
#include <QSettings> #include <QSettings>
#include "keychain.h" #include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
class SecureQSettings : public QObject class SecureQSettings : public QObject
{ {

View file

@ -443,6 +443,16 @@ void Settings::setKillSwitchEnabled(bool enabled)
setValue("Conf/killSwitchEnabled", enabled); setValue("Conf/killSwitchEnabled", enabled);
} }
bool Settings::isStrictKillSwitchEnabled() const
{
return value("Conf/strictKillSwitchEnabled", false).toBool();
}
void Settings::setStrictKillSwitchEnabled(bool enabled)
{
setValue("Conf/strictKillSwitchEnabled", enabled);
}
QString Settings::getInstallationUuid(const bool needCreate) QString Settings::getInstallationUuid(const bool needCreate)
{ {
auto uuid = value("Conf/installationUuid", "").toString(); auto uuid = value("Conf/installationUuid", "").toString();

View file

@ -213,6 +213,10 @@ public:
bool isKillSwitchEnabled() const; bool isKillSwitchEnabled() const;
void setKillSwitchEnabled(bool enabled); void setKillSwitchEnabled(bool enabled);
bool isStrictKillSwitchEnabled() const;
void setStrictKillSwitchEnabled(bool enabled);
QString getInstallationUuid(const bool needCreate); QString getInstallationUuid(const bool needCreate);
void resetGatewayEndpoint(); void resetGatewayEndpoint();

View file

@ -31,6 +31,7 @@ namespace PageLoader
PageSettingsLogging, PageSettingsLogging,
PageSettingsSplitTunneling, PageSettingsSplitTunneling,
PageSettingsAppSplitTunneling, PageSettingsAppSplitTunneling,
PageSettingsKillSwitch,
PageSettingsApiServerInfo, PageSettingsApiServerInfo,
PageSettingsApiAvailableCountries, PageSettingsApiAvailableCountries,
PageSettingsApiSupport, PageSettingsApiSupport,

View file

@ -245,6 +245,24 @@ bool SettingsController::isKillSwitchEnabled()
void SettingsController::toggleKillSwitch(bool enable) void SettingsController::toggleKillSwitch(bool enable)
{ {
m_settings->setKillSwitchEnabled(enable); m_settings->setKillSwitchEnabled(enable);
emit killSwitchEnabledChanged();
if (enable == false) {
emit strictKillSwitchEnabledChanged(false);
} else {
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
}
}
bool SettingsController::isStrictKillSwitchEnabled()
{
return m_settings->isStrictKillSwitchEnabled();
}
void SettingsController::toggleStrictKillSwitch(bool enable)
{
m_settings->setStrictKillSwitchEnabled(enable);
emit strictKillSwitchEnabledChanged(enable);
} }
bool SettingsController::isNotificationPermissionGranted() bool SettingsController::isNotificationPermissionGranted()

View file

@ -24,6 +24,8 @@ public:
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged) Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged) Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged) Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged)
Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged)
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled) Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
@ -75,6 +77,9 @@ public slots:
bool isKillSwitchEnabled(); bool isKillSwitchEnabled();
void toggleKillSwitch(bool enable); void toggleKillSwitch(bool enable);
bool isStrictKillSwitchEnabled();
void toggleStrictKillSwitch(bool enable);
bool isNotificationPermissionGranted(); bool isNotificationPermissionGranted();
void requestNotificationPermission(); void requestNotificationPermission();
@ -98,6 +103,8 @@ signals:
void primaryDnsChanged(); void primaryDnsChanged();
void secondaryDnsChanged(); void secondaryDnsChanged();
void loggingStateChanged(); void loggingStateChanged();
void killSwitchEnabledChanged();
void strictKillSwitchEnabledChanged(bool enabled);
void restoreBackupFinished(); void restoreBackupFinished();
void changeSettingsFinished(const QString &finishedMessage); void changeSettingsFinished(const QString &finishedMessage);

View file

@ -0,0 +1,45 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
id: root
property string headerText
property int headerTextMaximumLineCount: 2
property int headerTextElide: Qt.ElideRight
property string descriptionText
property alias headerRow: headerRow
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
RowLayout {
id: headerRow
Header1TextType {
id: header
Layout.fillWidth: true
text: root.headerText
maximumLineCount: root.headerTextMaximumLineCount
elide: root.headerTextElide
}
}
ParagraphTextType {
id: description
Layout.topMargin: 16
Layout.fillWidth: true
text: root.descriptionText
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}
}
}

View file

@ -1,86 +0,0 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
id: root
property string actionButtonImage
property var actionButtonFunction
property alias actionButton: headerActionButton
property string headerText
property int headerTextMaximumLineCount: 2
property int headerTextElide: Qt.ElideRight
property string descriptionText
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
RowLayout {
Header1TextType {
id: header
Layout.fillWidth: true
text: root.headerText
maximumLineCount: root.headerTextMaximumLineCount
elide: root.headerTextElide
}
ImageButtonType {
id: headerActionButton
implicitWidth: 40
implicitHeight: 40
Layout.alignment: Qt.AlignRight
image: root.actionButtonImage
imageColor: AmneziaStyle.color.paleGray
visible: image ? true : false
onClicked: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}
}
ParagraphTextType {
id: description
Layout.topMargin: 16
Layout.fillWidth: true
text: root.descriptionText
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}
}
Keys.onEnterPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
Keys.onReturnPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}

View file

@ -0,0 +1,44 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
BaseHeaderType {
id: root
property string actionButtonImage
property var actionButtonFunction
property alias actionButton: headerActionButton
Component.onCompleted: {
headerRow.children.push(headerActionButton)
}
ImageButtonType {
id: headerActionButton
implicitWidth: 40
implicitHeight: 40
Layout.alignment: Qt.AlignRight
image: root.actionButtonImage
imageColor: AmneziaStyle.color.paleGray
visible: image ? true : false
onClicked: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}
Keys.onEnterPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
Keys.onReturnPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}

View file

@ -0,0 +1,28 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
BaseHeaderType {
id: root
property var switcherFunction
property bool showSwitcher: false
property alias switcher: headerSwitcher
Component.onCompleted: {
headerRow.children.push(headerSwitcher)
}
SwitcherType {
id: headerSwitcher
Layout.alignment: Qt.AlignRight
visible: root.showSwitcher
onToggled: {
if (switcherFunction && typeof switcherFunction === "function") {
switcherFunction(checked)
}
}
}
}

View file

@ -20,7 +20,11 @@ RadioButton {
property string selectedColor: AmneziaStyle.color.transparent property string selectedColor: AmneziaStyle.color.transparent
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string selectedTextColor: AmneziaStyle.color.goldenApricot property string selectedTextColor: AmneziaStyle.color.goldenApricot
property string selectedTextDisabledColor: AmneziaStyle.color.burntOrange
property string descriptionColor: AmneziaStyle.color.mutedGray
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
@ -30,6 +34,12 @@ RadioButton {
property bool isFocusable: true property bool isFocusable: true
property string radioButtonInnerCirclePressedSource: "qrc:/images/controls/radio-button-inner-circle-pressed.png"
property string radioButtonInnerCircleSource: "qrc:/images/controls/radio-button-inner-circle.png"
property string radioButtonPressedSource: "qrc:/images/controls/radio-button-pressed.svg"
property string radioButtonDefaultSource: "qrc:/images/controls/radio-button.svg"
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
@ -94,14 +104,15 @@ RadioButton {
if (showImage) { if (showImage) {
return imageSource return imageSource
} else if (root.pressed) { } else if (root.pressed) {
return "qrc:/images/controls/radio-button-inner-circle-pressed.png" return root.radioButtonInnerCirclePressedSource
} else if (root.checked) { } else if (root.checked) {
return "qrc:/images/controls/radio-button-inner-circle.png" return root.radioButtonInnerCircleSource
} }
return "" return ""
} }
opacity: root.enabled ? 1.0 : 0.3
anchors.centerIn: parent anchors.centerIn: parent
width: 24 width: 24
@ -113,12 +124,13 @@ RadioButton {
if (showImage) { if (showImage) {
return "" return ""
} else if (root.pressed || root.checked) { } else if (root.pressed || root.checked) {
return "qrc:/images/controls/radio-button-pressed.svg" return root.radioButtonPressedSource
} else { } else {
return "qrc:/images/controls/radio-button.svg" return root.radioButtonDefaultSource
} }
} }
opacity: root.enabled ? 1.0 : 0.3
anchors.centerIn: parent anchors.centerIn: parent
width: 24 width: 24
@ -148,10 +160,11 @@ RadioButton {
elide: root.textElide elide: root.textElide
color: { color: {
if (root.checked) { if (root.enabled) {
return selectedTextColor return root.checked ? selectedTextColor : textColor
} else {
return root.checked ? selectedTextDisabledColor : textDisabledColor
} }
return textColor
} }
Layout.fillWidth: true Layout.fillWidth: true
@ -164,7 +177,7 @@ RadioButton {
CaptionTextType { CaptionTextType {
id: description id: description
color: AmneziaStyle.color.mutedGray color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor
text: root.descriptionText text: root.descriptionText
visible: root.descriptionText !== "" visible: root.descriptionText !== ""

View file

@ -56,7 +56,7 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20

View file

@ -39,7 +39,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -91,7 +91,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings") headerText: qsTr("AmneziaWG settings")

View file

@ -91,7 +91,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings") headerText: qsTr("AmneziaWG settings")

View file

@ -76,7 +76,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Cloak settings") headerText: qsTr("Cloak settings")

View file

@ -75,7 +75,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("OpenVPN settings") headerText: qsTr("OpenVPN settings")

View file

@ -32,7 +32,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -78,7 +78,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Shadowsocks settings") headerText: qsTr("Shadowsocks settings")

View file

@ -85,7 +85,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("WG settings") headerText: qsTr("WG settings")

View file

@ -77,7 +77,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("WG settings") headerText: qsTr("WG settings")
} }

View file

@ -75,7 +75,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("XRay settings") headerText: qsTr("XRay settings")
} }

View file

@ -43,7 +43,7 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -85,7 +85,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -77,7 +77,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -217,7 +217,7 @@ PageType {
} }
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("SOCKS5 settings") headerText: qsTr("SOCKS5 settings")

View file

@ -54,7 +54,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -29,7 +29,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24

View file

@ -69,7 +69,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { HeaderTypeWithButton {
id: headerContent id: headerContent
objectName: "headerContent" objectName: "headerContent"

View file

@ -91,7 +91,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -38,7 +38,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -93,7 +93,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { HeaderTypeWithButton {
id: headerContent id: headerContent
objectName: "headerContent" objectName: "headerContent"

View file

@ -71,7 +71,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -79,29 +79,22 @@ PageType {
id: backButton id: backButton
} }
RowLayout { HeaderTypeWithSwitcher {
HeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.leftMargin: 16
Layout.leftMargin: 16 Layout.rightMargin: 16
headerText: qsTr("App split tunneling") headerText: qsTr("App split tunneling")
enabled: root.pageEnabled
showSwitcher: true
switcher {
checked: AppSplitTunnelingModel.isTunnelingEnabled
enabled: root.pageEnabled enabled: root.pageEnabled
} }
switcherFunction: function(checked) {
SwitcherType { AppSplitTunnelingModel.toggleSplitTunneling(checked)
id: switcher selector.text = root.routeModesModel[getRouteModesModelIndex()].name
Layout.fillWidth: true
Layout.rightMargin: 16
enabled: root.pageEnabled
checked: AppSplitTunnelingModel.isTunnelingEnabled
onToggled: {
AppSplitTunnelingModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
} }
} }

View file

@ -38,7 +38,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -60,7 +60,7 @@ PageType {
spacing: 16 spacing: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Back up your configuration") headerText: qsTr("Back up your configuration")

View file

@ -36,7 +36,7 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -94,9 +94,7 @@ PageType {
} }
} }
DividerType { DividerType {}
visible: root.isAppSplitTinnelingEnabled
}
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton2 id: splitTunnelingButton2
@ -119,29 +117,20 @@ PageType {
visible: root.isAppSplitTinnelingEnabled visible: root.isAppSplitTinnelingEnabled
} }
SwitcherType { LabelWithButtonType {
id: killSwitchSwitcher id: killSwitchButton
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16
text: qsTr("KillSwitch") text: qsTr("KillSwitch")
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.") descriptionText: qsTr("Blocks network connections without VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl parentFlickable: fl
checked: SettingsController.isKillSwitchEnabled() clickedFunction: function() {
checkable: !ConnectionController.isConnected PageController.goToPage(PageEnum.PageSettingsKillSwitch)
onCheckedChanged: {
if (checked !== SettingsController.isKillSwitchEnabled()) {
SettingsController.toggleKillSwitch(checked)
}
}
onClicked: {
if (!checkable) {
PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection"))
}
} }
} }

View file

@ -50,7 +50,7 @@ PageType {
spacing: 16 spacing: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("DNS servers") headerText: qsTr("DNS servers")

View file

@ -0,0 +1,98 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
PageType {
id: root
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
HeaderTypeWithSwitcher {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Kill Switch")
descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops")
showSwitcher: true
switcher {
checked: SettingsController.isKillSwitchEnabled
enabled: !ConnectionController.isConnected
}
switcherFunction: function(checked) {
if (!ConnectionController.isConnected) {
SettingsController.isKillSwitchEnabled = checked
} else {
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
switcher.checked = SettingsController.isKillSwitchEnabled
}
}
}
VerticalRadioButton {
id: softKillSwitch
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
checked: !SettingsController.strictKillSwitchEnabled
text: qsTr("Soft Kill Switch")
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally")
onClicked: {
SettingsController.strictKillSwitchEnabled = false
}
}
DividerType {}
VerticalRadioButton {
id: strictKillSwitch
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
checked: SettingsController.strictKillSwitchEnabled
text: qsTr("Strict Kill Switch")
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started")
onClicked: {
SettingsController.strictKillSwitchEnabled = true
}
}
DividerType {}
}
}
}

View file

@ -40,7 +40,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -71,7 +71,7 @@ PageType {
objectName: "backButton" objectName: "backButton"
} }
HeaderType { HeaderTypeWithButton {
id: headerContent id: headerContent
objectName: "headerContent" objectName: "headerContent"

View file

@ -34,7 +34,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -31,7 +31,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -94,33 +94,22 @@ PageType {
id: backButton id: backButton
} }
RowLayout { HeaderTypeWithSwitcher {
HeaderType { Layout.fillWidth: true
enabled: root.pageEnabled Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true headerText: qsTr("Split tunneling")
Layout.leftMargin: 16
headerText: qsTr("Split tunneling")
}
SwitcherType {
id: switcher
enabled: root.pageEnabled
Layout.fillWidth: true
Layout.rightMargin: 16
function onToggledFunc() {
SitesModel.toggleSplitTunneling(this.checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
enabled: root.pageEnabled
showSwitcher: true
switcher {
checked: SitesModel.isTunnelingEnabled checked: SitesModel.isTunnelingEnabled
onToggled: { onToggledFunc() } enabled: root.pageEnabled
Keys.onEnterPressed: { onToggledFunc() } }
Keys.onReturnPressed: { onToggledFunc() } switcherFunction: function(checked) {
SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
} }
} }

View file

@ -35,7 +35,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -28,7 +28,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -43,7 +43,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { HeaderTypeWithButton {
id: moreButton id: moreButton
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
@ -74,7 +74,7 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.leftMargin: 16

View file

@ -66,7 +66,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -59,7 +59,7 @@ PageType {
spacing: 16 spacing: 16
HeaderType { BaseHeaderType {
id: header id: header
implicitWidth: parent.width implicitWidth: parent.width

View file

@ -118,7 +118,7 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20

View file

@ -96,7 +96,7 @@ PageType {
Layout.leftMargin: -16 Layout.leftMargin: -16
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -58,7 +58,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -33,7 +33,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16

View file

@ -61,7 +61,7 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
HeaderType { BaseHeaderType {
headerText: qsTr("New connection") headerText: qsTr("New connection")
} }

View file

@ -169,7 +169,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { HeaderTypeWithButton {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24

View file

@ -44,7 +44,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24

View file

@ -52,6 +52,28 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
emit bytesChanged(receivedBytes, sentBytes); emit bytesChanged(receivedBytes, sentBytes);
} }
void VpnConnection::onKillSwitchModeChanged(bool enabled)
{
#ifdef AMNEZIA_DESKTOP
if (!m_IpcClient) {
m_IpcClient = new IpcClient(this);
}
if (!m_IpcClient->isSocketConnected()) {
if (!IpcClient::init(m_IpcClient)) {
qWarning() << "Error occurred when init IPC client";
emit serviceIsNotReady();
return;
}
}
if (IpcClient::Interface()) {
qDebug() << "Set KillSwitch Strict mode enabled " << enabled;
IpcClient::Interface()->refreshKillSwitch(enabled);
}
#endif
}
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{ {

View file

@ -48,14 +48,14 @@ public:
public slots: public slots:
void connectToVpn(int serverIndex, void connectToVpn(int serverIndex,
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn(); void disconnectFromVpn();
void addRoutes(const QStringList &ips); void addRoutes(const QStringList &ips);
void deleteRoutes(const QStringList &ips); void deleteRoutes(const QStringList &ips);
void flushDns(); void flushDns();
void onKillSwitchModeChanged(bool enabled);
signals: signals:
void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void bytesChanged(quint64 receivedBytes, quint64 sentBytes);

View file

@ -29,6 +29,9 @@ class IpcInterface
SLOT( void StopRoutingIpv6() ); SLOT( void StopRoutingIpv6() );
SLOT( bool disableKillSwitch() ); SLOT( bool disableKillSwitch() );
SLOT( bool disableAllTraffic() );
SLOT( bool refreshKillSwitch( bool enabled ) );
SLOT( bool allowTrafficTo( const QStringList ranges ) );
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) ); SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );

View file

@ -8,21 +8,12 @@
#include "logger.h" #include "logger.h"
#include "router.h" #include "router.h"
#include "../core/networkUtilities.h" #include "killswitch.h"
#include "../client/protocols/protocols_defs.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "../client/platforms/windows/daemon/windowsdaemon.h"
#include "../client/platforms/windows/daemon/windowsfirewall.h"
#include "tapcontroller_win.h" #include "tapcontroller_win.h"
#endif #endif
#ifdef Q_OS_LINUX
#include "../client/platforms/linux/daemon/linuxfirewall.h"
#endif
#ifdef Q_OS_MACOS
#include "../client/platforms/macos/daemon/macosfirewall.h"
#endif
IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
@ -188,193 +179,32 @@ void IpcServer::setLogsEnabled(bool enabled)
} }
} }
bool IpcServer::allowTrafficTo(QStringList ranges)
{
return KillSwitch::instance()->allowTrafficTo(ranges);
}
bool IpcServer::disableAllTraffic()
{
return KillSwitch::instance()->disableAllTraffic();
}
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
{ {
#ifdef Q_OS_WIN return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex);
auto firewallManager = WindowsFirewall::create(this);
Q_ASSERT(firewallManager != nullptr);
return firewallManager->enableInterface(vpnAdapterIndex);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
bool blockAll = 0;
bool allowNets = 0;
bool blockNets = 0;
QStringList allownets;
QStringList blocknets;
if (splitTunnelType == 0) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
} else if (splitTunnelType == 1) {
blockNets = true;
for (auto v : splitTunnelSites) {
blocknets.append(v.toString());
}
} else if (splitTunnelType == 2) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
for (auto v : splitTunnelSites) {
allownets.append(v.toString());
}
}
#endif
#ifdef Q_OS_LINUX
// double-check + ensure our firewall is installed and enabled
if (!LinuxFirewall::isInstalled())
LinuxFirewall::install();
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);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), 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");
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
if (!dns.isString()) {
break;
}
dnsServers.append(dns.toString());
}
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)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
if (!dns.isString()) {
break;
}
dnsServers.append(dns.toString());
}
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
#endif
return true;
} }
bool IpcServer::disableKillSwitch() bool IpcServer::disableKillSwitch()
{ {
#ifdef Q_OS_WIN return KillSwitch::instance()->disableKillSwitch();
auto firewallManager = WindowsFirewall::create(this);
Q_ASSERT(firewallManager != nullptr);
return firewallManager->disableKillSwitch();
#endif
#ifdef Q_OS_LINUX
LinuxFirewall::uninstall();
#endif
#ifdef Q_OS_MACOS
MacOSFirewall::uninstall();
#endif
return true;
} }
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
{ {
#ifdef Q_OS_WIN return KillSwitch::instance()->enablePeerTraffic(configStr);
InterfaceConfig config; }
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
config.m_serverPublicKey = "openvpn"; bool IpcServer::refreshKillSwitch(bool enabled)
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); {
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); return KillSwitch::instance()->refresh(enabled);
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1) {
config.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
} else {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
}
}
}
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
config.m_excludedAddresses.append(ipRange);
}
}
for (auto app : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
if (!app.isString()) {
break;
}
config.m_vpnDisabledApps.append(app.toString());
}
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
if (!dns.isString()) {
break;
}
config.m_vpnDisabledApps.append(i.toString());
config.m_allowedDnsServers.append(dns.toString());
}
// killSwitch toggle
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
auto firewallManager = WindowsFirewall::create(this);
Q_ASSERT(firewallManager != nullptr);
firewallManager->enablePeerTraffic(config);
}
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
#endif
return true;
} }

View file

@ -34,9 +34,12 @@ public:
virtual bool deleteTun(const QString &dev) override; virtual bool deleteTun(const QString &dev) override;
virtual void StartRoutingIpv6() override; virtual void StartRoutingIpv6() override;
virtual void StopRoutingIpv6() override; virtual void StopRoutingIpv6() override;
virtual bool disableAllTraffic() override;
virtual bool allowTrafficTo(QStringList ranges) override;
virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
virtual bool disableKillSwitch() override; virtual bool disableKillSwitch() override;
virtual bool refreshKillSwitch( bool enabled ) override;
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override; virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
private: private:

View file

@ -12,8 +12,47 @@ qt_standard_project_setup()
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
set(QSIMPLECRYPTO_DIR ${CMAKE_CURRENT_LIST_DIR}/../../client/3rd/QSimpleCrypto/src)
set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../client/3rd-prebuilt/3rd-prebuilt/openssl/")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib")
else()
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif()
if(WIN32)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib")
else()
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif()
elseif(APPLE AND NOT IOS)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
elseif(LINUX)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/linux/include")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libcrypto.a")
endif()
set(OPENSSL_USE_STATIC_LIBS TRUE)
include_directories(
${OPENSSL_INCLUDE_DIR}
${QSIMPLECRYPTO_DIR}
)
set(HEADERS set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h
${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.h
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
@ -22,12 +61,20 @@ set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/localserver.h ${CMAKE_CURRENT_LIST_DIR}/localserver.h
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
${CMAKE_CURRENT_LIST_DIR}/router.h ${CMAKE_CURRENT_LIST_DIR}/router.h
${CMAKE_CURRENT_LIST_DIR}/killswitch.h
${CMAKE_CURRENT_LIST_DIR}/systemservice.h ${CMAKE_CURRENT_LIST_DIR}/systemservice.h
${CMAKE_CURRENT_BINARY_DIR}/version.h ${CMAKE_CURRENT_BINARY_DIR}/version.h
${QSIMPLECRYPTO_DIR}/include/QAead.h
${QSIMPLECRYPTO_DIR}/include/QBlockCipher.h
${QSIMPLECRYPTO_DIR}/include/QRsa.h
${QSIMPLECRYPTO_DIR}/include/QSimpleCrypto_global.h
${QSIMPLECRYPTO_DIR}/include/QX509.h
${QSIMPLECRYPTO_DIR}/include/QX509Store.h
) )
set(SOURCES set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp
@ -36,7 +83,13 @@ set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp
${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp
${CMAKE_CURRENT_LIST_DIR}/router.cpp ${CMAKE_CURRENT_LIST_DIR}/router.cpp
${CMAKE_CURRENT_LIST_DIR}/killswitch.cpp
${CMAKE_CURRENT_LIST_DIR}/systemservice.cpp ${CMAKE_CURRENT_LIST_DIR}/systemservice.cpp
${QSIMPLECRYPTO_DIR}/sources/QAead.cpp
${QSIMPLECRYPTO_DIR}/sources/QBlockCipher.cpp
${QSIMPLECRYPTO_DIR}/sources/QRsa.cpp
${QSIMPLECRYPTO_DIR}/sources/QX509.cpp
${QSIMPLECRYPTO_DIR}/sources/QX509Store.cpp
) )
# Mozilla headres # Mozilla headres
@ -133,6 +186,7 @@ if(WIN32)
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp ${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp
${CMAKE_CURRENT_LIST_DIR}/router_win.cpp ${CMAKE_CURRENT_LIST_DIR}/router_win.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemon.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp
@ -159,6 +213,8 @@ if(WIN32)
gdi32 gdi32
Advapi32 Advapi32
Kernel32 Kernel32
${OPENSSL_LIB_CRYPTO_PATH}
qt6keychain
) )
add_compile_definitions(_WINSOCKAPI_) add_compile_definitions(_WINSOCKAPI_)
@ -203,6 +259,9 @@ if(APPLE)
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/wireguardutilsmacos.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/wireguardutilsmacos.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/macosfirewall.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/macosfirewall.cpp
) )
set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain)
endif() endif()
if(LINUX) if(LINUX)
@ -233,6 +292,9 @@ if(LINUX)
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxfirewall.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxfirewall.cpp
) )
set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain -static-libstdc++ -static-libgcc -ldl)
endif() endif()
include(${CMAKE_CURRENT_LIST_DIR}/../src/qtservice.cmake) include(${CMAKE_CURRENT_LIST_DIR}/../src/qtservice.cmake)
@ -245,6 +307,7 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
) )
add_executable(${PROJECT} ${SOURCES} ${HEADERS}) add_executable(${PROJECT} ${SOURCES} ${HEADERS})
target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS}) target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS})
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>") target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")

View file

@ -0,0 +1,314 @@
#include "killswitch.h"
#include <QApplication>
#include <QHostAddress>
#include "../client/protocols/protocols_defs.h"
#include "qjsonarray.h"
#include "version.h"
#ifdef Q_OS_WIN
#include "../client/platforms/windows/daemon/windowsfirewall.h"
#include "../client/platforms/windows/daemon/windowsdaemon.h"
#endif
#ifdef Q_OS_LINUX
#include "../client/platforms/linux/daemon/linuxfirewall.h"
#endif
#ifdef Q_OS_MACOS
#include "../client/platforms/macos/daemon/macosfirewall.h"
#endif
KillSwitch* s_instance = nullptr;
KillSwitch* KillSwitch::instance()
{
if (s_instance == nullptr) {
s_instance = new KillSwitch(qApp);
}
return s_instance;
}
bool KillSwitch::init()
{
#ifdef Q_OS_LINUX
if (!LinuxFirewall::isInstalled()) {
LinuxFirewall::install();
}
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
#endif
#ifdef Q_OS_MACOS
if (!MacOSFirewall::isInstalled()) {
MacOSFirewall::install();
}
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
#endif
if (isStrictKillSwitchEnabled()) {
return disableAllTraffic();
}
return true;
}
bool KillSwitch::refresh(bool enabled)
{
#ifdef Q_OS_WIN
QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME)
+ "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat);
RegHLM.setValue("strictKillSwitchEnabled", enabled);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled);
#endif
if (isStrictKillSwitchEnabled()) {
return disableAllTraffic();
} else {
return disableKillSwitch();
}
}
bool KillSwitch::isStrictKillSwitchEnabled()
{
#ifdef Q_OS_WIN
QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME)
+ "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat);
return RegHLM.value("strictKillSwitchEnabled", false).toBool();
#endif
m_appSettigns->sync();
return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool();
}
bool KillSwitch::disableKillSwitch() {
#ifdef Q_OS_LINUX
if (isStrictKillSwitchEnabled()) {
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false);
} else {
LinuxFirewall::uninstall();
}
#endif
#ifdef Q_OS_MACOS
if (isStrictKillSwitchEnabled()) {
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), false);
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), false);
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), false);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), false);
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), false);
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), false);
} else {
MacOSFirewall::uninstall();
}
#endif
#ifdef Q_OS_WIN
if (isStrictKillSwitchEnabled()) {
return disableAllTraffic();
}
return WindowsFirewall::create(this)->allowAllTraffic();
#endif
return true;
}
bool KillSwitch::disableAllTraffic() {
#ifdef Q_OS_WIN
WindowsFirewall::create(this)->enableInterface(-1);
#endif
#ifdef Q_OS_LINUX
if (!LinuxFirewall::isInstalled()) {
LinuxFirewall::install();
}
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), 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)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
#endif
return true;
}
bool KillSwitch::allowTrafficTo(const QStringList &ranges) {
#ifdef Q_OS_LINUX
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), true);
LinuxFirewall::updateAllowNets(ranges);
#endif
#ifdef Q_OS_MACOS
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), true);
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), ranges);
#endif
return true;
}
bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
#ifdef Q_OS_WIN
InterfaceConfig config;
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
config.m_serverPublicKey = "openvpn";
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1) {
config.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
} else {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
}
}
}
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
config.m_excludedAddresses.append(ipRange);
}
}
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
if (!i.isString()) {
break;
}
config.m_vpnDisabledApps.append(i.toString());
}
// killSwitch toggle
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
WindowsFirewall::create(this)->enablePeerTraffic(config);
}
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
#endif
return true;
}
bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) {
#ifdef Q_OS_WIN
return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
bool blockAll = 0;
bool allowNets = 0;
bool blockNets = 0;
QStringList allownets;
QStringList blocknets;
if (splitTunnelType == 0) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
} else if (splitTunnelType == 1) {
blockNets = true;
for (auto v : splitTunnelSites) {
blocknets.append(v.toString());
}
} else if (splitTunnelType == 2) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
for (auto v : splitTunnelSites) {
allownets.append(v.toString());
}
}
#endif
#ifdef Q_OS_LINUX
if (!LinuxFirewall::isInstalled()) {
LinuxFirewall::install();
}
// 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);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), 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)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
#endif
return true;
}

View file

@ -0,0 +1,29 @@
#ifndef KILLSWITCH_H
#define KILLSWITCH_H
#include <QJsonObject>
#include <QSharedPointer>
#include "secure_qsettings.h"
class KillSwitch : public QObject
{
Q_OBJECT
public:
static KillSwitch *instance();
bool init();
bool refresh(bool enabled);
bool disableKillSwitch();
bool disableAllTraffic();
bool enablePeerTraffic( const QJsonObject &configStr);
bool enableKillSwitch( const QJsonObject &configStr, int vpnAdapterIndex);
bool allowTrafficTo(const QStringList &ranges);
bool isStrictKillSwitchEnabled();
private:
KillSwitch(QObject* parent) {};
QSharedPointer<SecureQSettings> m_appSettigns;
};
#endif // KILLSWITCH_H

View file

@ -5,13 +5,12 @@
#include "ipc.h" #include "ipc.h"
#include "localserver.h" #include "localserver.h"
#include "utilities.h"
#include "router.h" #include "killswitch.h"
#include "logger.h" #include "logger.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "tapcontroller_win.h" #include "tapcontroller_win.h"
#endif #endif
namespace { namespace {
@ -47,6 +46,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
return; return;
} }
KillSwitch::instance()->init();
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// Signal handling for a proper shutdown. // Signal handling for a proper shutdown.
QObject::connect(qApp, &QCoreApplication::aboutToQuit, QObject::connect(qApp, &QCoreApplication::aboutToQuit,