- no dockerhub

- trafic masking
This commit is contained in:
pokamest 2021-04-04 23:12:36 +03:00
parent 059c6404ab
commit 85b6b06cc9
31 changed files with 1106 additions and 256 deletions

View file

@ -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 \

View file

@ -0,0 +1,45 @@
#include "cloak_configurator.h"
#include <QFile>
#include <QJsonObject>
#include <QJsonDocument>
#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;
}

View file

@ -0,0 +1,18 @@
#ifndef CLOAK_CONFIGURATOR_H
#define CLOAK_CONFIGURATOR_H
#include <QObject>
#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

View file

@ -1,24 +1,15 @@
#include "openvpnconfigurator.h"
#include "openvpn_configurator.h"
#include <QApplication>
#include <QProcess>
#include <QString>
#include <QRandomGenerator>
#include <QTemporaryDir>
#include <QDebug>
#include <QTemporaryFile>
#include <utils.h>
QString OpenVpnConfigurator::getRandomString(int len)
{
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
QString randomString;
for(int i=0; i<len; ++i) {
quint32 index = QRandomGenerator::global()->generate() % 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);
}

View file

@ -1,13 +1,12 @@
#ifndef OPENVPNCONFIGURATOR_H
#define OPENVPNCONFIGURATOR_H
#ifndef OPENVPN_CONFIGURATOR_H
#define OPENVPN_CONFIGURATOR_H
#include <QObject>
#include <QProcessEnvironment>
#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

View file

@ -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

View file

@ -0,0 +1,60 @@
#include "scripts_registry.h"
#include <QObject>
#include <QDebug>
#include <QFile>
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;
}

View file

@ -0,0 +1,34 @@
#ifndef SCRIPTS_REGISTRY_H
#define SCRIPTS_REGISTRY_H
#include <QLatin1String>
#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

View file

@ -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);
}

19
client/core/server_defs.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef SERVER_DEFS_H
#define SERVER_DEFS_H
#include <QObject>
#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

View file

