mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
feat: add OpenVPN configuration validation and regeneration logic to VpnConfigurationsController
This commit is contained in:
@@ -8,6 +8,58 @@
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "configurators/xray_configurator.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace {
|
||||
bool openVpnConfigHasInlineBlock(const QString &config, const QString &openTag, const QString &closeTag, const QString &marker)
|
||||
{
|
||||
const int start = config.indexOf(openTag);
|
||||
if (start < 0) {
|
||||
return false;
|
||||
}
|
||||
const int end = config.indexOf(closeTag, start + openTag.size());
|
||||
if (end < 0 || end <= start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString block = config.mid(start + openTag.size(), end - (start + openTag.size()));
|
||||
return block.contains(marker);
|
||||
}
|
||||
|
||||
bool openVpnConfigHasClientCredentials(const QString &config)
|
||||
{
|
||||
const bool hasCert = openVpnConfigHasInlineBlock(config, "<cert>", "</cert>", "BEGIN CERTIFICATE");
|
||||
const bool hasKey = openVpnConfigHasInlineBlock(config, "<key>", "</key>", "BEGIN PRIVATE KEY")
|
||||
|| openVpnConfigHasInlineBlock(config, "<key>", "</key>", "BEGIN RSA PRIVATE KEY");
|
||||
return hasCert && hasKey;
|
||||
}
|
||||
|
||||
QString openVpnConfigFromProtocolConfig(const QJsonObject &protocolConfig)
|
||||
{
|
||||
const QString directConfig = protocolConfig.value(config_key::config).toString();
|
||||
if (!directConfig.isEmpty()) {
|
||||
return directConfig;
|
||||
}
|
||||
|
||||
const QString lastConfig = protocolConfig.value(config_key::last_config).toString();
|
||||
if (lastConfig.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const QJsonDocument lastConfigDoc = QJsonDocument::fromJson(lastConfig.toUtf8());
|
||||
if (!lastConfigDoc.isObject()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return lastConfigDoc.object().value(config_key::config).toString();
|
||||
}
|
||||
|
||||
bool openVpnConfigUsesUserPass(const QString &config)
|
||||
{
|
||||
return config.contains("auth-user-pass");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
VpnConfigurationsController::VpnConfigurationsController(const std::shared_ptr<Settings> &settings,
|
||||
QSharedPointer<ServerController> serverController, QObject *parent)
|
||||
: QObject { parent }, m_settings(settings), m_serverController(serverController)
|
||||
@@ -54,6 +106,41 @@ ErrorCode VpnConfigurationsController::createProtocolConfigForContainer(const Se
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
ErrorCode VpnConfigurationsController::ensureContainerConfigReadyForConnection(const ServerCredentials &credentials, const int serverIndex,
|
||||
const DockerContainer container, QJsonObject &containerConfig)
|
||||
{
|
||||
if (!ContainerProps::protocolsForContainer(container).contains(Proto::OpenVpn)) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
QJsonObject openVpnConfig = containerConfig.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject();
|
||||
const bool isThirdParty = openVpnConfig.value(config_key::isThirdPartyConfig).toBool(false);
|
||||
if (isThirdParty) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
const QString config = openVpnConfigFromProtocolConfig(openVpnConfig);
|
||||
const bool configHasCreds = !config.isEmpty() && openVpnConfigHasClientCredentials(config);
|
||||
const bool configAllowsUserPass = !config.isEmpty() && openVpnConfigUsesUserPass(config);
|
||||
if (configHasCreds || configAllowsUserPass) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
if (!credentials.isValid()) {
|
||||
qWarning() << "Missing OpenVPN client credentials and no valid server credentials to regenerate config";
|
||||
return ErrorCode::OpenVpnConfigMissing;
|
||||
}
|
||||
|
||||
const ErrorCode regenError = createProtocolConfigForContainer(credentials, container, containerConfig);
|
||||
if (regenError != ErrorCode::NoError) {
|
||||
qWarning() << "Failed to regenerate OpenVPN client config:" << regenError;
|
||||
return regenError;
|
||||
}
|
||||
|
||||
m_settings->setContainerConfig(serverIndex, container, containerConfig);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode VpnConfigurationsController::createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns,
|
||||
const ServerCredentials &credentials, const DockerContainer container,
|
||||
const QJsonObject &containerConfig, const Proto protocol,
|
||||
|
||||
@@ -18,6 +18,8 @@ public:
|
||||
public slots:
|
||||
ErrorCode createProtocolConfigForContainer(const ServerCredentials &credentials, const DockerContainer container,
|
||||
QJsonObject &containerConfig);
|
||||
ErrorCode ensureContainerConfigReadyForConnection(const ServerCredentials &credentials, const int serverIndex,
|
||||
const DockerContainer container, QJsonObject &containerConfig);
|
||||
ErrorCode createProtocolConfigString(const bool isApiConfig, const QPair<QString, QString> &dns, const ServerCredentials &credentials,
|
||||
const DockerContainer container, const QJsonObject &containerConfig, const Proto protocol,
|
||||
QString &protocolConfigString);
|
||||
|
||||
@@ -56,6 +56,12 @@ void ConnectionController::openConnection()
|
||||
|
||||
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||
const ErrorCode preparationError =
|
||||
vpnConfigurationController.ensureContainerConfigReadyForConnection(credentials, serverIndex, container, containerConfig);
|
||||
if (preparationError != ErrorCode::NoError) {
|
||||
emit connectionErrorOccurred(preparationError);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dns = m_serversModel->getDnsPair(serverIndex);
|
||||
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
#include <configurators/shadowsocks_configurator.h>
|
||||
#include <configurators/wireguard_configurator.h>
|
||||
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/controllers/vpnConfigurationController.h"
|
||||
#endif
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
#include "core/ipcclient.h"
|
||||
#include <protocols/wireguardprotocol.h>
|
||||
@@ -39,54 +34,6 @@
|
||||
#include "core/networkUtilities.h"
|
||||
#include "vpnconnection.h"
|
||||
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
static bool openVpnConfigHasInlineBlock(const QString &config, const QString &openTag, const QString &closeTag, const QString &marker)
|
||||
{
|
||||
const int start = config.indexOf(openTag);
|
||||
if (start < 0) {
|
||||
return false;
|
||||
}
|
||||
const int end = config.indexOf(closeTag, start + openTag.size());
|
||||
if (end < 0 || end <= start) {
|
||||
return false;
|
||||
}
|
||||
const QString block = config.mid(start + openTag.size(), end - (start + openTag.size()));
|
||||
return block.contains(marker);
|
||||
}
|
||||
|
||||
static bool openVpnConfigHasClientCredentials(const QString &config)
|
||||
{
|
||||
const bool hasCert = openVpnConfigHasInlineBlock(config, "<cert>", "</cert>", "BEGIN CERTIFICATE");
|
||||
const bool hasKey = openVpnConfigHasInlineBlock(config, "<key>", "</key>", "BEGIN PRIVATE KEY")
|
||||
|| openVpnConfigHasInlineBlock(config, "<key>", "</key>", "BEGIN RSA PRIVATE KEY");
|
||||
return hasCert && hasKey;
|
||||
}
|
||||
|
||||
static QString openVpnConfigFromProtocolConfig(const QJsonObject &protocolConfig)
|
||||
{
|
||||
const QString directConfig = protocolConfig.value(config_key::config).toString();
|
||||
if (!directConfig.isEmpty()) {
|
||||
return directConfig;
|
||||
}
|
||||
|
||||
const QString lastConfig = protocolConfig.value(config_key::last_config).toString();
|
||||
if (lastConfig.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const QJsonDocument lastConfigDoc = QJsonDocument::fromJson(lastConfig.toUtf8());
|
||||
if (!lastConfigDoc.isObject()) {
|
||||
return {};
|
||||
}
|
||||
return lastConfigDoc.object().value(config_key::config).toString();
|
||||
}
|
||||
|
||||
static bool openVpnConfigUsesUserPass(const QString &config)
|
||||
{
|
||||
return config.contains("auth-user-pass");
|
||||
}
|
||||
#endif
|
||||
|
||||
VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_checkTimer(new QTimer(this))
|
||||
{
|
||||
@@ -356,49 +303,6 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
||||
|
||||
m_vpnProtocol.reset(androidVpnProtocol);
|
||||
#elif defined Q_OS_IOS || defined(MACOS_NE)
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
if (ContainerProps::protocolsForContainer(container).contains(Proto::OpenVpn)) {
|
||||
QJsonObject openVpnConfig = m_vpnConfiguration.value(ProtocolProps::key_proto_config_data(Proto::OpenVpn)).toObject();
|
||||
const bool isThirdParty = openVpnConfig.value(config_key::isThirdPartyConfig).toBool(false);
|
||||
const QString config = openVpnConfigFromProtocolConfig(openVpnConfig);
|
||||
const bool configHasCreds = !config.isEmpty() && openVpnConfigHasClientCredentials(config);
|
||||
const bool configAllowsUserPass = !config.isEmpty() && openVpnConfigUsesUserPass(config);
|
||||
|
||||
if (!isThirdParty && (!configHasCreds && !configAllowsUserPass)) {
|
||||
if (!credentials.isValid()) {
|
||||
qWarning() << "Missing OpenVPN client credentials and no valid server credentials to regenerate config";
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
QJsonObject containerConfig = m_settings->containerConfig(serverIndex, container);
|
||||
const QString storedConfig =
|
||||
openVpnConfigFromProtocolConfig(containerConfig.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject());
|
||||
const bool storedHasCreds = !storedConfig.isEmpty() && openVpnConfigHasClientCredentials(storedConfig);
|
||||
if (!storedHasCreds) {
|
||||
const ErrorCode regenError =
|
||||
vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
|
||||
if (regenError != ErrorCode::NoError) {
|
||||
qWarning() << "Failed to regenerate OpenVPN client config:" << regenError;
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_settings->setContainerConfig(serverIndex, container, containerConfig);
|
||||
|
||||
const QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
|
||||
const QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
|
||||
QJsonObject serverConfig = m_settings->server(serverIndex);
|
||||
m_vpnConfiguration = vpnConfigurationController.createVpnConfiguration({ dns1, dns2 },
|
||||
serverConfig,
|
||||
containerConfig,
|
||||
container);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Proto proto = ContainerProps::defaultProtocol(container);
|
||||
IosController::Instance()->connectVpn(proto, m_vpnConfiguration);
|
||||
connect(&m_checkTimer, &QTimer::timeout, IosController::Instance(), &IosController::checkStatus);
|
||||
|
||||
Reference in New Issue
Block a user