Merge branch 'debian-packaging' into 'master'

add debian packaging

See merge request !2
This commit is contained in:
Simon Stürz 2018-08-14 19:07:43 +02:00
commit a9e79bde97
45 changed files with 916 additions and 438 deletions

1
.gitignore vendored
View File

@ -72,3 +72,4 @@ Thumbs.db
*.exe
coverage-html
client/nymea-remoteproxy-client

275
README.md
View File

@ -1,275 +0,0 @@
# nymea remote proxy server
----------------------------------------------
The nymea remote proxy server is the meeting point of nymea servers and nymea clients in order to establishing a secure remote connection.
# Build
In order to build the proxy server you need to install the qt default package.
apt install qt5-default
Change into the source directory and run following commands
cd nymea-remoteproxy
mkdir build
cd build
qmake ../
make -j$(nproc)
In the build directory you can find the resulting library and binary files.
If you want to start the proxy server from the build directory, you need to export the library path before starting the application:
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libnymea-remoteproxy:$(pwd)/libnymea-remoteproxyclient
$ ./server/nymea-remoteproxy -c ../nymea-remoteproxy/tests/test-certificate.crt -k ../nymea-remoteproxy/tests/test-certificate.key
# Install
# Configure
# Test
In order to run the test, you can call `make check` in the build directory or run the resulting executable:
$ nymea-remoteproxy-tests
## Test coverage report
If you want to create a line coverage report from the tests simply run following command in the source directory:
$ apt install lcov gcovr
$ ./create-coverage-html.sh
The resulting coverage report will be place in the `coverage-html` directory.
# Usage
In order to get information about the server you can start the command with the `--help` parameter.
$ nymea-remoteproxy --help
Usage: nymea-remoteproxy [options]
The nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection.
Server version: 0.0.1
API version: 0.1
Copyright © 2018 Simon Stürz <simon.stuerz@guh.io>
Options:
-h, --help Displays this help.
-v, --version Displays version information.
-l, --logging <logfile> Write log file to the given logfile.
-s, --server <hostaddress> The server address this proxy will
listen on. Default is 127.0.0.1
-p, --port <port> The proxy server port. Default is
1212
-c, --certificate <certificate> The path to the SSL certificate used
for this proxy server.
-k, --certificate-key <certificate-key> The path to the SSL certificate key
used for this proxy server.
-a, --authentication-server <url> The server url of the AWS
authentication server.
# Server API
Once a client connects to the proxy server, he must authenticate him self by passing the token received from the nymea-cloud mqtt connection request.
## Message format
#### Request
{
"id": integer,
"method": "Namespace.Method",
"o:params" { }
}
#### Response
{
"id": integer,
"status": "string",
"o:params" { },
"o:error": "string"
}
#### Notification
{
"id": integer,
"notification": "Namespace.Notification",
"o:params" { }
}
## Say Hello
#### Request
{
"id": 0,
"method": "RemoteProxy.Hello"
}
#### Response
{
"id": 0,
"params": {
"apiVersion": "0.1",
"name": "nymea-remoteproxy-testserver",
"server": "nymea-remoteproxy",
"version": "0.0.1"
},
"status": "success"
}
## Authenticate the connection
The first data a client **must** send to the proxy server is the authentication request. This request contains the token which will be verified agains the nymea-cloud infrastructure.
#### Request
{
"id": 1,
"method": "Authentication.Authenticate",
"params": {
"id": "string",
"name": "string",
"token": "tokenstring"
}
}
#### Response
* **On Success**: If the token was authenticated successfully, the response will look like this:
{
"id": 1,
"status": "success"
}
* **On Failure** If the token was invalid, the response will look like this and the server will close the connection immediatly:
{
"id": 1,
"status": "error",
"error": "Invalid token. You are not allowed to use this server."
}
#### Tunnel established
Once the other client is here and ready, the server will send a notification to the clients indicating that the tunnel has been established successfully. This message is the last data comming from the proxy server.
> **Important:** Any data traffic following after this notification comes from the tunnel endpoint, __not__ from the __proxy server__ any more.
{
"id": "0",
"notification": "RemoteProxy.TunnelEstablished",
"params": {
"name": "String",
"uuid": "String"
}
}
## Introspect the API
#### Request
{
"id": 0,
"method": "RemoteProxy.Introspect"
}
#### Response
{
"id": 0,
"params": {
"methods": {
"Authentication.Authenticate": {
"description": "Authenticate this connection. The returned AuthenticationError informs about the result. If the authentication was not successfull, the server will close the connection immediatly after sending the error response. The given id should be a unique id the other tunnel client can understand. Once the authentication was successfull, you can wait for the RemoteProxy.TunnelEstablished notification. If you send any data before getting this notification, the server will close the connection. If the tunnel client does not show up within 10 seconds, the server will close the connection.",
"params": {
"name": "String",
"token": "String",
"uuid": "String"
},
"returns": {
"authenticationError": "$ref:AuthenticationError"
}
},
"RemoteProxy.Hello": {
"description": "Once connected to this server, a client can get information about the server by saying Hello. The response informs the client about this proxy server.",
"params": {
},
"returns": {
"apiVersion": "String",
"name": "String",
"server": "String",
"version": "String"
}
},
"RemoteProxy.Introspect": {
"description": "Introspect this API.",
"params": {
},
"returns": {
"methods": "Object",
"notifications": "Object",
"types": "Object"
}
}
},
"notifications": {
"RemoteProxy.TunnelEstablished": {
"description": "Emitted whenever the tunnel has been established successfully. This is the last message from the remote proxy server! Any following data will be from the other tunnel client until the connection will be closed. The parameter contain some information about the other tunnel client.",
"params": {
"name": "String",
"uuid": "String"
}
}
},
"types": {
"AuthenticationError": [
"AuthenticationErrorNoError",
"AuthenticationErrorUnknown",
"AuthenticationErrorTimeout",
"AuthenticationErrorAborted",
"AuthenticationErrorAuthenticationFailed",
"AuthenticationErrorAuthenticationServerNotResponding"
],
"BasicType": [
"Uuid",
"String",
"Int",
"UInt",
"Double",
"Bool",
"Variant",
"Object"
]
}
},
"status": "success"
}
# License
This is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.

