Files
amnezia-client/client/configurators/openvpn_configurator.cpp
T

313 lines
9.7 KiB
C++
Raw Normal View History

2021-04-04 23:12:36 +03:00
#include "openvpn_configurator.h"
2020-12-18 14:57:22 +03:00
#include <QApplication>
#include <QProcess>
#include <QString>
#include <QTemporaryDir>
#include <QDebug>
2021-03-14 21:19:11 +03:00
#include <QTemporaryFile>
2020-12-18 14:57:22 +03:00
2021-04-04 23:12:36 +03:00
#include "core/server_defs.h"
2021-09-09 20:15:44 +03:00
#include "containers/containers_defs.h"
2021-04-04 23:12:36 +03:00
#include "core/scripts_registry.h"
2021-06-12 11:59:36 +03:00
#include "utils.h"
2020-12-18 14:57:22 +03:00
QString OpenVpnConfigurator::getEasyRsaShPath()
{
2021-01-07 20:53:42 +03:00
#ifdef Q_OS_WIN
// easyrsa sh path should looks like
2020-12-18 14:57:22 +03:00
QString easyRsaShPath = QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\easyrsa\\easyrsa";
2021-03-06 15:07:43 +03:00
qDebug().noquote() << "EasyRsa sh path" << easyRsaShPath;
return easyRsaShPath;
2021-08-04 10:08:00 -07:00
2021-01-07 20:53:42 +03:00
#else
2021-01-08 15:43:45 +03:00
return QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/easyrsa";
2021-01-07 20:53:42 +03:00
#endif
2020-12-18 14:57:22 +03:00
}
QProcessEnvironment OpenVpnConfigurator::prepareEnv()
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString pathEnvVar = env.value("PATH");
2021-01-08 15:43:45 +03:00
2021-08-04 10:08:00 -07:00
#if defined Q_OS_WIN
2021-03-06 15:07:43 +03:00
pathEnvVar.clear();
2021-03-14 21:19:11 +03:00
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
2021-06-02 17:51:04 +03:00
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
2021-08-04 10:08:00 -07:00
#elif defined Q_OS_MAC
2021-01-08 15:43:45 +03:00
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
2021-08-04 10:08:00 -07:00
#elif defined Q_OS_LINUX
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/openvpn");
2021-01-08 15:43:45 +03:00
#endif
2020-12-18 14:57:22 +03:00
env.insert("PATH", pathEnvVar);
2021-03-06 15:07:43 +03:00
//qDebug().noquote() << "ENV PATH" << pathEnvVar;
2020-12-18 14:57:22 +03:00
return env;
}
2021-01-08 15:43:45 +03:00
ErrorCode OpenVpnConfigurator::initPKI(const QString &path)
2020-12-18 14:57:22 +03:00
{
2021-09-15 08:03:28 -07:00
#ifndef Q_OS_IOS
2020-12-18 14:57:22 +03:00
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
2021-01-08 15:43:45 +03:00
#ifdef Q_OS_WIN
2021-01-11 16:41:17 +03:00
p.setProcessEnvironment(prepareEnv());
2021-01-08 15:43:45 +03:00
p.setProgram("cmd.exe");
2021-06-02 17:51:04 +03:00
p.setNativeArguments(QString("/C \"ash.exe \"%1\" %2\"").arg(getEasyRsaShPath()).arg("init-pki"));
2021-03-06 15:07:43 +03:00
qDebug().noquote() << "EasyRsa tmp path" << path;
qDebug().noquote() << "EasyRsa args" << p.nativeArguments();
2021-01-08 15:43:45 +03:00
#else
p.setProgram(getEasyRsaShPath());
p.setArguments(QStringList() << "init-pki");
#endif
2020-12-18 14:57:22 +03:00
p.setWorkingDirectory(path);
2021-03-06 15:07:43 +03:00
QObject::connect(&p, &QProcess::channelReadyRead, [&](){
qDebug().noquote() << "Init PKI" << p.readAll();
});
2021-01-08 15:43:45 +03:00
p.start();
2020-12-18 14:57:22 +03:00
p.waitForFinished();
2021-01-08 15:43:45 +03:00
if (p.exitCode() == 0) return ErrorCode::NoError;
else return ErrorCode::EasyRsaError;
2021-09-15 08:03:28 -07:00
#else
return ErrorCode::NotImplementedError;
#endif
2020-12-18 14:57:22 +03:00
}
2021-01-08 15:43:45 +03:00
ErrorCode OpenVpnConfigurator::genReq(const QString &path, const QString &clientId)
2020-12-18 14:57:22 +03:00
{
2021-09-15 08:03:28 -07:00
#ifndef Q_OS_IOS
2020-12-18 14:57:22 +03:00
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
2021-01-08 15:43:45 +03:00
#ifdef Q_OS_WIN
2021-01-11 16:41:17 +03:00
p.setProcessEnvironment(prepareEnv());
2021-01-08 15:43:45 +03:00
p.setProgram("cmd.exe");
2021-06-02 17:51:04 +03:00
p.setNativeArguments(QString("/C \"ash.exe \"%1\" %2 %3 %4\"")
.arg(getEasyRsaShPath())
.arg("gen-req").arg(clientId).arg("nopass"));
2021-03-06 15:07:43 +03:00
qDebug().noquote() << "EasyRsa args" << p.nativeArguments();
2021-01-08 15:43:45 +03:00
#else
p.setArguments(QStringList() << "gen-req" << clientId << "nopass");
p.setProgram(getEasyRsaShPath());
#endif
2020-12-18 14:57:22 +03:00
p.setWorkingDirectory(path);
QObject::connect(&p, &QProcess::channelReadyRead, [&](){
QString data = p.readAll();
2021-03-06 15:07:43 +03:00
qDebug().noquote() << data;
2020-12-18 14:57:22 +03:00
if (data.contains("Common Name (eg: your user, host, or server name)")) {
p.write("\n");
}
});
2021-01-08 15:43:45 +03:00
p.start();
2020-12-18 14:57:22 +03:00
p.waitForFinished();
2021-01-08 15:43:45 +03:00
if (p.exitCode() == 0) return ErrorCode::NoError;
else return ErrorCode::EasyRsaError;
2021-09-15 08:03:28 -07:00
#else
return ErrorCode::NotImplementedError;
#endif
2020-12-18 14:57:22 +03:00
}
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()
{
OpenVpnConfigurator::ConnectionData connData;
2021-04-04 23:12:36 +03:00
connData.clientId = Utils::getRandomString(32);
2020-12-18 14:57:22 +03:00
QTemporaryDir dir;
// if (dir.isValid()) {
// // dir.path() returns the unique directory path
// }
2020-12-18 14:57:22 +03:00
QString path = dir.path();
initPKI(path);
2021-01-08 15:43:45 +03:00
ErrorCode errorCode = genReq(path, connData.clientId);
2020-12-18 14:57:22 +03:00
Q_UNUSED(errorCode)
2020-12-18 14:57:22 +03:00
2021-08-15 11:48:31 -07:00
#if defined Q_OS_LINUX
if (!QDir(path).exists())
{
QDir().mkdir(path);
}
if (!QDir(path + "/pki/").exists())
{
QDir().mkdir(path + "/pki/");
QDir().mkdir(path + "/pki/reqs/");
QDir().mkdir(path + "/pki/private/");
}
#endif
2020-12-18 14:57:22 +03:00
QFile req(path + "/pki/reqs/" + connData.clientId + ".req");
2021-08-15 11:48:31 -07:00
req.open(QIODevice::ReadWrite);
2020-12-18 14:57:22 +03:00
connData.request = req.readAll();
QFile key(path + "/pki/private/" + connData.clientId + ".key");
2021-08-15 11:48:31 -07:00
key.open(QIODevice::ReadWrite);
2020-12-18 14:57:22 +03:00
connData.privKey = key.readAll();
// qDebug().noquote() << connData.request;
// qDebug().noquote() << connData.privKey;
2020-12-18 14:57:22 +03:00
return connData;
}
2021-01-15 23:36:35 +03:00
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(const ServerCredentials &credentials,
2021-04-26 23:19:19 +03:00
DockerContainer container, ErrorCode *errorCode)
2020-12-18 14:57:22 +03:00
{
OpenVpnConfigurator::ConnectionData connData = OpenVpnConfigurator::createCertRequest();
2021-01-06 17:12:24 +03:00
connData.host = credentials.hostName;
2020-12-18 14:57:22 +03:00
2021-01-07 20:53:42 +03:00
if (connData.privKey.isEmpty() || connData.request.isEmpty()) {
2021-01-15 23:36:35 +03:00
if (errorCode) *errorCode = ErrorCode::EasyRsaExecutableMissing;
2021-01-07 20:53:42 +03:00
return connData;
}
2021-04-04 23:12:36 +03:00
QString reqFileName = QString("%1/%2.req").
2021-04-26 22:54:31 +03:00
arg(amnezia::protocols::openvpn::clientsDirPath).
2021-04-04 23:12:36 +03:00
arg(connData.clientId);
2021-01-15 23:36:35 +03:00
ErrorCode e = ServerController::uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
2021-01-06 17:12:24 +03:00
if (e) {
2021-01-15 23:36:35 +03:00
if (errorCode) *errorCode = e;
2021-01-06 17:12:24 +03:00
return connData;
}
2020-12-18 14:57:22 +03:00
2021-04-04 23:12:36 +03:00
e = signCert(container, credentials, connData.clientId);
2021-01-15 23:36:35 +03:00
if (e) {
if (errorCode) *errorCode = e;
return connData;
}
2020-12-18 14:57:22 +03:00
2021-04-26 22:54:31 +03:00
connData.caCert = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e);
2021-04-04 23:12:36 +03:00
connData.clientCert = ServerController::getTextFileFromContainer(container, credentials,
2021-04-26 22:54:31 +03:00
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e);
2021-04-04 23:12:36 +03:00
2021-01-06 17:12:24 +03:00
if (e) {
2021-01-15 23:36:35 +03:00
if (errorCode) *errorCode = e;
2021-01-06 17:12:24 +03:00
return connData;
}
2020-12-18 14:57:22 +03:00
2021-04-26 22:54:31 +03:00
connData.taKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e);
2021-01-15 23:36:35 +03:00
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError;
}
2020-12-18 14:57:22 +03:00
return connData;
}
2021-02-18 15:00:41 +03:00
Settings &OpenVpnConfigurator::m_settings()
{
static Settings s;
return s;
}
2021-01-15 23:36:35 +03:00
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials,
2021-05-07 23:28:37 +03:00
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
2020-12-18 14:57:22 +03:00
{
2021-05-07 23:28:37 +03:00
QString config = ServerController::replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
ServerController::genVarsForScript(credentials, container, containerConfig));
2020-12-18 14:57:22 +03:00
2021-04-26 23:19:19 +03:00
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
2021-04-04 23:12:36 +03:00
if (errorCode && *errorCode) {
return "";
}
2021-01-15 23:36:35 +03:00
2021-05-07 23:28:37 +03:00
config.replace("$OPENVPN_CA_CERT", connData.caCert);
config.replace("$OPENVPN_CLIENT_CERT", connData.clientCert);
config.replace("$OPENVPN_PRIV_KEY", connData.privKey);
2021-05-18 15:50:52 +03:00
if (config.contains("$OPENVPN_TA_KEY")) {
config.replace("$OPENVPN_TA_KEY", connData.taKey);
}
else {
config.replace("<tls-auth>", "");
config.replace("</tls-auth>", "");
}
2020-12-18 14:57:22 +03:00
2021-08-04 10:08:00 -07:00
#if defined Q_OS_MAC || defined(Q_OS_LINUX)
2021-02-21 09:44:53 -08:00
config.replace("block-outside-dns", "");
#endif
2021-05-10 02:33:31 +03:00
2021-02-18 15:00:41 +03:00
//qDebug().noquote() << config;
2021-05-20 15:59:58 +03:00
return config;
2021-05-10 02:33:31 +03:00
}
QString OpenVpnConfigurator::processConfigWithLocalSettings(QString config)
{
// TODO replace DNS if it already set
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
2021-06-12 11:59:36 +03:00
if (m_settings().routeMode() != Settings::VpnAllSites) {
2021-05-10 02:33:31 +03:00
config.replace("redirect-gateway def1 bypass-dhcp", "");
}
2021-05-18 15:50:52 +03:00
else {
if(!config.contains("redirect-gateway def1 bypass-dhcp")) {
config.append("redirect-gateway def1 bypass-dhcp\n");
}
}
2021-05-10 02:33:31 +03:00
2021-08-04 10:08:00 -07:00
#if defined Q_OS_MAC || defined(Q_OS_LINUX)
2021-05-11 09:36:43 -07:00
config.replace("block-outside-dns", "");
2021-05-13 08:23:56 -07:00
QString dnsConf = QString(
"\nscript-security 2\n"
"up %1/update-resolv-conf.sh\n"
"down %1/update-resolv-conf.sh\n").
arg(qApp->applicationDirPath());
config.append(dnsConf);
2021-05-11 09:36:43 -07:00
#endif
2020-12-18 14:57:22 +03:00
return config;
}
2021-03-14 21:19:11 +03:00
2021-05-20 15:59:58 +03:00
QString OpenVpnConfigurator::processConfigWithExportSettings(QString config)
{
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
if(!config.contains("redirect-gateway def1 bypass-dhcp")) {
config.append("redirect-gateway def1 bypass-dhcp\n");
}
2021-08-04 10:08:00 -07:00
#if defined Q_OS_MAC || defined(Q_OS_LINUX)
2021-05-20 15:59:58 +03:00
config.replace("block-outside-dns", "");
#endif
return config;
}
2021-04-04 23:12:36 +03:00
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\"")
2021-09-20 21:51:28 +03:00
.arg(ContainerProps::containerToString(container))
2021-04-26 22:54:31 +03:00
.arg(amnezia::protocols::openvpn::clientsDirPath)
2021-04-04 23:12:36 +03:00
.arg(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\"")
2021-09-20 21:51:28 +03:00
.arg(ContainerProps::containerToString(container))
2021-04-04 23:12:36 +03:00
.arg(clientId);
QStringList scriptList {script_import, script_sign};
QString script = ServerController::replaceVars(scriptList.join("\n"), ServerController::genVarsForScript(credentials, container));
return ServerController::runScript(ServerController::sshParams(credentials), script);
}