Add configuration for the tunnel proxy server

pull/455/head
Michael Zanetti 2022-01-20 16:59:56 +01:00
parent fea2ae0866
commit 47add31e63
18 changed files with 283 additions and 78 deletions

View File

@ -94,7 +94,7 @@ MqttChannel *MqttProviderImplementation::createChannel(const QString &clientId,
foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) {
if (clientAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) {
foreach (const ServerConfiguration &config, m_broker->configurations()) {
if (config.address == QHostAddress("0.0.0.0") || clientAddress.isInSubnet(config.address, addressEntry.prefixLength())) {
if (QHostAddress(config.address) == QHostAddress("0.0.0.0") || clientAddress.isInSubnet(QHostAddress(config.address), addressEntry.prefixLength())) {
channel->m_serverAddress = addressEntry.ip();
channel->m_serverPort = config.port;
break;
@ -145,9 +145,9 @@ MqttClient *MqttProviderImplementation::createInternalClient(const QString &clie
ServerConfiguration preferredConfig;
foreach (const ServerConfiguration &config, m_broker->configurations()) {
if (config.address == QHostAddress::Any
|| config.address == QHostAddress::AnyIPv4
|| config.address == QHostAddress::LocalHost) {
if (QHostAddress(config.address) == QHostAddress::Any
|| QHostAddress(config.address) == QHostAddress::AnyIPv4
|| QHostAddress(config.address) == QHostAddress::LocalHost) {
preferredConfig = config;
break;
}
@ -176,12 +176,12 @@ MqttClient *MqttProviderImplementation::createInternalClient(const QString &clie
m_broker->removePolicy(clientId);
});
if (preferredConfig.address == QHostAddress::Any
|| preferredConfig.address == QHostAddress::AnyIPv4
|| preferredConfig.address == QHostAddress::LocalHost) {
if (QHostAddress(preferredConfig.address) == QHostAddress::Any
|| QHostAddress(preferredConfig.address) == QHostAddress::AnyIPv4
|| QHostAddress(preferredConfig.address) == QHostAddress::LocalHost) {
client->connectToHost("127.0.0.1", preferredConfig.port);
} else {
client->connectToHost(preferredConfig.address.toString(), preferredConfig.port);
client->connectToHost(preferredConfig.address, preferredConfig.port);
}
return client;
}

View File

@ -86,6 +86,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
// Objects
registerObject<ServerConfiguration>();
registerObject<WebServerConfiguration>();
registerObject<TunnelProxyServerConfiguration>();
registerObject<MqttPolicy>();
// Methods
@ -118,12 +119,15 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
QVariantList webSocketServerConfigurations;
webSocketServerConfigurations.append(objectRef<ServerConfiguration>());
returns.insert("webSocketServerConfigurations", webSocketServerConfigurations);
QVariantList tunnelProxyServerConfigurations;
tunnelProxyServerConfigurations.append(objectRef<TunnelProxyServerConfiguration>());
returns.insert("tunnelProxyServerConfigurations", tunnelProxyServerConfigurations);
QVariantList mqttServerConfigurations;
mqttServerConfigurations.append(objectRef<ServerConfiguration>());
QVariantMap cloudConfiguration;
cloudConfiguration.insert("enabled", enumValueName(Bool));
returns.insert("cloud", cloudConfiguration);
registerMethod("GetConfigurations", description, params, returns);
registerMethod("GetConfigurations", description, params, returns, Types::PermissionScopeNone);
params.clear(); returns.clear();
description = "Set the name of the server. Default is nymea.";
@ -173,6 +177,18 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
registerMethod("DeleteWebSocketServerConfiguration", description, params, returns);
params.clear(); returns.clear();
description = "Configure a Tunnel Proxy Server interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.";
params.insert("configuration", objectRef<TunnelProxyServerConfiguration>());
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
registerMethod("SetTunnelProxyServerConfiguration", description, params, returns);
params.clear(); returns.clear();
description = "Delete a Tunnel Proxy Server interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.";
params.insert("id", enumValueName(String));
returns.insert("configurationError", enumRef<NymeaConfiguration::ConfigurationError>());
registerMethod("DeleteTunnelProxyServerConfiguration", description, params, returns);
params.clear(); returns.clear();
description = "Configure a WebServer interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added.";
params.insert("configuration", objectRef<WebServerConfiguration>());
@ -257,6 +273,16 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
params.insert("id", enumValueName(String));
registerNotification("WebSocketServerConfigurationRemoved", description, params);
params.clear(); returns.clear();
description = "Emitted whenever the tunnel proxy server configuration changes.";
params.insert("tunnelProxyServerConfiguration", objectRef<TunnelProxyServerConfiguration>());
registerNotification("TunnelProxyServerConfigurationChanged", description, params);
params.clear(); returns.clear();
description = "Emitted whenever a tunnel proxy server configuration is removed.";
params.insert("id", enumValueName(String));
registerNotification("TunnelProxyServerConfigurationRemoved", description, params);
params.clear(); returns.clear();
description = "Emitted whenever the MQTT broker configuration is changed.";
params.insert("mqttServerConfiguration", objectRef<ServerConfiguration>());
@ -303,6 +329,8 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent):
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webServerConfigurationRemoved, this, &ConfigurationHandler::onWebServerConfigurationRemoved);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webSocketServerConfigurationChanged, this, &ConfigurationHandler::onWebSocketServerConfigurationChanged);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webSocketServerConfigurationRemoved, this, &ConfigurationHandler::onWebSocketServerConfigurationRemoved);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::tunnelProxyServerConfigurationChanged, this, &ConfigurationHandler::onTunnelProxyServerConfigurationChanged);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::tunnelProxyServerConfigurationRemoved, this, &ConfigurationHandler::onTunnelProxyServerConfigurationRemoved);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::mqttServerConfigurationChanged, this, &ConfigurationHandler::onMqttServerConfigurationChanged);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::mqttServerConfigurationRemoved, this, &ConfigurationHandler::onMqttServerConfigurationRemoved);
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::mqttPolicyChanged, this, &ConfigurationHandler::onMqttPolicyChanged);
@ -340,6 +368,12 @@ JsonReply *ConfigurationHandler::GetConfigurations(const QVariantMap &params) co
}
returns.insert("webSocketServerConfigurations", webSocketServerConfigs);
QVariantList tunnelProxyServerConfigs;
foreach (const TunnelProxyServerConfiguration &config, NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations()) {
tunnelProxyServerConfigs.append(pack(config));
}
returns.insert("tunnelProxyServerConfigurations", tunnelProxyServerConfigs);
QVariantMap cloudConfig;
cloudConfig.insert("enabled", NymeaCore::instance()->configuration()->cloudEnabled());
returns.insert("cloud", cloudConfig);
@ -422,7 +456,7 @@ JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &pa
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
}
qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(config.address.toString()).arg(config.port);
qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setTcpServerConfiguration(config);
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError));
@ -453,7 +487,7 @@ JsonReply *ConfigurationHandler::SetWebServerConfiguration(const QVariantMap &pa
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
}
qCDebug(dcJsonRpc()) << QString("Configure web server %1:%2").arg(config.address.toString()).arg(config.port);
qCDebug(dcJsonRpc()) << QString("Configure web server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError));
@ -483,7 +517,7 @@ JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantM
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
}
qCDebug(dcJsonRpc()) << QString("Configuring web socket server %1:%2").arg(config.address.toString()).arg(config.port);
qCDebug(dcJsonRpc()) << QString("Configuring web socket server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setWebSocketServerConfiguration(config);
@ -500,6 +534,37 @@ JsonReply *ConfigurationHandler::DeleteWebSocketServerConfiguration(const QVaria
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::SetTunnelProxyServerConfiguration(const QVariantMap &params) const
{
TunnelProxyServerConfiguration config = unpack<TunnelProxyServerConfiguration>(params.value("configuration").toMap());
if (config.id.isEmpty()) {
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress));
if (config.port <= 0 || config.port > 65535) {
qCWarning(dcJsonRpc()) << "Port out of range";
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
}
qCDebug(dcJsonRpc()) << QString("Configuring tunnel proxy server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setTunnelProxyServerConfiguration(config);
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::DeleteTunnelProxyServerConfiguration(const QVariantMap &params) const
{
QString id = params.value("id").toString();
if (id.isEmpty() || !NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().contains(id)) {
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId));
}
NymeaCore::instance()->configuration()->removeTunnelProxyServerConfiguration(id);
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError));
}
JsonReply *ConfigurationHandler::GetMqttServerConfigurations(const QVariantMap &params) const
{
Q_UNUSED(params)
@ -526,7 +591,7 @@ JsonReply *ConfigurationHandler::SetMqttServerConfiguration(const QVariantMap &p
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
}
qCDebug(dcJsonRpc()) << QString("Configure MQTT server %1:%2").arg(config.address.toString()).arg(config.port);
qCDebug(dcJsonRpc()) << QString("Configure MQTT server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setMqttServerConfiguration(config);
@ -639,6 +704,22 @@ void ConfigurationHandler::onWebSocketServerConfigurationRemoved(const QString &
emit WebSocketServerConfigurationRemoved(params);
}
void ConfigurationHandler::onTunnelProxyServerConfigurationChanged(const QString &id)
{
qCDebug(dcJsonRpc()) << "Notification: Tunnel proxy server configuration changed";
QVariantMap params;
params.insert("tunnelProxyServerConfiguration", pack(NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().value(id)));
emit TunnelProxyServerConfigurationChanged(params);
}
void ConfigurationHandler::onTunnelProxyServerConfigurationRemoved(const QString &id)
{
qCDebug(dcJsonRpc()) << "Notification: Tunnel proxy server configuration removed";
QVariantMap params;
params.insert("id", id);
emit TunnelProxyServerConfigurationRemoved(params);
}
void ConfigurationHandler::onMqttServerConfigurationChanged(const QString &id)
{
qCDebug(dcJsonRpc()) << "Notification: MQTT server configuration changed";

View File

@ -60,6 +60,8 @@ public:
Q_INVOKABLE JsonReply *DeleteWebServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetWebSocketServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *DeleteWebSocketServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetTunnelProxyServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *DeleteTunnelProxyServerConfiguration(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *GetMqttServerConfigurations(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *SetMqttServerConfiguration(const QVariantMap &params) const;
@ -79,6 +81,8 @@ signals:
void WebServerConfigurationRemoved(const QVariantMap &params);
void WebSocketServerConfigurationChanged(const QVariantMap &params);
void WebSocketServerConfigurationRemoved(const QVariantMap &params);
void TunnelProxyServerConfigurationChanged(const QVariantMap &params);
void TunnelProxyServerConfigurationRemoved(const QVariantMap &params);
void MqttServerConfigurationChanged(const QVariantMap &params);
void MqttServerConfigurationRemoved(const QVariantMap &params);
@ -95,6 +99,8 @@ private slots:
void onWebServerConfigurationRemoved(const QString &id);
void onWebSocketServerConfigurationChanged(const QString &id);
void onWebSocketServerConfigurationRemoved(const QString &id);
void onTunnelProxyServerConfigurationChanged(const QString &id);
void onTunnelProxyServerConfigurationRemoved(const QString &id);
void onMqttServerConfigurationChanged(const QString &id);
void onMqttServerConfigurationRemoved(const QString &id);
void onMqttPolicyChanged(const QString &clientId);

View File

@ -93,7 +93,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) :
qCWarning(dcConfiguration) << "No TCP server configuration found. Generating default of nymeas://0.0.0.0:2222";
ServerConfiguration config;
config.id = "default";
config.address = QHostAddress("0.0.0.0");
config.address = QHostAddress("0.0.0.0").toString();
config.port = 2222;
config.sslEnabled = true;
config.authenticationEnabled = true;
@ -121,7 +121,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) :
qCWarning(dcConfiguration) << "No Web server configuration found. Generating defaults of http://0.0.0.0:80 and https://0.0.0.0:443";
WebServerConfiguration insecureConfig;
insecureConfig.id = "insecure";
insecureConfig.address = QHostAddress("0.0.0.0");
insecureConfig.address = QHostAddress("0.0.0.0").toString();
insecureConfig.port = 80;
insecureConfig.sslEnabled = false;
insecureConfig.authenticationEnabled = false;
@ -132,7 +132,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) :
WebServerConfiguration secureConfig;
secureConfig.id = "secure";
secureConfig.address = QHostAddress("0.0.0.0");
secureConfig.address = QHostAddress("0.0.0.0").toString();
secureConfig.port = 443;
secureConfig.sslEnabled = true;
secureConfig.authenticationEnabled = false;
@ -162,7 +162,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) :
qCWarning(dcConfiguration) << "No WebSocket server configuration found. Generating default of wss://0.0.0.0:4444";
ServerConfiguration config;
config.id = "default";
config.address = QHostAddress("0.0.0.0");
config.address = QHostAddress("0.0.0.0").toString();
config.port = 4444;
config.sslEnabled = true;
config.authenticationEnabled = true;
@ -190,7 +190,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) :
qCWarning(dcConfiguration) << "No MQTT server configuration found. Generating default of 0.0.0.0:1883";
ServerConfiguration config;
config.id = "default";
config.address = QHostAddress("0.0.0.0");
config.address = QHostAddress("0.0.0.0").toString();
config.port = 1883;
config.sslEnabled = false;
config.authenticationEnabled = true;
@ -212,19 +212,33 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) :
}
// Tunnel Proxy Server
// FIXME: maybe configure default proxy
createDefaults = !settings.childGroups().contains("TunnelProxyServer");
if (settings.childGroups().contains("TunnelProxyServer")) {
settings.beginGroup("TunnelProxyServer");
if (settings.value("disabled").toBool()) {
qCDebug(dcConfiguration) << "Tunnel proxy server disabled by configuration.";
} else if (!settings.childGroups().isEmpty()) {
foreach (const QString &key, settings.childGroups()) {
ServerConfiguration config = readServerConfig("TunnelProxyServer", key);
TunnelProxyServerConfiguration config = readTunnelProxyServerConfig(key);
m_tunnelProxyServerConfigs[config.id] = config;
}
} else {
createDefaults = true;
}
settings.endGroup();
}
if (createDefaults) {
qCWarning(dcConfiguration) << "No Tunnel Proxy server configuration found. Generating default of remoteproxy.nymea.io:2213";
TunnelProxyServerConfiguration config;
config.id = "default";
config.address = "remoteproxy.nymea.io";
config.port = 2213;
config.sslEnabled = true;
config.authenticationEnabled = true;
config.ignoreSslErrors = false;
m_tunnelProxyServerConfigs[config.id] = config;
storeTunnelProxyServerConfig(config);
}
// Write defaults for log settings
settings.beginGroup("Logs");
@ -381,12 +395,12 @@ void NymeaConfiguration::removeWebSocketServerConfiguration(const QString &id)
emit webSocketServerConfigurationRemoved(id);
}
QHash<QString, ServerConfiguration> NymeaConfiguration::tunnelProxyServerConfigurations() const
QHash<QString, TunnelProxyServerConfiguration> NymeaConfiguration::tunnelProxyServerConfigurations() const
{
return m_tunnelProxyServerConfigs;
}
void NymeaConfiguration::setTunnelProxyServerConfiguration(const ServerConfiguration &config)
void NymeaConfiguration::setTunnelProxyServerConfiguration(const TunnelProxyServerConfiguration &config)
{
m_tunnelProxyServerConfigs[config.id] = config;
storeServerConfig("TunnelProxyServer", config);
@ -661,7 +675,7 @@ void NymeaConfiguration::storeServerConfig(const QString &group, const ServerCon
settings.beginGroup(group);
settings.remove("disabled");
settings.beginGroup(config.id);
settings.setValue("address", config.address.toString());
settings.setValue("address", config.address);
settings.setValue("port", config.port);
settings.setValue("sslEnabled", config.sslEnabled);
settings.setValue("authenticationEnabled", config.authenticationEnabled);
@ -676,7 +690,7 @@ ServerConfiguration NymeaConfiguration::readServerConfig(const QString &group, c
settings.beginGroup(group);
settings.beginGroup(id);
config.id = id;
config.address = QHostAddress(settings.value("address").toString());
config.address = settings.value("address").toString();
config.port = settings.value("port").toUInt();
config.sslEnabled = settings.value("sslEnabled", true).toBool();
config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool();
@ -715,7 +729,7 @@ WebServerConfiguration NymeaConfiguration::readWebServerConfig(const QString &id
settings.beginGroup("WebServer");
settings.beginGroup(id);
config.id = id;
config.address = QHostAddress(settings.value("address").toString());
config.address = settings.value("address").toString();
config.port = settings.value("port").toUInt();
config.sslEnabled = settings.value("sslEnabled", true).toBool();
config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool();
@ -726,6 +740,34 @@ WebServerConfiguration NymeaConfiguration::readWebServerConfig(const QString &id
return config;
}
void NymeaConfiguration::storeTunnelProxyServerConfig(const TunnelProxyServerConfiguration &config)
{
storeServerConfig("TunnelProxyServer", config);
NymeaSettings settings(NymeaSettings::SettingsRoleGlobal);
settings.beginGroup("TunnelProxyServer");
settings.beginGroup(config.id);
settings.setValue("ignoreSslErrors", config.ignoreSslErrors);
settings.endGroup();
settings.endGroup();
}
TunnelProxyServerConfiguration NymeaConfiguration::readTunnelProxyServerConfig(const QString &id)
{
TunnelProxyServerConfiguration config;
NymeaSettings settings(NymeaSettings::SettingsRoleGlobal);
settings.beginGroup("TunnelProxyServer");
settings.beginGroup(id);
config.id = id;
config.address = settings.value("address").toString();
config.port = settings.value("port").toUInt();
config.sslEnabled = settings.value("sslEnabled", true).toBool();
config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool();
config.ignoreSslErrors = settings.value("ignoreSslErrors").toBool();
settings.endGroup();
settings.endGroup();
return config;
}
void NymeaConfiguration::storeMqttPolicy(const MqttPolicy &policy)
{
NymeaSettings settings(NymeaSettings::SettingsRoleMqttPolicies);
@ -747,7 +789,7 @@ QDebug operator <<(QDebug debug, const ServerConfiguration &configuration)
{
debug.nospace() << "ServerConfiguration(" << configuration.address;
debug.nospace() << ", " << configuration.id;
debug.nospace() << ", " << QString("%1:%2").arg(configuration.address.toString()).arg(configuration.port);
debug.nospace() << ", " << QString("%1:%2").arg(configuration.address).arg(configuration.port);
debug.nospace() << ") ";
return debug;
}

View File

@ -42,15 +42,14 @@ namespace nymeaserver {
class ServerConfiguration {
Q_GADGET
Q_PROPERTY(QString id MEMBER id)
Q_PROPERTY(QString address READ addressString WRITE setAddress)
Q_PROPERTY(QString address MEMBER address)
Q_PROPERTY(uint port MEMBER port)
Q_PROPERTY(bool sslEnabled MEMBER sslEnabled)
Q_PROPERTY(bool authenticationEnabled MEMBER authenticationEnabled)
public:
QString id;
QHostAddress address;
QString addressString() { return address.toString(); }
void setAddress(const QString &addressString) {address = QHostAddress(addressString); }
QString address;
void setAddress(const QString &address) {this->address = address; }
uint port = 0;
bool sslEnabled = true;
bool authenticationEnabled = true;
@ -75,6 +74,14 @@ public:
bool restServerEnabled = false;
};
class TunnelProxyServerConfiguration: public ServerConfiguration
{
Q_GADGET
Q_PROPERTY(bool ignoreSslErrors MEMBER ignoreSslErrors)
public:
bool ignoreSslErrors = false;
};
class MqttPolicy
{
Q_GADGET
@ -148,8 +155,8 @@ public:
void removeWebSocketServerConfiguration(const QString &id);
// Tunnel proxy server
QHash<QString, ServerConfiguration> tunnelProxyServerConfigurations() const;
void setTunnelProxyServerConfiguration(const ServerConfiguration &config);
QHash<QString, TunnelProxyServerConfiguration> tunnelProxyServerConfigurations() const;
void setTunnelProxyServerConfiguration(const TunnelProxyServerConfiguration &config);
void removeTunnelProxyServerConfiguration(const QString &id);
// MQTT
@ -192,7 +199,7 @@ private:
QHash<QString, ServerConfiguration> m_webSocketServerConfigs;
QHash<QString, ServerConfiguration> m_mqttServerConfigs;
QHash<QString, MqttPolicy> m_mqttPolicies;
QHash<QString, ServerConfiguration> m_tunnelProxyServerConfigs;
QHash<QString, TunnelProxyServerConfiguration> m_tunnelProxyServerConfigs;
void setServerUuid(const QUuid &uuid);
void setWebServerPublicFolder(const QString & path);
@ -204,6 +211,8 @@ private:
void deleteServerConfig(const QString &group, const QString &id);
void storeWebServerConfig(const WebServerConfiguration &config);
WebServerConfiguration readWebServerConfig(const QString &id);
void storeTunnelProxyServerConfig(const TunnelProxyServerConfiguration &config);
TunnelProxyServerConfiguration readTunnelProxyServerConfig(const QString &id);
void storeMqttPolicy(const MqttPolicy &policy);
MqttPolicy readMqttPolicy(const QString &clientId);

View File

@ -70,6 +70,7 @@ namespace nymeaserver {
ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configuration, const QStringList &additionalInterfaces, QObject *parent) :
QObject(parent),
m_platform(platform),
m_nymeaConfiguration(configuration),
m_sslConfiguration(QSslConfiguration())
{
if (!QSslSocket::supportsSsl()) {
@ -120,7 +121,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
QUrl additionalInterface(interfaceString);
ServerConfiguration config;
config.id = "tmp-" + additionalInterface.host();
config.address = QHostAddress(additionalInterface.host());
config.address = additionalInterface.host();
config.port = additionalInterface.port();
TransportInterface *server = nullptr;
QString serverType, serviceType;
@ -166,7 +167,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
m_bluetoothServer->startServer();
}
foreach (const ServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) {
foreach (const TunnelProxyServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) {
TunnelProxyServer *tunnelProxyServer = new TunnelProxyServer(configuration->serverName(), configuration->serverUuid(), config, this);
qCDebug(dcServerManager()) << "Creating tunnel proxy server" << config;
m_tunnelProxyServers.insert(config.id, tunnelProxyServer);
@ -318,7 +319,7 @@ void ServerManager::webServerConfigurationChanged(const QString &id)
server->stopServer();
server->setConfiguration(config);
} else {
qDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address.toString() << config.port << "(SSL:" << config.sslEnabled << ")";
qDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address << config.port << "(SSL:" << config.sslEnabled << ")";
server = new WebServer(config, m_sslConfiguration, this);
m_webServers.insert(config.id, server);
}
@ -369,14 +370,32 @@ void ServerManager::mqttPolicyRemoved(const QString &clientId)
void ServerManager::tunnelProxyServerConfigurationChanged(const QString &id)
{
// FIXME: not dynamic configurable for now
Q_UNUSED(id)
TunnelProxyServer *server = m_tunnelProxyServers.value(id);
TunnelProxyServerConfiguration config = NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().value(id);
if (server) {
qDebug(dcServerManager()) << "Restarting tunnel proxy connection for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled");
unregisterZeroConfService(id, "ws");
server->stopServer();
server->setConfiguration(config);
} else {
qDebug(dcServerManager()) << "Received a tunnel proxy config change event but don't have a WebSocket Server instance for it. Creating new instance.";
server = new TunnelProxyServer(m_nymeaConfiguration->serverName(), m_nymeaConfiguration->serverUuid(), config, this);
m_tunnelProxyServers.insert(server->configuration().id, server);
}
m_jsonServer->registerTransportInterface(server, config.authenticationEnabled);
server->startServer();
}
void ServerManager::tunnelProxyServerConfigurationRemoved(const QString &id)
{
// FIXME: not dynamic configurable for now
Q_UNUSED(id)
if (!m_tunnelProxyServers.contains(id)) {
qWarning(dcServerManager()) << "Received a tunnel proxy config removed event but don't have a tunnel proxy connection for it.";
return;
}
TunnelProxyServer *server = m_tunnelProxyServers.take(id);
m_jsonServer->unregisterTransportInterface(server);
server->stopServer();
server->deleteLater();
}
void ServerManager::cloudEnabledChanged(bool enabled)
@ -402,7 +421,7 @@ bool ServerManager::registerZeroConfService(const ServerConfiguration &configura
txt.insert("name", NymeaCore::instance()->configuration()->serverName());
txt.insert("sslEnabled", configuration.sslEnabled ? "true" : "false");
QString name = "nymea-" + serverType + "-" + configuration.id;
if (!m_platform->zeroConfController()->servicePublisher()->registerService(name, configuration.address, static_cast<quint16>(configuration.port), serviceType, txt)) {
if (!m_platform->zeroConfController()->servicePublisher()->registerService(name, QHostAddress(configuration.address), static_cast<quint16>(configuration.port), serviceType, txt)) {
qCWarning(dcServerManager()) << "Could not register ZeroConf service for" << configuration;
return false;
}

View File

@ -69,6 +69,10 @@ public:
MqttBroker *mqttBroker() const;
public slots:
void setServerName(const QString &serverName);
private slots:
void tcpServerConfigurationChanged(const QString &id);
void tcpServerConfigurationRemoved(const QString &id);
@ -89,8 +93,11 @@ private:
bool registerZeroConfService(const ServerConfiguration &configuration, const QString &serverType, const QString &serviceType);
void unregisterZeroConfService(const QString &configId, const QString &serverType);
bool loadCertificate(const QString &certificateKeyFileName, const QString &certificateFileName);
private:
Platform *m_platform = nullptr;
NymeaConfiguration *m_nymeaConfiguration = nullptr;
// Interfaces
JsonRPCServerImplementation *m_jsonServer;
@ -108,12 +115,6 @@ private:
QSslConfiguration m_sslConfiguration;
QSslKey m_certificateKey;
QSslCertificate m_certificate;
bool loadCertificate(const QString &certificateKeyFileName, const QString &certificateFileName);
public slots:
void setServerName(const QString &serverName);
};
}

View File

@ -140,7 +140,7 @@ MqttBroker::~MqttBroker()
bool MqttBroker::startServer(const ServerConfiguration &config, const QSslConfiguration &sslConfiguration)
{
int serverAddressId = m_server->listen(config.address, config.port, config.sslEnabled ? sslConfiguration : QSslConfiguration());
int serverAddressId = m_server->listen(QHostAddress(config.address), config.port, config.sslEnabled ? sslConfiguration : QSslConfiguration());
if (serverAddressId == -1) {
qCWarning(dcMqtt) << "Error starting MQTT server on port" << config.port;
return false;

View File

@ -101,7 +101,7 @@ TcpServer::~TcpServer()
/*! Returns the URL of this server. */
QUrl TcpServer::serverUrl() const
{
return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "nymeas" : "nymea")).arg(configuration().address.toString()).arg(configuration().port));
return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "nymeas" : "nymea")).arg(configuration().address).arg(configuration().port));
}
/*! Sending \a data to a list of \a clients.*/
@ -177,8 +177,8 @@ void TcpServer::setServerName(const QString &serverName)
bool TcpServer::startServer()
{
m_server = new SslServer(configuration().sslEnabled, m_sslConfig);
if(!m_server->listen(configuration().address, static_cast<quint16>(configuration().port))) {
qCWarning(dcTcpServer()) << "Tcp server error: can not listen on" << configuration().address.toString() << configuration().port;
if(!m_server->listen(QHostAddress(configuration().address), static_cast<quint16>(configuration().port))) {
qCWarning(dcTcpServer()) << "Tcp server error: can not listen on" << configuration().address << configuration().port;
delete m_server;
m_server = nullptr;
return false;

View File

@ -35,21 +35,20 @@ NYMEA_LOGGING_CATEGORY(dcTunnelProxyServer, "TunnelProxyServer")
namespace nymeaserver {
TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const ServerConfiguration &configuration, QObject *parent) :
TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const TunnelProxyServerConfiguration &configuration, QObject *parent) :
TransportInterface(configuration, parent),
m_config(configuration),
m_serverName(serverName),
m_serverUuid(serverUuid)
{
// Note: the authentication must always be enabled on the tunnel proxy server
if (!configuration.authenticationEnabled) {
qCWarning(dcTunnelProxyServer()) << "=============================================================================================================================";
qCWarning(dcTunnelProxyServer()) << "WARNING! The tunnel proxy server has authentication disabled! This is very dangerous and exposes your system to the internet.";
qCWarning(dcTunnelProxyServer()) << "=============================================================================================================================";
qCWarning(dcTunnelProxyServer()) << "=====================================================================================================================";
qCWarning(dcTunnelProxyServer()) << "WARNING! Tunnel proxy connection has authentication disabled! The system will be publicly accessible on the internet.";
qCWarning(dcTunnelProxyServer()) << "=====================================================================================================================";
}
// Default to ssl
m_serverUrl.setScheme("ssl");
m_serverUrl.setHost(configuration.address.toString());
m_serverUrl.setScheme(configuration.sslEnabled ? "ssl" : "tcp");
m_serverUrl.setHost(configuration.address);
m_serverUrl.setPort(configuration.port);
m_tunnelProxySocketServer = new TunnelProxySocketServer(m_serverUuid, m_serverName, this);
@ -102,7 +101,7 @@ void TunnelProxyServer::setServerName(const QString &serverName)
bool TunnelProxyServer::startServer()
{
qCDebug(dcTunnelProxyServer()) << "Connecting to tunnel proxy server at:" << m_serverUrl;
m_tunnelProxySocketServer->startServer(m_serverUrl);
return true;
}
@ -144,7 +143,12 @@ void TunnelProxyServer::onSslErrors(const QList<QSslError> &errors)
{
qCDebug(dcTunnelProxyServer()) << "Remote proxy connection SSL errors occurred" << errors;
// FIXME: make this configurable
m_tunnelProxySocketServer->ignoreSslErrors();
if (m_config.ignoreSslErrors) {
qCWarning(dcTunnelProxyServer()) << "Ingoring SSL errors on tunnel proxy connection:" << errors;
m_tunnelProxySocketServer->ignoreSslErrors();
} else {
qCWarning(dcTunnelProxyServer()) << "The remote proxy connection failed due to SSL errors:" << errors;
}
}
void TunnelProxyServer::onClientConnected(TunnelProxySocket *tunnelProxySocket)

View File

@ -47,7 +47,7 @@ class TunnelProxyServer : public TransportInterface
{
Q_OBJECT
public:
explicit TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const ServerConfiguration &configuration, QObject *parent = nullptr);
explicit TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const TunnelProxyServerConfiguration &configuration, QObject *parent = nullptr);
~TunnelProxyServer() override;
void sendData(const QUuid &clientId, const QByteArray &data) override;
@ -74,6 +74,7 @@ private slots:
void onClientDisconnected(TunnelProxySocket *tunnelProxySocket);
private:
TunnelProxyServerConfiguration m_config;
TunnelProxySocketServer *m_tunnelProxySocketServer = nullptr;
QString m_serverName;
QUuid m_serverUuid;

View File

@ -129,7 +129,7 @@ WebServer::~WebServer()
/*! Returns the server URL of this WebServer. */
QUrl WebServer::serverUrl() const
{
return QUrl(QString("%1://%2:%3").arg((m_configuration.sslEnabled ? "https" : "http")).arg(m_configuration.address.toString()).arg(m_configuration.port));
return QUrl(QString("%1://%2:%3").arg((m_configuration.sslEnabled ? "https" : "http")).arg(m_configuration.address).arg(m_configuration.port));
}
/*! Send the given \a reply map to the corresponding client.
@ -561,7 +561,7 @@ void WebServer::setServerName(const QString &serverName)
/*! Returns true if this \l{WebServer} started successfully. */
bool WebServer::startServer()
{
if (!listen(m_configuration.address, static_cast<quint16>(m_configuration.port))) {
if (!listen(QHostAddress(m_configuration.address), static_cast<quint16>(m_configuration.port))) {
qCWarning(dcWebServer()) << "Webserver could not listen on" << serverUrl().toString() << errorString();
m_enabled = false;
return false;
@ -712,7 +712,7 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address)
int counter = 1;
int sslCounter = 1;
foreach (const ServerConfiguration &config, NymeaCore::instance()->configuration()->webSocketServerConfigurations()) {
if (config.address == QHostAddress("0.0.0.0") || config.address == address) {
if (QHostAddress(config.address) == QHostAddress("0.0.0.0") || QHostAddress(config.address) == address) {
writer.writeStartElement("service");
writer.writeTextElement("serviceType", QString("urn:nymea.io:service:%1:%2").arg(config.sslEnabled ? "wss" : "ws").arg(config.sslEnabled ? sslCounter : counter));
writer.writeTextElement("serviceId", QString("urn:nymea.io:serviceId:%1:%2").arg(config.sslEnabled ? "wss" : "ws").arg(config.sslEnabled ? sslCounter++ : counter++));
@ -725,7 +725,7 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address)
counter = 1;
sslCounter = 1;
foreach (const ServerConfiguration &config, NymeaCore::instance()->configuration()->tcpServerConfigurations()) {
if (config.address == QHostAddress("0.0.0.0") || config.address == address) {
if (QHostAddress(config.address) == QHostAddress("0.0.0.0") || QHostAddress(config.address) == address) {
writer.writeStartElement("service");
writer.writeTextElement("serviceType", QString("urn:nymea.io:service:%1:%2").arg(config.sslEnabled ? "nymeas" : "nymea").arg(config.sslEnabled ? sslCounter : counter));
writer.writeTextElement("serviceId", QString("urn:nymea.io:serviceId:%1:%2").arg(config.sslEnabled ? "nymeas" : "nymea").arg(config.sslEnabled ? sslCounter++ : counter++));

View File

@ -87,7 +87,7 @@ WebSocketServer::~WebSocketServer()
/*! Returns the url of this server. */
QUrl WebSocketServer::serverUrl() const
{
return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "wss" : "ws")).arg(configuration().address.toString()).arg(configuration().port));
return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "wss" : "ws")).arg(configuration().address).arg(configuration().port));
}
/*! Send the given \a data map to the client with the given \a clientId.
@ -219,7 +219,7 @@ bool WebSocketServer::startServer()
connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected);
connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onServerError);
if (!m_server->listen(configuration().address, static_cast<quint16>(configuration().port))) {
if (!m_server->listen(QHostAddress(configuration().address), static_cast<quint16>(configuration().port))) {
qCWarning(dcWebSocketServer()) << "Error listening on" << serverUrl().toString();
return false;
}

View File

@ -471,6 +471,16 @@
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.DeleteTunnelProxyServerConfiguration": {
"description": "Delete a Tunnel Proxy Server interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.",
"params": {
"id": "String"
},
"permissionScope": "PermissionScopeAdmin",
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.DeleteWebServerConfiguration": {
"description": "Delete a WebServer interface of the server.",
"params": {
@ -507,7 +517,7 @@
"description": "Get all configuration parameters of the server.",
"params": {
},
"permissionScope": "PermissionScopeAdmin",
"permissionScope": "PermissionScopeNone",
"returns": {
"basicConfiguration": {
"d:language": "String",
@ -523,6 +533,9 @@
"tcpServerConfigurations": [
"$ref:ServerConfiguration"
],
"tunnelProxyServerConfigurations": [
"$ref:TunnelProxyServerConfiguration"
],
"webServerConfigurations": [
"$ref:WebServerConfiguration"
],
@ -647,6 +660,16 @@
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.SetTunnelProxyServerConfiguration": {
"description": "Configure a Tunnel Proxy Server interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.",
"params": {
"configuration": "$ref:TunnelProxyServerConfiguration"
},
"permissionScope": "PermissionScopeAdmin",
"returns": {
"configurationError": "$ref:ConfigurationError"
}
},
"Configuration.SetWebServerConfiguration": {
"description": "Configure a WebServer interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added.",
"params": {
@ -2043,6 +2066,18 @@
"id": "String"
}
},
"Configuration.TunnelProxyServerConfigurationChanged": {
"description": "Emitted whenever the tunnel proxy server configuration changes.",
"params": {
"tunnelProxyServerConfiguration": "$ref:TunnelProxyServerConfiguration"
}
},
"Configuration.TunnelProxyServerConfigurationRemoved": {
"description": "Emitted whenever a tunnel proxy server configuration is removed.",
"params": {
"id": "String"
}
},
"Configuration.WebServerConfigurationChanged": {
"description": "Emitted whenever the web server configuration changes.",
"params": {
@ -2830,6 +2865,14 @@
"TokenInfoList": [
"$ref:TokenInfo"
],
"TunnelProxyServerConfiguration": {
"address": "String",
"authenticationEnabled": "Bool",
"id": "String",
"ignoreSslErrors": "Bool",
"port": "Uint",
"sslEnabled": "Bool"
},
"UserInfo": {
"r:displayName": "String",
"r:email": "String",

View File

@ -278,7 +278,7 @@ void TestConfigurations::testDebugServerConfiguration()
qCDebug(dcTests()) << "TestWebserver starting";
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
if (config.port == 3333 && (QHostAddress(config.address) == QHostAddress("127.0.0.1") || QHostAddress(config.address) == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
@ -287,7 +287,7 @@ void TestConfigurations::testDebugServerConfiguration()
qCDebug(dcTests) << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.id = "Testwebserver for debug server interface";
config.address = QHostAddress("127.0.0.1");
config.address = "127.0.0.1";
config.port = 3333;
config.sslEnabled = true;
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);

View File

@ -61,8 +61,7 @@ private slots:
void TestMqttBroker::initTestCase()
{
NymeaTestBase::initTestCase();
QLoggingCategory::setFilterRules("*.debug=false\nnymea.mqtt*.debug=true\nMqtt.debug=true\nJsonRpc.debug=true");
NymeaTestBase::initTestCase("*.debug=false\nnymea.mqtt*.debug=true\nMqtt.debug=true\nJsonRpc.debug=true");
}
void TestMqttBroker::testServerConfigurationAPI()

View File

@ -79,7 +79,7 @@ void TestWebserver::initTestCase()
qDebug() << "TestWebserver starting";
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
if (config.port == 3333 && (QHostAddress(config.address) == QHostAddress("127.0.0.1") || QHostAddress(config.address) == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
@ -87,7 +87,7 @@ void TestWebserver::initTestCase()
qDebug() << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.address = QHostAddress("127.0.0.1");
config.address = "127.0.0.1";
config.port = 3333;
config.sslEnabled = true;
config.restServerEnabled = true;
@ -215,7 +215,7 @@ void TestWebserver::checkAllowedMethodCall()
QFETCH(int, expectedStatusCode);
QNetworkAccessManager nam;
connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply* reply, const QList<QSslError> &) {
connect(&nam, &QNetworkAccessManager::sslErrors, [](QNetworkReply* reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*)));

View File

@ -72,7 +72,7 @@ void TestWebSocketServer::initTestCase()
ServerConfiguration config;
foreach (const ServerConfiguration &c, NymeaCore::instance()->configuration()->webSocketServerConfigurations()) {
if (c.port == 4444 && (c.address == QHostAddress("127.0.0.1") || c.address == QHostAddress("0.0.0.0"))) {
if (c.port == 4444 && (QHostAddress(c.address) == QHostAddress("127.0.0.1") || QHostAddress(c.address) == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a websocketserver listening on 127.0.0.1:4444";
config = c;
break;
@ -80,7 +80,7 @@ void TestWebSocketServer::initTestCase()
}
qDebug() << "Creating new websocketserver instance on 127.0.0.1:4444";
config.address = QHostAddress("127.0.0.1");
config.address = "127.0.0.1";
config.port = 4444;
config.sslEnabled = true;
config.authenticationEnabled = true;