Prepare client binary
This commit is contained in:
parent
089c4ff743
commit
935f6d1fa4
17
client/client.pro
Normal file
17
client/client.pro
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
include(../nymea-remoteproxy.pri)
|
||||||
|
|
||||||
|
TARGET = nymea-remoteproxy-client
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
INCLUDEPATH += ../libnymea-remoteproxy
|
||||||
|
|
||||||
|
LIBS += -L$$top_builddir/libnymea-remoteproxyclient/ -lnymea-remoteproxyclient
|
||||||
|
|
||||||
|
SOURCES += main.cpp \
|
||||||
|
proxyclient.cpp
|
||||||
|
|
||||||
|
target.path = /usr/bin
|
||||||
|
INSTALLS += target
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
proxyclient.h
|
||||||
47
client/main.cpp
Normal file
47
client/main.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <QDir>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QSslKey>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QMessageLogger>
|
||||||
|
#include <QSslCertificate>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QSslConfiguration>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QCommandLineOption>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
QCoreApplication application(argc, argv);
|
||||||
|
application.setApplicationName(SERVER_NAME_STRING);
|
||||||
|
application.setOrganizationName("guh");
|
||||||
|
application.setApplicationVersion(SERVER_VERSION_STRING);
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.addVersionOption();
|
||||||
|
parser.setApplicationDescription(QString("\nThe nymea remote proxy server. This server allowes nymea-cloud users and "
|
||||||
|
"registered nymea deamons to establish a tunnel connection.\n\n"
|
||||||
|
"Server version: %1\n"
|
||||||
|
"API version: %2\n\n"
|
||||||
|
"Copyright %3 2018 Simon Stürz <simon.stuerz@guh.io>\n")
|
||||||
|
.arg(SERVER_VERSION_STRING)
|
||||||
|
.arg(API_VERSION_STRING)
|
||||||
|
.arg(QChar(0xA9)));
|
||||||
|
|
||||||
|
|
||||||
|
QCommandLineOption tokenOption(QStringList() << "t" << "token", "The AWS token for authentication.", "token");
|
||||||
|
parser.addOption(tokenOption);
|
||||||
|
|
||||||
|
|
||||||
|
parser.process(application);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return application.exec();
|
||||||
|
}
|
||||||
6
client/proxyclient.cpp
Normal file
6
client/proxyclient.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "proxyclient.h"
|
||||||
|
|
||||||
|
ProxyClient::ProxyClient(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
18
client/proxyclient.h
Normal file
18
client/proxyclient.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef PROXYCLIENT_H
|
||||||
|
#define PROXYCLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyClient : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ProxyClient(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PROXYCLIENT_H
|
||||||
118
libnymea-remoteproxy/authentication/authenticationprocess.cpp
Normal file
118
libnymea-remoteproxy/authentication/authenticationprocess.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include "authenticationprocess.h"
|
||||||
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
|
AuthenticationProcess::AuthenticationProcess(QNetworkAccessManager *manager, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_manager(manager)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticationProcess::useDynamicCredentials(bool dynamicCredentials)
|
||||||
|
{
|
||||||
|
m_dynamicCredentials = dynamicCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticationProcess::requestDynamicCredentials()
|
||||||
|
{
|
||||||
|
QNetworkReply *reply = m_manager->get(QNetworkRequest(QUrl("http://169.254.169.254/latest/meta-data/iam/security-credentials/EC2-Remote-Connection-Proxy-Role")));
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &AuthenticationProcess::onDynamicCredentialsReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticationProcess::startVerificationProcess()
|
||||||
|
{
|
||||||
|
if (m_process->state() != QProcess::NotRunning) {
|
||||||
|
qCWarning(dcAuthenticator()) << "Authentication process already running. Killing the running process and restart.";
|
||||||
|
m_process->kill();
|
||||||
|
m_process->deleteLater();
|
||||||
|
m_process = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_process = new QProcess(this);
|
||||||
|
connect(m_process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &AuthenticationProcess::onProcessFinished);
|
||||||
|
|
||||||
|
// Create request map
|
||||||
|
QVariantMap request;
|
||||||
|
request.insert("token", m_token);
|
||||||
|
|
||||||
|
|
||||||
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
env.insert("AWS_DEFAULT_REGION", "eu-west-1");
|
||||||
|
|
||||||
|
if (m_dynamicCredentials) {
|
||||||
|
env.insert("AWS_ACCESS_KEY_ID", m_awsAccessKeyId);
|
||||||
|
env.insert("AWS_SECRET_ACCESS_KEY", m_awsSecretAccessKey);
|
||||||
|
env.insert("AWS_SESSION_TOKEN", m_awsSessionToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output file name
|
||||||
|
m_resultFileName = "/tmp/" + QUuid::createUuid().toString().remove("{").remove("}").remove("-") + ".json";
|
||||||
|
|
||||||
|
qCDebug(dcAuthenticator()) << "Start authenticator process and store result in" << m_resultFileName;
|
||||||
|
|
||||||
|
m_process->start("aws", { "lambda", "invoke",
|
||||||
|
"--function-name", "system-services-authorizer-dev-checkToken",
|
||||||
|
"--invocation-type", "RequestResponse",
|
||||||
|
"--payload", QString("'%1'").arg(QString::fromUtf8(QJsonDocument::fromVariant(request).toJson())),
|
||||||
|
m_resultFileName });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticationProcess::onDynamicCredentialsReady()
|
||||||
|
{
|
||||||
|
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if (reply->error()) {
|
||||||
|
qCWarning(dcAuthenticator()) << "Dynamic credentials reply error: " << reply->errorString();
|
||||||
|
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data = reply->readAll();
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||||
|
|
||||||
|
if(error.error != QJsonParseError::NoError) {
|
||||||
|
qCWarning(dcAuthenticator()) << "Failed to parse dynamic credentials reply data" << data << ":" << error.errorString();
|
||||||
|
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||||
|
qCDebug(dcAuthenticator()) << "-->" << response;
|
||||||
|
|
||||||
|
m_awsAccessKeyId = response.value("AccessKeyId").toString();
|
||||||
|
m_awsSecretAccessKey = response.value("SecretAccessKey").toString();
|
||||||
|
m_awsSessionToken = response.value("Token").toString();
|
||||||
|
|
||||||
|
startVerificationProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticationProcess::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||||
|
{
|
||||||
|
qCDebug(dcAuthenticator()) << "Authentication process finished" << exitCode << exitStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthenticationProcess::authenticate(const QString &token)
|
||||||
|
{
|
||||||
|
qCDebug(dcAuthenticator()) << "Start authentication process for token";
|
||||||
|
m_token = token;
|
||||||
|
|
||||||
|
if (m_dynamicCredentials) {
|
||||||
|
// Request the access information
|
||||||
|
requestDynamicCredentials();
|
||||||
|
} else {
|
||||||
|
startVerificationProcess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
libnymea-remoteproxy/authentication/authenticationprocess.h
Normal file
49
libnymea-remoteproxy/authentication/authenticationprocess.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef AUTHENTICATIONPROCESS_H
|
||||||
|
#define AUTHENTICATIONPROCESS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
|
#include "authenticator.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
|
class AuthenticationProcess : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit AuthenticationProcess(QNetworkAccessManager *manager, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void useDynamicCredentials(bool dynamicCredentials);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_token;
|
||||||
|
QString m_resultFileName;
|
||||||
|
|
||||||
|
bool m_dynamicCredentials = true;
|
||||||
|
QString m_awsAccessKeyId;
|
||||||
|
QString m_awsSecretAccessKey;
|
||||||
|
QString m_awsSessionToken;
|
||||||
|
|
||||||
|
QNetworkAccessManager *m_manager = nullptr;
|
||||||
|
QProcess *m_process = nullptr;
|
||||||
|
|
||||||
|
void requestDynamicCredentials();
|
||||||
|
void startVerificationProcess();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void authenticationFinished(Authenticator::AuthenticationError error);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onDynamicCredentialsReady();
|
||||||
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void authenticate(const QString &token);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AUTHENTICATIONPROCESS_H
|
||||||
@ -7,7 +7,11 @@ AuthenticationReply::AuthenticationReply(ProxyClient *proxyClient, QObject *pare
|
|||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_proxyClient(proxyClient)
|
m_proxyClient(proxyClient)
|
||||||
{
|
{
|
||||||
|
m_timer = new QTimer(this);
|
||||||
|
m_timer->setInterval(10000);
|
||||||
|
m_timer->setSingleShot(true);
|
||||||
|
|
||||||
|
m_process = new QProcess(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyClient *AuthenticationReply::proxyClient() const
|
ProxyClient *AuthenticationReply::proxyClient() const
|
||||||
@ -37,6 +41,7 @@ void AuthenticationReply::setError(Authenticator::AuthenticationError error)
|
|||||||
|
|
||||||
void AuthenticationReply::setFinished()
|
void AuthenticationReply::setFinished()
|
||||||
{
|
{
|
||||||
|
m_timer->stop();
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,12 +49,16 @@ void AuthenticationReply::onTimeout()
|
|||||||
{
|
{
|
||||||
m_timedOut = true;
|
m_timedOut = true;
|
||||||
m_error = Authenticator::AuthenticationErrorTimeout;
|
m_error = Authenticator::AuthenticationErrorTimeout;
|
||||||
|
m_timer->stop();
|
||||||
|
m_process->kill();
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticationReply::abort()
|
void AuthenticationReply::abort()
|
||||||
{
|
{
|
||||||
m_error = Authenticator::AuthenticationErrorAborted;
|
m_error = Authenticator::AuthenticationErrorAborted;
|
||||||
|
m_timer->stop();
|
||||||
|
m_process->kill();
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QProcess>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
#include "authenticator.h"
|
#include "authenticator.h"
|
||||||
@ -25,9 +26,9 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
explicit AuthenticationReply(ProxyClient *proxyClient, QObject *parent = nullptr);
|
explicit AuthenticationReply(ProxyClient *proxyClient, QObject *parent = nullptr);
|
||||||
|
|
||||||
ProxyClient *m_proxyClient = nullptr;
|
ProxyClient *m_proxyClient = nullptr;
|
||||||
QTimer m_timer;
|
QTimer *m_timer = nullptr;
|
||||||
|
QProcess *m_process = nullptr;
|
||||||
|
|
||||||
bool m_timedOut = false;
|
bool m_timedOut = false;
|
||||||
bool m_finished = false;
|
bool m_finished = false;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public:
|
|||||||
AuthenticationErrorTimeout,
|
AuthenticationErrorTimeout,
|
||||||
AuthenticationErrorAborted,
|
AuthenticationErrorAborted,
|
||||||
AuthenticationErrorAuthenticationFailed,
|
AuthenticationErrorAuthenticationFailed,
|
||||||
AuthenticationErrorAuthenticationServerNotResponding
|
AuthenticationErrorProxyError
|
||||||
};
|
};
|
||||||
Q_ENUM(AuthenticationError)
|
Q_ENUM(AuthenticationError)
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include "engine.h"
|
||||||
#include "proxyclient.h"
|
#include "proxyclient.h"
|
||||||
#include "awsauthenticator.h"
|
#include "awsauthenticator.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
@ -5,7 +6,8 @@
|
|||||||
namespace remoteproxy {
|
namespace remoteproxy {
|
||||||
|
|
||||||
AwsAuthenticator::AwsAuthenticator(QObject *parent) :
|
AwsAuthenticator::AwsAuthenticator(QObject *parent) :
|
||||||
Authenticator(parent)
|
Authenticator(parent),
|
||||||
|
m_manager(new QNetworkAccessManager(this))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -20,13 +22,31 @@ QString AwsAuthenticator::name() const
|
|||||||
return "AWS authenticator";
|
return "AWS authenticator";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AwsAuthenticator::onAuthenticationProcessFinished(Authenticator::AuthenticationError error)
|
||||||
|
{
|
||||||
|
AuthenticationProcess *process = static_cast<AuthenticationProcess *>(sender());
|
||||||
|
AuthenticationReply *reply = m_runningProcesses.take(process);
|
||||||
|
|
||||||
|
setReplyError(reply, error);
|
||||||
|
setReplyFinished(reply);
|
||||||
|
|
||||||
|
qCDebug(dcAuthenticator()) << "" << error;
|
||||||
|
}
|
||||||
|
|
||||||
AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient)
|
AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient)
|
||||||
{
|
{
|
||||||
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
|
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
|
||||||
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
|
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
|
||||||
|
|
||||||
// TODO: start authentication request
|
AuthenticationProcess *process = new AuthenticationProcess(m_manager, this);
|
||||||
|
process->useDynamicCredentials(!Engine::instance()->developerMode());
|
||||||
|
connect(process, &AuthenticationProcess::authenticationFinished, this, &AwsAuthenticator::onAuthenticationProcessFinished);
|
||||||
|
|
||||||
|
// Configure process
|
||||||
|
m_runningProcesses.insert(process, reply);
|
||||||
|
|
||||||
|
// Start authentication process
|
||||||
|
process->authenticate(proxyClient->token());
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,11 @@
|
|||||||
#define AWSAUTHENTICATOR_H
|
#define AWSAUTHENTICATOR_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
#include "authenticator.h"
|
#include "authenticator.h"
|
||||||
#include "authenticationreply.h"
|
#include "authenticationreply.h"
|
||||||
|
#include "authenticationprocess.h"
|
||||||
|
|
||||||
namespace remoteproxy {
|
namespace remoteproxy {
|
||||||
|
|
||||||
@ -18,7 +20,11 @@ public:
|
|||||||
QString name() const override;
|
QString name() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QNetworkAccessManager *m_manager = nullptr;
|
||||||
|
QHash<AuthenticationProcess *, AuthenticationReply *> m_runningProcesses;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onAuthenticationProcessFinished(Authenticator::AuthenticationError error);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;
|
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;
|
||||||
|
|||||||
@ -1,123 +0,0 @@
|
|||||||
#include "sigv4utils.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QMessageAuthenticationCode>
|
|
||||||
#include <QtDebug>
|
|
||||||
#include <QUrlQuery>
|
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
SigV4Utils::SigV4Utils()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getCurrentDateTime()
|
|
||||||
{
|
|
||||||
return QDateTime::currentDateTime().toUTC().toString("yyyyMMddThhmmssZ").toUtf8();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getCanonicalQueryString(const QNetworkRequest &request, const QByteArray &accessKeyId, const QByteArray &secretAccessKey, const QByteArray &sessionToken, const QByteArray ®ion, const QByteArray &service, const QByteArray &payload)
|
|
||||||
{
|
|
||||||
QByteArray algorithm = "AWS4-HMAC-SHA256";
|
|
||||||
QByteArray dateTime = getCurrentDateTime();
|
|
||||||
QByteArray credentialScope = getCredentialScope(algorithm, dateTime, region, service);
|
|
||||||
|
|
||||||
QByteArray canonicalQueryString;
|
|
||||||
canonicalQueryString += "X-Amz-Algorithm=AWS4-HMAC-SHA256";
|
|
||||||
canonicalQueryString += "&X-Amz-Credential=" + QByteArray(accessKeyId + '/' + credentialScope).toPercentEncoding();
|
|
||||||
canonicalQueryString += "&X-Amz-Date=" + dateTime;
|
|
||||||
if (request.rawHeaderList().count() > 0){
|
|
||||||
canonicalQueryString += "&X-Amz-SignedHeaders=" + request.rawHeaderList().join(';').toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray canonicalRequest = getCanonicalRequest(QNetworkAccessManager::GetOperation, request, payload);
|
|
||||||
|
|
||||||
QByteArray stringToSign = getStringToSign(canonicalRequest, dateTime, region, service);
|
|
||||||
QByteArray signature = getSignature(stringToSign, secretAccessKey, dateTime, region, service);
|
|
||||||
|
|
||||||
canonicalQueryString += "&X-Amz-Signature=" + signature;
|
|
||||||
|
|
||||||
if (!sessionToken.isEmpty()) {
|
|
||||||
canonicalQueryString += "&X-Amz-Security-Token=" + sessionToken.toPercentEncoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
return canonicalQueryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getSignatureKey(const QByteArray &key, const QByteArray &date, const QByteArray ®ion, const QByteArray &service)
|
|
||||||
{
|
|
||||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
|
|
||||||
return QMessageAuthenticationCode::hash("aws4_request",
|
|
||||||
QMessageAuthenticationCode::hash(service,
|
|
||||||
QMessageAuthenticationCode::hash(region,
|
|
||||||
QMessageAuthenticationCode::hash(date, "AWS4"+key,
|
|
||||||
hashAlgorithm), hashAlgorithm), hashAlgorithm), hashAlgorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getCanonicalRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, const QByteArray &payload)
|
|
||||||
{
|
|
||||||
QByteArray canonicalRequest;
|
|
||||||
|
|
||||||
QByteArray method;
|
|
||||||
switch (operation) {
|
|
||||||
case QNetworkAccessManager::GetOperation:
|
|
||||||
method = "GET";
|
|
||||||
break;
|
|
||||||
case QNetworkAccessManager::PostOperation:
|
|
||||||
method = "POST";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Q_ASSERT_X(false, "Network operation not implemented", "SigV4Utils");
|
|
||||||
}
|
|
||||||
QByteArray uri = request.url().path().toUtf8();
|
|
||||||
QUrlQuery query(request.url());
|
|
||||||
QList<QPair<QString, QString> > queryItems = query.queryItems();
|
|
||||||
QStringList queryItemStrings;
|
|
||||||
for (int i = 0; i < queryItems.count(); i++) {
|
|
||||||
QPair<QString, QString> queryItem = queryItems.at(i);
|
|
||||||
queryItemStrings.append(queryItem.first + '=' + queryItem.second);
|
|
||||||
}
|
|
||||||
queryItemStrings.sort(Qt::CaseInsensitive);
|
|
||||||
|
|
||||||
QByteArray canonicalQueryString = queryItemStrings.join('&').toUtf8();
|
|
||||||
|
|
||||||
QByteArray canonicalHeaders;
|
|
||||||
foreach(const QByteArray &headerName, request.rawHeaderList()) {
|
|
||||||
canonicalHeaders += headerName.toLower() + ':' + request.rawHeader(headerName) + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray payloadHash = QCryptographicHash::hash(payload, QCryptographicHash::Sha256).toHex();
|
|
||||||
|
|
||||||
canonicalRequest = method + '\n' + uri + '\n' + canonicalQueryString + '\n' + canonicalHeaders + '\n' + request.rawHeaderList().join(';').toLower() + '\n' + payloadHash;
|
|
||||||
return canonicalRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getCredentialScope(const QByteArray &algorithm, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service)
|
|
||||||
{
|
|
||||||
Q_UNUSED(algorithm)
|
|
||||||
QByteArray credentialScope = dateTime.left(8) + '/' + region + '/' + service + "/aws4_request";
|
|
||||||
return credentialScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getStringToSign(const QByteArray &canonicalRequest, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service)
|
|
||||||
{
|
|
||||||
QByteArray algorithm = "AWS4-HMAC-SHA256";
|
|
||||||
QByteArray credentialScope = getCredentialScope(algorithm, dateTime, region, service);
|
|
||||||
|
|
||||||
QByteArray stringToSign = algorithm + '\n' + dateTime + '\n' + credentialScope + '\n' + QCryptographicHash::hash(canonicalRequest, QCryptographicHash::Sha256).toHex();
|
|
||||||
return stringToSign;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getSignature(const QByteArray &stringToSign, const QByteArray &secretAccessKey, const QByteArray &dateTime, const QString ®ion, const QString &service)
|
|
||||||
{
|
|
||||||
QByteArray signingKey = getSignatureKey(secretAccessKey, dateTime.left(8), region.toUtf8(), service.toUtf8());
|
|
||||||
QByteArray signature = QMessageAuthenticationCode::hash(stringToSign, signingKey, QCryptographicHash::Sha256).toHex();
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SigV4Utils::getAuthorizationHeader(const QByteArray &accessKeyId, const QByteArray &dateTime, const QString ®ion, const QString &service, const QNetworkRequest &request, const QByteArray &signature)
|
|
||||||
{
|
|
||||||
QByteArray authHeader = "AWS4-HMAC-SHA256 Credential=" + accessKeyId + '/' + dateTime.left(8) + '/' + region.toUtf8() + '/' + service.toUtf8() + '/' + "aws4_request, SignedHeaders=" + request.rawHeaderList().join(';').toLower() + ", Signature=" + signature;
|
|
||||||
return authHeader;
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
#ifndef SIGV4UTILS_H
|
|
||||||
#define SIGV4UTILS_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
|
|
||||||
class SigV4Utils
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SigV4Utils();
|
|
||||||
|
|
||||||
static QByteArray getCurrentDateTime();
|
|
||||||
|
|
||||||
|
|
||||||
static QByteArray getCanonicalQueryString(const QNetworkRequest &request, const QByteArray &accessKeyId, const QByteArray &secretAccessKey, const QByteArray &sessionToken, const QByteArray ®ion, const QByteArray &service, const QByteArray &payload);
|
|
||||||
static QByteArray getCanonicalRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, const QByteArray &payload);
|
|
||||||
static QByteArray getCanonicalHeaders(const QNetworkRequest &request);
|
|
||||||
static QByteArray getCredentialScope(const QByteArray &algorithm, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service);
|
|
||||||
static QByteArray getStringToSign(const QByteArray &canonicalRequest, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service);
|
|
||||||
static QByteArray getSignatureKey(const QByteArray &key, const QByteArray &date, const QByteArray ®ion, const QByteArray &service);
|
|
||||||
static QByteArray getSignature(const QByteArray &stringToSign, const QByteArray &secretAccessKey, const QByteArray &dateTime, const QString ®ion, const QString &service);
|
|
||||||
static QByteArray getAuthorizationHeader(const QByteArray &accessKeyId, const QByteArray &dateTime, const QString ®ion, const QString &service, const QNetworkRequest &request, const QByteArray &signature);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SIGV4UTILS_H
|
|
||||||
@ -32,6 +32,10 @@ bool Engine::exists()
|
|||||||
|
|
||||||
void Engine::start()
|
void Engine::start()
|
||||||
{
|
{
|
||||||
|
// Make sure an authenticator was registered
|
||||||
|
Q_ASSERT_X(m_authenticator != nullptr, "Engine", "There is no authenticator registerd.");
|
||||||
|
Q_ASSERT_X(m_configuration != nullptr, "Engine", "There is no configuration set.");
|
||||||
|
|
||||||
if (!m_running)
|
if (!m_running)
|
||||||
qCDebug(dcEngine()) << "Start server engine";
|
qCDebug(dcEngine()) << "Start server engine";
|
||||||
|
|
||||||
@ -44,15 +48,12 @@ void Engine::start()
|
|||||||
|
|
||||||
QUrl websocketServerUrl;
|
QUrl websocketServerUrl;
|
||||||
websocketServerUrl.setScheme("wss");
|
websocketServerUrl.setScheme("wss");
|
||||||
websocketServerUrl.setHost(m_webSocketServerHostAddress.toString());
|
websocketServerUrl.setHost(m_configuration->webSocketServerHost().toString());
|
||||||
websocketServerUrl.setPort(m_webSocketServerPort);
|
websocketServerUrl.setPort(m_configuration->webSocketServerPort());
|
||||||
m_webSocketServer->setServerUrl(websocketServerUrl);
|
m_webSocketServer->setServerUrl(websocketServerUrl);
|
||||||
|
|
||||||
m_proxyServer->registerTransportInterface(m_webSocketServer);
|
m_proxyServer->registerTransportInterface(m_webSocketServer);
|
||||||
|
|
||||||
// Make sure an authenticator was registered
|
|
||||||
Q_ASSERT_X(m_authenticator != nullptr, "Engine", "There is no authenticator registerd.");
|
|
||||||
|
|
||||||
qCDebug(dcEngine()) << "Starting proxy server";
|
qCDebug(dcEngine()) << "Starting proxy server";
|
||||||
m_proxyServer->startServer();
|
m_proxyServer->startServer();
|
||||||
|
|
||||||
@ -74,6 +75,11 @@ bool Engine::running() const
|
|||||||
return m_running;
|
return m_running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Engine::developerMode() const
|
||||||
|
{
|
||||||
|
return m_developerMode;
|
||||||
|
}
|
||||||
|
|
||||||
QString Engine::serverName() const
|
QString Engine::serverName() const
|
||||||
{
|
{
|
||||||
return m_serverName;
|
return m_serverName;
|
||||||
@ -84,36 +90,24 @@ void Engine::setServerName(const QString &serverName)
|
|||||||
m_serverName = serverName;
|
m_serverName = serverName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::setWebSocketServerHostAddress(const QHostAddress &hostAddress)
|
void Engine::setConfiguration(ProxyConfiguration *configuration)
|
||||||
{
|
{
|
||||||
qCDebug(dcEngine()) << "Websocket server host address:" << hostAddress;
|
m_configuration = configuration;
|
||||||
m_webSocketServerHostAddress = hostAddress;
|
qCDebug(dcApplication()) << "Set configuration" << m_configuration;
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::setWebSocketServerPort(const quint16 &port)
|
|
||||||
{
|
|
||||||
qCDebug(dcEngine()) << "Websocket server port:" << port;
|
|
||||||
m_webSocketServerPort = port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::setSslConfiguration(const QSslConfiguration &configuration)
|
void Engine::setSslConfiguration(const QSslConfiguration &configuration)
|
||||||
{
|
{
|
||||||
qCDebug(dcEngine()) << "SSL certificate information:";
|
|
||||||
qCDebug(dcEngine()) << " Common name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CommonName);
|
|
||||||
qCDebug(dcEngine()) << " Organisation:" << configuration.localCertificate().issuerInfo(QSslCertificate::Organization);
|
|
||||||
qCDebug(dcEngine()) << " Organisation unit name:" << configuration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
|
||||||
qCDebug(dcEngine()) << " Country name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CountryName);
|
|
||||||
qCDebug(dcEngine()) << " Locality name:" << configuration.localCertificate().issuerInfo(QSslCertificate::LocalityName);
|
|
||||||
qCDebug(dcEngine()) << " State/Province:" << configuration.localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName);
|
|
||||||
qCDebug(dcEngine()) << " Email address:" << configuration.localCertificate().issuerInfo(QSslCertificate::EmailAddress);
|
|
||||||
|
|
||||||
m_sslConfiguration = configuration;
|
m_sslConfiguration = configuration;
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::setAuthenticationServerUrl(const QUrl &url)
|
qCDebug(dcEngine()) << "SSL certificate information:";
|
||||||
{
|
qCDebug(dcEngine()) << " Common name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::CommonName);
|
||||||
qCDebug(dcEngine()) << "Authentication server URL" << url.toString();
|
qCDebug(dcEngine()) << " Organisation:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::Organization);
|
||||||
m_authenticationServerUrl = url;
|
qCDebug(dcEngine()) << " Organisation unit name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
||||||
|
qCDebug(dcEngine()) << " Country name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::CountryName);
|
||||||
|
qCDebug(dcEngine()) << " Locality name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::LocalityName);
|
||||||
|
qCDebug(dcEngine()) << " State/Province:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName);
|
||||||
|
qCDebug(dcEngine()) << " Email address:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::EmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::setAuthenticator(Authenticator *authenticator)
|
void Engine::setAuthenticator(Authenticator *authenticator)
|
||||||
@ -132,6 +126,11 @@ void Engine::setAuthenticator(Authenticator *authenticator)
|
|||||||
// FIXME: connect
|
// FIXME: connect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Engine::setDeveloperModeEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
m_developerMode = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
Authenticator *Engine::authenticator() const
|
Authenticator *Engine::authenticator() const
|
||||||
{
|
{
|
||||||
return m_authenticator;
|
return m_authenticator;
|
||||||
@ -172,7 +171,6 @@ void Engine::clean()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_configuration) {
|
if (m_configuration) {
|
||||||
delete m_configuration;
|
|
||||||
m_configuration = nullptr;
|
m_configuration = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,17 +26,17 @@ public:
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
bool running() const;
|
bool running() const;
|
||||||
|
bool developerMode() const;
|
||||||
|
|
||||||
QString serverName() const;
|
QString serverName() const;
|
||||||
void setServerName(const QString &serverName);
|
void setServerName(const QString &serverName);
|
||||||
|
|
||||||
void setWebSocketServerHostAddress(const QHostAddress &hostAddress);
|
void setConfiguration(ProxyConfiguration *configuration);
|
||||||
void setWebSocketServerPort(const quint16 &port);
|
|
||||||
void setSslConfiguration(const QSslConfiguration &configuration);
|
void setSslConfiguration(const QSslConfiguration &configuration);
|
||||||
void setAuthenticationServerUrl(const QUrl &url);
|
|
||||||
|
|
||||||
void setAuthenticator(Authenticator *authenticator);
|
void setAuthenticator(Authenticator *authenticator);
|
||||||
|
|
||||||
|
void setDeveloperModeEnabled(bool enabled);
|
||||||
|
|
||||||
Authenticator *authenticator() const;
|
Authenticator *authenticator() const;
|
||||||
ProxyServer *proxyServer() const;
|
ProxyServer *proxyServer() const;
|
||||||
WebSocketServer *webSocketServer() const;
|
WebSocketServer *webSocketServer() const;
|
||||||
@ -47,13 +47,10 @@ private:
|
|||||||
static Engine *s_instance;
|
static Engine *s_instance;
|
||||||
|
|
||||||
bool m_running = false;
|
bool m_running = false;
|
||||||
|
bool m_developerMode = false;
|
||||||
QString m_serverName;
|
QString m_serverName;
|
||||||
|
|
||||||
quint16 m_webSocketServerPort = 1212;
|
|
||||||
QHostAddress m_webSocketServerHostAddress = QHostAddress::LocalHost;
|
|
||||||
QSslConfiguration m_sslConfiguration;
|
QSslConfiguration m_sslConfiguration;
|
||||||
QUrl m_authenticationServerUrl;
|
|
||||||
|
|
||||||
ProxyConfiguration *m_configuration = nullptr;
|
ProxyConfiguration *m_configuration = nullptr;
|
||||||
Authenticator *m_authenticator = nullptr;
|
Authenticator *m_authenticator = nullptr;
|
||||||
ProxyServer *m_proxyServer = nullptr;
|
ProxyServer *m_proxyServer = nullptr;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ HEADERS += \
|
|||||||
authentication/authenticationreply.h \
|
authentication/authenticationreply.h \
|
||||||
proxyconfiguration.h \
|
proxyconfiguration.h \
|
||||||
tunnelconnection.h \
|
tunnelconnection.h \
|
||||||
authentication/sigv4utils.h
|
authentication/authenticationprocess.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
engine.cpp \
|
engine.cpp \
|
||||||
@ -45,7 +45,7 @@ SOURCES += \
|
|||||||
authentication/authenticationreply.cpp \
|
authentication/authenticationreply.cpp \
|
||||||
proxyconfiguration.cpp \
|
proxyconfiguration.cpp \
|
||||||
tunnelconnection.cpp \
|
tunnelconnection.cpp \
|
||||||
authentication/sigv4utils.cpp
|
authentication/authenticationprocess.cpp
|
||||||
|
|
||||||
|
|
||||||
# install header file with relative subdirectory
|
# install header file with relative subdirectory
|
||||||
|
|||||||
@ -14,18 +14,18 @@ ProxyConfiguration::ProxyConfiguration(QObject *parent) :
|
|||||||
bool ProxyConfiguration::loadConfiguration(const QString &fileName)
|
bool ProxyConfiguration::loadConfiguration(const QString &fileName)
|
||||||
{
|
{
|
||||||
QFileInfo fileInfo(fileName);
|
QFileInfo fileInfo(fileName);
|
||||||
|
|
||||||
if (!fileInfo.exists()) {
|
if (!fileInfo.exists()) {
|
||||||
qCWarning(dcApplication()) << "Could not find configuration file" << fileName;
|
qCWarning(dcApplication()) << "Configuration: Could not find configuration file" << fileName;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileInfo.isReadable()) {
|
if (!fileInfo.isReadable()) {
|
||||||
qCWarning(dcApplication()) << "Cannot read configuration file" << fileName;
|
qCWarning(dcApplication()) << "Configuration: Cannot read configuration file" << fileName;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSettings settings(fileName, QSettings::IniFormat);
|
QSettings settings(fileName, QSettings::IniFormat);
|
||||||
|
qCDebug(dcApplication()) << settings.childGroups();
|
||||||
|
|
||||||
settings.beginGroup("General");
|
settings.beginGroup("General");
|
||||||
setWriteLogFile(settings.value("writeLogs", false).toBool());
|
setWriteLogFile(settings.value("writeLogs", false).toBool());
|
||||||
@ -127,6 +127,25 @@ void ProxyConfiguration::setTcpServerPort(quint16 port)
|
|||||||
m_tcpServerPort = port;
|
m_tcpServerPort = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, ProxyConfiguration *configuration)
|
||||||
|
{
|
||||||
|
debug.nospace() << endl << "========== ProxyConfiguration ==========" << endl;
|
||||||
|
debug.nospace() << "General" << endl;
|
||||||
|
debug.nospace() << " - write logfile:" << configuration->writeLogFile() << endl;
|
||||||
|
debug.nospace() << " - logfile:" << configuration->logFileName() << endl;
|
||||||
|
debug.nospace() << " - certificate:" << configuration->sslCertificateFileName() << endl;
|
||||||
|
debug.nospace() << " - certificate key:" << configuration->sslCertificateKeyFileName() << endl;
|
||||||
|
debug.nospace() << "WebSocketServer" << endl;
|
||||||
|
debug.nospace() << " - host:" << configuration->webSocketServerHost().toString() << endl;
|
||||||
|
debug.nospace() << " - port:" << configuration->webSocketServerPort() << endl;
|
||||||
|
debug.nospace() << "TcpServer" << endl;
|
||||||
|
debug.nospace() << " - host:" << configuration->tcpServerHost().toString() << endl;
|
||||||
|
debug.nospace() << " - port:" << configuration->tcpServerPort() << endl;
|
||||||
|
debug.nospace() << "========== ProxyConfiguration ==========" << endl;
|
||||||
|
|
||||||
|
return debug; return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,8 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QDebug operator<< (QDebug debug, ProxyConfiguration *configuration);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PROXYCONFIGURATION_H
|
#endif // PROXYCONFIGURATION_H
|
||||||
|
|||||||
13
nymea-remoteproxy.conf
Normal file
13
nymea-remoteproxy.conf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[General]
|
||||||
|
writeLogs=false
|
||||||
|
logFile=/var/log/nymea-remoteproxy.log
|
||||||
|
certificate=/etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||||
|
certificateKey=/etc/ssl/private/ssl-cert-snakeoil.key
|
||||||
|
|
||||||
|
[WebSocketServer]
|
||||||
|
host=0.0.0.0
|
||||||
|
port=443
|
||||||
|
|
||||||
|
[TcpServer]
|
||||||
|
host=0.0.0.0
|
||||||
|
port=80
|
||||||
@ -1,11 +1,11 @@
|
|||||||
include(nymea-remoteproxy.pri)
|
include(nymea-remoteproxy.pri)
|
||||||
|
|
||||||
TEMPLATE=subdirs
|
TEMPLATE=subdirs
|
||||||
SUBDIRS += server libnymea-remoteproxy libnymea-remoteproxyclient tests
|
SUBDIRS += server client libnymea-remoteproxy libnymea-remoteproxyclient tests
|
||||||
|
|
||||||
server.depends = libnymea-remoteproxy
|
server.depends = libnymea-remoteproxy
|
||||||
tests.depends = libnymea-remoteproxy libnymea-remoteproxyclient
|
tests.depends = libnymea-remoteproxy libnymea-remoteproxyclient
|
||||||
|
client.depends = libnymea-remoteproxyclient
|
||||||
|
|
||||||
message("----------------------------------------------------------")
|
message("----------------------------------------------------------")
|
||||||
message("Building nymea-remoteproxy $${SERVER_VERSION}")
|
message("Building nymea-remoteproxy $${SERVER_VERSION}")
|
||||||
|
|||||||
136
server/main.cpp
136
server/main.cpp
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
#include "proxyconfiguration.h"
|
||||||
#include "authentication/awsauthenticator.h"
|
#include "authentication/awsauthenticator.h"
|
||||||
|
|
||||||
using namespace remoteproxy;
|
using namespace remoteproxy;
|
||||||
@ -115,28 +116,30 @@ int main(int argc, char *argv[])
|
|||||||
"logfile", "/var/log/nymea-remoteproxy.log");
|
"logfile", "/var/log/nymea-remoteproxy.log");
|
||||||
parser.addOption(logfileOption);
|
parser.addOption(logfileOption);
|
||||||
|
|
||||||
QCommandLineOption serverOption(QStringList() << "s" << "server", "The server address this proxy will listen on. "
|
QCommandLineOption developmentOption(QStringList() << "d" << "development", "Enable the development mode. This enabled the server "
|
||||||
"Default is 127.0.0.1", "hostaddress", "127.0.0.1");
|
"assumes there are static AWS credentials provided to aws-cli.");
|
||||||
parser.addOption(serverOption);
|
parser.addOption(developmentOption);
|
||||||
|
|
||||||
QCommandLineOption portOption(QStringList() << "p" << "port", "The proxy server port. Default is 1212", "port", "1212");
|
QCommandLineOption configOption(QStringList() << "c" <<"config", "The path to the proxy server configuration file. The default is /etc/nymea-remoteproxy/nymea-remoteproxy.conf", "configuration");
|
||||||
parser.addOption(portOption);
|
configOption.setDefaultValue("/etc/nymea-remoteproxy/nymea-remoteproxy.conf");
|
||||||
|
parser.addOption(configOption);
|
||||||
QCommandLineOption certOption(QStringList() << "c" <<"certificate", "The path to the SSL certificate used for "
|
|
||||||
"this proxy server.", "certificate");
|
|
||||||
certOption.setDefaultValue("/etc/ssl/certs/ssl-cert-snakeoil.pem");
|
|
||||||
parser.addOption(certOption);
|
|
||||||
|
|
||||||
QCommandLineOption certKeyOption(QStringList() << "k" << "certificate-key", "The path to the SSL certificate key "
|
|
||||||
"used for this proxy server.", "certificate-key");
|
|
||||||
certKeyOption.setDefaultValue("/etc/ssl/private/ssl-cert-snakeoil.key");
|
|
||||||
parser.addOption(certKeyOption);
|
|
||||||
|
|
||||||
QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print more verbose.");
|
QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print more verbose.");
|
||||||
parser.addOption(verboseOption);
|
parser.addOption(verboseOption);
|
||||||
|
|
||||||
parser.process(application);
|
parser.process(application);
|
||||||
|
|
||||||
|
// Create a default configuration
|
||||||
|
ProxyConfiguration *configuration = new ProxyConfiguration(nullptr);
|
||||||
|
if (parser.isSet(configOption)) {
|
||||||
|
qCDebug(dcApplication()) << "Loading configuration file from" << parser.value(configOption);
|
||||||
|
if (!configuration->loadConfiguration(parser.value(configOption))) {
|
||||||
|
qCCritical(dcApplication()) << "Invalid configuration file passed" << parser.value(configOption);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (parser.isSet(verboseOption)) {
|
if (parser.isSet(verboseOption)) {
|
||||||
s_loggingFilters["Debug"] = true;
|
s_loggingFilters["Debug"] = true;
|
||||||
s_loggingFilters["WebSocketServerTraffic"] = true;
|
s_loggingFilters["WebSocketServerTraffic"] = true;
|
||||||
@ -144,84 +147,62 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QLoggingCategory::installFilter(loggingCategoryFilter);
|
QLoggingCategory::installFilter(loggingCategoryFilter);
|
||||||
|
|
||||||
// Open the logfile, if any specified
|
// Verify webserver configuration
|
||||||
if (parser.isSet(logfileOption)) {
|
if (configuration->webSocketServerHost().isNull()) {
|
||||||
QFileInfo fi(parser.value(logfileOption));
|
qCCritical(dcApplication()) << "Invalid web socket host address passed.";
|
||||||
QDir logDir(fi.absolutePath());
|
|
||||||
if (!logDir.exists() && !logDir.mkpath(logDir.absolutePath())) {
|
|
||||||
qCCritical(dcApplication()) << "Error opening log file" << parser.value(logfileOption);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
s_logFile.setFileName(parser.value(logfileOption));
|
|
||||||
if (!s_logFile.open(QFile::WriteOnly | QFile::Append)) {
|
|
||||||
qCCritical(dcApplication()) << "Error opening log file" << parser.value(logfileOption);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
s_loggingEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy server host address
|
|
||||||
QHostAddress serverHostAddress = QHostAddress(parser.value(serverOption));
|
|
||||||
if (serverHostAddress.isNull()) {
|
|
||||||
qCCritical(dcApplication()) << "Invalid hostaddress for the proxy server:" << parser.value(serverOption);
|
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Port
|
// Verify tcp server configuration
|
||||||
bool ok = false;
|
if (configuration->tcpServerHost().isNull()) {
|
||||||
uint port = parser.value(portOption).toUInt(&ok);
|
qCCritical(dcApplication()) << "Invalid TCP server host address passed.";
|
||||||
if (!ok) {
|
|
||||||
qCCritical(dcApplication()) << "Invalid port value:" << parser.value(portOption);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port > 65535) {
|
|
||||||
qCCritical(dcApplication()) << "Port value is out of range:" << parser.value(portOption);
|
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSL certificate
|
// SSL certificate
|
||||||
QSslConfiguration sslConfiguration;
|
QSslConfiguration sslConfiguration;
|
||||||
if (parser.isSet(certOption)) {
|
// Load certificate
|
||||||
// Load certificate
|
QFile certFile(configuration->sslCertificateFileName());
|
||||||
QFile certFile(parser.value(certOption));
|
if (!certFile.open(QIODevice::ReadOnly)) {
|
||||||
if (!certFile.open(QIODevice::ReadOnly)) {
|
qCCritical(dcApplication()) << "Could not open certificate file" << configuration->sslCertificateFileName() << certFile.errorString();
|
||||||
qCCritical(dcApplication()) << "Could not open certificate file:" << parser.value(certOption) << certFile.errorString();
|
exit(-1);
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSslCertificate certificate(&certFile, QSsl::Pem);
|
|
||||||
qCDebug(dcApplication()) << "Loaded successfully certificate" << parser.value(certOption);
|
|
||||||
certFile.close();
|
|
||||||
|
|
||||||
// Create SSL configuration
|
|
||||||
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
|
||||||
sslConfiguration.setLocalCertificate(certificate);
|
|
||||||
sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSslCertificate certificate(&certFile, QSsl::Pem);
|
||||||
|
qCDebug(dcApplication()) << "Loaded successfully certificate" << configuration->sslCertificateFileName();
|
||||||
|
certFile.close();
|
||||||
|
|
||||||
|
// Create SSL configuration
|
||||||
|
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
sslConfiguration.setLocalCertificate(certificate);
|
||||||
|
sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater);
|
||||||
|
|
||||||
// SSL key
|
// SSL key
|
||||||
if (parser.isSet(certKeyOption)) {
|
QFile certKeyFile(configuration->sslCertificateKeyFileName());
|
||||||
QFile certKeyFile(parser.value(certKeyOption));
|
if (!certKeyFile.open(QIODevice::ReadOnly)) {
|
||||||
if (!certKeyFile.open(QIODevice::ReadOnly)) {
|
qCCritical(dcApplication()) << "Could not open certificate key file:" << configuration->sslCertificateKeyFileName() << certKeyFile.errorString();
|
||||||
qCCritical(dcApplication()) << "Could not open certificate key file:" << parser.value(certKeyOption) << certKeyFile.errorString();
|
exit(-1);
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSslKey sslKey(&certKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
|
||||||
qCDebug(dcApplication()) << "Loaded successfully certificate key" << parser.value(certKeyOption);
|
|
||||||
certKeyFile.close();
|
|
||||||
sslConfiguration.setPrivateKey(sslKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSslKey sslKey(&certKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||||
|
qCDebug(dcApplication()) << "Loaded successfully certificate key" << configuration->sslCertificateKeyFileName();
|
||||||
|
certKeyFile.close();
|
||||||
|
sslConfiguration.setPrivateKey(sslKey);
|
||||||
|
|
||||||
if (sslConfiguration.isNull()) {
|
if (sslConfiguration.isNull()) {
|
||||||
qCCritical(dcApplication()) << "No SSL configuration specified. The server does not suppoert insecure connections.";
|
qCCritical(dcApplication()) << "No SSL configuration specified. The server does not suppoert insecure connections.";
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(dcApplication()) << "==============================================";
|
if (parser.isSet(developmentOption)) {
|
||||||
|
qCWarning(dcApplication()) << "##########################################################";
|
||||||
|
qCWarning(dcApplication()) << "# DEVELOPMENT MODE #";
|
||||||
|
qCWarning(dcApplication()) << "##########################################################";
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcApplication()) << "==========================================================";
|
||||||
qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion();
|
qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion();
|
||||||
qCDebug(dcApplication()) << "==============================================";
|
qCDebug(dcApplication()) << "==========================================================";
|
||||||
|
|
||||||
if (s_loggingEnabled)
|
if (s_loggingEnabled)
|
||||||
qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName();
|
qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName();
|
||||||
@ -230,10 +211,11 @@ int main(int argc, char *argv[])
|
|||||||
AwsAuthenticator *authenticator = new AwsAuthenticator(nullptr);
|
AwsAuthenticator *authenticator = new AwsAuthenticator(nullptr);
|
||||||
|
|
||||||
// Configure and start the engines
|
// Configure and start the engines
|
||||||
Engine::instance()->setAuthenticator(authenticator);
|
Engine::instance()->setConfiguration(configuration);
|
||||||
Engine::instance()->setWebSocketServerHostAddress(serverHostAddress);
|
Engine::instance()->setDeveloperModeEnabled(parser.isSet(developmentOption));
|
||||||
Engine::instance()->setWebSocketServerPort(static_cast<quint16>(port));
|
|
||||||
Engine::instance()->setSslConfiguration(sslConfiguration);
|
Engine::instance()->setSslConfiguration(sslConfiguration);
|
||||||
|
Engine::instance()->setAuthenticator(authenticator);
|
||||||
|
|
||||||
Engine::instance()->start();
|
Engine::instance()->start();
|
||||||
|
|
||||||
return application.exec();
|
return application.exec();
|
||||||
|
|||||||
@ -13,6 +13,8 @@
|
|||||||
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
|
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
|
m_configuration = new ProxyConfiguration(this);
|
||||||
|
|
||||||
QFile certificateFile(":/test-certificate.crt");
|
QFile certificateFile(":/test-certificate.crt");
|
||||||
if (!certificateFile.open(QIODevice::ReadOnly)) {
|
if (!certificateFile.open(QIODevice::ReadOnly)) {
|
||||||
qWarning() << "Could not open resource file" << certificateFile.fileName();
|
qWarning() << "Could not open resource file" << certificateFile.fileName();
|
||||||
@ -72,11 +74,10 @@ void RemoteProxyTests::startEngine()
|
|||||||
if (!Engine::exists()) {
|
if (!Engine::exists()) {
|
||||||
QString serverName = "nymea-remoteproxy-testserver";
|
QString serverName = "nymea-remoteproxy-testserver";
|
||||||
Engine::instance()->setAuthenticator(m_authenticator);
|
Engine::instance()->setAuthenticator(m_authenticator);
|
||||||
Engine::instance()->setAuthenticationServerUrl(QUrl("https://localhost"));
|
|
||||||
Engine::instance()->setServerName(serverName);
|
Engine::instance()->setServerName(serverName);
|
||||||
Engine::instance()->setWebSocketServerPort(m_port);
|
Engine::instance()->setConfiguration(m_configuration);
|
||||||
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
|
|
||||||
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
||||||
|
Engine::instance()->setDeveloperModeEnabled(true);
|
||||||
|
|
||||||
QVERIFY(Engine::exists());
|
QVERIFY(Engine::exists());
|
||||||
QVERIFY(Engine::instance()->serverName() == serverName);
|
QVERIFY(Engine::instance()->serverName() == serverName);
|
||||||
@ -89,6 +90,8 @@ void RemoteProxyTests::startServer()
|
|||||||
|
|
||||||
if (!Engine::instance()->running()) {
|
if (!Engine::instance()->running()) {
|
||||||
QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged);
|
QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged);
|
||||||
|
Engine::instance()->setConfiguration(m_configuration);
|
||||||
|
Engine::instance()->setDeveloperModeEnabled(true);
|
||||||
Engine::instance()->start();
|
Engine::instance()->start();
|
||||||
runningSpy.wait();
|
runningSpy.wait();
|
||||||
QVERIFY(runningSpy.count() == 1);
|
QVERIFY(runningSpy.count() == 1);
|
||||||
@ -217,9 +220,8 @@ void RemoteProxyTests::webserverConnectionBlocked()
|
|||||||
dummyServer.listen(QHostAddress::LocalHost, m_port);
|
dummyServer.listen(QHostAddress::LocalHost, m_port);
|
||||||
|
|
||||||
// Start proxy webserver
|
// Start proxy webserver
|
||||||
Engine::instance()->setWebSocketServerPort(m_port);
|
Engine::instance()->setConfiguration(m_configuration);
|
||||||
Engine::instance()->setAuthenticator(m_authenticator);
|
Engine::instance()->setAuthenticator(m_authenticator);
|
||||||
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
|
|
||||||
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
||||||
|
|
||||||
QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged);
|
QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged);
|
||||||
@ -324,7 +326,7 @@ void RemoteProxyTests::authenticate_data()
|
|||||||
<< 100 << Authenticator::AuthenticationErrorAuthenticationFailed;
|
<< 100 << Authenticator::AuthenticationErrorAuthenticationFailed;
|
||||||
|
|
||||||
QTest::newRow("not responding") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
|
QTest::newRow("not responding") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
|
||||||
<< 200 << Authenticator::AuthenticationErrorAuthenticationServerNotResponding;
|
<< 200 << Authenticator::AuthenticationErrorProxyError;
|
||||||
|
|
||||||
QTest::newRow("aborted") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
|
QTest::newRow("aborted") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << m_testToken
|
||||||
<< 100 << Authenticator::AuthenticationErrorAborted;
|
<< 100 << Authenticator::AuthenticationErrorAborted;
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "jsonrpc/jsontypes.h"
|
#include "jsonrpc/jsontypes.h"
|
||||||
#include "mockauthenticator.h"
|
#include "mockauthenticator.h"
|
||||||
|
#include "proxyconfiguration.h"
|
||||||
#include "remoteproxyconnection.h"
|
#include "remoteproxyconnection.h"
|
||||||
|
|
||||||
using namespace remoteproxy;
|
using namespace remoteproxy;
|
||||||
@ -24,8 +25,11 @@ public:
|
|||||||
explicit RemoteProxyTests(QObject *parent = nullptr);
|
explicit RemoteProxyTests(QObject *parent = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ProxyConfiguration *m_configuration = nullptr;
|
||||||
|
|
||||||
quint16 m_port = 1212;
|
quint16 m_port = 1212;
|
||||||
QHostAddress m_serverAddress = QHostAddress::LocalHost;
|
QHostAddress m_serverAddress = QHostAddress::LocalHost;
|
||||||
|
|
||||||
QSslConfiguration m_sslConfiguration;
|
QSslConfiguration m_sslConfiguration;
|
||||||
MockAuthenticator *m_authenticator = nullptr;
|
MockAuthenticator *m_authenticator = nullptr;
|
||||||
QString m_testToken;
|
QString m_testToken;
|
||||||
|
|||||||
Reference in New Issue
Block a user