Disable insecure interfaces if configured using env

This commit is contained in:
Simon Stürz 2025-10-13 13:16:16 +02:00
parent a74f747df8
commit e8244e9313
6 changed files with 78 additions and 17 deletions

View File

@ -449,9 +449,9 @@ JsonReply *ConfigurationHandler::SetLocation(const QVariantMap &params) const
JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &params) const JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &params) const
{ {
ServerConfiguration config = unpack<ServerConfiguration>(params.value("configuration").toMap()); ServerConfiguration config = unpack<ServerConfiguration>(params.value("configuration").toMap());
if (config.id.isEmpty()) { if (config.id.isEmpty())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull()) if (config.address.isNull())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress));
@ -460,8 +460,17 @@ JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &pa
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
} }
qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(config.address).arg(config.port); // To be compliant with the EN18031 we have to make sure the user cannot configure an insecure interface to the server.
if (qEnvironmentVariable("NYMEA_INSECURE_INTERFACES_DISABLED", "0") != "0") {
bool isLocalhost = config.address == "localhost" || config.address == "127.0.0.1";
bool isSecured = config.sslEnabled && config.authenticationEnabled;
if (!isLocalhost && !isSecured) {
qCWarning(dcJsonRpc()) << "Cannot add insecure TCP server configuration" << config << "because insecure interfaces to the core are explicit disabled.";
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorUnsupported));
}
}
qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setTcpServerConfiguration(config); NymeaCore::instance()->configuration()->setTcpServerConfiguration(config);
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError));
} }
@ -510,9 +519,9 @@ JsonReply *ConfigurationHandler::DeleteWebServerConfiguration(const QVariantMap
JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantMap &params) const JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantMap &params) const
{ {
ServerConfiguration config = unpack<ServerConfiguration>(params.value("configuration").toMap()); ServerConfiguration config = unpack<ServerConfiguration>(params.value("configuration").toMap());
if (config.id.isEmpty()) { if (config.id.isEmpty())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull()) if (config.address.isNull())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress));
@ -521,6 +530,16 @@ JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantM
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
} }
// To be compliant with the EN18031 we have to make sure the user cannot configure an insecure interface to the server.
if (qEnvironmentVariable("NYMEA_INSECURE_INTERFACES_DISABLED", "0") != "0") {
bool isLocalhost = config.address == "localhost" || config.address == "127.0.0.1";
bool isSecured = config.sslEnabled && config.authenticationEnabled;
if (!isLocalhost && !isSecured) {
qCWarning(dcJsonRpc()) << "Cannot add insecure WebSocket server configuration" << config << "because insecure interfaces to the core are explicit disabled.";
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorUnsupported));
}
}
qCDebug(dcJsonRpc()) << QString("Configuring web socket server %1:%2").arg(config.address).arg(config.port); qCDebug(dcJsonRpc()) << QString("Configuring web socket server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setWebSocketServerConfiguration(config); NymeaCore::instance()->configuration()->setWebSocketServerConfiguration(config);
@ -541,9 +560,9 @@ JsonReply *ConfigurationHandler::DeleteWebSocketServerConfiguration(const QVaria
JsonReply *ConfigurationHandler::SetTunnelProxyServerConfiguration(const QVariantMap &params) const JsonReply *ConfigurationHandler::SetTunnelProxyServerConfiguration(const QVariantMap &params) const
{ {
TunnelProxyServerConfiguration config = unpack<TunnelProxyServerConfiguration>(params.value("configuration").toMap()); TunnelProxyServerConfiguration config = unpack<TunnelProxyServerConfiguration>(params.value("configuration").toMap());
if (config.id.isEmpty()) { if (config.id.isEmpty())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId));
}
if (config.address.isNull()) if (config.address.isNull())
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress));
@ -552,6 +571,14 @@ JsonReply *ConfigurationHandler::SetTunnelProxyServerConfiguration(const QVarian
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort));
} }
// To be compliant with the EN18031 we have to make sure the user cannot configure an insecure interface to the server.
if (qEnvironmentVariable("NYMEA_INSECURE_INTERFACES_DISABLED", "0") != "0") {
if (!config.sslEnabled || !config.authenticationEnabled || config.ignoreSslErrors) {
qCWarning(dcJsonRpc()) << "Cannot add insecure tunnelproxy server configuration" << config << "because insecure interfaces to the core are explicit disabled.";
return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorUnsupported));
}
}
qCDebug(dcJsonRpc()) << QString("Configuring tunnel proxy server %1:%2").arg(config.address).arg(config.port); qCDebug(dcJsonRpc()) << QString("Configuring tunnel proxy server %1:%2").arg(config.address).arg(config.port);
NymeaCore::instance()->configuration()->setTunnelProxyServerConfiguration(config); NymeaCore::instance()->configuration()->setTunnelProxyServerConfiguration(config);
@ -758,10 +785,10 @@ QVariantMap ConfigurationHandler::packBasicConfiguration()
basicConfiguration.insert("timeZone", QTimeZone::systemTimeZoneId()); basicConfiguration.insert("timeZone", QTimeZone::systemTimeZoneId());
basicConfiguration.insert("language", NymeaCore::instance()->configuration()->locale().name()); basicConfiguration.insert("language", NymeaCore::instance()->configuration()->locale().name());
basicConfiguration.insert("location", QVariantMap{ basicConfiguration.insert("location", QVariantMap{
{"latitude", NymeaCore::instance()->configuration()->locationLatitude()}, {"latitude", NymeaCore::instance()->configuration()->locationLatitude()},
{"longitude", NymeaCore::instance()->configuration()->locationLongitude()}, {"longitude", NymeaCore::instance()->configuration()->locationLongitude()},
{"name", NymeaCore::instance()->configuration()->locationName()} {"name", NymeaCore::instance()->configuration()->locationName()}
}); });
basicConfiguration.insert("debugServerEnabled", NymeaCore::instance()->configuration()->debugServerEnabled()); basicConfiguration.insert("debugServerEnabled", NymeaCore::instance()->configuration()->debugServerEnabled());
return basicConfiguration; return basicConfiguration;
} }

