This commit is contained in:
JoshDW19
2016-02-11 11:12:47 -05:00
15 changed files with 1111 additions and 27 deletions

View File

@@ -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", "createbe" |
| | | |
+---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+
@@ -137,4 +137,105 @@ 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"
}
.. 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". 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**
.. 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"
}

View File

@@ -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) <https://github.com/iocage/iocage/blob/master/iocage.8.txt>`_.
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:

View File

@@ -20,7 +20,8 @@ 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", "issetup", "create", "install", |
| | | "start", "stop" |
| | | |
+---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+
@@ -244,4 +245,300 @@ 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"
}
.. 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"
}
.. 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"
}
.. 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 <name>` 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"
}
.. 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:
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"
}

View File

@@ -68,6 +68,52 @@ 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;
}
void AuthorizationManager::ListCertificates(QString token, QJsonObject *out){
QStringList keys; //Format: "RegisteredCerts/<user>/<key>"
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; i<keys.length(); i++){
if(username!=keys[i].section("/",1,1)){
if(!user.isEmpty()){ out->insert(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()){ out->insert(username, user); }
}
//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
@@ -89,14 +135,14 @@ 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);
}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,12 +154,55 @@ 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<QSslCertificate> 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; i<certs.length() && !ok; i++){
if(CONFIG->contains("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) );
bool localhost = ( (host== QHostAddress::LocalHost) || (host== QHostAddress::LocalHostIPv6) || (host.toString()=="::ffff:127.0.0.1") );
//Login a particular automated service
qDebug() << "Service Login Attempt:" << service << " Success:" << localhost;
@@ -136,13 +225,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<TOKENLENGTH; i++){
tok.append( AUTHCHARS.at( qrand() % AUTHCHARS.length() ) );
@@ -150,10 +239,10 @@ QString AuthorizationManager::generateNewToken(bool isOp){
if( !hashID(tok).isEmpty() ){
//Just in case the randomizer came up with something identical - re-run it
tok = generateNewToken(isOp);
tok = generateNewToken(isOp, user);
}else{
//unique token created - add it to the hash with the current time (+timeout)
QString id = tok + "::::"+(isOp ? "operator" : "user"); //append operator status to auth key
QString id = tok + "::::"+(isOp ? "operator" : "user")+"::::"+user; //append operator status to auth key
HASH.insert(id, QDateTime::currentDateTime().addSecs(TIMEOUTSECS) );
}
return tok;

View File

@@ -19,17 +19,23 @@ 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 (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
void ListCertificates(QString token, QJsonObject *out);
int checkAuthTimeoutSecs(QString token); //Return the number of seconds that a token is valid for
// == Token Generation functions
QString LoginUP(QHostAddress host, QString user, QString pass); //Login w/ username & password
QString LoginUC(QHostAddress host, QString user, QList<QSslCertificate> certs); //Login w/ username & SSL certificate
QString LoginService(QHostAddress host, QString service); //Login a particular automated service
private:
QHash<QString, QDateTime> HASH;
QHash <QString, QDateTime> IPFAIL;
QString generateNewToken(bool isOperator);
QString generateNewToken(bool isOperator, QString name);
QStringList getUserGroups(QString user);
//Failure count management

View File

@@ -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);

View File

@@ -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);

View File

@@ -31,6 +31,8 @@ RestOutputStruct::ExitCode WebSocket::AvailableSubsystems(bool allaccess, QJsonO
<namespace2/name2> : <read/write/other>,
}
*/
// - server settings (always available)
out->insert("sysadm/settings","read/write");
// - syscache
if(QFile::exists("/var/run/syscache.pipe")){
@@ -41,7 +43,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
@@ -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<QSslCertificate> certs;
if(SOCKET!=0){ certs = SOCKET->sslConfiguration().peerCertificateChain(); }
else if(TSOCKET!=0){ certs = TSOCKET->peerCertificateChain(); }
for(int i=0; i<certs.length() && !ok; i++){
if(certs[i].publicKey().toPem()==pub_key){
//Certificate found - register it
ok = AUTHSYSTEM->RegisterCertificate(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)
@@ -436,6 +474,26 @@ 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());
}
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()));
}
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());
@@ -504,6 +562,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());
@@ -512,6 +574,14 @@ 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());
}
if(act=="renameiso"){
ok = true;
out->insert("renameiso", sysadm::Iohyve::renameISO(in_args.toObject()));
@@ -520,6 +590,18 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmIohyveRequest(const QJsonVal
ok = true;
out->insert("rmiso", sysadm::Iohyve::rmISO(in_args.toObject()));
}
if(act=="setup"){
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()));
}
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

View File

@@ -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<QSslCertificate> 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"){

View File

@@ -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);

View File

@@ -12,6 +12,207 @@ 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;
// 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;
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;
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;
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;
@@ -291,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++)
@@ -312,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;
}

View File

@@ -14,6 +14,11 @@ namespace sysadm{
class Iocage{
public:
static QJsonObject execJail(QJsonObject);
static QJsonObject df();
static QJsonObject destroyJail(QJsonObject);
static QJsonObject createJail(QJsonObject);
static QJsonObject cloneJail(QJsonObject);
static QJsonObject cleanAll();
static QJsonObject cleanTemplates();
static QJsonObject cleanReleases();

View File

@@ -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;
@@ -39,6 +69,49 @@ 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;
// 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() {
@@ -156,3 +229,65 @@ 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;
}
// 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;
}

View File

@@ -14,11 +14,16 @@ namespace sysadm{
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);
static QJsonObject rmISO(QJsonObject);
static QJsonObject setupIohyve(QJsonObject);
static QJsonObject startGuest(QJsonObject);
static QJsonObject stopGuest(QJsonObject);
};
} //end of pcbsd namespace

View File

@@ -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);