From a36081c77ebf2ad73239282c52c2a4f4b2399cc1 Mon Sep 17 00:00:00 2001 From: Kris Moore Date: Tue, 9 Feb 2016 12:46:41 -0500 Subject: [PATCH 01/24] Add new API call to iohyve, which returns true/false if iohyve has been setup on the box REST Request: ------------------------------- PUT /sysadm/iohyve { "action" : "issetup" } WebSocket Request: ------------------------------- { "id" : "fooid", "namespace" : "sysadm", "args" : { "action" : "issetup" }, "name" : "iohyve" } Response: ------------------------------- { "args": { "issetup": { "setup": "true" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 ++++ src/server/library/sysadm-iohyve.cpp | 14 ++++++++++++++ src/server/library/sysadm-iohyve.h | 1 + 3 files changed, 19 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index f936ccf..fa6bf04 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -506,6 +506,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIohyveRequest(const QJsonVal ok = true; out->insert("fetchiso", sysadm::Iohyve::fetchISO(in_args.toObject())); } + if(act=="issetup"){ + ok = true; + out->insert("issetup", sysadm::Iohyve::isSetup()); + } if(act=="renameiso"){ ok = true; out->insert("renameiso", sysadm::Iohyve::renameISO(in_args.toObject())); diff --git a/src/server/library/sysadm-iohyve.cpp b/src/server/library/sysadm-iohyve.cpp index c28d393..2c184af 100644 --- a/src/server/library/sysadm-iohyve.cpp +++ b/src/server/library/sysadm-iohyve.cpp @@ -39,6 +39,20 @@ QJsonObject Iohyve::fetchISO(QJsonObject jsin) { return retObject; } +// Return if iohyve is setup on the box +QJsonObject Iohyve::isSetup() { + QJsonObject retObject; + + // Check if iohyve is setup on this box + // We check the flags variable, enabling / disabling is done via service mgmt + QString ioflags = General::getConfFileValue("/etc/rc.conf", "iohyve_flags=", 1); + if ( ioflags.isEmpty() ) + retObject.insert("setup", "false"); + else + retObject.insert("setup", "true"); + + return retObject; +} // List the VMs on the box QJsonObject Iohyve::listVMs() { diff --git a/src/server/library/sysadm-iohyve.h b/src/server/library/sysadm-iohyve.h index ecd5051..ba71700 100644 --- a/src/server/library/sysadm-iohyve.h +++ b/src/server/library/sysadm-iohyve.h @@ -15,6 +15,7 @@ namespace sysadm{ class Iohyve{ public: static QJsonObject fetchISO(QJsonObject); + static QJsonObject isSetup(); static QJsonObject listVMs(); static QJsonObject renameISO(QJsonObject); static QJsonObject rmISO(QJsonObject); From 4524a7970b10b33fbb52d211c31de1b4742f0826 Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 13:02:19 -0500 Subject: [PATCH 02/24] Doc activatebe action. --- api/classes/beadm.rst | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/api/classes/beadm.rst b/api/classes/beadm.rst index b280685..7e770f9 100644 --- a/api/classes/beadm.rst +++ b/api/classes/beadm.rst @@ -20,7 +20,7 @@ Every beadm class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listbes", "renamebe" | +| action | | supported actions include "listbes", "renamebe", "activatebe" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -137,4 +137,53 @@ The "renamebe" action renames the specified boot environment. When using this ac "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: activatebe, beadm + +.. _Activate Boot Environment: + +Activate Boot Environment +========================= + +The "activatebe" action activates the specified boot environment (target) so that it will be the default at next boot. + + +**REST Request** + +.. code-block:: json + + PUT /sysadm/beadm + { + "target" : "bootthingy", + "action" : "activatebe" + } + +**WebSocket Request** + +.. code-block:: json + + { + "name" : "beadm", + "args" : { + "action" : "activatebe", + "target" : "bootthingy" + }, + "namespace" : "sysadm", + "id" : "fooid" + } + +**Response** + +.. code-block:: json + + { + "args": { + "activatebe": { + "target": "bootthingy" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From dd0bf124fbe5e92e15b6ee5ef768269648861dec Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 13:13:08 -0500 Subject: [PATCH 03/24] Doc createbe action. --- api/classes/beadm.rst | 54 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/api/classes/beadm.rst b/api/classes/beadm.rst index 7e770f9..0e11983 100644 --- a/api/classes/beadm.rst +++ b/api/classes/beadm.rst @@ -20,7 +20,7 @@ Every beadm class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listbes", "renamebe", "activatebe" | +| action | | supported actions include "listbes", "renamebe", "activatebe", "createbe" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -186,4 +186,56 @@ The "activatebe" action activates the specified boot environment (target) so tha "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: createbe, beadm + +.. _Create Boot Environment: + +Create Boot Environment +======================= + +The "create" action creates a new boot environment. Specify the name of the boot environment as the "newbe". If you wish to create a new boot environment which is a clone of an existing +boot environment, also include "clonefrom" to specify which boot environment to clone. + +**REST Request** + +.. code-block:: json + + PUT /sysadm/beadm + { + "action" : "createbe", + "newbe" : "red", + "clonefrom" : "green" + } + +**WebSocket Request** + +.. code-block:: json + + { + "id" : "fooid", + "args" : { + "newbe" : "red", + "clonefrom" : "green", + "action" : "createbe" + }, + "namespace" : "sysadm", + "name" : "beadm" + } + +**Response** + +.. code-block:: json + + { + "args": { + "createbe": { + "clonefrom": "green", + "newbe": "red" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From 2fa64a20319882ede1a7335deea35fe67d792a1f Mon Sep 17 00:00:00 2001 From: Kris Moore Date: Tue, 9 Feb 2016 13:49:33 -0500 Subject: [PATCH 04/24] Add API call to create a new iohyve guest REST Request: ------------------------------- PUT /sysadm/iohyve { "action" : "create", "name" : "bsdguest", "size" : "10G" } WebSocket Request: ------------------------------- { "name" : "iohyve", "namespace" : "sysadm", "id" : "fooid", "args" : { "name" : "bsdguest", "action" : "create", "size" : "10G" } } Response: ------------------------------- { "args": { "create": { "name": "bsdguest", "size": "10G" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 ++++ src/server/library/sysadm-iohyve.cpp | 30 ++++++++++++++++++++++++++++ src/server/library/sysadm-iohyve.h | 1 + 3 files changed, 35 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 9a295b4..7f8a102 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -501,6 +501,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIohyveRequest(const QJsonVal bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="create"){ + ok = true; + out->insert("create", sysadm::Iohyve::createGuest(in_args.toObject())); + } if(act=="listvms"){ ok = true; out->insert("listvms", sysadm::Iohyve::listVMs()); diff --git a/src/server/library/sysadm-iohyve.cpp b/src/server/library/sysadm-iohyve.cpp index 2c184af..767d326 100644 --- a/src/server/library/sysadm-iohyve.cpp +++ b/src/server/library/sysadm-iohyve.cpp @@ -13,6 +13,36 @@ using namespace sysadm; //PLEASE: Keep the functions in the same order as listed in pcbsd-general.h + +// Create a new guest VM +QJsonObject Iohyve::createGuest(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("name") || !keys.contains("size") ) { + retObject.insert("error", "Missing required key(s) 'name/size'"); + return retObject; + } + + // Get the key values + QString name = jsin.value("name").toString(); + QString size = jsin.value("size").toString(); + + QStringList output = General::RunCommand("iohyve create " + name + " " + size).split("\n"); + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).indexOf("cannot create") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } + } + + // Return some details to user that the action was queued + retObject.insert("name", name); + retObject.insert("size", size); + return retObject; +} + // Queue the fetch of an ISO QJsonObject Iohyve::fetchISO(QJsonObject jsin) { QJsonObject retObject; diff --git a/src/server/library/sysadm-iohyve.h b/src/server/library/sysadm-iohyve.h index ba71700..23d8009 100644 --- a/src/server/library/sysadm-iohyve.h +++ b/src/server/library/sysadm-iohyve.h @@ -14,6 +14,7 @@ namespace sysadm{ class Iohyve{ public: + static QJsonObject createGuest(QJsonObject); static QJsonObject fetchISO(QJsonObject); static QJsonObject isSetup(); static QJsonObject listVMs(); From f568b582288b0ff6d7949d2ee7bb8d075b7c3a7a Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 14:11:22 -0500 Subject: [PATCH 05/24] Doc setup action. --- api/classes/iohyve.rst | 53 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/api/classes/iohyve.rst b/api/classes/iohyve.rst index a944590..2d89406 100644 --- a/api/classes/iohyve.rst +++ b/api/classes/iohyve.rst @@ -20,7 +20,7 @@ Every iohyve class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso" | +| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -244,4 +244,55 @@ The "rmiso" action is used to to remove an existing ISO file from disk. Specify "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: setup, iohyve + +.. _Setup Iohyve: + +Setup Iohyve +============ + +The "setup" action performs the initial setup of Iohyve. It is mandatory to specify the FreeBSD device name of the "nic" and the ZFS "pool" to use. + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iohyve + { + "nic" : "re0", + "pool" : "tank", + "action" : "setup" + } + +**WebSocket Request** + +.. code-block:: json + + { + "id" : "fooid", + "name" : "iohyve", + "args" : { + "pool" : "tank", + "nic" : "re0", + "action" : "setup" + }, + "namespace" : "sysadm" + } + +**Response** + +.. code-block:: json + + { + "args": { + "setup": { + "nic": "re0", + "pool": "tank" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From 566e4e35c38bed2023560057d4fe3c74deab776f Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 14:13:33 -0500 Subject: [PATCH 06/24] Clarify clonefrom. --- api/classes/beadm.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/classes/beadm.rst b/api/classes/beadm.rst index 0e11983..8424ea3 100644 --- a/api/classes/beadm.rst +++ b/api/classes/beadm.rst @@ -195,8 +195,8 @@ The "activatebe" action activates the specified boot environment (target) so tha Create Boot Environment ======================= -The "create" action creates a new boot environment. Specify the name of the boot environment as the "newbe". If you wish to create a new boot environment which is a clone of an existing -boot environment, also include "clonefrom" to specify which boot environment to clone. +The "create" action creates a new boot environment. Specify the name of the boot environment as the "newbe". By default, this action clones the active boot environment. +To specify another, inactive boot environment, also include "clonefrom" to specify which boot environment to clone from. **REST Request** From e6b285ebfd9bc1123c14fa1780cd4c2de85889a6 Mon Sep 17 00:00:00 2001 From: Kris Moore Date: Tue, 9 Feb 2016 14:21:34 -0500 Subject: [PATCH 07/24] Add API call to start the iohyve installation of a guest from an ISO FYI, this only boots the VM with the ISO, to do the install the user still needs to run "iohyve console " from the system, which uses serial mode to bring up a terminal REST Request: ------------------------------- PUT /sysadm/iohyve { "name" : "bsdguest", "iso" : "FreeBSD-10.2-RELEASE-amd64-disc1.iso", "action" : "install" } WebSocket Request: ------------------------------- { "namespace" : "sysadm", "name" : "iohyve", "id" : "fooid", "args" : { "action" : "install", "iso" : "FreeBSD-10.2-RELEASE-amd64-disc1.iso", "name" : "bsdguest" } } Response: ------------------------------- { "args": { "install": { "iso": "FreeBSD-10.2-RELEASE-amd64-disc1.iso", "name": "bsdguest" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 ++++ src/server/library/sysadm-iohyve.cpp | 29 ++++++++++++++++++++++++++++ src/server/library/sysadm-iohyve.h | 1 + 3 files changed, 34 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 7f8a102..70d3c95 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -513,6 +513,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIohyveRequest(const QJsonVal ok = true; out->insert("fetchiso", sysadm::Iohyve::fetchISO(in_args.toObject())); } + if(act=="install"){ + ok = true; + out->insert("install", sysadm::Iohyve::installGuest(in_args.toObject())); + } if(act=="issetup"){ ok = true; out->insert("issetup", sysadm::Iohyve::isSetup()); diff --git a/src/server/library/sysadm-iohyve.cpp b/src/server/library/sysadm-iohyve.cpp index 767d326..3bddd62 100644 --- a/src/server/library/sysadm-iohyve.cpp +++ b/src/server/library/sysadm-iohyve.cpp @@ -69,6 +69,35 @@ QJsonObject Iohyve::fetchISO(QJsonObject jsin) { return retObject; } +// Create a new guest VM +QJsonObject Iohyve::installGuest(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("name") || !keys.contains("iso") ) { + retObject.insert("error", "Missing required key(s) 'name/iso'"); + return retObject; + } + + // Get the key values + QString name = jsin.value("name").toString(); + QString iso = jsin.value("iso").toString(); + + QStringList output = General::RunCommand("iohyve install " + name + " " + iso).split("\n"); + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).indexOf("Could not open") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } + } + + // Return some details to user that the action was queued + retObject.insert("name", name); + retObject.insert("iso", iso); + return retObject; +} + // Return if iohyve is setup on the box QJsonObject Iohyve::isSetup() { QJsonObject retObject; diff --git a/src/server/library/sysadm-iohyve.h b/src/server/library/sysadm-iohyve.h index 23d8009..20ffd6a 100644 --- a/src/server/library/sysadm-iohyve.h +++ b/src/server/library/sysadm-iohyve.h @@ -16,6 +16,7 @@ class Iohyve{ public: static QJsonObject createGuest(QJsonObject); static QJsonObject fetchISO(QJsonObject); + static QJsonObject installGuest(QJsonObject); static QJsonObject isSetup(); static QJsonObject listVMs(); static QJsonObject renameISO(QJsonObject); From c8b123df654a4de3a32b08ecc3ab99a2dac8ca1b Mon Sep 17 00:00:00 2001 From: Kris Moore Date: Tue, 9 Feb 2016 14:42:56 -0500 Subject: [PATCH 08/24] Add new API call to start a iohyve guest REST Request: ------------------------------- PUT /sysadm/iohyve { "action" : "start", "name" : "bsdguest" } WebSocket Request: ------------------------------- { "name" : "iohyve", "id" : "fooid", "args" : { "action" : "start", "name" : "bsdguest" }, "namespace" : "sysadm" } Response: ------------------------------- { "args": { "start": { "name": "bsdguest" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 ++++ src/server/library/sysadm-iohyve.cpp | 27 +++++++++++++++++++++++++++ src/server/library/sysadm-iohyve.h | 1 + 3 files changed, 32 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 70d3c95..f5e7736 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -533,6 +533,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIohyveRequest(const QJsonVal ok = true; out->insert("setup", sysadm::Iohyve::setupIohyve(in_args.toObject())); } + if(act=="start"){ + ok = true; + out->insert("start", sysadm::Iohyve::startGuest(in_args.toObject())); + } } //end of "action" key usage //If nothing done - return the proper code diff --git a/src/server/library/sysadm-iohyve.cpp b/src/server/library/sysadm-iohyve.cpp index 3bddd62..167dd03 100644 --- a/src/server/library/sysadm-iohyve.cpp +++ b/src/server/library/sysadm-iohyve.cpp @@ -229,3 +229,30 @@ QJsonObject Iohyve::setupIohyve(QJsonObject jsin) { retObject.insert("nic", nic); return retObject; } + +// Start a guest +QJsonObject Iohyve::startGuest(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("name") ) { + retObject.insert("error", "Missing required key 'name'"); + return retObject; + } + + // Get the key values + QString name = jsin.value("name").toString(); + + // Do the setup right now + QStringList output = General::RunCommand("iohyve start " + name).split("\n"); + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).indexOf("Not a valid") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } + } + + retObject.insert("name", name); + return retObject; +} diff --git a/src/server/library/sysadm-iohyve.h b/src/server/library/sysadm-iohyve.h index 20ffd6a..1cbe8e0 100644 --- a/src/server/library/sysadm-iohyve.h +++ b/src/server/library/sysadm-iohyve.h @@ -22,6 +22,7 @@ public: static QJsonObject renameISO(QJsonObject); static QJsonObject rmISO(QJsonObject); static QJsonObject setupIohyve(QJsonObject); + static QJsonObject startGuest(QJsonObject); }; } //end of pcbsd namespace From 486cd5d099e0569d4b9187a57aebdf4c66391521 Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 14:52:58 -0500 Subject: [PATCH 09/24] Doc issetup action. --- api/classes/iohyve.rst | 54 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/api/classes/iohyve.rst b/api/classes/iohyve.rst index 2d89406..9ce2bfd 100644 --- a/api/classes/iohyve.rst +++ b/api/classes/iohyve.rst @@ -20,7 +20,7 @@ Every iohyve class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup" | +| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -248,12 +248,12 @@ The "rmiso" action is used to to remove an existing ISO file from disk. Specify .. index:: setup, iohyve -.. _Setup Iohyve: +.. _Setup iohyve: -Setup Iohyve +Setup iohyve ============ -The "setup" action performs the initial setup of Iohyve. It is mandatory to specify the FreeBSD device name of the "nic" and the ZFS "pool" to use. +The "setup" action performs the initial setup of iohyve. It is mandatory to specify the FreeBSD device name of the "nic" and the ZFS "pool" to use. **REST Request** @@ -295,4 +295,50 @@ The "setup" action performs the initial setup of Iohyve. It is mandatory to spec "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: issetup, iohyve + +.. _Determine iohyve Setup: + +Determine iohyve Setup +====================== + +The "issetup" action queries if iohyve has been setup and returns either "true" or "false". + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iohyve + { + "action" : "issetup" + } + +**WebSocket Request** + +.. code-block:: json + + { + "id" : "fooid", + "namespace" : "sysadm", + "args" : { + "action" : "issetup" + }, + "name" : "iohyve" + } + +**Response** + +.. code-block:: json + + { + "args": { + "issetup": { + "setup": "true" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From f48954ebf421e6602e7e9c4e3f858b16081f187e Mon Sep 17 00:00:00 2001 From: Kris Moore Date: Tue, 9 Feb 2016 14:54:03 -0500 Subject: [PATCH 10/24] Add new API call to stop a guest VM REST Request: ------------------------------- PUT /sysadm/iohyve { "action" : "stop", "name" : "bsdguest" } WebSocket Request: ------------------------------- { "id" : "fooid", "args" : { "action" : "stop", "name" : "bsdguest" }, "name" : "iohyve", "namespace" : "sysadm" } Response: ------------------------------- { "args": { "stop": { "name": "bsdguest" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 ++++ src/server/library/sysadm-iohyve.cpp | 35 ++++++++++++++++++++++++++++ src/server/library/sysadm-iohyve.h | 1 + 3 files changed, 40 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index f5e7736..0fd60f1 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -537,6 +537,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIohyveRequest(const QJsonVal ok = true; out->insert("start", sysadm::Iohyve::startGuest(in_args.toObject())); } + if(act=="stop"){ + ok = true; + out->insert("stop", sysadm::Iohyve::stopGuest(in_args.toObject())); + } } //end of "action" key usage //If nothing done - return the proper code diff --git a/src/server/library/sysadm-iohyve.cpp b/src/server/library/sysadm-iohyve.cpp index 167dd03..a90d165 100644 --- a/src/server/library/sysadm-iohyve.cpp +++ b/src/server/library/sysadm-iohyve.cpp @@ -256,3 +256,38 @@ QJsonObject Iohyve::startGuest(QJsonObject jsin) { retObject.insert("name", name); return retObject; } + +// Stop a guest +QJsonObject Iohyve::stopGuest(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("name") ) { + retObject.insert("error", "Missing required key 'name'"); + return retObject; + } + + // Get the key values + QString name = jsin.value("name").toString(); + + QString stoparg = "stop"; + if (! keys.contains("force") ) { + if ( jsin.value("force").toString() == "true" ) { + stoparg = "forcekill"; + } + } + + // Do the stop right now + QStringList output = General::RunCommand("iohyve " + stoparg + " " + name).split("\n"); + for ( int i = 0; i < output.size(); i++) + { + // This doesn't work, iohyve doesn't return error message right now + if ( output.at(i).indexOf("No such guest") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } + } + + retObject.insert("name", name); + return retObject; +} diff --git a/src/server/library/sysadm-iohyve.h b/src/server/library/sysadm-iohyve.h index 1cbe8e0..8878b34 100644 --- a/src/server/library/sysadm-iohyve.h +++ b/src/server/library/sysadm-iohyve.h @@ -23,6 +23,7 @@ public: static QJsonObject rmISO(QJsonObject); static QJsonObject setupIohyve(QJsonObject); static QJsonObject startGuest(QJsonObject); + static QJsonObject stopGuest(QJsonObject); }; } //end of pcbsd namespace From 14a79c65a54f7825e43e9ffaf52ae5e08d16ccd7 Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 14:57:26 -0500 Subject: [PATCH 11/24] Doc create action. --- api/classes/iohyve.rst | 53 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/api/classes/iohyve.rst b/api/classes/iohyve.rst index 9ce2bfd..5f83878 100644 --- a/api/classes/iohyve.rst +++ b/api/classes/iohyve.rst @@ -20,7 +20,7 @@ Every iohyve class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup" | +| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup", "create" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -341,4 +341,55 @@ The "issetup" action queries if iohyve has been setup and returns either "true" "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: create, iohyve + +.. _Create Guest: + +Create Guest +============ + +The "create" action creates a new iohyve guest of the specified "name" and "size". + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iohyve + { + "action" : "create", + "name" : "bsdguest", + "size" : "10G" + } + +**WebSocket Request** + +.. code-block:: json + + { + "name" : "iohyve", + "namespace" : "sysadm", + "id" : "fooid", + "args" : { + "name" : "bsdguest", + "action" : "create", + "size" : "10G" + } + } + +**Response** + +.. code-block:: json + + { + "args": { + "create": { + "name": "bsdguest", + "size": "10G" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From 45780390ca4b8297a141de719f93eaf187cb086b Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 15:17:23 -0500 Subject: [PATCH 12/24] Doc install action. --- api/classes/iohyve.rst | 54 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/api/classes/iohyve.rst b/api/classes/iohyve.rst index 5f83878..eec4d8f 100644 --- a/api/classes/iohyve.rst +++ b/api/classes/iohyve.rst @@ -20,7 +20,7 @@ Every iohyve class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup", "create" | +| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup", "create", "install" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -392,4 +392,56 @@ The "create" action creates a new iohyve guest of the specified "name" and "size "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: install, iohyve + +.. _Install Guest: + +Install Guest +============= + +The "install" action starts the iohyve installation of the specified guest from the specified ISO. This action only boots the VM with the ISO; to do the actual installation, +run :command:`iohyve console ` from the system. + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iohyve + { + "name" : "bsdguest", + "iso" : "FreeBSD-10.2-RELEASE-amd64-disc1.iso", + "action" : "install" + } + +**WebSocket Request** + +.. code-block:: json + + { + "namespace" : "sysadm", + "name" : "iohyve", + "id" : "fooid", + "args" : { + "action" : "install", + "iso" : "FreeBSD-10.2-RELEASE-amd64-disc1.iso", + "name" : "bsdguest" + } + } + +**Response** + +.. code-block:: json + + { + "args": { + "install": { + "iso": "FreeBSD-10.2-RELEASE-amd64-disc1.iso", + "name": "bsdguest" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From e2cea7b05b39ac7d93e5e6361553cbda3f961d12 Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 15:23:54 -0500 Subject: [PATCH 13/24] Doc stop action. --- api/classes/iohyve.rst | 51 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/api/classes/iohyve.rst b/api/classes/iohyve.rst index eec4d8f..57cffde 100644 --- a/api/classes/iohyve.rst +++ b/api/classes/iohyve.rst @@ -20,7 +20,8 @@ Every iohyve class request contains the following parameters: | namespace | sysadm | | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ -| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup", "create", "install" | +| action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup", "create", "install", | +| | | "stop" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -444,4 +445,52 @@ run :command:`iohyve console ` from the system. "id": "fooid", "name": "response", "namespace": "sysadm" + } + +.. index:: stop, iohyve + +.. _Stop VM: + +Stop VM +======= + +The "stop" action stops the specified VM. + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iohyve + { + "action" : "stop", + "name" : "bsdguest" + } + +**WebSocket Request** + +.. code-block:: json + + { + "id" : "fooid", + "args" : { + "action" : "stop", + "name" : "bsdguest" + }, + "name" : "iohyve", + "namespace" : "sysadm" + } + +**Response** + +.. code-block:: json + + { + "args": { + "stop": { + "name": "bsdguest" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" } \ No newline at end of file From 9af28e536932ade7f10d87d5b18ae2fc892d44a2 Mon Sep 17 00:00:00 2001 From: dlavigne Date: Tue, 9 Feb 2016 15:38:53 -0500 Subject: [PATCH 14/24] Doc start action. --- api/classes/iohyve.rst | 50 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/api/classes/iohyve.rst b/api/classes/iohyve.rst index 57cffde..8343c85 100644 --- a/api/classes/iohyve.rst +++ b/api/classes/iohyve.rst @@ -21,7 +21,7 @@ Every iohyve class request contains the following parameters: | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ | action | | supported actions include "listvms", "fetchiso", "renameiso", "rmiso", "setup", "issetup", "create", "install", | -| | | "stop" | +| | | "start", "stop" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -447,6 +447,54 @@ run :command:`iohyve console ` from the system. "namespace": "sysadm" } +.. index:: start, iohyve + +.. _Start VM: + +Start VM +======== + +The "start" action starts the specified VM. + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iohyve + { + "action" : "start", + "name" : "bsdguest" + } + +**WebSocket Request** + +.. code-block:: json + + { + "name" : "iohyve", + "id" : "fooid", + "args" : { + "action" : "start", + "name" : "bsdguest" + }, + "namespace" : "sysadm" + } + +**Response** + +.. code-block:: json + + { + "args": { + "start": { + "name": "bsdguest" + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" + } + .. index:: stop, iohyve .. _Stop VM: From 2adb8537e109c9c28bc59ce1df162896934626f0 Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Tue, 9 Feb 2016 23:55:18 -0600 Subject: [PATCH 15/24] Add new iocage API call to clone jails. If you supply props your output will look like this: REST Request: ------------------------------- PUT /sysadm/iocage { "props" : "tag=newtest", "jail" : "test", "action" : "clonejail" } WebSocket Request: ------------------------------- { "namespace" : "sysadm", "name" : "iocage", "args" : { "action" : "clonejail", "jail" : "test", "props" : "tag=newtest" }, "id" : "fooid" } Response: ------------------------------- { "args": { "clonejail": { "jail": "test", "props": "tag=newtest", "success": { "Successfully created": " 5e1fe97e-cfba-11e5-8209-d05099728dbf (newtest)" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } Otherwise iocage will populate it's own values and the props returned by the API will be empty: REST Request: ------------------------------- PUT /sysadm/iocage { "action" : "clonejail", "jail" : "test" } WebSocket Request: ------------------------------- { "args" : { "jail" : "test", "action" : "clonejail" }, "name" : "iocage", "namespace" : "sysadm", "id" : "fooid" } Response: ------------------------------- { "args": { "clonejail": { "jail": "test", "props": "", "success": { "Successfully created": " 89e78032-cfba-11e5-8209-d05099728dbf (2016-02-09@23" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 8 ++++-- src/server/library/sysadm-iocage.cpp | 40 ++++++++++++++++++++++++++++ src/server/library/sysadm-iocage.h | 1 + 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 0fd60f1..fced59c 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -41,7 +41,7 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO if(QFile::exists("/usr/local/sbin/beadm")){ out->insert("sysadm/beadm", "read/write"); } - + // - dispatcher (Internal to server - always available) //"read" is the event notifications, "write" is the ability to queue up jobs @@ -213,7 +213,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmBEADMRequest(const QJsonValu }else if(act=="destroybe"){ ok = true; out->insert("destroybe", sysadm::BEADM::destroyBE(in_args.toObject())); - } + } } //end of "action" key usage //If nothing done - return the proper code @@ -433,6 +433,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="clonejail"){ + ok = true; + out->insert("clonejail", sysadm::Iocage::cloneJail(in_args.toObject())); + } if(act=="cleanall"){ ok = true; out->insert("cleanall", sysadm::Iocage::cleanAll()); diff --git a/src/server/library/sysadm-iocage.cpp b/src/server/library/sysadm-iocage.cpp index 36c35fa..9b0133b 100644 --- a/src/server/library/sysadm-iocage.cpp +++ b/src/server/library/sysadm-iocage.cpp @@ -12,6 +12,46 @@ using namespace sysadm; //PLEASE: Keep the functions in the same order as listed in pcbsd-general.h +// Clone a jail on the box +QJsonObject Iocage::cloneJail(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("jail") ) { + retObject.insert("error", "Missing required keys"); + return retObject; + } + + // Get the key values + QString jail = jsin.value("jail").toString(); + QString props = jsin.value("props").toString(); + + QStringList output = General::RunCommand("iocage clone " + jail + " " + props).split("\n"); + + QJsonObject vals; + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).isEmpty() ) + break; + + if ( output.at(i).indexOf("ERROR:") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } else { + QString key = output.at(i).simplified().section(":", 0, 0); + QString value = output.at(i).simplified().section(":", 1, 1); + + vals.insert(key, value); + } + } + + retObject.insert("jail", jail); + retObject.insert("props", props); + retObject.insert("success", vals); + + return retObject; +} + // Clean everything iocage related on a box QJsonObject Iocage::cleanAll() { QJsonObject retObject; diff --git a/src/server/library/sysadm-iocage.h b/src/server/library/sysadm-iocage.h index 0497f16..e3e6471 100644 --- a/src/server/library/sysadm-iocage.h +++ b/src/server/library/sysadm-iocage.h @@ -14,6 +14,7 @@ namespace sysadm{ class Iocage{ public: + static QJsonObject cloneJail(QJsonObject); static QJsonObject cleanAll(); static QJsonObject cleanTemplates(); static QJsonObject cleanReleases(); From 04020c8a8c5838cd9ac1bbe73f7b6c7c01675c7d Mon Sep 17 00:00:00 2001 From: dlavigne Date: Wed, 10 Feb 2016 09:38:58 -0500 Subject: [PATCH 16/24] Doc clonejail action. --- api/classes/iocage.rst | 105 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/api/classes/iocage.rst b/api/classes/iocage.rst index 4708288..6e211ac 100644 --- a/api/classes/iocage.rst +++ b/api/classes/iocage.rst @@ -21,7 +21,8 @@ Every iocage class request contains the following parameters: | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ | action | | supported actions include "getdefaultsettings", "listjails", "getjailsettings", "startjail", "stopjail", | -| | | "capjail", "cleanjails", "cleanreleases", "cleantemplates", "cleanall", "activatepool", and "deactivatepool" | +| | | "capjail", "clonejail", "cleanjails", "cleanreleases", "cleantemplates", "cleanall", "activatepool", and | +| | | "deactivatepool" | | | | | +---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ @@ -839,6 +840,108 @@ the jail. "namespace": "sysadm" } +.. index:: clonejail, iocage + +.. _Clone a Jail: + +Clone a Jail +============ + +The "clonejail" action clones the specified "jail". By default, the clone will inherit that jail's properties. Use "props" to specify any properties that should differ. All available +properties are described in `iocage(8) `_. + +In this example, the "tag" property is specified so that the new jail has a different name than the jail it was cloned from. + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iocage + { + "props" : "tag=newtest", + "jail" : "test", + "action" : "clonejail" + } + +**WebSocket Request** + +.. code-block:: json + + { + "namespace" : "sysadm", + "name" : "iocage", + "args" : { + "action" : "clonejail", + "jail" : "test", + "props" : "tag=newtest" + }, + "id" : "fooid" + } + +**Response** + +.. code-block:: json + + { + "args": { + "clonejail": { + "jail": "test", + "props": "tag=newtest", + "success": { + "Successfully created": " 5e1fe97e-cfba-11e5-8209-d05099728dbf (newtest)" + } + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" + } + +In this example, no properties are specified so iocage populates its own values and the props returned in the response is empty: + +**REST Request** + +.. code-block:: json + + PUT /sysadm/iocage + { + "action" : "clonejail", + "jail" : "test" + } + +**WebSocket Request** + +.. code-block:: json + + { + "args" : { + "jail" : "test", + "action" : "clonejail" + }, + "name" : "iocage", + "namespace" : "sysadm", + "id" : "fooid" + } + +**Response** + +.. code-block:: json + + { + "args": { + "clonejail": { + "jail": "test", + "props": "", + "success": { + "Successfully created": " 89e78032-cfba-11e5-8209-d05099728dbf (2016-02-09@23" + } + } + }, + "id": "fooid", + "name": "response", + "namespace": "sysadm" + } + .. index:: cleanjails, iocage .. _Clean Jails: From edbef3e759f287672cc4ca03b1883abc99f160d1 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Wed, 10 Feb 2016 12:43:06 -0500 Subject: [PATCH 17/24] Add a full chain of SSL Certificate management fucntions to the authorization manager, and setup the main "auth" API call to use the cert system if no password was supplied for authorization. While here, also move the location of the server config file to /usr/local/etc/sysadm.conf, and print out that location in the main server log file. --- src/server/AuthorizationManager.cpp | 104 ++++++++++++++++++++++++++-- src/server/AuthorizationManager.h | 8 ++- src/server/EventWatcher.cpp | 1 - src/server/SslServer.h | 2 +- src/server/WebSocket.cpp | 13 +++- src/server/main.cpp | 3 +- 6 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/server/AuthorizationManager.cpp b/src/server/AuthorizationManager.cpp index 9429419..070beb4 100644 --- a/src/server/AuthorizationManager.cpp +++ b/src/server/AuthorizationManager.cpp @@ -68,6 +68,55 @@ bool AuthorizationManager::hasFullAccess(QString token){ return ok; } +//SSL Certificate register/revoke/list +bool AuthorizationManager::RegisterCertificate(QString token, QSslCertificate cert){ + if(!checkAuth(token)){ return false; } + QString user = hashID(token).section("::::",2,2); //get the user name from the currently-valid token + CONFIG->setValue("RegisteredCerts/"+user+"/"+QString(cert.publicKey().toPem()), cert.toText()); + return true; +} + +bool AuthorizationManager::RevokeCertificate(QString token, QString key, QString user){ + //user will be the current user if not empty - cannot touch other user's certs without full perms on current session + QString cuser = hashID(token).section("::::",2,2); + if(user.isEmpty()){ user = cuser; } //only probe current user + if(user !=cuser){ + //Check permissions for this cross-user action + if(!hasFullAccess(token)){ return false; } + } + //Check that the given cert exists first + if( !CONFIG->contains("RegisteredCerts/"+user+"/"+key) ){ return false; } + CONFIG->remove("RegisteredCerts/"+user+"/"+key); + return true; +} + +QJsonObject AuthorizationManager::ListCertificates(QString token){ + QJsonObject obj; + QStringList keys; //Format: "RegisteredCerts//" + if( hasFullAccess(token) ){ + //Read all user's certs + keys = CONFIG->allKeys().filter("RegisteredCerts/"); + }else{ + //Only list certs for current user + QString cuser = hashID(token).section("::::",2,2); + keys = CONFIG->allKeys().filter("RegisteredCerts/"+cuser+"/"); + } + keys.sort(); + //Now put the known keys into the output structure arranged by username/key + QJsonObject user; QString username; + for(int i=0; ivalue(keys[i]).toString() ); //just in case the key has additional "/" in it + } + if(!user.isEmpty() && !username.isEmpty()){ obj.insert(username, user); } + + return obj; +} + +//Generic functions int AuthorizationManager::checkAuthTimeoutSecs(QString token){ //Return the number of seconds that a token is valid for if(!HASH.contains(token)){ return 0; } //invalid token @@ -96,7 +145,7 @@ QString AuthorizationManager::LoginUP(QHostAddress host, QString user, QString p }else{ ok = true; //allow local access for users without password } - + qDebug() << "User Login Attempt:" << user << " Success:" << ok << " IP:" << host.toString(); LogManager::log(LogManager::HOST, QString("User Login Attempt: ")+user+" Success: "+(ok?"true":"false")+" IP: "+host.toString() ); if(!ok){ @@ -108,10 +157,53 @@ QString AuthorizationManager::LoginUP(QHostAddress host, QString user, QString p }else{ //valid login - generate a new token for it ClearHostFail(host.toString()); - return generateNewToken(isOperator); + return generateNewToken(isOperator, user); } } +QString AuthorizationManager::LoginUC(QHostAddress host, QString user, QList certs){ + //Login w/ username & SSL certificate + bool localhost = ( (host== QHostAddress::LocalHost) || (host== QHostAddress::LocalHostIPv6) || (host.toString()=="::ffff:127.0.0.1") ); + bool ok = false; + //First check that the user is valid on the system and part of the operator group + bool isOperator = false; + if(user!="root" && user!="toor"){ + QStringList groups = getUserGroups(user); + if(groups.contains("wheel")){ isOperator = true; } //full-access user + else if(!groups.contains("operator")){ + return ""; //user not allowed access if not in either of the wheel/operator groups + } + }else{ isOperator = true; } + qDebug() << "Check username/certificate combination" << user << localhost; + + //Need to check the registered certificates for the designated user + if(!localhost || user=="root" || user=="toor"){ + for(int i=0; icontains("RegisteredCerts/"+user+"/"+QString(certs[i].publicKey().toPem()) ) ){ + //Cert was registered - check expiration info + // TO-DO + ok = true; + } + } + }else{ + ok = true; //allow local access for users without password + } + + qDebug() << "User Login Attempt:" << user << " Success:" << ok << " IP:" << host.toString(); + LogManager::log(LogManager::HOST, QString("User Login Attempt: ")+user+" Success: "+(ok?"true":"false")+" IP: "+host.toString() ); + if(!ok){ + //invalid login + //Bump the fail count for this host + bool overlimit = BumpFailCount(host.toString()); + if(overlimit){ emit BlockHost(host); } + return (overlimit ? "REFUSED" : ""); + }else{ + //valid login - generate a new token for it + ClearHostFail(host.toString()); + return generateNewToken(isOperator, user); + } +} + QString AuthorizationManager::LoginService(QHostAddress host, QString service){ bool localhost = ( (host== QHostAddress::LocalHost) || (host== QHostAddress::LocalHostIPv6) ); @@ -136,13 +228,13 @@ QString AuthorizationManager::LoginService(QHostAddress host, QString service){ }else{ return ""; } - }else{ return generateNewToken(false); }//services are never given operator privileges + }else{ return generateNewToken(false, service); }//services are never given operator privileges } // ========================= // PRIVATE // ========================= -QString AuthorizationManager::generateNewToken(bool isOp){ +QString AuthorizationManager::generateNewToken(bool isOp, QString user){ QString tok; for(int i=0; i certs); //Login w/ username & SSL certificate QString LoginService(QHostAddress host, QString service); //Login a particular automated service private: QHash HASH; QHash IPFAIL; - QString generateNewToken(bool isOperator); + QString generateNewToken(bool isOperator, QString name); QStringList getUserGroups(QString user); //Failure count management diff --git a/src/server/EventWatcher.cpp b/src/server/EventWatcher.cpp index 34756e7..4e5aee4 100644 --- a/src/server/EventWatcher.cpp +++ b/src/server/EventWatcher.cpp @@ -25,7 +25,6 @@ EventWatcher::~EventWatcher(){ } void EventWatcher::start(){ - // - DISPATCH Events starting = true; // - Life Preserver Events WatcherUpdate(LPLOG); //load it initially (will also add it to the watcher); diff --git a/src/server/SslServer.h b/src/server/SslServer.h index 021d4ec..54e8b5b 100644 --- a/src/server/SslServer.h +++ b/src/server/SslServer.h @@ -30,7 +30,7 @@ public: protected: void incomingConnection(qintptr socketDescriptor){ QSslSocket *serverSocket = new QSslSocket(this); - qDebug() << "New Ssl Connection:"; + //qDebug() << "New Ssl Connection:"; //setup any supported encruption types here serverSocket->setSslConfiguration(QSslConfiguration::defaultConfiguration()); serverSocket->setProtocol(SSLVERSION); diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index 31b9580..c58b79c 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -153,11 +153,20 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ if(DEBUG){ qDebug() << "Authenticate Peer:" << SOCKET->peerAddress().toString(); } //Now do the auth if(out.in_struct.name=="auth" && out.in_struct.args.isObject() ){ - //username/password authentication + //username/[password/cert] authentication QString user, pass; if(out.in_struct.args.toObject().contains("username")){ user = JsonValueToString(out.in_struct.args.toObject().value("username")); } if(out.in_struct.args.toObject().contains("password")){ pass = JsonValueToString(out.in_struct.args.toObject().value("password")); } - SockAuthToken = AUTHSYSTEM->LoginUP(host, user, pass); + if(!pass.isEmpty()){ + //Use the given password + SockAuthToken = AUTHSYSTEM->LoginUP(host, user, pass); + }else{ + //No password - use the current SSL certificates instead + QList certs; + if(SOCKET!=0){ certs = SOCKET->sslConfiguration().peerCertificateChain(); } + else if(TSOCKET!=0){ certs = TSOCKET->peerCertificateChain(); } + SockAuthToken = AUTHSYSTEM->LoginUC(host, user, certs); + } }else if(out.in_struct.name == "auth_token" && out.in_struct.args.isObject()){ SockAuthToken = JsonValueToString(out.in_struct.args.toObject().value("token")); }else if(out.in_struct.name == "auth_clear"){ diff --git a/src/server/main.cpp b/src/server/main.cpp index e4ee487..44c1550 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -13,7 +13,7 @@ #define DEBUG 0 //Create any global classes -QSettings *CONFIG = new QSettings("PCBSD","sysadm"); +QSettings *CONFIG = new QSettings("/usr/local/etc/sysadm.conf", QSettings::IniFormat); EventWatcher *EVENTS = new EventWatcher(); Dispatcher *DISPATCHER = new Dispatcher(); bool WS_MODE = false; @@ -91,6 +91,7 @@ int main( int argc, char ** argv ) //Start the daemon int ret = 1; //error return value if( w->startServer(port, websocket) ){ + qDebug() << " - Configuration File:" << CONFIG->fileName(); QThread TBACK, TBACK2; EVENTS->moveToThread(&TBACK); DISPATCHER->moveToThread(&TBACK2); From 06edd4394542d83b4bab17e4521b1130c1336765 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Wed, 10 Feb 2016 13:26:46 -0500 Subject: [PATCH 18/24] Add API call for list/register/revoke SSL Certificate management (auth system: alternate for the user/password combo). I don't have a way to test this just yet (still need to write the other side of the system in the client first), but here is the expected inputs: Namespace: "sysadm" Name: "settings" Arguments structure needs the "action" variable/value for all calls: Action: "list_ssl_certs" - No additional input needed: will list the known/registered certificates organized by : { : } Action: "register_ssl_cert" Example Payload: {"action" : "register_ssl_cert", "pub_key" : } The string needs to match the public key of one of the certificates currently loaded into the server/client connection. This will register that certificate on the server and allow that user to authenticate without a password as long as that same certificate is loaded up in any future connections. No special outputs are send back (just overall error/ok status). Action: "revoke_ssl_cert" Example Payload: {"action" : "revoke_ssl_cert", "pub_key" : , "user" : } The string needs to match one of the keys given by the list function (does not need to match any currently-loaded certs). The "user" field is optional, and allows a connection with full admin privileges to revoke certs belonging to other users. Note about current user/connection permissions level: If the current user has full admin access, the "list_ssl_certs" API call will return the registered certificates for all users on the system - otherwise it will only return the certificates for the current user. Similarly, the "revoke_ssl_cert" may be used to remove certs registered to other users only if the current user/connection has full admin access - otherwise it may only be used to manage the current user's certificates. --- src/server/AuthorizationManager.cpp | 9 +++---- src/server/AuthorizationManager.h | 4 +-- src/server/WebBackend.cpp | 42 +++++++++++++++++++++++++++-- src/server/WebSocket.h | 6 +++++ 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/server/AuthorizationManager.cpp b/src/server/AuthorizationManager.cpp index 070beb4..fd21cb2 100644 --- a/src/server/AuthorizationManager.cpp +++ b/src/server/AuthorizationManager.cpp @@ -90,8 +90,7 @@ bool AuthorizationManager::RevokeCertificate(QString token, QString key, QString return true; } -QJsonObject AuthorizationManager::ListCertificates(QString token){ - QJsonObject obj; +void AuthorizationManager::ListCertificates(QString token, QJsonObject *out){ QStringList keys; //Format: "RegisteredCerts//" if( hasFullAccess(token) ){ //Read all user's certs @@ -106,14 +105,12 @@ QJsonObject AuthorizationManager::ListCertificates(QString token){ QJsonObject user; QString username; for(int i=0; iinsert(username, user); user = QJsonObject(); } //save the current info to the output username = keys[i].section("/",1,1); //save the new username for later } user.insert(keys[i].section("/",2,3000), CONFIG->value(keys[i]).toString() ); //just in case the key has additional "/" in it } - if(!user.isEmpty() && !username.isEmpty()){ obj.insert(username, user); } - - return obj; + if(!user.isEmpty() && !username.isEmpty()){ out->insert(username, user); } } //Generic functions diff --git a/src/server/AuthorizationManager.h b/src/server/AuthorizationManager.h index db96752..5242b4b 100644 --- a/src/server/AuthorizationManager.h +++ b/src/server/AuthorizationManager.h @@ -19,10 +19,10 @@ public: bool checkAuth(QString token); //see if the given token is valid bool hasFullAccess(QString token); //see if the token is associated with a full-access account - //SSL Certificate register/revoke/list + //SSL Certificate register/revoke/list (should only run if the current token is valid) bool RegisterCertificate(QString token, QSslCertificate cert); //if token is valid, register the given cert for future logins bool RevokeCertificate(QString token, QString key, QString user=""); //user will be the current user if not empty - cannot touch other user's certs without full perms on current session - QJsonObject ListCertificates(QString token); + void ListCertificates(QString token, QJsonObject *out); int checkAuthTimeoutSecs(QString token); //Return the number of seconds that a token is valid for diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index fced59c..494aeca 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -31,7 +31,9 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO : , } */ - + // - server settings (always available) + out->insert("sysadm/settings","read/write"); + // - syscache if(QFile::exists("/var/run/syscache.pipe")){ out->insert("rpc/syscache","read"); //no write to syscache - only reads @@ -95,7 +97,9 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru } //Go through and forward this request to the appropriate sub-system - if(namesp=="rpc" && name=="dispatcher"){ + if(namesp=="sysadm" && name=="settings"){ + return EvaluateSysadmSettingsRequest(IN.args, out); + }else if(namesp=="rpc" && name=="dispatcher"){ return EvaluateDispatcherRequest(IN.fullaccess, IN.args, out); }else if(namesp=="sysadm" && name=="beadm"){ return EvaluateSysadmBEADMRequest(IN.args, out); @@ -119,6 +123,40 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(const RestInputStru } +// === SYSADM SETTINGS === +RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSettingsRequest(const QJsonValue in_args, QJsonObject *out){ + if(!in_args.isObject()){ return RestOutputStruct::BADREQUEST; } + QJsonObject argsO = in_args.toObject(); + QStringList keys = argsO.keys(); + if(!keys.contains("action")){ return RestOutputStruct::BADREQUEST; } + QString act = argsO.value("action").toString(); + bool ok = false; + if(act=="register_ssl_cert" && keys.contains("pub_key")){ + //Additional arguments: "pub_key" (String), and the cert with that key must already be loaded into the connection + QString pub_key = argsO.value("pub_key").toString();\ + //Now find the currently-loaded certificate with the given public key + QList certs; + if(SOCKET!=0){ certs = SOCKET->sslConfiguration().peerCertificateChain(); } + else if(TSOCKET!=0){ certs = TSOCKET->peerCertificateChain(); } + for(int i=0; iRegisterCertificate(SockAuthToken, certs[i]); + } + } + }else if(act=="list_ssl_certs"){ + AUTHSYSTEM->ListCertificates(SockAuthToken, out); + ok = true; //always works for current user (even if nothing found) + }else if(act=="revoke_ssl_cert" && keys.contains("pub_key") ){ + //Additional arguments: "user" (optional), "pub_key" (String) + QString user; if(keys.contains("user")){ user = argsO.value("user").toString(); } + ok = AUTHSYSTEM->RevokeCertificate(SockAuthToken,argsO.value("pub_key").toString(), user); + } + + if(ok){ return RestOutputStruct::OK; } + else{ return RestOutputStruct::BADREQUEST; } +} + //==== SYSCACHE ==== RestOutputStruct::ExitCode WebSocket::EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out){ //syscache only needs a list of sub-commands at the moment (might change later) diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 4547a69..b30d837 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -43,8 +43,14 @@ private: RestOutputStruct::ExitCode AvailableSubsystems(bool fullaccess, QJsonObject *out); // -- Main subsystem parser RestOutputStruct::ExitCode EvaluateBackendRequest(const RestInputStruct&, QJsonObject *out); + + // -- Individual subsystems + // -- Server Settings Modification API + RestOutputStruct::ExitCode EvaluateSysadmSettingsRequest(const QJsonValue in_args, QJsonObject *out); + // -- rpc syscache API RestOutputStruct::ExitCode EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out); + // -- rpc dispatcher API RestOutputStruct::ExitCode EvaluateDispatcherRequest(bool allaccess, const QJsonValue in_args, QJsonObject *out); // -- sysadm beadm API RestOutputStruct::ExitCode EvaluateSysadmBEADMRequest(const QJsonValue in_args, QJsonObject *out); From 4eaa321034ce43fc100c0c6c3ed1ea58ca15d8b8 Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Wed, 10 Feb 2016 19:27:30 -0600 Subject: [PATCH 19/24] Add new iocage API call to create jails. The `switches` object is only for specifying empty jails currently, but kept it vague as more may eventually happen. There are 2 different outputs depending on if a switch is supplied. Without any switches: REST Request: ------------------------------- PUT /sysadm/iocage { "action" : "createjail", "props" : "tag=test release=10.2-RELEASE" } WebSocket Request: ------------------------------- { "args" : { "props" : "tag=test release=10.2-RELEASE", "action" : "createjail" }, "namespace" : "sysadm", "name" : "iocage", "id" : "fooid" } Response: ------------------------------- { "args": { "createjail": { "props": "tag=test release=10.2-RELEASE", "success": { "Successfully created": " 3030c554-d05e-11e5-8209-d05099728dbf (test)" }, "switches": "" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } With switches: REST Request: ------------------------------- PUT /sysadm/iocage { "switches" : "-e", "action" : "createjail", "props" : "tag=emptytest" } WebSocket Request: ------------------------------- { "namespace" : "sysadm", "args" : { "props" : "tag=emptytest", "action" : "createjail", "switches" : "-e" }, "name" : "iocage", "id" : "fooid" } Response: ------------------------------- { "args": { "createjail": { "props": "tag=emptytest", "success": { "uuid": "1325b8bc-d05e-11e5-8209-d05099728dbf" }, "switches": "-e" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 10 ++++-- src/server/library/sysadm-iocage.cpp | 49 ++++++++++++++++++++++++++-- src/server/library/sysadm-iocage.h | 1 + 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 494aeca..6b755dc 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -33,7 +33,7 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO */ // - server settings (always available) out->insert("sysadm/settings","read/write"); - + // - syscache if(QFile::exists("/var/run/syscache.pipe")){ out->insert("rpc/syscache","read"); //no write to syscache - only reads @@ -150,9 +150,9 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmSettingsRequest(const QJsonV }else if(act=="revoke_ssl_cert" && keys.contains("pub_key") ){ //Additional arguments: "user" (optional), "pub_key" (String) QString user; if(keys.contains("user")){ user = argsO.value("user").toString(); } - ok = AUTHSYSTEM->RevokeCertificate(SockAuthToken,argsO.value("pub_key").toString(), user); + ok = AUTHSYSTEM->RevokeCertificate(SockAuthToken,argsO.value("pub_key").toString(), user); } - + if(ok){ return RestOutputStruct::OK; } else{ return RestOutputStruct::BADREQUEST; } } @@ -471,6 +471,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="createjail"){ + ok = true; + out->insert("createjail", sysadm::Iocage::createJail(in_args.toObject())); + } if(act=="clonejail"){ ok = true; out->insert("clonejail", sysadm::Iocage::cloneJail(in_args.toObject())); diff --git a/src/server/library/sysadm-iocage.cpp b/src/server/library/sysadm-iocage.cpp index 9b0133b..2ebd8be 100644 --- a/src/server/library/sysadm-iocage.cpp +++ b/src/server/library/sysadm-iocage.cpp @@ -12,6 +12,51 @@ using namespace sysadm; //PLEASE: Keep the functions in the same order as listed in pcbsd-general.h +// Create a jail on the box +QJsonObject Iocage::createJail(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + + // Get the key values + QString switches = jsin.value("switches").toString(); + QString props = jsin.value("props").toString(); + QStringList output; + + if ( keys.contains("switches" ) ) { + output = General::RunCommand("iocage create " + switches + " " + props).split("\n"); + } else { + output = General::RunCommand("iocage create " + props).split("\n"); + } + + QJsonObject vals; + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).isEmpty() ) + break; + + if ( output.at(i).indexOf("ERROR:") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } else { + QString key = output.at(i).simplified().section(":", 0, 0); + QString value = output.at(i).simplified().section(":", 1, 1); + + if ( keys.contains("switches" ) ) { + vals.insert("uuid", key); + } else { + vals.insert(key, value); + } + } + } + + retObject.insert("switches", switches); + retObject.insert("props", props); + retObject.insert("success", vals); + + return retObject; +} + // Clone a jail on the box QJsonObject Iocage::cloneJail(QJsonObject jsin) { QJsonObject retObject; @@ -38,8 +83,8 @@ QJsonObject Iocage::cloneJail(QJsonObject jsin) { retObject.insert("error", output.at(i)); return retObject; } else { - QString key = output.at(i).simplified().section(":", 0, 0); - QString value = output.at(i).simplified().section(":", 1, 1); + QString key = output.at(i).simplified().section(":", 0, 0); + QString value = output.at(i).simplified().section(":", 1, 1); vals.insert(key, value); } diff --git a/src/server/library/sysadm-iocage.h b/src/server/library/sysadm-iocage.h index e3e6471..52785c0 100644 --- a/src/server/library/sysadm-iocage.h +++ b/src/server/library/sysadm-iocage.h @@ -14,6 +14,7 @@ namespace sysadm{ class Iocage{ public: + static QJsonObject createJail(QJsonObject); static QJsonObject cloneJail(QJsonObject); static QJsonObject cleanAll(); static QJsonObject cleanTemplates(); From e2cede4fa09f3533d4537de2743bede2ed792287 Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Wed, 10 Feb 2016 19:56:43 -0600 Subject: [PATCH 20/24] Add new iocage API call to destroy jails. This uses the `force` switch to bypass interaction. REST Request: ------------------------------- PUT /sysadm/iocage { "action" : "destroyjail", "jail" : "test" } WebSocket Request: ------------------------------- { "args" : { "action" : "destroyjail", "jail" : "test" }, "name" : "iocage", "id" : "fooid", "namespace" : "sysadm" } Response: ------------------------------- { "args": { "destroyjail": { "success": { "Destroying": " 3030c554-d05e-11e5-8209-d05099728dbf" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 +++ src/server/library/sysadm-iocage.cpp | 38 ++++++++++++++++++++++++++++ src/server/library/sysadm-iocage.h | 1 + 3 files changed, 43 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 6b755dc..1273c1d 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -471,6 +471,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="destroyjail"){ + ok = true; + out->insert("destroyjail", sysadm::Iocage::destroyJail(in_args.toObject())); + } if(act=="createjail"){ ok = true; out->insert("createjail", sysadm::Iocage::createJail(in_args.toObject())); diff --git a/src/server/library/sysadm-iocage.cpp b/src/server/library/sysadm-iocage.cpp index 2ebd8be..589f8c6 100644 --- a/src/server/library/sysadm-iocage.cpp +++ b/src/server/library/sysadm-iocage.cpp @@ -12,6 +12,44 @@ using namespace sysadm; //PLEASE: Keep the functions in the same order as listed in pcbsd-general.h +// Destroy a jail on the box +QJsonObject Iocage::destroyJail(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("jail") ) { + retObject.insert("error", "Missing required keys"); + return retObject; + } + + // Get the key values + QString jail = jsin.value("jail").toString(); + QStringList output; + + output = General::RunCommand("iocage destroy -f " + jail).split("\n"); + + QJsonObject vals; + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).isEmpty() ) + break; + + if ( output.at(i).indexOf("ERROR:") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } else { + QString key = output.at(i).simplified().section(":", 0, 0); + QString value = output.at(i).simplified().section(":", 1, 1); + + vals.insert(key, value); + } + } + + retObject.insert("success", vals); + + return retObject; +} + // Create a jail on the box QJsonObject Iocage::createJail(QJsonObject jsin) { QJsonObject retObject; diff --git a/src/server/library/sysadm-iocage.h b/src/server/library/sysadm-iocage.h index 52785c0..e393abf 100644 --- a/src/server/library/sysadm-iocage.h +++ b/src/server/library/sysadm-iocage.h @@ -14,6 +14,7 @@ namespace sysadm{ class Iocage{ public: + static QJsonObject destroyJail(QJsonObject); static QJsonObject createJail(QJsonObject); static QJsonObject cloneJail(QJsonObject); static QJsonObject cleanAll(); From 76dec01bb45f7025caa7e281b0e4489457196b2c Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Wed, 10 Feb 2016 20:14:03 -0600 Subject: [PATCH 21/24] Add new iocage API call to list resource usage for jails. REST Request: ------------------------------- PUT /sysadm/iocage { "action" : "df" } WebSocket Request: ------------------------------- { "namespace" : "sysadm", "name" : "iocage", "id" : "fooid", "args" : { "action" : "df" } } Response: ------------------------------- { "args": { "df": { "f250ab25-d062-11e5-8209-d05099728dbf": { "ava": "83.4G", "crt": "2.30x", "qta": "none", "res": "none", "tag": "test", "use": "1.69M" }, "f39318ae-d064-11e5-8209-d05099728dbf": { "ava": "83.4G", "crt": "2.30x", "qta": "none", "res": "none", "tag": "test2", "use": "1.69M" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 ++++ src/server/library/sysadm-iocage.cpp | 35 ++++++++++++++++++++++++++++ src/server/library/sysadm-iocage.h | 1 + 3 files changed, 40 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 1273c1d..b107e7a 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -471,6 +471,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="df"){ + ok = true; + out->insert("df", sysadm::Iocage::df()); + } if(act=="destroyjail"){ ok = true; out->insert("destroyjail", sysadm::Iocage::destroyJail(in_args.toObject())); diff --git a/src/server/library/sysadm-iocage.cpp b/src/server/library/sysadm-iocage.cpp index 589f8c6..50f973e 100644 --- a/src/server/library/sysadm-iocage.cpp +++ b/src/server/library/sysadm-iocage.cpp @@ -12,6 +12,41 @@ using namespace sysadm; //PLEASE: Keep the functions in the same order as listed in pcbsd-general.h +// Show resource usage for jails on the box +QJsonObject Iocage::df() { + QJsonObject retObject; + + // Get the key values + QStringList output = General::RunCommand("iocage df").split("\n"); + QJsonObject vals; + + for ( int i = 0; i < output.size(); i++) + { + // Null output at first + if ( output.at(i).isEmpty() ) + continue; + + QJsonObject jail; + QString line = output.at(i).simplified(); + QString uuid = line.section(" ", 0, 0); + + // Otherwise we get a list of what we already know. + if ( line.section(" ", 0, 0) == "UUID" ) + continue; + + jail.insert("crt", line.section(" ", 1, 1)); + jail.insert("res", line.section(" ", 2, 2)); + jail.insert("qta", line.section(" ", 3, 3)); + jail.insert("use", line.section(" ", 4, 4)); + jail.insert("ava", line.section(" ", 5, 5)); + jail.insert("tag", line.section(" ", 6, 6)); + + retObject.insert(uuid, jail); + } + + return retObject; +} + // Destroy a jail on the box QJsonObject Iocage::destroyJail(QJsonObject jsin) { QJsonObject retObject; diff --git a/src/server/library/sysadm-iocage.h b/src/server/library/sysadm-iocage.h index e393abf..50435ef 100644 --- a/src/server/library/sysadm-iocage.h +++ b/src/server/library/sysadm-iocage.h @@ -14,6 +14,7 @@ namespace sysadm{ class Iocage{ public: + static QJsonObject df(); static QJsonObject destroyJail(QJsonObject); static QJsonObject createJail(QJsonObject); static QJsonObject cloneJail(QJsonObject); From 9e3ab39de4ce3c5845ba7633a05243f4d7279a27 Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Wed, 10 Feb 2016 20:24:53 -0600 Subject: [PATCH 22/24] Add new iocage API call to execute a process on a jail. REST Request: ------------------------------- PUT /sysadm/iocage { "action" : "execjail", "jail" : "test", "command" : "echo hi", "user" : "root" } WebSocket Request: ------------------------------- { "namespace" : "sysadm", "name" : "iocage", "args" : { "user" : "root", "action" : "execjail", "jail" : "test", "command" : "echo hi" }, "id" : "fooid" } Response: ------------------------------- { "args": { "execjail": { "success": { "hi": "" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/WebBackend.cpp | 4 +++ src/server/library/sysadm-iocage.cpp | 43 ++++++++++++++++++++++++++++ src/server/library/sysadm-iocage.h | 1 + 3 files changed, 48 insertions(+) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index b107e7a..1092fdd 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -471,6 +471,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIocageRequest(const QJsonVal bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="execjail"){ + ok = true; + out->insert("execjail", sysadm::Iocage::execJail(in_args.toObject())); + } if(act=="df"){ ok = true; out->insert("df", sysadm::Iocage::df()); diff --git a/src/server/library/sysadm-iocage.cpp b/src/server/library/sysadm-iocage.cpp index 50f973e..d1d2d7b 100644 --- a/src/server/library/sysadm-iocage.cpp +++ b/src/server/library/sysadm-iocage.cpp @@ -12,6 +12,49 @@ using namespace sysadm; //PLEASE: Keep the functions in the same order as listed in pcbsd-general.h +// Execute a process in a jail on the box +QJsonObject Iocage::execJail(QJsonObject jsin) { + QJsonObject retObject; + + QStringList keys = jsin.keys(); + if (! keys.contains("jail") + || ! keys.contains("user") + || ! keys.contains("command") ) { + retObject.insert("error", "Missing required keys"); + return retObject; + } + + // Get the key values + QString jail = jsin.value("jail").toString(); + QString user = jsin.value("user").toString(); + QString command = jsin.value("command").toString(); + + QStringList output; + + output = General::RunCommand("iocage exec -U " + user + " " + jail + " " + command).split("\n"); + + QJsonObject vals; + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).isEmpty() ) + break; + + if ( output.at(i).indexOf("execvp:") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } else { + QString key = output.at(i).simplified().section(":", 0, 0); + QString value = output.at(i).simplified().section(":", 1, 1); + + vals.insert(key, value); + } + } + + retObject.insert("success", vals); + + return retObject; +} + // Show resource usage for jails on the box QJsonObject Iocage::df() { QJsonObject retObject; diff --git a/src/server/library/sysadm-iocage.h b/src/server/library/sysadm-iocage.h index 50435ef..6eb59f0 100644 --- a/src/server/library/sysadm-iocage.h +++ b/src/server/library/sysadm-iocage.h @@ -14,6 +14,7 @@ namespace sysadm{ class Iocage{ public: + static QJsonObject execJail(QJsonObject); static QJsonObject df(); static QJsonObject destroyJail(QJsonObject); static QJsonObject createJail(QJsonObject); From 621b2d6233595efaabc24e1a83a637fa4537948a Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Wed, 10 Feb 2016 21:49:26 -0600 Subject: [PATCH 23/24] Change iocage API call getjailsettings to align better with iocage syntax. The new behavior has 4 modes. The user can specify a prop + jail, -r and a prop, all + jail, or just jail. For prop + jail: REST Request: ------------------------------- PUT /sysadm/iocage { "jail" : "test", "action" : "getjailsettings", "prop" : "vnet" } WebSocket Request: ------------------------------- { "name" : "iocage", "id" : "fooid", "namespace" : "sysadm", "args" : { "prop" : "vnet", "action" : "getjailsettings", "jail" : "test" } } Response: ------------------------------- { "args": { "getjailsettings": { "test": { "vnet": "off" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } -r and a prop: REST Request: ------------------------------- PUT /sysadm/iocage { "switches" : "-r", "prop" : "vnet", "action" : "getjailsettings" } WebSocket Request: ------------------------------- { "name" : "iocage", "namespace" : "sysadm", "args" : { "prop" : "vnet", "action" : "getjailsettings", "switches" : "-r" }, "id" : "fooid" } Response: ------------------------------- { "args": { "getjailsettings": { "9b8e1033-d065-11e5-8209-d05099728dbf": { "TAG": "test", "vnet": "off" }, "b67065a9-cfb9-11e5-8209-d05099728dbf": { "TAG": "2016-02-09@23:47:04", "vnet": "off" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } all + jail (or just jail, they are identical outputs.) : REST Request: ------------------------------- PUT /sysadm/iocage { "jail" : "test", "action" : "getjailsettings", "prop" : "all" } WebSocket Request: ------------------------------- { "id" : "fooid", "name" : "iocage", "namespace" : "sysadm", "args" : { "jail" : "test", "action" : "getjailsettings", "prop" : "all" } } Response: ------------------------------- { "args": { "getjailsettings": { "test": { "allow_chflags": "0", "allow_mount": "0", "allow_mount_devfs": "0", "allow_mount_nullfs": "0", "allow_mount_procfs": "0", "allow_mount_tmpfs": "0", "allow_mount_zfs": "0", "allow_quotas": "0", "allow_raw_sockets": "0", "allow_set_hostname": "1", "allow_socket_af": "0", "allow_sysvipc": "0", "available": "83.4G", "boot": "off", "bpf": "off", "branch": "-", "children_max": "0", "compression": "lz4", "compressratio": "2.27x", "coredumpsize": "off", "count": "1", "cpuset": "off", "cputime": "off", "datasize": "off", "dedup": "off", "defaultrouter": "none", "defaultrouter6": "none", "devfs_ruleset": "4", "dhcp": "off", "enforce_statfs": "2", "exec_clean": "1", "exec_fib": "0", "exec_jail_user": "root", "exec_poststart": "/usr/bin/true", "exec_poststop": "/usr/bin/true", "exec_prestart": "/usr/bin/true", "exec_prestop": "/usr/bin/true", "exec_start": "/bin/sh /etc/rc", "exec_stop": "/bin/sh /etc/rc.shutdown", "exec_system_jail_user": "0", "exec_system_user": "root", "exec_timeout": "60", "ftpdir": "-", "ftpfiles": "-", "ftphost": "-", "ftplocaldir": "-", "gitlocation": "https", "hack88": "0", "host_domainname": "none", "host_hostname": "9b8e1033-d065-11e5-8209-d05099728dbf", "host_hostuuid": "9b8e1033-d065-11e5-8209-d05099728dbf", "hostid": "a60db2df-3c0e-11e5-8986-d05099728dbf", "interfaces": "vnet0", "ip4": "new", "ip4_addr": "none", "ip4_autoend": "none", "ip4_autostart": "none", "ip4_autosubnet": "none", "ip4_saddrsel": "1", "ip6": "new", "ip6_addr": "none", "ip6_saddrsel": "1", "istemplate": "no", "jail_zfs": "off", "jail_zfs_dataset": "iocage/jails/9b7f1420-d065-11e5-8209-d05099728dbf/data", "jail_zfs_mountpoint": "none", "last_started": "2016-02-10_20", "login_flags": "-f root", "maxproc": "off", "memorylocked": "off", "memoryuse": "8G", "mount_devfs": "1", "mount_fdescfs": "1", "mount_linprocfs": "0", "mount_procfs": "0", "mountpoint": "/iocage/jails/9b8e1033-d065-11e5-8209-d05099728dbf", "msgqqueued": "off", "msgqsize": "off", "nmsgq": "off", "notes": "none", "nsemop": "off", "nshm": "off", "nthr": "off", "openfiles": "off", "origin": "-", "owner": "root", "pcpu": "off", "pkglist": "none", "priority": "99", "pseudoterminals": "off", "quota": "none", "release": "10.2-RELEASE", "reservation": "none", "resolver": "none", "rlimits": "off", "securelevel": "2", "shmsize": "off", "stacksize": "off", "start": "-", "stop_timeout": "30", "swapuse": "off", "sync_stat": "-", "sync_target": "none", "sync_tgt_zpool": "none", "tag": "test", "template": "-", "type": "basejail", "used": "1.76M", "vmemoryuse": "off", "vnet": "off", "vnet0_mac": "none", "vnet1_mac": "none", "vnet2_mac": "none", "vnet3_mac": "none", "wallclock": "off" } } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/server/library/sysadm-iocage.cpp | 61 ++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/src/server/library/sysadm-iocage.cpp b/src/server/library/sysadm-iocage.cpp index d1d2d7b..97ec8b0 100644 --- a/src/server/library/sysadm-iocage.cpp +++ b/src/server/library/sysadm-iocage.cpp @@ -492,17 +492,33 @@ QJsonObject Iocage::getDefaultSettings() { // Return all of the jail settings QJsonObject Iocage::getJailSettings(QJsonObject jsin) { QJsonObject retObject; - - QStringList keys = jsin.keys(); - if (! keys.contains("jail") ) { - retObject.insert("error", "Missing required keys"); - return retObject; - } + QStringList output; // Get the key values QString jail = jsin.value("jail").toString(); + QString prop = jsin.value("prop").toString(); - QStringList output = General::RunCommand("iocage get all " + jail).split("\n"); + QString switches = jsin.value("switches").toString(); + + QStringList keys = jsin.keys(); + if (! keys.contains("jail") + && keys.contains("prop") + && keys.contains("switches") ) { + output = General::RunCommand("iocage get " + switches + " " + prop).split("\n"); + } else if ( ! keys.contains("jail") + && ! keys.contains("prop") + && ! keys.contains("switches") ){ + retObject.insert("error", "Missing required keys"); + return retObject; + } + + if ( ! keys.contains("prop") + && ! keys.contains("switches") ) { + output = General::RunCommand("iocage get all " + jail).split("\n"); + } else if ( keys.contains("prop") + && ! keys.contains("switches") ) { + output = General::RunCommand("iocage get " + prop + " " + jail).split("\n"); + } QJsonObject vals; for ( int i = 0; i < output.size(); i++) @@ -513,13 +529,42 @@ QJsonObject Iocage::getJailSettings(QJsonObject jsin) { if ( output.at(i).isEmpty() ) break; + if ( output.at(i).indexOf("ERROR:") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } else { QString key = output.at(i).simplified().section(":", 0, 0); QString value = output.at(i).simplified().section(":", 1, 1); + if ( keys.contains("switches" ) ) { + QString line = output.at(i).simplified(); + + // Otherwise we get a list of what we already know. + if ( line.section(" ", 0, 0) == "UUID" ) + continue; + + QJsonObject jail; + QString uuid = line.section(" ", 0, 0); + + jail.insert("TAG", line.section(" ", 1, 1)); + jail.insert(prop, line.section(" ", 2, 2)); + retObject.insert(uuid, jail); + continue; + } + + if ( keys.contains("prop" ) + && ! keys.contains("switches") + && prop != "all") { + vals.insert(prop, key); + retObject.insert(jail, vals); + continue; + } + vals.insert(key, value); + retObject.insert(jail, vals); + } } - retObject.insert(jail, vals); return retObject; } From 750f7c8c8527e558440d22a73418f321b83986a4 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Thu, 11 Feb 2016 09:18:09 -0500 Subject: [PATCH 24/24] Turn off some debugging which was pringting the user/pass combo to the log file. --- src/server/AuthorizationManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/AuthorizationManager.cpp b/src/server/AuthorizationManager.cpp index fd21cb2..2a20164 100644 --- a/src/server/AuthorizationManager.cpp +++ b/src/server/AuthorizationManager.cpp @@ -135,7 +135,7 @@ QString AuthorizationManager::LoginUP(QHostAddress host, QString user, QString p return ""; //user not allowed access if not in either of the wheel/operator groups } }else{ isOperator = true; } - qDebug() << "Check username/password" << user << pass << localhost; + //qDebug() << "Check username/password" << user << pass << localhost; //Need to run the full username/password through PAM if(!localhost || user=="root" || user=="toor"){ ok = pam_checkPW(user,pass); @@ -171,7 +171,7 @@ QString AuthorizationManager::LoginUC(QHostAddress host, QString user, QList