mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-27 02:20:17 +00:00
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 <name> <url>": 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 <name>": 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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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; i<OpenSockets.length(); i++){
|
||||
if(OpenSockets[i]->ID().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; i<bridgeKeys.length(); i++){
|
||||
bridgeKeys[i] = CONFIG->value(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; i<OpenSockets.length(); i++){
|
||||
if( bridgeKeys.contains( OpenSockets[i]->ID() ) ){
|
||||
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; i<bridgeKeys.length(); i++){
|
||||
WebSocket *sock = new WebSocket(bridgeKeys[i], bridgeKeys[i], AUTH);
|
||||
connect(sock, SIGNAL(SocketClosed(QString)), this, SLOT(SocketClosed(QString)) );
|
||||
OpenSockets << sock;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ WebSocket::WebSocket(QSslSocket *sock, QString ID, AuthorizationManager *auth){
|
||||
QTimer::singleShot(30000, this, SLOT(checkAuth()));
|
||||
}
|
||||
|
||||
WebSocket::WebSocket(QUrl url, QString ID, AuthorizationManager *auth){
|
||||
WebSocket::WebSocket(QString url, QString ID, AuthorizationManager *auth){
|
||||
//sets up a bridge connection (websocket only)
|
||||
SockID = ID;
|
||||
isBridge = true;
|
||||
@@ -81,7 +81,12 @@ WebSocket::WebSocket(QUrl url, QString ID, AuthorizationManager *auth){
|
||||
connect(SOCKET, SIGNAL(connected()), this, SLOT(startBridgeAuth()) );
|
||||
//idletimer->start(); //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; i<evlist.length(); i++){
|
||||
EventWatcher::EVENT_TYPE type = EventWatcher::typeFromString(evlist[i]);
|
||||
//qDebug() << " - type:" << type;
|
||||
if(isBridge){
|
||||
ForwardEvents.clear();
|
||||
if(!REQ.bridgeID.isEmpty()){ ForwardEvents = BRIDGE[REQ.bridgeID].sendEvents; }
|
||||
}
|
||||
if(type==EventWatcher::BADEVENT){ continue; }
|
||||
outargs.insert(out.in_struct.name,QJsonValue(evlist[i]));
|
||||
if(sub==1){
|
||||
@@ -293,6 +311,7 @@ if(out.in_struct.namesp.toLower() == "rpc"){
|
||||
}else{
|
||||
ForwardEvents.removeAll(type);
|
||||
}
|
||||
if(isBridge && !REQ.bridgeID.isEmpty()){ BRIDGE[REQ.bridgeID].sendEvents = ForwardEvents; }
|
||||
}
|
||||
out.out_args = outargs;
|
||||
out.CODE = RestOutputStruct::OK;
|
||||
@@ -542,14 +561,29 @@ void WebSocket::EventUpdate(EventWatcher::EVENT_TYPE evtype, QJsonValue msg){
|
||||
//qDebug() << "Got Socket Event Update:" << msg;
|
||||
if(msg.isNull()){ msg = EVENTS->lastEvent(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; i<conns.length(); i++){
|
||||
if( !BRIDGE[conns[i]].sendEvents.contains(evtype) ){ continue; }
|
||||
//Encrypt the data with the proper key
|
||||
QString key = BRIDGE[conns[i]].enc_key;
|
||||
QString enc_data = raw;
|
||||
if(!key.isEmpty()){ enc_data = AUTHSYSTEM->encryptString(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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
struct bridge_data{
|
||||
QString enc_key, auth_tok;
|
||||
QList<EventWatcher::EVENT_TYPE> 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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; i<argc; i++){
|
||||
if( QString(argv[i])=="-rest" ){ websocket = false;}
|
||||
else if( QString(argv[i])=="-p" && (i+1<argc) ){ i++; port = QString(argv[i]).toUInt(); }
|
||||
else if( QString(argv[i]).startsWith("bridge_") ){
|
||||
setonly = true;
|
||||
QString opt = QString(argv[i]).section("_",1,-1);
|
||||
if(opt=="list"){
|
||||
QStringList bridges = CONFIG->allKeys().filter("bridge_connections/");
|
||||
qDebug() << "Current Bridges:";
|
||||
for(int i=0; i<bridges.length(); i++){
|
||||
qDebug() << bridges[i].section("/",1,-1) + " ("+CONFIG->value(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"); }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user