Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions client/core/configurators/openVpnConfigurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,18 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}

connData.caCert =
m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, errorCode);
connData.clientCert = m_sshSession->getTextFileFromContainer(
container, credentials, QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), errorCode);

const QStringList certPaths = {
QString::fromLatin1(amnezia::protocols::openvpn::caCertPath),
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId),
QString::fromLatin1(amnezia::protocols::openvpn::taKeyPath)
};
const QList<QByteArray> certs = m_sshSession->getTextFilesFromContainer(container, credentials, certPaths, errorCode);
if (errorCode != ErrorCode::NoError) {
return connData;
}

connData.taKey = m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
connData.caCert = certs.value(0);
connData.clientCert = certs.value(1);
connData.taKey = certs.value(2);

if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
errorCode = ErrorCode::SshScpFailureError;
Expand Down
14 changes: 5 additions & 9 deletions client/core/configurators/wireguardConfigurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,16 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
connData.clientIP = nextIp.toString();

// Get keys
connData.serverPubKey =
m_sshSession->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", "");
const QList<QByteArray> keys =
m_sshSession->getTextFilesFromContainer(container, credentials, {m_serverPublicKeyPath, m_serverPskKeyPath}, errorCode);
if (errorCode != ErrorCode::NoError) {
return connData;
}

connData.pskKey = m_sshSession->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
connData.serverPubKey = keys.value(0);
connData.serverPubKey.replace("\n", "");
connData.pskKey = keys.value(1);
connData.pskKey.replace("\n", "");

if (errorCode != ErrorCode::NoError) {
return connData;
}

// Add client to config
QString configPart = QString("[Peer]\n"
"PublicKey = %1\n"
Expand Down
13 changes: 6 additions & 7 deletions client/core/controllers/selfhosted/installController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,9 @@ InstallController::~InstallController()
}

ErrorCode InstallController::setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config,
bool isUpdate)
SshSession &sshSession, bool isUpdate)
{
qDebug().noquote() << "InstallController::setupContainer" << ContainerUtils::containerToString(container);
SshSession sshSession(this);
ErrorCode e = ErrorCode::NoError;

e = isUserInSudo(credentials, sshSession);
Expand Down Expand Up @@ -199,7 +198,7 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont

ErrorCode errorCode = ErrorCode::NoError;
if (reinstallRequired) {
errorCode = setupContainer(credentials, container, newConfig, true);
errorCode = setupContainer(credentials, container, newConfig, sshSession, true);
} else {
errorCode = configureContainerWorker(credentials, container, newConfig, sshSession);
if (errorCode == ErrorCode::NoError) {
Expand Down Expand Up @@ -1033,10 +1032,10 @@ ContainerConfig InstallController::generateConfig(DockerContainer container, int
}

ErrorCode InstallController::installContainer(const ServerCredentials &credentials, DockerContainer container, int port,
TransportProto transportProto, ContainerConfig &config)
TransportProto transportProto, ContainerConfig &config, SshSession &sshSession)
{
config = generateConfig(container, port, transportProto);
return setupContainer(credentials, container, config, false);
return setupContainer(credentials, container, config, sshSession, false);
}


Expand Down Expand Up @@ -1142,7 +1141,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
wasContainerInstalled = false;
if (!installedContainers.contains(container)) {
ContainerConfig config;
errorCode = installContainer(credentials, container, port, transportProto, config);
errorCode = installContainer(credentials, container, port, transportProto, config, sshSession);
if (errorCode) {
return errorCode;
}
Expand Down Expand Up @@ -1212,7 +1211,7 @@ ErrorCode InstallController::installContainer(const QString &serverId, DockerCon
wasContainerInstalled = false;
if (!installedContainers.contains(container)) {
ContainerConfig config;
errorCode = installContainer(credentials, container, port, transportProto, config);
errorCode = installContainer(credentials, container, port, transportProto, config, sshSession);
if (errorCode) {
return errorCode;
}
Expand Down
4 changes: 2 additions & 2 deletions client/core/controllers/selfhosted/installController.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class InstallController : public QObject
QObject *parent = nullptr);
~InstallController();

ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession, bool isUpdate = false);
ErrorCode updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);

ErrorCode rebootServer(const QString &serverId);
Expand All @@ -55,7 +55,7 @@ class InstallController : public QObject

ErrorCode scanServerForInstalledContainers(const QString &serverId);

ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config, SshSession &sshSession);

ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
bool &wasContainerInstalled);
Expand Down
15 changes: 13 additions & 2 deletions client/core/utils/selfhosted/sshClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ namespace libssh {
ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str());
ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity);

