lxcpp: user namespace setup 87/50187/6
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Mon, 26 Oct 2015 15:03:46 +0000 (16:03 +0100)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Wed, 28 Oct 2015 13:49:26 +0000 (06:49 -0700)
[Feature]       Setting up of a user namespace. This also changes the
                way namespaces are chosen. This happens automatically now.
[Cause]         N/A
[Solution]      N/A
[Verification]  Run junk tester.

Change-Id: I70c57b56c94ed9650208b63831c77f1ad0c7905c

17 files changed:
junk/run-shell.cpp
libs/lxcpp/attach/attach-helper.cpp
libs/lxcpp/commands/attach.cpp
libs/lxcpp/commands/attach.hpp
libs/lxcpp/commands/setup-userns.cpp [new file with mode: 0644]
libs/lxcpp/commands/setup-userns.hpp [new file with mode: 0644]
libs/lxcpp/container-config.hpp
libs/lxcpp/container-impl.cpp
libs/lxcpp/container-impl.hpp
libs/lxcpp/container.hpp
libs/lxcpp/credentials.cpp
libs/lxcpp/credentials.hpp
libs/lxcpp/exception.hpp
libs/lxcpp/guard/guard.cpp
libs/lxcpp/guard/guard.hpp
libs/lxcpp/userns-config.hpp [new file with mode: 0644]
tests/unit_tests/lxcpp/ut-container.cpp

index 81ee0d8..cff5dee 100644 (file)
@@ -61,13 +61,17 @@ int main(int argc, char *argv[])
         c->setInit(args);
         c->setLogger(logger::LogType::LOG_FILE, logger::LogLevel::TRACE, "/tmp/lxcpp-shell.txt");
         c->setTerminalCount(4);
+        // make my own user root in a new namespace
+        c->addUIDMap(0, 1000, 1);
+        c->addGIDMap(0, 1000, 1);
+        // make root and system users non privileged ones
+        c->addUIDMap(1000, 0, 999);
+        c->addGIDMap(1000, 0, 999);
         c->start();
         c->console();
         // You could run the console for the second time to see if it can be reattached
         //c->console();
 
-        initPid = c->getInitPid();
-
         delete c;
     }
     catch (const std::exception &e)
index 5aaaecb..addfd37 100644 (file)
@@ -61,9 +61,9 @@ int child(void* data)
     setenv(config.envToSet);
 
     // Set uid/gids
-    lxcpp::setgid(config.gid);
+    lxcpp::setregid(config.gid, config.gid);
     setgroups(config.supplementaryGids);
