]> git.ipfire.org Git - people/ms/pakfire.git/commitdiff
Create an extra namespace for build environments and private network.
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 8 Mar 2013 10:02:18 +0000 (11:02 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 8 Mar 2013 10:02:18 +0000 (11:02 +0100)
Create a python binding for unshare(2) and use this to
unshare the IPC and UTS namespace (if the kernel supports that).

Also add the option to use private networking in the container.

examples/builder.conf
python/pakfire/builder.py
python/pakfire/cli.py
python/src/_pakfiremodule.c
python/src/util.c
python/src/util.h

index 128a118c831c0e524bcad4651e6180b74063d8a0..978c7d91936d9c397fb1c2a59d53547f564ec219 100644 (file)
@@ -23,6 +23,12 @@ file = /var/log/pakfire-builder.log
 # Create loop devices in build environment.
 #use_loop_devices = true
 
+# Use private network.
+# Setting this to true will result in the build
+# chroot having its own network - i.e. no network connection
+# to the outside world.
+#private_network = false
+
 [ccache]
 # Turn on compression to get more files into the cache.
 #compress = true
index 250a659799c4869ab9f903054b8b4213cce3cca5..5cb00aab7c524ab92a08fa0f5be8935e5cb69cf1 100644 (file)
@@ -69,7 +69,7 @@ class BuildEnviron(object):
        # The version of the kernel this machine is running.
        kernel_version = os.uname()[2]
 
-       def __init__(self, pakfire, filename=None, distro_name=None, build_id=None, logfile=None, release_build=True):
+       def __init__(self, pakfire, filename=None, distro_name=None, build_id=None, logfile=None, release_build=True, **kwargs):
                self.pakfire = pakfire
 
                # Check if the given pakfire instance is of the correct type.
@@ -117,6 +117,7 @@ class BuildEnviron(object):
                        "enable_icecream"     : self.config.get_bool("builder", "use_icecream", False),
                        "sign_packages"       : False,
                        "buildroot_tmpfs"     : self.config.get_bool("builder", "use_tmpfs", False),
+                       "private_network"     : self.config.get_bool("builder", "private_network", False),
                }
 
                # Get ccache settings.
@@ -130,6 +131,9 @@ class BuildEnviron(object):
                if self.keyring.get_host_key(secret=True):
                        self.settings["sign_packages"] = True
 
+               # Add settings from keyword arguments.
+               self.settings.update(kwargs)
+
                # Where do we put the result?
                self.resultdir = os.path.join(self.pakfire.path, "result")
 
@@ -164,6 +168,14 @@ class BuildEnviron(object):
        def start(self):
                assert not self.pakfire.initialized, "Pakfire has already been initialized"
 
+               # Unshare namepsace.
+               # If this fails because the kernel has no support for CLONE_NEWIPC or CLONE_NEWUTS,
+               # we try to fall back to just set CLONE_NEWNS.
+               try:
+                       _pakfire.unshare(_pakfire.SCHED_CLONE_NEWNS|_pakfire.SCHED_CLONE_NEWIPC|_pakfire.SCHED_CLONE_NEWUTS)
+               except RuntimeError, e:
+                       _pakfire.unshare(_pakfire.SCHED_CLONE_NEWNS)
+
                # Mount the directories.
                self._mountall()
 
@@ -173,6 +185,10 @@ class BuildEnviron(object):
                # Initialize pakfire instance.
                self.pakfire.initialize()
 
+               # Optionally enable private networking.
+               if self.settings.get("private_network", None):
+                       _pakfire.unshare(_pakfire.SCHED_CLONE_NEWNET)
+
                # Populate /dev.
                self.populate_dev()
 
index 232aad87329a1e871da8297a6f47b1647cd5c4a9..a80b397efbd987b29bc9555195282ccaa6a90e63 100644 (file)
@@ -543,6 +543,8 @@ class CliBuilder(Cli):
                        help=_("Run a shell after a successful build."))
                sub_build.add_argument("--no-install-test", action="store_true",
                        help=_("Do not perform the install test."))
+               sub_build.add_argument("--private-network", action="store_true",
+                       help=_("Disable network in container."))
 
        def parse_command_shell(self):
                # Implement the "shell" command.
@@ -554,6 +556,8 @@ class CliBuilder(Cli):
 
                sub_shell.add_argument("-m", "--mode", nargs="?", default="development",
                        help=_("Mode to run in. Is either 'release' or 'development' (default)."))
+               sub_shell.add_argument("--private-network", action="store_true",
+                       help=_("Disable network in container."))
 
        def parse_command_dist(self):
                # Implement the "dist" command.
@@ -580,22 +584,25 @@ class CliBuilder(Cli):
                else:
                        raise FileNotFoundError, pkg
 
-               # Check whether to enable the install test.
-               install_test = not self.args.no_install_test
+               # Build argument list.
+               kwargs = {
+                       "after_shell"   : self.args.after_shell,
+                       # Check whether to enable the install test.
+                       "install_test"  : not self.args.no_install_test,
+                       "result_dir"    : [self.args.resultdir,],
+                       "shell"         : True,
+               }
 
                if self.args.mode == "release":