long connectTimeoutSec = 30;
ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &connectTimeoutSec);

QFutureWatcher<int> watcher;
QFuture<int> future = QtConcurrent::run([this]() {
return ssh_connect(m_session);
Expand All @@ -61,7 +64,9 @@ namespace libssh {
int connectionResult = watcher.result();

if (connectionResult != SSH_OK) {
return fromLibsshErrorCode();
ErrorCode errorCode = fromLibsshErrorCode();
disconnectFromHost();
return errorCode;
}

std::string authUsername = credentials.userName.toStdString();
Expand Down Expand Up @@ -95,14 +100,20 @@ namespace libssh {
if (errorCode == ErrorCode::NoError) {
errorCode = ErrorCode::SshPrivateKeyFormatError;
}
disconnectFromHost();
return errorCode;
}
} else {
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.secretData.toStdString().c_str());
if (authResult != SSH_OK) {
return fromLibsshErrorCode();
ErrorCode errorCode = fromLibsshErrorCode();
disconnectFromHost();
return errorCode;
}
}

long sessionTimeoutSec = 86400;
ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &sessionTimeoutSec);
}
return ErrorCode::NoError;
}
Expand Down
128 changes: 72 additions & 56 deletions client/core/utils/selfhosted/sshSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ ErrorCode SshSession::runScript(const ServerCredentials &credentials, QString sc
qDebug() << "SshSession::Run script";

QString totalLine;
QStringList statements;
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
for (int i = 0; i < lines.count(); i++) {
QString currentLine = lines.at(i);
Expand All @@ -69,24 +70,31 @@ ErrorCode SshSession::runScript(const ServerCredentials &credentials, QString sc
totalLine = totalLine + "\n" + currentLine;
}

QString lineToExec;
if (currentLine.endsWith("\\")) {
continue;
} else {
lineToExec = totalLine;
totalLine.clear();
}

QString lineToExec = totalLine;
totalLine.clear();

if (lineToExec.startsWith("#")) {
continue;
}

qDebug().noquote() << lineToExec;
statements << lineToExec;
}

error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
if (error != ErrorCode::NoError) {
return error;
}
if (statements.isEmpty()) {
qDebug().noquote() << "SshSession::runScript finished (no statements)\n";
return ErrorCode::NoError;
}

const QString batchedScript = statements.join("\n");
qDebug().noquote() << batchedScript;

error = m_sshClient.executeCommand(batchedScript, cbReadStdOut, cbReadStdErr);
if (error != ErrorCode::NoError) {
return error;
}

qDebug().noquote() << "SshSession::runScript finished\n";
Expand All @@ -97,76 +105,52 @@ ErrorCode SshSession::runContainerScript(const ServerCredentials &credentials, D
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdOut,
const std::function<ErrorCode(const QString &, libssh::Client &)> &cbReadStdErr)
{
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";

ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
if (e)
return e;

const bool useSh = container == DockerContainer::Socks5Proxy || container == DockerContainer::MtProxy || container == DockerContainer::Telemt;
QString runner = QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, useSh ? "sh" : "bash");
e = runScript(credentials, replaceVars(runner, amnezia::genBaseVars(credentials, container, QString(), QString())), cbReadStdOut, cbReadStdErr);
const QString shell = useSh ? QStringLiteral("sh") : QStringLiteral("bash");
const QString b64 = QString::fromLatin1(script.toUtf8().toBase64());

QString remover = QString("sudo docker exec -i $CONTAINER_NAME rm %1 ").arg(fileName);
runScript(credentials, replaceVars(remover, amnezia::genBaseVars(credentials, container, QString(), QString())), cbReadStdOut, cbReadStdErr);
const QString command = QStringLiteral("printf '%s' '%1' | base64 -d | sudo docker exec -i $CONTAINER_NAME %2")
.arg(b64, shell);

return e;
return runScript(credentials,
replaceVars(command, amnezia::genBaseVars(credentials, container, QString(), QString())),
cbReadStdOut, cbReadStdErr);
}

ErrorCode SshSession::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
const QString &path, libssh::ScpOverwriteMode overwriteMode)
{
ErrorCode e = ErrorCode::NoError;
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
e = uploadFileToHost(credentials, file.toUtf8(), tmpFileName);
if (e)
return e;
if (overwriteMode != libssh::ScpOverwriteMode::ScpOverwriteExisting
&& overwriteMode != libssh::ScpOverwriteMode::ScpAppendToExisting) {
return ErrorCode::NotImplementedError;
}

QString stdOut;
auto cbReadStd = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};

// mkdir
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
auto baseVars = amnezia::genBaseVars(credentials, container, QString(), QString());