View File

@ -30,6 +30,7 @@
#include "tagshandler.h" #include "tagshandler.h"
#include "loggingcategories.h"
#include "nymeacore.h" #include "nymeacore.h"
#include "tagging/tagsstorage.h" #include "tagging/tagsstorage.h"

View File

@ -325,8 +325,12 @@ QLocale NymeaConfiguration::locale() const
void NymeaConfiguration::setLocale(const QLocale &locale) void NymeaConfiguration::setLocale(const QLocale &locale)
{ {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qCDebug(dcConfiguration()) << "Configuration: Set system locale:" << locale.name() << locale.nativeCountryName() << locale.nativeLanguageName(); qCDebug(dcConfiguration()) << "Configuration: Set system locale:" << locale.name() << locale.nativeCountryName() << locale.nativeLanguageName();
#else
qCDebug(dcConfiguration()) << "Configuration: Set system locale:" << locale.name() << locale.nativeTerritoryName() << locale.nativeLanguageName();
#endif
NymeaSettings settings(NymeaSettings::SettingsRoleGlobal); NymeaSettings settings(NymeaSettings::SettingsRoleGlobal);
settings.beginGroup("nymead"); settings.beginGroup("nymead");
if (settings.value("language").toString() == locale.name()) { if (settings.value("language").toString() == locale.name()) {

View File

@ -129,7 +129,8 @@ public:
ConfigurationErrorInvalidPort, ConfigurationErrorInvalidPort,
ConfigurationErrorInvalidHostAddress, ConfigurationErrorInvalidHostAddress,
ConfigurationErrorBluetoothHardwareNotAvailable, ConfigurationErrorBluetoothHardwareNotAvailable,
ConfigurationErrorInvalidCertificate ConfigurationErrorInvalidCertificate,
ConfigurationErrorUnsupported
}; };
Q_ENUM(ConfigurationError) Q_ENUM(ConfigurationError)

View File

@ -49,6 +49,7 @@
#include "platform/platform.h" #include "platform/platform.h"
#include "platform/platformzeroconfcontroller.h" #include "platform/platformzeroconfcontroller.h"
#include "version.h" #include "version.h"
#include "loggingcategories.h"
#include "jsonrpc/jsonrpcserverimplementation.h" #include "jsonrpc/jsonrpcserverimplementation.h"
#include "servers/mocktcpserver.h" #include "servers/mocktcpserver.h"
@ -73,6 +74,12 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
m_nymeaConfiguration(configuration), m_nymeaConfiguration(configuration),
m_sslConfiguration(QSslConfiguration()) m_sslConfiguration(QSslConfiguration())
{ {
// To be compliant with the EN18031 we have to make sure the user cannot configure an insecure interface to the server.
if (qEnvironmentVariable("NYMEA_INSECURE_INTERFACES_DISABLED", "0") != "0") {
qCInfo(dcServerManager()) << "Insecure interfaces to the core are explicit disabled. Not starting any unauthenticated or unencrypted interfaces.";
m_disableInsecureInterfaces = true;
}
if (!QSslSocket::supportsSsl()) { if (!QSslSocket::supportsSsl()) {
qCWarning(dcServerManager()) << "SSL is not supported/installed on this platform."; qCWarning(dcServerManager()) << "SSL is not supported/installed on this platform.";
} else { } else {
@ -102,8 +109,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
} }
} }
if (certsLoaded) { if (certsLoaded) {
// Update this to 1.3 when minimum required Qt is 5.12 (and known client apps can deal with it) m_sslConfiguration.setProtocol(QSsl::TlsV1_3OrLater);
m_sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater);
m_sslConfiguration.setPrivateKey(m_certificateKey); m_sslConfiguration.setPrivateKey(m_certificateKey);
m_sslConfiguration.setLocalCertificate(m_certificate); m_sslConfiguration.setLocalCertificate(m_certificate);
} }
@ -149,6 +155,11 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
} }
foreach (const ServerConfiguration &config, configuration->tcpServerConfigurations()) { foreach (const ServerConfiguration &config, configuration->tcpServerConfigurations()) {
if (m_disableInsecureInterfaces && (!config.sslEnabled || !config.authenticationEnabled)) {
qCWarning(dcServerManager()) << "Loaded insecure TCP server configuration" << config << "but insecure interfaces to the core are explicit disabled. This interface will not be started.";
continue;
}
TcpServer *tcpServer = new TcpServer(config, m_sslConfiguration, this); TcpServer *tcpServer = new TcpServer(config, m_sslConfiguration, this);
m_jsonServer->registerTransportInterface(tcpServer); m_jsonServer->registerTransportInterface(tcpServer);
m_tcpServers.insert(config.id, tcpServer); m_tcpServers.insert(config.id, tcpServer);
@ -158,6 +169,11 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
} }
foreach (const ServerConfiguration &config, configuration->webSocketServerConfigurations()) { foreach (const ServerConfiguration &config, configuration->webSocketServerConfigurations()) {
if (m_disableInsecureInterfaces && (!config.sslEnabled || !config.authenticationEnabled)) {
qCWarning(dcServerManager()) << "Loaded insecure WebSocket server configuration" << config << "but insecure interfaces to the core are explicit disabled. This interface will not be started.";
continue;
}
WebSocketServer *webSocketServer = new WebSocketServer(config, m_sslConfiguration, this); WebSocketServer *webSocketServer = new WebSocketServer(config, m_sslConfiguration, this);
m_jsonServer->registerTransportInterface(webSocketServer); m_jsonServer->registerTransportInterface(webSocketServer);
m_webSocketServers.insert(config.id, webSocketServer); m_webSocketServers.insert(config.id, webSocketServer);
@ -170,10 +186,19 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
m_bluetoothServer = new BluetoothServer(this); m_bluetoothServer = new BluetoothServer(this);
m_jsonServer->registerTransportInterface(m_bluetoothServer); m_jsonServer->registerTransportInterface(m_bluetoothServer);
if (configuration->bluetoothServerEnabled()) { if (configuration->bluetoothServerEnabled()) {
m_bluetoothServer->startServer(); if (m_disableInsecureInterfaces) {
qCWarning(dcServerManager()) << "The bluetooth RFCOM server is enabled, but insecure server interfaces have been disabled explicitly. Not starting the bluetooth server.";
} else {
m_bluetoothServer->startServer();
}
} }
foreach (const TunnelProxyServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) { foreach (const TunnelProxyServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) {
if (m_disableInsecureInterfaces && (!config.sslEnabled || !config.authenticationEnabled || config.ignoreSslErrors)) {
qCWarning(dcServerManager()) << "Loaded insecure tunnelproxy server configuration" << config << "but insecure interfaces to the core are explicit disabled. This interface will not be started.";
continue;
}
TunnelProxyServer *tunnelProxyServer = new TunnelProxyServer(configuration->serverName(), configuration->serverUuid(), config, this); TunnelProxyServer *tunnelProxyServer = new TunnelProxyServer(configuration->serverName(), configuration->serverUuid(), config, this);
qCDebug(dcServerManager()) << "Creating tunnel proxy server using" << config; qCDebug(dcServerManager()) << "Creating tunnel proxy server using" << config;
m_tunnelProxyServers.insert(config.id, tunnelProxyServer); m_tunnelProxyServers.insert(config.id, tunnelProxyServer);
@ -209,14 +234,18 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
connect(configuration, &NymeaConfiguration::tcpServerConfigurationChanged, this, &ServerManager::tcpServerConfigurationChanged); connect(configuration, &NymeaConfiguration::tcpServerConfigurationChanged, this, &ServerManager::tcpServerConfigurationChanged);
connect(configuration, &NymeaConfiguration::tcpServerConfigurationRemoved, this, &ServerManager::tcpServerConfigurationRemoved); connect(configuration, &NymeaConfiguration::tcpServerConfigurationRemoved, this, &ServerManager::tcpServerConfigurationRemoved);
connect(configuration, &NymeaConfiguration::webSocketServerConfigurationChanged, this, &ServerManager::webSocketServerConfigurationChanged); connect(configuration, &NymeaConfiguration::webSocketServerConfigurationChanged, this, &ServerManager::webSocketServerConfigurationChanged);
connect(configuration, &NymeaConfiguration::webSocketServerConfigurationRemoved, this, &ServerManager::webSocketServerConfigurationRemoved); connect(configuration, &NymeaConfiguration::webSocketServerConfigurationRemoved, this, &ServerManager::webSocketServerConfigurationRemoved);
connect(configuration, &NymeaConfiguration::webServerConfigurationChanged, this, &ServerManager::webServerConfigurationChanged); connect(configuration, &NymeaConfiguration::webServerConfigurationChanged, this, &ServerManager::webServerConfigurationChanged);
connect(configuration, &NymeaConfiguration::webServerConfigurationRemoved, this, &ServerManager::webServerConfigurationRemoved); connect(configuration, &NymeaConfiguration::webServerConfigurationRemoved, this, &ServerManager::webServerConfigurationRemoved);
connect(configuration, &NymeaConfiguration::mqttServerConfigurationChanged, this, &ServerManager::mqttServerConfigurationChanged); connect(configuration, &NymeaConfiguration::mqttServerConfigurationChanged, this, &ServerManager::mqttServerConfigurationChanged);
connect(configuration, &NymeaConfiguration::mqttServerConfigurationRemoved, this, &ServerManager::mqttServerConfigurationRemoved); connect(configuration, &NymeaConfiguration::mqttServerConfigurationRemoved, this, &ServerManager::mqttServerConfigurationRemoved);
connect(configuration, &NymeaConfiguration::mqttPolicyChanged, this, &ServerManager::mqttPolicyChanged); connect(configuration, &NymeaConfiguration::mqttPolicyChanged, this, &ServerManager::mqttPolicyChanged);
connect(configuration, &NymeaConfiguration::mqttPolicyRemoved, this, &ServerManager::mqttPolicyRemoved); connect(configuration, &NymeaConfiguration::mqttPolicyRemoved, this, &ServerManager::mqttPolicyRemoved);
connect(configuration, &NymeaConfiguration::tunnelProxyServerConfigurationChanged, this, &ServerManager::tunnelProxyServerConfigurationChanged); connect(configuration, &NymeaConfiguration::tunnelProxyServerConfigurationChanged, this, &ServerManager::tunnelProxyServerConfigurationChanged);
connect(configuration, &NymeaConfiguration::tunnelProxyServerConfigurationRemoved, this, &ServerManager::tunnelProxyServerConfigurationRemoved); connect(configuration, &NymeaConfiguration::tunnelProxyServerConfigurationRemoved, this, &ServerManager::tunnelProxyServerConfigurationRemoved);
} }

View File

@ -33,7 +33,6 @@
#include <QObject> #include <QObject>
#include "loggingcategories.h"
#include "nymeaconfiguration.h" #include "nymeaconfiguration.h"
#include <QSslConfiguration> #include <QSslConfiguration>
@ -72,7 +71,6 @@ public:
public slots: public slots:
void setServerName(const QString &serverName); void setServerName(const QString &serverName);
private slots: private slots:
void tcpServerConfigurationChanged(const QString &id); void tcpServerConfigurationChanged(const QString &id);
void tcpServerConfigurationRemoved(const QString &id); void tcpServerConfigurationRemoved(const QString &id);
@ -95,6 +93,7 @@ private:
private: private:
Platform *m_platform = nullptr; Platform *m_platform = nullptr;
bool m_disableInsecureInterfaces = false;
NymeaConfiguration *m_nymeaConfiguration = nullptr; NymeaConfiguration *m_nymeaConfiguration = nullptr;
// Interfaces // Interfaces