-    lxcpp::setuid(config.uid);
+    lxcpp::setreuid(config.uid, config.uid);
 
     // Set control TTY
     if(!setupControlTTY(config.ttyFD)) {
index a059986..b7201fb 100644 (file)
@@ -33,7 +33,7 @@
 
 namespace lxcpp {
 
-Attach::Attach(const lxcpp::ContainerImpl& container,
+Attach::Attach(const ContainerConfig& config,
                const std::vector<std::string>& argv,
                const uid_t uid,
                const gid_t gid,
@@ -45,8 +45,8 @@ Attach::Attach(const lxcpp::ContainerImpl& container,
                const std::vector<std::pair<std::string, std::string>>& envToSet)
     : mIntermChannel(false),
       mConfig(argv,
-              container.getInitPid(),
-              container.getNamespaces(),
+              config.mInitPid,
+              config.mNamespaces,
               uid,
               gid,
               supplementaryGids,
index 123a384..34945df 100644 (file)
@@ -44,7 +44,7 @@ public:
      * Object attach should be used immediately after creation.
      * It will not be stored for future use like most other commands.
      *
-     * @param container container to which it attaches
+     * @param config config of a container to which it attaches
      * @param argv path and arguments for the user's executable
      * @param uid uid in the container
      * @param gid gid in the container
@@ -55,7 +55,7 @@ public:
      * @param envToKeep environment variables that will be kept
      * @param envToSet new environment variables that will be set
      */
-    Attach(const lxcpp::ContainerImpl& container,
+    Attach(const ContainerConfig& config,
            const std::vector<std::string>& argv,
            const uid_t uid,
            const gid_t gid,
@@ -79,4 +79,4 @@ private:
 
 } // namespace lxcpp
 
-#endif // LXCPP_COMMANDS_ATTACH_HPP
\ No newline at end of file
+#endif // LXCPP_COMMANDS_ATTACH_HPP
diff --git a/libs/lxcpp/commands/setup-userns.cpp b/libs/lxcpp/commands/setup-userns.cpp
new file mode 100644 (file)
index 0000000..c9491b5
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Implementation of user namespace setup
+ */
+
+#include "lxcpp/commands/setup-userns.hpp"
+
+#include "logger/logger.hpp"
+#include "utils/fs.hpp"
+
+#include <vector>
+#include <iostream>
+
+
+namespace lxcpp {
+
+
+SetupUserNS::SetupUserNS(UserNSConfig &userNSConfig, pid_t initPid)
+    : mUserNSConfig(userNSConfig),
+      mInitPid(initPid)
+{
+}
+
+SetupUserNS::~SetupUserNS()
+{
+}
+
+void SetupUserNS::execute()
+{
+    const std::string proc = "/proc/" + std::to_string(mInitPid);
+    const std::string uid_map_path = proc + "/uid_map";
+    const std::string gid_map_path = proc + "/gid_map";
+
+    std::string uid_map;
+    for (const auto map : mUserNSConfig.mUIDMaps) {
+        uid_map.append(std::to_string(map.min));
+        uid_map.append(" ");
+        uid_map.append(std::to_string(map.max));
+        uid_map.append(" ");
+        uid_map.append(std::to_string(map.num));
+        uid_map.append("\n");
+    }
+    if (uid_map.size() && !utils::saveFileContent(uid_map_path, uid_map)) {
+        const std::string msg = "Failed to write the uid_map";
+        LOGE(msg);
+        throw UserNSException(msg);
+    }
+
+    std::string gid_map;
+    for (const auto map : mUserNSConfig.mGIDMaps) {
+        gid_map.append(std::to_string(map.min));
+        gid_map.append(" ");
+        gid_map.append(std::to_string(map.max));
+        gid_map.append(" ");
+        gid_map.append(std::to_string(map.num));
+        gid_map.append("\n");
+    }
+    if (gid_map.size() && !utils::saveFileContent(gid_map_path, gid_map)) {
+        const std::string msg = "Failed to write the gid_map";
+        LOGE(msg);
+        throw UserNSException(msg);
+    }
+}
+
+
+} // namespace lxcpp
diff --git a/libs/lxcpp/commands/setup-userns.hpp b/libs/lxcpp/commands/setup-userns.hpp
new file mode 100644 (file)
index 0000000..cb4680d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Headers of user namespace setup
+ */
+
+#ifndef LXCPP_COMMANDS_SETUP_USERNS_HPP
+#define LXCPP_COMMANDS_SETUP_USERNS_HPP
+
+#include "lxcpp/commands/command.hpp"
+#include "lxcpp/container-config.hpp"
+
+
+namespace lxcpp {
+
+
+class SetupUserNS final: Command {
+public:
+    /**
+     * Setups the user namespace by filling UID/GID mappings
+     *
+     * @param userNSConfig  A config containing UID and GID mappings
+     */
+    SetupUserNS(UserNSConfig &userNSConfig, pid_t initPid);
+    ~SetupUserNS();
+
+    void execute();
+
+private:
+    UserNSConfig &mUserNSConfig;
+    pid_t mInitPid;
+};
+
+
+} // namespace lxcpp
+
+
+#endif // LXCPP_COMMANDS_SETUP_USERNS_HPP
index 1a487cf..05f11ed 100644 (file)
@@ -28,6 +28,7 @@
 #include "lxcpp/network-config.hpp"
 #include "lxcpp/terminal-config.hpp"
 #include "lxcpp/provision-config.hpp"
+#include "lxcpp/userns-config.hpp"
 
 #include <config/config.hpp>
 #include <config/fields.hpp>
@@ -126,6 +127,14 @@ struct ContainerConfig {
      */
     ProvisionConfig mProvisions;
 
+    /**
+     * User namespace config (uid and gid mappings)
+     *
+     * Set: addUIDMap(), addGIDMap()
+     * Get: none
+     */
+    UserNSConfig mUserNSConfig;
+
     ContainerConfig() : mGuardPid(-1), mInitPid(-1), mNamespaces(0) {}
 
     CONFIG_REGISTER
@@ -138,7 +147,8 @@ struct ContainerConfig {
         mLogger,
         mTerminals,
         mNamespaces,
-        mProvisions
+        mProvisions,
+        mUserNSConfig
     )
 };
 
index 29d6cb0..33331a2 100644 (file)
@@ -72,6 +72,7 @@ ContainerImpl::ContainerImpl(const std::string &name, const std::string &path)
 
     mConfig.mName = name;
     mConfig.mRootPath = path;
+    mConfig.mNamespaces = CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS;
 }
 
 // TODO: the aim of this constructor is to create a new ContainerImpl based on an already
@@ -147,15 +148,30 @@ void ContainerImpl::setTerminalCount(const unsigned int count)
     mConfig.mTerminals.count = count;
 }
 
-void ContainerImpl::setNamespaces(const int namespaces)
+void ContainerImpl::addUIDMap(unsigned min, unsigned max, unsigned num)
 {
-    mConfig.mNamespaces = namespaces;
-}
+    mConfig.mNamespaces |= CLONE_NEWUSER;
+
+    if (mConfig.mUserNSConfig.mUIDMaps.size() >= 5) {
+        const std::string msg = "Max number of 5 UID mappings has been already reached";
+        LOGE(msg);
+        throw ConfigureException(msg);
+    }
 
+    mConfig.mUserNSConfig.mUIDMaps.emplace_back(min, max, num);
+}
 
-int ContainerImpl::getNamespaces() const
+void ContainerImpl::addGIDMap(unsigned min, unsigned max, unsigned num)
 {
-    return mConfig.mNamespaces;
+    mConfig.mNamespaces |= CLONE_NEWUSER;
+
+    if (mConfig.mUserNSConfig.mGIDMaps.size() >= 5) {
+        const std::string msg = "Max number of 5 GID mappings has been already reached";
+        LOGE(msg);
+        throw ConfigureException(msg);
+    }
+
+    mConfig.mUserNSConfig.mGIDMaps.emplace_back(min, max, num);
 }
 
 void ContainerImpl::start()
@@ -196,7 +212,7 @@ void ContainerImpl::reboot()
 void ContainerImpl::attach(const std::vector<std::string>& argv,
                            const std::string& cwdInContainer)
 {
-    Attach attach(*this,
+    Attach attach(mConfig,
                   argv,
                   /*uid in container*/ 0,
                   /*gid in container*/ 0,
@@ -228,6 +244,7 @@ void ContainerImpl::addInterfaceConfig(const std::string& hostif,
                                        const std::vector<InetAddr>& addrs,
                                        MacVLanMode mode)
 {
+    mConfig.mNamespaces |= CLONE_NEWNET;
     mConfig.mNetwork.addInterfaceConfig(hostif, zoneif, type, addrs, mode);
 }
 
index f1cba58..a759929 100644 (file)
@@ -55,8 +55,8 @@ public:
 
     void setTerminalCount(const unsigned int count);
 
-    void setNamespaces(const int namespaces);
-    int getNamespaces() const;
+    void addUIDMap(unsigned min, unsigned max, unsigned num);
+    void addGIDMap(unsigned min, unsigned max, unsigned num);
 
     // Execution actions
     void start();
index 2d0e696..bc43066 100644 (file)
@@ -65,8 +65,8 @@ public:
 
     virtual void setTerminalCount(const unsigned int count) = 0;
 
-    virtual void setNamespaces(const int namespaces) = 0;
-    virtual int getNamespaces() const = 0;
+    virtual void addUIDMap(unsigned min, unsigned max, unsigned num) = 0;
+    virtual void addGIDMap(unsigned min, unsigned max, unsigned num) = 0;
 
     // Execution actions
     virtual void start() = 0;
index c127170..10b477d 100644 (file)
@@ -42,20 +42,20 @@ void setgroups(const std::vector<gid_t>& gids)
     }
 }
 
-void setgid(const gid_t gid)
+void setregid(const gid_t rgid, const gid_t egid)
 {
-    if(-1 == ::setgid(gid)) {
-        const std::string msg = "setgid() failed: " +
+    if(-1 == ::setregid(rgid, egid)) {
+        const std::string msg = "setregid() failed: " +
                                 utils::getSystemErrorMessage();
         LOGE(msg);
         throw CredentialSetupException(msg);
     }
 }
 
-void setuid(const uid_t uid)
+void setreuid(const uid_t ruid, const uid_t euid)
 {
-    if(-1 == ::setuid(uid)) {
-        const std::string msg = "setuid() failed: " +
+    if(-1 == ::setreuid(ruid, euid)) {
+        const std::string msg = "setreuid() failed: " +
                                 utils::getSystemErrorMessage();
         LOGE(msg);
         throw CredentialSetupException(msg);
index aa56aca..e5be25d 100644 (file)
@@ -32,9 +32,9 @@ namespace lxcpp {
 
 void setgroups(const std::vector<gid_t>& groups);
 
-void setgid(const gid_t gid);
+void setregid(const gid_t rgid, const gid_t egid);
 
-void setuid(const uid_t uid);
+void setreuid(const uid_t ruid, const uid_t euid);
 
 pid_t setsid();
 
index 061bc6c..3ecd5ba 100644 (file)
@@ -106,6 +106,11 @@ struct ProvisionException: public Exception {
         : Exception(message) {}
 };
 
+struct UserNSException: public Exception {
+    explicit UserNSException(const std::string& message = "User namespace error")
+        : Exception(message) {}
+};
+
 } // namespace lxcpp
 
 #endif // LXCPP_EXCEPTION_HPP
index bc5f112..8a9f173 100644 (file)
 #include "lxcpp/utils.hpp"
 #include "lxcpp/guard/guard.hpp"
 #include "lxcpp/process.hpp"
+#include "lxcpp/credentials.hpp"
 #include "lxcpp/commands/prep-guest-terminal.hpp"
 #include "lxcpp/commands/provision.hpp"
+#include "lxcpp/commands/setup-userns.hpp"
 
 #include "config/manager.hpp"
 #include "logger/logger.hpp"
 
 namespace lxcpp {
 
-namespace {
-
-int startContainer(void* data)
+int Guard::startContainer(void* data)
 {
-    ContainerConfig& config = *static_cast<ContainerConfig*>(data);
+    ContainerConfig& config = static_cast<ContainerData*>(data)->mConfig;
+    utils::Channel& channel = static_cast<ContainerData*>(data)->mChannel;
+
+    // wait for continue sync from guard
+    channel.setRight();
+    channel.read<bool>();
+    channel.shutdown();
 
-    // TODO: container preparation part 2
+    // TODO: container preparation part 3: things to do in the container process
 
     Provisions provisions(config);
     provisions.execute();
@@ -49,14 +55,18 @@ int startContainer(void* data)
     PrepGuestTerminal terminals(config.mTerminals);
     terminals.execute();
 
+    if (config.mUserNSConfig.mUIDMaps.size()) {
+        lxcpp::setreuid(0, 0);
+    }
+    if (config.mUserNSConfig.mGIDMaps.size()) {
+        lxcpp::setregid(0, 0);
+    }
+
     lxcpp::execve(config.mInit);
 
     return EXIT_FAILURE;
 }
 
-} // namespace
-
-
 Guard::Guard(const int channelFD)
     : mChannel(channelFD)
 {
@@ -85,10 +95,13 @@ Guard::~Guard()
 
 int Guard::execute()
 {
-    // TODO: container preparation part 1
+    // TODO: container preparation part 1: things to do before clone
+
+    utils::Channel channel;
+    ContainerData data(mConfig, channel);
 
-    const pid_t initPid = lxcpp::clone(startContainer,
-                                       &mConfig,
+    const pid_t initPid = lxcpp::clone(Guard::startContainer,
+                                       &data,
                                        mConfig.mNamespaces);
 
     mConfig.mGuardPid = ::getpid();
@@ -98,10 +111,21 @@ int Guard::execute()
     mChannel.write(mConfig.mInitPid);
     mChannel.shutdown();
 
+    // TODO: container preparation part 2: things to do immediately after clone
+
+    SetupUserNS userNS(mConfig.mUserNSConfig, mConfig.mInitPid);
+    userNS.execute();
+
+    // send continue sync to container once userns, netns, cgroups, etc, are configured
+    channel.setLeft();
+    channel.write(true);
+    channel.shutdown();
+
     int status = lxcpp::waitpid(initPid);
     LOGD("Init exited with status: " << status);
 
-    // TODO: cleanup after child exits
+    // TODO: container (de)preparation part 4: cleanup after container quits
+
     Provisions provisions(mConfig);
     provisions.revert();
 
index 741d72a..fb53546 100644 (file)
@@ -39,6 +39,16 @@ public:
     int execute();
 
 private:
+    struct ContainerData {
+        ContainerConfig &mConfig;
+        utils::Channel &mChannel;
+
+        ContainerData(ContainerConfig &config, utils::Channel &channel)
+            : mConfig(config), mChannel(channel) {}
+    };
+
+    static int startContainer(void *data);
+
     utils::Channel mChannel;
     ContainerConfig mConfig;
 };
diff --git a/libs/lxcpp/userns-config.hpp b/libs/lxcpp/userns-config.hpp
new file mode 100644 (file)
index 0000000..e560078
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsumg.com)
+ * @brief   User namespace configuration
+ */
+
+#ifndef LXCPP_USERNS_CONFIG_HPP
+#define LXCPP_USERNS_CONFIG_HPP
+
+#include "config/config.hpp"
+#include "config/fields.hpp"
+
+#include <vector>
+#include <string>
+
+
+namespace lxcpp {
+
+
+struct UserNSConfig {
+    // TODO: Replace UserNSMap with std::tuple
+    struct UserNSMap {
+        unsigned min;
+        unsigned max;
+        unsigned num;
+
+        UserNSMap() = default;
+        UserNSMap(unsigned min, unsigned max, unsigned num)
+            : min(min), max(max), num(num) {}
+
+        CONFIG_REGISTER
+        (
+            min,
+            max,
+            num
+        )
+    };
+
+    std::vector<UserNSMap> mUIDMaps;
+    std::vector<UserNSMap> mGIDMaps;
+
+    CONFIG_REGISTER
+    (
+        mUIDMaps,
+        mGIDMaps
+    )
+};
+
+
+} //namespace lxcpp
+
+
+#endif // LXCPP_USERNS_CONFIG_HPP
index 17eee93..1c1a76c 100644 (file)
@@ -116,7 +116,6 @@ BOOST_AUTO_TEST_CASE(StartStop)
     BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE,
                                       logger::LogLevel::DEBUG,
                                       LOGGER_FILE));
-    BOOST_CHECK_NO_THROW(c->setNamespaces(CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWUTS));
     BOOST_CHECK_NO_THROW(c->start());
     BOOST_CHECK_NO_THROW(c->stop());
 }