Compare commits

..

5 Commits

Author SHA1 Message Date
lunardunno
7f41b8790e suppressing sudo password prompt 2026-05-28 20:24:14 +04:00
lunardunno
6bb3db7684 which LOCK_CMD with sudo
Run the "which" with sudo to check the $LOCK_CMD variable in case the user's PATH variable has incorrect values ​​if the user is not root and is only a member of the sudo group.
2026-05-28 16:26:00 +04:00
lunardunno
201e4063ed "which" as main, "command" as backup for check user 2026-05-28 14:25:58 +04:00
lunardunno
211bf51f1d "which" as main, "command" as backup. 2026-05-28 14:13:15 +04:00
lunardunno
7e0c35ba29 Attempting to use "command -v"
Switching to using "command -v" instead of "which".
2026-05-28 11:18:46 +04:00
34 changed files with 179 additions and 332 deletions

View File

@@ -23,9 +23,6 @@ jobs:
- 'recipes/**'
- 'conanfile.py'
- '.github/workflows/deploy.yml'
- 'cmake/conan_provider.cmake'
- 'cmake/platform_settings.cmake'
- 'cmake/recipes_bootstrap.cmake'
Bake-Prebuilts-Linux:
runs-on: ubuntu-latest

View File

@@ -18,9 +18,9 @@ project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
HOMEPAGE_URL "https://amnezia.org/"
)
# trigger conan to kick off `conan install` globally
find_package(OpenSSL REQUIRED)
if (PREBUILTS_ONLY)
# trigger conan to kick off `conan install`
find_package(OpenSSL REQUIRED)
return()
endif()

View File

@@ -212,32 +212,11 @@ endif()
install(TARGETS ${PROJECT}
DESTINATION ${CMAKE_INSTALL_BINDIR}
RUNTIME_DEPENDENCY_SET client_deps
COMPONENT AmneziaVPN
)
if(APPLE)
set(RUNTIME_DEPS_DIR ${CMAKE_INSTALL_BINDIR}/AmneziaVPN.app/Contents/Frameworks)
else()
set(RUNTIME_DEPS_DIR ${CMAKE_INSTALL_BINDIR})
endif()
install(RUNTIME_DEPENDENCY_SET client_deps
PRE_EXCLUDE_REGEXES
[[api-ms-win-.*]]
[[ext-ms-.*]]
[[kernel32\.dll]]
[[hvsifiletrust\.dll]]
[[libc\.so\..*]] [[libgcc_s\.so\..*]] [[libm\.so\..*]] [[libstdc\+\+\.so\..*]]
[[.*\.framework]]
[[^[Qq]t.*]]
POST_EXCLUDE_REGEXES
[[^.*[\\/]system32[\\/].*\.dll$]]
[[^/lib.*]]
[[^/usr/lib.*]]
DIRECTORIES ${CONAN_RUNTIME_LIB_DIRS}
install(FILES $<TARGET_RUNTIME_DLLS:${PROJECT}>
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT AmneziaVPN
DESTINATION "${RUNTIME_DEPS_DIR}"
)
set(deploy_tool_options "")

View File

@@ -54,6 +54,7 @@ target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
set_target_properties(${PROJECT} PROPERTIES
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Info.plist.in
MACOSX_BUNDLE_ICON_FILE "AppIcon"
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"

View File

@@ -63,18 +63,16 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
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);
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);
if (errorCode != ErrorCode::NoError) {
return connData;
}
connData.caCert = certs.value(0);
connData.clientCert = certs.value(1);
connData.taKey = certs.value(2);
connData.taKey = m_sshSession->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, errorCode);
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
errorCode = ErrorCode::SshScpFailureError;

View File