18
client/client.pro Normal file
View File

@ -0,0 +1,18 @@
include(../nymea-remoteproxy.pri)
include(../libnymea-remoteproxyclient/libnymea-remoteproxyclient.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

44
client/main.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <QUrl>
#include <QCoreApplication>
#include <QLoggingCategory>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include "proxyclient.h"
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);
if (!parser.isSet(tokenOption)) {
qWarning() << "Please specify the token for authentication." << endl;
exit(-1);
}
ProxyClient client;
client.start(parser.value(tokenOption));
return application.exec();
}

34
client/proxyclient.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "proxyclient.h"
ProxyClient::ProxyClient(QObject *parent) :
QObject(parent)
{
m_connection = new RemoteProxyConnection(QUuid::createUuid(), "nymea-remoteproxy-client", RemoteProxyConnection::ConnectionTypeWebSocket, this);
m_connection->setInsecureConnection(true);
connect(m_connection, &RemoteProxyConnection::ready, this, &ProxyClient::onClientReady);
connect(m_connection, &RemoteProxyConnection::authenticated, this, &ProxyClient::onAuthenticationFinished);
connect(m_connection, &RemoteProxyConnection::errorOccured, this, &ProxyClient::onErrorOccured);
}
void ProxyClient::onErrorOccured(RemoteProxyConnection::Error error)
{
qDebug() << "Error occured" << error << m_connection->errorString();
}
void ProxyClient::onClientReady()
{
m_connection->authenticate(m_token);
}
void ProxyClient::onAuthenticationFinished()
{
qDebug() << "Authentication finished.";
}
void ProxyClient::start(const QString &token)
{
m_token = token;
m_connection->connectServer(m_hostAddress, static_cast<quint16>(m_port));
}

39
client/proxyclient.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef PROXYCLIENT_H
#define PROXYCLIENT_H
#include <QObject>
#include "remoteproxyconnection.h"
using namespace remoteproxyclient;
class ProxyClient : public QObject
{
Q_OBJECT
public:
explicit ProxyClient(QObject *parent = nullptr);
void setHostAddress(const QHostAddress &hostAddress);
void setPort(const int &port);
private:
QString m_token;
QHostAddress m_hostAddress = QHostAddress::LocalHost;
int m_port = 1212;
RemoteProxyConnection *m_connection = nullptr;
signals:
private slots:
void onErrorOccured(RemoteProxyConnection::Error error);
void onClientReady();
void onAuthenticationFinished();
public slots:
void start(const QString &token);
};
#endif // PROXYCLIENT_H

24
copyright Normal file
View File

