Compare commits

...
Sign in to create a new pull request.

19 commits

Author SHA1 Message Date
vladimir.kuznetsov
44018ec94c Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/v2ray-container 2023-04-08 18:14:52 +03:00
vladimir.kuznetsov
89577651d6 fixed V2RayKeyMissing error handling 2023-03-27 08:37:28 +03:00
vladimir.kuznetsov
3b2d3a0b34 Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/v2ray-container 2023-03-19 14:40:30 +03:00
vladimir.kuznetsov
fe3228ed74 Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/v2ray-container 2023-02-28 05:34:34 +03:00
vladimir.kuznetsov
917942be94 Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/v2ray-container 2023-02-27 19:51:26 +03:00
vladimir.kuznetsov
6a3875ec7f Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/v2ray-container 2023-02-24 17:54:09 +03:00
vladimir.kuznetsov
e121ca9b53 added v2ray binaries for macos and linux 2023-02-15 18:27:00 +03:00
vladimir.kuznetsov
c23c8e8462 changed the way shadowsocks client config was created
- some code style refactoring
2023-02-14 19:46:08 +03:00
vladimir.kuznetsov
36fae9152f added local port field to v2ray settings page
- some code style refactoring
2023-02-14 08:35:03 +03:00
vladimir.kuznetsov
2b0ba2aff9 added settings page for v2ray 2023-02-13 18:17:42 +03:00
vladimir.kuznetsov
68830021d6 changed v2ray client config generation 2023-02-13 10:05:15 +03:00
vladimir.kuznetsov
6a903792ab Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/v2ray-container 2023-02-13 07:48:47 +03:00
vladimir.kuznetsov
d2d9c33837 Merge branch 'feature/v2ray-container' of github.com:amnezia-vpn/desktop-client into feature/v2ray-container 2023-02-10 20:07:33 +03:00
vladimir.kuznetsov
7d51cb7d58 added implementation of V2RayConfigurator and V2RayProtocol classes 2023-02-10 20:07:17 +03:00
Alexander Gertovskiy
7df3600a62 v2ray-container: added server scripts to start openvpn with v2ray.
* fixed client/server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json
2023-02-09 23:14:34 +02:00
Alexander Gertovskiy
634ca6cbe8 v2ray-container: added server scripts to start openvpn with v2ray.
* added client/server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json
* fixed client/server_scripts/openvpn_v2ray_vmess/template.ovpn
2023-02-09 23:00:14 +02:00
vladimir.kuznetsov
8032e55d7c added a template for the v2ray protocol 2023-02-09 12:41:10 +03:00
Alexander Gertovskiy
dc2a1467c7 v2ray-container: added server scripts to start openvpn with v2ray. 2023-02-08 19:00:51 +02:00
Alexander Gertovskiy
67c36f5b30 v2ray-container: added server scripts to start openvpn with v2ray. 2023-02-08 17:48:27 +02:00
56 changed files with 998 additions and 192 deletions

View file

@ -235,6 +235,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.h
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.h
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.h
${CMAKE_CURRENT_LIST_DIR}/protocols/v2rayprotocol.h
)
set(SOURCES ${SOURCES}
@ -245,6 +246,7 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
${CMAKE_CURRENT_LIST_DIR}/protocols/openvpnovercloakprotocol.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/shadowsocksvpnprotocol.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/wireguardprotocol.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/v2rayprotocol.cpp
)
endif()
@ -558,7 +560,7 @@ if(WIN32)
set(DEPLOY_ARTIFACT_PATH "windows/x32")
endif()
elseif(LINUX)
set(DEPLOY_ARTIFACT_PATH "linux/client")
set(DEPLOY_ARTIFACT_PATH "linux/client/bin")
elseif(APPLE AND NOT IOS)
set(DEPLOY_ARTIFACT_PATH "macos")
endif()

View file

@ -7,24 +7,24 @@
#include "core/servercontroller.h"
#include "containers/containers_defs.h"
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings,
QObject *parent): ConfiguratorBase(settings, parent)
{
}
QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
QString cloakPublicKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::cloak::ckPublicKeyPath, &e);
amnezia::protocols::cloak::ckPublicKeyPath, &e);
cloakPublicKey.replace("\n", "");
QString cloakBypassUid = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::cloak::ckBypassUidKeyPath, &e);
amnezia::protocols::cloak::ckBypassUidKeyPath, &e);
cloakBypassUid.replace("\n", "");
if (e) {

View file

@ -14,7 +14,7 @@ public:
CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genCloakConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
};
#endif // CLOAK_CONFIGURATOR_H

View file

@ -1,8 +1,6 @@
#include "configurator_base.h"
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, QObject *parent)
: QObject{parent},
m_settings(settings)
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings,
QObject *parent): QObject{parent}, m_settings(settings)
{
}

View file

@ -1,4 +1,5 @@
#include "ikev2_configurator.h"
#include <QApplication>
#include <QProcess>
#include <QString>
@ -15,14 +16,13 @@
#include "core/servercontroller.h"
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings,
QObject *parent): ConfiguratorBase(settings, parent)
{
}
Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const ServerCredentials &credentials,
DockerContainer container, ErrorCode *errorCode)
DockerContainer container, ErrorCode *errorCode)
{
Ikev2Configurator::ConnectionData connData;
connData.host = credentials.hostName;
@ -33,21 +33,18 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";
QString scriptCreateCert = QString("certutil -z <(head -c 1024 /dev/urandom) "\
"-S -c \"IKEv2 VPN CA\" -n \"%1\" "\
"-s \"O=IKEv2 VPN,CN=%1\" "\
"-k rsa -g 3072 -v 120 "\
"-d sql:/etc/ipsec.d -t \",,\" "\
"--keyUsage digitalSignature,keyEncipherment "\
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
.arg(connData.clientId);
"-S -c \"IKEv2 VPN CA\" -n \"%1\" "\
"-s \"O=IKEv2 VPN,CN=%1\" "\
"-k rsa -g 3072 -v 120 "\
"-d sql:/etc/ipsec.d -t \",,\" "\
"--keyUsage digitalSignature,keyEncipherment "\
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"").arg(connData.clientId);
ServerController serverController(m_settings);
ErrorCode e = serverController.runContainerScript(credentials, container, scriptCreateCert);
QString scriptExportCert = QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"")
.arg(connData.password)
.arg(connData.clientId)
.arg(certFileName);
.arg(connData.password, connData.clientId, certFileName);
e = serverController.runContainerScript(credentials, container, scriptExportCert);
connData.clientCert = serverController.getTextFileFromContainer(container, credentials, certFileName, &e);
@ -59,8 +56,8 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
return connData;
}
QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
QString Ikev2Configurator::genIkev2Config(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
{
Q_UNUSED(containerConfig)

View file

@ -22,14 +22,14 @@ public:
};
QString genIkev2Config(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
QString genIkev2Config(const ConnectionData &connData);
QString genMobileConfig(const ConnectionData &connData);
QString genStrongSwanConfig(const ConnectionData &connData);
ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
DockerContainer container, ErrorCode *errorCode = nullptr);
ConnectionData prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container,
ErrorCode *errorCode = nullptr);
};
#endif // IKEV2_CONFIGURATOR_H

View file

