Skip to content

Commit

Permalink
ONEM-19817: Extend OCIContainer Thunder Plugin to support crypted
Browse files Browse the repository at this point in the history
Integration with omi
  • Loading branch information
Marcin Hajkowski authored and mikolaj-staworzynski-red committed Mar 29, 2022
1 parent cad4acc commit acfd215
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 1 deletion.
15 changes: 14 additions & 1 deletion OCIContainer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ find_package_handle_standard_args(
target_include_directories(${MODULE_NAME}
PRIVATE
../helpers

)

target_link_libraries(${MODULE_NAME}
Expand All @@ -55,6 +54,20 @@ target_link_libraries(${MODULE_NAME}
${NAMESPACE}Plugins::${NAMESPACE}Plugins
${SYSTEMD_LIBRARIES}
)


find_library(OMIPROXY omiclientlib)
if(OMIPROXY)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I=/usr/include/glib-2.0 -I=/usr/lib/glib-2.0/include")
target_link_libraries (${MODULE_NAME} PRIVATE omiclientlib)

else()

target_include_directories(${MODULE_NAME} PRIVATE stubs)

endif (OMIPROXY)

# ${NAMESPACE}Protocols::${NAMESPACE}Protocols
install(TARGETS ${MODULE_NAME}
DESTINATION lib/${STORAGE_DIRECTORY}/plugins)
Expand Down
146 changes: 146 additions & 0 deletions OCIContainer/OCIContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <Dobby/DobbyProxy.h>
#include <Dobby/IpcService/IpcFactory.h>
#include <omi_proxy.hpp>


namespace WPEFramework
Expand All @@ -22,6 +23,7 @@ OCIContainer::OCIContainer()
Register("getContainerState", &OCIContainer::getContainerState, this);
Register("getContainerInfo", &OCIContainer::getContainerInfo, this);
Register("startContainer", &OCIContainer::startContainer, this);
Register("startContainerFromCryptedBundle", &OCIContainer::startContainerFromCryptedBundle, this);
Register("startContainerFromDobbySpec", &OCIContainer::startContainerFromDobbySpec, this);
Register("stopContainer", &OCIContainer::stopContainer, this);
Register("pauseContainer", &OCIContainer::pauseContainer, this);
Expand All @@ -31,6 +33,7 @@ OCIContainer::OCIContainer()

OCIContainer::~OCIContainer()
{

}

const string OCIContainer::Initialize(PluginHost::IShell *service)
Expand All @@ -55,16 +58,22 @@ const string OCIContainer::Initialize(PluginHost::IShell *service)
// Register a state change event listener
mEventListenerId = mDobbyProxy->registerListener(stateListener, static_cast<const void*>(this));

mOmiProxy = std::make_shared<omi::OmiProxy>();

mOmiListenerId = mOmiProxy->registerListener(omiErrorListener, static_cast<const void*>(this));

return string();
}

void OCIContainer::Deinitialize(PluginHost::IShell *service)
{
mDobbyProxy->unregisterListener(mEventListenerId);
mOmiProxy->unregisterListener(mOmiListenerId);
Unregister("listContainers");
Unregister("getContainerState");
Unregister("getContainerInfo");
Unregister("startContainer");
Unregister("startContainerFromCryptedBundle");
Unregister("startContainerFromDobbySpec");
Unregister("stopContainer");
Unregister("pauseContainer");
Expand Down Expand Up @@ -269,6 +278,79 @@ uint32_t OCIContainer::startContainer(const JsonObject &parameters, JsonObject &
returnResponse(true);
}

/**
* @brief Starts a container from a crypted OCI bundle
*
* @param[in] parameters - Must include 'containerId', 'rootFSPath' and 'configFilePath'.
* @param[out] response - Dobby descriptor of the started container.
*
* @return A code indicating success.
*/
uint32_t OCIContainer::startContainerFromCryptedBundle(const JsonObject &parameters, JsonObject &response)
{
LOGINFO("Start container from crypted OCI bundle");

// To start a container, we need a path to an OCI bundle and an ID for the container
returnIfStringParamNotFound(parameters, "containerId");
returnIfStringParamNotFound(parameters, "rootFSPath");
returnIfStringParamNotFound(parameters, "configFilePath");

std::string id = parameters["containerId"].String();
std::string rootfsPath = parameters["rootFSPath"].String();
std::string configPath = parameters["configFilePath"].String();
std::string command = parameters["command"].String();
std::string westerosSocket = parameters["westerosSocket"].String();

// Can be used to pass file descriptors to container construction.
// Currently unsupported, see DobbyProxy::startContainerFromBundle().
std::list<int> emptyList;

int descriptor;

std::string containerPath;

if (!mOmiProxy->mountCryptedBundle(id,
rootfsPath,
configPath,
containerPath))
{
LOGERR("Failed to start container - sync mount request to omi failed.");
returnResponse(false);
}


LOGINFO("Mount request to omi succeeded, contenerPath: %s", containerPath.c_str());

// If no additional arguments, start the container
if ((command == "null" || command.empty()) && (westerosSocket == "null" || westerosSocket.empty()))
{
descriptor = mDobbyProxy->startContainerFromBundle(id, containerPath, emptyList);
}
else
{
// Dobby expects empty strings if values not set
if (command == "null" || command.empty())
{
command = "";
}
if (westerosSocket == "null" || westerosSocket.empty())
{
westerosSocket = "";
}
descriptor = mDobbyProxy->startContainerFromBundle(id, containerPath, emptyList, command, westerosSocket);
}

// startContainer returns -1 on failure
if (descriptor <= 0)
{
LOGERR("Failed to start container - internal Dobby error.");
returnResponse(false);
}

response["descriptor"] = descriptor;
returnResponse(true);
}

/**
* @brief Starts a container using a Dobby spec file
*
Expand Down Expand Up @@ -500,6 +582,15 @@ void OCIContainer::onContainerStarted(int32_t descriptor, const std::string& nam
*/
void OCIContainer::onContainerStopped(int32_t descriptor, const std::string& name)
{
if (!mOmiProxy->umountCryptedBundle(name))
{
LOGERR("Failed to umount container %s - sync unmount request to omi failed.", name.c_str());
}
else
{
LOGINFO("Container %s properly unmounted.", name.c_str());
}

JsonObject params;
params["descriptor"] = std::to_string(descriptor);
params["name"] = name;
Expand Down Expand Up @@ -570,6 +661,61 @@ const void OCIContainer::stateListener(int32_t descriptor, const std::string& na
LOGINFO("Received an unknown state event for container '%s'.", name.c_str());
}
}

/**
* @brief Callback listener for OMI error events.
*
* @param id Container id
* @param err Error type
* @param _this Callback parameters, or in this case, the pointer to 'this'
*/
const void OCIContainer::omiErrorListener(const std::string& id, omi::IOmiProxy::ErrorType err, const void* _this)
{

// Cast const void* back to OCIContainer* type to get 'this'
OCIContainer* __this = const_cast<OCIContainer*>(reinterpret_cast<const OCIContainer*>(_this));

if (err == omi::IOmiProxy::ErrorType::verityFailed)
{
__this->onVerityFailed(id);
}
else
{
LOGINFO("Received an unknown error type from OMI");
}
}

/**
* @brief Handle failure of verity check for container.
*
* @param name Container name.
*/
void OCIContainer::onVerityFailed(const std::string& name)
{
LOGINFO("Handle onVerityFailed");
int cd = GetContainerDescriptorFromId(name);
if (cd < 0)
{
LOGERR("Failed to acquire container descriptor - cannot stop container due to verity failure");
return;
}

JsonObject params;
params["descriptor"] = cd;
params["name"] = name;
// Set error type to Verity Error (1)
params["errorCode"] = 1;
sendNotify("onContainerFailed", params);

bool stoppedSuccessfully = mDobbyProxy->stopContainer(cd, true);

if (!stoppedSuccessfully)
{
LOGERR("Failed to stop container - internal Dobby error.");
return;
}
}

// End Internal methods

} // namespace Plugin
Expand Down
6 changes: 6 additions & 0 deletions OCIContainer/OCIContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <vector>
#include <map>
#include <i_omi_proxy.hpp>

namespace WPEFramework
{
Expand Down Expand Up @@ -39,6 +40,7 @@ class OCIContainer : public PluginHost::IPlugin, public PluginHost::JSONRPC
uint32_t getContainerState(const JsonObject &parameters, JsonObject &response);
uint32_t getContainerInfo(const JsonObject &parameters, JsonObject &response);
uint32_t startContainer(const JsonObject &parameters, JsonObject &response);
uint32_t startContainerFromCryptedBundle(const JsonObject &parameters, JsonObject &response);
uint32_t startContainerFromDobbySpec(const JsonObject &parameters, JsonObject &response);
uint32_t stopContainer(const JsonObject &parameters, JsonObject &response);
uint32_t pauseContainer(const JsonObject &parameters, JsonObject &response);
Expand All @@ -49,6 +51,7 @@ class OCIContainer : public PluginHost::IPlugin, public PluginHost::JSONRPC
//Begin events
void onContainerStarted(int32_t descriptor, const std::string& name);
void onContainerStopped(int32_t descriptor, const std::string& name);
void onVerityFailed(const std::string& name);
//End events

//Build QueryInterface implementation, specifying all possible interfaces to be returned.
Expand All @@ -66,10 +69,13 @@ class OCIContainer : public PluginHost::IPlugin, public PluginHost::JSONRPC

private:
int mEventListenerId; // Dobby event listener ID
long unsigned mOmiListenerId;
std::shared_ptr<IDobbyProxy> mDobbyProxy; // DobbyProxy instance
std::shared_ptr<AI_IPC::IIpcService> mIpcService; // Ipc Service instance
const int GetContainerDescriptorFromId(const std::string& containerId);
static const void stateListener(int32_t descriptor, const std::string& name, IDobbyProxyEvents::ContainerState state, const void* _this);
static const void omiErrorListener(const std::string& id, omi::IOmiProxy::ErrorType err, const void* _this);
std::shared_ptr<omi::IOmiProxy> mOmiProxy;
};
} // namespace Plugin
} // namespace WPEFramework
57 changes: 57 additions & 0 deletions OCIContainer/OCIContainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,63 @@
]
}
},
"startContainerFromCryptedBundle":{
"summary": "Starts a new container from an existing encrypted OCI bundle",
"params": {
"type": "object",
"properties": {
"containerId": {
"$ref": "#/definitions/containerId"
},
"rootFSPath": {
"summary": "Path to the enrypted OCI bundle containing the rootfs to use to create the container",
"type": "string",
"example": "/containers/rootfs/myBundle"
},
"configFilePath": {
"summary": "Path to the enrypted OCI bundle containing the config file to use to create the container",
"type": "string",
"example": "/containers/var/myBundle"
},
"command": {
"$ref": "#/definitions/command"
},
"westerosSocket":{
"summary": "Path to a Westeros socket to mount inside the container",
"type": "string",
"example": "/usr/mySocket"
},
"envvar": {
"summary": "A list of environment variables to add to the container",
"type": "array",
"items": {
"type": "string",
"example": "FOO=BAR"
}
}
},
"required": [
"containerId",
"rootFSPath",
"configFilePath"
]
},
"result": {
"type": "object",
"properties": {
"descriptor": {
"$ref": "#/definitions/Descriptor"
},
"success":{
"$ref": "#/definitions/success"
}
},
"required": [
"descriptor",
"success"
]
}
},
"startContainerFromDobbySpec":{
"summary": "Starts a new container from a legacy Dobby JSON specification.\n \n### Events \n| Event | Description | \n| :----------- | :----------- | \n| `onContainerStarted` | Triggers when a new container starts running.|",
"events": [
Expand Down
53 changes: 53 additions & 0 deletions OCIContainer/stubs/i_omi_proxy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2021, LIBERTY GLOBAL all rights reserved.
*/

#ifndef I_OMI_PROXY_HPP_
#define I_OMI_PROXY_HPP_

#include <string>
#include <functional>

namespace omi
{

/**
* @interface IOmiProxy
* @brief Wrapper around an omi_dbus_api that provides simpler method
* calls and give possibility to register/unregister for incoming signals.
*/
class IOmiProxy
{
public:
enum class ErrorType { verityFailed };

typedef std::function<void(const std::string&, ErrorType, const void*)> OmiErrorListener;

// Mount crypted bundle
// id [IN] - Container ID in reverse domain name notation
// rootfs_file_path [IN] - Absolute pathname for filesystem image
// config_json_path [IN] - Absolute pathname for config.json.jwt
// bundlePath [OUT] - Absolute pathname for decrypted config.json payload
// Returns TRUE on success, FALSE on error
virtual bool mountCryptedBundle(const std::string& id,
const std::string& rootfs_file_path,
const std::string& config_json_path,
std::string& bundlePath /*out parameter*/) = 0;

// Unmount crypted bundle
// id [IN] - Container ID in reverse domain name notation
// Returns TRUE on success, FALSE on error
virtual bool umountCryptedBundle(const std::string& id) = 0;

// Register listener
// listener [IN] - OMI error listener which will be called on error event occurrence
// Returns listener ID (0<) or 0 on error
virtual long unsigned registerListener(const OmiErrorListener &listener, const void* cbParams) = 0;

// Unregister listener
// tag [IN] - ID which defines listener to be unregistered
virtual void unregisterListener(long unsigned tag) = 0;

};
} // namespace omi
#endif // #ifndef I_OMI_PROXY_HPP_
Loading

0 comments on commit acfd215

Please sign in to comment.