From 2f6fb0d557ec3ede7338ad12cacbf67369cb1655 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sat, 12 Jun 2021 11:59:36 +0300 Subject: [PATCH] Wireguard protocol + refactoring --- client/client.pro | 6 + client/configurators/openvpn_configurator.cpp | 38 +-- client/configurators/openvpn_configurator.h | 2 - client/configurators/ssh_configurator.cpp | 95 ++++++ client/configurators/ssh_configurator.h | 20 ++ .../configurators/wireguard_configurator.cpp | 178 +++++++++++ client/configurators/wireguard_configurator.h | 41 +++ client/core/defs.h | 5 +- client/core/ipcclient.cpp | 2 +- client/core/scripts_registry.cpp | 1 + client/core/scripts_registry.h | 3 +- client/core/servercontroller.cpp | 72 +++-- client/core/servercontroller.h | 10 +- client/defines.h | 4 +- client/protocols/openvpnprotocol.cpp | 19 +- client/protocols/openvpnprotocol.h | 2 + client/protocols/protocols_defs.h | 21 +- client/protocols/wireguardprotocol.cpp | 232 +++++++++++++++ client/protocols/wireguardprotocol.h | 46 +++ client/resources.qrc | 5 + .../openvpn/configure_container.sh | 2 +- client/server_scripts/openvpn/start.sh | 5 +- .../openvpn_cloak/configure_container.sh | 2 +- client/server_scripts/openvpn_cloak/start.sh | 5 +- .../configure_container.sh | 2 +- .../openvpn_shadowsocks/start.sh | 5 +- client/server_scripts/wireguard/Dockerfile | 47 +++ .../wireguard/configure_container.sh | 13 + .../server_scripts/wireguard/run_container.sh | 15 + client/server_scripts/wireguard/start.sh | 25 ++ client/server_scripts/wireguard/template.conf | 11 + client/settings.h | 21 -- client/ui/mainwindow.cpp | 97 ++++-- client/ui/mainwindow.ui | 278 +++++++++++++++++- client/utils.cpp | 10 - client/utils.h | 2 - client/vpnconnection.cpp | 43 ++- client/vpnconnection.h | 3 + ipc/ipcinterface.rep | 2 + ipc/ipcserverprocess.cpp | 6 + ipc/ipcserverprocess.h | 1 + 41 files changed, 1245 insertions(+), 152 deletions(-) create mode 100644 client/configurators/ssh_configurator.cpp create mode 100644 client/configurators/ssh_configurator.h create mode 100644 client/configurators/wireguard_configurator.cpp create mode 100644 client/configurators/wireguard_configurator.h create mode 100644 client/protocols/wireguardprotocol.cpp create mode 100644 client/protocols/wireguardprotocol.h create mode 100644 client/server_scripts/wireguard/Dockerfile create mode 100644 client/server_scripts/wireguard/configure_container.sh create mode 100644 client/server_scripts/wireguard/run_container.sh create mode 100644 client/server_scripts/wireguard/start.sh create mode 100644 client/server_scripts/wireguard/template.conf diff --git a/client/client.pro b/client/client.pro index 6680ecd0..2b210fed 100644 --- a/client/client.pro +++ b/client/client.pro @@ -15,7 +15,9 @@ HEADERS += \ ../ipc/ipc.h \ configurators/cloak_configurator.h \ configurators/shadowsocks_configurator.h \ + configurators/ssh_configurator.h \ configurators/vpn_configurator.h \ + configurators/wireguard_configurator.h \ core/defs.h \ core/errorstrings.h \ core/ipcclient.h \ @@ -29,6 +31,7 @@ HEADERS += \ protocols/openvpnovercloakprotocol.h \ protocols/protocols_defs.h \ protocols/shadowsocksvpnprotocol.h \ + protocols/wireguardprotocol.h \ settings.h \ ui/Controls/SlidingStackedWidget.h \ ui/mainwindow.h \ @@ -43,7 +46,9 @@ HEADERS += \ SOURCES += \ configurators/cloak_configurator.cpp \ configurators/shadowsocks_configurator.cpp \ + configurators/ssh_configurator.cpp \ configurators/vpn_configurator.cpp \ + configurators/wireguard_configurator.cpp \ core/errorstrings.cpp \ core/ipcclient.cpp \ configurators/openvpn_configurator.cpp \ @@ -56,6 +61,7 @@ SOURCES += \ protocols/openvpnovercloakprotocol.cpp \ protocols/protocols_defs.cpp \ protocols/shadowsocksvpnprotocol.cpp \ + protocols/wireguardprotocol.cpp \ settings.cpp \ ui/Controls/SlidingStackedWidget.cpp \ ui/mainwindow.cpp \ diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index d3ba8190..8bb8a360 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -5,11 +5,11 @@ #include #include #include -#include #include "core/server_defs.h" #include "protocols/protocols_defs.h" #include "core/scripts_registry.h" +#include "utils.h" QString OpenVpnConfigurator::getEasyRsaShPath() { @@ -227,7 +227,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString config) config.replace("$PRIMARY_DNS", m_settings().primaryDns()); config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); - if (m_settings().routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings().routeMode() != Settings::VpnAllSites) { config.replace("redirect-gateway def1 bypass-dhcp", ""); } else { @@ -266,40 +266,6 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(QString config) return config; } -QString OpenVpnConfigurator::convertOpenSShKey(const QString &key) -{ - QProcess p; - p.setProcessChannelMode(QProcess::MergedChannels); - - QTemporaryFile tmp; -#ifdef QT_DEBUG - tmp.setAutoRemove(false); -#endif - tmp.open(); - tmp.write(key.toUtf8()); - tmp.close(); - - // ssh-keygen -p -P "" -N "" -m pem -f id_ssh - -#ifdef Q_OS_WIN - p.setProcessEnvironment(prepareEnv()); - p.setProgram("cmd.exe"); - p.setNativeArguments(QString("/C \"ssh-keygen.exe -p -P \"\" -N \"\" -m pem -f \"%1\"\"").arg(tmp.fileName())); -#else - p.setProgram("ssh-keygen"); - p.setArguments(QStringList() << "-p" << "-P" << "" << "-N" << "" << "-m" << "pem" << "-f" << tmp.fileName()); -#endif - - p.start(); - p.waitForFinished(); - - qDebug().noquote() << "OpenVpnConfigurator::convertOpenSShKey" << p.exitCode() << p.exitStatus() << p.readAll(); - - tmp.open(); - - return tmp.readAll(); -} - ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId) { diff --git a/client/configurators/openvpn_configurator.h b/client/configurators/openvpn_configurator.h index f73175fe..33e7b997 100644 --- a/client/configurators/openvpn_configurator.h +++ b/client/configurators/openvpn_configurator.h @@ -28,8 +28,6 @@ public: static QString processConfigWithLocalSettings(QString config); static QString processConfigWithExportSettings(QString config); - static QString convertOpenSShKey(const QString &key); - static ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId); diff --git a/client/configurators/ssh_configurator.cpp b/client/configurators/ssh_configurator.cpp new file mode 100644 index 00000000..fe2019ad --- /dev/null +++ b/client/configurators/ssh_configurator.cpp @@ -0,0 +1,95 @@ +#include "ssh_configurator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/server_defs.h" +#include "utils.h" + +using namespace QSsh; + + +QString SshConfigurator::convertOpenSShKey(const QString &key) +{ + QProcess p; + p.setProcessChannelMode(QProcess::MergedChannels); + + QTemporaryFile tmp; +#ifdef QT_DEBUG + tmp.setAutoRemove(false); +#endif + tmp.open(); + tmp.write(key.toUtf8()); + tmp.close(); + + // ssh-keygen -p -P "" -N "" -m pem -f id_ssh + +#ifdef Q_OS_WIN + p.setProcessEnvironment(prepareEnv()); + p.setProgram("cmd.exe"); + p.setNativeArguments(QString("/C \"ssh-keygen.exe -p -P \"\" -N \"\" -m pem -f \"%1\"\"").arg(tmp.fileName())); +#else + p.setProgram("ssh-keygen"); + p.setArguments(QStringList() << "-p" << "-P" << "" << "-N" << "" << "-m" << "pem" << "-f" << tmp.fileName()); +#endif + + p.start(); + p.waitForFinished(); + + qDebug().noquote() << "OpenVpnConfigurator::convertOpenSShKey" << p.exitCode() << p.exitStatus() << p.readAll(); + + tmp.open(); + + return tmp.readAll(); +} + +void SshConfigurator::openSshTerminal(const ServerCredentials &credentials) +{ + QProcess *p = new QProcess(); + p->setReadChannelMode(QProcess::SeparateChannels); + +#ifdef Q_OS_WIN + p->setProcessEnvironment(prepareEnv()); + p->setProgram(qApp->applicationDirPath() + "\\cygwin\\putty.exe"); + + if (credentials.password.contains("PRIVATE KEY")) { + // todo: connect by key +// p->setNativeArguments(QString("%1@%2") +// .arg(credentials.userName).arg(credentials.hostName).arg(credentials.password)); + } + else { + p->setNativeArguments(QString("%1@%2 -pw %3") + .arg(credentials.userName).arg(credentials.hostName).arg(credentials.password)); + } +#else + p.setProgram("/bin/bash"); +#endif + + p->startDetached(); +} + +QProcessEnvironment SshConfigurator::prepareEnv() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString pathEnvVar = env.value("PATH"); + +#ifdef Q_OS_WIN + pathEnvVar.clear(); + pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;"); + pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;"); +#else + pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS"); +#endif + + env.insert("PATH", pathEnvVar); + //qDebug().noquote() << "ENV PATH" << pathEnvVar; + return env; +} diff --git a/client/configurators/ssh_configurator.h b/client/configurators/ssh_configurator.h new file mode 100644 index 00000000..71146a1c --- /dev/null +++ b/client/configurators/ssh_configurator.h @@ -0,0 +1,20 @@ +#ifndef SSH_CONFIGURATOR_H +#define SSH_CONFIGURATOR_H + +#include +#include + +#include "core/defs.h" +#include "settings.h" +#include "core/servercontroller.h" + +class SshConfigurator +{ +public: + static QProcessEnvironment prepareEnv(); + static QString convertOpenSShKey(const QString &key); + static void openSshTerminal(const ServerCredentials &credentials); + +}; + +#endif // SSH_CONFIGURATOR_H diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp new file mode 100644 index 00000000..366e0dcb --- /dev/null +++ b/client/configurators/wireguard_configurator.cpp @@ -0,0 +1,178 @@ +#include "wireguard_configurator.h" +#include +#include +#include +#include +#include +#include + +#include "sftpdefs.h" + +#include "core/server_defs.h" +#include "protocols/protocols_defs.h" +#include "core/scripts_registry.h" +#include "utils.h" + +QProcessEnvironment WireguardConfigurator::prepareEnv() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString pathEnvVar = env.value("PATH"); + +#ifdef Q_OS_WIN + pathEnvVar.clear(); + pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\wireguard;"); +#else + pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS"); +#endif + + env.insert("PATH", pathEnvVar); + qDebug().noquote() << "ENV PATH" << pathEnvVar; + return env; +} + +WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys() +{ + ConnectionData connData; + + QString program; +#ifdef Q_OS_WIN + program = QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\wireguard\\wg.exe"; +#else + program = QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS/wg"; +#endif + + // Priv + { + QProcess p; + p.setProcessEnvironment(prepareEnv()); + p.setProcessChannelMode(QProcess::MergedChannels); + p.setProgram(program); + + p.setArguments(QStringList() << "genkey"); + + p.start(); + p.waitForFinished(); + + connData.clientPrivKey = QString(p.readAll()); + connData.clientPrivKey.replace("\r", ""); + connData.clientPrivKey.replace("\n", ""); + } + + // Pub + { + QProcess p; + p.setProcessEnvironment(prepareEnv()); + p.setProcessChannelMode(QProcess::MergedChannels); + p.setProgram(program); + + p.setArguments(QStringList() << "pubkey"); + + p.start(); + p.write(connData.clientPrivKey.toUtf8()); + p.closeWriteChannel(); + p.waitForFinished(); + + connData.clientPubKey = QString(p.readAll()); + connData.clientPubKey.replace("\r", ""); + connData.clientPubKey.replace("\n", ""); + } + + return connData; +} + +WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials, + DockerContainer container, ErrorCode *errorCode) +{ + WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys(); + connData.host = credentials.hostName; + + if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) { + if (errorCode) *errorCode = ErrorCode::EasyRsaExecutableMissing; + return connData; + } + + ErrorCode e = ErrorCode::NoError; + connData.serverPubKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e); + connData.serverPubKey.replace("\n", ""); + if (e) { + if (errorCode) *errorCode = e; + return connData; + } + + connData.pskKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e); + connData.pskKey.replace("\n", ""); + + if (e) { + if (errorCode) *errorCode = e; + return connData; + } + + + QString configPart = QString( + "[Peer]\n" + "PublicKey = %1\n" + "PresharedKey = %2\n" + "AllowedIPs = $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR\n\n"). + arg(connData.clientPubKey). + arg(connData.pskKey); + + configPart = ServerController::replaceVars(configPart, ServerController::genVarsForScript(credentials, container)); + + qDebug().noquote() << "Adding wg conf part to server" << configPart; + + e = ServerController::uploadTextFileToContainer(container, credentials, configPart, + protocols::wireguard::serverConfigPath, QSsh::SftpOverwriteMode::SftpAppendToExisting); + + if (e) { + if (errorCode) *errorCode = e; + return connData; + } + + e = ServerController::runScript(ServerController::sshParams(credentials), + ServerController::replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'", + ServerController::genVarsForScript(credentials, container))); + + return connData; +} + +Settings &WireguardConfigurator::m_settings() +{ + static Settings s; + return s; +} + +QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, + DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode) +{ + QString config = ServerController::replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container), + ServerController::genVarsForScript(credentials, container, containerConfig)); + + ConnectionData connData = prepareWireguardConfig(credentials, container, errorCode); + if (errorCode && *errorCode) { + return ""; + } + + config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey); + config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey); + config.replace("$WIREGUARD_PSK", connData.pskKey); + + qDebug().noquote() << config; + return config; +} + +QString WireguardConfigurator::processConfigWithLocalSettings(QString config) +{ + // TODO replace DNS if it already set + config.replace("$PRIMARY_DNS", m_settings().primaryDns()); + config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); + + return config; +} + +QString WireguardConfigurator::processConfigWithExportSettings(QString config) +{ + config.replace("$PRIMARY_DNS", m_settings().primaryDns()); + config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); + + return config; +} diff --git a/client/configurators/wireguard_configurator.h b/client/configurators/wireguard_configurator.h new file mode 100644 index 00000000..777dfaaa --- /dev/null +++ b/client/configurators/wireguard_configurator.h @@ -0,0 +1,41 @@ +#ifndef WIREGUARD_CONFIGURATOR_H +#define WIREGUARD_CONFIGURATOR_H + +#include +#include + +#include "core/defs.h" +#include "settings.h" +#include "core/servercontroller.h" + +class WireguardConfigurator +{ +public: + + struct ConnectionData { + QString clientPrivKey; // client private key + QString clientPubKey; // client public key + QString serverPubKey; // tls-auth key + QString pskKey; // preshared key + QString host; // host ip + }; + + static QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, + const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr); + + static QString processConfigWithLocalSettings(QString config); + static QString processConfigWithExportSettings(QString config); + + +private: + static QProcessEnvironment prepareEnv(); + + static ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, + DockerContainer container, ErrorCode *errorCode = nullptr); + + static ConnectionData genClientKeys(); + + static Settings &m_settings(); +}; + +#endif // WIREGUARD_CONFIGURATOR_H diff --git a/client/core/defs.h b/client/core/defs.h index 2b6889f6..4f715006 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -13,7 +13,7 @@ struct ServerCredentials QString password; int port = 22; - bool isValid() { return !hostName.isEmpty() && !userName.isEmpty() && !password.isEmpty() && port > 0; } + bool isValid() const { return !hostName.isEmpty() && !userName.isEmpty() && !password.isEmpty() && port > 0; } }; enum ErrorCode @@ -44,6 +44,7 @@ enum ErrorCode OpenVpnConfigMissing, OpenVpnManagementServerError, EasyRsaError, + ConfigMissing, // Distro errors OpenVpnExecutableMissing, @@ -51,6 +52,7 @@ enum ErrorCode ShadowSocksExecutableMissing, CloakExecutableMissing, AmneziaServiceConnectionFailed, + ExecutableMissing, // VPN errors OpenVpnAdaptersInUseError, @@ -69,6 +71,7 @@ const char key_openvpn_config_data[] = "openvpn_config_data"; const char key_openvpn_config_path[] = "openvpn_config_path"; const char key_shadowsocks_config_data[] = "shadowsocks_config_data"; const char key_cloak_config_data[] = "cloak_config_data"; +const char key_wireguard_config_data[] = "wireguard_config_data"; } diff --git a/client/core/ipcclient.cpp b/client/core/ipcclient.cpp index c8b5ede9..6fa7e61b 100644 --- a/client/core/ipcclient.cpp +++ b/client/core/ipcclient.cpp @@ -48,7 +48,7 @@ QSharedPointer IpcClient::CreatePrivilegedProcess() qWarning() << "IpcProcessInterfaceReplica replica is not connected!"; } - connect(pd->ipcProcess.data(), &IpcProcessInterfaceReplica::destroyed, pd->ipcProcess.data(), [pd](){ + QObject::connect(pd->ipcProcess.data(), &IpcProcessInterfaceReplica::destroyed, pd->ipcProcess.data(), [pd](){ pd->replicaNode->deleteLater(); }); } diff --git a/client/core/scripts_registry.cpp b/client/core/scripts_registry.cpp index f8be1c50..9610f0a6 100644 --- a/client/core/scripts_registry.cpp +++ b/client/core/scripts_registry.cpp @@ -36,6 +36,7 @@ QString amnezia::scriptName(ProtocolScriptType type) case ProtocolScriptType::configure_container: return QLatin1String("configure_container.sh"); case ProtocolScriptType::container_startup: return QLatin1String("start.sh"); case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn"); + case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf"); } } diff --git a/client/core/scripts_registry.h b/client/core/scripts_registry.h index d7522c02..f40a4288 100644 --- a/client/core/scripts_registry.h +++ b/client/core/scripts_registry.h @@ -23,7 +23,8 @@ enum ProtocolScriptType { run_container, configure_container, container_startup, - openvpn_template + openvpn_template, + wireguard_template }; diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index e1507657..0304a28f 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -120,7 +120,8 @@ ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, } ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, - const ServerCredentials &credentials, const QString &file, const QString &path) + const ServerCredentials &credentials, const QString &file, const QString &path, + QSsh::SftpOverwriteMode overwriteMode) { ErrorCode e = ErrorCode::NoError; QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16)); @@ -132,11 +133,29 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, stdOut += data + "\n"; }; - e = runScript(sshParams(credentials), - replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), - genVarsForScript(credentials, container)), cbReadStd, cbReadStd); + if (overwriteMode == QSsh::SftpOverwriteMode::SftpOverwriteExisting) { + e = runScript(sshParams(credentials), + replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), + genVarsForScript(credentials, container)), cbReadStd, cbReadStd); + + if (e) return e; + } + else if (overwriteMode == QSsh::SftpOverwriteMode::SftpAppendToExisting) { + e = runScript(sshParams(credentials), + replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), + genVarsForScript(credentials, container)), cbReadStd, cbReadStd); + + if (e) return e; + + e = runScript(sshParams(credentials), + replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path), + genVarsForScript(credentials, container)), cbReadStd, cbReadStd); + + if (e) return e; + } + else return ErrorCode::NotImplementedError; + - if (e) return e; if (stdOut.contains("Error: No such container:")) { return ErrorCode::ServerContainerMissingError; } @@ -199,15 +218,16 @@ QString ServerController::getTextFileFromContainer(DockerContainer container, if (errorCode) *errorCode = fromSshProcessExitStatus(exitStatus); } + if (errorCode) *errorCode = ErrorCode::NoError; return proc->readAllStandardOutput(); } ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials) { QString caCert = ServerController::getTextFileFromContainer(container, - credentials, amnezia::protocols::openvpn::caCertPath); + credentials, protocols::openvpn::caCertPath); QString taKey = ServerController::getTextFileFromContainer(container, - credentials, amnezia::protocols::openvpn::taKeyPath); + credentials, protocols::openvpn::taKeyPath); if (!caCert.isEmpty() && !taKey.isEmpty()) { return ErrorCode::NoError; @@ -217,7 +237,8 @@ ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const } } -ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath) +ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, + QSsh::SftpOverwriteMode overwriteMode) { SshConnection *client = connectToHost(sshParams(credentials)); if (client->state() != SshConnection::State::Connected) { @@ -508,11 +529,12 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred genVarsForScript(credentials, container, config))); } -ServerController::Vars ServerController:: genVarsForScript(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config) +ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config) { const QJsonObject &openvpnConfig = config.value(config_key::openvpn).toObject(); const QJsonObject &cloakConfig = config.value(config_key::cloak).toObject(); const QJsonObject &ssConfig = config.value(config_key::shadowsocks).toObject(); + const QJsonObject &wireguarConfig = config.value(config_key::wireguard).toObject(); // Vars vars; @@ -520,20 +542,20 @@ ServerController::Vars ServerController:: genVarsForScript(const ServerCredent vars.append({{"$REMOTE_HOST", credentials.hostName}}); // OpenVPN vars - vars.append({{"$VPN_SUBNET_IP", openvpnConfig.value(config_key::subnet_address).toString(amnezia::protocols::vpnDefaultSubnetAddress) }}); - vars.append({{"$VPN_SUBNET_MASK_VAL", openvpnConfig.value(config_key::subnet_mask_val).toString(amnezia::protocols::vpnDefaultSubnetMaskVal) }}); - vars.append({{"$VPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(amnezia::protocols::vpnDefaultSubnetMask) }}); + vars.append({{"$OPENVPN_SUBNET_IP", openvpnConfig.value(config_key::subnet_address).toString(protocols::openvpn::defaultSubnetAddress) }}); + vars.append({{"$OPENVPN_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::openvpn::defaultSubnetCidr) }}); + vars.append({{"$OPENVPN_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::openvpn::defaultSubnetMask) }}); - vars.append({{"$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(amnezia::protocols::openvpn::defaultPort) }}); - vars.append({{"$OPENVPN_TRANSPORT_PROTO", openvpnConfig.value(config_key::transport_proto).toString(amnezia::protocols::openvpn::defaultTransportProto) }}); + vars.append({{"$OPENVPN_PORT", openvpnConfig.value(config_key::port).toString(protocols::openvpn::defaultPort) }}); + vars.append({{"$OPENVPN_TRANSPORT_PROTO", openvpnConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) }}); - bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(amnezia::protocols::openvpn::defaultNcpDisable); + bool isNcpDisabled = openvpnConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable); vars.append({{"$OPENVPN_NCP_DISABLE", isNcpDisabled ? protocols::openvpn::ncpDisableString : "" }}); - vars.append({{"$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(amnezia::protocols::openvpn::defaultCipher) }}); - vars.append({{"$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(amnezia::protocols::openvpn::defaultHash) }}); + vars.append({{"$OPENVPN_CIPHER", openvpnConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher) }}); + vars.append({{"$OPENVPN_HASH", openvpnConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash) }}); - bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(amnezia::protocols::openvpn::defaultTlsAuth); + bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth); vars.append({{"$OPENVPN_TLS_AUTH", isTlsAuth ? protocols::openvpn::tlsAuthString : "" }}); if (!isTlsAuth) { // erase $OPENVPN_TA_KEY, so it will not set in OpenVpnConfigurator::genOpenVpnConfig @@ -541,9 +563,9 @@ ServerController::Vars ServerController:: genVarsForScript(const ServerCredent } // ShadowSocks vars - vars.append({{"$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(amnezia::protocols::shadowsocks::defaultPort) }}); - vars.append({{"$SHADOWSOCKS_LOCAL_PORT", ssConfig.value(config_key::local_port).toString(amnezia::protocols::shadowsocks::defaultLocalProxyPort) }}); - vars.append({{"$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(amnezia::protocols::shadowsocks::defaultCipher) }}); + vars.append({{"$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) }}); + vars.append({{"$SHADOWSOCKS_LOCAL_PORT", ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) }}); + vars.append({{"$SHADOWSOCKS_CIPHER", ssConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher) }}); vars.append({{"$CONTAINER_NAME", amnezia::containerToString(container)}}); vars.append({{"$DOCKERFILE_FOLDER", "/opt/amnezia/" + amnezia::containerToString(container)}}); @@ -552,6 +574,14 @@ ServerController::Vars ServerController:: genVarsForScript(const ServerCredent vars.append({{"$CLOAK_SERVER_PORT", cloakConfig.value(config_key::port).toString(protocols::cloak::defaultPort) }}); vars.append({{"$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) }}); + // Wireguard vars + vars.append({{"$WIREGUARD_SUBNET_IP", openvpnConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) }}); + vars.append({{"$WIREGUARD_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) }}); + vars.append({{"$WIREGUARD_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) }}); + + vars.append({{"$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) }}); + + QString serverIp = Utils::getIPAddress(credentials.hostName); if (!serverIp.isEmpty()) { vars.append({{"$SERVER_IP_ADDRESS", serverIp}}); diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 7ea9244f..9b5b5f40 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -8,6 +8,7 @@ #include "defs.h" #include "protocols/protocols_defs.h" +#include "sftpdefs.h" using namespace amnezia; @@ -35,10 +36,12 @@ public: static ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials); - static ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath); + static ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, + const QString &remotePath, QSsh::SftpOverwriteMode overwriteMode = QSsh::SftpOverwriteMode::SftpOverwriteExisting); static ErrorCode uploadTextFileToContainer(DockerContainer container, - const ServerCredentials &credentials, const QString &file, const QString &path); + const ServerCredentials &credentials, const QString &file, const QString &path, + QSsh::SftpOverwriteMode overwriteMode = QSsh::SftpOverwriteMode::SftpOverwriteExisting); static QString getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); @@ -54,9 +57,10 @@ public: static Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject()); static QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); -private: static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); +private: + static ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); static ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); static ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); diff --git a/client/defines.h b/client/defines.h index ca2579e7..ae54ddfd 100644 --- a/client/defines.h +++ b/client/defines.h @@ -4,7 +4,7 @@ #define APPLICATION_NAME "AmneziaVPN" #define SERVICE_NAME "AmneziaVPN-service" #define ORGANIZATION_NAME "AmneziaVPN.ORG" -#define APP_MAJOR_VERSION "1.7.4" -#define APP_VERSION "1.7.4.0" +#define APP_MAJOR_VERSION "1.7.5" +#define APP_VERSION "1.7.5.3" #endif // DEFINES_H diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 73da3b6c..c47795c0 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -5,12 +5,16 @@ #include #include "debug.h" -#include "openvpnprotocol.h" +#include "defines.h" #include "utils.h" +#include "openvpnprotocol.h" + OpenVpnProtocol::OpenVpnProtocol(const QJsonObject &configuration, QObject* parent) : VpnProtocol(configuration, parent) { + Utils::initializePath(defaultConfigPath()); + readOpenVpnConfiguration(configuration); connect(&m_managementServer, &ManagementServer::readyRead, this, &OpenVpnProtocol::onReadyReadDataFromManagementServer); } @@ -22,6 +26,17 @@ OpenVpnProtocol::~OpenVpnProtocol() QThread::msleep(200); } +QString OpenVpnProtocol::defaultConfigFileName() +{ + qDebug() << "OpenVpnProtocol::defaultConfigFileName" << defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME); + return defaultConfigPath() + QString("/%1.ovpn").arg(APPLICATION_NAME); +} + +QString OpenVpnProtocol::defaultConfigPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config"; +} + void OpenVpnProtocol::stop() { qDebug() << "OpenVpnProtocol::stop()"; @@ -81,7 +96,7 @@ void OpenVpnProtocol::readOpenVpnConfiguration(const QJsonObject &configuration) QFileInfo file(m_configFileName); if (file.fileName().isEmpty()) { - m_configFileName = Utils::defaultVpnConfigFileName(); + m_configFileName = defaultConfigFileName(); } qDebug().noquote() << QString("Set config file: '%1'").arg(configPath()); diff --git a/client/protocols/openvpnprotocol.h b/client/protocols/openvpnprotocol.h index db4b0802..854c2574 100644 --- a/client/protocols/openvpnprotocol.h +++ b/client/protocols/openvpnprotocol.h @@ -22,6 +22,8 @@ public: void stop() override; ErrorCode checkAndSetupTapDriver(); + static QString defaultConfigFileName(); + static QString defaultConfigPath(); protected slots: void onReadyReadDataFromManagementServer(); diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 5b4620bb..6f0bc6c5 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -36,7 +36,7 @@ constexpr char block_outside_dns[] = "block_outside_dns"; constexpr char subnet_address[] = "subnet_address"; constexpr char subnet_mask[] = "subnet_mask"; -constexpr char subnet_mask_val[] = "subnet_mask_val"; +constexpr char subnet_cidr[] = "subnet_cidr"; // proto config keys constexpr char last_config[] = "last_config"; @@ -55,14 +55,16 @@ constexpr char amnezia_wireguard[] = "amnezia-wireguard"; namespace protocols { -constexpr char vpnDefaultSubnetAddress[] = "10.8.0.0"; -constexpr char vpnDefaultSubnetMask[] = "255.255.255.0"; -constexpr char vpnDefaultSubnetMaskVal[] = "24"; + constexpr char UDP[] = "udp"; // case sens constexpr char TCP[] = "tcp"; namespace openvpn { +constexpr char defaultSubnetAddress[] = "10.8.0.0"; +constexpr char defaultSubnetMask[] = "255.255.255.0"; +constexpr char defaultSubnetCidr[] = "24"; + constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt"; constexpr char clientCertPath[] = "/opt/amnezia/openvpn/pki/issued"; constexpr char taKeyPath[] = "/opt/amnezia/openvpn/ta.key"; @@ -96,6 +98,17 @@ constexpr char defaultCipher[] = "chacha20-ietf-poly1305"; } +namespace wireguard { +constexpr char defaultSubnetAddress[] = "10.8.1.0"; +constexpr char defaultSubnetMask[] = "255.255.255.0"; +constexpr char defaultSubnetCidr[] = "24"; + +constexpr char defaultPort[] = "51820"; +constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf"; +constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key"; +constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; + +} } // namespace protocols diff --git a/client/protocols/wireguardprotocol.cpp b/client/protocols/wireguardprotocol.cpp new file mode 100644 index 00000000..4c5b6f0f --- /dev/null +++ b/client/protocols/wireguardprotocol.cpp @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "wireguardprotocol.h" +#include "utils.h" + +WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject* parent) : + VpnProtocol(configuration, parent) +{ + //m_configFile.setFileTemplate(QDir::tempPath() + QDir::separator() + serviceName() + ".conf"); + m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf"); + readWireguardConfiguration(configuration); +} + +WireguardProtocol::~WireguardProtocol() +{ + qDebug() << "WireguardProtocol::~WireguardProtocol()"; + WireguardProtocol::stop(); + QThread::msleep(200); +} + +void WireguardProtocol::stop() +{ + if (!QFileInfo::exists(wireguardExecPath())) { + qCritical() << "Wireguard executable missing!"; + setLastError(ErrorCode::ExecutableMissing); + return; + } + + m_wireguardStopProcess = IpcClient::CreatePrivilegedProcess(); + + if (!m_wireguardStopProcess) { + qCritical() << "IpcProcess replica is not created!"; + setLastError(ErrorCode::AmneziaServiceConnectionFailed); + return; + } + + m_wireguardStopProcess->waitForSource(1000); + if (!m_wireguardStopProcess->isInitialized()) { + qWarning() << "IpcProcess replica is not connected!"; + setLastError(ErrorCode::AmneziaServiceConnectionFailed); + return; + } + + m_wireguardStopProcess->setProgram(wireguardExecPath()); + + + QStringList arguments({"/uninstalltunnelservice", serviceName(), }); + m_wireguardStopProcess->setArguments(arguments); + + qDebug() << arguments.join(" "); + + connect(m_wireguardStopProcess.data(), &IpcProcessInterfaceReplica::errorOccurred, this, [this](QProcess::ProcessError error) { + qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error; + setConnectionState(ConnectionState::Disconnected); + }); + + connect(m_wireguardStopProcess.data(), &IpcProcessInterfaceReplica::stateChanged, this, [this](QProcess::ProcessState newState) { + qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState; + }); + + m_wireguardStopProcess->start(); + + setConnectionState(VpnProtocol::Disconnected); +} + +void WireguardProtocol::readWireguardConfiguration(const QJsonObject &configuration) +{ + if (configuration.contains(config::key_wireguard_config_data)) { + if (!m_configFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qCritical() << "Failed to save wireguard config to" << m_configFile.fileName(); + return; + } + + m_isConfigLoaded = true; + + m_configFile.write(configuration.value(config::key_wireguard_config_data).toString().toUtf8()); + m_configFile.close(); + m_configFileName = m_configFile.fileName(); + + qDebug().noquote() << QString("Set config data") << m_configFileName; + qDebug().noquote() << QString("Set config data") << configuration.value(config::key_wireguard_config_data).toString().toUtf8(); + } +// else if (configuration.contains(config::key_wireguard_config_path)) { +// m_configFileName = configuration.value(config::key_wireguard_config_path).toString(); +// QFileInfo file(m_configFileName); + +// if (file.fileName().isEmpty()) { +// m_configFileName = defaultConfigFileName(); +// } + +// qDebug().noquote() << QString("Set config file: '%1'").arg(configPath()); +// } +} + +//bool WireguardProtocol::openVpnProcessIsRunning() const +//{ +// return Utils::processIsRunning("openvpn"); +//} + +QString WireguardProtocol::configPath() const +{ + return m_configFileName; +} + +void WireguardProtocol::updateRouteGateway(QString line) +{ + // TODO: fix for macos + line = line.split("ROUTE_GATEWAY", QString::SkipEmptyParts).at(1); + if (!line.contains("/")) return; + m_routeGateway = line.split("/", QString::SkipEmptyParts).first(); + m_routeGateway.replace(" ", ""); + qDebug() << "Set VPN route gateway" << m_routeGateway; +} + +QString WireguardProtocol::wireguardExecPath() const +{ +#ifdef Q_OS_WIN + return Utils::executable("wireguard/wireguard", true); +#else + return Utils::executable("/wireguard", true); +#endif +} + +ErrorCode WireguardProtocol::start() +{ + if (!m_isConfigLoaded) { + setLastError(ErrorCode::ConfigMissing); + return lastError(); + } + + //qDebug() << "Start OpenVPN connection"; + WireguardProtocol::stop(); + + if (!QFileInfo::exists(wireguardExecPath())) { + setLastError(ErrorCode::ExecutableMissing); + return lastError(); + } + + if (!QFileInfo::exists(configPath())) { + setLastError(ErrorCode::ConfigMissing); + return lastError(); + } + + setConnectionState(ConnectionState::Connecting); + + m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess(); + + if (!m_wireguardStartProcess) { + //qWarning() << "IpcProcess replica is not created!"; + setLastError(ErrorCode::AmneziaServiceConnectionFailed); + return ErrorCode::AmneziaServiceConnectionFailed; + } + + m_wireguardStartProcess->waitForSource(1000); + if (!m_wireguardStartProcess->isInitialized()) { + qWarning() << "IpcProcess replica is not connected!"; + setLastError(ErrorCode::AmneziaServiceConnectionFailed); + return ErrorCode::AmneziaServiceConnectionFailed; + } + + m_wireguardStartProcess->setProgram(wireguardExecPath()); + + + QStringList arguments({"/installtunnelservice", configPath(), }); + m_wireguardStartProcess->setArguments(arguments); + + qDebug() << arguments.join(" "); + + connect(m_wireguardStartProcess.data(), &IpcProcessInterfaceReplica::errorOccurred, this, [this](QProcess::ProcessError error) { + qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error; + setConnectionState(ConnectionState::Disconnected); + }); + + connect(m_wireguardStartProcess.data(), &IpcProcessInterfaceReplica::stateChanged, this, [this](QProcess::ProcessState newState) { + qDebug() << "WireguardProtocol::WireguardProtocol stateChanged" << newState; + }); + + connect(m_wireguardStartProcess.data(), &IpcProcessInterfaceReplica::finished, this, [this]() { + setConnectionState(ConnectionState::Connected); + }); + + connect(m_wireguardStartProcess.data(), &IpcProcessInterfaceReplica::readyRead, this, [this]() { + QRemoteObjectPendingReply reply = m_wireguardStartProcess->readAll(); + reply.waitForFinished(1000); + qDebug() << "WireguardProtocol::WireguardProtocol readyRead" << reply.returnValue(); + }); + + connect(m_wireguardStartProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, [this]() { + QRemoteObjectPendingReply reply = m_wireguardStartProcess->readAllStandardOutput(); + reply.waitForFinished(1000); + qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardOutput" << reply.returnValue(); + }); + + connect(m_wireguardStartProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardError, this, [this]() { + QRemoteObjectPendingReply reply = m_wireguardStartProcess->readAllStandardError(); + reply.waitForFinished(1000); + qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardError" << reply.returnValue(); + }); + + m_wireguardStartProcess->start(); + + return ErrorCode::NoError; +} + +void WireguardProtocol::updateVpnGateway(const QString &line) +{ +// // line looks like +// // PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM' + +// QStringList params = line.split(","); +// for (const QString &l : params) { +// if (l.contains("ifconfig")) { +// if (l.split(" ").size() == 3) { +// m_vpnLocalAddress = l.split(" ").at(1); +// m_vpnGateway = l.split(" ").at(2); + +// qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway()); +// } +// } + // } +} + +QString WireguardProtocol::serviceName() const +{ + return "AmneziaVPN.WireGuard0"; +} diff --git a/client/protocols/wireguardprotocol.h b/client/protocols/wireguardprotocol.h new file mode 100644 index 00000000..ea205e1e --- /dev/null +++ b/client/protocols/wireguardprotocol.h @@ -0,0 +1,46 @@ +#ifndef WIREGUARDPROTOCOL_H +#define WIREGUARDPROTOCOL_H + +#include +#include +#include +#include +#include + +#include "vpnprotocol.h" +#include "core/ipcclient.h" + +class WireguardProtocol : public VpnProtocol +{ + Q_OBJECT + +public: + explicit WireguardProtocol(const QJsonObject& configuration, QObject* parent = nullptr); + virtual ~WireguardProtocol() override; + + ErrorCode start() override; + void stop() override; + +private: + QString configPath() const; + QString wireguardExecPath() const; + //bool openVpnProcessIsRunning() const; + void readWireguardConfiguration(const QJsonObject &configuration); + + void updateRouteGateway(QString line); + void updateVpnGateway(const QString &line); + QString serviceName() const; + + +private: + QString m_configFileName; + QFile m_configFile; + + QSharedPointer m_wireguardStartProcess; + QSharedPointer m_wireguardStopProcess; + + bool m_isConfigLoaded = false; + +}; + +#endif // WIREGUARDPROTOCOL_H diff --git a/client/resources.qrc b/client/resources.qrc index 98e4cd0b..3c2c26c2 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -62,5 +62,10 @@ server_scripts/openvpn_shadowsocks/start.sh server_scripts/openvpn_shadowsocks/template.ovpn images/folder.png + server_scripts/wireguard/configure_container.sh + server_scripts/wireguard/Dockerfile + server_scripts/wireguard/run_container.sh + server_scripts/wireguard/start.sh + server_scripts/wireguard/template.conf diff --git a/client/server_scripts/openvpn/configure_container.sh b/client/server_scripts/openvpn/configure_container.sh index 0402c328..ceb8c993 100644 --- a/client/server_scripts/openvpn/configure_container.sh +++ b/client/server_scripts/openvpn/configure_container.sh @@ -7,7 +7,7 @@ ca /opt/amnezia/openvpn/ca.crt \\n\ cert /opt/amnezia/openvpn/AmneziaReq.crt \\n\ key /opt/amnezia/openvpn/AmneziaReq.key \\n\ dh /opt/amnezia/openvpn/dh.pem \\n\ -server $VPN_SUBNET_IP $VPN_SUBNET_MASK \\n\ +server $OPENVPN_SUBNET_IP $OPENVPN_SUBNET_MASK \\n\ ifconfig-pool-persist ipp.txt \\n\ duplicate-cn \\n\ keepalive 10 120 \\n\ diff --git a/client/server_scripts/openvpn/start.sh b/client/server_scripts/openvpn/start.sh index 696ce73f..f9783557 100644 --- a/client/server_scripts/openvpn/start.sh +++ b/client/server_scripts/openvpn/start.sh @@ -3,6 +3,7 @@ # This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts echo "Container startup" +ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up if [ ! -c /dev/net/tun ]; then mkdir -p /dev/net; mknod /dev/net/tun c 10 200; fi @@ -12,10 +13,10 @@ iptables -A FORWARD -i tun0 -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT # Allow forwarding traffic only from the VPN. -iptables -A FORWARD -i tun0 -o eth0 -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -j ACCEPT +iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT -iptables -t nat -A POSTROUTING -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -o eth0 -j MASQUERADE +iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE # kill daemons in case of restart killall -KILL openvpn diff --git a/client/server_scripts/openvpn_cloak/configure_container.sh b/client/server_scripts/openvpn_cloak/configure_container.sh index 21209892..4a564b0a 100644 --- a/client/server_scripts/openvpn_cloak/configure_container.sh +++ b/client/server_scripts/openvpn_cloak/configure_container.sh @@ -7,7 +7,7 @@ ca /opt/amnezia/openvpn/ca.crt \\n\ cert /opt/amnezia/openvpn/AmneziaReq.crt \\n\ key /opt/amnezia/openvpn/AmneziaReq.key \\n\ dh /opt/amnezia/openvpn/dh.pem \\n\ -server $VPN_SUBNET_IP $VPN_SUBNET_MASK \\n\ +server $OPENVPN_SUBNET_IP $OPENVPN_SUBNET_MASK \\n\ ifconfig-pool-persist ipp.txt \\n\ duplicate-cn \\n\ keepalive 10 120 \\n\ diff --git a/client/server_scripts/openvpn_cloak/start.sh b/client/server_scripts/openvpn_cloak/start.sh index 711733ba..a9b7a8c5 100644 --- a/client/server_scripts/openvpn_cloak/start.sh +++ b/client/server_scripts/openvpn_cloak/start.sh @@ -3,6 +3,7 @@ # This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts echo "Container startup" +ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up if [ ! -c /dev/net/tun ]; then mkdir -p /dev/net; mknod /dev/net/tun c 10 200; fi @@ -12,10 +13,10 @@ iptables -A FORWARD -i tun0 -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT # Allow forwarding traffic only from the VPN. -iptables -A FORWARD -i tun0 -o eth0 -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -j ACCEPT +iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT -iptables -t nat -A POSTROUTING -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -o eth0 -j MASQUERADE +iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE # kill daemons in case of restart killall -KILL openvpn diff --git a/client/server_scripts/openvpn_shadowsocks/configure_container.sh b/client/server_scripts/openvpn_shadowsocks/configure_container.sh index a7a55250..ba34520d 100644 --- a/client/server_scripts/openvpn_shadowsocks/configure_container.sh +++ b/client/server_scripts/openvpn_shadowsocks/configure_container.sh @@ -7,7 +7,7 @@ ca /opt/amnezia/openvpn/ca.crt \\n\ cert /opt/amnezia/openvpn/AmneziaReq.crt \\n\ key /opt/amnezia/openvpn/AmneziaReq.key \\n\ dh /opt/amnezia/openvpn/dh.pem \\n\ -server $VPN_SUBNET_IP $VPN_SUBNET_MASK \\n\ +server $OPENVPN_SUBNET_IP $OPENVPN_SUBNET_MASK \\n\ ifconfig-pool-persist ipp.txt \\n\ duplicate-cn \\n\ keepalive 10 120 \\n\ diff --git a/client/server_scripts/openvpn_shadowsocks/start.sh b/client/server_scripts/openvpn_shadowsocks/start.sh index 8ec252be..1b75b968 100644 --- a/client/server_scripts/openvpn_shadowsocks/start.sh +++ b/client/server_scripts/openvpn_shadowsocks/start.sh @@ -3,6 +3,7 @@ # This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts echo "Container startup" +ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up if [ ! -c /dev/net/tun ]; then mkdir -p /dev/net; mknod /dev/net/tun c 10 200; fi @@ -12,10 +13,10 @@ iptables -A FORWARD -i tun0 -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT # Allow forwarding traffic only from the VPN. -iptables -A FORWARD -i tun0 -o eth0 -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -j ACCEPT +iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT -iptables -t nat -A POSTROUTING -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -o eth0 -j MASQUERADE +iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE # kill daemons in case of restart killall -KILL openvpn diff --git a/client/server_scripts/wireguard/Dockerfile b/client/server_scripts/wireguard/Dockerfile new file mode 100644 index 00000000..e2d8a7ac --- /dev/null +++ b/client/server_scripts/wireguard/Dockerfile @@ -0,0 +1,47 @@ +#FROM alpine:latest +FROM itsthenetwork/alpine-tcpdump:latest + +LABEL maintainer="AmneziaVPN" + +#Install required packages +RUN apk add --no-cache curl wireguard-tools dumb-init +RUN apk --update upgrade --no-cache + +RUN mkdir -p /opt/amnezia +RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh +RUN chmod a+x /opt/amnezia/start.sh + +# Tune network +RUN echo -e " \n\ + fs.file-max = 51200 \n\ + \n\ + net.core.rmem_max = 67108864 \n\ + net.core.wmem_max = 67108864 \n\ + net.core.netdev_max_backlog = 250000 \n\ + net.core.somaxconn = 4096 \n\ + \n\ + net.ipv4.tcp_syncookies = 1 \n\ + net.ipv4.tcp_tw_reuse = 1 \n\ + net.ipv4.tcp_tw_recycle = 0 \n\ + net.ipv4.tcp_fin_timeout = 30 \n\ + net.ipv4.tcp_keepalive_time = 1200 \n\ + net.ipv4.ip_local_port_range = 10000 65000 \n\ + net.ipv4.tcp_max_syn_backlog = 8192 \n\ + net.ipv4.tcp_max_tw_buckets = 5000 \n\ + net.ipv4.tcp_fastopen = 3 \n\ + net.ipv4.tcp_mem = 25600 51200 102400 \n\ + net.ipv4.tcp_rmem = 4096 87380 67108864 \n\ + net.ipv4.tcp_wmem = 4096 65536 67108864 \n\ + net.ipv4.tcp_mtu_probing = 1 \n\ + net.ipv4.tcp_congestion_control = hybla \n\ + # for low-latency network, use cubic instead \n\ + # net.ipv4.tcp_congestion_control = cubic \n\ + " | sed -e 's/^\s\+//g' | tee -a /etc/sysctl.conf && \ + mkdir -p /etc/security && \ + echo -e " \n\ + * soft nofile 51200 \n\ + * hard nofile 51200 \n\ + " | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf + +ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ] +CMD [ "" ] diff --git a/client/server_scripts/wireguard/configure_container.sh b/client/server_scripts/wireguard/configure_container.sh new file mode 100644 index 00000000..ecb72e6c --- /dev/null +++ b/client/server_scripts/wireguard/configure_container.sh @@ -0,0 +1,13 @@ +# Wireguard config +sudo docker exec -i $CONTAINER_NAME bash -c '\ +mkdir -p /opt/amnezia/wireguard; \ +cd /opt/amnezia/wireguard || exit 1; \ +WIREGUARD_SERVER_PRIVATE_KEY=$(wg genkey) && echo $WIREGUARD_SERVER_PRIVATE_KEY > /opt/amnezia/wireguard/wireguard_server_private_key.key; \ +WIREGUARD_SERVER_PUBLIC_KEY=$(echo $WIREGUARD_SERVER_PRIVATE_KEY | wg pubkey) && echo $WIREGUARD_SERVER_PUBLIC_KEY > /opt/amnezia/wireguard/wireguard_server_public_key.key; \ +WIREGUARD_PSK=$(wg genpsk) && echo $WIREGUARD_PSK > /opt/amnezia/wireguard/wireguard_psk.key; \ +echo -e "\ +[Interface]\\n\ +PrivateKey = $WIREGUARD_SERVER_PRIVATE_KEY \\n\ +Address = $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR \\n\ +ListenPort = $WIREGUARD_SERVER_PORT \\n\ +" >/opt/amnezia/wireguard/wg0.conf' diff --git a/client/server_scripts/wireguard/run_container.sh b/client/server_scripts/wireguard/run_container.sh new file mode 100644 index 00000000..3d4db2e6 --- /dev/null +++ b/client/server_scripts/wireguard/run_container.sh @@ -0,0 +1,15 @@ +# Run container +sudo docker run -d \ +--restart always \ +--privileged \ +--cap-add=NET_ADMIN \ +--cap-add=SYS_MODULE \ +-p $WIREGUARD_SERVER_PORT:$WIREGUARD_SERVER_PORT/udp \ +-v /lib/modules:/lib/modules \ +--sysctl="net.ipv4.conf.all.src_valid_mark=1" \ +--name $CONTAINER_NAME \ +$CONTAINER_NAME + +# Prevent to route packets outside of the container in case if server behind of the NAT +#sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up" + diff --git a/client/server_scripts/wireguard/start.sh b/client/server_scripts/wireguard/start.sh new file mode 100644 index 00000000..aebd283e --- /dev/null +++ b/client/server_scripts/wireguard/start.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts + +echo "Container startup" +#ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up + +# kill daemons in case of restart +wg-quick down /opt/amnezia/wireguard/wg0.conf + +# start daemons if configured +if [ -f /opt/amnezia/wireguard/wg0.conf ]; then (wg-quick up /opt/amnezia/wireguard/wg0.conf); fi + +# Allow traffic on the TUN interface. +iptables -A INPUT -i wg0 -j ACCEPT +iptables -A FORWARD -i wg0 -j ACCEPT +iptables -A OUTPUT -o wg0 -j ACCEPT + +# Allow forwarding traffic only from the VPN. +iptables -A FORWARD -i wg0 -o eth0 -s $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_MASK_CIDR -j ACCEPT +iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT + +iptables -t nat -A POSTROUTING -s $WIREGUARD_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE + +tail -f /dev/null diff --git a/client/server_scripts/wireguard/template.conf b/client/server_scripts/wireguard/template.conf new file mode 100644 index 00000000..19217947 --- /dev/null +++ b/client/server_scripts/wireguard/template.conf @@ -0,0 +1,11 @@ +[Interface] +Address = 10.8.1.2/32 +DNS = 1.1.1.1, 1.0.0.1 +PrivateKey = $WIREGUARD_CLIENT_PRIVATE_KEY + +[Peer] +PublicKey = $WIREGUARD_SERVER_PUBLIC_KEY +PresharedKey = $WIREGUARD_PSK +AllowedIPs = 0.0.0.0/0, ::/0 +Endpoint = $SERVER_IP_ADDRESS:$WIREGUARD_SERVER_PORT +PersistentKeepalive = 25 diff --git a/client/settings.h b/client/settings.h index 68947855..09cd6fdb 100644 --- a/client/settings.h +++ b/client/settings.h @@ -78,17 +78,6 @@ public: RouteMode routeMode() const { return static_cast(m_settings.value("Conf/routeMode", 0).toInt()); } void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); } - // bool customRouting() const { return m_settings.value("Conf/customRouting", false).toBool(); } -// void setCustomRouting(bool customRouting) { m_settings.setValue("Conf/customRouting", customRouting); } - -// // list of sites to pass blocking added by user -// QStringList customSites() { return m_settings.value("Conf/customSites").toStringList(); } -// void setCustomSites(const QStringList &customSites) { m_settings.setValue("Conf/customSites", customSites); } - -// // list of ips to pass blocking generated from customSites -// QStringList customIps() { return m_settings.value("Conf/customIps").toStringList(); } -// void setCustomIps(const QStringList &customIps) { m_settings.setValue("Conf/customIps", customIps); } - QVariantMap vpnSites(RouteMode mode) const { return m_settings.value("Conf/" + routeModeString(mode)).toMap(); } void setVpnSites(RouteMode mode, const QVariantMap &sites) { m_settings.setValue("Conf/"+ routeModeString(mode), sites); m_settings.sync(); } @@ -100,16 +89,6 @@ public: void removeVpnSites(RouteMode mode, const QStringList &sites); -// QVariantMap vpnForwardSites() const { return m_settings.value("Conf/vpnForwardSites").toMap(); } -// void setVpnForwardSites(const QVariantMap &sites) { m_settings.setValue("Conf/vpnForwardSites", sites); } -// void addVpnForwardSite(const QString &site, const QString &ip); -// QStringList getVpnForwardIps() const; - -// QVariantMap vpnExceptSites() const { return m_settings.value("Conf/vpnExceptSites").toMap(); } -// void setVpnExceptSites(const QVariantMap &sites) { m_settings.setValue("Conf/vpnExceptSites", sites); } -// void addVpnExceptSite(const QString &site, const QString &ip); -// QStringList getVpnExceptIps() const; - QString primaryDns() const; QString secondaryDns() const; diff --git a/client/ui/mainwindow.cpp b/client/ui/mainwindow.cpp index 3826b157..4085b306 100644 --- a/client/ui/mainwindow.cpp +++ b/client/ui/mainwindow.cpp @@ -16,11 +16,11 @@ #include #include - +#include "configurators/cloak_configurator.h" #include "configurators/vpn_configurator.h" #include "configurators/openvpn_configurator.h" #include "configurators/shadowsocks_configurator.h" -#include "configurators/cloak_configurator.h" +#include "configurators/ssh_configurator.h" #include "core/servercontroller.h" #include "core/server_defs.h" @@ -113,7 +113,6 @@ MainWindow::MainWindow(QWidget *parent) : qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION); qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture()); - Utils::initializePath(Utils::configPath()); m_vpnConnection = new VpnConnection(this); connect(m_vpnConnection, SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); @@ -129,8 +128,6 @@ MainWindow::MainWindow(QWidget *parent) : }); } - qDebug().noquote() << QString("Default config: %1").arg(Utils::defaultVpnConfigFileName()); - m_ipAddressValidator.setRegExp(Utils::ipAddressRegExp()); m_ipAddressPortValidator.setRegExp(Utils::ipAddressPortRegExp()); m_ipNetwok24Validator.setRegExp(Utils::ipNetwork24RegExp()); @@ -378,6 +375,9 @@ void MainWindow::keyPressEvent(QKeyEvent *event) selectedDockerContainer = m_settings.defaultContainer(selectedServerIndex); goToPage(Page::ServerVpnProtocols); break; + case Qt::Key_T: + SshConfigurator::openSshTerminal(m_settings.serverCredentials(m_settings.defaultServerIndex())); + break; case Qt::Key_Escape: if (currentPage() == Page::Vpn) break; if (currentPage() == Page::ServerConfiguring) break; @@ -470,7 +470,7 @@ void MainWindow::onPushButtonNewServerConnect(bool) } if (key.contains("OPENSSH") && key.contains("BEGIN") && key.contains("PRIVATE KEY")) { - key = OpenVpnConfigurator::convertOpenSShKey(key); + key = SshConfigurator::convertOpenSShKey(key); } serverCredentials.password = key; @@ -1272,18 +1272,20 @@ void MainWindow::setupProtocolsPageConnections() { QJsonObject openvpnConfig; - // default buttons + // all containers QList containers { DockerContainer::OpenVpn, DockerContainer::OpenVpnOverShadowSocks, - DockerContainer::OpenVpnOverCloak + DockerContainer::OpenVpnOverCloak, + DockerContainer::WireGuard }; // default buttons QList defaultButtons { ui->pushButton_proto_openvpn_cont_default, ui->pushButton_proto_ss_openvpn_cont_default, - ui->pushButton_proto_cloak_openvpn_cont_default + ui->pushButton_proto_cloak_openvpn_cont_default, + ui->pushButton_proto_wireguard_cont_default }; for (int i = 0; i < containers.size(); ++i) { @@ -1297,7 +1299,8 @@ void MainWindow::setupProtocolsPageConnections() QList installButtons { ui->pushButton_proto_openvpn_cont_install, ui->pushButton_proto_ss_openvpn_cont_install, - ui->pushButton_proto_cloak_openvpn_cont_install + ui->pushButton_proto_cloak_openvpn_cont_install, + ui->pushButton_proto_wireguard_cont_install }; for (int i = 0; i < containers.size(); ++i) { @@ -1338,7 +1341,8 @@ void MainWindow::setupProtocolsPageConnections() QList shareButtons { ui->pushButton_proto_openvpn_cont_share, ui->pushButton_proto_ss_openvpn_cont_share, - ui->pushButton_proto_cloak_openvpn_cont_share + ui->pushButton_proto_cloak_openvpn_cont_share, + ui->pushButton_proto_wireguard_cont_share }; for (int i = 0; i < containers.size(); ++i) { @@ -1827,6 +1831,10 @@ void MainWindow::onPushButtonAddCustomSitesClicked() m_vpnConnection->addRoutes(QStringList() << ip); m_vpnConnection->flushDns(); } + else if (Utils::ipAddressWithSubnetRegExp().exactMatch(newSite)) { + m_vpnConnection->addRoutes(QStringList() << newSite); + m_vpnConnection->flushDns(); + } updateSitesPage(); }; @@ -1961,33 +1969,58 @@ void MainWindow::updateProtocolsPage() ui->progressBar_protocols_container_reinstall->hide(); auto containers = m_settings.containers(selectedServerIndex); - + DockerContainer defaultContainer = m_settings.defaultContainer(selectedServerIndex); bool haveAuthData = m_settings.haveAuthData(selectedServerIndex); - DockerContainer defaultContainer = m_settings.defaultContainer(selectedServerIndex); - ui->pushButton_proto_cloak_openvpn_cont_default->setChecked(defaultContainer == DockerContainer::OpenVpnOverCloak); - ui->pushButton_proto_ss_openvpn_cont_default->setChecked(defaultContainer == DockerContainer::OpenVpnOverShadowSocks); - ui->pushButton_proto_openvpn_cont_default->setChecked(defaultContainer == DockerContainer::OpenVpn); + // all containers + QList allContainers { + DockerContainer::OpenVpn, + DockerContainer::OpenVpnOverShadowSocks, + DockerContainer::OpenVpnOverCloak, + DockerContainer::WireGuard + }; - ui->pushButton_proto_cloak_openvpn_cont_default->setVisible(haveAuthData && containers.contains(DockerContainer::OpenVpnOverCloak)); - ui->pushButton_proto_ss_openvpn_cont_default->setVisible(haveAuthData && containers.contains(DockerContainer::OpenVpnOverShadowSocks)); - ui->pushButton_proto_openvpn_cont_default->setVisible(haveAuthData && containers.contains(DockerContainer::OpenVpn)); + // install buttons + QList installButtons { + ui->pushButton_proto_openvpn_cont_install, + ui->pushButton_proto_ss_openvpn_cont_install, + ui->pushButton_proto_cloak_openvpn_cont_install, + ui->pushButton_proto_wireguard_cont_install + }; - ui->pushButton_proto_cloak_openvpn_cont_share->setVisible(haveAuthData && containers.contains(DockerContainer::OpenVpnOverCloak)); - ui->pushButton_proto_ss_openvpn_cont_share->setVisible(haveAuthData && containers.contains(DockerContainer::OpenVpnOverShadowSocks)); - ui->pushButton_proto_openvpn_cont_share->setVisible(haveAuthData && containers.contains(DockerContainer::OpenVpn)); + // default buttons + QList defaultButtons { + ui->pushButton_proto_openvpn_cont_default, + ui->pushButton_proto_ss_openvpn_cont_default, + ui->pushButton_proto_cloak_openvpn_cont_default, + ui->pushButton_proto_wireguard_cont_default + }; - ui->pushButton_proto_cloak_openvpn_cont_install->setChecked(containers.contains(DockerContainer::OpenVpnOverCloak)); - ui->pushButton_proto_ss_openvpn_cont_install->setChecked(containers.contains(DockerContainer::OpenVpnOverShadowSocks)); - ui->pushButton_proto_openvpn_cont_install->setChecked(containers.contains(DockerContainer::OpenVpn)); + // share buttons + QList shareButtons { + ui->pushButton_proto_openvpn_cont_share, + ui->pushButton_proto_ss_openvpn_cont_share, + ui->pushButton_proto_cloak_openvpn_cont_share, + ui->pushButton_proto_wireguard_cont_share + }; - ui->pushButton_proto_cloak_openvpn_cont_install->setEnabled(haveAuthData); - ui->pushButton_proto_ss_openvpn_cont_install->setEnabled(haveAuthData); - ui->pushButton_proto_openvpn_cont_install->setEnabled(haveAuthData); + // frames + QList frames { + ui->frame_openvpn_settings, + ui->frame_openvpn_ss_settings, + ui->frame_openvpn_ss_cloak_settings, + ui->frame_wireguard_settings + }; - ui->frame_openvpn_ss_cloak_settings->setVisible(containers.contains(DockerContainer::OpenVpnOverCloak)); - ui->frame_openvpn_ss_settings->setVisible(containers.contains(DockerContainer::OpenVpnOverShadowSocks)); - ui->frame_openvpn_settings->setVisible(containers.contains(DockerContainer::OpenVpn)); + for (int i = 0; i < allContainers.size(); ++i) { + defaultButtons.at(i)->setChecked(defaultContainer == allContainers.at(i)); + defaultButtons.at(i)->setVisible(haveAuthData && containers.contains(allContainers.at(i))); + shareButtons.at(i)->setVisible(haveAuthData && containers.contains(allContainers.at(i))); + installButtons.at(i)->setChecked(containers.contains(allContainers.at(i))); + installButtons.at(i)->setEnabled(haveAuthData); + frames.at(i)->setVisible(containers.contains(allContainers.at(i))); + + } } void MainWindow::updateOpenVpnPage(const QJsonObject &openvpnConfig, DockerContainer container, bool haveAuthData) @@ -2000,7 +2033,7 @@ void MainWindow::updateOpenVpnPage(const QJsonObject &openvpnConfig, DockerConta ui->radioButton_proto_openvpn_tcp->setEnabled(true); ui->lineEdit_proto_openvpn_subnet->setText(openvpnConfig.value(config_key::subnet_address). - toString(protocols::vpnDefaultSubnetAddress)); + toString(protocols::openvpn::defaultSubnetAddress)); QString trasnsport = openvpnConfig.value(config_key::transport_proto). toString(protocols::openvpn::defaultTransportProto); diff --git a/client/ui/mainwindow.ui b/client/ui/mainwindow.ui index ac13df52..6fb5b318 100644 --- a/client/ui/mainwindow.ui +++ b/client/ui/mainwindow.ui @@ -274,7 +274,7 @@ QPushButton:hover { - 13 + 8 @@ -2578,6 +2578,127 @@ border: none; + + + + + 0 + 100 + + + + + + + + QLayout::SetMinAndMaxSize + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + + + + 0 + 0 + + + + + 0 + 24 + + + + WireGuard + + + + + + + + 0 + 0 + + + + + 24 + 24 + + + + PointingHandCursor + + + + + + true + + + + + + + + + + + + + + 130 + 0 + + + + + 130 + 16777215 + + + + Port + + + + + + + + 185 + 0 + + + + + 185 + 16777215 + + + + + + + + + + @@ -5346,6 +5467,161 @@ QPushButton:!checked { + + + + + 0 + 100 + + + + + + + + QLayout::SetMinAndMaxSize + + + + + + + WireGuard container + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + QPushButton { + background: transparent; + image: url(:/images/check.png); + padding: 0px; + margin: 0px; +} +QPushButton:checked { + image: url(:/images/check.png); +} +QPushButton:!checked { + image: url(:/images/uncheck.png); +} + + + + + + + true + + + false + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +image: url(:/images/share.png); +padding: 0px; +margin: 0px; + + + + + + + + + + + 36 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + QPushButton { + background: transparent; + padding: 0px; + margin: 0px; +} +QPushButton:checked { + image: url(:/images/connect_button_connected.png); +} +QPushButton:!checked { + image: url(:/images/connect_button_disconnected.png); +} + + + + + + + true + + + + + + + + + + + + PointingHandCursor + + + WireGuard settings + + + + + + + + + diff --git a/client/utils.cpp b/client/utils.cpp index 4f20920b..42056e94 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -23,11 +23,6 @@ QString Utils::getRandomString(int len) return randomString; } -QString Utils::defaultVpnConfigFileName() -{ - return configPath() + QString("/%1.ovpn").arg(APPLICATION_NAME); -} - QString Utils::systemLogPath() { #ifdef Q_OS_WIN @@ -54,11 +49,6 @@ bool Utils::initializePath(const QString& path) return true; } -QString Utils::configPath() -{ - return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config"; -} - bool Utils::createEmptyFile(const QString& path) { QFile f(path); diff --git a/client/utils.h b/client/utils.h index 9d2a9d99..3a0ef2ce 100644 --- a/client/utils.h +++ b/client/utils.h @@ -13,8 +13,6 @@ class Utils { public: static QString getRandomString(int len); - static QString configPath(); - static QString defaultVpnConfigFileName(); static QString executable(const QString& baseName, bool absPath); static QString systemLogPath(); static bool createEmptyFile(const QString& path); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 9c58b735..a7ec5f04 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include "ipc.h" #include "core/ipcclient.h" @@ -43,7 +45,7 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state) if (state == VpnProtocol::Connected){ IpcClient::Interface()->flushDns(); - if (m_settings.routeMode() == Settings::VpnOnlyForwardSites) { + if (m_settings.routeMode() != Settings::VpnAllSites) { IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0"); //qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size(); } @@ -55,6 +57,10 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state) IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), m_settings.getVpnIps(Settings::VpnOnlyForwardSites)); } else if (m_settings.routeMode() == Settings::VpnAllExceptSites) { + IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1"); + IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1"); + + IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress()); IpcClient::Interface()->routeAddList(m_vpnProtocol->routeGateway(), m_settings.getVpnIps(Settings::VpnAllExceptSites)); } @@ -71,6 +77,11 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state) emit connectionStateChanged(state); } +const QString &VpnConnection::remoteAddress() const +{ + return m_remoteAddress; +} + QSharedPointer VpnConnection::vpnProtocol() const { return m_vpnProtocol; @@ -159,6 +170,10 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, configData = ShadowSocksConfigurator::genShadowSocksConfig(credentials, container, containerConfig, &e); } + else if (proto == Protocol::WireGuard) { + configData = WireguardConfigurator::genWireguardConfig(credentials, + container, containerConfig, &e); + } if (errorCode && e) { *errorCode = e; @@ -197,7 +212,7 @@ ErrorCode VpnConnection::createVpnConfiguration(int serverIndex, return errorCode; } - QFile file(Utils::defaultVpnConfigFileName()); + QFile file(OpenVpnProtocol::defaultConfigFileName()); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)){ QTextStream stream(&file); stream << openVpnConfigData << endl; @@ -226,6 +241,13 @@ ErrorCode VpnConnection::createVpnConfiguration(int serverIndex, m_vpnConfiguration.insert(config::key_cloak_config_data, cloakConfigData); } + if (container == DockerContainer::WireGuard) { + QString wgConfigData = createVpnConfigurationForProto( + serverIndex, credentials, container, containerConfig, Protocol::WireGuard, &errorCode); + + m_vpnConfiguration.insert(config::key_wireguard_config_data, wgConfigData); + } + //qDebug().noquote() << "VPN config" << QJsonDocument(m_vpnConfiguration).toJson(); return ErrorCode::NoError; } @@ -233,11 +255,15 @@ ErrorCode VpnConnection::createVpnConfiguration(int serverIndex, ErrorCode VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig) { - qDebug() << "СonnectToVpn, Route mode is" << m_settings.routeMode(); + qDebug() << QString("СonnectToVpn, Server index is %1, container is %2, route mode is") + .arg(serverIndex).arg(containerToString(container)) << m_settings.routeMode(); + m_remoteAddress = credentials.hostName; emit connectionStateChanged(VpnProtocol::Connecting); - ServerController::setupServerFirewall(credentials); + if (credentials.isValid()) { + ServerController::setupServerFirewall(credentials); + } if (m_vpnProtocol) { disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); @@ -287,6 +313,15 @@ ErrorCode VpnConnection::connectToVpn(int serverIndex, return e; } } + else if (container == DockerContainer::WireGuard) { + ErrorCode e = createVpnConfiguration(serverIndex, credentials, DockerContainer::WireGuard, containerConfig); + if (e) { + emit connectionStateChanged(VpnProtocol::Error); + return e; + } + + m_vpnProtocol.reset(new WireguardProtocol(m_vpnConfiguration)); + } connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState))); diff --git a/client/vpnconnection.h b/client/vpnconnection.h index a9790f70..23d377c3 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -48,6 +48,8 @@ public: void deleteRoutes(const QStringList &ips); void flushDns(); + const QString &remoteAddress() const; + signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void connectionStateChanged(VpnProtocol::ConnectionState state); @@ -66,6 +68,7 @@ private: Settings m_settings; QJsonObject m_vpnConfiguration; QJsonObject m_routeMode; + QString m_remoteAddress; }; diff --git a/ipc/ipcinterface.rep b/ipc/ipcinterface.rep index 1fcc4b4d..4fe47517 100644 --- a/ipc/ipcinterface.rep +++ b/ipc/ipcinterface.rep @@ -29,12 +29,14 @@ class IpcProcessInterface SLOT( setProgram(const QString &program) ); SLOT( setWorkingDirectory(const QString &dir) ); + SLOT( QByteArray readAll() ); SLOT( QByteArray readAllStandardError() ); SLOT( QByteArray readAllStandardOutput() ); SIGNAL( errorOccurred(QProcess::ProcessError error) ); SIGNAL( finished(int exitCode, QProcess::ExitStatus exitStatus) ); + SIGNAL( readyRead() ); SIGNAL( readyReadStandardError() ); SIGNAL( readyReadStandardOutput() ); SIGNAL( started() ); diff --git a/ipc/ipcserverprocess.cpp b/ipc/ipcserverprocess.cpp index 547834fd..3fa4eb3c 100644 --- a/ipc/ipcserverprocess.cpp +++ b/ipc/ipcserverprocess.cpp @@ -7,6 +7,7 @@ IpcServerProcess::IpcServerProcess(QObject *parent) : { connect(m_process.data(), &QProcess::errorOccurred, this, &IpcServerProcess::errorOccurred); connect(m_process.data(), QOverload::of(&QProcess::finished), this, &IpcServerProcess::finished); + connect(m_process.data(), &QProcess::readyRead, this, &IpcServerProcess::readyRead); connect(m_process.data(), &QProcess::readyReadStandardError, this, &IpcServerProcess::readyReadStandardError); connect(m_process.data(), &QProcess::readyReadStandardOutput, this, &IpcServerProcess::readyReadStandardOutput); connect(m_process.data(), &QProcess::started, this, &IpcServerProcess::started); @@ -88,6 +89,11 @@ void IpcServerProcess::setWorkingDirectory(const QString &dir) m_process->setWorkingDirectory(dir); } +QByteArray IpcServerProcess::readAll() +{ + return m_process->readAll(); +} + QByteArray IpcServerProcess::readAllStandardError() { return m_process->readAllStandardError(); diff --git a/ipc/ipcserverprocess.h b/ipc/ipcserverprocess.h index 61b2579e..ccbba354 100644 --- a/ipc/ipcserverprocess.h +++ b/ipc/ipcserverprocess.h @@ -23,6 +23,7 @@ public: void setProgram(const QString &program) override; void setWorkingDirectory(const QString &dir) override; + QByteArray readAll() override; QByteArray readAllStandardError() override; QByteArray readAllStandardOutput() override;