From 56c0c5b3c3de5aa00db46ebe069abc2a5f53b93d Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 10 May 2021 18:55:08 +0200 Subject: [PATCH] Rework reverse ssh plugin --- debian/control | 6 +- ....in => nymea-plugin-reversessh.install.in} | 2 +- nymea-plugins.pro | 2 +- remotessh/README.md | 19 -- remotessh/integrationpluginremotessh.cpp | 217 ------------------ remotessh/remotessh.pro | 10 - reversessh/README.md | 51 ++++ reversessh/integrationpluginreversessh.cpp | 196 ++++++++++++++++ .../integrationpluginreversessh.h | 32 +-- .../integrationpluginreversessh.json | 74 +++--- {remotessh => reversessh}/meta.json | 0 reversessh/reversessh.pro | 10 + .../reversessh.svg | 0 ...5d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts | 77 +++---- 14 files changed, 331 insertions(+), 365 deletions(-) rename debian/{nymea-plugin-remotessh.install.in => nymea-plugin-reversessh.install.in} (77%) delete mode 100644 remotessh/README.md delete mode 100644 remotessh/integrationpluginremotessh.cpp delete mode 100644 remotessh/remotessh.pro create mode 100644 reversessh/README.md create mode 100644 reversessh/integrationpluginreversessh.cpp rename remotessh/integrationpluginremotessh.h => reversessh/integrationpluginreversessh.h (70%) rename remotessh/integrationpluginremotessh.json => reversessh/integrationpluginreversessh.json (50%) rename {remotessh => reversessh}/meta.json (100%) create mode 100644 reversessh/reversessh.pro rename remotessh/remotessh.svg => reversessh/reversessh.svg (100%) rename {remotessh => reversessh}/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts (52%) diff --git a/debian/control b/debian/control index ea89a128..34fb91cd 100644 --- a/debian/control +++ b/debian/control @@ -986,11 +986,15 @@ Description: nymea.io plugin for keba This package will install the nymea.io plugin for keba -Package: nymea-plugin-remotessh +Package: nymea-plugin-reversessh Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, + sshpass, nymea-plugins-translations, +Recommends: openssh-server +Replaces: nymea-plugin-remotessh +Conflicts: nymea-plugin-remotessh Description: nymea.io plugin to configure remotessh for your nymea:core The nymea daemon is a plugin based IoT (Internet of Things) server. The server works like a translator for devices, things and services and diff --git a/debian/nymea-plugin-remotessh.install.in b/debian/nymea-plugin-reversessh.install.in similarity index 77% rename from debian/nymea-plugin-remotessh.install.in rename to debian/nymea-plugin-reversessh.install.in index 294ceb70..b4d37fbd 100644 --- a/debian/nymea-plugin-remotessh.install.in +++ b/debian/nymea-plugin-reversessh.install.in @@ -1 +1 @@ -usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginremotessh.so +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginreversessh.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index e32bd80e..91c50e3b 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -48,7 +48,7 @@ PLUGIN_DIRS = \ shelly \ solarlog \ systemmonitor \ - remotessh \ + reversessh \ senic \ serialportcommander \ simulation \ diff --git a/remotessh/README.md b/remotessh/README.md deleted file mode 100644 index 226ed8d7..00000000 --- a/remotessh/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Remote ssh - -This plugin allowes to to establish a reverse SSH tunnel to the device where nymea is running. This requires a reverse ssh proxy running somewhere accessable from the system. - -## Supported Things - -* Remote SSH - * Reverse SSH connection - * Config target IP-address, local and remote port - * Username and Password login - -## Requirements - -* The package “nymea-plugin-remotessh” must be installed -* Reverse SSH proxy server - -## More - -https://www.howtogeek.com/428413/what-is-reverse-ssh-tunneling-and-how-to-use-it/ diff --git a/remotessh/integrationpluginremotessh.cpp b/remotessh/integrationpluginremotessh.cpp deleted file mode 100644 index 76340441..00000000 --- a/remotessh/integrationpluginremotessh.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "integrationpluginremotessh.h" -#include "plugininfo.h" - -#include -#include - -IntegrationPluginRemoteSsh::IntegrationPluginRemoteSsh() -{ - -} - -void IntegrationPluginRemoteSsh::init() -{ - m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); - connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginRemoteSsh::onPluginTimeout); -} - -void IntegrationPluginRemoteSsh::setupThing(ThingSetupInfo *info) -{ - Thing *thing = info->thing(); - - qCDebug(dcRemoteSsh()) << "Setup" << thing->name() << thing->params(); - - if (thing->thingClassId() == reverseSshThingClassId) { - m_identityFilePath = QString("%1/.ssh/id_rsa_guh").arg(QDir::homePath()); - return info->finish(Thing::ThingErrorNoError); - } -} - -void IntegrationPluginRemoteSsh::executeAction(ThingActionInfo *info) -{ - Thing *thing = info->thing(); - Action action = info->action(); - if (thing->thingClassId() == reverseSshThingClassId ) { - - if (action.actionTypeId() == reverseSshConnectedActionTypeId) { - - if (action.param(reverseSshConnectedActionConnectedParamTypeId).value().toBool() == true) { - QProcess *process = startReverseSSHProcess(thing); - m_reverseSSHProcess.insert(process, thing); - m_startingProcess.insert(process, info); - // in case action call is cancelled, detach result reporting - connect(info, &ThingActionInfo::destroyed, process, [this, process]{ - m_startingProcess.remove(process); - }); - return; - } else { - QProcess *process = m_reverseSSHProcess.key(thing); - - // Check if the application is running... - if (!process) - return info->finish(Thing::ThingErrorNoError); - - if (process->state() == QProcess::NotRunning) - return info->finish(Thing::ThingErrorNoError); - - process->kill(); - m_killingProcess.insert(process, info); - // in case action call is cancelled, detach result reporting - connect(info, &ThingActionInfo::destroyed, process, [this, process]{ - m_killingProcess.remove(process); - }); - return; - } - } - } -} - - -void IntegrationPluginRemoteSsh::thingRemoved(Thing *thing) -{ - if (thing->thingClassId() == reverseSshThingClassId) { - QProcess *process = m_reverseSSHProcess.key(thing); - if (!process) - return; - - m_reverseSSHProcess.remove(process); - if (process->state() != QProcess::NotRunning) { - process->kill(); - } - process->deleteLater(); - } -} - -void IntegrationPluginRemoteSsh::processReadyRead() -{ - QByteArray data = static_cast(sender())->readAll(); - qCWarning(dcRemoteSsh()) << "process read" << data; -} - - -QProcess * IntegrationPluginRemoteSsh::startReverseSSHProcess(Thing *thing) -{ - qCDebug(dcRemoteSsh()) << "Start reverse SSH"; - QProcess *process = new QProcess(this); - process->setProcessChannelMode(QProcess::MergedChannels); - - connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); - connect(process, &QProcess::stateChanged, this, &IntegrationPluginRemoteSsh::processStateChanged); - connect(process, &QProcess::readyRead, this, &IntegrationPluginRemoteSsh::processReadyRead); - - QStringList arguments; - int localPort = thing->paramValue(reverseSshThingLocalPortParamTypeId).toInt(); - int remotePort = thing->paramValue(reverseSshThingRemotePortParamTypeId).toInt(); - QString user = thing->paramValue(reverseSshThingUserParamTypeId).toString(); - QString password = thing->paramValue(reverseSshThingPasswordParamTypeId).toString(); - QString address = thing->paramValue(reverseSshThingAddressParamTypeId).toString(); - - arguments << "-p" << password; - arguments << "ssh" << "-o StrictHostKeyChecking=no" << "-oUserKnownHostsFile=/dev/null"; - arguments << "-TN" << "-R" << QString("%1:localhost:%2").arg(remotePort).arg(localPort) << QString("%1@%2").arg(user, address); - process->start(QStringLiteral("sshpass"), arguments); - qCDebug(dcRemoteSsh()) << "Command:" << process->program() << process->arguments(); - return process; -} - -void IntegrationPluginRemoteSsh::onPluginTimeout() -{ - foreach(QProcess *process, m_reverseSSHProcess.keys()) { - if (process->state() == QProcess::NotRunning) - qCDebug(dcRemoteSsh()) << "SSH Process not running"; - } -} - - -void IntegrationPluginRemoteSsh::processFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - Q_UNUSED(exitCode); - QProcess *process = static_cast(sender()); - - if(exitStatus != QProcess::NormalExit || exitCode != 0) { - qCWarning(dcRemoteSsh()) << "Error:" << process->readAllStandardError(); - } - - if (m_reverseSSHProcess.contains(process)) { - qCDebug(dcRemoteSsh()) << "SSH process finished"; - Thing *thing = m_reverseSSHProcess.value(process); - thing->setStateValue(reverseSshConnectedStateTypeId, false); - m_reverseSSHProcess.remove(process); - - } else if (m_sshKeyGenProcess.contains(process)) { - qCDebug(dcRemoteSsh()) << "SSH Key generation process finished" << process->readAll(); - Thing *thing = m_sshKeyGenProcess.value(process); - QFile file(QString(m_identityFilePath + ".pub")); - if(!file.open(QIODevice::ReadOnly)) - qCWarning(dcRemoteSsh()) << "error" << file.errorString(); - - QTextStream in(&file); - QString sshKey = in.readLine(); - thing->setStateValue(reverseSshSshKeyStateTypeId, sshKey); - process->kill(); - m_sshKeyGenProcess.remove(process); - file.close(); - } -} - - -void IntegrationPluginRemoteSsh::processStateChanged(QProcess::ProcessState state) -{ - QProcess *process = static_cast(sender()); - Thing *thing = m_reverseSSHProcess.value(process); - - switch (state) { - case QProcess::Running: - thing->setStateValue(reverseSshConnectedStateTypeId, true); - if (m_startingProcess.contains(process)) { - m_startingProcess.take(process)->finish(Thing::ThingErrorNoError); - } - break; - - case QProcess::NotRunning: - if (thing) - thing->setStateValue(reverseSshConnectedStateTypeId, false); - - if (m_startingProcess.contains(process)) { - m_startingProcess.take(process)->finish(Thing::ThingErrorInvalidParameter); - } - - if (m_killingProcess.contains(process)) { - m_killingProcess.take(process)->finish(Thing::ThingErrorNoError); - m_reverseSSHProcess.remove(process); - } - break; - default: - break; - } -} diff --git a/remotessh/remotessh.pro b/remotessh/remotessh.pro deleted file mode 100644 index 55234390..00000000 --- a/remotessh/remotessh.pro +++ /dev/null @@ -1,10 +0,0 @@ -include(../plugins.pri) - -TARGET = $$qtLibraryTarget(nymea_integrationpluginremotessh) - -SOURCES += \ - integrationpluginremotessh.cpp \ - -HEADERS += \ - integrationpluginremotessh.h \ - diff --git a/reversessh/README.md b/reversessh/README.md new file mode 100644 index 00000000..a895efc3 --- /dev/null +++ b/reversessh/README.md @@ -0,0 +1,51 @@ +# Reverse ssh + +This plugin allows to establish a reverse SSH tunnel to the device where nymea is running. + +This is useful when maintaining remote nymea setups which may be hidden behind a firewall +and cannot be accessed from the public internet. A user can easily enable SSH access for +a nymea setup by adding a Thing using the app without having to deal with DNS and NAT. + +## Requirements + +In order to establish a reverse SSH tunnel, a SSH server is required to be accessible from +both, the nymea instance and the client log in from. Also a SSH server is required +to run on the system where nymea is running (If using the nymea images, this is already the case). + +## Setup + +### SSH Server setup + +The SSH server can be hosted anywhere, for instance on a vserver somewhere in the internet. +The following settings must be enabled on the SSH server for it to work (assuming openssh): + +* AllowTcpForwarding yes (To allow this sort of forwarding generally) +* GatewayPorts yes (To allow reverse ssh from other hosts than the reverse proxy itself - it would listen to localhost only otherwise) + +Create a user on the SSH server. Note that if sharing the credentials with someone else it +might be advisable to confine the SSH server in a container, however, such a setup is beyond +the scope of this manual. This will assume that both ends are trusted and SSH credentials +for the server can be shared. + +### Nymea setup + +During the thing setup, enter the server connection information for SSH server: + +* SSH server address: The hostname or IP of the SSH server +* SSH server port: The SSH port of the SSH server (22 by default) +* Local SSH server port: The SSH port of the local SSH server running on the nymea system (22 by default) +* Remote port to be opened: This is the port on which the nymea system will be reachable. This can be any port number which isn't in use already, and, unless you intend to log in as root (not advisable) this must be a port higher than 1024 + +In the next step, provide the SSH credentials for the user on the SSH server which has been created before. Once the login succeeds, the thing should become connected. + +### Connecting with an SSH client + +Once the above setup succeeded, the nymea system can be reached via SSH using: + + ssh @ -p + +where `user` is a user on the nymea system, server is the IP or hostname of the SSH server and `remote open port` is the port that has been picked during the thing setup. + +## More + +https://www.howtogeek.com/428413/what-is-reverse-ssh-tunneling-and-how-to-use-it/ diff --git a/reversessh/integrationpluginreversessh.cpp b/reversessh/integrationpluginreversessh.cpp new file mode 100644 index 00000000..a3d7c7db --- /dev/null +++ b/reversessh/integrationpluginreversessh.cpp @@ -0,0 +1,196 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "integrationpluginreversessh.h" +#include "plugininfo.h" + +#include +#include + +IntegrationPluginReverseSsh::IntegrationPluginReverseSsh() +{ + +} + +IntegrationPluginReverseSsh::~IntegrationPluginReverseSsh() +{ + foreach (QProcess *process, m_processes) { + process->terminate(); + } +} + +void IntegrationPluginReverseSsh::startPairing(ThingPairingInfo *info) +{ + info->finish(Thing::ThingErrorNoError, QString(QT_TR_NOOP("Please enter your login credentials for %1.")).arg(info->params().paramValue(reverseSshThingAddressParamTypeId).toString())); +} + +void IntegrationPluginReverseSsh::confirmPairing(ThingPairingInfo *info, const QString &user, const QString &secret) +{ + // Perform a test login on the remote server + QString address = info->params().paramValue(reverseSshThingAddressParamTypeId).toString(); + int remotePort = info->params().paramValue(reverseSshThingRemotePortParamTypeId).toInt(); + + QStringList arguments; + arguments << "-p" << secret << "ssh" << "-o StrictHostKeyChecking=no" << "-oUserKnownHostsFile=/dev/null"; + arguments << QString("%1@%2").arg(user, address) << "-p" << QString::number(remotePort) << "whoami"; + + QProcess *process = new QProcess(this); + process->setProgram("sshpass"); + process->setArguments(arguments); + + arguments.replace(1, "xxxxxx"); + qCInfo(dcReverseSsh()) << "Testing SSH connection:" << process->program() << arguments.join(" "); + + typedef void (QProcess:: *finishedSignal)(int exitCode, QProcess::ExitStatus exitStatus); + connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus){ + process->deleteLater(); + qCDebug(dcReverseSsh()) << "Testing process finished. Exit code:" << exitCode << "Exit status:" << exitStatus; + + switch (exitCode) { + case 0: + pluginStorage()->beginGroup(info->thingId().toString()); + pluginStorage()->setValue("username", user); + pluginStorage()->setValue("password", secret); + pluginStorage()->endGroup(); + qCInfo(dcReverseSsh()) << "Reverse SSH test login successful."; + info->finish(Thing::ThingErrorNoError); + break; + case 5: + qCWarning(dcReverseSsh()) << "Reverse SSH test login failed."; + info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Login error on remote SSH server.")); + break; + default: + qCWarning(dcReverseSsh()) << "Reverse SSH test login unable to connect to SSH server."; + info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Cannot connect to remote SSH server.")); + } + + }); + process->start(); +} + +void IntegrationPluginReverseSsh::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + + + QStringList arguments; + int localPort = thing->paramValue(reverseSshThingLocalPortParamTypeId).toInt(); + int remoteOpenPort = thing->paramValue(reverseSshThingRemoteOpenPortParamTypeId).toInt(); + int remotePort = thing->paramValue(reverseSshThingRemotePortParamTypeId).toInt(); + QString address = thing->paramValue(reverseSshThingAddressParamTypeId).toString(); + + pluginStorage()->beginGroup(thing->id().toString()); + QString user = pluginStorage()->value("username").toString(); + QString password = pluginStorage()->value("password").toString(); + pluginStorage()->endGroup(); + + arguments << "-p" << password << "ssh" << "-o StrictHostKeyChecking=no" << "-oUserKnownHostsFile=/dev/null"; + arguments << "-TN" << "-R" << QString("%1:localhost:%2").arg(remoteOpenPort).arg(localPort) << QString("%1@%2").arg(user, address) << "-p" << QString::number(remotePort); + QProcess *process = new QProcess(thing); + process->setProgram("sshpass"); + process->setArguments(arguments); + process->setProcessChannelMode(QProcess::MergedChannels); + arguments.replace(1, "xxxxxx"); + qCDebug(dcReverseSsh()) << "Reverse SSH command:" << process->program() << arguments; + + m_processes.insert(info->thing(), process); + + connect(process, &QProcess::stateChanged, thing, [=](QProcess::ProcessState newState){ + switch (newState) { + case QProcess::Starting: + qCDebug(dcReverseSsh()) << "Connection starting for" << thing->name(); + return ; + case QProcess::Running: + qCInfo(dcReverseSsh()) << "Reverse SSH connected for" << thing->name(); + thing->setStateValue(reverseSshConnectedStateTypeId, true); + return; + case QProcess::NotRunning: + qCInfo(dcReverseSsh()) << "Reverse SSH disconnected for" << thing->name(); + thing->setStateValue(reverseSshConnectedStateTypeId, false); + return; + } + }); + connect(process, &QProcess::readyRead, thing, [=](){ + QByteArray data = process->readAll(); + qCWarning(dcReverseSsh()) << "Reverse SSH connection data for" << thing->name() << data; + }); + + + // Start up now if enabled + bool enabled = thing->setting(reverseSshSettingsActiveParamTypeId).toBool(); + if (enabled) { + process->start(); + } + + // And connect to the enabled setting + connect(thing, &Thing::settingChanged, this, [=](const ParamTypeId &settingId, const QVariant &value){ + if (settingId == reverseSshSettingsActiveParamTypeId) { + if (value.toBool()) { + process->start(); + } else { + process->terminate(); + } + } + }); + + info->finish(Thing::ThingErrorNoError); + + + // Create a watchdog to reconnect if a connection drops... + if (!m_watchdog) { + m_watchdog = hardwareManager()->pluginTimerManager()->registerTimer(10); + connect(m_watchdog, &PluginTimer::timeout, this, [this](){ + foreach (Thing *thing, m_processes.keys()) { + QProcess *process = m_processes.value(thing); + if (thing->setting(reverseSshSettingsActiveParamTypeId).toBool() && process->state() == QProcess::NotRunning) { + qCInfo(dcReverseSsh()) << "Reconnecting reverse SSH for" << thing->name(); + process->start(); + } + } + }); + } +} + + +void IntegrationPluginReverseSsh::thingRemoved(Thing *thing) +{ + if (thing->thingClassId() == reverseSshThingClassId) { + QProcess *process = m_processes.take(thing); + if (process->state() != QProcess::NotRunning) { + process->terminate(); + process->waitForFinished(); + } + } + + if (myThings().isEmpty()) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_watchdog); + m_watchdog = nullptr; + } +} diff --git a/remotessh/integrationpluginremotessh.h b/reversessh/integrationpluginreversessh.h similarity index 70% rename from remotessh/integrationpluginremotessh.h rename to reversessh/integrationpluginreversessh.h index 03c52e64..5f8b16db 100644 --- a/remotessh/integrationpluginremotessh.h +++ b/reversessh/integrationpluginreversessh.h @@ -33,43 +33,29 @@ #include "plugintimer.h" #include "integrations/integrationplugin.h" +#include "extern-plugininfo.h" #include -class IntegrationPluginRemoteSsh : public IntegrationPlugin +class IntegrationPluginReverseSsh : public IntegrationPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginremotessh.json") + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginreversessh.json") Q_INTERFACES(IntegrationPlugin) public: - explicit IntegrationPluginRemoteSsh(); + explicit IntegrationPluginReverseSsh(); + ~IntegrationPluginReverseSsh(); - void init() override; + void startPairing(ThingPairingInfo *info) override; + void confirmPairing(ThingPairingInfo *info, const QString &user, const QString &secret) override; void setupThing(ThingSetupInfo *info) override; void thingRemoved(Thing *thing) override; - void executeAction(ThingActionInfo *info) override; private: - QHash m_reverseSSHProcess; - QHash m_sshKeyGenProcess; - - QHash m_startingProcess; - QHash m_killingProcess; - - PluginTimer *m_pluginTimer = nullptr; - - bool m_aboutToQuit = false; - QString m_identityFilePath; - - QProcess *startReverseSSHProcess(Thing *thing); - -private slots: - void onPluginTimeout(); - void processReadyRead(); - void processStateChanged(QProcess::ProcessState state); - void processFinished(int exitCode, QProcess::ExitStatus exitStatus); + QHash m_processes; + PluginTimer *m_watchdog = nullptr; }; #endif // INTEGRATIONPLUGINREMOTESSH_H diff --git a/remotessh/integrationpluginremotessh.json b/reversessh/integrationpluginreversessh.json similarity index 50% rename from remotessh/integrationpluginremotessh.json rename to reversessh/integrationpluginreversessh.json index a5dea82c..3cf231ef 100644 --- a/remotessh/integrationpluginremotessh.json +++ b/reversessh/integrationpluginreversessh.json @@ -1,7 +1,7 @@ { "id": "cd75d899-3f53-43fa-9ee8-f6b36646a27d", - "name": "RemoteSsh", - "displayName": "Remote SSH", + "name": "ReverseSsh", + "displayName": "Reverse SSH", "vendors": [ { "id": "e87ad7b1-1705-46b1-a962-282126646b4d", @@ -13,73 +13,57 @@ "name": "reverseSsh", "displayName": "Reverse SSH", "createMethods": ["user"], + "setupMethod": "userandpassword", "interfaces": ["connectable"], + "settingsTypes": [ + { + "id": "478aea93-68c4-4527-ac91-15b8da77f1e5", + "name": "active", + "displayName": "Active", + "type": "bool", + "defaultValue": true + } + ], "paramTypes": [ { "id": "92747d75-d18a-4915-bd48-0edd5cc5f19a", "name": "address", - "displayName": "Address", + "displayName": "SSH server address", "type": "QString", - "inputType": "IPv4Address", - "defaultValue": "127.0.0.1" - }, - { - "id": "7f7aa198-c719-415e-b31c-7a676b9d8e01", - "name": "localPort", - "displayName": "Local Port", - "type": "int", - "defaultValue": 22 + "defaultValue": "" }, { "id": "988aec42-1026-4aef-85d1-329ee1a34208", "name": "remotePort", - "displayName": "Remote Port", + "displayName": "SSH server port", "type": "int", - "defaultValue": 2022 + "defaultValue": 22 }, { - "id": "c675f7ea-f94a-46e9-bf0f-92682182d6dd", - "name": "user", - "displayName": "User Name", - "type": "QString", - "inputType": "TextLine", - "defaultValue": "Enter your user" + "id": "7f7aa198-c719-415e-b31c-7a676b9d8e01", + "name": "localPort", + "displayName": "Local SSH server port", + "type": "int", + "defaultValue": 22 }, { - "id": "d8cc7177-bf35-4394-ab7b-881184bd8c8b", - "name": "password", - "displayName": "Password", - "type": "QString", - "inputType": "TextLine", - "defaultValue": "Enter your password" + "id": "e999ce4b-bc8c-4664-8c1c-d502a1589e78", + "name": "remoteOpenPort", + "displayName": "Remote port to be opened", + "type": "int", + "defaultValue": 2201, + "minValue": 0, + "maxValue": 65535 } ], "stateTypes":[ - { - "id": "19f079f0-1654-44c3-ab10-e7d7f9742e09", - "name": "reachable", - "displayName": "Server Reachable", - "displayNameEvent": "reachable status changed", - "type": "bool", - "defaultValue": true - }, { "id": "1ae425b2-d642-42ca-be41-4d06dff5c5cd", "name": "connected", "displayName": "Connected", "type": "bool", "defaultValue": false, - "displayNameEvent": "Connected changed", - "displayNameAction": "Connect to Server", - "writable": true - }, - { - "id": "d8bb619e-6602-4c89-8654-85e111520561", - "name": "sshKey", - "displayName": "SSH public key", - "displayNameEvent": "SSH key changed", - "type": "QString", - "defaultValue": "-" + "displayNameEvent": "Connected changed" } ] } diff --git a/remotessh/meta.json b/reversessh/meta.json similarity index 100% rename from remotessh/meta.json rename to reversessh/meta.json diff --git a/reversessh/reversessh.pro b/reversessh/reversessh.pro new file mode 100644 index 00000000..8746ec49 --- /dev/null +++ b/reversessh/reversessh.pro @@ -0,0 +1,10 @@ +include(../plugins.pri) + +TARGET = $$qtLibraryTarget(nymea_integrationpluginreversessh) + +SOURCES += \ + integrationpluginreversessh.cpp \ + +HEADERS += \ + integrationpluginreversessh.h \ + diff --git a/remotessh/remotessh.svg b/reversessh/reversessh.svg similarity index 100% rename from remotessh/remotessh.svg rename to reversessh/reversessh.svg diff --git a/remotessh/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts b/reversessh/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts similarity index 52% rename from remotessh/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts rename to reversessh/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts index 7206c033..fa483b46 100644 --- a/remotessh/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts +++ b/reversessh/translations/cd75d899-3f53-43fa-9ee8-f6b36646a27d-en_US.ts @@ -2,45 +2,32 @@ - RemoteSsh + IntegrationPluginReverseSsh - Remote Access - The name of the vendor ({e87ad7b1-1705-46b1-a962-282126646b4d}) + Please enter your login credentials for %1. - Reverse SSH - The name of the ThingClass ({a4f12741-4f30-40ca-a319-7f15e9c0c43a}) + Login error on remote SSH server. - Address - The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {92747d75-d18a-4915-bd48-0edd5cc5f19a}) + Cannot connect to remote SSH server. + + + + + ReverseSsh + + Active + The name of the ParamType (ThingClass: reverseSsh, Type: settings, ID: {478aea93-68c4-4527-ac91-15b8da77f1e5}) - Local Port - The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {7f7aa198-c719-415e-b31c-7a676b9d8e01}) - - - - Remote Port - The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {988aec42-1026-4aef-85d1-329ee1a34208}) - - - - User Name - The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {c675f7ea-f94a-46e9-bf0f-92682182d6dd}) - - - - Password - The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {d8cc7177-bf35-4394-ab7b-881184bd8c8b}) - - - - reachable status changed - The name of the EventType ({19f079f0-1654-44c3-ab10-e7d7f9742e09}) of ThingClass reverseSsh + Connected + The name of the ParamType (ThingClass: reverseSsh, EventType: connected, ID: {1ae425b2-d642-42ca-be41-4d06dff5c5cd}) +---------- +The name of the StateType ({1ae425b2-d642-42ca-be41-4d06dff5c5cd}) of ThingClass reverseSsh @@ -49,41 +36,35 @@ - Connect to Server - The name of the ActionType ({1ae425b2-d642-42ca-be41-4d06dff5c5cd}) of ThingClass reverseSsh + Local SSH server port + The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {7f7aa198-c719-415e-b31c-7a676b9d8e01}) - SSH key changed - The name of the EventType ({d8bb619e-6602-4c89-8654-85e111520561}) of ThingClass reverseSsh + Remote Access + The name of the vendor ({e87ad7b1-1705-46b1-a962-282126646b4d}) - Remote SSH - The name of the plugin RemoteSsh ({cd75d899-3f53-43fa-9ee8-f6b36646a27d}) + Remote port to be opened + The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {e999ce4b-bc8c-4664-8c1c-d502a1589e78}) - Server Reachable - The name of the ParamType (ThingClass: reverseSsh, EventType: reachable, ID: {19f079f0-1654-44c3-ab10-e7d7f9742e09}) + Reverse SSH + The name of the ThingClass ({a4f12741-4f30-40ca-a319-7f15e9c0c43a}) ---------- -The name of the StateType ({19f079f0-1654-44c3-ab10-e7d7f9742e09}) of ThingClass reverseSsh +The name of the plugin ReverseSsh ({cd75d899-3f53-43fa-9ee8-f6b36646a27d}) - Connected - The name of the ParamType (ThingClass: reverseSsh, ActionType: connected, ID: {1ae425b2-d642-42ca-be41-4d06dff5c5cd}) ----------- -The name of the ParamType (ThingClass: reverseSsh, EventType: connected, ID: {1ae425b2-d642-42ca-be41-4d06dff5c5cd}) ----------- -The name of the StateType ({1ae425b2-d642-42ca-be41-4d06dff5c5cd}) of ThingClass reverseSsh + SSH server address + The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {92747d75-d18a-4915-bd48-0edd5cc5f19a}) - SSH public key - The name of the ParamType (ThingClass: reverseSsh, EventType: sshKey, ID: {d8bb619e-6602-4c89-8654-85e111520561}) ----------- -The name of the StateType ({d8bb619e-6602-4c89-8654-85e111520561}) of ThingClass reverseSsh + SSH server port + The name of the ParamType (ThingClass: reverseSsh, Type: thing, ID: {988aec42-1026-4aef-85d1-329ee1a34208})