const QString b64 = QString::fromLatin1(file.toUtf8().toBase64());
const QString dir = QFileInfo(path).path();
const QString redirect = (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting)
? QStringLiteral(">>")
: QStringLiteral(">");

e = runScript(credentials, replaceVars(mkdir, amnezia::genBaseVars(credentials, container, QString(), QString())));
const QString command = QStringLiteral("printf '%s' '%1' | base64 -d | "
"sudo docker exec -i $CONTAINER_NAME sh -c 'mkdir -p \"%2\" && cat %3 \"%4\"'")
.arg(b64, dir, redirect, path);

ErrorCode e = runScript(credentials, replaceVars(command, baseVars), cbReadStd, cbReadStd);
if (e)
return e;

if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path),
amnezia::genBaseVars(credentials, container, QString(), QString())),
cbReadStd, cbReadStd);

if (e)
return e;
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName),
amnezia::genBaseVars(credentials, container, QString(), QString())),
cbReadStd, cbReadStd);

if (e)
return e;

e = runScript(credentials,
replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path),
amnezia::genBaseVars(credentials, container, QString(), QString())),
cbReadStd, cbReadStd);

if (e)
return e;
} else
return ErrorCode::NotImplementedError;

if (stdOut.contains("Error") && stdOut.contains("No such container")) {
return ErrorCode::ServerContainerMissingError;
}

runScript(credentials, replaceVars(QString("sudo shred -u %1").arg(tmpFileName), amnezia::genBaseVars(credentials, container, QString(), QString())));
return e;
}

Expand All @@ -188,6 +172,38 @@ QByteArray SshSession::getTextFileFromContainer(DockerContainer container, const
return QByteArray::fromHex(stdOut.toUtf8());
}

QList<QByteArray> SshSession::getTextFilesFromContainer(DockerContainer container, const ServerCredentials &credentials,
const QStringList &paths, ErrorCode &errorCode)
{
errorCode = ErrorCode::NoError;
QList<QByteArray> result;
if (paths.isEmpty()) {
return result;
}

const QString sep = QStringLiteral("ZZAMNSEPZZ");
QString inner;
for (const QString &path : paths) {
inner += QStringLiteral("xxd -p '%1'; echo '%2'; ").arg(path, sep);
}
QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"%2\"")
.arg(ContainerUtils::containerToString(container), inner);

QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data;
return ErrorCode::NoError;
};
errorCode = runScript(credentials, script, cbReadStdOut);

const QStringList parts = stdOut.split(sep);
for (int i = 0; i < paths.size(); ++i) {
const QString hex = (i < parts.size()) ? parts.at(i) : QString();
result.append(QByteArray::fromHex(hex.toUtf8()));
}
return result;
}

ErrorCode SshSession::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
libssh::ScpOverwriteMode overwriteMode)
{
Expand Down
2 changes: 2 additions & 0 deletions client/core/utils/selfhosted/sshSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class SshSession : public QObject
libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, const QString &path,
ErrorCode &errorCode);
QList<QByteArray> getTextFilesFromContainer(DockerContainer container, const ServerCredentials &credentials,
const QStringList &paths, ErrorCode &errorCode);

static QString replaceVars(const QString &script, const Vars &vars);

Expand Down
3 changes: 2 additions & 1 deletion client/server_scripts/openvpn/configure_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ dev tun
ca /opt/amnezia/openvpn/ca.crt
cert /opt/amnezia/openvpn/AmneziaReq.crt
key /opt/amnezia/openvpn/AmneziaReq.key
dh /opt/amnezia/openvpn/dh.pem
dh none
ecdh-curve secp384r1
server $OPENVPN_SUBNET_IP $OPENVPN_SUBNET_MASK
ifconfig-pool-persist ipp.txt
duplicate-cn
Expand Down
5 changes: 3 additions & 2 deletions client/server_scripts/openvpn/run_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ sudo docker run -d \
-p $OPENVPN_PORT:$OPENVPN_PORT/$OPENVPN_TRANSPORT_PROTO \
--name $CONTAINER_NAME $CONTAINER_NAME

amn_i=0; while [ "$(sudo docker inspect -f '{{.State.Running}}' $CONTAINER_NAME 2>/dev/null)" != "true" ] && [ $amn_i -lt 30 ]; do sleep 0.5; amn_i=$((amn_i+1)); done

sudo docker network connect amnezia-dns-net $CONTAINER_NAME

# Create tun device if not exist
Expand All @@ -18,8 +20,7 @@ sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS ne
# 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 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;\
Expand Down
Loading