@ -0,0 +1,24 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: nymea-remoteproxy
Upstream-Contact: Simon Stürz <simon.stuerz@guh.io>
Copyright: 2018, guh GmbH
Download: http://www.github.com/guh/nymea-remoteproxy
Source: https://github.com/guh/nymea-remoteproxy.git
License: GPL-2+
On Debian systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL-2'.
License: LGPL-2.1
On Debian systems, the complete text of the GNU Lesser General
Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
License: LGPL-3
On Debian systems, the complete text of the GNU Lesser General
Public License can be found in `/usr/share/common-licenses/LGPL-3'.
Files: *
License: GPL-2+
Copyright: 2018, Simon Stürz <simon.stuerz@guh.io>

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
nymea-remoteproxy (0.0.1) xenial; urgency=medium
* Initial release.
-- Michael Zanetti <michael.zanetti@guh.io> Thu, 13 Aug 2018 21:33:51 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

42
debian/control vendored Normal file
View File

@ -0,0 +1,42 @@
Source: nymea-remoteproxy
Section: utils
Priority: options
Maintainer: Simon Stürz <simon.stürz@guh.io>
Build-depends: debhelper (>= 0.0.0),
libqt5websockets5-dev,
Standards-Version: 3.9.3
Package: nymea-remoteproxy
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: The nymea remote proxy server
The nymea remote proxy server
Package: nymea-remoteproxy-client
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: The nymea remote proxy client for testing
The nymea remote proxy client for testing
Package: libnymea-remoteproxyclient
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: The nymea remote proxy server client lib
The nymea remote proxy server client lib
Package: libnymea-remoteproxyclient-dev
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: The nymea remote proxy server client lib, development files
The nymea remote proxy server client lib, development files
Package: nymea-remoteproxy-tests
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: The nymea remote proxy server tests
The nymea remote proxy server tests

View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-remoteproxyclient.so.*

View File

@ -0,0 +1 @@
usr/bin/nymea-remoteproxy-client

View File

@ -0,0 +1 @@
usr/bin/nymea-remoteproxy-tests

2
debian/nymea-remoteproxy.install.in vendored Normal file
View File

@ -0,0 +1,2 @@
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-remoteproxy.so*
usr/bin/nymea-remoteproxy

15
debian/rules vendored Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/make -f
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
PREPROCESS_FILES := $(wildcard debian/*.in)
$(PREPROCESS_FILES:.in=): %: %.in
sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@
override_dh_install: $(PREPROCESS_FILES:.in=)
dh_install
%:
dh $@

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -0,0 +1,171 @@
#include "authenticationprocess.h"
#include "loggingcategories.h"
#include <QUrl>
#include <QFile>
#include <QNetworkReply>
#include <QJsonDocument>
namespace remoteproxy {
AuthenticationProcess::AuthenticationProcess(QNetworkAccessManager *manager, QObject *parent) :
QObject(parent),
m_manager(manager)
{
m_process = new QProcess(this);
m_process->setProcessChannelMode(QProcess::MergedChannels);
connect(m_process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &AuthenticationProcess::onProcessFinished);
}
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(dcAuthenticationProcess()) << "Authentication process already running. Killing the running process and restart.";
m_process->kill();
}
// 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);
}
// FIXME: check how to clean this up properly
m_resultFileName = "/tmp/" + QUuid::createUuid().toString().remove("{").remove("}").remove("-") + ".json";
qCDebug(dcAuthentication()) << "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::fromUtf8(QJsonDocument::fromVariant(request).toJson()),
m_resultFileName });
}
void AuthenticationProcess::onDynamicCredentialsReady()
{
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
reply->deleteLater();
if (reply->error()) {
qCWarning(dcAuthenticationProcess()) << "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(dcAuthenticationProcess()) << "Failed to parse dynamic credentials reply data" << data << ":" << error.errorString();
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
return;
}
QVariantMap response = jsonDoc.toVariant().toMap();
qCDebug(dcAuthentication()) << "-->" << 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)
{
if (exitStatus == QProcess::CrashExit) {
qCWarning(dcAuthenticationProcess()) << "Authentication process crashed:" << endl << qUtf8Printable(m_process->readAll());
}
if (exitCode != 0) {
qCWarning(dcAuthenticationProcess()) << "The authentication process finished with error" << exitCode << endl << qUtf8Printable(m_process->readAll());
}
qCDebug(dcAuthenticationProcess()) << "Finished successfully";
QFile resultFile(m_resultFileName);
if (!resultFile.exists()) {
qCWarning(dcAuthenticationProcess()) << "The process output file does not exist.";
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
return;
}
if (!resultFile.open(QIODevice::ReadWrite)) {
qCWarning(dcAuthenticationProcess()) << "Could not open result file from process:" << resultFile.errorString();
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
return;
}
QByteArray resultData = resultFile.readAll();
resultFile.close();
if (!resultFile.remove()) {
qCWarning(dcAuthenticationProcess()) << "Could not clean up result file from process:" << resultFile.errorString();
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(resultData, &error);
if(error.error != QJsonParseError::NoError) {
qCWarning(dcAuthenticationProcess()) << "Failed to parse lambda invoke result data" << resultData << ":" << error.errorString();
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
return;
}
QVariantMap response = jsonDoc.toVariant().toMap();
qCDebug(dcAuthenticationProcess()) << "-->" << response;
if (response.isEmpty()) {
qCWarning(dcAuthenticationProcess()) << "Received empty lambda result.";
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
return;
}
bool isValid = response.value("isValid").toBool();
if (isValid) {
emit authenticationFinished(Authenticator::AuthenticationErrorNoError);
} else {
emit authenticationFinished(Authenticator::AuthenticationErrorAuthenticationFailed);
}
}
void AuthenticationProcess::authenticate(const QString &token)
{
qCDebug(dcAuthenticationProcess()) << "Start authentication process for token" << token;
m_token = token;
if (m_dynamicCredentials) {
// Request the access information
requestDynamicCredentials();
} else {
// Direct call aws cli and assume the credentials will be provided static
startVerificationProcess();
}
}
}

View File

@ -0,0 +1,50 @@
#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();
void cleanUp();
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

View File

@ -7,7 +7,11 @@ AuthenticationReply::AuthenticationReply(ProxyClient *proxyClient, QObject *pare
QObject(parent),
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
@ -37,19 +41,25 @@ void AuthenticationReply::setError(Authenticator::AuthenticationError error)
void AuthenticationReply::setFinished()
{
emit finished();
m_timer->stop();
// emit in next event loop
QTimer::singleShot(0, this, &AuthenticationReply::finished);
}
void AuthenticationReply::onTimeout()
{
m_timedOut = true;
m_error = Authenticator::AuthenticationErrorTimeout;
m_timer->stop();
m_process->kill();
emit finished();
}
void AuthenticationReply::abort()
{
m_error = Authenticator::AuthenticationErrorAborted;
m_timer->stop();
m_process->kill();
emit finished();
}

View File

@ -4,6 +4,7 @@
#include <QUuid>
#include <QTimer>
#include <QObject>
#include <QProcess>
#include <QElapsedTimer>
#include "authenticator.h"
@ -25,9 +26,9 @@ public:
private:
explicit AuthenticationReply(ProxyClient *proxyClient, QObject *parent = nullptr);
ProxyClient *m_proxyClient = nullptr;
QTimer m_timer;
QTimer *m_timer = nullptr;
QProcess *m_process = nullptr;
bool m_timedOut = false;
bool m_finished = false;

View File

@ -19,7 +19,7 @@ public:
AuthenticationErrorTimeout,
AuthenticationErrorAborted,
AuthenticationErrorAuthenticationFailed,
AuthenticationErrorAuthenticationServerNotResponding
AuthenticationErrorProxyError
};
Q_ENUM(AuthenticationError)

View File

@ -1,3 +1,4 @@
#include "engine.h"
#include "proxyclient.h"
#include "awsauthenticator.h"
#include "loggingcategories.h"
@ -5,20 +6,47 @@
namespace remoteproxy {
AwsAuthenticator::AwsAuthenticator(QObject *parent) :
Authenticator(parent)
Authenticator(parent),
m_manager(new QNetworkAccessManager(this))
{
}
AwsAuthenticator::~AwsAuthenticator()
{
qCDebug(dcAuthentication()) << "Shutting down" << name();
}
QString AwsAuthenticator::name() const
{
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(dcAuthentication()) << "" << error;
}
AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient)
{
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
qCDebug(dcAuthentication()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
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;
}

View File

@ -2,9 +2,11 @@
#define AWSAUTHENTICATOR_H
#include <QObject>
#include <QNetworkAccessManager>
#include "authenticator.h"
#include "authenticationreply.h"
#include "authenticationprocess.h"
namespace remoteproxy {
@ -13,10 +15,17 @@ class AwsAuthenticator : public Authenticator
Q_OBJECT
public:
explicit AwsAuthenticator(QObject *parent = nullptr);
~AwsAuthenticator() override = default;
~AwsAuthenticator() override;
QString name() const override;
private:
QNetworkAccessManager *m_manager = nullptr;
QHash<AuthenticationProcess *, AuthenticationReply *> m_runningProcesses;
private slots:
void onAuthenticationProcessFinished(Authenticator::AuthenticationError error);
public slots:
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;

View File

@ -0,0 +1,30 @@
#include "dummyauthenticator.h"
#include "loggingcategories.h"
#include <QTimer>
namespace remoteproxy {
DummyAuthenticator::DummyAuthenticator(QObject *parent) :
Authenticator(parent)
{
}
QString DummyAuthenticator::name() const
{
return "Dummy authenticator";
}
AuthenticationReply *DummyAuthenticator::authenticate(ProxyClient *proxyClient)
{
qCDebug(dcAuthentication()) << name() << "validate" << proxyClient;
qCWarning(dcAuthentication()) << "Attention: This authenticator will always succeed! This is a security risk and was enabled explitly!";
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
setReplyError(reply, AuthenticationErrorNoError);
setReplyFinished(reply);
return reply;
}
}

View File

@ -0,0 +1,27 @@
#ifndef DUMMYAUTHENTICATOR_H
#define DUMMYAUTHENTICATOR_H
#include <QObject>
#include "proxyclient.h"
#include "authenticator.h"
namespace remoteproxy {
class DummyAuthenticator : public Authenticator
{
Q_OBJECT
public:
explicit DummyAuthenticator(QObject *parent = nullptr);
~DummyAuthenticator() override = default;
QString name() const override;
public slots:
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;
};
}
#endif // DUMMYAUTHENTICATOR_H

View File

@ -32,6 +32,10 @@ bool Engine::exists()
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)
qCDebug(dcEngine()) << "Start server engine";
@ -44,15 +48,12 @@ void Engine::start()
QUrl websocketServerUrl;
websocketServerUrl.setScheme("wss");
websocketServerUrl.setHost(m_webSocketServerHostAddress.toString());
websocketServerUrl.setPort(m_webSocketServerPort);
websocketServerUrl.setHost(m_configuration->webSocketServerHost().toString());
websocketServerUrl.setPort(m_configuration->webSocketServerPort());
m_webSocketServer->setServerUrl(websocketServerUrl);
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";
m_proxyServer->startServer();
@ -74,6 +75,11 @@ bool Engine::running() const
return m_running;
}
bool Engine::developerMode() const
{
return m_developerMode;
}
QString Engine::serverName() const
{
return m_serverName;
@ -84,36 +90,24 @@ void Engine::setServerName(const QString &serverName)
m_serverName = serverName;
}
void Engine::setWebSocketServerHostAddress(const QHostAddress &hostAddress)
void Engine::setConfiguration(ProxyConfiguration *configuration)
{
qCDebug(dcEngine()) << "Websocket server host address:" << hostAddress;
m_webSocketServerHostAddress = hostAddress;
}
void Engine::setWebSocketServerPort(const quint16 &port)
{
qCDebug(dcEngine()) << "Websocket server port:" << port;
m_webSocketServerPort = port;
m_configuration = configuration;
qCDebug(dcApplication()) << "Set configuration" << m_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;
}
void Engine::setAuthenticationServerUrl(const QUrl &url)
{
qCDebug(dcEngine()) << "Authentication server URL" << url.toString();
m_authenticationServerUrl = url;
qCDebug(dcEngine()) << "SSL certificate information:";
qCDebug(dcEngine()) << " Common name:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::CommonName);
qCDebug(dcEngine()) << " Organisation:" << m_sslConfiguration.localCertificate().issuerInfo(QSslCertificate::Organization);
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)
@ -132,6 +126,11 @@ void Engine::setAuthenticator(Authenticator *authenticator)
// FIXME: connect
}
void Engine::setDeveloperModeEnabled(bool enabled)
{
m_developerMode = enabled;
}
Authenticator *Engine::authenticator() const
{
return m_authenticator;
@ -172,7 +171,6 @@ void Engine::clean()
}
if (m_configuration) {
delete m_configuration;
m_configuration = nullptr;
}
}

View File

@ -26,17 +26,17 @@ public:
void stop();
bool running() const;
bool developerMode() const;
QString serverName() const;
void setServerName(const QString &serverName);
void setWebSocketServerHostAddress(const QHostAddress &hostAddress);
void setWebSocketServerPort(const quint16 &port);
void setConfiguration(ProxyConfiguration *configuration);
void setSslConfiguration(const QSslConfiguration &configuration);
void setAuthenticationServerUrl(const QUrl &url);
void setAuthenticator(Authenticator *authenticator);
void setDeveloperModeEnabled(bool enabled);
Authenticator *authenticator() const;
ProxyServer *proxyServer() const;
WebSocketServer *webSocketServer() const;
@ -47,13 +47,10 @@ private:
static Engine *s_instance;
bool m_running = false;
bool m_developerMode = false;
QString m_serverName;
quint16 m_webSocketServerPort = 1212;
QHostAddress m_webSocketServerHostAddress = QHostAddress::LocalHost;
QSslConfiguration m_sslConfiguration;
QUrl m_authenticationServerUrl;
ProxyConfiguration *m_configuration = nullptr;
Authenticator *m_authenticator = nullptr;
ProxyServer *m_proxyServer = nullptr;

View File

@ -3,6 +3,12 @@ include(../nymea-remoteproxy.pri)
TEMPLATE = lib
TARGET = nymea-remoteproxy
# -L/home/timon/guh/development/cloud/aws-sdk-cpp/build/install/lib
# -laws-cpp-sdk-access-management \
# -laws-cpp-sdk-cognito-identity \
# -laws-cpp-sdk-iam \
# -laws-cpp-sdk-kinesis\
HEADERS += \
engine.h \
loggingcategories.h \
@ -19,7 +25,9 @@ HEADERS += \
authentication/awsauthenticator.h \
authentication/authenticationreply.h \
proxyconfiguration.h \
tunnelconnection.h
tunnelconnection.h \
authentication/authenticationprocess.h \
authentication/dummyauthenticator.h
SOURCES += \
engine.cpp \
@ -37,7 +45,9 @@ SOURCES += \
authentication/awsauthenticator.cpp \
authentication/authenticationreply.cpp \
proxyconfiguration.cpp \
tunnelconnection.cpp
tunnelconnection.cpp \
authentication/authenticationprocess.cpp \
authentication/dummyauthenticator.cpp
# install header file with relative subdirectory
@ -47,3 +57,6 @@ for(header, HEADERS) {
eval(headers_$${path}.path = $${path})
eval(INSTALLS *= headers_$${path})
}
target.path = /usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')
INSTALLS += target

View File

@ -6,7 +6,8 @@ Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc")
Q_LOGGING_CATEGORY(dcJsonRpcTraffic, "JsonRpcTraffic")
Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer")
Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic")
Q_LOGGING_CATEGORY(dcAuthenticator, "Authenticator")
Q_LOGGING_CATEGORY(dcAuthentication, "Authentication")
Q_LOGGING_CATEGORY(dcAuthenticationProcess, "AuthenticationProcess")
Q_LOGGING_CATEGORY(dcProxyServer, "ProxyServer")
Q_LOGGING_CATEGORY(dcProxyServerTraffic, "ProxyServerTraffic")

View File

@ -10,7 +10,8 @@ Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc)
Q_DECLARE_LOGGING_CATEGORY(dcJsonRpcTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer)
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcAuthenticator)
Q_DECLARE_LOGGING_CATEGORY(dcAuthentication)
Q_DECLARE_LOGGING_CATEGORY(dcAuthenticationProcess)
Q_DECLARE_LOGGING_CATEGORY(dcProxyServer)
Q_DECLARE_LOGGING_CATEGORY(dcProxyServerTraffic)

View File

@ -1,10 +1,151 @@
#include "loggingcategories.h"
#include "proxyconfiguration.h"
#include <QFileInfo>
namespace remoteproxy {
ProxyConfiguration::ProxyConfiguration(QObject *parent) : QObject(parent)
ProxyConfiguration::ProxyConfiguration(QObject *parent) :
QObject(parent)
{
}
bool ProxyConfiguration::loadConfiguration(const QString &fileName)
{
QFileInfo fileInfo(fileName);
if (!fileInfo.exists()) {
qCWarning(dcApplication()) << "Configuration: Could not find configuration file" << fileName;
return false;
}
if (!fileInfo.isReadable()) {
qCWarning(dcApplication()) << "Configuration: Cannot read configuration file" << fileName;
return false;
}
QSettings settings(fileName, QSettings::IniFormat);
qCDebug(dcApplication()) << settings.childGroups();
settings.beginGroup("General");
setWriteLogFile(settings.value("writeLogs", false).toBool());
setLogFileName(settings.value("logFile", "/var/log/nymea-remoteproxy.log").toString());
setSslCertificateFileName(settings.value("certificate", "/etc/ssl/certs/ssl-cert-snakeoil.pem").toString());
setSslCertificateKeyFileName(settings.value("certificateKey", "/etc/ssl/private/ssl-cert-snakeoil.key").toString());
settings.endGroup();
settings.beginGroup("WebSocketServer");
setWebSocketServerHost(QHostAddress(settings.value("host", "127.0.0.1").toString()));
setWebSocketServerPort(static_cast<quint16>(settings.value("port", 1212).toInt()));
settings.endGroup();
settings.beginGroup("TcpServer");
setWebSocketServerHost(QHostAddress(settings.value("host", "127.0.0.1").toString()));
setWebSocketServerPort(static_cast<quint16>(settings.value("port", 1213).toInt()));
settings.endGroup();
return true;
}
bool ProxyConfiguration::writeLogFile() const
{
return m_writeLogFile;
}
void ProxyConfiguration::setWriteLogFile(bool enabled)
{
m_writeLogFile = enabled;
}
QString ProxyConfiguration::logFileName() const
{
return m_logFileName;
}
void ProxyConfiguration::setLogFileName(const QString &logFileName)
{
m_logFileName = logFileName;
}
QString ProxyConfiguration::sslCertificateFileName() const
{
return m_sslCertificateFileName;
}
void ProxyConfiguration::setSslCertificateFileName(const QString &fileName)
{
m_logFileName = fileName;
}
QString ProxyConfiguration::sslCertificateKeyFileName() const
{
return m_sslCertificateKeyFileName;
}
void ProxyConfiguration::setSslCertificateKeyFileName(const QString &fileName)
{
m_sslCertificateKeyFileName = fileName;
}
QHostAddress ProxyConfiguration::webSocketServerHost() const
{
return m_webSocketServerHost;
}
void ProxyConfiguration::setWebSocketServerHost(const QHostAddress &address)
{
m_webSocketServerHost = address;
}
quint16 ProxyConfiguration::webSocketServerPort() const
{
return m_webSocketServerPort;
}
void ProxyConfiguration::setWebSocketServerPort(quint16 port)
{
m_webSocketServerPort = port;
}
QHostAddress ProxyConfiguration::tcpServerHost() const
{
return m_tcpServerHost;
}
void ProxyConfiguration::setTcpServerHost(const QHostAddress &address)
{
m_tcpServerHost = address;
}
quint16 ProxyConfiguration::tcpServerPort() const
{
return m_tcpServerPort;
}
void ProxyConfiguration::setTcpServerPort(quint16 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;
}
}

View File

@ -2,6 +2,8 @@
#define PROXYCONFIGURATION_H
#include <QObject>
#include <QSettings>
#include <QHostAddress>
namespace remoteproxy {
@ -11,12 +13,54 @@ class ProxyConfiguration : public QObject
public:
explicit ProxyConfiguration(QObject *parent = nullptr);
signals:
bool loadConfiguration(const QString &fileName);
public slots:
// General
bool writeLogFile() const;
void setWriteLogFile(bool enabled);
QString logFileName() const;
void setLogFileName(const QString &logFileName);
QString sslCertificateFileName() const;
void setSslCertificateFileName(const QString &fileName);
QString sslCertificateKeyFileName() const;
void setSslCertificateKeyFileName(const QString &fileName);
// WebSocketServer
QHostAddress webSocketServerHost() const;
void setWebSocketServerHost(const QHostAddress &address);
quint16 webSocketServerPort() const;
void setWebSocketServerPort(quint16 port);
// TcpServer
QHostAddress tcpServerHost() const;
void setTcpServerHost(const QHostAddress &address);
quint16 tcpServerPort() const;
void setTcpServerPort(quint16 port);
private:
// General
bool m_writeLogFile = false;
QString m_logFileName = "/var/log/nymea-remoteproxy.log";
QString m_sslCertificateFileName = "/etc/ssl/certs/ssl-cert-snakeoil.pem";
QString m_sslCertificateKeyFileName = "/etc/ssl/private/ssl-cert-snakeoil.key";
// WebSocketServer
QHostAddress m_webSocketServerHost = QHostAddress::LocalHost;
quint16 m_webSocketServerPort = 1212;
// TcpServer
QHostAddress m_tcpServerHost = QHostAddress::LocalHost;
quint16 m_tcpServerPort = 1213;
};
QDebug operator<< (QDebug debug, ProxyConfiguration *configuration);
}
#endif // PROXYCONFIGURATION_H

View File

@ -1,14 +1,14 @@
INCLUDEPATH += $${PWD}
HEADERS += \
$${PWD}/jsonrpcclient.h \
$${PWD}/proxyjsonrpcclient.h \
$${PWD}/jsonreply.h \
$${PWD}/remoteproxyconnection.h \
$${PWD}/proxyconnection.h \
$${PWD}/websocketconnection.h
SOURCES += \
$${PWD}/jsonrpcclient.cpp \
$${PWD}/proxyjsonrpcclient.cpp \
$${PWD}/jsonreply.cpp \
$${PWD}/remoteproxyconnection.cpp \
$${PWD}/proxyconnection.cpp \

View File

@ -2,13 +2,11 @@ include(../nymea-remoteproxy.pri)
TEMPLATE = lib
TARGET = nymea-remoteproxyclient
target.path = /usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')
include(libnymea-remoteproxyclient.pri)
# install header file with relative subdirectory
for(header, HEADERS) {
path = /usr/include/nymea-remoteproxyclient/$${dirname(header)}
eval(headers_$${path}.files += $${header})
eval(headers_$${path}.path = $${path})
eval(INSTALLS *= headers_$${path})
}
installheaders.files = remoteproxyconnection.h
installheaders.path = /usr/include/nymea-remoteproxyclient/
INSTALLS += target installheaders

View File

@ -1,4 +1,4 @@
#include "jsonrpcclient.h"
#include "proxyjsonrpcclient.h"
#include "proxyconnection.h"
#include <QJsonDocument>

View File

@ -1,5 +1,7 @@
#include "remoteproxyconnection.h"
#include "websocketconnection.h"
#include "proxyjsonrpcclient.h"
#include "proxyconnection.h"
Q_LOGGING_CATEGORY(dcRemoteProxyClientConnection, "RemoteProxyClientConnection")
Q_LOGGING_CATEGORY(dcRemoteProxyClientConnectionTraffic, "RemoteProxyClientConnectionTraffic")

View File

@ -8,14 +8,14 @@
#include <QHostAddress>
#include <QLoggingCategory>
#include "jsonrpcclient.h"
#include "proxyconnection.h"
Q_DECLARE_LOGGING_CATEGORY(dcRemoteProxyClientConnection)
Q_DECLARE_LOGGING_CATEGORY(dcRemoteProxyClientConnectionTraffic)
namespace remoteproxyclient {
class JsonRpcClient;
class ProxyConnection;
class RemoteProxyConnection : public QObject
{
Q_OBJECT

13
nymea-remoteproxy.conf Normal file
View 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

View File

@ -16,6 +16,9 @@ CONFIG += c++11 console
QMAKE_CXXFLAGS *= -Werror -std=c++11 -g
QMAKE_LFLAGS *= -std=c++11
INCLUDEPATH += /home/timon/guh/development/cloud/aws-sdk-cpp/build/install/include
LIBS += -L/home/timon/guh/development/cloud/aws-sdk-cpp/build/install/lib -laws-cpp-sdk-core -laws-cpp-sdk-lambda
top_srcdir=$$PWD
top_builddir=$$shadowed($$PWD)

View File

@ -1,11 +1,11 @@
include(nymea-remoteproxy.pri)
TEMPLATE=subdirs
SUBDIRS += server libnymea-remoteproxy libnymea-remoteproxyclient tests
SUBDIRS += server client libnymea-remoteproxy libnymea-remoteproxyclient tests
server.depends = libnymea-remoteproxy
tests.depends = libnymea-remoteproxy libnymea-remoteproxyclient
client.depends = libnymea-remoteproxyclient
message("----------------------------------------------------------")
message("Building nymea-remoteproxy $${SERVER_VERSION}")

View File

@ -19,7 +19,9 @@
#include "engine.h"
#include "loggingcategories.h"
#include "proxyconfiguration.h"
#include "authentication/awsauthenticator.h"
#include "authentication/dummyauthenticator.h"
using namespace remoteproxy;
@ -95,7 +97,7 @@ int main(int argc, char *argv[])
s_loggingFilters.insert("JsonRpcTraffic", true);
s_loggingFilters.insert("WebSocketServer", true);
s_loggingFilters.insert("WebSocketServerTraffic", false);
s_loggingFilters.insert("Authenticator", true);
s_loggingFilters.insert("Authentication", true);
s_loggingFilters.insert("ProxyServer", true);
// command line parser
@ -115,30 +117,33 @@ int main(int argc, char *argv[])
"logfile", "/var/log/nymea-remoteproxy.log");
parser.addOption(logfileOption);
QCommandLineOption serverOption(QStringList() << "s" << "server", "The server address this proxy will listen on. "
"Default is 127.0.0.1", "hostaddress", "127.0.0.1");
parser.addOption(serverOption);
QCommandLineOption developmentOption(QStringList() << "d" << "development", "Enable the development mode. This enabled the server "
"assumes there are static AWS credentials provided to aws-cli.");
parser.addOption(developmentOption);
QCommandLineOption portOption(QStringList() << "p" << "port", "The proxy server port. Default is 1212", "port", "1212");
parser.addOption(portOption);
QCommandLineOption mockAuthenticatorOption(QStringList() << "m" << "mock-authenticator", "Start the server using a mock authenticator which returns always true.");
parser.addOption(mockAuthenticatorOption);
QCommandLineOption certOption(QStringList() << "c" <<"certificate", "The path to the SSL certificate used for "
"this proxy server.", "certificate");
parser.addOption(certOption);
QCommandLineOption certKeyOption(QStringList() << "k" << "certificate-key", "The path to the SSL certificate key "
"used for this proxy server.", "certificate-key");
parser.addOption(certKeyOption);
QCommandLineOption authenticationUrlOption(QStringList() << "a" << "authentication-server",
"The server url of the AWS authentication server.", "url", "https://127.0.0.1");
parser.addOption(authenticationUrlOption);
QCommandLineOption configOption(QStringList() << "c" <<"configuration", "The path to the proxy server configuration file. The default is /etc/nymea-remoteproxy/nymea-remoteproxy.conf", "configuration");
configOption.setDefaultValue("/etc/nymea-remoteproxy/nymea-remoteproxy.conf");
parser.addOption(configOption);
QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print more verbose.");
parser.addOption(verboseOption);
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)) {
s_loggingFilters["Debug"] = true;
s_loggingFilters["WebSocketServerTraffic"] = true;
@ -146,104 +151,80 @@ int main(int argc, char *argv[])
QLoggingCategory::installFilter(loggingCategoryFilter);
// Open the logfile, if any specified
if (parser.isSet(logfileOption)) {
QFileInfo fi(parser.value(logfileOption));
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);
// Verify webserver configuration
if (configuration->webSocketServerHost().isNull()) {
qCCritical(dcApplication()) << "Invalid web socket host address passed.";
exit(-1);
}
// Port
bool ok = false;
uint port = parser.value(portOption).toUInt(&ok);
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);
// Verify tcp server configuration
if (configuration->tcpServerHost().isNull()) {
qCCritical(dcApplication()) << "Invalid TCP server host address passed.";
exit(-1);
}
// SSL certificate
QSslConfiguration sslConfiguration;
if (parser.isSet(certOption)) {
// Load certificate
QFile certFile(parser.value(certOption));
if (!certFile.open(QIODevice::ReadOnly)) {
qCCritical(dcApplication()) << "Could not open certificate file:" << parser.value(certOption) << certFile.errorString();
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);
// Load certificate
QFile certFile(configuration->sslCertificateFileName());
if (!certFile.open(QIODevice::ReadOnly)) {
qCCritical(dcApplication()) << "Could not open certificate file" << configuration->sslCertificateFileName() << certFile.errorString();
exit(-1);
}
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
if (parser.isSet(certKeyOption)) {
QFile certKeyFile(parser.value(certKeyOption));
if (!certKeyFile.open(QIODevice::ReadOnly)) {
qCCritical(dcApplication()) << "Could not open certificate key file:" << parser.value(certKeyOption) << certKeyFile.errorString();
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);
QFile certKeyFile(configuration->sslCertificateKeyFileName());
if (!certKeyFile.open(QIODevice::ReadOnly)) {
qCCritical(dcApplication()) << "Could not open certificate key file:" << configuration->sslCertificateKeyFileName() << certKeyFile.errorString();
exit(-1);
}
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()) {
qCCritical(dcApplication()) << "No SSL configuration specified. The server does not suppoert insecure connections.";
exit(-1);
}
// Authentication server url
QUrl authenticationServerUrl(parser.value(authenticationUrlOption));
if (!authenticationServerUrl.isValid()) {
qCCritical(dcApplication()) << "Invalid authentication server url:" << parser.value(authenticationUrlOption);
exit(-1);
}
qCDebug(dcApplication()) << "==============================================";
qCDebug(dcApplication()) << "==========================================================";
qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion();
qCDebug(dcApplication()) << "==============================================";
qCDebug(dcApplication()) << "==========================================================";
if (parser.isSet(developmentOption)) {
qCWarning(dcApplication()) << "##########################################################";
qCWarning(dcApplication()) << "# DEVELOPMENT MODE #";
qCWarning(dcApplication()) << "##########################################################";
}
if (s_loggingEnabled)
qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName();
// Create default authenticator
AwsAuthenticator *authenticator = new AwsAuthenticator(nullptr);
Authenticator *authenticator = nullptr;
if (parser.isSet(mockAuthenticatorOption)) {
authenticator = qobject_cast<Authenticator *>(new DummyAuthenticator(nullptr));
} else {
// Create default authenticator
authenticator = qobject_cast<Authenticator *>(new AwsAuthenticator(nullptr));
}
// Configure and start the engines
Engine::instance()->setAuthenticator(authenticator);
Engine::instance()->setWebSocketServerHostAddress(serverHostAddress);
Engine::instance()->setWebSocketServerPort(static_cast<quint16>(port));
Engine::instance()->setConfiguration(configuration);
Engine::instance()->setDeveloperModeEnabled(parser.isSet(developmentOption));
Engine::instance()->setSslConfiguration(sslConfiguration);
Engine::instance()->setAuthenticationServerUrl(authenticationServerUrl);
Engine::instance()->setAuthenticator(authenticator);
Engine::instance()->start();
return application.exec();

View File

@ -29,7 +29,7 @@ void MockAuthenticator::replyFinished()
MockAuthenticationReply *reply = static_cast<MockAuthenticationReply *>(sender());
reply->deleteLater();
qCDebug(dcAuthenticator()) << name() << "Authentication finished.";
qCDebug(dcAuthentication()) << name() << "Authentication finished.";
setReplyError(reply->authenticationReply(), reply->error());
setReplyFinished(reply->authenticationReply());
@ -37,7 +37,7 @@ void MockAuthenticator::replyFinished()
AuthenticationReply *MockAuthenticator::authenticate(ProxyClient *proxyClient)
{
qCDebug(dcAuthenticator()) << name() << "Start authentication for" << proxyClient << "using token" << proxyClient->token();
qCDebug(dcAuthentication()) << name() << "Start authentication for" << proxyClient << "using token" << proxyClient->token();
AuthenticationReply *authenticationReply = createAuthenticationReply(proxyClient, this);

View File

@ -13,6 +13,8 @@
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
QObject(parent)
{
m_configuration = new ProxyConfiguration(this);
QFile certificateFile(":/test-certificate.crt");
if (!certificateFile.open(QIODevice::ReadOnly)) {
qWarning() << "Could not open resource file" << certificateFile.fileName();
@ -72,11 +74,10 @@ void RemoteProxyTests::startEngine()
if (!Engine::exists()) {
QString serverName = "nymea-remoteproxy-testserver";
Engine::instance()->setAuthenticator(m_authenticator);
Engine::instance()->setAuthenticationServerUrl(QUrl("https://localhost"));
Engine::instance()->setServerName(serverName);
Engine::instance()->setWebSocketServerPort(m_port);
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
Engine::instance()->setConfiguration(m_configuration);
Engine::instance()->setSslConfiguration(m_sslConfiguration);
Engine::instance()->setDeveloperModeEnabled(true);
QVERIFY(Engine::exists());
QVERIFY(Engine::instance()->serverName() == serverName);
@ -89,6 +90,8 @@ void RemoteProxyTests::startServer()
if (!Engine::instance()->running()) {
QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged);
Engine::instance()->setConfiguration(m_configuration);
Engine::instance()->setDeveloperModeEnabled(true);
Engine::instance()->start();
runningSpy.wait();
QVERIFY(runningSpy.count() == 1);
@ -217,9 +220,8 @@ void RemoteProxyTests::webserverConnectionBlocked()
dummyServer.listen(QHostAddress::LocalHost, m_port);
// Start proxy webserver
Engine::instance()->setWebSocketServerPort(m_port);
Engine::instance()->setConfiguration(m_configuration);
Engine::instance()->setAuthenticator(m_authenticator);
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
Engine::instance()->setSslConfiguration(m_sslConfiguration);
QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged);
@ -324,7 +326,7 @@ void RemoteProxyTests::authenticate_data()
<< 100 << Authenticator::AuthenticationErrorAuthenticationFailed;
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
<< 100 << Authenticator::AuthenticationErrorAborted;

View File

@ -12,6 +12,7 @@
#include "jsonrpc/jsontypes.h"
#include "mockauthenticator.h"
#include "proxyconfiguration.h"
#include "remoteproxyconnection.h"
using namespace remoteproxy;
@ -24,8 +25,11 @@ public:
explicit RemoteProxyTests(QObject *parent = nullptr);
private:
ProxyConfiguration *m_configuration = nullptr;
quint16 m_port = 1212;
QHostAddress m_serverAddress = QHostAddress::LocalHost;
QSslConfiguration m_sslConfiguration;
MockAuthenticator *m_authenticator = nullptr;
QString m_testToken;