@@ -165,16 +165,20 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
connData.clientIP = nextIp.toString();
// Get keys
const QList<QByteArray> keys =
m_sshSession->getTextFilesFromContainer(container, credentials, {m_serverPublicKeyPath, m_serverPskKeyPath}, errorCode);
connData.serverPubKey =
m_sshSession->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return connData;
}
connData.serverPubKey = keys.value(0);
connData.serverPubKey.replace("\n", "");
connData.pskKey = keys.value(1);
connData.pskKey = m_sshSession->getTextFileFromContainer(container, credentials, m_serverPskKeyPath, errorCode);
connData.pskKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return connData;
}
// Add client to config
QString configPart = QString("[Peer]\n"
"PublicKey = %1\n"

View File

@@ -486,7 +486,7 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data) const
QJsonObject config;
config[configKey::containers] = arr;
config[configKey::defaultContainer] = configKey::amneziaOpenvpn;
config[configKey::description] = m_serversRepository->nextAvailableServerName();
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)");
QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(data);
@@ -645,7 +645,7 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data, Config
QJsonObject config;
config[configKey::containers] = arr;
config[configKey::defaultContainer] = containerName;
config[configKey::description] = m_serversRepository->nextAvailableServerName();
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
const static QRegularExpression dnsRegExp(
"DNS = "
@@ -699,7 +699,7 @@ QJsonObject ImportController::extractXrayConfig(const QString &data, ConfigTypes
? configKey::amneziaSsxray
: configKey::amneziaXray;
if (description.isEmpty()) {
config[configKey::description] = m_serversRepository->nextAvailableServerName();
config[configKey::description] = m_appSettingsRepository->nextAvailableServerName();
} else {
config[configKey::description] = description;
}

View File

@@ -90,9 +90,10 @@ InstallController::~InstallController()
}
ErrorCode InstallController::setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config,
SshSession &sshSession, bool isUpdate)
bool isUpdate)
{
qDebug().noquote() << "InstallController::setupContainer" << ContainerUtils::containerToString(container);
SshSession sshSession(this);
ErrorCode e = ErrorCode::NoError;
e = isUserInSudo(credentials, sshSession);
@@ -198,7 +199,7 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
ErrorCode errorCode = ErrorCode::NoError;
if (reinstallRequired) {
errorCode = setupContainer(credentials, container, newConfig, sshSession, true);
errorCode = setupContainer(credentials, container, newConfig, true);
} else {
errorCode = configureContainerWorker(credentials, container, newConfig, sshSession);
if (errorCode == ErrorCode::NoError) {
@@ -357,7 +358,7 @@ void InstallController::addEmptyServer(const ServerCredentials &credentials)
serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData;
serverConfig.port = credentials.port;
serverConfig.description = m_serversRepository->nextAvailableServerName();
serverConfig.description = m_appSettingsRepository->nextAvailableServerName();
serverConfig.displayName = serverConfig.description.isEmpty() ? serverConfig.hostName : serverConfig.description;
serverConfig.defaultContainer = DockerContainer::None;
@@ -1032,10 +1033,10 @@ ContainerConfig InstallController::generateConfig(DockerContainer container, int
}
ErrorCode InstallController::installContainer(const ServerCredentials &credentials, DockerContainer container, int port,
TransportProto transportProto, ContainerConfig &config, SshSession &sshSession)
TransportProto transportProto, ContainerConfig &config)
{
config = generateConfig(container, port, transportProto);
return setupContainer(credentials, container, config, sshSession, false);
return setupContainer(credentials, container, config, false);
}
@@ -1141,7 +1142,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
wasContainerInstalled = false;
if (!installedContainers.contains(container)) {
ContainerConfig config;
errorCode = installContainer(credentials, container, port, transportProto, config, sshSession);
errorCode = installContainer(credentials, container, port, transportProto, config);
if (errorCode) {
return errorCode;
}
@@ -1169,7 +1170,7 @@ ErrorCode InstallController::installServer(const ServerCredentials &credentials,
serverConfig.userName = credentials.userName;
serverConfig.password = credentials.secretData;
serverConfig.port = credentials.port;
serverConfig.description = m_serversRepository->nextAvailableServerName();
serverConfig.description = m_appSettingsRepository->nextAvailableServerName();
for (auto iterator = preparedContainers.begin(); iterator != preparedContainers.end(); iterator++) {
serverConfig.containers.insert(iterator.key(), iterator.value());
@@ -1211,7 +1212,7 @@ ErrorCode InstallController::installContainer(const QString &serverId, DockerCon
wasContainerInstalled = false;
if (!installedContainers.contains(container)) {
ContainerConfig config;
errorCode = installContainer(credentials, container, port, transportProto, config, sshSession);
errorCode = installContainer(credentials, container, port, transportProto, config);
if (errorCode) {
return errorCode;
}
@@ -1239,26 +1240,28 @@ ErrorCode InstallController::installContainer(const QString &serverId, DockerCon
return ErrorCode::NoError;
}
ErrorCode InstallController::checkSshConnection(ServerCredentials &credentials, QString &output,
ErrorCode InstallController::checkSshConnection(const ServerCredentials &credentials, QString &output,
std::function<QString()> passphraseCallback)
{
SshSession sshSession(this);
ErrorCode errorCode = ErrorCode::NoError;
if (credentials.secretData.contains("BEGIN") && credentials.secretData.contains("PRIVATE KEY")) {
ServerCredentials processedCredentials = credentials;
if (processedCredentials.secretData.contains("BEGIN") && processedCredentials.secretData.contains("PRIVATE KEY")) {
if (!passphraseCallback) {
return ErrorCode::SshPrivateKeyError;
}
QString decryptedPrivateKey;
errorCode = sshSession.getDecryptedPrivateKey(credentials, decryptedPrivateKey, passphraseCallback);
errorCode = sshSession.getDecryptedPrivateKey(processedCredentials, decryptedPrivateKey, passphraseCallback);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
credentials.secretData = decryptedPrivateKey;
processedCredentials.secretData = decryptedPrivateKey;
}
output = sshSession.checkSshConnection(credentials, errorCode);
output = sshSession.checkSshConnection(processedCredentials, errorCode);
return errorCode;
}

View File

@@ -33,7 +33,7 @@ public:
QObject *parent = nullptr);
~InstallController();
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, SshSession &sshSession, bool isUpdate = false);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
ErrorCode updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
ErrorCode rebootServer(const QString &serverId);
@@ -55,7 +55,7 @@ public:
ErrorCode scanServerForInstalledContainers(const QString &serverId);
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config, SshSession &sshSession);
ErrorCode installContainer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto, ContainerConfig &config);
ErrorCode installServer(const ServerCredentials &credentials, DockerContainer container, int port, TransportProto transportProto,
bool &wasContainerInstalled);
@@ -64,8 +64,7 @@ public:
bool isUpdateDockerContainerRequired(DockerContainer container, const ContainerConfig &oldConfig, const ContainerConfig &newConfig);
ErrorCode checkSshConnection(ServerCredentials &credentials, QString &output,
std::function<QString()> passphraseCallback = nullptr);
ErrorCode checkSshConnection(const ServerCredentials &credentials, QString &output, std::function<QString()> passphraseCallback = nullptr);
bool isServerAlreadyExists(const ServerCredentials &credentials, int &existingServerIndex);

View File

@@ -363,6 +363,6 @@ void SettingsController::disablePremV1MigrationReminder()
QString SettingsController::nextAvailableServerName() const
{
return m_serversRepository->nextAvailableServerName();
return m_appSettingsRepository->nextAvailableServerName();
}

View File

@@ -13,6 +13,7 @@
#include "version.h"
#include "core/controllers/gatewayController.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/errorStrings.h"
#include "core/utils/selfhosted/scriptsRegistry.h"
namespace
@@ -108,7 +109,7 @@ void UpdateController::fetchGatewayUrl()
.then(this, [this, gatewayController](QPair<ErrorCode, QByteArray> result) {
auto [err, gatewayResponse] = result;
if (err != ErrorCode::NoError) {
logger.error() << "Gateway request failed, error code:" << static_cast<int>(err);
logger.error() << errorString(err);
finishUpdateCheck();
return;
}
@@ -249,9 +250,17 @@ void UpdateController::runInstaller()
runLinuxInstaller(kInstallerLocalPath);
#endif
} else {
logger.error() << "Installer download failed, network error:" << static_cast<int>(reply->error())
<< reply->errorString();
logger.error() << "HTTP status:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError
|| reply->error() == QNetworkReply::NetworkError::TimeoutError) {
logger.error() << errorString(ErrorCode::ApiConfigTimeoutError);
} else {
QString err = reply->errorString();
logger.error() << QString::fromUtf8(reply->readAll());
logger.error() << "Network error code:" << QString::number(static_cast<int>(reply->error()));
logger.error() << "Error message:" << err;
logger.error() << "HTTP status:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
logger.error() << errorString(ErrorCode::ApiConfigDownloadError);
}
}
reply->deleteLater();
});

View File

@@ -426,6 +426,26 @@ void SecureAppSettingsRepository::clearSettings()
emit settingsCleared();
}
QString SecureAppSettingsRepository::nextAvailableServerName() const
{
int i = 0;
bool nameExist = false;
do {
i++;
nameExist = false;
QJsonArray servers = QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
for (const QJsonValue &server : servers) {
if (server.toObject().value(configKey::description).toString() == QString("Server") + " " + QString::number(i)) {
nameExist = true;
break;
}
}
} while (nameExist);
return QString("Server") + " " + QString::number(i);
}
void SecureAppSettingsRepository::setInstallationUuid(const QString &uuid)
{
m_settings->setValue("Conf/installationUuid", uuid);

View File

@@ -90,6 +90,8 @@ public:
bool restoreAppConfig(const QByteArray &cfg);
void clearSettings();
QString nextAvailableServerName() const;
QByteArray xraySavedConfigs() const;
void setXraySavedConfigs(const QByteArray &data);

View File

@@ -3,7 +3,6 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonValue>
#include <QSet>
#include <QUuid>
#include "core/utils/serverConfigUtils.h"
@@ -33,45 +32,6 @@ QJsonObject embedStorageServerId(const QString &serverId, const QJsonObject &pay
return o;
}
QString storedServerDisplayName(const SecureServersRepository *repository, const QString &serverId)
{
using Kind = serverConfigUtils::ConfigType;
switch (repository->serverKind(serverId)) {
case Kind::SelfHostedAdmin:
if (const auto cfg = repository->selfHostedAdminConfig(serverId)) {
return cfg->displayName;
}
break;
case Kind::SelfHostedUser:
if (const auto cfg = repository->selfHostedUserConfig(serverId)) {
return cfg->displayName;
}
break;
case Kind::Native:
if (const auto cfg = repository->nativeConfig(serverId)) {
return cfg->displayName;
}
break;
case Kind::AmneziaPremiumV2:
case Kind::AmneziaFreeV3:
case Kind::ExternalPremium:
if (const auto cfg = repository->apiV2Config(serverId)) {
return cfg->displayName;
}
break;
case Kind::AmneziaPremiumV1:
case Kind::AmneziaFreeV2:
if (const auto cfg = repository->legacyApiConfig(serverId)) {
return cfg->displayName;
}
break;
case Kind::Invalid:
default:
break;
}
return {};
}
} // namespace
SecureServersRepository::SecureServersRepository(SecureQSettings *settings, QObject *parent)
@@ -193,28 +153,6 @@ void SecureServersRepository::clearServers()
syncToStorage();
}
QString SecureServersRepository::nextAvailableServerName() const
{
QSet<QString> usedNames;
usedNames.reserve(m_orderedServerIds.size());
for (const QString &serverId : m_orderedServerIds) {
const QString displayName = storedServerDisplayName(this, serverId);
if (!displayName.isEmpty()) {
usedNames.insert(displayName);
}
}
int i = 0;
QString candidate;
do {
i++;
candidate = QStringLiteral("Server %1").arg(i);
} while (usedNames.contains(candidate));
return candidate;
}
QString SecureServersRepository::addServer(const QString &serverId, const QJsonObject &serverJson, serverConfigUtils::ConfigType kind)
{
const QString id = normalizedOrGeneratedServerId(serverId);

View File

@@ -48,8 +48,6 @@ public:
void clearServers();
QString nextAvailableServerName() const;
void invalidateCache();
signals:

View File

@@ -48,9 +48,6 @@ 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);
@@ -64,9 +61,7 @@ namespace libssh {
int connectionResult = watcher.result();
if (connectionResult != SSH_OK) {
ErrorCode errorCode = fromLibsshErrorCode();
disconnectFromHost();
return errorCode;
return fromLibsshErrorCode();
}
std::string authUsername = credentials.userName.toStdString();
@@ -100,20 +95,14 @@ 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) {
ErrorCode errorCode = fromLibsshErrorCode();
disconnectFromHost();
return errorCode;
return fromLibsshErrorCode();
}
}
long sessionTimeoutSec = 86400;
ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &sessionTimeoutSec);
}
return ErrorCode::NoError;
}