-                       release_build = True
+                       kwargs["release_build"] = True
                else:
-                       release_build = False
+                       kwargs["release_build"] = False
+
+               if self.args.private_network:
+                       kwargs["private_network"] = True
 
                p = self.create_pakfire()
-               p.build(pkg,
-                       install_test=install_test,
-                       resultdirs=[self.args.resultdir,],
-                       shell=True,
-                       after_shell=self.args.after_shell,
-                       release_build=release_build,
-               )
+               p.build(pkg, **kwargs)
 
        def handle_shell(self):
                pkg = None
@@ -617,7 +624,16 @@ class CliBuilder(Cli):
                        release_build = False
 
                p = self.create_pakfire()
-               p.shell(pkg, release_build=release_build)
+
+               kwargs = {
+                       "release_build" : release_build,
+               }
+
+               # Private network
+               if self.args.private_network:
+                       kwargs["private_network"] = True
+
+               p.shell(pkg, **kwargs)
 
        def handle_dist(self):
                # Get the packages from the command line options
index 4c94c5a9b276db332ea47c74a6e5b3e241a07112..c208634919ffe1e8ba609d91274c71b961af250a 100644 (file)
 #                                                                             #
 #############################################################################*/
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 #include <Python.h>
 
 #include <locale.h>
+#include <sched.h>
 #include <sys/personality.h>
 
 #include "capabilities.h"
@@ -43,6 +48,7 @@ static PyMethodDef pakfireModuleMethods[] = {
        {"set_capabilities", (PyCFunction)set_capabilities, METH_VARARGS, NULL},
        {"personality", (PyCFunction)_personality, METH_VARARGS, NULL},
        {"sync", (PyCFunction)_sync, METH_NOARGS, NULL},
+       {"unshare", (PyCFunction)_unshare, METH_VARARGS, NULL},
        { NULL, NULL, 0, NULL }
 };
 
@@ -275,6 +281,13 @@ void init_pakfire(void) {
        PyDict_SetItemString(d, "PERSONALITY_LINUX",   Py_BuildValue("i", PER_LINUX));
        PyDict_SetItemString(d, "PERSONALITY_LINUX32", Py_BuildValue("i", PER_LINUX32));
 
+       // Namespace stuff
+       PyDict_SetItemString(d, "SCHED_CLONE_NEWIPC", Py_BuildValue("i", CLONE_NEWIPC));
+       PyDict_SetItemString(d, "SCHED_CLONE_NEWPID", Py_BuildValue("i", CLONE_NEWPID));
+       PyDict_SetItemString(d, "SCHED_CLONE_NEWNET", Py_BuildValue("i", CLONE_NEWNET));
+       PyDict_SetItemString(d, "SCHED_CLONE_NEWNS",  Py_BuildValue("i", CLONE_NEWNS));
+       PyDict_SetItemString(d, "SCHED_CLONE_NEWUTS", Py_BuildValue("i", CLONE_NEWUTS));
+
        // Add constants for relations
        PyDict_SetItemString(d, "REL_EQ", Py_BuildValue("i", REL_EQ));
        PyDict_SetItemString(d, "REL_LT", Py_BuildValue("i", REL_LT));
index ed555f5496dc8d9ed6fca6fa1973b6a39278230e..acea90a0d642da13178a34542ae05b8e933874b4 100644 (file)
 #                                                                             #
 #############################################################################*/
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 #include <Python.h>
 
+#include <errno.h>
+#include <sched.h>
 #include <sys/personality.h>
 #include <unistd.h>
 
+#include "config.h"
 #include "util.h"
 
 PyObject *_personality(PyObject *self, PyObject *args) {
@@ -52,6 +59,21 @@ PyObject *_sync(PyObject *self, PyObject *args) {
        Py_RETURN_NONE;
 }
 
+PyObject *_unshare(PyObject *self, PyObject *args) {
+       int flags = 0;
+
+       if (!PyArg_ParseTuple(args, "i", &flags)) {
+               return NULL;
+       }
+
+       int ret = unshare(flags);
+       if (ret < 0) {
+               return PyErr_SetFromErrno(PyExc_RuntimeError);
+       }
+
+       return Py_BuildValue("i", ret);
+}
+
 PyObject *version_compare(PyObject *self, PyObject *args) {
        Pool *pool;
        const char *evr1, *evr2;
index 6ed9d1460e913bf8624ac39c9b9c7f4e3212bff4..0322d1c8f0cbbd3ce4f19591c8bd2ff210822c07 100644 (file)
@@ -27,6 +27,7 @@
 
 extern PyObject *_personality(PyObject *self, PyObject *args);
 extern PyObject *_sync(PyObject *self, PyObject *args);
+extern PyObject *_unshare(PyObject *self, PyObject *args);
 extern PyObject *version_compare(PyObject *self, PyObject *args);
 
 #endif