From 61e1a446576fc294a9d7f01b197afbcae91540a9 Mon Sep 17 00:00:00 2001
From: Piotr Bartosiewicz
Date: Wed, 10 Sep 2014 13:35:50 +0200
Subject: [PATCH 01/16] Unit test cleanup
[Bug/Feature] Update unit test of configuration to test changes in
libConfig (added missing piece of code).
[Cause] N/A
[Solution] N/A
[Verification] Build, install, run tests
Change-Id: Ifab57f3537cfcc4afa046608bd337b5386099b6e
---
tests/unit_tests/config/ut-configuration.cpp | 69 +++-------------------------
1 file changed, 6 insertions(+), 63 deletions(-)
diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp
index 6220a58..1e98d22 100644
--- a/tests/unit_tests/config/ut-configuration.cpp
+++ b/tests/unit_tests/config/ut-configuration.cpp
@@ -285,76 +285,19 @@ BOOST_AUTO_TEST_CASE(HasVisibleInternalHelperTest)
BOOST_CHECK(isVisitable());
}
-namespace saveLoadKVStoreTest {
-
-// This struct is like TestConfig, but without a list of structures.
-struct PoorTestConfig {
- // subtree class
- struct SubConfig {
-
- struct SubSubConfig {
- int intVal;
-
- CONFIG_REGISTER
- (
- intVal
- )
- };
-
- int intVal;
- SubSubConfig subSubObj;
-
- CONFIG_REGISTER
- (
- intVal,
- subSubObj
- )
- };
-
- int intVal;
- std::int64_t int64Val;
- std::string stringVal;
- double doubleVal;
- bool boolVal;
-
- std::vector intVector;
- std::vector stringVector;
- std::vector doubleVector;
-
- SubConfig subObj;
-
- CONFIG_REGISTER
- (
- intVal,
- int64Val,
- stringVal,
- doubleVal,
- boolVal,
-
- intVector,
- stringVector,
- doubleVector,
-
- subObj
- )
-};
-} // saveLoadKVStoreTest
-
-
BOOST_AUTO_TEST_CASE(FromToKVStoreTest)
{
- using namespace saveLoadKVStoreTest;
-
- // TODO: Change this to TestConfig and delete PoorTestConfig when serialization is implemented
- PoorTestConfig config;
+ TestConfig config;
loadFromString(jsonTestString, config);
std::string dbPath = fs::unique_path("/tmp/kvstore-%%%%.db3").string();
saveToKVStore(dbPath, config);
- loadFromKVStore(dbPath, config);
- saveToKVStore(dbPath, config, "some_config");
- loadFromKVStore(dbPath, config, "some_config");
+ TestConfig outConfig;
+ loadFromKVStore(dbPath, outConfig);
+
+ std::string out = saveToString(outConfig);
+ BOOST_CHECK_EQUAL(out, jsonTestString);
fs::remove(dbPath);
}
--
2.7.4
From e1302ae96a14947703e37029398bdb059b4f209f Mon Sep 17 00:00:00 2001
From: Lukasz Kostyra
Date: Tue, 5 Aug 2014 13:49:56 +0200
Subject: [PATCH 02/16] Add API to create new containers
[Feature] Dbus method to add new containers
[Cause] Need of dynamic management of containers
[Solution] Added dbus API to add new containers.
Added new functions to utils needed during dynamic container creation.
[Verification] Build, install, run unit tests.
Change-Id: I2044c416947dccc3e0e90302f6b56ea49db0baa1
---
CMakeLists.txt | 4 +
common/utils/fs.cpp | 89 ++++++++
common/utils/fs.hpp | 12 ++
common/utils/img.cpp | 240 +++++++++++++++++++++
common/utils/img.hpp | 55 +++++
common/utils/paths.hpp | 12 ++
packaging/security-containers.spec | 8 +-
server/configs/CMakeLists.txt | 5 +-
server/configs/daemon.conf | 3 +
.../configs/systemd/security-containers.service.in | 1 +
server/configs/templates/template-network.xml | 12 ++
server/configs/templates/template-nwfilter.xml | 9 +
server/configs/templates/template.conf | 13 ++
server/configs/templates/template.xml | 123 +++++++++++
server/container-admin.cpp | 4 +
server/container.cpp | 28 +++
server/container.hpp | 10 +
server/containers-manager-config.hpp | 19 ++
server/containers-manager.cpp | 236 +++++++++++++++++---
server/containers-manager.hpp | 15 +-
server/host-connection.cpp | 14 ++
server/host-connection.hpp | 9 +
server/host-dbus-definitions.hpp | 5 +
server/server.cpp | 18 +-
.../client/configs/ut-client/test-dbus-daemon.conf | 3 +
tests/unit_tests/server/configs/CMakeLists.txt | 9 +
.../ut-containers-manager/buggy-daemon.conf | 3 +
.../buggy-default-daemon.conf | 3 +
.../buggy-foreground-daemon.conf | 3 +
.../templates/template-network.xml | 4 +
.../templates/template-nwfilter.xml | 3 +
.../ut-containers-manager/templates/template.conf | 13 ++
.../templates/template.xml.in | 15 ++
.../configs/ut-containers-manager/test-daemon.conf | 3 +
.../ut-containers-manager/test-dbus-daemon.conf | 3 +
.../server/configs/ut-server/buggy-daemon.conf | 3 +
.../server/configs/ut-server/test-daemon.conf | 3 +
tests/unit_tests/server/ut-containers-manager.cpp | 74 +++++++
tests/unit_tests/utils/ut-fs.cpp | 51 +++++
39 files changed, 1099 insertions(+), 38 deletions(-)
create mode 100644 common/utils/img.cpp
create mode 100644 common/utils/img.hpp
create mode 100644 server/configs/templates/template-network.xml
create mode 100644 server/configs/templates/template-nwfilter.xml
create mode 100644 server/configs/templates/template.conf
create mode 100644 server/configs/templates/template.xml
create mode 100644 tests/unit_tests/server/configs/ut-containers-manager/templates/template-network.xml
create mode 100644 tests/unit_tests/server/configs/ut-containers-manager/templates/template-nwfilter.xml
create mode 100644 tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf
create mode 100644 tests/unit_tests/server/configs/ut-containers-manager/templates/template.xml.in
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 266773a..9cb880a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,10 +69,14 @@ ENDIF(NOT DEFINED LIBVIRT_GROUP)
IF(NOT DEFINED INPUT_EVENT_GROUP)
SET(INPUT_EVENT_GROUP "input")
ENDIF(NOT DEFINED INPUT_EVENT_GROUP)
+IF(NOT DEFINED DISK_GROUP)
+ SET(DISK_GROUP "disk")
+ENDIF(NOT DEFINED DISK_GROUP)
ADD_DEFINITIONS(-DSECURITY_CONTAINERS_USER="${SECURITY_CONTAINERS_USER}")
ADD_DEFINITIONS(-DLIBVIRT_GROUP="${LIBVIRT_GROUP}")
ADD_DEFINITIONS(-DINPUT_EVENT_GROUP="${INPUT_EVENT_GROUP}")
+ADD_DEFINITIONS(-DDISK_GROUP="${DISK_GROUP}")
## Python packages directory ###################################################
diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp
index d663831..a03c139 100644
--- a/common/utils/fs.cpp
+++ b/common/utils/fs.cpp
@@ -39,6 +39,8 @@
#include
#include
+#include
+
namespace security_containers {
namespace utils {
@@ -199,6 +201,63 @@ bool moveFile(const std::string& src, const std::string& dst)
return true;
}
+namespace {
+
+bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesystem::path& dst)
+{
+ namespace fs = boost::filesystem;
+
+ // TODO: Right now this function skips files which produce error when copying. Errors show up
+ // when:
+ // a) fs::directory_iterator file(src) is created
+ // b) fs::copy(...) is called
+ // In both cases lack of permissions is the issue.
+ //
+ // In a) case we can't do much - SCS won't be able to read the directory and its contents. Such
+ // directories are not common in the filesystem, so they *probably* can be skipped.
+ //
+ // In b) case multiple directories have too strict permissions to be directly copied. This
+ // is a problem for some files crucial to container launch (ex. we cannot copy
+ // /usr/lib/systemd/systemd because /usr/lib has 555 permissions).
+ // To fix b) issue, copying must be done in two steps:
+ // 1. Copy file contents without permissions (this probably can be achieved by opening two
+ // files in-code with fstream and programatically copying data from one file to another).
+ // 2. Apply all available file attributes from source (permissions, owner UID/GID, xattrs...)
+
+ try {
+ for (fs::directory_iterator file(src);
+ file != fs::directory_iterator();
+ ++file) {
+ fs::path current(file->path());
+
+ boost::system::error_code ec;
+ fs::copy(current, dst / current.filename(), ec);
+ if(ec.value() != boost::system::errc::success) {
+ LOGW("Failed to copy " << current << ": " << ec.message());
+ }
+
+ if (!fs::is_symlink(current) && fs::is_directory(current)) {
+ if (!copyDirContentsRec(current, dst / current.filename())) {
+ return false;
+ }
+ }
+ }
+ } catch (fs::filesystem_error& e) {
+ LOGW(e.what());
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool copyDirContents(const std::string& src, const std::string& dst)
+{
+ namespace fs = boost::filesystem;
+
+ return copyDirContentsRec(fs::path(src), fs::path(dst));
+}
+
bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode)
{
namespace fs = boost::filesystem;
@@ -239,5 +298,35 @@ bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem:
return true;
}
+bool createEmptyDir(const std::string& path)
+{
+ namespace fs = boost::filesystem;
+
+ fs::path dirPath(path);
+ boost::system::error_code ec;
+ bool cleanDirCreated = false;
+
+ if (!fs::exists(dirPath)) {
+ if (!fs::create_directory(dirPath, ec)) {
+ LOGE("Failed to create dir. Error: " << ec.message());
+ return false;
+ }
+ cleanDirCreated = true;
+ } else if (!fs::is_directory(dirPath)) {
+ LOGE("Provided path already exists and is not a dir, cannot create.");
+ return false;
+ }
+
+ if (!cleanDirCreated) {
+ // check if directory is empty if it was already created
+ if (!fs::is_empty(dirPath)) {
+ LOGE("Directory has some data inside, cannot be used.");
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace utils
} // namespace security_containers
diff --git a/common/utils/fs.hpp b/common/utils/fs.hpp
index 9cff7a9..0e2f00e 100644
--- a/common/utils/fs.hpp
+++ b/common/utils/fs.hpp
@@ -87,10 +87,22 @@ bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool&
bool moveFile(const std::string& src, const std::string& dst);
/**
+ * Recursively copy contents of src dir to dst dir.
+ */
+bool copyDirContents(const std::string& src, const std::string& dst);
+
+/**
* Creates a directory with specific UID, GID and permissions set.
*/
bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode);
+/**
+ * Creates an empty directory, ready to serve as mount point.
+ * Succeeds either if path did not exist and was created successfully, or if already existing dir
+ * under the same path is empty and is not a mount point.
+ */
+bool createEmptyDir(const std::string& path);
+
} // namespace utils
} // namespace security_containers
diff --git a/common/utils/img.cpp b/common/utils/img.cpp
new file mode 100644
index 0000000..4d7de88
--- /dev/null
+++ b/common/utils/img.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Lukasz Kostyra
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file img.hpp
+ * @author Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief Image utility functions declaration
+ */
+
+#include "config.hpp"
+#include "logger/logger.hpp"
+#include "utils/img.hpp"
+#include "utils/fs.hpp"
+#include "utils/paths.hpp"
+
+#include
+#include
+#include
+
+namespace security_containers {
+namespace utils {
+
+namespace {
+
+const std::string LOOP_DEV_PREFIX = "/dev/loop";
+const std::string LOOP_MOUNT_POINT_OPTIONS = "";
+const std::string LOOP_MOUNT_POINT_TYPE = "ext4";
+const unsigned long LOOP_MOUNT_POINT_FLAGS = MS_RDONLY;
+
+// Writes to ret if loop device (provided in loopdev arg) is free to use.
+// Returns true if check was successful, false if loop device FD was unavailable for some reason.
+bool isLoopDevFree(const std::string& loopdev, bool& ret)
+{
+ // initialize
+ ret = false;
+
+ // open loop device FD
+ int loopFD = ::open(loopdev.c_str(), O_RDWR);
+ if (loopFD < 0) {
+ LOGD("Failed to open loop device descriptor: " << ::strerror(errno));
+ return false;
+ }
+
+ // if ioctl with LOOP_GET_STATUS fails, device is not assigned and free to use
+ struct loop_info linfo;
+ if (::ioctl(loopFD, LOOP_GET_STATUS, &linfo)) {
+ ret = true;
+ }
+
+ ::close(loopFD);
+ return true;
+}
+
+bool mountLoop(const std::string& img,
+ const std::string& loopdev,
+ const std::string& path,
+ const std::string& type,
+ unsigned long flags,
+ const std::string& options)
+{
+ // to mount an image, we need to connect image FD with loop device FD
+ // get image file FD
+ int fileFD = ::open(img.c_str(), O_RDWR);
+ if (fileFD < 0) {
+ LOGD("Failed to open image file descriptor: " << ::strerror(errno));
+ return false;
+ }
+
+ // get loop device FD
+ int loopFD = ::open(loopdev.c_str(), O_RDWR);
+ if (loopFD < 0) {
+ LOGD("Failed to open loop device descriptor: " << ::strerror(errno));
+ ::close(fileFD);
+ return false;
+ }
+
+ // set loop device
+ if (::ioctl(loopFD, LOOP_SET_FD, fileFD)) {
+ LOGD("Failed to assign loop device to image: " << ::strerror(errno));
+ ::close(fileFD);
+ ::close(loopFD);
+ return false;
+ }
+
+ // mount loop device to path
+ if (::mount(loopdev.c_str(), path.c_str(), type.c_str(), flags, options.c_str()) != 0) {
+ LOGD("Mount failed for '" << path << "', options=" << options << ": " << strerror(errno));
+ ::ioctl(loopFD, LOOP_CLR_FD, 0);
+ ::close(fileFD);
+ ::close(loopFD);
+ return false;
+ }
+
+ ::close(fileFD);
+ ::close(loopFD);
+ return true;
+}
+
+} // namespace
+
+// Finds first available loop device and returns its path through ret.
+// Returns false if an error occurs, or if all available loop devices are taken.
+bool getFreeLoopDevice(std::string& ret)
+{
+ for (unsigned int i = 0; i < 8; ++i) {
+ // build path to loop device
+ const std::string loopdev = LOOP_DEV_PREFIX + std::to_string(i);
+ bool isFree = false;
+
+ // check if it is free
+ if (!isLoopDevFree(loopdev, isFree)) {
+ LOGD("Failed to check status of " << loopdev);
+ return false;
+ }
+
+ // if checked loop device is free, we can exit the function and return it
+ if (isFree) {
+ ret = loopdev;
+ return true;
+ }
+ }
+
+ LOGD("All loop devices are taken.");
+ return false;
+}
+
+bool mountImage(const std::string& image, const std::string& path, const std::string& loopdev)
+{
+ return mountLoop(image, path, loopdev,
+ LOOP_MOUNT_POINT_TYPE,
+ LOOP_MOUNT_POINT_FLAGS,
+ LOOP_MOUNT_POINT_OPTIONS);
+}
+
+bool umountImage(const std::string& path, const std::string& loopdev)
+{
+ if (::umount(path.c_str()) != 0) {
+ LOGD("Umount failed for '" << path << "': " << strerror(errno));
+ return false;
+ }
+
+ // clear loop device
+ int loopFD = ::open(loopdev.c_str(), O_RDWR);
+ if (loopFD < 0) {
+ LOGD("Failed to open fd for loop device 0");
+ return false;
+ }
+
+ if (::ioctl(loopFD, LOOP_CLR_FD, 0) < 0) {
+ LOGD("Failed to clear loop device.");
+ close(loopFD);
+ return false;
+ }
+
+ close(loopFD);
+ return true;
+}
+
+bool copyImageContents(const std::string& img, const std::string& dst)
+{
+ namespace fs = boost::filesystem;
+ boost::system::error_code ec;
+
+ // make sure that image exists
+ if (!fs::exists(fs::path(img))) {
+ LOGE("Image " << img << " does not exist");
+ return false;
+ }
+
+ const std::string mountPoint = createFilePath(dirName(img), "/mp/");
+ // create a mount point for copied image
+ if (!createEmptyDir(mountPoint)) {
+ LOGE("Cannot create mount point for copied image.");
+ return false;
+ }
+
+ // create dst directory
+ if (!createEmptyDir(dst)) {
+ LOGE("Cannot create directory for data.");
+ return false;
+ }
+
+ // find free loop device for image
+ std::string loopdev;
+ if (!utils::getFreeLoopDevice(loopdev)) {
+ LOGE("Failed to get free loop device.");
+ return false;
+ }
+
+ LOGT("Using " << loopdev << " to mount image");
+ // mount an image
+ if (!utils::mountImage(img, loopdev, mountPoint)) {
+ LOGE("Cannot mount image.");
+ return false;
+ }
+
+ // copy data
+ LOGI("Beginning image copy");
+ if (!utils::copyDirContents(mountPoint, dst)) {
+ LOGE("Failed to copy image.");
+ utils::umountImage(mountPoint, loopdev);
+ LOGD("Removing already copied data");
+ fs::remove_all(fs::path(dst));
+ return false;
+ }
+ LOGI("Finished image copy");
+
+ // umount image
+ if (!utils::umountImage(mountPoint, loopdev)) {
+ LOGE("Failed to umount image");
+ LOGD("Removing copied data");
+ fs::remove_all(fs::path(dst));
+ return false;
+ }
+
+ // remove mount point
+ if (!fs::remove(fs::path(mountPoint), ec)) {
+ LOGW("Failed to remove mount point: " << ec.message());
+ }
+
+ return true;
+}
+
+} // namespace utils
+} // namespace security_containers
diff --git a/common/utils/img.hpp b/common/utils/img.hpp
new file mode 100644
index 0000000..d42300e
--- /dev/null
+++ b/common/utils/img.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Lukasz Kostyra
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file img.hpp
+ * @author Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief Image utility functions declaration
+ */
+
+#ifndef COMMON_UTILS_IMG_HPP
+#define COMMON_UTILS_IMG_HPP
+
+namespace security_containers {
+namespace utils {
+
+/**
+ * Returns string with first free loop device.
+ */
+bool getFreeLoopDevice(std::string& ret);
+
+/**
+ * Mount an ext4 image from file on a given path by using a loop device.
+ */
+bool mountImage(const std::string& image, const std::string& loopdev, const std::string& path);
+
+/**
+ * Umounts previously mounted image.
+ * This call will also free loop device used to mount specified path.
+ */
+bool umountImage(const std::string& path, const std::string& loopdev);
+
+/**
+ * Mounts an image and copies its contents to dst directory.
+ */
+bool copyImageContents(const std::string& img, const std::string& dst);
+
+} // namespace utils
+} // namespace security_containers
+
+#endif // COMMON_UTILS_IMG_HPP
diff --git a/common/utils/paths.hpp b/common/utils/paths.hpp
index e357c71..14132b4 100644
--- a/common/utils/paths.hpp
+++ b/common/utils/paths.hpp
@@ -107,6 +107,18 @@ inline std::string dirName(std::string path)
return path;
}
+/*
+ * Gets absolute path to specified file (if needed)
+ */
+inline std::string getAbsolutePath(const std::string& path, const std::string& base)
+{
+ if (path[0] == '/') {
+ return path;
+ } else {
+ return utils::createFilePath(base, "/", path);
+ }
+}
+
} // namespace utils
} // namespace security_containers
diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec
index 30914b0..97bf4d5 100644
--- a/packaging/security-containers.spec
+++ b/packaging/security-containers.spec
@@ -5,6 +5,8 @@
# The group that has read and write access to /dev/input/event* devices.
# It may vary between platforms.
%define input_event_group video
+# The group has access to /dev/loop* devices.
+%define disk_group disk
Name: security-containers
Version: 0.1.1
@@ -40,9 +42,12 @@ between them. A process from inside a container can request a switch of context
%dir /etc/security-containers
%dir /etc/security-containers/containers
%dir /etc/security-containers/libvirt-config
+%dir /etc/security-containers/templates
%config /etc/security-containers/daemon.conf
%config /etc/security-containers/containers/*.conf
%config /etc/security-containers/libvirt-config/*.xml
+%config /etc/security-containers/templates/*.conf
+%config /etc/security-containers/templates/*.xml
%{_unitdir}/security-containers.service
%{_unitdir}/multi-user.target.wants/security-containers.service
/etc/dbus-1/system.d/org.tizen.containers.host.conf
@@ -65,7 +70,8 @@ between them. A process from inside a container can request a switch of context
-DPYTHON_SITELIB=%{python_sitelib} \
-DSECURITY_CONTAINERS_USER=%{scs_user} \
-DLIBVIRT_GROUP=%{libvirt_group} \
- -DINPUT_EVENT_GROUP=%{input_event_group}
+ -DINPUT_EVENT_GROUP=%{input_event_group} \
+ -DDISK_GROUP=%{disk_group}
make -k %{?jobs:-j%jobs}
%install
diff --git a/server/configs/CMakeLists.txt b/server/configs/CMakeLists.txt
index def06a2..81859c1 100644
--- a/server/configs/CMakeLists.txt
+++ b/server/configs/CMakeLists.txt
@@ -21,7 +21,7 @@ MESSAGE(STATUS "Installing configs to " ${SC_CONFIG_INSTALL_DIR})
FILE(GLOB container_CONF containers/*.conf)
FILE(GLOB admin_CONF libvirt-config/*.xml)
-
+FILE(GLOB template_CONF templates/*.conf templates/*.xml)
## Generate ####################################################################
CONFIGURE_FILE(systemd/security-containers.service.in
@@ -45,5 +45,8 @@ INSTALL(FILES ${container_CONF}
INSTALL(FILES ${admin_CONF}
DESTINATION ${SC_CONFIG_INSTALL_DIR}/libvirt-config)
+INSTALL(FILES ${template_CONF}
+ DESTINATION ${SC_CONFIG_INSTALL_DIR}/templates)
+
INSTALL(FILES ${CMAKE_BINARY_DIR}/systemd/security-containers.service
DESTINATION ${SYSTEMD_UNIT_DIR})
diff --git a/server/configs/daemon.conf b/server/configs/daemon.conf
index 67ef356..db85284 100644
--- a/server/configs/daemon.conf
+++ b/server/configs/daemon.conf
@@ -1,6 +1,9 @@
{
"containerConfigs" : ["containers/private.conf", "containers/business.conf" ],
"containersPath" : "/opt/usr/containers",
+ "containerImagePath" : "/opt/usr/containers/img/system-data.img",
+ "containerTemplatePath" : "templates",
+ "containerNewConfigPrefix" : "/var/lib/security-containers",
"runMountPointPrefix" : "/var/run/containers",
"foregroundId" : "private",
"defaultId" : "private",
diff --git a/server/configs/systemd/security-containers.service.in b/server/configs/systemd/security-containers.service.in
index f06dea3..c271341 100644
--- a/server/configs/systemd/security-containers.service.in
+++ b/server/configs/systemd/security-containers.service.in
@@ -2,6 +2,7 @@
Description=Security Containers Server
After=libvirtd.service
Requires=libvirtd.service
+ConditionVirtualization=no
[Service]
Type=simple
diff --git a/server/configs/templates/template-network.xml b/server/configs/templates/template-network.xml
new file mode 100644
index 0000000..f5ec171
--- /dev/null
+++ b/server/configs/templates/template-network.xml
@@ -0,0 +1,12 @@
+
+ ~NAME~
+ ~UUID~
+
+
+
+
+
+
+
+
+
diff --git a/server/configs/templates/template-nwfilter.xml b/server/configs/templates/template-nwfilter.xml
new file mode 100644
index 0000000..82ea8cc
--- /dev/null
+++ b/server/configs/templates/template-nwfilter.xml
@@ -0,0 +1,9 @@
+
+ ~UUID~
+
+
+
+
+
+
+
diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf
new file mode 100644
index 0000000..17480a0
--- /dev/null
+++ b/server/configs/templates/template.conf
@@ -0,0 +1,13 @@
+{
+ "cpuQuotaForeground" : -1,
+ "cpuQuotaBackground" : 1000,
+ "privilege" : 10,
+ "switchToDefaultAfterTimeout" : true,
+ "enableDbusIntegration" : true,
+ "config" : "../libvirt-config/~NAME~.xml",
+ "networkConfig" : "../libvirt-config/~NAME~-network.xml",
+ "networkFilterConfig" : "../libvirt-config/~NAME~-nwfilter.xml",
+ "runMountPoint" : "~NAME~/run",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
+}
diff --git a/server/configs/templates/template.xml b/server/configs/templates/template.xml
new file mode 100644
index 0000000..bdb452d
--- /dev/null
+++ b/server/configs/templates/template.xml
@@ -0,0 +1,123 @@
+
+ ~NAME~
+ ~UUID~
+ 102400
+
+ exe
+ /usr/lib/systemd/systemd
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server/container-admin.cpp b/server/container-admin.cpp
index 805e9c1..f60d942 100644
--- a/server/container-admin.cpp
+++ b/server/container-admin.cpp
@@ -90,6 +90,8 @@ ContainerAdmin::ContainerAdmin(const ContainerConfig& config)
throw ContainerOperationException(mId + ": Failed to register a libvirt lifecycle callback");
}
+ LOGT(mId << ": registered lifecycle callback");
+
mRebootCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
mDom.get(),
VIR_DOMAIN_EVENT_ID_REBOOT,
@@ -103,6 +105,8 @@ ContainerAdmin::ContainerAdmin(const ContainerConfig& config)
mLifecycleCallbackId);
throw ContainerOperationException(mId + ": Failed to register a libvirt reboot callback");
}
+
+ LOGT(mId << ": registered reboot callback");
}
diff --git a/server/container.cpp b/server/container.cpp
index 60d124e..494b0ae 100644
--- a/server/container.cpp
+++ b/server/container.cpp
@@ -91,6 +91,10 @@ Container::~Container()
if (mReconnectThread.joinable()) {
mReconnectThread.join();
}
+
+ if (mStartThread.joinable()) {
+ mStartThread.join();
+ }
}
const std::vector& Container::getPermittedToSend() const
@@ -131,6 +135,30 @@ void Container::start()
goBackground();
}
+void Container::startAsync(const StartAsyncResultCallback& callback)
+{
+ if (mStartThread.joinable()) {
+ mStartThread.join();
+ }
+
+ auto startWrapper = [this, callback]() {
+ bool succeeded = false;
+
+ try {
+ start();
+ succeeded = true;
+ } catch(std::exception& e) {
+ LOGE(getId() << ": failed to start: " << e.what());
+ }
+
+ if (callback) {
+ callback(succeeded);
+ }
+ };
+
+ mStartThread = std::thread(startWrapper);
+}
+
void Container::stop()
{
Lock lock(mReconnectMutex);
diff --git a/server/container.hpp b/server/container.hpp
index f2d67c0..f741464 100644
--- a/server/container.hpp
+++ b/server/container.hpp
@@ -56,6 +56,7 @@ public:
typedef ContainerConnection::ProxyCallCallback ProxyCallCallback;
typedef std::function DbusStateChangedCallback;
+ typedef std::function StartAsyncResultCallback;
/**
* Returns a vector of regexps defining files permitted to be
@@ -87,6 +88,14 @@ public:
void start();
/**
+ * Boot the container to the background in separate thread. This function immediately exits
+ * after container booting is started in another thread.
+ *
+ * @param callback Called after starting the container. Passes bool with result of starting.
+ */
+ void startAsync(const StartAsyncResultCallback& callback);
+
+ /**
* Try to shutdown the container, if failed, destroy it.
*/
void stop();
@@ -197,6 +206,7 @@ private:
std::unique_ptr mAdmin;
std::unique_ptr mConnection;
std::thread mReconnectThread;
+ std::thread mStartThread;
mutable std::recursive_mutex mReconnectMutex;
NotifyActiveContainerCallback mNotifyCallback;
DisplayOffCallback mDisplayOffCallback;
diff --git a/server/containers-manager-config.hpp b/server/containers-manager-config.hpp
index 310dca3..06b83be 100644
--- a/server/containers-manager-config.hpp
+++ b/server/containers-manager-config.hpp
@@ -62,6 +62,22 @@ struct ContainersManagerConfig {
*/
std::string containersPath;
+ /**
+ * A path where the containers image reside. Empty path means that containers image won't be
+ * copied to containersPath when creating new container.
+ */
+ std::string containerImagePath;
+
+ /**
+ * A path where template configuration files for new containers reside
+ */
+ std::string containerTemplatePath;
+
+ /**
+ * Prefix added to a path for new container configuration files
+ */
+ std::string containerNewConfigPrefix;
+
/*
* Parameters describing input device used to switch between containers
*/
@@ -83,6 +99,9 @@ struct ContainersManagerConfig {
foregroundId,
defaultId,
containersPath,
+ containerImagePath,
+ containerTemplatePath,
+ containerNewConfigPrefix,
inputConfig,
runMountPointPrefix,
proxyCallRules
diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp
index 90a6cfc..5dc909b 100644
--- a/server/containers-manager.cpp
+++ b/server/containers-manager.cpp
@@ -36,9 +36,14 @@
#include "config/manager.hpp"
#include "dbus/exception.hpp"
#include "utils/fs.hpp"
+#include "utils/img.hpp"
#include
#include
+#include
+#include
+#include
+#include
#include
#include
#include
@@ -61,13 +66,25 @@ bool regexMatchVector(const std::string& str, const std::vector& v
}
const std::string HOST_ID = "host";
+const std::string CONTAINER_TEMPLATE_CONFIG_PATH = "template.conf";
+const std::string CONTAINER_TEMPLATE_LIBVIRT_CONFIG_PATH = "template.xml";
+const std::string CONTAINER_TEMPLATE_LIBVIRT_NETWORK_PATH = "template-network.xml";
+const std::string CONTAINER_TEMPLATE_LIBVIRT_NETWORK_FILTER_PATH = "template-nwfilter.xml";
+
+const boost::regex CONTAINER_NAME_REGEX("~NAME~");
+const boost::regex CONTAINER_UUID_REGEX("~UUID~");
+const boost::regex CONTAINER_IP_THIRD_OCTET_REGEX("~IP~");
+
+const unsigned int CONTAINER_IP_BASE_THIRD_OCTET = 100;
} // namespace
ContainersManager::ContainersManager(const std::string& managerConfigPath): mDetachOnExit(false)
{
LOGD("Instantiating ContainersManager object...");
- config::loadFromFile(managerConfigPath, mConfig);
+
+ mConfigPath = managerConfigPath;
+ config::loadFromFile(mConfigPath, mConfig);
mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
@@ -87,40 +104,11 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
mHostConnection.setSetActiveContainerCallback(bind(&ContainersManager::handleSetActiveContainerCall,
this, _1, _2));
- for (auto& containerConfig : mConfig.containerConfigs) {
- std::string containerConfigPath;
-
- if (containerConfig[0] == '/') {
- containerConfigPath = containerConfig;
- } else {
- std::string baseConfigPath = utils::dirName(managerConfigPath);
- containerConfigPath = utils::createFilePath(baseConfigPath, "/", containerConfig);
- }
-
- LOGD("Creating Container " << containerConfigPath);
- std::unique_ptr c(new Container(containerConfigPath,
- mConfig.runMountPointPrefix));
- const std::string id = c->getId();
- if (id == HOST_ID) {
- throw ContainerOperationException("Cannot use reserved container ID");
- }
-
- c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
- this, id, _1, _2));
+ mHostConnection.setAddContainerCallback(bind(&ContainersManager::handleAddContainerCall,
+ this, _1, _2));
- c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
- this, id));
-
- c->setFileMoveRequestCallback(std::bind(&ContainersManager::handleContainerMoveFileRequest,
- this, id, _1, _2, _3));
-
- c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
- this, id, _1, _2, _3, _4, _5, _6, _7));
-
- c->setDbusStateChangedCallback(bind(&ContainersManager::handleDbusStateChanged,
- this, id, _1));
-
- mContainers.insert(ContainerMap::value_type(id, std::move(c)));
+ for (auto& containerConfig : mConfig.containerConfigs) {
+ addContainer(containerConfig);
}
// check if default container exists, throw ContainerOperationException if not found
@@ -139,6 +127,8 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
std::bind(&ContainersManager::switchingSequenceMonitorNotify,
this)));
}
+
+
}
ContainersManager::~ContainersManager()
@@ -156,6 +146,38 @@ ContainersManager::~ContainersManager()
LOGD("ContainersManager object destroyed");
}
+void ContainersManager::addContainer(const std::string& containerConfig)
+{
+ std::string baseConfigPath = utils::dirName(mConfigPath);
+ std::string containerConfigPath = utils::getAbsolutePath(containerConfig, baseConfigPath);
+
+ LOGT("Creating Container " << containerConfigPath);
+ std::unique_ptr c(new Container(containerConfigPath,
+ mConfig.runMountPointPrefix));
+ const std::string id = c->getId();
+ if (id == HOST_ID) {
+ throw ContainerOperationException("Cannot use reserved container ID");
+ }
+
+ using namespace std::placeholders;
+ c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
+ this, id, _1, _2));
+
+ c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
+ this, id));
+
+ c->setFileMoveRequestCallback(bind(&ContainersManager::handleContainerMoveFileRequest,
+ this, id, _1, _2, _3));
+
+ c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
+ this, id, _1, _2, _3, _4, _5, _6, _7));
+
+ c->setDbusStateChangedCallback(bind(&ContainersManager::handleDbusStateChanged,
+ this, id, _1));
+
+ mContainers.insert(ContainerMap::value_type(id, std::move(c)));
+}
+
void ContainersManager::focus(const std::string& containerId)
{
/* try to access the object first to throw immediately if it doesn't exist */
@@ -465,4 +487,150 @@ void ContainersManager::handleSetActiveContainerCall(const std::string& id,
result->setVoid();
}
+
+void ContainersManager::generateNewConfig(const std::string& id,
+ const std::string& templatePath,
+ const std::string& resultPath)
+{
+ namespace fs = boost::filesystem;
+
+ std::string resultFileDir = utils::dirName(resultPath);
+ if (!fs::exists(resultFileDir)) {
+ if (!utils::createEmptyDir(resultFileDir)) {
+ LOGE("Unable to create directory for new config.");
+ throw ContainerOperationException("Unable to create directory for new config.");
+ }
+ }
+
+ fs::path resultFile(resultPath);
+ if (fs::exists(resultFile)) {
+ LOGT(resultPath << " already exists, removing");
+ fs::remove(resultFile);
+ }
+
+ std::string config;
+ if (!utils::readFileContent(templatePath, config)) {
+ LOGE("Failed to read template config file.");
+ throw ContainerOperationException("Failed to read template config file.");
+ }
+
+ std::string resultConfig = boost::regex_replace(config, CONTAINER_NAME_REGEX, id);
+
+ boost::uuids::uuid u = boost::uuids::random_generator()();
+ std::string uuidStr = to_string(u);
+ LOGD("uuid: " << uuidStr);
+ resultConfig = boost::regex_replace(resultConfig, CONTAINER_UUID_REGEX, uuidStr);
+
+ // generate third IP octet for network config
+ std::string thirdOctetStr = std::to_string(CONTAINER_IP_BASE_THIRD_OCTET + mContainers.size() + 1);
+ LOGD("ip_third_octet: " << thirdOctetStr);
+ resultConfig = boost::regex_replace(resultConfig, CONTAINER_IP_THIRD_OCTET_REGEX, thirdOctetStr);
+
+ if (!utils::saveFileContent(resultPath, resultConfig)) {
+ LOGE("Faield to save new config file.");
+ throw ContainerOperationException("Failed to save new config file.");
+ }
+
+ // restrict new config file so that only owner (security-containers) can write it
+ fs::permissions(resultPath, fs::perms::owner_all |
+ fs::perms::group_read |
+ fs::perms::others_read);
+}
+
+void ContainersManager::handleAddContainerCall(const std::string& id,
+ dbus::MethodResultBuilder::Pointer result)
+{
+ LOGI("Adding container " << id);
+
+ // TODO: This solution is temporary. It utilizes direct access to config files when creating new
+ // containers. Update this handler when config database will appear.
+ namespace fs = boost::filesystem;
+
+ boost::system::error_code ec;
+ const std::string containerPathStr = utils::createFilePath(mConfig.containersPath, "/", id, "/");
+
+ // check if container does not exist
+ if (mContainers.find(id) != mContainers.end()) {
+ LOGE("Cannot create " << id << " container - already exists!");
+ result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
+ "Cannot create " + id + " container - already exists!");
+ return;
+ }
+
+ // copy container image if config contains path to image
+ LOGT("image path: " << mConfig.containerImagePath);
+ if (!mConfig.containerImagePath.empty()) {
+ if (!utils::copyImageContents(mConfig.containerImagePath, containerPathStr)) {
+ LOGE("Failed to copy container image.");
+ result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
+ "Failed to copy container image.");
+ return;
+ }
+ }
+
+ // generate paths to new configuration files
+ std::string baseDir = utils::dirName(mConfigPath);
+ std::string configDir = utils::getAbsolutePath(mConfig.containerNewConfigPrefix, baseDir);
+ std::string templateDir = utils::getAbsolutePath(mConfig.containerTemplatePath, baseDir);
+
+ std::string configPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_CONFIG_PATH);
+ std::string newConfigPath = utils::createFilePath(configDir, "/containers/", id + ".conf");
+ std::string libvirtConfigPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_CONFIG_PATH);
+ std::string newLibvirtConfigPath = utils::createFilePath(configDir, "/libvirt-config/", id + ".xml");
+ std::string libvirtNetworkPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_NETWORK_PATH);
+ std::string newLibvirtNetworkPath = utils::createFilePath(configDir, "/libvirt-config/", id + "-network.xml");
+ std::string libvirtNetworkFilterPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_NETWORK_FILTER_PATH);
+ std::string newLibvirtNetworkFilterPath = utils::createFilePath(configDir, "/libvirt-config/", id + "-nwfilter.xml");
+
+ auto removeAllWrapper = [](const std::string& path) {
+ try {
+ LOGD("Removing copied data");
+ fs::remove_all(fs::path(path));
+ } catch(const boost::exception& e) {
+ LOGW("Failed to remove data: " << boost::diagnostic_information(e));
+ }
+ };
+
+ try {
+ LOGI("Generating config from " << configPath << " to " << newConfigPath);
+ generateNewConfig(id, configPath, newConfigPath);
+
+ LOGI("Generating config from " << libvirtConfigPath << " to " << newLibvirtConfigPath);
+ generateNewConfig(id, libvirtConfigPath, newLibvirtConfigPath);
+
+ LOGI("Generating config from " << libvirtNetworkPath << " to " << newLibvirtNetworkPath);
+ generateNewConfig(id, libvirtNetworkPath, newLibvirtNetworkPath);
+
+ LOGI("Generating config from " << libvirtNetworkFilterPath << " to " << newLibvirtNetworkFilterPath);
+ generateNewConfig(id, libvirtNetworkFilterPath, newLibvirtNetworkFilterPath);
+ } catch (SecurityContainersException& e) {
+ LOGE(e.what());
+ removeAllWrapper(containerPathStr);
+ result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what());
+ return;
+ }
+
+ LOGT("Adding new container");
+ try {
+ addContainer(newConfigPath);
+ } catch (SecurityContainersException& e) {
+ LOGE(e.what());
+ removeAllWrapper(containerPathStr);
+ result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what());
+ return;
+ }
+
+ auto resultCallback = [result, containerPathStr, removeAllWrapper](bool succeeded) {
+ if (succeeded) {
+ result->setVoid();
+ } else {
+ LOGE("Failed to start container.");
+ removeAllWrapper(containerPathStr);
+ result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
+ "Failed to start container.");
+ }
+ };
+ mContainers[id]->startAsync(resultCallback);
+}
+
} // namespace security_containers
diff --git a/server/containers-manager.hpp b/server/containers-manager.hpp
index 33767a9..cd1194d 100644
--- a/server/containers-manager.hpp
+++ b/server/containers-manager.hpp
@@ -48,6 +48,13 @@ public:
~ContainersManager();
/**
+ * Add new container.
+ *
+ * @param containerConfig config of new container
+ */
+ void addContainer(const std::string& containerConfig);
+
+ /**
* Focus this container, put it to the foreground.
* Method blocks until the focus is switched.
*
@@ -77,6 +84,7 @@ public:
private:
ContainersManagerConfig mConfig;
+ std::string mConfigPath;
HostConnection mHostConnection;
// to hold InputMonitor pointer to monitor if container switching sequence is recognized
std::unique_ptr mSwitchingSequenceMonitor;
@@ -86,6 +94,10 @@ private:
bool mDetachOnExit;
void switchingSequenceMonitorNotify();
+ void generateNewConfig(const std::string& id,
+ const std::string& templatePath,
+ const std::string& resultPath);
+
void notifyActiveContainerHandler(const std::string& caller,
const std::string& appliaction,
const std::string& message);
@@ -108,7 +120,8 @@ private:
void handleGetActiveContainerIdCall(dbus::MethodResultBuilder::Pointer result);
void handleSetActiveContainerCall(const std::string& id,
dbus::MethodResultBuilder::Pointer result);
-
+ void handleAddContainerCall(const std::string& id,
+ dbus::MethodResultBuilder::Pointer result);
};
diff --git a/server/host-connection.cpp b/server/host-connection.cpp
index ae32de2..d25bee7 100644
--- a/server/host-connection.cpp
+++ b/server/host-connection.cpp
@@ -130,6 +130,11 @@ void HostConnection::setSetActiveContainerCallback(const SetActiveContainerCallb
mSetActiveContainerCallback = callback;
}
+void HostConnection::setAddContainerCallback(const AddContainerCallback& callback)
+{
+ mAddContainerCallback = callback;
+}
+
void HostConnection::onMessageCall(const std::string& objectPath,
const std::string& interface,
const std::string& methodName,
@@ -199,6 +204,15 @@ void HostConnection::onMessageCall(const std::string& objectPath,
}
return;
}
+
+ if (methodName == api::host::METHOD_ADD_CONTAINER) {
+ const gchar* id = NULL;
+ g_variant_get(parameters, "(&s)", &id);
+
+ if (mAddContainerCallback){
+ mAddContainerCallback(id, result);
+ }
+ }
}
void HostConnection::proxyCallAsync(const std::string& busName,
diff --git a/server/host-connection.hpp b/server/host-connection.hpp
index bc9015d..c5d1bcc 100644
--- a/server/host-connection.hpp
+++ b/server/host-connection.hpp
@@ -60,6 +60,9 @@ public:
typedef std::function SetActiveContainerCallback;
+ typedef std::function AddContainerCallback;
/**
* Register proxy call callback
@@ -92,6 +95,11 @@ public:
void setSetActiveContainerCallback(const SetActiveContainerCallback& callback);
/**
+ * Register a callback called to create new container
+ */
+ void setAddContainerCallback(const AddContainerCallback& callback);
+
+ /**
* Make a proxy call
*/
void proxyCallAsync(const std::string& busName,
@@ -112,6 +120,7 @@ private:
GetContainerIdsCallback mGetContainerIdsCallback;
GetActiveContainerIdCallback mGetActiveContainerIdCallback;
SetActiveContainerCallback mSetActiveContainerCallback;
+ AddContainerCallback mAddContainerCallback;
void onNameAcquired();
void onNameLost();
diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp
index 12e8d4a..040b10d 100644
--- a/server/host-dbus-definitions.hpp
+++ b/server/host-dbus-definitions.hpp
@@ -37,11 +37,13 @@ const std::string OBJECT_PATH = "/org/tizen/containers/host";
const std::string INTERFACE = "org.tizen.containers.host.manager";
const std::string ERROR_CONTAINER_STOPPED = "org.tizen.containers.host.Error.ContainersStopped";
+const std::string ERROR_CONTAINER_CREATE_FAILED = "org.tizen.containers.host.Error.ContainerCreateFailed";
const std::string METHOD_GET_CONTAINER_DBUSES = "GetContainerDbuses";
const std::string METHOD_GET_CONTAINER_ID_LIST = "GetContainerIds";
const std::string METHOD_GET_ACTIVE_CONTAINER_ID = "GetActiveContainerId";
const std::string METHOD_SET_ACTIVE_CONTAINER = "SetActiveContainer";
+const std::string METHOD_ADD_CONTAINER = "AddContainer";
const std::string SIGNAL_CONTAINER_DBUS_STATE = "ContainerDbusState";
@@ -70,6 +72,9 @@ const std::string DEFINITION =
" "
" "
" "
+ " "
+ " "
+ " "
" "
" "
" "
diff --git a/server/server.cpp b/server/server.cpp
index 5c3420d..547d023 100644
--- a/server/server.cpp
+++ b/server/server.cpp
@@ -58,6 +58,10 @@
#error "LIBVIRT_GROUP must be defined!"
#endif
+#ifndef DISK_GROUP
+#error "DISK_GROUP must be defined!"
+#endif
+
extern char** environ;
namespace security_containers {
@@ -165,6 +169,16 @@ bool Server::prepareEnvironment(const std::string& configPath, bool runAsRoot)
}
}
+ // create directory for additional container data (if needed)
+ if (!config.containerNewConfigPrefix.empty()) {
+ if (!utils::createDir(config.containerNewConfigPrefix, uid, gid,
+ fs::perms::owner_all |
+ fs::perms::group_read | fs::perms::group_exe |
+ fs::perms::others_read | fs::perms::others_exe)) {
+ return false;
+ }
+ }
+
// Omit supplementaty group setup and root drop if the user is already switched.
// This situation will happen during daemon update triggered by SIGUSR1.
if (!runAsRoot && geteuid() == uid) {
@@ -173,7 +187,9 @@ bool Server::prepareEnvironment(const std::string& configPath, bool runAsRoot)
// LIBVIRT_GROUP provides access to libvirt's daemon socket.
// INPUT_EVENT_GROUP provides access to /dev/input/event* devices used by InputMonitor.
- if (!utils::setSuppGroups({LIBVIRT_GROUP, INPUT_EVENT_GROUP})) {
+ // DISK_GROUP provides access to /dev/loop* devices, needed when adding new container to copy
+ // containers image
+ if (!utils::setSuppGroups({LIBVIRT_GROUP, INPUT_EVENT_GROUP, DISK_GROUP})) {
return false;
}
diff --git a/tests/unit_tests/client/configs/ut-client/test-dbus-daemon.conf b/tests/unit_tests/client/configs/ut-client/test-dbus-daemon.conf
index 2a6ad35..707be02 100644
--- a/tests/unit_tests/client/configs/ut-client/test-dbus-daemon.conf
+++ b/tests/unit_tests/client/configs/ut-client/test-dbus-daemon.conf
@@ -5,6 +5,9 @@
"foregroundId" : "ut-containers-manager-console1-dbus",
"defaultId" : "ut-containers-manager-console1-dbus",
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "",
+ "containerNewConfigPrefix" : "",
"runMountPointPrefix" : "",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
diff --git a/tests/unit_tests/server/configs/CMakeLists.txt b/tests/unit_tests/server/configs/CMakeLists.txt
index 816a5bc..ee1c7d9 100644
--- a/tests/unit_tests/server/configs/CMakeLists.txt
+++ b/tests/unit_tests/server/configs/CMakeLists.txt
@@ -26,6 +26,8 @@ FILE(GLOB server_admin_CONF ut-server/libvirt-config/*.xml)
FILE(GLOB manager_manager_CONF ut-containers-manager/*.conf)
FILE(GLOB manager_container_CONF ut-containers-manager/containers/*.conf)
FILE(GLOB manager_admin_CONF ut-containers-manager/libvirt-config/*.xml)
+FILE(GLOB manager_admin_TEMPLATE ut-containers-manager/templates/*.conf
+ ut-containers-manager/templates/*.xml)
FILE(GLOB container_CONF ut-container/*.conf)
FILE(GLOB container_container_CONF ut-container/containers/*.conf)
@@ -61,7 +63,10 @@ CONFIGURE_FILE(ut-containers-manager/libvirt-config/console2-dbus.xml.in
${CMAKE_BINARY_DIR}/ut-containers-manager/libvirt-config/console2-dbus.xml @ONLY)
CONFIGURE_FILE(ut-containers-manager/libvirt-config/console3-dbus.xml.in
${CMAKE_BINARY_DIR}/ut-containers-manager/libvirt-config/console3-dbus.xml @ONLY)
+CONFIGURE_FILE(ut-containers-manager/templates/template.xml.in
+ ${CMAKE_BINARY_DIR}/ut-containers-manager/templates/template.xml @ONLY)
FILE(GLOB manager_admin_CONF_GEN ${CMAKE_BINARY_DIR}/ut-containers-manager/libvirt-config/*.xml)
+FILE(GLOB manager_admin_TEMPLATE_GEN ${CMAKE_BINARY_DIR}/ut-containers-manager/templates/*.xml)
CONFIGURE_FILE(ut-container/libvirt-config/test-dbus.xml.in
${CMAKE_BINARY_DIR}/ut-container/libvirt-config/test-dbus.xml @ONLY)
@@ -84,6 +89,10 @@ INSTALL(FILES ${manager_admin_CONF}
DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/libvirt-config)
INSTALL(FILES ${manager_admin_CONF_GEN}
DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/libvirt-config)
+INSTALL(FILES ${manager_admin_TEMPLATE}
+ DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/templates)
+INSTALL(FILES ${manager_admin_TEMPLATE_GEN}
+ DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/templates)
INSTALL(FILES ${container_CONF}
DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container)
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/buggy-daemon.conf b/tests/unit_tests/server/configs/ut-containers-manager/buggy-daemon.conf
index 92abbe2..5ec98cc 100644
--- a/tests/unit_tests/server/configs/ut-containers-manager/buggy-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-containers-manager/buggy-daemon.conf
@@ -4,6 +4,9 @@
"foregroundId" : "ut-containers-manager-console1",
"defaultId" : "ut-containers-manager-console1",
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "templates",
+ "containerNewConfigPrefix" : "/usr/share/security-containers/tests/server/ut-containers-manager/",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/buggy-default-daemon.conf b/tests/unit_tests/server/configs/ut-containers-manager/buggy-default-daemon.conf
index a19268c..ef5a597 100644
--- a/tests/unit_tests/server/configs/ut-containers-manager/buggy-default-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-containers-manager/buggy-default-daemon.conf
@@ -4,6 +4,9 @@
"foregroundId" : "ut-containers-manager-console1",
"defaultId" : "in_no_way_there_is_a_valid_id_here",
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "templates",
+ "containerNewConfigPrefix" : "/usr/share/security-containers/tests/server/ut-containers-manager/",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/buggy-foreground-daemon.conf b/tests/unit_tests/server/configs/ut-containers-manager/buggy-foreground-daemon.conf
index bcaba00..3faa0f7 100644
--- a/tests/unit_tests/server/configs/ut-containers-manager/buggy-foreground-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-containers-manager/buggy-foreground-daemon.conf
@@ -4,6 +4,9 @@
"foregroundId" : "this_id_does_not_exist",
"defaultId" : "ut-containers-manager-console1",
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "templates",
+ "containerNewConfigPrefix" : "/usr/share/security-containers/tests/server/ut-containers-manager/",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/templates/template-network.xml b/tests/unit_tests/server/configs/ut-containers-manager/templates/template-network.xml
new file mode 100644
index 0000000..b357c0e
--- /dev/null
+++ b/tests/unit_tests/server/configs/ut-containers-manager/templates/template-network.xml
@@ -0,0 +1,4 @@
+
+ ~NAME~-network
+ ~UUID~
+
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/templates/template-nwfilter.xml b/tests/unit_tests/server/configs/ut-containers-manager/templates/template-nwfilter.xml
new file mode 100644
index 0000000..b96197b
--- /dev/null
+++ b/tests/unit_tests/server/configs/ut-containers-manager/templates/template-nwfilter.xml
@@ -0,0 +1,3 @@
+
+ ~UUID~
+
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf
new file mode 100644
index 0000000..40dcacc
--- /dev/null
+++ b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf
@@ -0,0 +1,13 @@
+{
+ "privilege" : 20,
+ "switchToDefaultAfterTimeout" : true,
+ "config" : "../libvirt-config/~NAME~.xml",
+ "networkConfig" : "../libvirt-config/~NAME~-network.xml",
+ "networkFilterConfig" : "../libvirt-config/~NAME~-nwfilter.xml",
+ "cpuQuotaForeground" : -1,
+ "cpuQuotaBackground" : 1000,
+ "runMountPoint" : "/tmp/ut-containers-manager/~NAME~-dbus",
+ "enableDbusIntegration" : true,
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
+}
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/templates/template.xml.in b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.xml.in
new file mode 100644
index 0000000..fbb12a5
--- /dev/null
+++ b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.xml.in
@@ -0,0 +1,15 @@
+
+ ~NAME~
+ ~UUID~
+ 102400
+
+ exe
+ /usr/bin/dbus-daemon
+ --nofork
+ --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf
+ --address=unix:path=/tmp/ut-containers-manager/~NAME~-dbus/dbus/system_bus_socket
+
+
+
+
+
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/test-daemon.conf b/tests/unit_tests/server/configs/ut-containers-manager/test-daemon.conf
index 6147c03..9317b9d 100644
--- a/tests/unit_tests/server/configs/ut-containers-manager/test-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-containers-manager/test-daemon.conf
@@ -4,6 +4,9 @@
"foregroundId" : "ut-containers-manager-console1",
"defaultId" : "ut-containers-manager-console1",
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "templates",
+ "containerNewConfigPrefix" : "/usr/share/security-containers/tests/server/ut-containers-manager/",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf b/tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf
index 2a6ad35..af183a1 100644
--- a/tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf
@@ -5,6 +5,9 @@
"foregroundId" : "ut-containers-manager-console1-dbus",
"defaultId" : "ut-containers-manager-console1-dbus",
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "templates",
+ "containerNewConfigPrefix" : "/usr/share/security-containers/tests/server/ut-containers-manager/",
"runMountPointPrefix" : "",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
diff --git a/tests/unit_tests/server/configs/ut-server/buggy-daemon.conf b/tests/unit_tests/server/configs/ut-server/buggy-daemon.conf
index cad519c..4758406 100644
--- a/tests/unit_tests/server/configs/ut-server/buggy-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-server/buggy-daemon.conf
@@ -1,9 +1,12 @@
{
"containerConfigs" : ["containers/container1.conf", "missing/file/path/missing.conf", "containers/container3.conf"],
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "no_need_for_templates_in_this_test",
"runMountPointPrefix" : "",
"foregroundId" : "ut-server-container1",
"defaultId" : "ut-server-container1",
+ "containerNewConfigPrefix" : "",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
diff --git a/tests/unit_tests/server/configs/ut-server/test-daemon.conf b/tests/unit_tests/server/configs/ut-server/test-daemon.conf
index 868f12b..767b3a5 100644
--- a/tests/unit_tests/server/configs/ut-server/test-daemon.conf
+++ b/tests/unit_tests/server/configs/ut-server/test-daemon.conf
@@ -1,6 +1,9 @@
{
"containerConfigs" : ["containers/container1.conf", "containers/container2.conf", "containers/container3.conf"],
"containersPath" : "/tmp",
+ "containerImagePath" : "",
+ "containerTemplatePath" : "no_need_for_templates_in_this_test",
+ "containerNewConfigPrefix" : "",
"runMountPointPrefix" : "",
"foregroundId" : "ut-server-container1",
"defaultId" : "ut-server-container1",
diff --git a/tests/unit_tests/server/ut-containers-manager.cpp b/tests/unit_tests/server/ut-containers-manager.cpp
index 54a34bb..528d744 100644
--- a/tests/unit_tests/server/ut-containers-manager.cpp
+++ b/tests/unit_tests/server/ut-containers-manager.cpp
@@ -40,6 +40,7 @@
#include "config/exception.hpp"
#include "utils/latch.hpp"
#include "utils/fs.hpp"
+#include "utils/img.hpp"
#include
#include