@ -1,4 +1,5 @@
#include "openvpn_configurator.h"
#include <QApplication>
#include <QProcess>
#include <QString>
@ -19,14 +20,13 @@
#include <openssl/x509.h>
#include <openssl/pem.h>
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings,
QObject *parent): ConfiguratorBase(settings, parent)
{
}
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container, ErrorCode *errorCode)
DockerContainer container, ErrorCode *errorCode)
{
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
connData.host = credentials.hostName;
@ -36,9 +36,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
QString reqFileName = QString("%1/%2.req").
arg(amnezia::protocols::openvpn::clientsDirPath).
arg(connData.clientId);
QString reqFileName = QString("%1/%2.req").arg(amnezia::protocols::openvpn::clientsDirPath, connData.clientId);
ServerController serverController(m_settings);
ErrorCode e = serverController.uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
@ -53,9 +51,11 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
connData.caCert = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e);
connData.caCert = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::openvpn::caCertPath, &e);
connData.clientCert = serverController.getTextFileFromContainer(container, credentials,
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e);
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath, connData.clientId),
&e);
if (e) {
if (errorCode) *errorCode = e;
@ -71,12 +71,12 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ServerController serverController(m_settings);
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
serverController.genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
if (errorCode && *errorCode) {
@ -89,8 +89,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
if (config.contains("$OPENVPN_TA_KEY")) {
config.replace("$OPENVPN_TA_KEY", connData.taKey);
}
else {
} else {
config.replace("<tls-auth>", "");
config.replace("</tls-auth>", "");
}
@ -112,8 +111,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
if (m_settings->routeMode() != Settings::VpnAllSites) {
config.replace("redirect-gateway def1 bypass-dhcp", "");
}
else {
} else {
if(!config.contains("redirect-gateway def1 bypass-dhcp")) {
config.append("redirect-gateway def1 bypass-dhcp\n");
}
@ -151,23 +149,22 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(QString jsonConfig)
return QJsonDocument(json).toJson();
}
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container,
const ServerCredentials &credentials, QString clientId)
ErrorCode OpenVpnConfigurator::signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId)
{
QString script_import = QString("sudo docker exec -i %1 bash -c \"cd /opt/amnezia/openvpn && "
"easyrsa import-req %2/%3.req %3\"")
.arg(ContainerProps::containerToString(container))
.arg(amnezia::protocols::openvpn::clientsDirPath)
.arg(clientId);
"easyrsa import-req %2/%3.req %3\"")
.arg(ContainerProps::containerToString(container),
amnezia::protocols::openvpn::clientsDirPath,
clientId);
QString script_sign = QString("sudo docker exec -i %1 bash -c \"export EASYRSA_BATCH=1; cd /opt/amnezia/openvpn && "
"easyrsa sign-req client %2\"")
.arg(ContainerProps::containerToString(container))
.arg(clientId);
"easyrsa sign-req client %2\"")
.arg(ContainerProps::containerToString(container), clientId);
ServerController serverController(m_settings);
QStringList scriptList {script_import, script_sign};
QString script = serverController.replaceVars(scriptList.join("\n"), serverController.genVarsForScript(credentials, container));
QString script = serverController.replaceVars(scriptList.join("\n"),
serverController.genVarsForScript(credentials, container));
return serverController.runScript(credentials, script);
}
@ -177,8 +174,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
ConnectionData connData;
connData.clientId = Utils::getRandomString(32);
int ret = 0;
int nVersion = 1;
int ret = 0;
int nVersion = 1;
QByteArray clientIdUtf8 = connData.clientId.toUtf8();
@ -211,7 +208,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
// 4. set public key of x509 req
ret = X509_REQ_set_pubkey(x509_req, pKey);
if (ret != 1){
if (ret != 1) {
qWarning() << "Could not set pubkey!";
X509_REQ_free(x509_req);
EVP_PKEY_free(pKey);
@ -220,7 +217,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
// 5. set sign key of x509 req
ret = X509_REQ_sign(x509_req, pKey, EVP_sha256()); // return x509_req->signature->length
if (ret <= 0){
if (ret <= 0) {
qWarning() << "Could not sign request!";
X509_REQ_free(x509_req);
EVP_PKEY_free(pKey);
@ -230,8 +227,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
// save private key
BIO * bp_private = BIO_new(BIO_s_mem());
q_check_ptr(bp_private);
if (PEM_write_bio_PrivateKey(bp_private, pKey, nullptr, nullptr, 0, nullptr, nullptr) != 1)
{
if (PEM_write_bio_PrivateKey(bp_private, pKey, nullptr, nullptr, 0, nullptr, nullptr) != 1) {
qFatal("PEM_write_bio_PrivateKey");
EVP_PKEY_free(pKey);
BIO_free_all(bp_private);

View file

@ -24,20 +24,18 @@ public:
};
QString genOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
QString processConfigWithLocalSettings(QString jsonConfig);
QString processConfigWithExportSettings(QString jsonConfig);
ErrorCode signCert(DockerContainer container,
const ServerCredentials &credentials, QString clientId);
ErrorCode signCert(DockerContainer container, const ServerCredentials &credentials, QString clientId);
private:
ConnectionData createCertRequest();
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container, ErrorCode *errorCode = nullptr);
ConnectionData prepareOpenVpnConfig(const ServerCredentials &credentials, DockerContainer container,
ErrorCode *errorCode = nullptr);
};
#endif // OPENVPN_CONFIGURATOR_H

View file

@ -5,16 +5,16 @@
#include <QJsonDocument>
#include "containers/containers_defs.h"
#include "core/scripts_registry.h"
#include "core/servercontroller.h"
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings,
QObject *parent): ConfiguratorBase(settings, parent)
{
}
QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
@ -28,18 +28,12 @@ QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &c
return "";
}
QJsonObject config;
config.insert("server", credentials.hostName);
config.insert("server_port", "$SHADOWSOCKS_SERVER_PORT");
config.insert("local_port", "$SHADOWSOCKS_LOCAL_PORT");
config.insert("password", ssKey);
config.insert("timeout", 60);
config.insert("method", "$SHADOWSOCKS_CIPHER");
QString ssClientConfig = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::shadowsocks_client_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
serverController.genVarsForScript(credentials, container, containerConfig));
ssClientConfig.replace("$SHADOWSOCKS_PASSWORD", ssKey);
ssClientConfig = serverController.replaceVars(ssClientConfig, serverController.genVarsForScript(credentials, container, containerConfig));
//qDebug().noquote() << textCfg;
return textCfg;
return ssClientConfig;
}

View file

@ -13,7 +13,7 @@ public:
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
};
#endif // SHADOWSOCKS_CONFIGURATOR_H

View file

@ -1,4 +1,5 @@
#include "ssh_configurator.h"
#include <QApplication>
#include <QProcess>
#include <QString>
@ -14,11 +15,9 @@
#include "core/server_defs.h"
#include "utilities.h"
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings,
QObject *parent): ConfiguratorBase(settings, parent)
{
}
QString SshConfigurator::convertOpenSShKey(const QString &key)
@ -73,10 +72,8 @@ void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
// todo: connect by key
// p->setNativeArguments(QString("%1@%2")
// .arg(credentials.userName).arg(credentials.hostName).arg(credentials.password));
}
else {
p->setNativeArguments(QString("%1@%2 -pw %3")
.arg(credentials.userName).arg(credentials.hostName).arg(credentials.password));
} else {
p->setNativeArguments(QString("%1@%2 -pw %3").arg(credentials.userName).arg(credentials.hostName, credentials.password));
}
#else
p->setProgram("/bin/bash");

View file

@ -16,7 +16,6 @@ public:
QProcessEnvironment prepareEnv();
QString convertOpenSShKey(const QString &key);
void openSshTerminal(const ServerCredentials &credentials);
};
#endif // SSH_CONFIGURATOR_H

View file

@ -0,0 +1,46 @@
#include "v2ray_configurator.h"
#include <QFile>
#include <QPair>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include "core/servercontroller.h"
#include "core/scripts_registry.h"
#include "containers/containers_defs.h"
V2RayConfigurator::V2RayConfigurator(std::shared_ptr<Settings> settings,
QObject *parent) : ConfiguratorBase(settings, parent)
{
}
QString V2RayConfigurator::genV2RayConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
QString v2RayVmessClientUuid = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::v2ray::v2rayKeyPath, &e);
if (v2RayVmessClientUuid.isEmpty()) {
if (errorCode) *errorCode = ErrorCode::V2RayKeyMissing;
return "";
}
v2RayVmessClientUuid.replace("\n", "");
if (e) {
if (errorCode) *errorCode = e;
return "";
}
QString v2RayClientConfig = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::v2ray_client_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
v2RayClientConfig.replace("$V2RAY_VMESS_CLIENT_UUID", v2RayVmessClientUuid);
v2RayClientConfig = serverController.replaceVars(v2RayClientConfig,
serverController.genVarsForScript(credentials, container, containerConfig));
return v2RayClientConfig;
}

