From 85b6b06cc9d5b0d79e5d9d5feb31303368d1c746 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sun, 4 Apr 2021 23:12:36 +0300 Subject: [PATCH] - no dockerhub - trafic masking --- client/client.pro | 13 +- client/configurators/cloak_configurator.cpp | 45 ++ client/configurators/cloak_configurator.h | 18 + .../openvpn_configurator.cpp} | 98 ++-- .../openvpn_configurator.h} | 15 +- client/core/defs.h | 23 +- client/core/scripts_registry.cpp | 60 +++ client/core/scripts_registry.h | 34 ++ client/core/server_defs.cpp | 16 + client/core/server_defs.h | 19 + client/core/servercontroller.cpp | 476 ++++++++++++------ client/core/servercontroller.h | 36 +- client/protocols/openvpnovercloakprotocol.cpp | 105 ++++ client/protocols/openvpnovercloakprotocol.h | 30 ++ client/protocols/protocols_defs.h | 36 ++ client/protocols/shadowsocksvpnprotocol.cpp | 7 +- client/resources.qrc | 9 +- client/server_scripts/build_container.sh | 1 + client/server_scripts/install_docker.sh | 6 + .../server_scripts/openvpn_cloak/Dockerfile | 52 ++ .../openvpn_cloak/configure_container.sh | 84 ++++ client/server_scripts/openvpn_cloak/start.sh | 23 + .../openvpn_cloak/template.ovpn | 35 ++ client/server_scripts/prepare_host.sh | 3 + client/server_scripts/setup_host_firewall.sh | 45 +- .../setup_shadowsocks_server.sh | 17 +- client/ui/mainwindow.cpp | 5 +- client/ui/mainwindow.ui | 6 +- client/utils.cpp | 13 + client/utils.h | 2 + client/vpnconnection.cpp | 30 +- 31 files changed, 1106 insertions(+), 256 deletions(-) create mode 100644 client/configurators/cloak_configurator.cpp create mode 100644 client/configurators/cloak_configurator.h rename client/{core/openvpnconfigurator.cpp => configurators/openvpn_configurator.cpp} (73%) rename client/{core/openvpnconfigurator.h => configurators/openvpn_configurator.h} (79%) create mode 100644 client/core/scripts_registry.cpp create mode 100644 client/core/scripts_registry.h create mode 100644 client/core/server_defs.cpp create mode 100644 client/core/server_defs.h create mode 100644 client/protocols/openvpnovercloakprotocol.cpp create mode 100644 client/protocols/openvpnovercloakprotocol.h create mode 100644 client/protocols/protocols_defs.h create mode 100644 client/server_scripts/build_container.sh create mode 100644 client/server_scripts/install_docker.sh create mode 100644 client/server_scripts/openvpn_cloak/Dockerfile create mode 100644 client/server_scripts/openvpn_cloak/configure_container.sh create mode 100644 client/server_scripts/openvpn_cloak/start.sh create mode 100644 client/server_scripts/openvpn_cloak/template.ovpn create mode 100644 client/server_scripts/prepare_host.sh diff --git a/client/client.pro b/client/client.pro index 9ed6ac10..e56f69fa 100644 --- a/client/client.pro +++ b/client/client.pro @@ -13,14 +13,19 @@ include("3rd/QRCodeGenerator/QRCodeGenerator.pri") HEADERS += \ ../ipc/ipc.h \ + configurators/cloak_configurator.h \ core/defs.h \ core/errorstrings.h \ core/ipcclient.h \ - core/openvpnconfigurator.h \ + configurators/openvpn_configurator.h \ + core/scripts_registry.h \ + core/server_defs.h \ core/servercontroller.h \ debug.h \ defines.h \ managementserver.h \ + protocols/openvpnovercloakprotocol.h \ + protocols/protocols_defs.h \ protocols/shadowsocksvpnprotocol.h \ settings.h \ ui/Controls/SlidingStackedWidget.h \ @@ -32,12 +37,16 @@ HEADERS += \ protocols/openvpnprotocol.h \ SOURCES += \ + configurators/cloak_configurator.cpp \ core/ipcclient.cpp \ - core/openvpnconfigurator.cpp \ + configurators/openvpn_configurator.cpp \ + core/scripts_registry.cpp \ + core/server_defs.cpp \ core/servercontroller.cpp \ debug.cpp \ main.cpp \ managementserver.cpp \ + protocols/openvpnovercloakprotocol.cpp \ protocols/shadowsocksvpnprotocol.cpp \ settings.cpp \ ui/Controls/SlidingStackedWidget.cpp \ diff --git a/client/configurators/cloak_configurator.cpp b/client/configurators/cloak_configurator.cpp new file mode 100644 index 00000000..dddec337 --- /dev/null +++ b/client/configurators/cloak_configurator.cpp @@ -0,0 +1,45 @@ +#include "cloak_configurator.h" + +#include +#include +#include + +#include "protocols/protocols_defs.h" + +QJsonObject CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, + Protocol proto, ErrorCode *errorCode) +{ + ErrorCode e = ErrorCode::NoError; + + DockerContainer container = amnezia::containerForProto(proto); + + QString cloakPublicKey = ServerController::getTextFileFromContainer(container, credentials, + amnezia::protocols::cloak::ckPublicKeyPath(), &e); + cloakPublicKey.replace("\n", ""); + + QString cloakBypassUid = ServerController::getTextFileFromContainer(container, credentials, + amnezia::protocols::cloak::ckBypassUidKeyPath(), &e); + cloakBypassUid.replace("\n", ""); + + if (e) { + if (errorCode) *errorCode = e; + return QJsonObject(); + } + + QJsonObject config; + config.insert("Transport", "direct"); + config.insert("ProxyMethod", "openvpn"); + config.insert("EncryptionMethod", "aes-gcm"); + config.insert("UID", cloakBypassUid); + config.insert("PublicKey", cloakPublicKey); + config.insert("ServerName", amnezia::protocols::cloak::ckDefaultRedirSite()); + config.insert("NumConn", 4); + config.insert("BrowserSig", "chrome"); + config.insert("StreamTimeout", 300); + + // Amnezia field + config.insert("Remote", credentials.hostName); + + qDebug().noquote() << QJsonDocument(config).toJson(); + return config; +} diff --git a/client/configurators/cloak_configurator.h b/client/configurators/cloak_configurator.h new file mode 100644 index 00000000..b2dd9d5e --- /dev/null +++ b/client/configurators/cloak_configurator.h @@ -0,0 +1,18 @@ +#ifndef CLOAK_CONFIGURATOR_H +#define CLOAK_CONFIGURATOR_H + +#include + +#include "core/defs.h" +#include "settings.h" +#include "core/servercontroller.h" + +class CloakConfigurator +{ +public: + + static QJsonObject genCloakConfig(const ServerCredentials &credentials, Protocol proto, + ErrorCode *errorCode = nullptr); +}; + +#endif // CLOAK_CONFIGURATOR_H diff --git a/client/core/openvpnconfigurator.cpp b/client/configurators/openvpn_configurator.cpp similarity index 73% rename from client/core/openvpnconfigurator.cpp rename to client/configurators/openvpn_configurator.cpp index 723f719b..b9e0c0d2 100644 --- a/client/core/openvpnconfigurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -1,24 +1,15 @@ -#include "openvpnconfigurator.h" +#include "openvpn_configurator.h" #include #include #include -#include #include #include #include +#include -QString OpenVpnConfigurator::getRandomString(int len) -{ - const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - - QString randomString; - for(int i=0; igenerate() % possibleCharacters.length(); - QChar nextChar = possibleCharacters.at(index); - randomString.append(nextChar); - } - return randomString; -} +#include "core/server_defs.h" +#include "protocols/protocols_defs.h" +#include "core/scripts_registry.h" QString OpenVpnConfigurator::getEasyRsaShPath() { @@ -26,17 +17,10 @@ QString OpenVpnConfigurator::getEasyRsaShPath() // easyrsa sh path should looks like // "/Program Files (x86)/AmneziaVPN/easyrsa/easyrsa" QString easyRsaShPath = QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\easyrsa\\easyrsa"; -// easyRsaShPath.replace("C:\\", "/cygdrive/c/"); -// easyRsaShPath.replace("\\", "/"); easyRsaShPath = "\"" + easyRsaShPath + "\""; - -// easyRsaShPath = "\"/cygdrive/c/Program Files (x86)/AmneziaVPN/easyrsa/easyrsa\""; - -// easyRsaShPath = "\"C:\\Program Files (x86)\\AmneziaVPN\\easyrsa\\easyrsa\""; qDebug().noquote() << "EasyRsa sh path" << easyRsaShPath; return easyRsaShPath; -// return "\"/Program Files (x86)/AmneziaVPN/easyrsa/easyrsa\""; #else return QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/easyrsa"; #endif @@ -126,7 +110,7 @@ ErrorCode OpenVpnConfigurator::genReq(const QString &path, const QString &client OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest() { OpenVpnConfigurator::ConnectionData connData; - connData.clientId = getRandomString(32); + connData.clientId = Utils::getRandomString(32); QTemporaryDir dir; // if (dir.isValid()) { @@ -165,15 +149,11 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } - QString reqFileName = QString("/opt/amneziavpn_data/clients/%1.req").arg(connData.clientId); + QString reqFileName = QString("%1/%2.req"). + arg(amnezia::protocols::openvpn::clientsDirPath()). + arg(connData.clientId); - DockerContainer container; - if (proto == Protocol::OpenVpn) container = DockerContainer::OpenVpn; - else if (proto == Protocol::ShadowSocks) container = DockerContainer::ShadowSocks; - else { - if (errorCode) *errorCode = ErrorCode::InternalError; - return connData; - } + DockerContainer container = amnezia::containerForProto(proto); ErrorCode e = ServerController::uploadTextFileToContainer(container, credentials, connData.request, reqFileName); if (e) { @@ -181,20 +161,22 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co return connData; } - e = ServerController::signCert(container, credentials, connData.clientId); + e = signCert(container, credentials, connData.clientId); if (e) { if (errorCode) *errorCode = e; return connData; } - connData.caCert = ServerController::getTextFileFromContainer(container, credentials, ServerController::caCertPath(), &e); - connData.clientCert = ServerController::getTextFileFromContainer(container, credentials, ServerController::clientCertPath() + QString("%1.crt").arg(connData.clientId), &e); + connData.caCert = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath(), &e); + connData.clientCert = ServerController::getTextFileFromContainer(container, credentials, + QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath()).arg(connData.clientId), &e); + if (e) { if (errorCode) *errorCode = e; return connData; } - connData.taKey = ServerController::getTextFileFromContainer(container, credentials, ServerController::taKeyPath(), &e); + connData.taKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath(), &e); if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError; @@ -214,23 +196,31 @@ Settings &OpenVpnConfigurator::m_settings() QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, Protocol proto, ErrorCode *errorCode) { - QFile configTemplFile; - if (proto == Protocol::OpenVpn) - configTemplFile.setFileName(":/server_scripts/template_openvpn.ovpn"); - else if (proto == Protocol::ShadowSocks) { - configTemplFile.setFileName(":/server_scripts/template_shadowsocks.ovpn"); - } +// QFile configTemplFile; +// if (proto == Protocol::OpenVpn) +// configTemplFile.setFileName(":/server_scripts/template_openvpn.ovpn"); +// else if (proto == Protocol::ShadowSocks) { +// configTemplFile.setFileName(":/server_scripts/template_shadowsocks.ovpn"); +// } - configTemplFile.open(QIODevice::ReadOnly); - QString config = configTemplFile.readAll(); +// configTemplFile.open(QIODevice::ReadOnly); +// QString config = configTemplFile.readAll(); + + QString config = amnezia::scriptData(ProtocolScriptType::openvpn_template, proto); ConnectionData connData = prepareOpenVpnConfig(credentials, proto, errorCode); + if (errorCode && *errorCode) { + return ""; + } if (proto == Protocol::OpenVpn) config.replace("$PROTO", "udp"); else if (proto == Protocol::ShadowSocks) { config.replace("$PROTO", "tcp"); - config.replace("$LOCAL_PROXY_PORT", QString::number(ServerController::ssContainerPort())); + config.replace("$LOCAL_PROXY_PORT", QString::number(amnezia::protocols::shadowsocks::ssContainerPort())); + } + else if (proto == Protocol::OpenVpnOverCloak) { + config.replace("$PROTO", "tcp"); } config.replace("$PRIMARY_DNS", m_settings().primaryDns()); @@ -241,7 +231,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia } config.replace("$REMOTE_HOST", connData.host); - config.replace("$REMOTE_PORT", "1194"); + config.replace("$REMOTE_PORT", amnezia::protocols::openvpn::openvpnDefaultPort()); config.replace("$CA_CERT", connData.caCert); config.replace("$CLIENT_CERT", connData.clientCert); config.replace("$PRIV_KEY", connData.privKey); @@ -287,3 +277,23 @@ QString OpenVpnConfigurator::convertOpenSShKey(const QString &key) return tmp.readAll(); } + +ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, + const ServerCredentials &credentials, QString clientId) +{ + QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && " + "easyrsa import-req %2/%3.req %3\"") + .arg(amnezia::server::getContainerName(container)) + .arg(amnezia::protocols::openvpn::clientsDirPath()) + .arg(clientId); + + QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && " + "easyrsa sign-req client %2\"") + .arg(amnezia::server::getContainerName(container)) + .arg(clientId); + + QStringList scriptList {script_import, script_sign}; + QString script = ServerController::replaceVars(scriptList.join("\n"), ServerController::genVarsForScript(credentials, container)); + + return ServerController::runScript(ServerController::sshParams(credentials), script); +} diff --git a/client/core/openvpnconfigurator.h b/client/configurators/openvpn_configurator.h similarity index 79% rename from client/core/openvpnconfigurator.h rename to client/configurators/openvpn_configurator.h index b4455581..4e118888 100644 --- a/client/core/openvpnconfigurator.h +++ b/client/configurators/openvpn_configurator.h @@ -1,13 +1,12 @@ -#ifndef OPENVPNCONFIGURATOR_H -#define OPENVPNCONFIGURATOR_H +#ifndef OPENVPN_CONFIGURATOR_H +#define OPENVPN_CONFIGURATOR_H #include #include -#include "defs.h" +#include "core/defs.h" #include "settings.h" -#include "servercontroller.h" - +#include "core/servercontroller.h" class OpenVpnConfigurator { @@ -28,8 +27,10 @@ public: static QString convertOpenSShKey(const QString &key); + static ErrorCode signCert(DockerContainer container, + const ServerCredentials &credentials, QString clientId); + private: - static QString getRandomString(int len); static QString getEasyRsaShPath(); static QProcessEnvironment prepareEnv(); @@ -44,4 +45,4 @@ private: static Settings &m_settings(); }; -#endif // OPENVPNCONFIGURATOR_H +#endif // OPENVPN_CONFIGURATOR_H diff --git a/client/core/defs.h b/client/core/defs.h index bc6fdd89..d90947b4 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -9,15 +9,31 @@ enum class Protocol { Any, OpenVpn, ShadowSocks, + OpenVpnOverCloak, WireGuard }; enum class DockerContainer { + None, OpenVpn, ShadowSocks, + OpenVpnOverCloak, WireGuard }; +static DockerContainer containerForProto(Protocol proto) +{ + Q_ASSERT(proto != Protocol::Any); + + switch (proto) { + case Protocol::OpenVpn: return DockerContainer::OpenVpn; + case Protocol::OpenVpnOverCloak: return DockerContainer::OpenVpnOverCloak; + case Protocol::ShadowSocks: return DockerContainer::ShadowSocks; + case Protocol::WireGuard: return DockerContainer::WireGuard; + case Protocol::Any: return DockerContainer::None; + } +} + struct ServerCredentials { QString hostName; @@ -48,6 +64,7 @@ enum ErrorCode // Ssh remote process errors SshRemoteProcessCreationError, FailedToStartRemoteProcessError, RemoteProcessCrashError, + SshSftpError, // Local errors FailedToSaveConfigData, @@ -59,6 +76,7 @@ enum ErrorCode OpenVpnExecutableMissing, EasyRsaExecutableMissing, ShadowSocksExecutableMissing, + CloakExecutableMissing, AmneziaServiceConnectionFailed, // VPN errors @@ -67,7 +85,8 @@ enum ErrorCode // 3rd party utils errors OpenVpnExecutableCrashed, - ShadowSocksExecutableCrashed + ShadowSocksExecutableCrashed, + CloakExecutableCrashed }; namespace config { @@ -75,10 +94,10 @@ namespace config { static QString key_openvpn_config_data() { return "openvpn_config_data"; } static QString key_openvpn_config_path() { return "openvpn_config_path"; } static QString key_shadowsocks_config_data() { return "shadowsocks_config_data"; } +static QString key_cloak_config_data() { return "cloak_config_data"; } } - } // namespace amnezia #endif // DEFS_H diff --git a/client/core/scripts_registry.cpp b/client/core/scripts_registry.cpp new file mode 100644 index 00000000..5496ecb8 --- /dev/null +++ b/client/core/scripts_registry.cpp @@ -0,0 +1,60 @@ +#include "scripts_registry.h" + +#include +#include +#include + +QString amnezia::scriptFolder(amnezia::Protocol proto) +{ + switch (proto) { + case Protocol::OpenVpn: return QLatin1String("openvpn"); + case Protocol::OpenVpnOverCloak: return QLatin1String("openvpn_cloak"); + case Protocol::ShadowSocks: return QLatin1String("openvpn_shadowsocks"); + case Protocol::WireGuard: return QLatin1String("wireguard"); + default: return ""; + } +} + +QString amnezia::scriptName(SharedScriptType type) +{ + switch (type) { + case SharedScriptType::prepare_host: return QLatin1String("prepare_host.sh"); + case SharedScriptType::install_docker: return QLatin1String("install_docker.sh"); + case SharedScriptType::build_container: return QLatin1String("build_container.sh"); + case SharedScriptType::setup_host_firewall: return QLatin1String("setup_host_firewall.sh"); + } +} + +QString amnezia::scriptName(ProtocolScriptType type) +{ + switch (type) { + case ProtocolScriptType::dockerfile: return QLatin1String("Dockerfile"); + 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"); + } +} + +QString amnezia::scriptData(amnezia::SharedScriptType type) +{ + QString fileName = QString(":/server_scripts/%1").arg(amnezia::scriptName(type)); + QFile file(fileName); + if (! file.open(QIODevice::ReadOnly)) { + qDebug() << "Error opening script" << fileName; + return ""; + } + return file.readAll(); +} + +QString amnezia::scriptData(amnezia::ProtocolScriptType type, amnezia::Protocol proto) +{ + QString fileName = QString(":/server_scripts/%1/%2").arg(amnezia::scriptFolder(proto), amnezia::scriptName(type)); + QFile file(fileName); + if (! file.open(QIODevice::ReadOnly)) { + qDebug() << "Error opening script" << fileName; + return ""; + } + QByteArray data = file.readAll(); + data.replace("\r", ""); + return data; +} diff --git a/client/core/scripts_registry.h b/client/core/scripts_registry.h new file mode 100644 index 00000000..a59f9cbb --- /dev/null +++ b/client/core/scripts_registry.h @@ -0,0 +1,34 @@ +#ifndef SCRIPTS_REGISTRY_H +#define SCRIPTS_REGISTRY_H + +#include +#include "core/defs.h" + +namespace amnezia { + +enum SharedScriptType { + // General scripts + prepare_host, + install_docker, + build_container, + setup_host_firewall, +}; +enum ProtocolScriptType { + // Protocol scripts + dockerfile, + configure_container, + container_startup, + openvpn_template +}; + + +QString scriptFolder(Protocol proto); + +QString scriptName(SharedScriptType type); +QString scriptName(ProtocolScriptType type); + +QString scriptData(SharedScriptType type); +QString scriptData(ProtocolScriptType type, Protocol proto); +} + +#endif // SCRIPTS_REGISTRY_H diff --git a/client/core/server_defs.cpp b/client/core/server_defs.cpp new file mode 100644 index 00000000..eea22a5a --- /dev/null +++ b/client/core/server_defs.cpp @@ -0,0 +1,16 @@ +#include "server_defs.h" + +QString amnezia::server::getContainerName(amnezia::DockerContainer container) +{ + switch (container) { + case(DockerContainer::OpenVpn): return "amnezia-openvpn"; + case(DockerContainer::OpenVpnOverCloak): return "amnezia-openvpn-cloak"; + case(DockerContainer::ShadowSocks): return "amnezia-shadowsocks"; + default: return ""; + } +} + +QString amnezia::server::getDockerfileFolder(amnezia::DockerContainer container) +{ + return "/opt/amnezia/" + getContainerName(container); +} diff --git a/client/core/server_defs.h b/client/core/server_defs.h new file mode 100644 index 00000000..2475771b --- /dev/null +++ b/client/core/server_defs.h @@ -0,0 +1,19 @@ +#ifndef SERVER_DEFS_H +#define SERVER_DEFS_H + +#include +#include "core/defs.h" + +namespace amnezia { +namespace server { +QString getContainerName(amnezia::DockerContainer container); +QString getDockerfileFolder(amnezia::DockerContainer container); + +static QString vpnDefaultSubnetIp() { return "10.8.0.0"; } +static QString vpnDefaultSubnetMask() { return "255.255.255.0"; } +static QString vpnDefaultSubnetMaskVal() { return "24"; } + +} +} + +#endif // SERVER_DEFS_H diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 4ce35ae2..befff7b8 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -9,24 +9,20 @@ #include #include #include +#include +#include "sftpchannel.h" #include "sshconnectionmanager.h" + +#include "protocols/protocols_defs.h" +#include "server_defs.h" +#include "scripts_registry.h" #include "utils.h" using namespace QSsh; -QString ServerController::getContainerName(DockerContainer container) -{ - switch (container) { - case(DockerContainer::OpenVpn): return "amnezia-openvpn"; - case(DockerContainer::ShadowSocks): return "amnezia-shadowsocks"; - default: return ""; - } -} - -ErrorCode ServerController::runScript(const QHash &vars, - const SshConnectionParameters &sshParams, QString script, +ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, QString script, const std::function)> &cbReadStdOut, const std::function)> &cbReadStdErr) { @@ -39,21 +35,36 @@ ErrorCode ServerController::runScript(const QHash &vars, qDebug() << "Run script"; + QString totalLine; const QStringList &lines = script.split("\n", QString::SkipEmptyParts); for (int i = 0; i < lines.count(); i++) { - QString line = lines.at(i); + QString currentLine = lines.at(i); + QString nextLine; + if (i + 1 < lines.count()) nextLine = lines.at(i+1); - for (const QString &var : vars.keys()) { - //qDebug() << "Replacing" << var << vars.value(var); - line.replace(var, vars.value(var)); + if (totalLine.isEmpty()) { + totalLine = currentLine; + } + else { + totalLine = totalLine + "\n" + currentLine; } - if (line.startsWith("#")) { + QString lineToExec; + if (currentLine.endsWith("\\")) { + continue; + } + else { + lineToExec = totalLine; + totalLine.clear(); + } + + // Run collected line + if (totalLine.startsWith("#")) { continue; } - qDebug().noquote() << "EXEC" << line; - QSharedPointer proc = client->createRemoteProcess(line.toUtf8()); + qDebug().noquote() << "EXEC" << lineToExec; + QSharedPointer proc = client->createRemoteProcess(lineToExec.toUtf8()); if (!proc) { qCritical() << "Failed to create SshRemoteProcess, breaking."; @@ -103,61 +114,106 @@ ErrorCode ServerController::runScript(const QHash &vars, return ErrorCode::NoError; } -ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, - const ServerCredentials &credentials, QString &file, const QString &path) +ErrorCode ServerController::installDocker(const ServerCredentials &credentials) { - QString script = QString("sudo docker exec -i %1 sh -c \"echo \'%2\' > %3\""). - arg(getContainerName(container)).arg(file).arg(path); + // Setup openvpn part + QString scriptData = amnezia::scriptData(SharedScriptType::install_docker); + if (scriptData.isEmpty()) return ErrorCode::InternalError; - // qDebug().noquote() << "uploadTextFileToContainer\n" << script; + QString stdOut; + auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { + stdOut += data + "\n"; - SshConnection *client = connectToHost(sshParams(credentials)); - if (client->state() != SshConnection::State::Connected) { - return fromSshConnectionErrorCode(client->errorState()); - } + if (data.contains("Automatically restart Docker daemon?")) { + proc->write("yes\n"); + } + }; + auto cbReadStdErr = [&](const QString &data, QSharedPointer ) { + stdOut += data + "\n"; + }; - QSharedPointer proc = client->createRemoteProcess(script.toUtf8()); + return runScript(sshParams(credentials), + replaceVars(scriptData, genVarsForScript(credentials, DockerContainer::OpenVpnOverCloak)), + cbReadStdOut, cbReadStdErr); +} - if (!proc) { - qCritical() << "Failed to create SshRemoteProcess, breaking."; - return ErrorCode::SshRemoteProcessCreationError; - } +ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, + const ServerCredentials &credentials, const QString &file, const QString &path) +{ + ErrorCode e; + QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16)); + uploadFileToHost(credentials, file.toUtf8(), tmpFileName); - QEventLoop wait; - int exitStatus = -1; + e = runScript(sshParams(credentials), + replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), + genVarsForScript(credentials, container))); -// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ -// qDebug() << "uploadTextFileToContainer started"; + if (e) return e; + + runScript(sshParams(credentials), + replaceVars(QString("sudo shred %1").arg(tmpFileName), + genVarsForScript(credentials, container))); + + runScript(sshParams(credentials), + replaceVars(QString("sudo rm %1").arg(tmpFileName), + genVarsForScript(credentials, container))); + + return e; + + // QString script = QString("sudo docker exec -i %1 sh -c \"echo \'%2\' > %3\""). +// arg(amnezia::server::getContainerName(container)).arg(file).arg(path); + +// qDebug().noquote() << "uploadTextFileToContainer\n" << script; + +// SshConnection *client = connectToHost(sshParams(credentials)); +// if (client->state() != SshConnection::State::Connected) { +// return fromSshConnectionErrorCode(client->errorState()); +// } + +// QSharedPointer proc = client->createRemoteProcess(script.toUtf8()); + +// if (!proc) { +// qCritical() << "Failed to create SshRemoteProcess, breaking."; +// return ErrorCode::SshRemoteProcessCreationError; +// } + +// QEventLoop wait; +// int exitStatus = -1; + +//// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){ +//// qDebug() << "uploadTextFileToContainer started"; +//// }); + +// QObject::connect(proc.data(), &SshRemoteProcess::closed, &wait, [&](int status){ +// //qDebug() << "Remote process exited with status" << status; +// exitStatus = status; +// wait.quit(); // }); - QObject::connect(proc.data(), &SshRemoteProcess::closed, &wait, [&](int status){ - //qDebug() << "Remote process exited with status" << status; - exitStatus = status; - wait.quit(); - }); +// QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){ +// qDebug().noquote() << proc->readAllStandardOutput(); +// }); - QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, [proc](){ - qDebug().noquote() << proc->readAllStandardOutput(); - }); +// QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){ +// qDebug().noquote() << proc->readAllStandardError(); +// }); - QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, [proc](){ - qDebug().noquote() << proc->readAllStandardError(); - }); +// proc->start(); - proc->start(); +// if (exitStatus < 0) { +// wait.exec(); +// } - if (exitStatus < 0) { - wait.exec(); - } - - return fromSshProcessExitStatus(exitStatus); +// return fromSshProcessExitStatus(exitStatus); } QString ServerController::getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode) { + if (errorCode) *errorCode = ErrorCode::NoError; + QString script = QString("sudo docker exec -i %1 sh -c \"cat \'%2\'\""). - arg(getContainerName(container)).arg(path); + arg(amnezia::server::getContainerName(container)).arg(path); qDebug().noquote() << "Copy file from container\n" << script; @@ -201,28 +257,12 @@ QString ServerController::getTextFileFromContainer(DockerContainer container, return proc->readAllStandardOutput(); } -ErrorCode ServerController::signCert(DockerContainer container, - const ServerCredentials &credentials, QString clientId) -{ - QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amneziavpn_data && " - "easyrsa import-req /opt/amneziavpn_data/clients/%2.req %2\"") - .arg(getContainerName(container)).arg(clientId); - - QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amneziavpn_data && " - "easyrsa sign-req client %2\"") - .arg(getContainerName(container)).arg(clientId); - - QStringList script {script_import, script_sign}; - - return runScript(genVarsForScript(credentials, container), sshParams(credentials), script.join("\n")); -} - ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials) { QString caCert = ServerController::getTextFileFromContainer(container, - credentials, ServerController::caCertPath()); + credentials, amnezia::protocols::openvpn::caCertPath()); QString taKey = ServerController::getTextFileFromContainer(container, - credentials, ServerController::taKeyPath()); + credentials, amnezia::protocols::openvpn::taKeyPath()); if (!caCert.isEmpty() && !taKey.isEmpty()) { return ErrorCode::NoError; @@ -232,6 +272,68 @@ ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const } } +ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath) +{ + SshConnection *client = connectToHost(sshParams(credentials)); + if (client->state() != SshConnection::State::Connected) { + return fromSshConnectionErrorCode(client->errorState()); + } + + bool err = false; + + QEventLoop wait; + QTimer timer; + timer.setSingleShot(true); + timer.start(3000); + + QSharedPointer sftp = client->createSftpChannel(); + sftp->initialize(); + + QObject::connect(sftp.data(), &SftpChannel::initialized, &wait, [&](){ + timer.stop(); + wait.quit(); + }); + QObject::connect(&timer, &QTimer::timeout, &wait, [&](){ + err= true; + wait.quit(); + }); + + wait.exec(); + + if (!sftp) { + qCritical() << "Failed to create SftpChannel, breaking."; + return ErrorCode::SshRemoteProcessCreationError; + } + + QTemporaryFile localFile; + localFile.open(); + localFile.write(data); + localFile.close(); + + auto job = sftp->uploadFile(localFile.fileName(), remotePath, QSsh::SftpOverwriteMode::SftpOverwriteExisting); + QObject::connect(sftp.data(), &SftpChannel::finished, &wait, [&](QSsh::SftpJobId j, const QString &error){ + if (job == j) { + qDebug() << "Sftp finished with status" << error; + wait.quit(); + } + }); + + QObject::connect(sftp.data(), &SftpChannel::channelError, &wait, [&](const QString &reason){ + qDebug() << "Sftp finished with error" << reason; + err= true; + wait.quit(); + }); + + wait.exec(); + + if (err) { + return ErrorCode::SshSftpError; + } + else { + return ErrorCode::NoError; + } +} + ErrorCode ServerController::fromSshConnectionErrorCode(SshError error) { switch (error) { @@ -311,11 +413,16 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P scriptData = file.readAll(); if (scriptData.isEmpty()) return ErrorCode::InternalError; - return runScript(genVarsForScript(credentials, container), sshParams(credentials), scriptData); + return runScript(sshParams(credentials), replaceVars(scriptData, genVarsForScript(credentials, container))); } ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Protocol proto) { + ErrorCode e = runScript(sshParams(credentials), + replaceVars(amnezia::scriptData(SharedScriptType::install_docker), + genVarsForScript(credentials))); + if (e) return e; + if (proto == Protocol::OpenVpn) { return ErrorCode::NoError; //return setupOpenVpnServer(credentials); @@ -326,9 +433,9 @@ ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Pr else if (proto == Protocol::Any) { //return ErrorCode::NotImplementedError; - // TODO: run concurently //setupOpenVpnServer(credentials); - return setupShadowSocksServer(credentials); + //return setupShadowSocksServer(credentials); + return setupOpenVpnOverCloakServer(credentials); } return ErrorCode::NoError; @@ -336,100 +443,177 @@ ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Pr ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credentials) { - QString scriptData; - QString scriptFileName = ":/server_scripts/setup_openvpn_server.sh"; - QFile file(scriptFileName); - if (! file.open(QIODevice::ReadOnly)) return ErrorCode::InternalError; + return ErrorCode::NotImplementedError; - scriptData = file.readAll(); +// QString scriptData; +// QString scriptFileName = ":/server_scripts/setup_openvpn_server.sh"; +// QFile file(scriptFileName); +// if (! file.open(QIODevice::ReadOnly)) return ErrorCode::InternalError; + +// scriptData = file.readAll(); +// if (scriptData.isEmpty()) return ErrorCode::InternalError; + +// QString stdOut; +// auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { +// stdOut += data + "\n"; + +// if (data.contains("Automatically restart Docker daemon?")) { +// proc->write("yes\n"); +// } +// }; +// auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { +// stdOut += data + "\n"; +// }; + +// ErrorCode e = runScript(genVarsForScript(credentials, DockerContainer::OpenVpn), sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); +// if (e) return e; +// QApplication::processEvents(); + +// if (stdOut.contains("port is already allocated")) return ErrorCode::ServerPortAlreadyAllocatedError; +// if (stdOut.contains("Error response from daemon")) return ErrorCode::ServerCheckFailed; + +// return checkOpenVpnServer(DockerContainer::OpenVpn, credentials); +} + +ErrorCode ServerController::setupOpenVpnOverCloakServer(const ServerCredentials &credentials) +{ + ErrorCode e; + DockerContainer container = DockerContainer::OpenVpnOverCloak; + + // create folder on host + e = runScript(sshParams(credentials), + replaceVars(amnezia::scriptData(SharedScriptType::prepare_host), + genVarsForScript(credentials, container))); + if (e) return e; + + uploadFileToHost(credentials, amnezia::scriptData(ProtocolScriptType::dockerfile, Protocol::OpenVpnOverCloak).toUtf8(), + amnezia::server::getDockerfileFolder(container) + "/Dockerfile"); + + + // Setup openvpn part + QString scriptData = amnezia::scriptData(SharedScriptType::build_container); if (scriptData.isEmpty()) return ErrorCode::InternalError; - QString stdOut; - auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { - stdOut += data + "\n"; +// QString stdOut; +// auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { +// stdOut += data + "\n"; +// }; +// auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { +// stdOut += data + "\n"; +// }; - if (data.contains("Automatically restart Docker daemon?")) { - proc->write("yes\n"); - } - }; - auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { - stdOut += data + "\n"; - }; - - ErrorCode e = runScript(genVarsForScript(credentials, DockerContainer::OpenVpn), sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); + e = runScript(sshParams(credentials), + replaceVars(scriptData, + genVarsForScript(credentials, container))); if (e) return e; - QApplication::processEvents(); - if (stdOut.contains("port is already allocated")) return ErrorCode::ServerPortAlreadyAllocatedError; - if (stdOut.contains("Error response from daemon")) return ErrorCode::ServerCheckFailed; - return checkOpenVpnServer(DockerContainer::OpenVpn, credentials); + runScript(sshParams(credentials), + replaceVars(amnezia::scriptData(ProtocolScriptType::configure_container, Protocol::OpenVpnOverCloak), + genVarsForScript(credentials, container))); + if (e) return e; + + uploadTextFileToContainer(DockerContainer::OpenVpnOverCloak, credentials, + replaceVars(amnezia::scriptData(ProtocolScriptType::container_startup, Protocol::OpenVpnOverCloak), + genVarsForScript(credentials, container)), + "/opt/amnezia/start.sh"); + +// qDebug().noquote() << "AAAA" +// << amnezia::scriptData(ProtocolScriptType::container_startup, Protocol::OpenVpnOverCloak), +// replaceVars("/opt/amnezia/start.sh", +// genVarsForScript(credentials, container)); + + runScript(sshParams(credentials), + replaceVars("sudo docker exec -d $CONTAINER_NAME sh -c \"chmod a+x /opt/amnezia/start.sh && /opt/amnezia/start.sh\"", + genVarsForScript(credentials, container))); + if (e) return e; + + return e; } ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &credentials) { - // Setup openvpn part - QString scriptData; - QString scriptFileName = ":/server_scripts/setup_shadowsocks_server.sh"; - QFile file(scriptFileName); - if (! file.open(QIODevice::ReadOnly)) return ErrorCode::InternalError; + return ErrorCode::NotImplementedError; +// // Setup openvpn part +// QString scriptData; +// QString scriptFileName = ":/server_scripts/setup_shadowsocks_server.sh"; +// QFile file(scriptFileName); +// if (! file.open(QIODevice::ReadOnly)) return ErrorCode::InternalError; - scriptData = file.readAll(); - if (scriptData.isEmpty()) return ErrorCode::InternalError; +// scriptData = file.readAll(); +// if (scriptData.isEmpty()) return ErrorCode::InternalError; - QString stdOut; - auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { - stdOut += data + "\n"; +// QString stdOut; +// auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { +// stdOut += data + "\n"; - if (data.contains("Automatically restart Docker daemon?")) { - proc->write("yes\n"); - } - }; - auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { - stdOut += data + "\n"; - }; +// if (data.contains("Automatically restart Docker daemon?")) { +// proc->write("yes\n"); +// } +// }; +// auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { +// stdOut += data + "\n"; +// }; - ErrorCode e = runScript(genVarsForScript(credentials, DockerContainer::ShadowSocks), sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); - if (e) return e; +// ErrorCode e = runScript(genVarsForScript(credentials, DockerContainer::ShadowSocks), sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr); +// if (e) return e; - // Create ss config - QJsonObject ssConfig; - ssConfig.insert("server", "0.0.0.0"); - ssConfig.insert("server_port", ssRemotePort()); - ssConfig.insert("local_port", ssContainerPort()); - ssConfig.insert("password", QString(QCryptographicHash::hash(credentials.password.toUtf8(), QCryptographicHash::Sha256).toHex())); - ssConfig.insert("timeout", 60); - ssConfig.insert("method", ssEncryption()); - QString configData = QJsonDocument(ssConfig).toJson(); - QString sSConfigPath = "/opt/amneziavpn_data/ssConfig.json"; +// // Create ss config +// QJsonObject ssConfig; +// ssConfig.insert("server", "0.0.0.0"); +// ssConfig.insert("server_port", amnezia::protocols::shadowsocks::ssRemotePort()); +// ssConfig.insert("local_port", amnezia::protocols::shadowsocks::ssContainerPort()); +// ssConfig.insert("password", QString(QCryptographicHash::hash(credentials.password.toUtf8(), QCryptographicHash::Sha256).toHex())); +// ssConfig.insert("timeout", 60); +// ssConfig.insert("method", amnezia::protocols::shadowsocks::ssEncryption()); +// QString configData = QJsonDocument(ssConfig).toJson(); +// QString sSConfigPath = "/opt/amneziavpn_data/ssConfig.json"; - configData.replace("\"", "\\\""); - //qDebug().noquote() << configData; +// configData.replace("\"", "\\\""); +// //qDebug().noquote() << configData; - uploadTextFileToContainer(DockerContainer::ShadowSocks, credentials, configData, sSConfigPath); +// uploadTextFileToContainer(DockerContainer::ShadowSocks, credentials, configData, sSConfigPath); - // Start ss - QString script = QString("sudo docker exec -d %1 sh -c \"ss-server -c %2\""). - arg(getContainerName(DockerContainer::ShadowSocks)).arg(sSConfigPath); +// // Start ss +// QString script = QString("sudo docker exec -d %1 sh -c \"ss-server -c %2\""). +// arg(amnezia::server::getContainerName(DockerContainer::ShadowSocks)).arg(sSConfigPath); - e = runScript(genVarsForScript(credentials, DockerContainer::ShadowSocks), sshParams(credentials), script); - return e; +// e = runScript(genVarsForScript(credentials, DockerContainer::ShadowSocks), sshParams(credentials), script); +// return e; } -QHash ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container) +ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container) { - QHash vars; + Vars vars; - vars.insert("$CONTAINER_NAME", getContainerName(container)); + vars.append(qMakePair("$VPN_SUBNET_IP", amnezia::server::vpnDefaultSubnetIp())); + vars.append(qMakePair("$VPN_SUBNET_MASK_VAL", amnezia::server::vpnDefaultSubnetMaskVal())); + vars.append(qMakePair("$VPN_SUBNET_MASK", amnezia::server::vpnDefaultSubnetMask())); + + vars.append(qMakePair("$CONTAINER_NAME", amnezia::server::getContainerName(container))); + vars.append(qMakePair("$DOCKERFILE_FOLDER", "/opt/amnezia/" + amnezia::server::getContainerName(container))); QString serverIp = Utils::getIPAddress(credentials.hostName); if (!serverIp.isEmpty()) { - vars.insert("$SERVER_IP_ADDRESS", serverIp); + vars.append(qMakePair("$SERVER_IP_ADDRESS", serverIp)); } else { qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName"; } + + // + if (container == DockerContainer::OpenVpn) { + vars.append(qMakePair("$SERVER_PORT", amnezia::protocols::openvpn::openvpnDefaultPort())); + } + else if (container == DockerContainer::OpenVpnOverCloak) { + vars.append(qMakePair("$SERVER_PORT", amnezia::protocols::cloak::ckDefaultPort())); + vars.append(qMakePair("$FAKE_WEB_SITE_ADDRESS", amnezia::protocols::cloak::ckDefaultRedirSite())); + } + else if (container == DockerContainer::ShadowSocks) { + vars.append(qMakePair("$SERVER_PORT", "6789")); + } + return vars; } @@ -488,9 +672,17 @@ SshConnection *ServerController::connectToHost(const SshConnectionParameters &ss ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) { - QFile file(":/server_scripts/setup_firewall.sh"); - file.open(QIODevice::ReadOnly); - - QString script = file.readAll(); - return runScript(genVarsForScript(credentials, DockerContainer::OpenVpn), sshParams(credentials), script); + return runScript(sshParams(credentials), + replaceVars(amnezia::scriptData(SharedScriptType::setup_host_firewall), + genVarsForScript(credentials, DockerContainer::OpenVpnOverCloak))); +} + +QString ServerController::replaceVars(const QString &script, const Vars &vars) +{ + QString s = script; + for (const QPair &var : vars) { + //qDebug() << "Replacing" << var << vars.value(var); + s.replace(var.first, var.second); + } + return s; } diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index a5a90515..b1e47406 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -12,18 +12,13 @@ class ServerController : public QObject { Q_OBJECT public: + typedef QList> Vars; static ErrorCode fromSshConnectionErrorCode(QSsh::SshError error); // QSsh exitCode and exitStatus are different things static ErrorCode fromSshProcessExitStatus(int exitStatus); - static QString caCertPath() { return "/opt/amneziavpn_data/pki/ca.crt"; } - static QString clientCertPath() { return "/opt/amneziavpn_data/pki/issued/"; } - static QString taKeyPath() { return "/opt/amneziavpn_data/ta.key"; } - - static QString getContainerName(amnezia::DockerContainer container); - static QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); static ErrorCode removeServer(const ServerCredentials &credentials, Protocol proto); @@ -31,33 +26,32 @@ public: static ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials); + static ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath); + static ErrorCode uploadTextFileToContainer(DockerContainer container, - const ServerCredentials &credentials, QString &file, const QString &path); + const ServerCredentials &credentials, const QString &file, const QString &path); static QString getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); - static ErrorCode signCert(DockerContainer container, - const ServerCredentials &credentials, QString clientId); - - static int ssRemotePort() { return 6789; } // TODO move to ShadowSocksDefs.h - static int ssContainerPort() { return 8585; } // TODO move to ShadowSocksDefs.h - static QString ssEncryption() { return "chacha20-ietf-poly1305"; } // TODO move to ShadowSocksDefs.h - static ErrorCode setupServerFirewall(const ServerCredentials &credentials); -private: - static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); - static ErrorCode runScript(const QHash &vars, - const QSsh::SshConnectionParameters &sshParams, QString script, + static QString replaceVars(const QString &script, const Vars &vars); + + static ErrorCode runScript(const QSsh::SshConnectionParameters &sshParams, QString script, const std::function)> &cbReadStdOut = nullptr, const std::function)> &cbReadStdErr = nullptr); + static Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None); + +private: + static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams); + + static ErrorCode installDocker(const ServerCredentials &credentials); + static ErrorCode setupOpenVpnServer(const ServerCredentials &credentials); + static ErrorCode setupOpenVpnOverCloakServer(const ServerCredentials &credentials); static ErrorCode setupShadowSocksServer(const ServerCredentials &credentials); - - - static QHash genVarsForScript(const ServerCredentials &credentials, DockerContainer container); }; #endif // SERVERCONTROLLER_H diff --git a/client/protocols/openvpnovercloakprotocol.cpp b/client/protocols/openvpnovercloakprotocol.cpp new file mode 100644 index 00000000..314706d6 --- /dev/null +++ b/client/protocols/openvpnovercloakprotocol.cpp @@ -0,0 +1,105 @@ +#include "openvpnovercloakprotocol.h" +#include "core/servercontroller.h" + +#include "utils.h" +#include "protocols/protocols_defs.h" + +#include +#include +#include +#include + +OpenVpnOverCloakProtocol::OpenVpnOverCloakProtocol(const QJsonObject &configuration, QObject *parent): + OpenVpnProtocol(configuration, parent) +{ + readCloakConfiguration(configuration); +} + +OpenVpnOverCloakProtocol::~OpenVpnOverCloakProtocol() +{ + qDebug() << "OpenVpnOverCloakProtocol::~OpenVpnOverCloakProtocol"; + OpenVpnOverCloakProtocol::stop(); + QThread::msleep(200); + m_ckProcess.close(); +} + +ErrorCode OpenVpnOverCloakProtocol::start() +{ + if (Utils::processIsRunning(Utils::executable("ck-client", false))) { + Utils::killProcessByName(Utils::executable("ck-client", false)); + } + +#ifdef QT_DEBUG + m_cloakCfgFile.setAutoRemove(false); +#endif + m_cloakCfgFile.open(); + m_cloakCfgFile.write(QJsonDocument(m_cloakConfig).toJson()); + m_cloakCfgFile.close(); + + QStringList args = QStringList() << "-c" << m_cloakCfgFile.fileName() + << "-s" << m_cloakConfig.value("Remote").toString() + << "-p" << amnezia::protocols::cloak::ckDefaultPort() + << "-l" << amnezia::protocols::openvpn::openvpnDefaultPort(); + + qDebug().noquote() << "OpenVpnOverCloakProtocol::start()" + << cloakExecPath() << args.join(" "); + + m_ckProcess.setProcessChannelMode(QProcess::MergedChannels); + + m_ckProcess.setProgram(cloakExecPath()); + m_ckProcess.setArguments(args); + + connect(&m_ckProcess, &QProcess::readyReadStandardOutput, this, [this](){ + qDebug().noquote() << "ck-client:" << m_ckProcess.readAllStandardOutput(); + }); + + connect(&m_ckProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){ + qDebug().noquote() << "OpenVpnOverCloakProtocol finished, exitCode, exiStatus" << exitCode << exitStatus; + setConnectionState(VpnProtocol::ConnectionState::Disconnected); + if (exitStatus != QProcess::NormalExit){ + emit protocolError(amnezia::ErrorCode::CloakExecutableCrashed); + stop(); + } + if (exitCode !=0 ){ + emit protocolError(amnezia::ErrorCode::InternalError); + stop(); + } + }); + + m_ckProcess.start(); + m_ckProcess.waitForStarted(); + + if (m_ckProcess.state() == QProcess::ProcessState::Running) { + setConnectionState(ConnectionState::Connecting); + + return OpenVpnProtocol::start(); + } + else return ErrorCode::CloakExecutableMissing; +} + +void OpenVpnOverCloakProtocol::stop() +{ + OpenVpnProtocol::stop(); + + qDebug() << "OpenVpnOverCloakProtocol::stop()"; + +#ifdef Q_OS_WIN + Utils::signalCtrl(m_ckProcess.processId(), CTRL_C_EVENT); +#endif + + m_ckProcess.terminate(); +} + +QString OpenVpnOverCloakProtocol::cloakExecPath() +{ +#ifdef Q_OS_WIN + return Utils::executable(QString("cloak/ck-client"), true); +#else + return Utils::executable(QString("/ck-client"), true); +#endif +} + +void OpenVpnOverCloakProtocol::readCloakConfiguration(const QJsonObject &configuration) +{ + m_cloakConfig = configuration.value(config::key_cloak_config_data()).toObject(); +} diff --git a/client/protocols/openvpnovercloakprotocol.h b/client/protocols/openvpnovercloakprotocol.h new file mode 100644 index 00000000..9244d6ff --- /dev/null +++ b/client/protocols/openvpnovercloakprotocol.h @@ -0,0 +1,30 @@ +#ifndef OPENVPNOVERCLOAKPROTOCOL_H +#define OPENVPNOVERCLOAKPROTOCOL_H + +#include "openvpnprotocol.h" +#include "QProcess" + +class OpenVpnOverCloakProtocol : public OpenVpnProtocol +{ +public: + OpenVpnOverCloakProtocol(const QJsonObject& configuration, QObject* parent = nullptr); + virtual ~OpenVpnOverCloakProtocol() override; + + ErrorCode start() override; + void stop() override; + +protected: + void readCloakConfiguration(const QJsonObject &configuration); + +protected: + QJsonObject m_cloakConfig; + +private: + static QString cloakExecPath(); + +private: + QProcess m_ckProcess; + QTemporaryFile m_cloakCfgFile; +}; + +#endif // OPENVPNOVERCLOAKPROTOCOL_H diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h new file mode 100644 index 00000000..7a97852e --- /dev/null +++ b/client/protocols/protocols_defs.h @@ -0,0 +1,36 @@ +#ifndef PROTOCOLS_DEFS_H +#define PROTOCOLS_DEFS_H + +#include + +namespace amnezia { +namespace protocols { +namespace openvpn { +static QString caCertPath() { return "/opt/amnezia/openvpn/pki/ca.crt"; } +static QString clientCertPath() { return "/opt/amnezia/openvpn/pki/issued"; } +static QString taKeyPath() { return "/opt/amnezia/openvpn/ta.key"; } +static QString clientsDirPath() { return "/opt/amnezia/openvpn/clients"; } +static QString openvpnDefaultPort() { return "1194"; } + +} + +namespace shadowsocks { +static int ssRemotePort() { return 6789; } +static int ssContainerPort() { return 8585; } +static QString ssEncryption() { return "chacha20-ietf-poly1305"; } +} + +namespace cloak { +static QString ckPublicKeyPath() { return "/opt/amnezia/cloak/cloak_public.key"; } +static QString ckBypassUidKeyPath() { return "/opt/amnezia/cloak/cloak_bypass_uid.key"; } +static QString ckAdminKeyPath() { return "/opt/amnezia/cloak/cloak_admin_uid.key"; } +static QString ckDefaultPort() { return "443"; } +static QString ckDefaultRedirSite() { return "mail.ru"; } +} + + + +} // namespace protocols +} // namespace amnezia + +#endif // PROTOCOLS_DEFS_H diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index 8051ae96..83f66009 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -3,6 +3,7 @@ #include "debug.h" #include "utils.h" +#include "protocols/protocols_defs.h" #include #include @@ -99,11 +100,11 @@ QJsonObject ShadowSocksVpnProtocol::genShadowSocksConfig(const ServerCredentials { QJsonObject ssConfig; ssConfig.insert("server", credentials.hostName); - ssConfig.insert("server_port", ServerController::ssRemotePort()); - ssConfig.insert("local_port", ServerController::ssContainerPort()); + ssConfig.insert("server_port", amnezia::protocols::shadowsocks::ssRemotePort()); + ssConfig.insert("local_port", amnezia::protocols::shadowsocks::ssContainerPort()); ssConfig.insert("password", QString(QCryptographicHash::hash(credentials.password.toUtf8(), QCryptographicHash::Sha256).toHex())); ssConfig.insert("timeout", 60); - ssConfig.insert("method", ServerController::ssEncryption()); + ssConfig.insert("method", amnezia::protocols::shadowsocks::ssEncryption()); return ssConfig; } diff --git a/client/resources.qrc b/client/resources.qrc index d5e681ee..18045ed4 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -39,7 +39,14 @@ images/background_connected.png server_scripts/setup_shadowsocks_server.sh server_scripts/template_shadowsocks.ovpn - server_scripts/setup_firewall.sh + server_scripts/setup_host_firewall.sh images/reload.png + server_scripts/openvpn_cloak/Dockerfile + server_scripts/openvpn_cloak/configure_container.sh + server_scripts/openvpn_cloak/start.sh + server_scripts/openvpn_cloak/template.ovpn + server_scripts/install_docker.sh + server_scripts/build_container.sh + server_scripts/prepare_host.sh diff --git a/client/server_scripts/build_container.sh b/client/server_scripts/build_container.sh new file mode 100644 index 00000000..791d164b --- /dev/null +++ b/client/server_scripts/build_container.sh @@ -0,0 +1 @@ +sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh new file mode 100644 index 00000000..62c20cc5 --- /dev/null +++ b/client/server_scripts/install_docker.sh @@ -0,0 +1,6 @@ +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; if [[ ! -f "/usr/bin/sudo" ]]; then $pm update -y -q; $pm install -y -q sudo; fi +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm update -y -q +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then pm=$pm_apt; else pm=$pm_yum; fi; sudo $pm install -y -q curl +pm_apt="/usr/bin/apt-get"; pm_yum="/usr/bin/yum"; if [[ -f "$pm_apt" ]]; then sudo export DEBIAN_FRONTEND=noninteractive; sudo $pm_apt install -y -q docker.io; else sudo $pm_yum install -y -q docker; fi +sudo systemctl start docker + diff --git a/client/server_scripts/openvpn_cloak/Dockerfile b/client/server_scripts/openvpn_cloak/Dockerfile new file mode 100644 index 00000000..fe02f60f --- /dev/null +++ b/client/server_scripts/openvpn_cloak/Dockerfile @@ -0,0 +1,52 @@ +FROM alpine:latest + +LABEL maintainer="AmneziaVPN" + +#Install required packages +RUN apk add --no-cache curl openvpn easy-rsa bash netcat-openbsd dumb-init rng-tools +RUN apk --update upgrade --no-cache + +ENV EASYRSA_BATCH 1 +ENV PATH="/usr/share/easy-rsa:${PATH}" + +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 + +RUN curl -L https://github.com/cbeuw/Cloak/releases/download/v2.5.3/ck-server-linux-amd64-v2.5.3 > /usr/bin/ck-server +RUN chmod a+x /usr/bin/ck-server + +# 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/openvpn_cloak/configure_container.sh b/client/server_scripts/openvpn_cloak/configure_container.sh new file mode 100644 index 00000000..18f4b7fd --- /dev/null +++ b/client/server_scripts/openvpn_cloak/configure_container.sh @@ -0,0 +1,84 @@ +# CONTAINER_NAME=... this var will be set in ServerController +# Don't run commands in background like sh -c "openvpn &" +# SERVER_PORT=443 + +#sudo docker stop $CONTAINER_NAME +#sudo docker rm -f $CONTAINER_NAME +#sudo docker pull amneziavpn/openvpn-cloak:latest +#sudo docker run -d --restart always --cap-add=NET_ADMIN -p $SERVER_PORT:443/tcp --name $CONTAINER_NAME amneziavpn/openvpn-cloak:latest + +sudo docker stop $CONTAINER_NAME +sudo docker rm -f $CONTAINER_NAME +sudo docker run -d --restart always --cap-add=NET_ADMIN -p $SERVER_PORT:443/tcp --name $CONTAINER_NAME $CONTAINER_NAME + +# Create tun device if not exist +sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi' + +# 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" + +# OpenVPN config +sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /opt/amnezia/openvpn/clients; \ +cd /opt/amnezia/openvpn && easyrsa init-pki; \ +cd /opt/amnezia/openvpn && easyrsa gen-dh; \ +cd /opt/amnezia/openvpn && cp pki/dh.pem /opt/amnezia/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req AmneziaReq nopass << EOF2 yes EOF2;\ +cd /opt/amnezia/openvpn && easyrsa sign-req server AmneziaReq << EOF3 yes EOF3;\ +cd /opt/amnezia/openvpn && openvpn --genkey --secret ta.key << EOF4;\ +cd /opt/amnezia/openvpn && cp pki/ca.crt pki/issued/AmneziaReq.crt pki/private/AmneziaReq.key /opt/amnezia/openvpn' + +sudo docker exec -i $CONTAINER_NAME bash -c '\ +echo -e "\ +port 1194 \\n\ +proto tcp \\n\ +dev tun \\n\ +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\ +ifconfig-pool-persist ipp.txt \\n\ +duplicate-cn \\n\ +keepalive 10 120 \\n\ +cipher AES-256-GCM \\n\ +ncp-ciphers AES-256-GCM:AES-256-CBC \\n\ +auth SHA512 \\n\ +user nobody \\n\ +group nobody \\n\ +persist-key \\n\ +persist-tun \\n\ +status openvpn-status.log \\n\ +verb 1 \\n\ +tls-server \\n\ +tls-version-min 1.2 \\n\ +tls-auth /opt/amnezia/openvpn/ta.key 0" >>/opt/amnezia/openvpn/server.conf' + +#sudo docker exec -d $CONTAINER_NAME sh -c "openvpn --config /opt/amnezia/openvpn/server.conf" + +# Cloak config +sudo docker exec -i $CONTAINER_NAME bash -c '\ +mkdir -p /opt/amnezia/cloak; \ +cd /opt/amnezia/cloak || exit 1; \ +CLOAK_ADMIN_UID=$(ck-server -u) && echo $CLOAK_ADMIN_UID > /opt/amnezia/cloak/cloak_admin_uid.key; \ +CLOAK_BYPASS_UID=$(ck-server -u) && echo $CLOAK_BYPASS_UID > /opt/amnezia/cloak/cloak_bypass_uid.key; \ +IFS=, read CLOAK_PUBLIC_KEY CLOAK_PRIVATE_KEY <<<$(ck-server -k); \ +echo $CLOAK_PUBLIC_KEY > /opt/amnezia/cloak/cloak_public.key; \ +echo $CLOAK_PRIVATE_KEY > /opt/amnezia/cloak/cloak_private.key; \ +echo -e "{\\n\ + \"ProxyBook\": {\\n\ + \"openvpn\": [\\n\ + \"tcp\",\\n\ + \"localhost:1194\"\\n\ + ]\\n\ + },\\n\ + \"BypassUID\": [\\n\ + \"$CLOAK_BYPASS_UID\"\\n\ + ],\\n\ + \"BindAddr\":[\":443\"],\\n\ + \"RedirAddr\": \"$FAKE_WEB_SITE_ADDRESS\",\\n\ + \"PrivateKey\": \"$CLOAK_PRIVATE_KEY\",\\n\ + \"AdminUID\": \"$CLOAK_ADMIN_UID\",\\n\ + \"DatabasePath\": \"userinfo.db\",\\n\ + \"StreamTimeout\": 300\\n\ +}" >>/opt/amnezia/cloak/ck-config.json' + +#sudo docker exec -d $CONTAINER_NAME sh -c "/usr/bin/ck-server -c /opt/amnezia/cloak/ck-config.json" diff --git a/client/server_scripts/openvpn_cloak/start.sh b/client/server_scripts/openvpn_cloak/start.sh new file mode 100644 index 00000000..8cde6d6f --- /dev/null +++ b/client/server_scripts/openvpn_cloak/start.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts + +echo "Container Startup start" + +if [ ! -c /dev/net/tun ]; then mkdir -p /dev/net; mknod /dev/net/tun c 10 200; fi + +# Allow traffic on the TUN interface. +iptables -A INPUT -i tun0 -j ACCEPT +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 -m state --state ESTABLISHED,RELATED -j ACCEPT + +iptables -t nat -A POSTROUTING -s $VPN_SUBNET_IP/$VPN_SUBNET_MASK_VAL -o eth0 -j MASQUERADE + +if [ -f /opt/amnezia/openvpn/ca.crt ]; then (openvpn --config /opt/amnezia/openvpn/server.conf --daemon); fi +if [ -f /opt/amnezia/cloak/ck-config.json ]; then (ck-server -c /opt/amnezia/cloak/ck-config.json &); fi + +tail -f /dev/null diff --git a/client/server_scripts/openvpn_cloak/template.ovpn b/client/server_scripts/openvpn_cloak/template.ovpn new file mode 100644 index 00000000..2f9fb4fa --- /dev/null +++ b/client/server_scripts/openvpn_cloak/template.ovpn @@ -0,0 +1,35 @@ +client +dev tun +proto $PROTO +resolv-retry infinite +nobind +persist-key +persist-tun +cipher AES-256-GCM +auth SHA512 +verb 3 +tls-client +tls-version-min 1.2 +key-direction 1 +remote-cert-tls server +redirect-gateway def1 bypass-dhcp + +dhcp-option DNS $PRIMARY_DNS +dhcp-option DNS $SECONDARY_DNS +block-outside-dns + +route $REMOTE_HOST 255.255.255.255 net_gateway +remote 127.0.0.1 1194 + + +$CA_CERT + + +$CLIENT_CERT + + +$PRIV_KEY + + +$TA_KEY + diff --git a/client/server_scripts/prepare_host.sh b/client/server_scripts/prepare_host.sh new file mode 100644 index 00000000..94f7d727 --- /dev/null +++ b/client/server_scripts/prepare_host.sh @@ -0,0 +1,3 @@ +CUR_USER=$(whoami);\ +sudo mkdir -p $DOCKERFILE_FOLDER;\ +sudo chown $CUR_USER $DOCKERFILE_FOLDER diff --git a/client/server_scripts/setup_host_firewall.sh b/client/server_scripts/setup_host_firewall.sh index ab5ad4ee..13c93324 100644 --- a/client/server_scripts/setup_host_firewall.sh +++ b/client/server_scripts/setup_host_firewall.sh @@ -1,24 +1,31 @@ sudo sysctl -w net.ipv4.ip_forward=1 -sudo iptables -P FORWARD ACCEPT sudo iptables -C INPUT -p icmp --icmp-type echo-request -j DROP || sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP +#sudo iptables -P FORWARD ACCEPT +sudo iptables -A FORWARD -j DOCKER-USER +sudo iptables -A FORWARD -j DOCKER-ISOLATION-STAGE-1 +sudo iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +sudo iptables -A FORWARD -o docker0 -j DOCKER +sudo iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT +sudo iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT + # Tuning network -sudo sysctl fs.file-max=51200 -sudo sysctl net.core.rmem_max=67108864 -sudo sysctl net.core.wmem_max=67108864 -sudo sysctl net.core.netdev_max_backlog=250000 -sudo sysctl net.core.somaxconn=4096 -sudo sysctl net.ipv4.tcp_syncookies=1 -sudo sysctl net.ipv4.tcp_tw_reuse=1 -sudo sysctl net.ipv4.tcp_tw_recycle=0 -sudo sysctl net.ipv4.tcp_fin_timeout=30 -sudo sysctl net.ipv4.tcp_keepalive_time=1200 -sudo sysctl net.ipv4.ip_local_port_range="10000 65000" -sudo sysctl net.ipv4.tcp_max_syn_backlog=8192 -sudo sysctl net.ipv4.tcp_max_tw_buckets=5000 -sudo sysctl net.ipv4.tcp_fastopen=3 -sudo sysctl net.ipv4.tcp_mem="25600 51200 102400" -sudo sysctl net.ipv4.tcp_rmem="4096 87380 67108864" -sudo sysctl net.ipv4.tcp_wmem="4096 65536 67108864" -sudo sysctl net.ipv4.tcp_mtu_probing=1 +sudo sysctl fs.file-max=51200; \ +sudo sysctl net.core.rmem_max=67108864; \ +sudo sysctl net.core.wmem_max=67108864; \ +sudo sysctl net.core.netdev_max_backlog=250000; \ +sudo sysctl net.core.somaxconn=4096; \ +sudo sysctl net.ipv4.tcp_syncookies=1; \ +sudo sysctl net.ipv4.tcp_tw_reuse=1; \ +sudo sysctl net.ipv4.tcp_tw_recycle=0; \ +sudo sysctl net.ipv4.tcp_fin_timeout=30; \ +sudo sysctl net.ipv4.tcp_keepalive_time=1200; \ +sudo sysctl net.ipv4.ip_local_port_range="10000 65000"; \ +sudo sysctl net.ipv4.tcp_max_syn_backlog=8192; \ +sudo sysctl net.ipv4.tcp_max_tw_buckets=5000; \ +sudo sysctl net.ipv4.tcp_fastopen=3; \ +sudo sysctl net.ipv4.tcp_mem="25600 51200 102400"; \ +sudo sysctl net.ipv4.tcp_rmem="4096 87380 67108864"; \ +sudo sysctl net.ipv4.tcp_wmem="4096 65536 67108864"; \ +sudo sysctl net.ipv4.tcp_mtu_probing=1; \ sudo sysctl net.ipv4.tcp_congestion_control=hybla diff --git a/client/server_scripts/setup_shadowsocks_server.sh b/client/server_scripts/setup_shadowsocks_server.sh index 9178d285..c4ee4177 100644 --- a/client/server_scripts/setup_shadowsocks_server.sh +++ b/client/server_scripts/setup_shadowsocks_server.sh @@ -12,17 +12,18 @@ sudo systemctl start docker sudo docker stop $CONTAINER_NAME sudo docker rm -f $CONTAINER_NAME sudo docker pull amneziavpn/shadowsocks:latest -sudo docker run -d --restart always --cap-add=NET_ADMIN -p 1194:1194/tcp -p 6789:6789/tcp --name $CONTAINER_NAME amneziavpn/shadowsocks:latest +sudo docker run -d --restart always --cap-add=NET_ADMIN -p 6789:6789/tcp --name $CONTAINER_NAME amneziavpn/shadowsocks:latest # 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" -sudo docker exec -i $CONTAINER_NAME sh -c "mkdir -p /opt/amneziavpn_data/clients" -sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa init-pki" -sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa gen-dh" +# OpenVpn +sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /opt/amneziavpn_data/clients;\ +cd /opt/amneziavpn_data && easyrsa init-pki;\ +cd /opt/amneziavpn_data && easyrsa gen-dh;\ +cd /opt/amneziavpn_data && cp pki/dh.pem /etc/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req MyReq nopass << EOF2 yes EOF2;\ +cd /opt/amneziavpn_data && easyrsa sign-req server MyReq << EOF3 yes EOF3;\ +cd /opt/amneziavpn_data && openvpn --genkey --secret ta.key << EOF4;\ +cd /opt/amneziavpn_data && cp pki/ca.crt pki/issued/MyReq.crt pki/private/MyReq.key ta.key /etc/openvpn' -sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/dh.pem /etc/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req MyReq nopass << EOF2 yes EOF2" -sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && easyrsa sign-req server MyReq << EOF3 yes EOF3" -sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && openvpn --genkey --secret ta.key << EOF4" -sudo docker exec -i $CONTAINER_NAME sh -c "cd /opt/amneziavpn_data && cp pki/ca.crt pki/issued/MyReq.crt pki/private/MyReq.key ta.key /etc/openvpn" sudo docker exec -d $CONTAINER_NAME sh -c "openvpn --config /etc/openvpn/server.conf" diff --git a/client/ui/mainwindow.cpp b/client/ui/mainwindow.cpp index ce7cbe89..43637108 100644 --- a/client/ui/mainwindow.cpp +++ b/client/ui/mainwindow.cpp @@ -15,7 +15,7 @@ #include #include "core/errorstrings.h" -#include "core/openvpnconfigurator.h" +#include "configurators/openvpn_configurator.h" #include "core/servercontroller.h" #include "ui/qautostart.h" @@ -111,6 +111,9 @@ MainWindow::MainWindow(QWidget *parent) : ui->lineEdit_new_server_ip->setValidator(&m_ipAddressPortValidator); ui->lineEdit_network_settings_dns1->setValidator(&m_ipAddressValidator); ui->lineEdit_network_settings_dns2->setValidator(&m_ipAddressValidator); + + ui->toolBox_share_connection->removeItem(ui->toolBox_share_connection->indexOf(ui->page_share_shadowsocks)); + ui->page_share_shadowsocks->setVisible(false); } MainWindow::~MainWindow() diff --git a/client/ui/mainwindow.ui b/client/ui/mainwindow.ui index 657d429e..ebe00ae3 100644 --- a/client/ui/mainwindow.ui +++ b/client/ui/mainwindow.ui @@ -1918,7 +1918,7 @@ background: #211966; - Software version: 1.5.3 (19.03.2021) + Software version: 1.6.0 (31.03.2021) @@ -2659,8 +2659,8 @@ background: #282932; 0 0 - 360 - 500 + 100 + 30 diff --git a/client/utils.cpp b/client/utils.cpp index a7d36f6d..99079ff9 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -4,11 +4,24 @@ #include #include #include +#include #include #include "defines.h" #include "utils.h" +QString Utils::getRandomString(int len) +{ + const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + + QString randomString; + for(int i=0; igenerate() % possibleCharacters.length(); + QChar nextChar = possibleCharacters.at(index); + randomString.append(nextChar); + } + return randomString; +} QString Utils::defaultVpnConfigFileName() { diff --git a/client/utils.h b/client/utils.h index 3cef180c..dd981e21 100644 --- a/client/utils.h +++ b/client/utils.h @@ -11,6 +11,8 @@ class Utils { public: + static QString getRandomString(int len); + static QString configPath(); static QString defaultVpnConfigFileName(); static QString executable(const QString& baseName, bool absPath); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 1bfb8b42..155e9b6b 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -3,13 +3,16 @@ #include #include -#include +#include +#include #include #include "ipc.h" #include "core/ipcclient.h" #include "protocols/openvpnprotocol.h" +#include "protocols/openvpnovercloakprotocol.h" #include "protocols/shadowsocksvpnprotocol.h" + #include "utils.h" #include "vpnconnection.h" @@ -80,7 +83,7 @@ ErrorCode VpnConnection::lastError() const ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credentials, Protocol protocol) { ErrorCode errorCode = ErrorCode::NoError; - if (protocol == Protocol::OpenVpn || protocol == Protocol::ShadowSocks) { + if (protocol == Protocol::OpenVpn || protocol == Protocol::ShadowSocks || protocol == Protocol::OpenVpnOverCloak) { QString openVpnConfigData = OpenVpnConfigurator::genOpenVpnConfig(credentials, protocol, &errorCode); m_vpnConfiguration.insert(config::key_openvpn_config_data(), openVpnConfigData); if (errorCode) { @@ -91,6 +94,7 @@ ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credent if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)){ QTextStream stream(&file); stream << openVpnConfigData << endl; + file.close(); } else { return ErrorCode::FailedToSaveConfigData; @@ -102,6 +106,11 @@ ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credent m_vpnConfiguration.insert(config::key_shadowsocks_config_data(), ssConfigData); } + if (protocol == Protocol::OpenVpnOverCloak) { + QJsonObject cloakConfigData = CloakConfigurator::genCloakConfig(credentials, Protocol::OpenVpnOverCloak, &errorCode); + m_vpnConfiguration.insert(config::key_cloak_config_data(), cloakConfigData); + } + //qDebug().noquote() << "VPN config" << QJsonDocument(m_vpnConfiguration).toJson(); return ErrorCode::NoError; } @@ -111,7 +120,8 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot qDebug() << "connectToVpn, CustomRouting is" << m_settings.customRouting(); // qDebug() << "Cred" << m_settings.serverCredentials().hostName << // m_settings.serverCredentials().password; - protocol = Protocol::ShadowSocks; + //protocol = Protocol::ShadowSocks; + protocol = Protocol::OpenVpnOverCloak; // TODO: Try protocols one by one in case of Protocol::Any // TODO: Implement some behavior in case if connection not stable @@ -156,6 +166,20 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot return e; } } + else if (protocol == Protocol::OpenVpnOverCloak) { + ErrorCode e = createVpnConfiguration(credentials, Protocol::OpenVpnOverCloak); + if (e) { + emit connectionStateChanged(VpnProtocol::ConnectionState::Error); + return e; + } + + m_vpnProtocol.reset(new OpenVpnOverCloakProtocol(m_vpnConfiguration)); + e = static_cast(m_vpnProtocol.data())->checkAndSetupTapDriver(); + if (e) { + emit connectionStateChanged(VpnProtocol::ConnectionState::Error); + return e; + } + } connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState)));