From aae3cdcac1bb95ee6c05cc25b0b2a0afabc010e7 Mon Sep 17 00:00:00 2001 From: Nethius Date: Fri, 13 Sep 2024 13:53:21 +0400 Subject: [PATCH] added saving allowed_ips to the array of strings for old configs (#926) * added saving allowed_ips to the array of strings for old configs * Remove config string processing, add getting all AWG, WG parameters from JSON * fixed checking of default routes when adding split tunneling from the application * added check when processing siteBasedSplitTunneling --- client/android/awg/src/main/kotlin/Awg.kt | 77 +++------------- client/android/cloak/src/main/kotlin/Cloak.kt | 30 ------ .../amnezia/vpn/protocol/openvpn/OpenVpn.kt | 17 ---- .../android/utils/src/main/kotlin/JsonExt.kt | 9 ++ .../vpn/protocol/wireguard/Wireguard.kt | 92 +++++-------------- client/android/xray/src/main/kotlin/Xray.kt | 63 ------------- .../configurators/wireguard_configurator.cpp | 4 + client/mozilla/localsocketcontroller.cpp | 2 +- client/ui/controllers/importController.cpp | 10 +- client/vpnconnection.cpp | 89 ++++++++++++------ 10 files changed, 120 insertions(+), 273 deletions(-) create mode 100644 client/android/utils/src/main/kotlin/JsonExt.kt diff --git a/client/android/awg/src/main/kotlin/Awg.kt b/client/android/awg/src/main/kotlin/Awg.kt index f4175763..fbd1cce0 100644 --- a/client/android/awg/src/main/kotlin/Awg.kt +++ b/client/android/awg/src/main/kotlin/Awg.kt @@ -1,81 +1,28 @@ package org.amnezia.vpn.protocol.awg import org.amnezia.vpn.protocol.wireguard.Wireguard +import org.amnezia.vpn.util.optStringOrNull import org.json.JSONObject -/** - * Config example: - * { - * "protocol": "awg", - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "splitTunnelSites": [ - * ], - * "splitTunnelType": 0, - * "awg_config_data": { - * "H1": "969537490", - * "H2": "481688153", - * "H3": "2049399200", - * "H4": "52029755", - * "Jc": "3", - * "Jmax": "1000", - * "Jmin": "50", - * "S1": "49", - * "S2": "60", - * "client_ip": "10.8.1.1", - * "hostName": "100.100.100.0", - * "port": 12345, - * "client_pub_key": "clientPublicKeyBase64", - * "client_priv_key": "privateKeyBase64", - * "psk_key": "presharedKeyBase64", - * "server_pub_key": "publicKeyBase64", - * "config": "[Interface] - * Address = 10.8.1.1/32 - * DNS = 1.1.1.1, 1.0.0.1 - * PrivateKey = privateKeyBase64 - * Jc = 3 - * Jmin = 50 - * Jmax = 1000 - * S1 = 49 - * S2 = 60 - * H1 = 969537490 - * H2 = 481688153 - * H3 = 2049399200 - * H4 = 52029755 - * - * [Peer] - * PublicKey = publicKeyBase64 - * PresharedKey = presharedKeyBase64 - * AllowedIPs = 0.0.0.0/0, ::/0 - * Endpoint = 100.100.100.0:12345 - * PersistentKeepalive = 25 - * " - * } - * } - */ - class Awg : Wireguard() { override val ifName: String = "awg0" override fun parseConfig(config: JSONObject): AwgConfig { - val configDataJson = config.getJSONObject("awg_config_data") - val configData = parseConfigData(configDataJson.getString("config")) + val configData = config.getJSONObject("awg_config_data") return AwgConfig.build { - configWireguard(configData, configDataJson) + configWireguard(config, configData) configSplitTunneling(config) configAppSplitTunneling(config) - configData["Jc"]?.let { setJc(it.toInt()) } - configData["Jmin"]?.let { setJmin(it.toInt()) } - configData["Jmax"]?.let { setJmax(it.toInt()) } - configData["S1"]?.let { setS1(it.toInt()) } - configData["S2"]?.let { setS2(it.toInt()) } - configData["H1"]?.let { setH1(it.toLong()) } - configData["H2"]?.let { setH2(it.toLong()) } - configData["H3"]?.let { setH3(it.toLong()) } - configData["H4"]?.let { setH4(it.toLong()) } + configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) } + configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) } + configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) } + configData.optStringOrNull("S1")?.let { setS1(it.toInt()) } + configData.optStringOrNull("S2")?.let { setS2(it.toInt()) } + configData.optStringOrNull("H1")?.let { setH1(it.toLong()) } + configData.optStringOrNull("H2")?.let { setH2(it.toLong()) } + configData.optStringOrNull("H3")?.let { setH3(it.toLong()) } + configData.optStringOrNull("H4")?.let { setH4(it.toLong()) } } } } diff --git a/client/android/cloak/src/main/kotlin/Cloak.kt b/client/android/cloak/src/main/kotlin/Cloak.kt index 5a549130..18a5e6c7 100644 --- a/client/android/cloak/src/main/kotlin/Cloak.kt +++ b/client/android/cloak/src/main/kotlin/Cloak.kt @@ -5,36 +5,6 @@ import net.openvpn.ovpn3.ClientAPI_Config import org.amnezia.vpn.protocol.openvpn.OpenVpn import org.json.JSONObject -/** - * Config Example: - * { - * "protocol": "cloak", - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "splitTunnelSites": [ - * ], - * "splitTunnelType": 0, - * "openvpn_config_data": { - * "config": "openVpnConfig" - * } - * "cloak_config_data": { - * "BrowserSig": "chrome", - * "EncryptionMethod": "aes-gcm", - * "NumConn": 1, - * "ProxyMethod": "openvpn", - * "PublicKey": "PublicKey=", - * "RemoteHost": "100.100.100.0", - * "RemotePort": "443", - * "ServerName": "servername", - * "StreamTimeout": 300, - * "Transport": "direct", - * "UID": "UID=" - * } - * } - */ - class Cloak : OpenVpn() { override fun parseConfig(config: JSONObject): ClientAPI_Config { diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt index dd096c60..fa0e19f1 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt @@ -16,23 +16,6 @@ import org.amnezia.vpn.util.net.getLocalNetworks import org.amnezia.vpn.util.net.parseInetAddress import org.json.JSONObject -/** - * Config Example: - * { - * "protocol": "openvpn", - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "splitTunnelSites": [ - * ], - * "splitTunnelType": 0, - * "openvpn_config_data": { - * "config": "openVpnConfig" - * } - * } - */ - open class OpenVpn : Protocol() { private var openVpnClient: OpenVpnClient? = null diff --git a/client/android/utils/src/main/kotlin/JsonExt.kt b/client/android/utils/src/main/kotlin/JsonExt.kt new file mode 100644 index 00000000..45c5bacd --- /dev/null +++ b/client/android/utils/src/main/kotlin/JsonExt.kt @@ -0,0 +1,9 @@ +package org.amnezia.vpn.util + +import org.json.JSONArray +import org.json.JSONObject + +inline fun JSONArray.asSequence(): Sequence = + (0.., configDataJson: JSONObject) { - configData["Address"]?.split(",")?.map { address -> + protected fun WireguardConfig.Builder.configWireguard(config: JSONObject, configData: JSONObject) { + configData.getString("client_ip").split(",").map { address -> InetNetwork.parse(address.trim()) - }?.forEach(::addAddress) + }.forEach(::addAddress) - configData["DNS"]?.split(",")?.map { dns -> - parseInetAddress(dns.trim()) - }?.forEach(::addDnsServer) + config.optStringOrNull("dns1")?.let { dns -> + addDnsServer(parseInetAddress(dns.trim())) + } + + config.optStringOrNull("dns2")?.let { dns -> + addDnsServer(parseInetAddress(dns.trim())) + } val defRoutes = hashSetOf( InetNetwork("0.0.0.0", 0), InetNetwork("::", 0) ) val routes = hashSetOf() - configData["AllowedIPs"]?.split(",")?.map { route -> + configData.getJSONArray("allowed_ips").asSequence().map { route -> InetNetwork.parse(route.trim()) - }?.forEach(routes::add) + }.forEach(routes::add) // if the allowed IPs list contains at least one non-default route, disable global split tunneling if (routes.any { it !in defRoutes }) disableSplitTunneling() addRoutes(routes) - configDataJson.optString("mtu").let { mtu -> - if (mtu.isNotEmpty()) { - setMtu(mtu.toInt()) - } else { - configData["MTU"]?.let { setMtu(it.toInt()) } - } - } + configData.optStringOrNull("mtu")?.let { setMtu(it.toInt()) } - configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) } - configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) } - configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) } - configData["PublicKey"]?.let { setPublicKeyHex(it.base64ToHex()) } - configData["PresharedKey"]?.let { setPreSharedKeyHex(it.base64ToHex()) } - } + val host = configData.getString("hostName").let { parseInetAddress(it.trim()) } + val port = configData.getInt("port") + setEndpoint(InetEndpoint(host, port)) - protected fun parseConfigData(data: String): Map { - val parsedData = TreeMap(String.CASE_INSENSITIVE_ORDER) - data.lineSequence() - .filter { it.isNotEmpty() && !it.startsWith('[') } - .forEach { line -> - val attr = line.split("=", limit = 2) - parsedData[attr.first().trim()] = attr.last().trim() - } - return parsedData + configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) } + configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) } + configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) } + configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) } } private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) { diff --git a/client/android/xray/src/main/kotlin/Xray.kt b/client/android/xray/src/main/kotlin/Xray.kt index 94afec2a..ec22a4bd 100644 --- a/client/android/xray/src/main/kotlin/Xray.kt +++ b/client/android/xray/src/main/kotlin/Xray.kt @@ -20,69 +20,6 @@ import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.parseInetAddress import org.json.JSONObject -/** - * Config example: - * { - * "appSplitTunnelType": 0, - * "config_version": 0, - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "protocol": "xray", - * "splitTunnelApps": [], - * "splitTunnelSites": [], - * "splitTunnelType": 0, - * "xray_config_data": { - * "inbounds": [ - * { - * "listen": "127.0.0.1", - * "port": 8080, - * "protocol": "socks", - * "settings": { - * "udp": true - * } - * } - * ], - * "log": { - * "loglevel": "error" - * }, - * "outbounds": [ - * { - * "protocol": "vless", - * "settings": { - * "vnext": [ - * { - * "address": "100.100.100.0", - * "port": 443, - * "users": [ - * { - * "encryption": "none", - * "flow": "xtls-rprx-vision", - * "id": "id" - * } - * ] - * } - * ] - * }, - * "streamSettings": { - * "network": "tcp", - * "realitySettings": { - * "fingerprint": "chrome", - * "publicKey": "publicKey", - * "serverName": "google.com", - * "shortId": "id", - * "spiderX": "" - * }, - * "security": "reality" - * } - * } - * ] - * } - * } - * - */ - private const val TAG = "Xray" private const val LIBXRAY_TAG = "libXray" diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index f7faaa52..27db92ff 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -187,6 +187,10 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials jConfig[config_key::server_pub_key] = connData.serverPubKey; jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); + jConfig[config_key::persistent_keep_alive] = 25; + QJsonArray allowedIps { "0.0.0.0/0", "::/0" }; + jConfig[config_key::allowed_ips] = allowedIps; + jConfig[config_key::clientId] = connData.clientPubKey; return QJsonDocument(jConfig).toJson(); diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 0502facc..4d040288 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -149,7 +149,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { QJsonArray jsAllowedIPAddesses; QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray(); - QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(",")); + QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" }; if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) { // Use AllowedIP list from WG config because of higher priority diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index a50e793f..f64ba36e 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -392,7 +392,15 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) return QJsonObject(); } - QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(",")); + if (!configMap.value("MTU").isEmpty()) { + lastConfig[config_key::mtu] = configMap.value("MTU"); + } + + if (!configMap.value("PersistentKeepalive").isEmpty()) { + lastConfig[config_key::persistent_keep_alive] = configMap.value("PersistentKeepalive"); + } + + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(", ")); lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index daff1187..c4022be6 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -1,16 +1,16 @@ #include "qtimer.h" #include +#include #include #include #include -#include +#include "core/controllers/serverController.h" #include #include #include #include -#include "core/controllers/serverController.h" #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" @@ -34,8 +34,7 @@ VpnConnection::VpnConnection(std::shared_ptr settings, QObject *parent { m_checkTimer.setInterval(1000); #ifdef Q_OS_IOS - connect(IosController::Instance(), &IosController::connectionStateChanged, this, - &VpnConnection::onConnectionStateChanged); + connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged); connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged); #endif @@ -58,7 +57,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) #ifdef AMNEZIA_DESKTOP QString proto = m_settings->defaultContainerName(m_settings->defaultServerIndex()); - + if (IpcClient::Interface()) { if (state == Vpn::ConnectionState::Connected) { IpcClient::Interface()->resetIpStack(); @@ -72,7 +71,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) if (m_settings->isSitesSplitTunnelingEnabled()) { IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0"); - // qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); + // qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { QTimer::singleShot(1000, m_vpnProtocol.data(), [this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); }); @@ -291,27 +290,62 @@ void VpnConnection::appendKillSwitchConfig() void VpnConnection::appendSplitTunnelingConfig() { - if (m_vpnConfiguration.value(config_key::configVersion).toInt()) { - auto protocolName = m_vpnConfiguration.value(config_key::vpnproto).toString(); - if (protocolName == ProtocolProps::protoToString(Proto::Awg)) { - auto configData = m_vpnConfiguration.value(protocolName + "_config_data").toObject(); - QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configData.value("allowed_ips").toString().split(",")); - QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(",")); + bool allowSiteBasedSplitTunneling = true; - if (allowedIpsJsonArray != defaultAllowedIP) { - allowedIpsJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); - allowedIpsJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); - - m_vpnConfiguration.insert(config_key::splitTunnelType, Settings::RouteMode::VpnOnlyForwardSites); - m_vpnConfiguration.insert(config_key::splitTunnelSites, allowedIpsJsonArray); + // this block is for old native configs and for old self-hosted configs + auto protocolName = m_vpnConfiguration.value(config_key::vpnproto).toString(); + if (protocolName == ProtocolProps::protoToString(Proto::Awg) || protocolName == ProtocolProps::protoToString(Proto::WireGuard)) { + allowSiteBasedSplitTunneling = false; + auto configData = m_vpnConfiguration.value(protocolName + "_config_data").toObject(); + if (configData.value(config_key::allowed_ips).isString()) { + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configData.value(config_key::allowed_ips).toString().split(", ")); + configData.insert(config_key::allowed_ips, allowedIpsJsonArray); + m_vpnConfiguration.insert(protocolName + "_config_data", configData); + } else if (configData.value(config_key::allowed_ips).isUndefined()) { + auto nativeConfig = configData.value(config_key::config).toString(); + auto nativeConfigLines = nativeConfig.split("\n"); + for (auto &line : nativeConfigLines) { + if (line.contains("AllowedIPs")) { + auto allowedIpsString = line.split(" = "); + if (allowedIpsString.size() < 1) { + break; + } + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(allowedIpsString.at(1).split(", ")); + configData.insert(config_key::allowed_ips, allowedIpsJsonArray); + m_vpnConfiguration.insert(protocolName + "_config_data", configData); + break; + } } } - } else { - Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites; - QJsonArray sitesJsonArray; - if (m_settings->isSitesSplitTunnelingEnabled()) { - routeMode = m_settings->routeMode(); + if (configData.value(config_key::persistent_keep_alive).isUndefined()) { + auto nativeConfig = configData.value(config_key::config).toString(); + auto nativeConfigLines = nativeConfig.split("\n"); + for (auto &line : nativeConfigLines) { + if (line.contains("PersistentKeepalive")) { + auto persistentKeepaliveString = line.split(" = "); + if (persistentKeepaliveString.size() < 1) { + break; + } + configData.insert(config_key::persistent_keep_alive, persistentKeepaliveString.at(1)); + m_vpnConfiguration.insert(protocolName + "_config_data", configData); + break; + } + } + } + + QJsonArray allowedIpsJsonArray = configData.value(config_key::allowed_ips).toArray(); + if (allowedIpsJsonArray.contains("0.0.0.0/0") && allowedIpsJsonArray.contains("::/0")) { + allowSiteBasedSplitTunneling = true; + } + } + + Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites; + QJsonArray sitesJsonArray; + if (m_settings->isSitesSplitTunnelingEnabled()) { + routeMode = m_settings->routeMode(); + + if (allowSiteBasedSplitTunneling) { auto sites = m_settings->getVpnIps(routeMode); for (const auto &site : sites) { sitesJsonArray.append(site); @@ -323,11 +357,11 @@ void VpnConnection::appendSplitTunnelingConfig() sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); } } - - m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode); - m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray); } + m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode); + m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray); + Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps; QJsonArray appsJsonArray; if (m_settings->isAppsSplitTunnelingEnabled()) { @@ -359,8 +393,7 @@ void VpnConnection::createAndroidConnections() connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState); - connect(AndroidController::instance(), &AndroidController::statisticsUpdated, androidVpnProtocol, - &AndroidVpnProtocol::setBytesChanged); + connect(AndroidController::instance(), &AndroidController::statisticsUpdated, androidVpnProtocol, &AndroidVpnProtocol::setBytesChanged); } AndroidVpnProtocol *VpnConnection::createDefaultAndroidVpnProtocol()