Start authenticator work

more-debug
Simon Stürz 2018-08-13 17:49:49 +02:00
parent 84905bcd12
commit cac4a1491b
10 changed files with 376 additions and 18 deletions

View File

@ -26,6 +26,38 @@ If you want to start the proxy server from the build directory, you need to expo
$ ./server/nymea-remoteproxy -c ../nymea-remoteproxy/tests/test-certificate.crt -k ../nymea-remoteproxy/tests/test-certificate.key
## AWS SDK
Get the latest source code and build dependecies
$ apt update
$ apt install git build-essential cmake libcurl4-openssl-dev libssl-dev uuid-dev zlib1g-dev libpulse-dev
$ git clone https://github.com/aws/aws-sdk-cpp.git
Create the build and install folder
$ cd aws-sdk-cpp
$ mkdir -p build/install
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_ONLY="lambda" -DCMAKE_INSTALL_PREFIX=$(pwd)/install ../
$ make -j$(nproc)
Install build output into install directory
$ make install
#### Building debian package
$ git clone https://github.com/aws/aws-sdk-cpp.git
$ cd aws-sdk-cpp
$ git clone git@gitlab.guh.io:cloud/aws-sdk-cpp-debian.git debian
$ crossbuilder
# Install

View File

@ -10,6 +10,11 @@ AwsAuthenticator::AwsAuthenticator(QObject *parent) :
}
AwsAuthenticator::~AwsAuthenticator()
{
qCDebug(dcAuthenticator()) << "Shutting down" << name();
}
QString AwsAuthenticator::name() const
{
return "AWS authenticator";
@ -19,6 +24,9 @@ AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient)
{
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
// TODO: start authentication request
return reply;
}

View File

@ -13,10 +13,13 @@ class AwsAuthenticator : public Authenticator
Q_OBJECT
public:
explicit AwsAuthenticator(QObject *parent = nullptr);
~AwsAuthenticator() override = default;
~AwsAuthenticator() override;
QString name() const override;
private:
public slots:
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;

View File

@ -0,0 +1,123 @@
#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 &region, 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 &region, 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 &region, 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 &region, 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 &region, 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 &region, 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;
}

View File

@ -0,0 +1,27 @@
#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 &region, 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 &region, const QByteArray &service);
static QByteArray getStringToSign(const QByteArray &canonicalRequest, const QByteArray &dateTime, const QByteArray &region, const QByteArray &service);
static QByteArray getSignatureKey(const QByteArray &key, const QByteArray &date, const QByteArray &region, const QByteArray &service);
static QByteArray getSignature(const QByteArray &stringToSign, const QByteArray &secretAccessKey, const QByteArray &dateTime, const QString &region, const QString &service);
static QByteArray getAuthorizationHeader(const QByteArray &accessKeyId, const QByteArray &dateTime, const QString &region, const QString &service, const QNetworkRequest &request, const QByteArray &signature);
};
#endif // SIGV4UTILS_H

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,8 @@ HEADERS += \
authentication/awsauthenticator.h \
authentication/authenticationreply.h \
proxyconfiguration.h \
tunnelconnection.h
tunnelconnection.h \
authentication/sigv4utils.h
SOURCES += \
engine.cpp \
@ -37,7 +44,8 @@ SOURCES += \
authentication/awsauthenticator.cpp \
authentication/authenticationreply.cpp \
proxyconfiguration.cpp \
tunnelconnection.cpp
tunnelconnection.cpp \
authentication/sigv4utils.cpp
# install header file with relative subdirectory

View File

@ -1,10 +1,132 @@
#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()) << "Could not find configuration file" << fileName;
return false;
}
if (!fileInfo.isReadable()) {
qCWarning(dcApplication()) << "Cannot read configuration file" << fileName;
return false;
}
QSettings settings(fileName, QSettings::IniFormat);
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;
}
}

View File

@ -2,6 +2,8 @@
#define PROXYCONFIGURATION_H
#include <QObject>
#include <QSettings>
#include <QHostAddress>
namespace remoteproxy {
@ -11,9 +13,49 @@ 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;
};

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

@ -124,16 +124,14 @@ int main(int argc, char *argv[])
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 authenticationUrlOption(QStringList() << "a" << "authentication-server",
"The server url of the AWS authentication server.", "url", "https://127.0.0.1");
parser.addOption(authenticationUrlOption);
QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print more verbose.");
parser.addOption(verboseOption);
@ -221,13 +219,6 @@ int main(int argc, char *argv[])
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()) << "Starting" << application.applicationName() << application.applicationVersion();
qCDebug(dcApplication()) << "==============================================";
@ -243,7 +234,6 @@ int main(int argc, char *argv[])
Engine::instance()->setWebSocketServerHostAddress(serverHostAddress);
Engine::instance()->setWebSocketServerPort(static_cast<quint16>(port));
Engine::instance()->setSslConfiguration(sslConfiguration);
Engine::instance()->setAuthenticationServerUrl(authenticationServerUrl);
Engine::instance()->start();
return application.exec();