From 392371f70db6fe3df79a6e2306092857c4615a4b Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Fri, 8 Mar 2013 11:02:18 +0100 Subject: [PATCH] Create an extra namespace for build environments and private network. 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 | 6 ++++++ python/pakfire/builder.py | 18 ++++++++++++++++- python/pakfire/cli.py | 40 ++++++++++++++++++++++++++----------- python/src/_pakfiremodule.c | 13 ++++++++++++ python/src/util.c | 22 ++++++++++++++++++++ python/src/util.h | 1 + 6 files changed, 87 insertions(+), 13 deletions(-) diff --git a/examples/builder.conf b/examples/builder.conf index 128a118c8..978c7d919 100644 --- a/examples/builder.conf +++ b/examples/builder.conf @@ -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 diff --git a/python/pakfire/builder.py b/python/pakfire/builder.py index 250a65979..5cb00aab7 100644 --- a/python/pakfire/builder.py +++ b/python/pakfire/builder.py @@ -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() diff --git a/python/pakfire/cli.py b/python/pakfire/cli.py index 232aad873..a80b397ef 100644 --- a/python/pakfire/cli.py +++ b/python/pakfire/cli.py @@ -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 diff --git a/python/src/_pakfiremodule.c b/python/src/_pakfiremodule.c index 4c94c5a9b..c20863491 100644 --- a/python/src/_pakfiremodule.c +++ b/python/src/_pakfiremodule.c @@ -18,9 +18,14 @@ # # #############################################################################*/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include +#include #include #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)); diff --git a/python/src/util.c b/python/src/util.c index ed555f549..acea90a0d 100644 --- a/python/src/util.c +++ b/python/src/util.c @@ -18,11 +18,18 @@ # # #############################################################################*/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include +#include +#include #include #include +#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; diff --git a/python/src/util.h b/python/src/util.h index 6ed9d1460..0322d1c8f 100644 --- a/python/src/util.h +++ b/python/src/util.h @@ -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 -- 2.39.5