@ -9,24 +9,20 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QApplication>
#include <QTemporaryFile>
#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<QString, QString> &vars,
const SshConnectionParameters &sshParams, QString script,
ErrorCode ServerController::runScript(const SshConnectionParameters &sshParams, QString script,
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdOut,
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdErr)
{
@ -39,21 +35,36 @@ ErrorCode ServerController::runScript(const QHash<QString, QString> &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<SshRemoteProcess> proc = client->createRemoteProcess(line.toUtf8());
qDebug().noquote() << "EXEC" << lineToExec;
QSharedPointer<SshRemoteProcess> proc = client->createRemoteProcess(lineToExec.toUtf8());
if (!proc) {
qCritical() << "Failed to create SshRemoteProcess, breaking.";
@ -103,61 +114,106 @@ ErrorCode ServerController::runScript(const QHash<QString, QString> &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<QSsh::SshRemoteProcess> 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<QSsh::SshRemoteProcess> ) {
stdOut += data + "\n";
};
QSharedPointer<SshRemoteProcess> 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<SshRemoteProcess> 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<SftpChannel> 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<QSsh::SshRemoteProcess> proc) {
// stdOut += data + "\n";
// if (data.contains("Automatically restart Docker daemon?")) {
// proc->write("yes\n");
// }
// };
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> 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<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
// QString stdOut;
// auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
// stdOut += data + "\n";
// };
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
// stdOut += data + "\n";
// };
if (data.contains("Automatically restart Docker daemon?")) {
proc->write("yes\n");
}
};
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> 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<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
// QString stdOut;
// auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
// stdOut += data + "\n";
if (data.contains("Automatically restart Docker daemon?")) {
proc->write("yes\n");
}
};
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
};
// if (data.contains("Automatically restart Docker daemon?")) {
// proc->write("yes\n");
// }
// };
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> 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<QString, QString> ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container)
ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container)
{
QHash<QString, QString> vars;
Vars vars;
vars.insert("$CONTAINER_NAME", getContainerName(container));
vars.append(qMakePair<QString, QString>("$VPN_SUBNET_IP", amnezia::server::vpnDefaultSubnetIp()));
vars.append(qMakePair<QString, QString>("$VPN_SUBNET_MASK_VAL", amnezia::server::vpnDefaultSubnetMaskVal()));
vars.append(qMakePair<QString, QString>("$VPN_SUBNET_MASK", amnezia::server::vpnDefaultSubnetMask()));
vars.append(qMakePair<QString, QString>("$CONTAINER_NAME", amnezia::server::getContainerName(container)));
vars.append(qMakePair<QString, QString>("$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<QString, QString>("$SERVER_IP_ADDRESS", serverIp));
}
else {
qWarning() << "ServerController::genVarsForScript unable to resolve address for credentials.hostName";
}
//
if (container == DockerContainer::OpenVpn) {
vars.append(qMakePair<QString, QString>("$SERVER_PORT", amnezia::protocols::openvpn::openvpnDefaultPort()));
}
else if (container == DockerContainer::OpenVpnOverCloak) {
vars.append(qMakePair<QString, QString>("$SERVER_PORT", amnezia::protocols::cloak::ckDefaultPort()));
vars.append(qMakePair<QString, QString>("$FAKE_WEB_SITE_ADDRESS", amnezia::protocols::cloak::ckDefaultRedirSite()));
}
else if (container == DockerContainer::ShadowSocks) {
vars.append(qMakePair<QString, QString>("$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<QString, QString> &var : vars) {
//qDebug() << "Replacing" << var << vars.value(var);
s.replace(var.first, var.second);
}
return s;
}

View file

@ -12,18 +12,13 @@ class ServerController : public QObject
{
Q_OBJECT
public:
typedef QList<QPair<QString, QString>> 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<QString, QString> &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<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdOut = nullptr,
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &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<QString, QString> genVarsForScript(const ServerCredentials &credentials, DockerContainer container);
};
#endif // SERVERCONTROLLER_H

View file

@ -0,0 +1,105 @@
#include "openvpnovercloakprotocol.h"
#include "core/servercontroller.h"
#include "utils.h"
#include "protocols/protocols_defs.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
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<int, QProcess::ExitStatus>::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();
}

View file

@ -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

View file

@ -0,0 +1,36 @@
#ifndef PROTOCOLS_DEFS_H
#define PROTOCOLS_DEFS_H
#include <QObject>
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

View file

@ -3,6 +3,7 @@
#include "debug.h"
#include "utils.h"
#include "protocols/protocols_defs.h"
#include <QCryptographicHash>
#include <QJsonDocument>
@ -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;
}

View file

@ -39,7 +39,14 @@
<file>images/background_connected.png</file>
<file>server_scripts/setup_shadowsocks_server.sh</file>
<file>server_scripts/template_shadowsocks.ovpn</file>
<file>server_scripts/setup_firewall.sh</file>
<file>server_scripts/setup_host_firewall.sh</file>
<file>images/reload.png</file>
<file>server_scripts/openvpn_cloak/Dockerfile</file>
<file>server_scripts/openvpn_cloak/configure_container.sh</file>
<file>server_scripts/openvpn_cloak/start.sh</file>
<file>server_scripts/openvpn_cloak/template.ovpn</file>
<file>server_scripts/install_docker.sh</file>
<file>server_scripts/build_container.sh</file>
<file>server_scripts/prepare_host.sh</file>
</qresource>
</RCC>

View file

@ -0,0 +1 @@
sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER

View file

@ -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

View file

@ -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 [ "" ]

View file

@ -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"

View file

@ -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

View file

@ -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>
$CA_CERT
</ca>
<cert>
$CLIENT_CERT
</cert>
<key>
$PRIV_KEY
</key>
<tls-auth>
$TA_KEY
</tls-auth>

View file

@ -0,0 +1,3 @@
CUR_USER=$(whoami);\
sudo mkdir -p $DOCKERFILE_FOLDER;\
sudo chown $CUR_USER $DOCKERFILE_FOLDER

View file

@ -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

View file

@ -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"

View file

@ -15,7 +15,7 @@
#include <protocols/shadowsocksvpnprotocol.h>
#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()

View file

@ -1918,7 +1918,7 @@ background: #211966;
</rect>
</property>
<property name="text">
<string>Software version: 1.5.3 (19.03.2021)</string>
<string>Software version: 1.6.0 (31.03.2021)</string>
</property>
</widget>
</widget>
@ -2659,8 +2659,8 @@ background: #282932;
<rect>
<x>0</x>
<y>0</y>
<width>360</width>
<height>500</height>
<width>100</width>
<height>30</height>
</rect>
</property>
<property name="styleSheet">

View file

@ -4,11 +4,24 @@
#include <QHostAddress>
#include <QHostInfo>
#include <QProcess>
#include <QRandomGenerator>
#include <QStandardPaths>
#include "defines.h"
#include "utils.h"
QString Utils::getRandomString(int len)
{
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
QString randomString;
for(int i=0; i<len; ++i) {
quint32 index = QRandomGenerator::global()->generate() % possibleCharacters.length();
QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar);
}
return randomString;
}
QString Utils::defaultVpnConfigFileName()
{

View file

@ -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);

View file

@ -3,13 +3,16 @@
#include <QFile>
#include <QJsonObject>
#include <core/openvpnconfigurator.h>
#include <configurators/openvpn_configurator.h>
#include <configurators/cloak_configurator.h>
#include <core/servercontroller.h>
#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<OpenVpnProtocol *>(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)));