View File

@@ -59,7 +59,6 @@ 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);
@@ -70,31 +69,24 @@ 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;
}
statements << lineToExec;
}
qDebug().noquote() << lineToExec;
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;
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
if (error != ErrorCode::NoError) {
return error;
}
}
qDebug().noquote() << "SshSession::runScript finished\n";
@@ -105,25 +97,30 @@ 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;
const QString shell = useSh ? QStringLiteral("sh") : QStringLiteral("bash");
const QString b64 = QString::fromLatin1(script.toUtf8().toBase64());
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 command = QStringLiteral("printf '%s' '%1' | base64 -d | sudo docker exec -i $CONTAINER_NAME %2")
.arg(b64, shell);
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);
return runScript(credentials,
replaceVars(command, amnezia::genBaseVars(credentials, container, QString(), QString())),
cbReadStdOut, cbReadStdErr);
return e;
}
ErrorCode SshSession::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, const QString &file,
const QString &path, libssh::ScpOverwriteMode overwriteMode)
{
if (overwriteMode != libssh::ScpOverwriteMode::ScpOverwriteExisting
&& overwriteMode != libssh::ScpOverwriteMode::ScpAppendToExisting) {
return ErrorCode::NotImplementedError;
}
ErrorCode e = ErrorCode::NoError;
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
e = uploadFileToHost(credentials, file.toUtf8(), tmpFileName);
if (e)
return e;
QString stdOut;
auto cbReadStd = [&](const QString &data, libssh::Client &) {
@@ -131,26 +128,45 @@ ErrorCode SshSession::uploadTextFileToContainer(DockerContainer container, const
return ErrorCode::NoError;
};
auto baseVars = amnezia::genBaseVars(credentials, container, QString(), QString());
// mkdir
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"").arg(path);
const QString b64 = QString::fromLatin1(file.toUtf8().toBase64());
const QString dir = QFileInfo(path).path();
const QString redirect = (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting)
? QStringLiteral(">>")
: QStringLiteral(">");
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);
e = runScript(credentials, replaceVars(mkdir, amnezia::genBaseVars(credentials, container, QString(), QString())));
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;
}
@@ -172,38 +188,6 @@ 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)
{

View File

@@ -28,8 +28,6 @@ public:
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);

