Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux IPSec protocol #1031

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,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))
Expand Down
40 changes: 40 additions & 0 deletions client/configurators/ikev2_configurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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();
Expand Down Expand Up @@ -115,3 +136,22 @@ QString Ikev2Configurator::genStrongSwanConfig(const ConnectionData &connData)

return config;
}

QString Ikev2Configurator::processConfigWithLocalSettings(const QPair<QString, QString> &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<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfigString);
QJsonObject json;
json[config_key::config] = protocolConfigString;
return QJsonDocument(json).toJson();
}
4 changes: 4 additions & 0 deletions client/configurators/ikev2_configurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class Ikev2Configurator : public ConfiguratorBase
QString genIkev2Config(const ConnectionData &connData);
QString genMobileConfig(const ConnectionData &connData);
QString genStrongSwanConfig(const ConnectionData &connData);
QString genIPSecConfig(const ConnectionData &connData);

QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);

ConnectionData prepareIkev2Config(const ServerCredentials &credentials,
DockerContainer container, ErrorCode &errorCode);
Expand Down
9 changes: 1 addition & 8 deletions client/containers/containers_defs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions client/core/scripts_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down
3 changes: 2 additions & 1 deletion client/core/scripts_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ enum ProtocolScriptType {
openvpn_template,
wireguard_template,
awg_template,
xray_template
xray_template,
ipsec_template
};


Expand Down
159 changes: 159 additions & 0 deletions client/protocols/ikev2_vpn_protocol_linux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include <QCoreApplication>
#include <QFileInfo>
#include <QProcess>

#include <QThread>

#include <chrono>

#include "core/networkUtilities.h"

#include "logger.h"
#include "ikev2_vpn_protocol_linux.h"
#include "utilities.h"
#include "core/ipcclient.h"
#include <openssl/pkcs12.h>
#include <openssl/bio.h>
#include <openssl/pem.h>


static Ikev2Protocol* self = nullptr;


Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent) :
VpnProtocol(configuration, parent)
{
self = this;
readIkev2Configuration(configuration);
m_routeGateway = NetworkUtilities::getGatewayAndIface();
m_vpnGateway = "192.168.43.10";
m_vpnLocalAddress = "192.168.43.10";
m_remoteAddress = configuration.value(amnezia::config_key::hostName).toString();
m_routeMode = configuration.value(amnezia::config_key::splitTunnelType).toInt();
}

Ikev2Protocol::~Ikev2Protocol()
{
qDebug() << "IpsecProtocol::~IpsecProtocol()";
Ikev2Protocol::stop();
}

void Ikev2Protocol::stop()
{
setConnectionState(Vpn::ConnectionState::Disconnected);
Ikev2Protocol::disconnect_vpn();
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);
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::hostName].toString(),
m_config[config_key::userName].toString());

connect_to_vpn("ikev2-vpn");

if (!IpcClient::Interface()) {
return ErrorCode::AmneziaServiceConnectionFailed;
}

QString connectionStatus;

auto futureResult = IpcClient::Interface()->getTunnelStatus("ikev2-vpn");
futureResult.waitForFinished();

if (futureResult.returnValue().isEmpty()) {
auto futureResult = IpcClient::Interface()->getTunnelStatus("ikev2-vpn");
futureResult.waitForFinished();
}

connectionStatus = futureResult.returnValue();

if (connectionStatus.contains("ESTABLISHED")) {
QStringList lines = connectionStatus.split('\n');
for (auto iter = lines.begin(); iter!=lines.end(); iter++)
{
if (iter->contains("0.0.0.0/0")) {

m_routeGateway = iter->split("===", Qt::SkipEmptyParts).first();
m_routeGateway = m_routeGateway.split(" ").at(2);
m_routeGateway = m_routeGateway.split("/").first();
m_vpnLocalAddress = m_routeGateway;
qDebug() << "m_routeGateway " << m_routeGateway;


// killSwitch toggle
if (QVariant(m_config.value(config_key::killSwitchOption).toString()).toBool()) {
IpcClient::Interface()->enableKillSwitch(m_config, 0);
}

if (m_routeMode == 0) {
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
}

IpcClient::Interface()->StopRoutingIpv6();

}
}
setConnectionState(Vpn::ConnectionState::Connected);
} else {
setConnectionState(Vpn::ConnectionState::Disconnected);
}

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) {
IpcClient::Interface()->startIPsec(vpn_name);
QThread::msleep(3000);
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Ikev2Protocol::disconnect_vpn() {
IpcClient::Interface()->stopIPsec("ikev2-vpn");
IpcClient::Interface()->disableKillSwitch();
IpcClient::Interface()->StartRoutingIpv6();
return true;
}
51 changes: 51 additions & 0 deletions client/protocols/ikev2_vpn_protocol_linux.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#ifndef IKEV2_VPN_PROTOCOL_LINUX_H
#define IKEV2_VPN_PROTOCOL_LINUX_H

#include <QObject>
#include <QProcess>
#include <QString>
#include <QTemporaryFile>
#include <QTimer>

#include "vpnprotocol.h"

#include <string>
#include <memory>
#include <atomic>
#include <thread>
#include <condition_variable>
#include <mutex>

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;
QString m_remoteAddress;
int m_routeMode;


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
3 changes: 2 additions & 1 deletion client/protocols/ikev2_vpn_protocol_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE

void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration)
{
m_config = configuration.value(ProtocolProps::key_proto_config_data(Proto::Ikev2)).toObject();
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()
Expand Down
1 change: 1 addition & 0 deletions client/protocols/protocols_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
6 changes: 5 additions & 1 deletion client/protocols/vpnprotocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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) && !defined(Q_OS_ANDROID)
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))
Expand Down
1 change: 1 addition & 0 deletions client/resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
<file>server_scripts/socks5_proxy/Dockerfile</file>
<file>server_scripts/socks5_proxy/configure_container.sh</file>
<file>server_scripts/socks5_proxy/start.sh</file>
<file>server_scripts/ipsec/template.conf</file>
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
Expand Down
1 change: 1 addition & 0 deletions client/server_scripts/ipsec/configure_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ conn ikev2-cp
dpdtimeout=120
dpdaction=clear
auto=add
authby=rsa-sha1
ikev2=insist
rekey=no
pfs=no
Expand Down
Loading
Loading