View file

@ -0,0 +1,20 @@
#ifndef V2RAYCONFIGURATOR_H
#define V2RAYCONFIGURATOR_H
#include <QObject>
#include "configurator_base.h"
using namespace amnezia;
class V2RayConfigurator : ConfiguratorBase
{
public:
V2RayConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genV2RayConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
};
#endif // V2RAYCONFIGURATOR_H

View file

@ -5,6 +5,7 @@
#include "wireguard_configurator.h"
#include "ikev2_configurator.h"
#include "ssh_configurator.h"
#include "v2ray_configurator.h"
#include <QFile>
#include <QJsonObject>
@ -14,8 +15,8 @@
#include "utilities.h"
#include "settings.h"
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings,
QObject *parent) : ConfiguratorBase(settings, parent)
{
openVpnConfigurator = std::shared_ptr<OpenVpnConfigurator>(new OpenVpnConfigurator(settings, this));
shadowSocksConfigurator = std::shared_ptr<ShadowSocksConfigurator>(new ShadowSocksConfigurator(settings, this));
@ -25,25 +26,22 @@ VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *pa
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, this));
}
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode)
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode)
{
switch (proto) {
case Proto::OpenVpn:
return openVpnConfigurator->genOpenVpnConfig(credentials, container, containerConfig, errorCode);
case Proto::ShadowSocks:
return shadowSocksConfigurator->genShadowSocksConfig(credentials, container, containerConfig, errorCode);
case Proto::Cloak:
return cloakConfigurator->genCloakConfig(credentials, container, containerConfig, errorCode);
case Proto::WireGuard:
return wireguardConfigurator->genWireguardConfig(credentials, container, containerConfig, errorCode);
case Proto::Ikev2:
return ikev2Configurator->genIkev2Config(credentials, container, containerConfig, errorCode);
case Proto::V2Ray:
return v2RayConfigurator->genV2RayConfig(credentials, container, containerConfig, errorCode);
default:
return "";
}
@ -62,8 +60,7 @@ QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
if (useAmneziaDns && m_settings->containers(serverIndex).contains(DockerContainer::Dns)) {
dns.first = protocols::dns::amneziaDnsIp;
}
else dns.first = m_settings->primaryDns();
} else dns.first = m_settings->primaryDns();
}
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
dns.second = m_settings->secondaryDns();
@ -73,8 +70,7 @@ QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
return dns;
}
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container,
Proto proto, QString &config)
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config)
{
auto dns = getDnsForConfig(serverIndex);
@ -84,8 +80,7 @@ QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerCo
return config;
}
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container,
Proto proto, QString &config)
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config)
{
processConfigWithDnsSettings(serverIndex, container, proto, config);
@ -95,8 +90,7 @@ QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, Docker
return config;
}
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container,
Proto proto, QString &config)
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config)
{
processConfigWithDnsSettings(serverIndex, container, proto, config);
@ -107,7 +101,7 @@ QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, Docke
}
void VpnConfigurator::updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig,
const QString &stdOut)
const QString &stdOut)
{
Proto mainProto = ContainerProps::defaultProtocol(container);

View file

@ -13,6 +13,7 @@ class CloakConfigurator;
class WireguardConfigurator;
class Ikev2Configurator;
class SshConfigurator;
class V2RayConfigurator;
// Retrieve connection settings from server
class VpnConfigurator : ConfiguratorBase
@ -22,7 +23,7 @@ public:
VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
QPair<QString, QString> getDnsForConfig(int serverIndex);
QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
@ -31,8 +32,7 @@ public:
QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
// workaround for containers which is not support normal configaration
void updateContainerConfigAfterInstallation(DockerContainer container,
QJsonObject &containerConfig, const QString &stdOut);
void updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig, const QString &stdOut);
std::shared_ptr<OpenVpnConfigurator> openVpnConfigurator;
std::shared_ptr<ShadowSocksConfigurator> shadowSocksConfigurator;
@ -40,6 +40,7 @@ public:
std::shared_ptr<WireguardConfigurator> wireguardConfigurator;
std::shared_ptr<Ikev2Configurator> ikev2Configurator;
std::shared_ptr<SshConfigurator> sshConfigurator;
std::shared_ptr<V2RayConfigurator> v2RayConfigurator;
};
#endif // VPN_CONFIGURATOR_H

View file

@ -7,13 +7,11 @@
#include <QTemporaryFile>
#include <QJsonDocument>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include "containers/containers_defs.h"
#include "core/server_defs.h"
#include "core/scripts_registry.h"
@ -21,10 +19,9 @@
#include "core/servercontroller.h"
#include "settings.h"
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
QObject *parent): ConfiguratorBase(settings, parent)
{
}
WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
@ -59,7 +56,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
}
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
DockerContainer container,
const QJsonObject &containerConfig,
ErrorCode *errorCode)
{
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName;
@ -95,8 +94,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
// Calc next IP address
if (ips.isEmpty()) {
nextIpNumber = "2";
}
else {
} else {
int next = ips.last().split(".").last().toInt() + 1;
if (next > 254) {
if (errorCode) *errorCode = ErrorCode::AddressPoolError;
@ -120,14 +118,16 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
}
// Get keys
connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::wireguard::serverPublicKeyPath, &e);
connData.serverPubKey.replace("\n", "");
if (e) {
if (errorCode) *errorCode = e;
return connData;
}
connData.pskKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e);
connData.pskKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::wireguard::serverPskKeyPath, &e);
connData.pskKey.replace("\n", "");
if (e) {
@ -136,32 +136,29 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
}
// Add client to config
QString configPart = QString(
"[Peer]\n"
"PublicKey = %1\n"
"PresharedKey = %2\n"
"AllowedIPs = %3/32\n\n").
arg(connData.clientPubKey).
arg(connData.pskKey).
arg(connData.clientIP);
QString configPart = QString("[Peer]\n"
"PublicKey = %1\n"
"PresharedKey = %2\n"
"AllowedIPs = %3/32\n\n").arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
e = serverController.uploadTextFileToContainer(container, credentials, configPart,
protocols::wireguard::serverConfigPath, libssh::SftpOverwriteMode::SftpAppendToExisting);
protocols::wireguard::serverConfigPath,
libssh::SftpOverwriteMode::SftpAppendToExisting);
if (e) {
if (errorCode) *errorCode = e;
return connData;
}
QString script = "sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'";
e = serverController.runScript(credentials,
serverController.replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'",
serverController.genVarsForScript(credentials, container)));
serverController.replaceVars(script, serverController.genVarsForScript(credentials, container)));
return connData;
}
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ServerController serverController(m_settings);
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),

View file

@ -23,15 +23,14 @@ public:
};
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
QString processConfigWithLocalSettings(QString config);
QString processConfigWithExportSettings(QString config);
private:
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
ConnectionData genClientKeys();
};

View file

@ -55,6 +55,9 @@ QVector<amnezia::Proto> ContainerProps::protocolsForContainer(amnezia::DockerCon
case DockerContainer::Ipsec:
return { Proto::Ikev2 /*, Protocol::L2tp */};
case DockerContainer::V2Ray:
return { Proto::OpenVpn, Proto::V2Ray };
case DockerContainer::Dns:
return { };
@ -86,6 +89,7 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{DockerContainer::Cloak, "OpenVpn over Cloak"},
{DockerContainer::WireGuard, "WireGuard"},
{DockerContainer::Ipsec, QObject::tr("IPsec")},
{DockerContainer::V2Ray, "V2Ray"},
{DockerContainer::TorWebSite, QObject::tr("Web site in Tor network")},
{DockerContainer::Dns, QObject::tr("DNS Service")},
@ -103,6 +107,7 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
"configured with traffic masking by Cloak plugin")},
{DockerContainer::WireGuard, QObject::tr("WireGuard container")},
{DockerContainer::Ipsec, QObject::tr("IPsec container")},
{DockerContainer::V2Ray, QObject::tr("V2Ray container")},
{DockerContainer::TorWebSite, QObject::tr("Web site in Tor network")},
{DockerContainer::Dns, QObject::tr("DNS Service")},
@ -120,6 +125,8 @@ amnezia::ServiceType ContainerProps::containerService(DockerContainer c)
case DockerContainer::ShadowSocks : return ServiceType::Vpn;
case DockerContainer::WireGuard : return ServiceType::Vpn;
case DockerContainer::Ipsec : return ServiceType::Vpn;
case DockerContainer::V2Ray : return ServiceType::Vpn;
case DockerContainer::TorWebSite : return ServiceType::Other;
case DockerContainer::Dns : return ServiceType::Other;
//case DockerContainer::FileShare : return ServiceType::Other;
@ -137,6 +144,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
case DockerContainer::ShadowSocks : return Proto::ShadowSocks;
case DockerContainer::WireGuard : return Proto::WireGuard;
case DockerContainer::Ipsec : return Proto::Ikev2;
case DockerContainer::V2Ray : return Proto::V2Ray;
case DockerContainer::TorWebSite : return Proto::TorWebSite;
case DockerContainer::Dns : return Proto::Dns;

View file

@ -19,6 +19,7 @@ enum DockerContainer {
Cloak,
WireGuard,
Ipsec,
V2Ray,
//non-vpn
TorWebSite,

View file

@ -55,6 +55,7 @@ enum ErrorCode
ShadowSocksExecutableMissing,
CloakExecutableMissing,
AmneziaServiceConnectionFailed,
V2RayExecutableMissing,
ExecutableMissing,
// VPN errors
@ -67,7 +68,9 @@ enum ErrorCode
OpenSslFailed,
OpenVpnExecutableCrashed,
ShadowSocksExecutableCrashed,
CloakExecutableCrashed
CloakExecutableCrashed,
V2RayExecutableCrashed,
V2RayKeyMissing
};
} // namespace amnezia

View file

@ -51,6 +51,10 @@ QString errorString(ErrorCode code){
case (AmneziaServiceConnectionFailed): return QObject::tr("Amnezia helper service error");
case (OpenSslFailed): return QObject::tr("OpenSSL failed");
// V2Ray errors
case (V2RayExecutableMissing): return QObject::tr("V2Ray (v2ray) executable missing");
case (V2RayKeyMissing): return QObject::tr("V2Ray key missing");
// VPN errors
case (OpenVpnAdaptersInUseError): return QObject::tr("Can't connect: another VPN connection is active");
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");

View file

@ -12,6 +12,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
case DockerContainer::ShadowSocks: return QLatin1String("openvpn_shadowsocks");
case DockerContainer::WireGuard: return QLatin1String("wireguard");
case DockerContainer::Ipsec: return QLatin1String("ipsec");
case DockerContainer::V2Ray: return QLatin1String("openvpn_v2ray_vmess");
case DockerContainer::TorWebSite: return QLatin1String("website_tor");
case DockerContainer::Dns: return QLatin1String("dns");
@ -44,6 +45,8 @@ QString amnezia::scriptName(ProtocolScriptType type)
case ProtocolScriptType::container_startup: return QLatin1String("start.sh");
case ProtocolScriptType::openvpn_template: return QLatin1String("template.ovpn");
case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf");
case ProtocolScriptType::v2ray_client_template: return QLatin1String("template_v2ray_client.json");
case ProtocolScriptType::shadowsocks_client_template: return QLatin1String("template_ss_client.json");
}
}

View file

@ -25,7 +25,9 @@ enum ProtocolScriptType {
configure_container,
container_startup,
openvpn_template,
wireguard_template
wireguard_template,
v2ray_client_template,
shadowsocks_client_template
};

View file

@ -516,6 +516,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
const QJsonObject &v2RayConfig = config.value(ProtocolProps::protoToString(Proto::V2Ray)).toObject();
//
Vars vars;
@ -567,6 +568,10 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({{"$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) }});
// V2Ray vars
vars.append({{"$V2RAY_VMESS_PORT", v2RayConfig.value(config_key::port).toString(protocols::v2ray::defaultServerPort) }});
vars.append({{"$V2RAY_SOCKS_LOCAL_PORT", v2RayConfig.value(config_key::local_port).toString(protocols::v2ray::defaultLocalPort) }});
// IPsec vars
vars.append({{"$IPSEC_VPN_L2TP_NET", "192.168.42.0/24"}});
vars.append({{"$IPSEC_VPN_L2TP_POOL", "192.168.42.10-192.168.42.250"}});

View file

@ -52,26 +52,25 @@ ErrorCode OpenVpnOverCloakProtocol::start()
args << "-u";
}
qDebug().noquote() << "OpenVpnOverCloakProtocol::start()"
<< cloakExecPath() << args.join(" ");
qDebug().noquote() << "OpenVpnOverCloakProtocol::start()" << cloakExecPath() << args.join(" ");
m_ckProcess.setProcessChannelMode(QProcess::MergedChannels);
m_ckProcess.setProgram(cloakExecPath());
m_ckProcess.setArguments(args);
connect(&m_ckProcess, &QProcess::readyReadStandardOutput, this, [this](){
connect(&m_ckProcess, &QProcess::readyReadStandardOutput, this, [this]() {
qDebug().noquote() << "ck-client:" << m_ckProcess.readAllStandardOutput();
});
m_errorHandlerConnection = connect(&m_ckProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){
m_errorHandlerConnection = connect(&m_ckProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "OpenVpnOverCloakProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(VpnProtocol::Disconnected);
if (exitStatus != QProcess::NormalExit){
if (exitStatus != QProcess::NormalExit) {
emit protocolError(amnezia::ErrorCode::CloakExecutableCrashed);
stop();
}
if (exitCode !=0 ){
if (exitCode !=0 ) {
emit protocolError(amnezia::ErrorCode::InternalError);
stop();
}
@ -84,8 +83,9 @@ ErrorCode OpenVpnOverCloakProtocol::start()
setConnectionState(VpnConnectionState::Connecting);
return OpenVpnProtocol::start();
} else {
return ErrorCode::CloakExecutableMissing;
}
else return ErrorCode::CloakExecutableMissing;
#endif
}

View file

@ -75,6 +75,7 @@ QMap<amnezia::Proto, QString> ProtocolProps::protocolHumanNames()
{Proto::WireGuard, "WireGuard"},
{Proto::Ikev2, "IKEv2"},
{Proto::L2tp, "L2TP"},
{Proto::V2Ray, "V2Ray"},
{Proto::TorWebSite, "Web site in Tor network"},
{Proto::Dns, "DNS Service"},
@ -96,10 +97,12 @@ amnezia::ServiceType ProtocolProps::protocolService(Proto p)
case Proto::Cloak : return ServiceType::Vpn;
case Proto::ShadowSocks : return ServiceType::Vpn;
case Proto::WireGuard : return ServiceType::Vpn;
case Proto::TorWebSite : return ServiceType::Other;
case Proto::V2Ray : return ServiceType::Vpn;
case Proto::TorWebSite : return ServiceType::Other;
case Proto::Dns : return ServiceType::Other;
case Proto::FileShare : return ServiceType::Other;
default: return ServiceType::Other;
default: return ServiceType::Other;
}
}
@ -113,12 +116,13 @@ int ProtocolProps::defaultPort(Proto p)
case Proto::WireGuard : return 51820;
case Proto::Ikev2 : return -1;
case Proto::L2tp : return -1;
case Proto::V2Ray : return 10086;
case Proto::TorWebSite : return -1;
case Proto::Dns : return 53;
case Proto::FileShare : return 139;
case Proto::Sftp : return 222;
default: return -1;
default: return -1;
}
}
@ -132,12 +136,13 @@ bool ProtocolProps::defaultPortChangeable(Proto p)
case Proto::WireGuard : return true;
case Proto::Ikev2 : return false;
case Proto::L2tp : return false;
case Proto::V2Ray : return true;
case Proto::TorWebSite : return true;
case Proto::Dns : return false;
case Proto::FileShare : return false;
default: return -1;
default: return -1;
}
}
@ -151,6 +156,7 @@ TransportProto ProtocolProps::defaultTransportProto(Proto p)
case Proto::WireGuard : return TransportProto::Udp;
case Proto::Ikev2 : return TransportProto::Udp;
case Proto::L2tp : return TransportProto::Udp;
case Proto::V2Ray : return TransportProto::Tcp;
// non-vpn
case Proto::TorWebSite : return TransportProto::Tcp;
case Proto::Dns : return TransportProto::Udp;
@ -169,12 +175,13 @@ bool ProtocolProps::defaultTransportProtoChangeable(Proto p)
case Proto::WireGuard : return false;
case Proto::Ikev2 : return false;
case Proto::L2tp : return false;
case Proto::V2Ray : return false;
// non-vpn
case Proto::TorWebSite : return false;
case Proto::Dns : return false;
case Proto::FileShare : return false;
case Proto::Sftp : return false;
default: return false;
default: return false;
}
}

View file

@ -124,6 +124,12 @@ constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key";
}
namespace v2ray {
constexpr char v2rayKeyPath[] = "/opt/amnezia/v2ray/v2ray.key";
constexpr char defaultLocalPort[] = "1080";
constexpr char defaultServerPort[] = "10086";
}
namespace sftp {
constexpr char defaultUserName[] = "sftp_user";
@ -148,6 +154,7 @@ enum Proto {
WireGuard,
Ikev2,
L2tp,
V2Ray,
// non-vpn
TorWebSite,

View file

@ -9,7 +9,7 @@
#include <QJsonObject>
ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration, QObject *parent):
OpenVpnProtocol(configuration, parent)
OpenVpnProtocol(configuration, parent)
{
readShadowSocksConfiguration(configuration);
}
@ -32,7 +32,6 @@ ErrorCode ShadowSocksVpnProtocol::start()
return lastError();
}
#ifndef Q_OS_IOS
if (Utils::processIsRunning(Utils::executable("ss-local", false))) {
Utils::killProcessByName(Utils::executable("ss-local", false));
@ -48,30 +47,28 @@ ErrorCode ShadowSocksVpnProtocol::start()
#ifdef Q_OS_LINUX
QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName();
#else
QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName()
<< "--no-delay";
QStringList args = QStringList() << "-c" << m_shadowSocksCfgFile.fileName() << "--no-delay";
#endif
qDebug().noquote() << "ShadowSocksVpnProtocol::start()"
<< shadowSocksExecPath() << args.join(" ");
qDebug().noquote() << "ShadowSocksVpnProtocol::start()" << shadowSocksExecPath() << args.join(" ");
m_ssProcess.setProcessChannelMode(QProcess::MergedChannels);
m_ssProcess.setProgram(shadowSocksExecPath());
m_ssProcess.setArguments(args);
connect(&m_ssProcess, &QProcess::readyReadStandardOutput, this, [this](){
connect(&m_ssProcess, &QProcess::readyReadStandardOutput, this, [this]() {
qDebug().noquote() << "ss-local:" << m_ssProcess.readAllStandardOutput();
});
connect(&m_ssProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus){
connect(&m_ssProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "ShadowSocksVpnProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(VpnProtocol::Disconnected);
if (exitStatus != QProcess::NormalExit){
if (exitStatus != QProcess::NormalExit) {
emit protocolError(amnezia::ErrorCode::ShadowSocksExecutableCrashed);
stop();
}
if (exitCode !=0 ){
if (exitCode !=0 ) {
emit protocolError(amnezia::ErrorCode::InternalError);
stop();
}
@ -84,8 +81,9 @@ ErrorCode ShadowSocksVpnProtocol::start()
setConnectionState(VpnConnectionState::Connecting);
return OpenVpnProtocol::start();
} else {
return ErrorCode::ShadowSocksExecutableMissing;
}
else return ErrorCode::ShadowSocksExecutableMissing;
#else
return ErrorCode::NotImplementedError;
#endif
@ -116,17 +114,5 @@ QString ShadowSocksVpnProtocol::shadowSocksExecPath()
void ShadowSocksVpnProtocol::readShadowSocksConfiguration(const QJsonObject &configuration)
{
QJsonObject shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject();
bool isLocalPortConvertOk = false;
bool isServerPortConvertOk = false;
int localPort = shadowSocksConfig.value("local_port").toString().toInt(&isLocalPortConvertOk);
int serverPort = shadowSocksConfig.value("server_port").toString().toInt(&isServerPortConvertOk);
if (!isLocalPortConvertOk) {
qDebug() << "Error when converting local_port field in ShadowSocks config";
} else if (!isServerPortConvertOk) {
qDebug() << "Error when converting server_port field in ShadowSocks config";
}
shadowSocksConfig["local_port"] = localPort;
shadowSocksConfig["server_port"] = serverPort;
m_shadowSocksConfig = shadowSocksConfig;
m_shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject();
}

View file

@ -1,9 +1,9 @@
#ifndef SHADOWSOCKSVPNPROTOCOL_H
#define SHADOWSOCKSVPNPROTOCOL_H
#include "openvpnprotocol.h"
#include "QProcess"
#include "containers/containers_defs.h"
#include "openvpnprotocol.h"
class ShadowSocksVpnProtocol : public OpenVpnProtocol
{
@ -14,20 +14,18 @@ public:
ErrorCode start() override;
void stop() override;
protected:
void readShadowSocksConfiguration(const QJsonObject &configuration);
protected:
QJsonObject m_shadowSocksConfig;
private:
static QString shadowSocksExecPath();
void readShadowSocksConfiguration(const QJsonObject &configuration);
private:
#ifndef Q_OS_IOS
QProcess m_ssProcess;
#endif
QTemporaryFile m_shadowSocksCfgFile;
static QString shadowSocksExecPath();
};
#endif // SHADOWSOCKSVPNPROTOCOL_H

View file

@ -0,0 +1,108 @@
#include "v2rayprotocol.h"
#include <QThread>
#include <QFileInfo>
#include <QJsonDocument>
#include "utilities.h"
V2RayProtocol::V2RayProtocol(const QJsonObject &configuration, QObject *parent) : OpenVpnProtocol(configuration, parent)
{
writeV2RayConfiguration(configuration);
}
V2RayProtocol::~V2RayProtocol()
{
qDebug() << "V2RayProtocol::~V2RayProtocol";
V2RayProtocol::stop();
QThread::msleep(200);
}
ErrorCode V2RayProtocol::start()
{
#ifndef Q_OS_IOS
if (!QFileInfo::exists(v2RayExecPath())) {
setLastError(ErrorCode::V2RayExecutableMissing);
return lastError();
}
if (Utils::processIsRunning(Utils::executable("v2ray", false))) {
Utils::killProcessByName(Utils::executable("v2ray", false));
}
QStringList args = QStringList() << "-c" << m_v2RayConfigFile.fileName();
qDebug().noquote() << "V2RayProtocol::start()" << v2RayExecPath() << args.join(" ");
m_v2RayProcess.setProcessChannelMode(QProcess::MergedChannels);
m_v2RayProcess.setProgram(v2RayExecPath());
m_v2RayProcess.setArguments(args);
connect(&m_v2RayProcess, &QProcess::readyReadStandardOutput, this, [this]() {
qDebug().noquote() << "V2Ray:" << m_v2RayProcess.readAllStandardOutput();
});
connect(&m_v2RayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "V2RayProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
setConnectionState(VpnProtocol::Disconnected);
if (exitStatus != QProcess::NormalExit) {
emit protocolError(amnezia::ErrorCode::V2RayExecutableCrashed);
stop();
}
if (exitCode != 0 ) {
emit protocolError(amnezia::ErrorCode::InternalError);
stop();
}
});
m_v2RayProcess.start();
m_v2RayProcess.waitForStarted();
if (m_v2RayProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(VpnConnectionState::Connecting);
return OpenVpnProtocol::start();
} else {
return ErrorCode::V2RayExecutableMissing;
}
#else
return ErrorCode::NotImplementedError;
#endif
return ErrorCode::NoError;
}
void V2RayProtocol::stop()
{
OpenVpnProtocol::stop();
qDebug() << "V2RayProtocol::stop()";
#ifndef Q_OS_IOS
m_v2RayProcess.terminate();
#endif
#ifdef Q_OS_WIN
Utils::signalCtrl(m_v2RayProcess.processId(), CTRL_C_EVENT);
#endif
}
void V2RayProtocol::writeV2RayConfiguration(const QJsonObject &configuration)
{
m_v2RayConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::V2Ray)).toObject();
#ifdef QT_DEBUG
m_v2RayConfigFile.setAutoRemove(false);
#endif
m_v2RayConfigFile.open();
m_v2RayConfigFile.write(QJsonDocument(m_v2RayConfig).toJson());
m_v2RayConfigFile.close();
}
const QString V2RayProtocol::v2RayExecPath() const
{
#ifdef Q_OS_WIN
return Utils::executable(QString("v2ray/v2ray"), true);
#else
return Utils::executable(QString("v2ray"), true);
#endif
}

View file

@ -0,0 +1,30 @@
#ifndef V2RAYPROTOCOL_H
#define V2RAYPROTOCOL_H
#include "QProcess"
#include "QTemporaryFile"
#include "openvpnprotocol.h"
class V2RayProtocol : public OpenVpnProtocol
{
public:
V2RayProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~V2RayProtocol() override;
ErrorCode start() override;
void stop() override;
private:
QJsonObject m_v2RayConfig;
QTemporaryFile m_v2RayConfigFile;
#ifndef Q_OS_IOS
QProcess m_v2RayProcess;
#endif
void writeV2RayConfiguration(const QJsonObject &configuration);
const QString v2RayExecPath() const;
};
#endif // V2RAYPROTOCOL_H

View file

@ -9,6 +9,7 @@
#include "shadowsocksvpnprotocol.h"
#include "openvpnovercloakprotocol.h"
#include "wireguardprotocol.h"
#include "v2rayprotocol.h"
#endif
#ifdef Q_OS_WINDOWS
@ -114,6 +115,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject&
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
case DockerContainer::WireGuard: return new WireguardProtocol(configuration);
case DockerContainer::V2Ray: return new V2RayProtocol(configuration);
#endif
default: return nullptr;
}

View file

@ -166,5 +166,13 @@
<file>ui/qml/Pages/PageAdvancedServerSettings.qml</file>
<file>ui/qml/Controls/PopupWarning.qml</file>
<file>ui/qml/Controls/PopupWithTextField.qml</file>
<file>ui/qml/Pages/Protocols/PageProtoV2Ray.qml</file>
<file>server_scripts/openvpn_v2ray_vmess/configure_container.sh</file>
<file>server_scripts/openvpn_v2ray_vmess/Dockerfile</file>
<file>server_scripts/openvpn_v2ray_vmess/run_container.sh</file>
<file>server_scripts/openvpn_v2ray_vmess/start.sh</file>
<file>server_scripts/openvpn_v2ray_vmess/template.ovpn</file>
<file>server_scripts/openvpn_v2ray_vmess/template_v2ray_client.json</file>
<file>server_scripts/openvpn_shadowsocks/template_ss_client.json</file>
</qresource>
</RCC>

View file

@ -1 +1 @@
sudo docker build -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m)
sudo docker build --network host -t $CONTAINER_NAME $DOCKERFILE_FOLDER --build-arg SERVER_ARCH=$(uname -m)

View file

@ -0,0 +1,8 @@
{
"local_port": $SHADOWSOCKS_LOCAL_PORT,
"method": "$SHADOWSOCKS_CIPHER",
"password": "$SHADOWSOCKS_PASSWORD",
"server": "$REMOTE_HOST",
"server_port": $SHADOWSOCKS_SERVER_PORT,
"timeout": 60
}

View file

@ -0,0 +1,51 @@
FROM alpine:3.15
LABEL maintainer="AmneziaVPN"
#Install required packages
RUN apk add --no-cache curl openvpn easy-rsa bash netcat-openbsd dumb-init rng-tools
RUN apk --update upgrade --no-cache
ENV EASYRSA_BATCH 1
ENV PATH="/usr/share/easy-rsa:${PATH}"
RUN mkdir -p /opt/amnezia
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
RUN chmod a+x /opt/amnezia/start.sh
RUN apk add --no-cache v2ray
# Tune network
RUN echo -e " \n\
fs.file-max = 51200 \n\
\n\
net.core.rmem_max = 67108864 \n\
net.core.wmem_max = 67108864 \n\
net.core.netdev_max_backlog = 250000 \n\
net.core.somaxconn = 4096 \n\
\n\
net.ipv4.tcp_syncookies = 1 \n\
net.ipv4.tcp_tw_reuse = 1 \n\
net.ipv4.tcp_tw_recycle = 0 \n\
net.ipv4.tcp_fin_timeout = 30 \n\
net.ipv4.tcp_keepalive_time = 1200 \n\
net.ipv4.ip_local_port_range = 10000 65000 \n\
net.ipv4.tcp_max_syn_backlog = 8192 \n\
net.ipv4.tcp_max_tw_buckets = 5000 \n\
net.ipv4.tcp_fastopen = 3 \n\
net.ipv4.tcp_mem = 25600 51200 102400 \n\
net.ipv4.tcp_rmem = 4096 87380 67108864 \n\
net.ipv4.tcp_wmem = 4096 65536 67108864 \n\
net.ipv4.tcp_mtu_probing = 1 \n\
net.ipv4.tcp_congestion_control = hybla \n\
# for low-latency network, use cubic instead \n\
# net.ipv4.tcp_congestion_control = cubic \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/sysctl.conf && \
mkdir -p /etc/security && \
echo -e " \n\
* soft nofile 51200 \n\
* hard nofile 51200 \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ]
CMD [ "" ]

View file

@ -0,0 +1,70 @@
cat > /opt/amnezia/openvpn/server.conf <<EOF
port $OPENVPN_PORT
proto $OPENVPN_TRANSPORT_PROTO
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
server $OPENVPN_SUBNET_IP $OPENVPN_SUBNET_MASK
ifconfig-pool-persist ipp.txt
duplicate-cn
keepalive 10 120
$OPENVPN_NCP_DISABLE
cipher $OPENVPN_CIPHER
data-ciphers $OPENVPN_CIPHER
auth $OPENVPN_HASH
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
verb 1
tls-server
tls-version-min 1.2
$OPENVPN_TLS_AUTH
$OPENVPN_ADDITIONAL_SERVER_CONFIG
EOF
# V2RAY_VMESS_PORT port for v2ray listening, for example 10086.
# V2RAY_VMESS_CLIENT_UUID client's id and secret as UUID.
# UUID is 32 hexadecimal digits /([0-9a-f]-?){32}/ (128 bit value).
mkdir -p /opt/amnezia/v2ray
cd /opt/amnezia/v2ray
V2RAY_VMESS_CLIENT_UUID=$(cat /proc/sys/kernel/random/uuid)
echo $V2RAY_VMESS_CLIENT_UUID > /opt/amnezia/v2ray/v2ray.key
cat > /opt/amnezia/v2ray/v2ray-server.json <<EOF
{
"log": {
"loglevel": "None"
},
"inbounds": [
{
"port": $V2RAY_VMESS_PORT,
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "$V2RAY_VMESS_CLIENT_UUID",
"level": 1,
"alterId": 64
}
]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
],
"policy": {
"levels": {
"0": {"uplinkOnly": 0}
}
}
}
EOF

View file

@ -0,0 +1,24 @@
# Run container
sudo docker run -d \
--log-driver none \
--restart always \
--cap-add=NET_ADMIN \
-p $V2RAY_VMESS_PORT:$V2RAY_VMESS_PORT/tcp \
--name $CONTAINER_NAME $CONTAINER_NAME
sudo docker network connect amnezia-dns-net $CONTAINER_NAME
# Create tun device if not exist
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /dev/net; if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi'
# Prevent to route packets outside of the container in case if server behind of the NAT
sudo docker exec -i $CONTAINER_NAME sh -c "ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up"
# OpenVPN config
sudo docker exec -i $CONTAINER_NAME bash -c 'mkdir -p /opt/amnezia/openvpn/clients; \
cd /opt/amnezia/openvpn && easyrsa init-pki; \
cd /opt/amnezia/openvpn && easyrsa gen-dh; \
cd /opt/amnezia/openvpn && cp pki/dh.pem /opt/amnezia/openvpn && easyrsa build-ca nopass << EOF yes EOF && easyrsa gen-req AmneziaReq nopass << EOF2 yes EOF2;\
cd /opt/amnezia/openvpn && easyrsa sign-req server AmneziaReq << EOF3 yes EOF3;\
cd /opt/amnezia/openvpn && openvpn --genkey --secret ta.key << EOF4;\
cd /opt/amnezia/openvpn && cp pki/ca.crt pki/issued/AmneziaReq.crt pki/private/AmneziaReq.key /opt/amnezia/openvpn'

View file

@ -0,0 +1,31 @@
#!/bin/bash
# This scripts copied from Amnezia client to Docker container to /opt/amnezia and launched every time container starts
echo "Container startup"
ifconfig eth0:0 $SERVER_IP_ADDRESS netmask 255.255.255.255 up
if [ ! -c /dev/net/tun ]; then mkdir -p /dev/net; mknod /dev/net/tun c 10 200; fi
# Allow traffic on the TUN interface.
iptables -A INPUT -i tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -j ACCEPT
iptables -A OUTPUT -o tun0 -j ACCEPT
# Allow forwarding traffic only from the VPN.
iptables -A FORWARD -i tun0 -o eth0 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
iptables -A FORWARD -i tun0 -o eth1 -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s $OPENVPN_SUBNET_IP/$OPENVPN_SUBNET_CIDR -o eth1 -j MASQUERADE
# kill daemons in case of restart
killall -KILL openvpn
# start daemons if configured
if [ -f /opt/amnezia/openvpn/ca.crt ]; then (openvpn --config /opt/amnezia/openvpn/server.conf --daemon); fi
if [ -f /opt/amnezia/v2ray/v2ray-server.json ]; then (v2ray -config /opt/amnezia/v2ray/v2ray-server.json &); fi
tail -f /dev/null

View file

@ -0,0 +1,39 @@
client
dev tun
proto $OPENVPN_TRANSPORT_PROTO
resolv-retry infinite
nobind
persist-key
persist-tun
$OPENVPN_NCP_DISABLE
cipher $OPENVPN_CIPHER
auth $OPENVPN_HASH
verb 3
tls-client
tls-version-min 1.2
key-direction 1
remote-cert-tls server
redirect-gateway def1 bypass-dhcp
dhcp-option DNS $PRIMARY_DNS
dhcp-option DNS $SECONDARY_DNS
block-outside-dns
socks-proxy 127.0.0.1 $V2RAY_SOCKS_LOCAL_PORT
route $REMOTE_HOST 255.255.255.255 net_gateway
remote 127.0.0.1 $OPENVPN_PORT
$OPENVPN_ADDITIONAL_CLIENT_CONFIG
<ca>
$OPENVPN_CA_CERT
</ca>
<cert>
$OPENVPN_CLIENT_CERT
</cert>
<key>
$OPENVPN_PRIV_KEY
</key>
<tls-auth>
$OPENVPN_TA_KEY
</tls-auth>

View file

@ -0,0 +1,31 @@
{
"inbounds": [
{
"listen": "127.0.0.1",
"port": $V2RAY_SOCKS_LOCAL_PORT,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "$REMOTE_HOST",
"port": $V2RAY_VMESS_PORT,
"users": [
{
"id": "$V2RAY_VMESS_CLIENT_UUID"
}
]
}
]
}
}
]
}

View file

@ -55,6 +55,7 @@ public:
friend class ShadowSocksLogic;
friend class CloakLogic;
friend class UiLogic;
friend class V2RayLogic;
void onUpdatePage() override;
ErrorCode doInstallAction(const std::function<ErrorCode()> &action);

View file

@ -166,7 +166,7 @@ void OpenVpnLogic::onPushButtonSaveClicked()
auto installAction = [this, containerConfig, &newContainerConfig]() {
ServerController serverController(m_settings);
return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
};
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,

View file

@ -50,6 +50,7 @@ QJsonObject ShadowSocksLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
void ShadowSocksLogic::onPushButtonSaveClicked()
{
QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, Proto::ShadowSocks);
protocolConfig = getProtocolConfigFromPage(protocolConfig);
QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer);
QJsonObject newContainerConfig = containerConfig;
@ -108,7 +109,7 @@ void ShadowSocksLogic::onPushButtonSaveClicked()
auto installAction = [this, containerConfig, &newContainerConfig]() {
ServerController serverController(m_settings);
return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
};
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,

View file

@ -0,0 +1,128 @@
#include "V2RayLogic.h"
#include <functional>
#include "core/servercontroller.h"
#include "ui/pages_logic/ServerConfiguringProgressLogic.h"
#include "ui/uilogic.h"
using namespace amnezia;
using namespace PageEnumNS;
V2RayLogic::V2RayLogic(UiLogic *logic, QObject *parent):
PageProtocolLogicBase(logic, parent),
m_lineEditServerPortText{},
m_pushButtonSaveVisible{false},
m_progressBarResetVisible{false},
m_lineEditServerPortEnabled{false},
m_labelInfoVisible{true},
m_labelInfoText{},
m_progressBarResetValue{0},
m_progressBarResetMaximium{100},
m_lineEditLocalPortEnabled{false},
m_lineEditLocalPortText{}
{
}
void V2RayLogic::updateProtocolPage(const QJsonObject &v2RayConfig, DockerContainer container, bool haveAuthData)
{
set_pageEnabled(haveAuthData);
set_pushButtonSaveVisible(haveAuthData);
set_progressBarResetVisible(haveAuthData);
set_lineEditServerPortText(v2RayConfig.value(config_key::port).toString(protocols::v2ray::defaultServerPort));
set_lineEditServerPortEnabled(container == DockerContainer::V2Ray);
set_lineEditLocalPortText(v2RayConfig.value(config_key::local_port).toString(protocols::v2ray::defaultLocalPort));
set_lineEditLocalPortEnabled(container == DockerContainer::V2Ray);
}
QJsonObject V2RayLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
{
oldConfig.insert(config_key::port, lineEditServerPortText());
oldConfig.insert(config_key::local_port, lineEditLocalPortText());
return oldConfig;
}
void V2RayLogic::onPushButtonSaveClicked()
{
QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, Proto::V2Ray);
protocolConfig = getProtocolConfigFromPage(protocolConfig);
QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer);
QJsonObject newContainerConfig = containerConfig;
newContainerConfig.insert(ProtocolProps::protoToString(Proto::V2Ray), protocolConfig);
ServerConfiguringProgressLogic::PageFunc pageFunc;
pageFunc.setEnabledFunc = [this] (bool enabled) -> void {
set_pageEnabled(enabled);
};
ServerConfiguringProgressLogic::ButtonFunc saveButtonFunc;
saveButtonFunc.setVisibleFunc = [this] (bool visible) -> void {
set_pushButtonSaveVisible(visible);
};
ServerConfiguringProgressLogic::LabelFunc waitInfoFunc;
waitInfoFunc.setVisibleFunc = [this] (bool visible) -> void {
set_labelInfoVisible(visible);
};
waitInfoFunc.setTextFunc = [this] (const QString& text) -> void {
set_labelInfoText(text);
};
ServerConfiguringProgressLogic::ProgressFunc progressBarFunc;
progressBarFunc.setVisibleFunc = [this] (bool visible) -> void {
set_progressBarResetVisible(visible);
};
progressBarFunc.setValueFunc = [this] (int value) -> void {
set_progressBarResetValue(value);
};
progressBarFunc.getValueFunc = [this] (void) -> int {
return progressBarResetValue();
};
progressBarFunc.getMaximiumFunc = [this] (void) -> int {
return progressBarResetMaximium();
};
progressBarFunc.setTextVisibleFunc = [this] (bool visible) -> void {
set_progressBarTextVisible(visible);
};
progressBarFunc.setTextFunc = [this] (const QString& text) -> void {
set_progressBarText(text);
};
ServerConfiguringProgressLogic::LabelFunc busyInfoFuncy;
busyInfoFuncy.setTextFunc = [this] (const QString& text) -> void {
set_labelServerBusyText(text);
};
busyInfoFuncy.setVisibleFunc = [this] (bool visible) -> void {
set_labelServerBusyVisible(visible);
};
ServerConfiguringProgressLogic::ButtonFunc cancelButtonFunc;
cancelButtonFunc.setVisibleFunc = [this] (bool visible) -> void {
set_pushButtonCancelVisible(visible);
};
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
auto installAction = [this, containerConfig, &newContainerConfig]() {
ServerController serverController(m_settings);
return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
};
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, newContainerConfig);
m_settings->clearLastConnectionConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer);
}
qDebug() << "Protocol saved with code:" << e << "for" << uiLogic()->m_selectedServerIndex << uiLogic()->m_selectedDockerContainer;
}
void V2RayLogic::onPushButtonCancelClicked()
{
emit uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->cancelDoInstallAction(true);
}

View file

@ -0,0 +1,50 @@
#ifndef V2RAYLOGIC_H
#define V2RAYLOGIC_H
#include "PageProtocolLogicBase.h"
class UiLogic;
class V2RayLogic : public PageProtocolLogicBase
{
Q_OBJECT
AUTO_PROPERTY(bool, lineEditServerPortEnabled)
AUTO_PROPERTY(QString, lineEditServerPortText)
AUTO_PROPERTY(bool, lineEditLocalPortEnabled)
AUTO_PROPERTY(QString, lineEditLocalPortText)
AUTO_PROPERTY(bool, labelInfoVisible)
AUTO_PROPERTY(QString, labelInfoText)
AUTO_PROPERTY(int, progressBarResetValue)
AUTO_PROPERTY(int, progressBarResetMaximium)
AUTO_PROPERTY(bool, progressBarResetVisible)
AUTO_PROPERTY(bool, progressBarTextVisible)
AUTO_PROPERTY(QString, progressBarText)
AUTO_PROPERTY(bool, labelServerBusyVisible)
AUTO_PROPERTY(QString, labelServerBusyText)
AUTO_PROPERTY(bool, pushButtonSaveVisible)
AUTO_PROPERTY(bool, pushButtonCancelVisible)
public:
Q_INVOKABLE void onPushButtonSaveClicked();
Q_INVOKABLE void onPushButtonCancelClicked();
public:
explicit V2RayLogic(UiLogic *uiLogic, QObject *parent = nullptr);
~V2RayLogic() = default;
void updateProtocolPage(const QJsonObject &v2rayConfig, DockerContainer container, bool haveAuthData) override;
QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override;
private:
UiLogic *m_uiLogic;
};
#endif // V2RAYLOGIC_H

View file

@ -0,0 +1,158 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ProtocolEnum 1.0
import "../"
import "../../Controls"
import "../../Config"
PageProtocolBase {
id: root
protocol: ProtocolEnum.V2Ray
logic: UiLogic.protocolLogic(protocol)
BackButton {
id: back
enabled: !logic.pushButtonCancelVisible
}
Caption {
id: caption
text: qsTr("V2Ray Settings")
}
ColumnLayout {
id: content
enabled: logic.pageEnabled
anchors.top: caption.bottom
anchors.left: root.left
anchors.right: root.right
anchors.bottom: pb_save.top
anchors.margins: 20
anchors.topMargin: 10
RowLayout {
Layout.fillWidth: true
LabelType {
Layout.preferredWidth: 0.3 * root.width - 10
height: 31
text: qsTr("Port")
}
TextFieldType {
Layout.fillWidth: true
height: 31
text: logic.lineEditServerPortText
onEditingFinished: {
logic.lineEditServerPortText = text
}
enabled: logic.lineEditServerPortEnabled
}
}
RowLayout {
Layout.fillWidth: true
LabelType {
Layout.preferredWidth: 0.3 * root.width - 10
height: 31
text: qsTr("Local port")
}
TextFieldType {
Layout.fillWidth: true
height: 31
text: logic.lineEditLocalPortText
onEditingFinished: {
logic.lineEditLocalPortText = text
}
enabled: logic.lineEditLocalPortEnabled
}
}
Item {
Layout.fillHeight: true
}
LabelType {
horizontalAlignment: Text.AlignHCenter
Layout.maximumWidth: parent.width
Layout.fillWidth: true
visible: logic.labelServerBusyVisible
text: logic.labelServerBusyText
}
LabelType {
horizontalAlignment: Text.AlignHCenter
Layout.maximumWidth: parent.width
Layout.fillWidth: true
visible: logic.labelInfoVisible
text: logic.labelInfoText
}
}
ProgressBar {
id: progressBar_reset
anchors.fill: pb_save
from: 0
to: logic.progressBarResetMaximium
value: logic.progressBarResetValue
visible: logic.progressBarResetVisible
background: Rectangle {
implicitWidth: parent.width
implicitHeight: parent.height
color: "#100A44"
radius: 4
}
contentItem: Item {
implicitWidth: parent.width
implicitHeight: parent.height
Rectangle {
width: progressBar_reset.visualPosition * parent.width
height: parent.height
radius: 4
color: Qt.rgba(255, 255, 255, 0.15);
}
}
LabelType {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: logic.progressBarText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: "Lato"
font.styleName: "normal"
font.pixelSize: 16
color: "#D4D4D4"
visible: logic.progressBarTextVisible
}
}
BlueButtonType {
id: pb_save
enabled: logic.pageEnabled
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: root.bottom
anchors.bottomMargin: 20
width: root.width - 60
height: 40
text: qsTr("Save and restart VPN")
visible: logic.pushButtonSaveVisible
onClicked: {
logic.onPushButtonSaveClicked()
}
}
BlueButtonType {
anchors.fill: pb_save
text: qsTr("Cancel")
visible: logic.pushButtonCancelVisible
enabled: logic.pushButtonCancelVisible
onClicked: {
logic.onPushButtonCancelClicked()
}
}
}

View file

@ -72,6 +72,7 @@
#include "pages_logic/protocols/ShadowSocksLogic.h"
#include "pages_logic/protocols/OtherProtocolsLogic.h"
#include "pages_logic/protocols/WireGuardLogic.h"
#include "pages_logic/protocols/V2RayLogic.h"
using namespace amnezia;
using namespace PageEnumNS;
@ -93,6 +94,7 @@ UiLogic::UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigur
m_protocolLogicMap.insert(Proto::ShadowSocks, new ShadowSocksLogic(this));
m_protocolLogicMap.insert(Proto::Cloak, new CloakLogic(this));
m_protocolLogicMap.insert(Proto::WireGuard, new WireGuardLogic(this));
m_protocolLogicMap.insert(Proto::V2Ray, new V2RayLogic(this));
m_protocolLogicMap.insert(Proto::Dns, new OtherProtocolsLogic(this));
m_protocolLogicMap.insert(Proto::Sftp, new OtherProtocolsLogic(this));

View file

@ -49,6 +49,7 @@ class PageProtocolLogicBase;
class OpenVpnLogic;
class ShadowSocksLogic;
class CloakLogic;
class V2RayLogic;
class OtherProtocolsLogic;
@ -94,6 +95,7 @@ public:
friend class OpenVpnLogic;
friend class ShadowSocksLogic;
friend class CloakLogic;
friend class V2RayLogic;
friend class OtherProtocolsLogic;

Binary file not shown.

Binary file not shown.

BIN
deploy/data/macos/v2ctl Executable file

Binary file not shown.

BIN
deploy/data/macos/v2ray Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.