View File

@@ -26,8 +26,6 @@ set_target_properties(networkextension PROPERTIES
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks"
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
)
if(DEPLOY)
@@ -116,20 +114,10 @@ target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
find_package(openvpnadapter REQUIRED)
# FIXME(ygurov): https://github.com/conan-io/conan/issues/20034
set_property(TARGET amnezia::openvpnadapter APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_property(TARGET amnezia::openvpnadapter APPEND PROPERTY IMPORTED_CONFIGURATIONS MINSIZEREL)
set_property(TARGET amnezia::openvpnadapter APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET amnezia::openvpnadapter APPEND PROPERTY IMPORTED_CONFIGURATIONS RELWITHDEBINFO)
target_link_libraries(networkextension PRIVATE amnezia::openvpnadapter)
find_package(awg-apple REQUIRED)
target_link_libraries(networkextension PRIVATE amnezia::awg-apple)
find_package(hev-socks5-tunnel REQUIRED)
# FIXME(ygurov): https://github.com/conan-io/conan/issues/20034
set_property(TARGET heiher::hev-socks5-tunnel APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_property(TARGET heiher::hev-socks5-tunnel APPEND PROPERTY IMPORTED_CONFIGURATIONS MINSIZEREL)
set_property(TARGET heiher::hev-socks5-tunnel APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET heiher::hev-socks5-tunnel APPEND PROPERTY IMPORTED_CONFIGURATIONS RELWITHDEBINFO)
target_link_libraries(networkextension PRIVATE heiher::hev-socks5-tunnel)

View File

@@ -1,7 +1,8 @@
if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi
if which apt-get > /dev/null 2>&1 || command -v apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1 || command -v dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
elif which yum > /dev/null 2>&1 || command -v yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1 || command -v zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
elif which pacman > /dev/null 2>&1 || command -v pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1;\
fi;\
if sudo -n which $LOCK_CMD > /dev/null 2>&1 || command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi

View File

@@ -1,8 +1,8 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); opt="--version";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
if pm=$(which apt-get 2>/dev/null || command -v apt-get 2>/dev/null); then opt="--version";\
elif pm=$(which dnf 2>/dev/null || command -v dnf 2>/dev/null); then opt="--version";\
elif pm=$(which yum 2>/dev/null || command -v yum 2>/dev/null); then opt="--version";\
elif pm=$(which zypper 2>/dev/null || command -v zypper 2>/dev/null); then opt="--version";\
elif pm=$(which pacman 2>/dev/null || command -v pacman 2>/dev/null); then opt="--version";\
else pm="uname"; opt="-a";\
fi;\
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\

View File

@@ -5,8 +5,7 @@ dev tun
ca /opt/amnezia/openvpn/ca.crt
cert /opt/amnezia/openvpn/AmneziaReq.crt
key /opt/amnezia/openvpn/AmneziaReq.key
dh none
ecdh-curve secp384r1
dh /opt/amnezia/openvpn/dh.pem
server $OPENVPN_SUBNET_IP $OPENVPN_SUBNET_MASK
ifconfig-pool-persist ipp.txt
duplicate-cn

View File

@@ -7,8 +7,6 @@ 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
@@ -20,7 +18,8 @@ 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 build-ca nopass << EOF yes EOF && easyrsa gen-req AmneziaReq nopass << EOF2 yes EOF2;\
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;\

View File

@@ -1,6 +1,5 @@
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop;\
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm -fv;\
sudo docker images -a --format table | grep amnezia | awk '{print $3, $1 ":" $2}' | xargs sudo docker rmi;\
sudo docker volume ls | grep amnezia | awk '{print $2}' | xargs sudo docker volume rm -f;\
sudo docker network ls | grep amnezia-dns-net | awk '{print $1}' | xargs sudo docker network rm;\
sudo rm -frd /opt/amnezia

View File

@@ -112,7 +112,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {

View File

@@ -279,7 +279,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {

View File

@@ -17,10 +17,6 @@ import "../Components"
PageType {
id: root
enableTimer: false
property bool portDirty: false
function formatTransport(value) {
if (value === "raw") return "RAW (TCP)"
if (value === "xhttp") return "XHTTP"
@@ -43,8 +39,8 @@ PageType {
anchors.right: parent.right
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onActiveFocusChanged: {
if (backButton.enabled && backButton.activeFocus) {
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
@@ -64,6 +60,8 @@ PageType {
delegate: ColumnLayout {
width: listView.width
property alias focusItemId: textFieldWithHeaderType.textField
spacing: 0
Text {
@@ -109,32 +107,13 @@ PageType {
Layout.rightMargin: 16
enabled: listView.enabled
headerText: qsTr("Port")
Binding {
target: textFieldWithHeaderType.textField
property: "text"
value: port
when: !textFieldWithHeaderType.textField.activeFocus
restoreMode: Binding.RestoreNone
}
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator {
bottom: 1; top: 65535
}
textField.onActiveFocusChanged: {
if (textField.activeFocus && textField.text === "" && port !== "") {
textField.text = port
}
}
textField.onTextChanged: {
root.portDirty = (textField.text !== port)
}
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
root.portDirty = false
if (textField.text !== port) port = textField.text
}
checkEmptyText: true
}
@@ -193,8 +172,9 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: listView.enabled
&& (XrayConfigModel.hasUnsavedChanges || root.portDirty)
enabled: visible && textFieldWithHeaderType.textField.text !== ""
&& (XrayConfigModel.hasUnsavedChanges
|| textFieldWithHeaderType.textField.text !== port)
enabled: visible && textFieldWithHeaderType.errorText === ""
text: qsTr("Save")
onClicked: function() {
forceActiveFocus()

View File

@@ -742,7 +742,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {

View File

@@ -95,7 +95,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {

View File

@@ -211,7 +211,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {

View File

@@ -208,7 +208,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {

View File

@@ -20,7 +20,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android")
set(_CONAN_INSTALL_ARGS
"-c=tools.android:cmake_legacy_toolchain=false"
"-c=tools.build:sharedlinkflags=['-Wl,-z,max-page-size=16384']"
"-c=tools.build:exelinkflags=['-Wl,-z,max-page-size=16384']")
"-c=tools.build:exelinkflags=['-Wl,-z,max-page-size=16384']"
"-o=openssl/*:shared=True")
set(CMAKE_ANDROID_STL_TYPE "c++_shared" CACHE STRING "")
endif()
@@ -28,12 +29,6 @@ if (WIN32 OR APPLE)
set(CMAKE_INSTALL_BINDIR ".")
endif()
# Apple NE-based apps do not support any dylibs or variations
# So Qt would use the openssl bundled with system, not application
if (NOT(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR (APPLE AND MACOS_NE)))
list(APPEND _CONAN_INSTALL_ARGS "-o=openssl/*:shared=True")
endif()
list(PREPEND _CONAN_INSTALL_ARGS "--build=missing")
list(JOIN _CONAN_INSTALL_ARGS ";" _CONAN_INSTALL_ARGS_JOINED)
set(CONAN_INSTALL_ARGS ${_CONAN_INSTALL_ARGS_JOINED} CACHE STRING "" FORCE)

View File

@@ -5,7 +5,6 @@ from conan.errors import ConanInvalidConfiguration
from conan.tools.scm import Git
from conan.internal.model.pkg_type import PackageType
from conan.tools.files import chdir
from conan.tools.apple import XCRun
import os
import shutil
@@ -50,10 +49,7 @@ class OpenVPNAdapter(ConanFile):
def build(self):
with chdir(self, self.source_folder):
xcrun = XCRun(self)
xcodebuild = xcrun.find("xcodebuild")
self.run(f"{xcodebuild}"
self.run("xcrun xcodebuild"
" -project OpenVPNAdapter.xcodeproj"
" -scheme OpenVPNAdapter"
" -configuration Release"
@@ -61,20 +57,10 @@ class OpenVPNAdapter(ConanFile):
f" -sdk {self._sdk}"
f' "CONFIGURATION_BUILD_DIR={self.build_folder}"'
f' "BUILT_PRODUCTS_DIR={self.build_folder}"'
" MACH_O_TYPE=staticlib"
" BUILD_LIBRARY_FOR_DISTRIBUTION=YES"
" CODE_SIGNING_ALLOWED=NO"
)
openvpnadapter = os.path.join(self.build_folder, "OpenVPNAdapter.framework", "OpenVPNAdapter")
self.run(f"{xcrun.libtool} -static -o"
f" {openvpnadapter}"
f" {openvpnadapter}"
f' {os.path.join(self.build_folder, "OpenVPNClient.framework", "OpenVPNClient")}'
f' {os.path.join(self.build_folder, "LZ4.framework", "LZ4")}'
f' {os.path.join(self.build_folder, "mbedTLS.framework", "mbedTLS")}'
)
def package(self):
shutil.copytree(os.path.join(self.build_folder, "OpenVPNAdapter.framework"),
os.path.join(self.package_folder, "OpenVPNAdapter.framework"))
@@ -84,4 +70,3 @@ class OpenVPNAdapter(ConanFile):
self.cpp_info.type = PackageType.STATIC
self.cpp_info.package_framework = True
self.cpp_info.location = os.path.join(self.package_folder, "OpenVPNAdapter.framework")
self.cpp_info.frameworks = ["SystemConfiguration"]

View File

@@ -316,9 +316,12 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
endif()
if(APPLE)
set_target_properties(${PROJECT} PROPERTIES
INSTALL_RPATH "@executable_path/../Frameworks"
)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set_target_properties(${PROJECT} PROPERTIES
INSTALL_RPATH "@executable_path/../Frameworks"
BUILD_WITH_INSTALL_RPATH TRUE
)
endif()
find_library(FW_COREFOUNDATION CoreFoundation)
find_library(FW_SYSTEMCONFIG SystemConfiguration)
@@ -425,32 +428,11 @@ endif()
# install target
install(TARGETS ${PROJECT}
DESTINATION ${CMAKE_INSTALL_BINDIR}
RUNTIME_DEPENDENCY_SET service_deps
COMPONENT AmneziaVPN
)
if(APPLE)
set(RUNTIME_DEPS_DIR ${CMAKE_INSTALL_BINDIR}/../Frameworks)
else()
set(RUNTIME_DEPS_DIR ${CMAKE_INSTALL_BINDIR})
endif()
install(RUNTIME_DEPENDENCY_SET service_deps
PRE_EXCLUDE_REGEXES
[[api-ms-win-.*]]
[[ext-ms-.*]]
[[kernel32\.dll]]
[[hvsifiletrust\.dll]]
[[libc\.so\..*]] [[libgcc_s\.so\..*]] [[libm\.so\..*]] [[libstdc\+\+\.so\..*]]
[[.*\.framework]]
[[^[Qq]t.*]]
POST_EXCLUDE_REGEXES
[[^.*[\\/]system32[\\/].*\.dll$]]
[[^/lib.*]]
[[^/usr/lib.*]]
DIRECTORIES ${CONAN_RUNTIME_LIB_DIRS}
install(FILES $<TARGET_RUNTIME_DLLS:${PROJECT}>
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT AmneziaVPN
DESTINATION "${RUNTIME_DEPS_DIR}"
)
qt_generate_deploy_app_script(