From 8a16f9a4d04e3214f3e9506750e1699d06b68ce0 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Thu, 12 May 2016 10:25:16 -0400 Subject: [PATCH] Completely finish up the raw changes to the sysadm-server so it can operator over a bridge connection (untested). This also adds some stand-alone CLI options to the sysadm-binary utility: "bridge_list": List any bridge connections in the settings file. Output Format: "name (url)" "bridge_add ": Add a bridge connection to the settings with the given name. (if websocket server is running, this change will take effect within 5 minutes). "bridge_remove ": Remove a bridge connection from the settings. If a websocket server is running, this change will take effect within 5 minutes (closing the connection to the removed bridge as needed). There is also a new option in the global server config file: BRIDGE_CONNECTIONS_ONLY=[true/false] If true, this will allow the websocket server to run without listening on any ports, and instead force all traffic through the existing bridge connections. --- src/server/AuthorizationManager.cpp | 9 ++++-- src/server/WebServer.cpp | 31 ++++++++++++++++--- src/server/WebSocket.cpp | 46 +++++++++++++++++++++++++---- src/server/WebSocket.h | 4 ++- src/server/globals.h | 2 ++ src/server/main.cpp | 42 ++++++++++++++++++++++++-- src/server/sysadm-server | 6 ++++ 7 files changed, 123 insertions(+), 17 deletions(-) diff --git a/src/server/AuthorizationManager.cpp b/src/server/AuthorizationManager.cpp index 45b2b0c..8336c2e 100644 --- a/src/server/AuthorizationManager.cpp +++ b/src/server/AuthorizationManager.cpp @@ -233,9 +233,12 @@ QString AuthorizationManager::GenerateEncCheckString(){ QString AuthorizationManager::GenerateEncString_bridge(QString str){ //Get the private key - return str; //NOT IMPLEMENTED YET - QByteArray privkey = "";//SSL_cfg.privateKey().toPem(); - + QFile keyfile("/usr/local/etc/sysadm/ws_bridge.key"); + keyfile.open(QIODevice::ReadOnly); + QSslKey key(&keyfile, QSsl::Rsa); + QByteArray privkey = key.toPem(); + keyfile.close(); + //Now use this private key to encode the given string unsigned char encode[4098] = {}; RSA *rsa= NULL; diff --git a/src/server/WebServer.cpp b/src/server/WebServer.cpp index 28ce2de..3db6921 100644 --- a/src/server/WebServer.cpp +++ b/src/server/WebServer.cpp @@ -33,7 +33,8 @@ bool WebServer::startServer(quint16 port, bool websocket){ qDebug() << " - Version:" << QSslSocket::sslLibraryVersionString(); } bool ok = false; - if(websocket){ ok = setupWebSocket(port); } + if(websocket && BRIDGE_ONLY){ ok = true; } + else if(websocket){ ok = setupWebSocket(port); } else{ ok = setupTcp(port); } if(ok){ @@ -124,9 +125,11 @@ bool WebServer::allowConnection(QHostAddress addr){ } QString WebServer::generateID(){ - int id = 0; + int id = 1; //start integer ID's with 1 for(int i=0; iID().toInt()>=id){ id = OpenSockets[i]->ID().toInt()+1; } + bool ok=false; + int num = OpenSockets[i]->ID().toInt(&ok); //some ID's will not be a simple number (bridge connections) + if(ok && num>=id){ id = OpenSockets[i]->ID().toInt()+1; } } return QString::number(id); } @@ -216,5 +219,25 @@ void WebServer::SocketClosed(QString ID){ // BRIDGE Connection checks void WebServer::checkBridges(){ - + if(!WS_MODE){ return; } + //Get all the unique bridge URL's we need connections to + QStringList bridgeKeys = CONFIG->allKeys().filter("bridge_connections/"); + for(int i=0; ivalue(bridgeKeys[i]).toString(); //turn the key into the URL for the bridge (unique ID) + } + //Now browse through all the current connections and see if any are already active + for(int i=0; iID() ) ){ + bridgeKeys.removeAll( OpenSockets[i]->ID() ); //already running - remove from the temporary list + }else if( OpenSockets[i]->ID().toInt()==0 ){ + //non-integer ID - another bridge connection which must have been removed from the server settings + OpenSockets[i]->closeConnection(); + } + } + //Now startup any connections which are missing + for(int i=0; istart(); //do not idle out on a bridge connection /*QTimer::singleShot(30000, this, SLOT(checkAuth()));*/ - SOCKET->open(url); + //Assemble the URL as needed + if(!url.startsWith("wss://")){ url.prepend("wss://"); } + bool hasport = false; + url.section(":",-1).toInt(&hasport); //check if the last piece of the url is a valid number + if(!hasport){ url.append(":"+QString::number(BRIDGEPORTNUMBER)); } + SOCKET->open(QUrl(url)); } WebSocket::~WebSocket(){ @@ -101,6 +106,15 @@ QString WebSocket::ID(){ return SockID; } +void WebSocket::closeConnection(){ + if(SOCKET!=0 && SOCKET->isValid()){ + SOCKET->close(); + } + if(TSOCKET!=0 && TSOCKET->isValid()){ + TSOCKET->close(); + } +} + //======================= // PRIVATE //======================= @@ -285,6 +299,10 @@ if(out.in_struct.namesp.toLower() == "rpc"){ for(int i=0; ilastEvent(evtype); } if(msg.isNull()){ return; } //nothing to send - if( !ForwardEvents.contains(evtype) ){ return; } + if( !ForwardEvents.contains(evtype) && !isBridge ){ return; } RestOutputStruct out; out.CODE = RestOutputStruct::OK; out.in_struct.namesp = "events"; out.out_args = msg; - out.Header << "Content-Type: text/json; charset=utf-8"; //REST header info + //out.Header << "Content-Type: text/json; charset=utf-8"; //REST header info out.in_struct.name = EventWatcher::typeToString(evtype); //qDebug() << "Send Event:" << out.assembleMessage(); - //Now send the message back through the socket - this->emit SendMessage(out.assembleMessage()); + if(isBridge){ + QString raw = out.assembleMessage(); + QStringList conns = BRIDGE.keys(); + for(int i=0; iencryptString(raw, key); } + //Now add the destination ID + enc_data.prepend( conns[i]+"\n"); + this->emit SendMessage(enc_data); + } + }else{ + //NON-BRIDGE: Now send the message back through the socket + this->emit SendMessage(out.assembleMessage()); + } } diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 694a8c9..c7d611f 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -13,6 +13,7 @@ struct bridge_data{ QString enc_key, auth_tok; + QList sendEvents; }; class WebSocket : public QObject{ @@ -20,10 +21,11 @@ class WebSocket : public QObject{ public: WebSocket(QWebSocket*, QString ID, AuthorizationManager *auth); WebSocket(QSslSocket*, QString ID, AuthorizationManager *auth); - WebSocket(QUrl, QString ID, AuthorizationManager *auth); //sets up a bridge connection (websocket only) + WebSocket(QString url, QString ID, AuthorizationManager *auth); //sets up a bridge connection (websocket only) ~WebSocket(); QString ID(); + void closeConnection(); private: QTimer *idletimer; diff --git a/src/server/globals.h b/src/server/globals.h index 57ab16e..024d2fb 100644 --- a/src/server/globals.h +++ b/src/server/globals.h @@ -20,6 +20,7 @@ extern EventWatcher *EVENTS; extern Dispatcher *DISPATCHER; //Special defines +#define BRIDGEPORTNUMBER 12149 //Default port for a sysadm-bridge #define WSPORTNUMBER 12150 // WebSocket server default port #define PORTNUMBER 12151 // TCP server default port @@ -27,5 +28,6 @@ extern Dispatcher *DISPATCHER; extern int BlackList_BlockMinutes; extern int BlackList_AuthFailsToBlock; extern int BlackList_AuthFailResetMinutes; +extern bool BRIDGE_ONLY; //bridge-only mode (no listening on a socket) #endif diff --git a/src/server/main.cpp b/src/server/main.cpp index 16ce691..7190bd5 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -25,6 +25,7 @@ bool WS_MODE = false; int BlackList_BlockMinutes = 60; int BlackList_AuthFailsToBlock = 5; int BlackList_AuthFailResetMinutes = 10; +bool BRIDGE_ONLY = false; //Create the default logfile QFile logfile; @@ -64,7 +65,7 @@ inline QString ReadFile(QString path){ int main( int argc, char ** argv ) { - QCoreApplication a(argc, argv); + //Check whether running as root if( getuid() != 0){ qDebug() << "sysadm-server must be started as root!"; @@ -73,13 +74,44 @@ int main( int argc, char ** argv ) //Evaluate input arguments bool websocket = true; + bool setonly = false; quint16 port = 0; for(int i=1; iallKeys().filter("bridge_connections/"); + qDebug() << "Current Bridges:"; + for(int i=0; ivalue(bridges[i]).toString()+")"; + } + }else if(opt=="add" && argc > i+2){ + QString name = QString(argv[i+1]); + QString url = QString(argv[i+2]); + CONFIG->setValue("bridge_connections/"+name, url); + qDebug() << "New Bridge Added:" << name+" ("+url+")"; + i=i+2; + }else if(opt=="remove" && argc>i+1){ + QString name = QString(argv[i+1]); + CONFIG->remove("bridge_connections/"+name); + qDebug() << "Bridge Removed:" << name; + i=i+1; + }else{ + qDebug() << "Unknown option:" << argv[i]; + return 1; + } + }else{ + qDebug() << "Unknown option:" << argv[1]; + return 1; + } } + if(setonly){ CONFIG->sync(); return 0; } WS_MODE = websocket; //set the global variable too - + + QCoreApplication a(argc, argv); //Now load the config file QStringList conf = ReadFile(CONFFILE).split("\n"); if(!conf.filter("[internal]").isEmpty()){ @@ -123,7 +155,11 @@ int main( int argc, char ** argv ) int tmp = conf.filter(rg).first().section("=",1,1).simplified().toInt(&ok); if(ok){ BlackList_AuthFailResetMinutes = tmp; } } - + rg = QRegExp("BRIDGE_CONNECTIONS_ONLY=*",Qt::CaseSensitive,QRegExp::Wildcard); + if(!conf.filter(rg).isEmpty()){ + BRIDGE_ONLY = conf.filter(rg).first().section("=",1,1).simplified().toLower()=="true"; + } + //Setup the log file LogManager::checkLogDir(); //ensure the logging directory exists if(!websocket){ logfile.setFileName("/var/log/sysadm-server-tcp.log"); } diff --git a/src/server/sysadm-server b/src/server/sysadm-server index 4d8b0cb..1459416 100644 --- a/src/server/sysadm-server +++ b/src/server/sysadm-server @@ -17,6 +17,12 @@ ssl_keygen() -keyout /usr/local/etc/sysadm/wsserver.key \ -out /usr/local/etc/sysadm/wsserver.crt -days 1024 \ -subj "/C=US/ST=MY/L=NULL/O=SysAdm/OU=SysAdm/CN=SysAdm/emailAddress=none@example.org" 2>/dev/null + if [ -ne "/usr/local/etc/sysadm/ws_bridge.key" ]; then + openssl req -x509 -nodes -newkey rsa:2048 \ + -keyout /usr/local/etc/sysadm/ws_bridge.key \ + -out /usr/local/etc/sysadm/ws_bridge.crt -days 102400 \ + -subj "/C=US/ST=MY/L=NULL/O=SysAdm/OU=SysAdm/CN=SysAdm/emailAddress=none@example.org" 2>/dev/null + fi fi }