- no dockerhub
- trafic masking
This commit is contained in:
parent
059c6404ab
commit
85b6b06cc9
31 changed files with 1106 additions and 256 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,289 +0,0 @@
|
|||
#include "openvpnconfigurator.h"
|
||||
#include <QApplication>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QRandomGenerator>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDebug>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::getEasyRsaShPath()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
// 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
|
||||
}
|
||||
|
||||
QProcessEnvironment OpenVpnConfigurator::prepareEnv()
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
QString pathEnvVar = env.value("PATH");
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
pathEnvVar.clear();
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn\\i386;");
|
||||
#else
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||
#endif
|
||||
|
||||
env.insert("PATH", pathEnvVar);
|
||||
//qDebug().noquote() << "ENV PATH" << pathEnvVar;
|
||||
return env;
|
||||
}
|
||||
|
||||
ErrorCode OpenVpnConfigurator::initPKI(const QString &path)
|
||||
{
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
p.setProcessEnvironment(prepareEnv());
|
||||
p.setProgram("cmd.exe");
|
||||
p.setNativeArguments(QString("/C \"ash.exe %1\"").arg(getEasyRsaShPath() + " init-pki"));
|
||||
qDebug().noquote() << "EasyRsa tmp path" << path;
|
||||
qDebug().noquote() << "EasyRsa args" << p.nativeArguments();
|
||||
#else
|
||||
p.setProgram(getEasyRsaShPath());
|
||||
p.setArguments(QStringList() << "init-pki");
|
||||
#endif
|
||||
|
||||
p.setWorkingDirectory(path);
|
||||
|
||||
QObject::connect(&p, &QProcess::channelReadyRead, [&](){
|
||||
qDebug().noquote() << "Init PKI" << p.readAll();
|
||||
});
|
||||
|
||||
p.start();
|
||||
p.waitForFinished();
|
||||
|
||||
if (p.exitCode() == 0) return ErrorCode::NoError;
|
||||
else return ErrorCode::EasyRsaError;
|
||||
}
|
||||
|
||||
ErrorCode OpenVpnConfigurator::genReq(const QString &path, const QString &clientId)
|
||||
{
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
p.setProcessEnvironment(prepareEnv());
|
||||
p.setProgram("cmd.exe");
|
||||
p.setNativeArguments(QString("/C \"ash.exe %1\"").arg(getEasyRsaShPath() + " gen-req " + clientId + " nopass"));
|
||||
qDebug().noquote() << "EasyRsa args" << p.nativeArguments();
|
||||
#else
|
||||
p.setArguments(QStringList() << "gen-req" << clientId << "nopass");
|
||||
p.setProgram(getEasyRsaShPath());
|
||||
#endif
|
||||
|
||||
p.setWorkingDirectory(path);
|
||||
|
||||
QObject::connect(&p, &QProcess::channelReadyRead, [&](){
|
||||
QString data = p.readAll();
|
||||
qDebug().noquote() << data;
|
||||
|
||||
if (data.contains("Common Name (eg: your user, host, or server name)")) {
|
||||
p.write("\n");
|
||||
}
|
||||
});
|
||||
|
||||
p.start();
|
||||
p.waitForFinished();
|
||||
|
||||
if (p.exitCode() == 0) return ErrorCode::NoError;
|
||||
else return ErrorCode::EasyRsaError;
|
||||
}
|
||||
|
||||
|
||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData connData;
|
||||
connData.clientId = getRandomString(32);
|
||||
|
||||
QTemporaryDir dir;
|
||||
// if (dir.isValid()) {
|
||||
// // dir.path() returns the unique directory path
|
||||
// }
|
||||
|
||||
QString path = dir.path();
|
||||
|
||||
initPKI(path);
|
||||
ErrorCode errorCode = genReq(path, connData.clientId);
|
||||
|
||||
Q_UNUSED(errorCode)
|
||||
|
||||
QFile req(path + "/pki/reqs/" + connData.clientId + ".req");
|
||||
req.open(QIODevice::ReadOnly);
|
||||
connData.request = req.readAll();
|
||||
|
||||
QFile key(path + "/pki/private/" + connData.clientId + ".key");
|
||||
key.open(QIODevice::ReadOnly);
|
||||
connData.privKey = key.readAll();
|
||||
|
||||
// qDebug().noquote() << connData.request;
|
||||
// qDebug().noquote() << connData.privKey;
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
|
||||
Protocol proto, ErrorCode *errorCode)
|
||||
{
|
||||
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
|
||||
connData.host = credentials.hostName;
|
||||
|
||||
if (connData.privKey.isEmpty() || connData.request.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::EasyRsaExecutableMissing;
|
||||
return connData;
|
||||
}
|
||||
|
||||
QString reqFileName = QString("/opt/amneziavpn_data/clients/%1.req").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;
|
||||
}
|
||||
|
||||
ErrorCode e = ServerController::uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
e = ServerController::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);
|
||||
if (e) {
|
||||
if (errorCode) *errorCode = e;
|
||||
return connData;
|
||||
}
|
||||
|
||||
connData.taKey = ServerController::getTextFileFromContainer(container, credentials, ServerController::taKeyPath(), &e);
|
||||
|
||||
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError;
|
||||
}
|
||||
|
||||
ServerController::setupServerFirewall(credentials);
|
||||
|
||||
return connData;
|
||||
}
|
||||
|
||||
Settings &OpenVpnConfigurator::m_settings()
|
||||
{
|
||||
static Settings s;
|
||||
return s;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
configTemplFile.open(QIODevice::ReadOnly);
|
||||
QString config = configTemplFile.readAll();
|
||||
|
||||
ConnectionData connData = prepareOpenVpnConfig(credentials, proto, errorCode);
|
||||
|
||||
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("$PRIMARY_DNS", m_settings().primaryDns());
|
||||
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
|
||||
|
||||
if (m_settings().customRouting()) {
|
||||
config.replace("redirect-gateway def1 bypass-dhcp", "");
|
||||
}
|
||||
|
||||
config.replace("$REMOTE_HOST", connData.host);
|
||||
config.replace("$REMOTE_PORT", "1194");
|
||||
config.replace("$CA_CERT", connData.caCert);
|
||||
config.replace("$CLIENT_CERT", connData.clientCert);
|
||||
config.replace("$PRIV_KEY", connData.privKey);
|
||||
config.replace("$TA_KEY", connData.taKey);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
//qDebug().noquote() << config;
|
||||
return config;
|
||||
}
|
||||
|
||||
QString OpenVpnConfigurator::convertOpenSShKey(const QString &key)
|
||||
{
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
QTemporaryFile tmp;
|
||||
#ifdef QT_DEBUG
|
||||
tmp.setAutoRemove(false);
|
||||
#endif
|
||||
tmp.open();
|
||||
tmp.write(key.toUtf8());
|
||||
tmp.close();
|
||||
|
||||
// ssh-keygen -p -P "" -N "" -m pem -f id_ssh
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
p.setProcessEnvironment(prepareEnv());
|
||||
p.setProgram("cmd.exe");
|
||||
p.setNativeArguments(QString("/C \"ssh-keygen.exe -p -P \"\" -N \"\" -m pem -f \"%1\"\"").arg(tmp.fileName()));
|
||||
#else
|
||||
p.setProgram("ssh-keygen");
|
||||
p.setArguments(QStringList() << "-p" << "-P" << "" << "-N" << "" << "-m" << "pem" << "-f" << tmp.fileName());
|
||||
#endif
|
||||
|
||||
p.start();
|
||||
p.waitForFinished();
|
||||
|
||||
qDebug().noquote() << "OpenVpnConfigurator::convertOpenSShKey" << p.exitCode() << p.exitStatus() << p.readAll();
|
||||
|
||||
tmp.open();
|
||||
|
||||
return tmp.readAll();
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef OPENVPNCONFIGURATOR_H
|
||||
#define OPENVPNCONFIGURATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include "defs.h"
|
||||
#include "settings.h"
|
||||
#include "servercontroller.h"
|
||||
|
||||
|
||||
class OpenVpnConfigurator
|
||||
{
|
||||
public:
|
||||
|
||||
struct ConnectionData {
|
||||
QString clientId;
|
||||
QString request; // certificate request
|
||||
QString privKey; // client private key
|
||||
QString clientCert; // client signed certificate
|
||||
QString caCert; // server certificate
|
||||
QString taKey; // tls-auth key
|
||||
QString host; // host ip
|
||||
};
|
||||
|
||||
static QString genOpenVpnConfig(const ServerCredentials &credentials, Protocol proto,
|
||||
ErrorCode *errorCode = nullptr);
|
||||
|
||||
static QString convertOpenSShKey(const QString &key);
|
||||
|
||||
private:
|
||||
static QString getRandomString(int len);
|
||||
static QString getEasyRsaShPath();
|
||||
|
||||
static QProcessEnvironment prepareEnv();
|
||||
static ErrorCode initPKI(const QString &path);
|
||||
static ErrorCode genReq(const QString &path, const QString &clientId);
|
||||
|
||||
static ConnectionData createCertRequest();
|
||||
|
||||
static ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials,
|
||||
Protocol proto, ErrorCode *errorCode = nullptr);
|
||||
|
||||
static Settings &m_settings();
|
||||
};
|
||||
|
||||
#endif // OPENVPNCONFIGURATOR_H
|
||||
60
client/core/scripts_registry.cpp
Normal file
60
client/core/scripts_registry.cpp
Normal 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;
|
||||
}
|
||||
34
client/core/scripts_registry.h
Normal file
34
client/core/scripts_registry.h
Normal 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
|
||||
16
client/core/server_defs.cpp
Normal file
16
client/core/server_defs.cpp
Normal 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
19
client/core/server_defs.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue