From b0b185027e377ecf3fd13e8ad5ba60b6a9a482b3 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 1 Aug 2024 21:37:56 +0300 Subject: [PATCH] Linux IPSec initial --- client/CMakeLists.txt | 9 ++ client/configurators/ikev2_configurator.cpp | 40 +++++++ client/configurators/ikev2_configurator.h | 4 + client/containers/containers_defs.cpp | 9 +- client/core/scripts_registry.cpp | 1 + client/core/scripts_registry.h | 3 +- client/protocols/ikev2_vpn_protocol_linux.cpp | 101 ++++++++++++++++++ client/protocols/ikev2_vpn_protocol_linux.h | 49 +++++++++ client/protocols/protocols_defs.h | 1 + client/protocols/vpnprotocol.cpp | 6 +- client/resources.qrc | 1 + client/server_scripts/ipsec/template.conf | 30 ++++++ ipc/ipc_interface.rep | 6 ++ ipc/ipcserver.cpp | 57 ++++++++++ ipc/ipcserver.h | 6 ++ 15 files changed, 313 insertions(+), 10 deletions(-) create mode 100644 client/protocols/ikev2_vpn_protocol_linux.cpp create mode 100644 client/protocols/ikev2_vpn_protocol_linux.h create mode 100644 client/server_scripts/ipsec/template.conf diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1fc28b82..baad1b9a 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -306,6 +306,15 @@ endif() if(LINUX AND NOT ANDROID) set(LIBS ${LIBS} -static-libstdc++ -static-libgcc -ldl) link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux) + + set(HEADERS ${HEADERS} + ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_linux.h + ) + + set(SOURCES ${SOURCES} + ${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_linux.cpp + ) + endif() if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) diff --git a/client/configurators/ikev2_configurator.cpp b/client/configurators/ikev2_configurator.cpp index 894a0e3d..fea17f49 100644 --- a/client/configurators/ikev2_configurator.cpp +++ b/client/configurators/ikev2_configurator.cpp @@ -64,6 +64,26 @@ QString Ikev2Configurator::createConfig(const ServerCredentials &credentials, Do return ""; } +#if defined(Q_OS_LINUX) + QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::ipsec_template, container), + m_serverController->genVarsForScript(credentials, container, containerConfig)); + + config.replace("$CLIENT_NAME", connData.clientId); + config.replace("$UUID1", QUuid::createUuid().toString()); + config.replace("$SERVER_ADDR", connData.host); + + QJsonObject jConfig; + jConfig[config_key::config] = config; + + jConfig[config_key::hostName] = connData.host; + jConfig[config_key::userName] = connData.clientId; + jConfig[config_key::cert] = QString(connData.clientCert.toBase64()); + jConfig[config_key::cacert] = QString(connData.caCert); + jConfig[config_key::password] = connData.password; + + return QJsonDocument(jConfig).toJson(); +#endif + return genIkev2Config(connData); } @@ -73,6 +93,7 @@ QString Ikev2Configurator::genIkev2Config(const ConnectionData &connData) config[config_key::hostName] = connData.host; config[config_key::userName] = connData.clientId; config[config_key::cert] = QString(connData.clientCert.toBase64()); + config[config_key::cacert] = QString(connData.caCert); config[config_key::password] = connData.password; return QJsonDocument(config).toJson(); @@ -115,3 +136,22 @@ QString Ikev2Configurator::genStrongSwanConfig(const ConnectionData &connData) return config; } + +QString Ikev2Configurator::processConfigWithLocalSettings(const QPair &dns, const bool isApiConfig, + QString &protocolConfigString) +{ + processConfigWithDnsSettings(dns, protocolConfigString); + + QJsonObject json; + json[config_key::config] = protocolConfigString; + return QJsonDocument(json).toJson(); +} + +QString Ikev2Configurator::processConfigWithExportSettings(const QPair &dns, const bool isApiConfig, + QString &protocolConfigString) +{ + processConfigWithDnsSettings(dns, protocolConfigString); + QJsonObject json; + json[config_key::config] = protocolConfigString; + return QJsonDocument(json).toJson(); +} diff --git a/client/configurators/ikev2_configurator.h b/client/configurators/ikev2_configurator.h index e3a85216..673e5139 100644 --- a/client/configurators/ikev2_configurator.h +++ b/client/configurators/ikev2_configurator.h @@ -27,6 +27,10 @@ public: QString genIkev2Config(const ConnectionData &connData); QString genMobileConfig(const ConnectionData &connData); QString genStrongSwanConfig(const ConnectionData &connData); + QString genIPSecConfig(const ConnectionData &connData); + + QString processConfigWithLocalSettings(const QPair &dns, const bool isApiConfig, QString &protocolConfigString); + QString processConfigWithExportSettings(const QPair &dns, const bool isApiConfig, QString &protocolConfigString); ConnectionData prepareIkev2Config(const ServerCredentials &credentials, DockerContainer container, ErrorCode &errorCode); diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index 91d4b067..cdf14db8 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -277,7 +277,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c) bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c) { -#ifdef Q_OS_WINDOWS +#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) return true; #elif defined(Q_OS_IOS) @@ -309,13 +309,6 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c) case DockerContainer::SSXray: return true; default: return false; } - -#elif defined(Q_OS_LINUX) - switch (c) { - case DockerContainer::Ipsec: return false; - default: return true; - } - #else return false; #endif diff --git a/client/core/scripts_registry.cpp b/client/core/scripts_registry.cpp index 95b5df4a..15089992 100644 --- a/client/core/scripts_registry.cpp +++ b/client/core/scripts_registry.cpp @@ -50,6 +50,7 @@ QString amnezia::scriptName(ProtocolScriptType type) case ProtocolScriptType::wireguard_template: return QLatin1String("template.conf"); case ProtocolScriptType::awg_template: return QLatin1String("template.conf"); case ProtocolScriptType::xray_template: return QLatin1String("template.json"); + case ProtocolScriptType::ipsec_template: return QLatin1String("template.conf"); default: return QString(); } } diff --git a/client/core/scripts_registry.h b/client/core/scripts_registry.h index d952dafb..ced7eb00 100644 --- a/client/core/scripts_registry.h +++ b/client/core/scripts_registry.h @@ -28,7 +28,8 @@ enum ProtocolScriptType { openvpn_template, wireguard_template, awg_template, - xray_template + xray_template, + ipsec_template }; diff --git a/client/protocols/ikev2_vpn_protocol_linux.cpp b/client/protocols/ikev2_vpn_protocol_linux.cpp new file mode 100644 index 00000000..9465036b --- /dev/null +++ b/client/protocols/ikev2_vpn_protocol_linux.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#include + +#include + +#include "logger.h" +#include "ikev2_vpn_protocol_linux.h" +#include "utilities.h" +#include "core/ipcclient.h" +#include +#include +#include + + +static Ikev2Protocol* self = nullptr; + + +Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent) : + VpnProtocol(configuration, parent) +{ + self = this; + readIkev2Configuration(configuration); +} + +Ikev2Protocol::~Ikev2Protocol() +{ + qDebug() << "IpsecProtocol::~IpsecProtocol()"; + disconnect_vpn(); + Ikev2Protocol::stop(); +} + +void Ikev2Protocol::stop() +{ + setConnectionState(Vpn::ConnectionState::Disconnected); + qDebug() << "IpsecProtocol::stop()"; +} + + +void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration) +{ + QJsonObject ikev2_data = configuration.value(ProtocolProps::key_proto_config_data(Proto::Ikev2)).toObject(); + m_config = QJsonDocument::fromJson(ikev2_data.value(config_key::config).toString().toUtf8()).object(); + +} + +ErrorCode Ikev2Protocol::start() +{ + STACK_OF(X509) *certstack = sk_X509_new_null(); + BIO *p12 = BIO_new(BIO_s_mem()); + + EVP_PKEY *pkey; + X509 *cert; + + BIO_write(p12, QByteArray::fromBase64(m_config[config_key::cert].toString().toUtf8()), + QByteArray::fromBase64(m_config[config_key::cert].toString().toUtf8()).size()); + + PKCS12 *pkcs12 = d2i_PKCS12_bio(p12, NULL); + PKCS12_parse(pkcs12, m_config[config_key::password].toString().toStdString().c_str(), &pkey, &cert, &certstack); + BIO *bio = BIO_new(BIO_s_mem()); + PEM_write_bio_X509(bio, cert); + + BUF_MEM *mem = NULL; + BIO_get_mem_ptr(bio, &mem); + + std::string pem(mem->data, mem->length); + qDebug() << pem; + + QString alias(pem.c_str()); + + IpcClient::Interface()->writeIPsecUserCert(alias, m_config[config_key::userName].toString()); + IpcClient::Interface()->writeIPsecConfig(m_config[config_key::config].toString()); + IpcClient::Interface()->writeIPsecCaCert(m_config[config_key::cacert].toString(), m_config[config_key::userName].toString()); + IpcClient::Interface()->writeIPsecPrivate(m_config[config_key::cert].toString(), m_config[config_key::userName].toString()); + IpcClient::Interface()->writeIPsecPrivatePass(m_config[config_key::password].toString(), m_config[config_key::userName].toString()); + + + setConnectionState(Vpn::ConnectionState::Connected); + return ErrorCode::NoError; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::create_new_vpn(const QString & vpn_name, + const QString & serv_addr){ + qDebug() << "Ikev2Protocol::create_new_vpn()"; + return true; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::delete_vpn_connection(const QString &vpn_name){ + + return false; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name){ + return false; +} +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool Ikev2Protocol::disconnect_vpn(){ + return true; +} diff --git a/client/protocols/ikev2_vpn_protocol_linux.h b/client/protocols/ikev2_vpn_protocol_linux.h new file mode 100644 index 00000000..11ca2140 --- /dev/null +++ b/client/protocols/ikev2_vpn_protocol_linux.h @@ -0,0 +1,49 @@ +#ifndef IKEV2_VPN_PROTOCOL_LINUX_H +#define IKEV2_VPN_PROTOCOL_LINUX_H + +#include +#include +#include +#include +#include + +#include "vpnprotocol.h" + +#include +#include +#include +#include +#include +#include + +class Ikev2Protocol : public VpnProtocol +{ + Q_OBJECT + +public: + explicit Ikev2Protocol(const QJsonObject& configuration, QObject* parent = nullptr); + virtual ~Ikev2Protocol() override; + + ErrorCode start() override; + void stop() override; + + static QString tunnelName() { return "AmneziaVPN IKEv2"; } + + +private: + void readIkev2Configuration(const QJsonObject &configuration); + +private: + QJsonObject m_config; + + + bool create_new_vpn(const QString & vpn_name, + const QString & serv_addr); + bool delete_vpn_connection(const QString &vpn_name); + + bool connect_to_vpn(const QString & vpn_name); + bool disconnect_vpn(); +}; + + +#endif // IKEV2_VPN_PROTOCOL_LINUX_H diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 56be0d7d..39d0b3aa 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -24,6 +24,7 @@ namespace amnezia constexpr char description[] = "description"; constexpr char name[] = "name"; constexpr char cert[] = "cert"; + constexpr char cacert[] = "cacert"; constexpr char config[] = "config"; constexpr char containers[] = "containers"; diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp index 056089b8..765e86ab 100644 --- a/client/protocols/vpnprotocol.cpp +++ b/client/protocols/vpnprotocol.cpp @@ -16,6 +16,10 @@ #include "ikev2_vpn_protocol_windows.h" #endif +#ifdef Q_OS_LINUX +#include "ikev2_vpn_protocol_linux.h" +#endif + VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject *parent) : QObject(parent), m_connectionState(Vpn::ConnectionState::Unknown), @@ -106,7 +110,7 @@ QString VpnProtocol::vpnGateway() const VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration) { switch (container) { -#if defined(Q_OS_WINDOWS) +#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) case DockerContainer::Ipsec: return new Ikev2Protocol(configuration); #endif #if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) diff --git a/client/resources.qrc b/client/resources.qrc index 98f04802..a12b0805 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -236,5 +236,6 @@ server_scripts/socks5_proxy/Dockerfile server_scripts/socks5_proxy/configure_container.sh server_scripts/socks5_proxy/start.sh + server_scripts/ipsec/template.conf diff --git a/client/server_scripts/ipsec/template.conf b/client/server_scripts/ipsec/template.conf new file mode 100644 index 00000000..53fa44bd --- /dev/null +++ b/client/server_scripts/ipsec/template.conf @@ -0,0 +1,30 @@ +config setup + charondebug="ike 1, knl 1, cfg 0" + uniqueids=no + +conn ikev2-vpn + auto=add + compress=no + type=tunnel + keyexchange=ikev2 + fragmentation=yes + forceencaps=yes + dpdaction=clear + dpddelay=300s + rekey=no + left=%any + leftid=$CLIENT_NAME + leftcert=$CLIENT_NAME.crt + leftsendcert=always + leftsubnet=0.0.0.0/0 + right=%any + rightid=%any + rightauth=rsa + rightsourceip=$IPSEC_VPN_L2TP_NET + rightdns=$PRIMARY_DNS,$SECONDARY_DNS + rightsendcert=never + eap_identity=%identity + ike=aes256-sha1-modp1024,aes128-sha1-modp1024 + esp=aes256-sha1,aes256-sha2_512 + + diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 79f2d042..02e8c524 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -32,5 +32,11 @@ class IpcInterface SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool updateResolvers(const QString& ifname, const QList& resolvers) ); + + SLOT( bool writeIPsecCaCert(QString cacert, QString uuid) ); + SLOT( bool writeIPsecPrivate(QString privKey, QString uuid) ); + SLOT( bool writeIPsecConfig(QString config) ); + SLOT( bool writeIPsecUserCert(QString usercert, QString uuid) ); + SLOT( bool writeIPsecPrivatePass(QString pass, QString uuid) ); }; diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index c734912b..50138458 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -5,6 +5,7 @@ #include #include +#include "qjsonarray.h" #include "router.h" #include "logger.h" @@ -308,6 +309,62 @@ bool IpcServer::disableKillSwitch() return true; } +bool IpcServer::writeIPsecConfig(QString config) +{ + qDebug() << "IPSEC: IPSec config file"; + QString configFile = QString("/etc/ipsec.conf"); + QFile ipSecConfFile(configFile); + if (ipSecConfFile.open(QIODevice::WriteOnly)) { + ipSecConfFile.write(config.toUtf8()); + ipSecConfFile.close(); + } +} + +bool IpcServer::writeIPsecUserCert(QString usercert, QString uuid) +{ + qDebug() << "IPSEC: Write user cert " << uuid; + QString certName = QString("/etc/ipsec.d/certs/%1.crt").arg(uuid); + QFile userCertFile(certName); + if (userCertFile.open(QIODevice::WriteOnly)) { + userCertFile.write(usercert.toUtf8()); + userCertFile.close(); + } +} + +bool IpcServer::writeIPsecCaCert(QString cacert, QString uuid) +{ + qDebug() << "IPSEC: Write CA cert user " << uuid; + QString certName = QString("/etc/ipsec.d/cacerts/%1.crt").arg(uuid); + QFile caCertFile(certName); + if (caCertFile.open(QIODevice::WriteOnly)) { + caCertFile.write(cacert.toUtf8()); + caCertFile.close(); + } +} + +bool IpcServer::writeIPsecPrivate(QString privKey, QString uuid) +{ + qDebug() << "IPSEC: User private key " << uuid; + QString privateKey = QString("/etc/ipsec.d/private/%1.p12").arg(uuid); + QFile pKeyFile(privateKey); + if (pKeyFile.open(QIODevice::WriteOnly)) { + pKeyFile.write(QByteArray::fromBase64(privKey.toUtf8())); + pKeyFile.close(); + } +} + + +bool IpcServer::writeIPsecPrivatePass(QString pass, QString uuid) +{ + qDebug() << "IPSEC: User private key " << uuid; + QFile secretsFile("/etc/ipsec.secrets"); + QString P12 = QString(": P12 %1.p12 \"%2\" \n").arg(uuid, pass); + if (secretsFile.open(QIODevice::WriteOnly | QIODevice::Append)) { + secretsFile.write(P12.toUtf8()); + secretsFile.close(); + } +} + bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) { #ifdef Q_OS_WIN diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index bd474481..43ab3210 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -35,6 +35,12 @@ public: virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; + virtual bool writeIPsecCaCert(QString cacert, QString uuid) override; + virtual bool writeIPsecPrivate(QString privKey, QString uuid) override; + virtual bool writeIPsecConfig(QString config) override; + virtual bool writeIPsecUserCert(QString usercert, QString uuid) override; + virtual bool writeIPsecPrivatePass(QString pass, QString uuid) override; + private: int m_localpid = 0;