[lxc-devel] [RFC 1/1] Introduce support for seccomp.

Serge Hallyn serge.hallyn at canonical.com
Sat Jul 28 02:13:53 UTC 2012


Hi,

This patch is so far just a proof of concept.  The libseccomp api will be
changing soon so it probably wouldn't be worth pulling this until it is
updated for the new API. 

This patch introduces support for seccomp to lxc.  Seccomp lets a program
restrict its own (and its children's) future access to system calls.  It
uses a simple whitelist system call policy file.  It would probably be
better to switch to something more symbolic (i.e specifying 'open' rather
than the syscall #, especially given container arch flexibility).

I just wanted to get this out there as a first step.  You can also get
source for an ubuntu package based on this patch at
https://code.launchpad.net/~serge-hallyn/ubuntu/quantal/lxc/lxc-seccomp

Signed-off-by: Serge Hallyn <serge.hallyn at canonical.com>
---
 README               |   24 ++++++++++
 configure.ac         |   10 +++++
 src/lxc/Makefile.am  |    9 +++-
 src/lxc/conf.h       |    1 +
 src/lxc/confile.c    |   22 +++++++++
 src/lxc/lxc-clone.in |    2 +-
 src/lxc/lxcseccomp.h |   41 +++++++++++++++++
 src/lxc/seccomp.c    |  121 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/start.c      |    8 ++++
 9 files changed, 235 insertions(+), 3 deletions(-)
 create mode 100644 src/lxc/lxcseccomp.h
 create mode 100644 src/lxc/seccomp.c

diff --git a/README b/README
index 74fa6ff..0cf0246 100644
--- a/README
+++ b/README
@@ -52,3 +52,27 @@ Portability:
 
 AUTHOR
        Daniel Lezcano <daniel.lezcano at free.fr>
+
+Seccomp with LXC
+----------------
+
+To restrict a container with seccomp, you must specify a profile which is
+basically a whitelist of system calls it may execute.  In the container
+config file, add a line like
+
+lxc.seccomp = /var/lib/lxc/q1/seccomp.full
+
+I created a usable (but basically worthless) seccomp.full file using
+
+cat > seccomp.full << EOF
+1
+whitelist
+EOF
+for i in `seq 0 300`; do
+	echo $i >> secomp.full
+done
+for i in `seq 1024 1079`; do
+	echo $i >> seccomp.full
+done
+
+ -- Serge Hallyn <serge.hallyn at ubuntu.com>  Fri, 27 Jul 2012 15:47:02 +0600
diff --git a/configure.ac b/configure.ac
index 70c74ec..7f3fdbe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,6 +23,11 @@ AC_ARG_ENABLE([apparmor],
 	[], [enable_apparmor=yes])
 AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"])
 
+AC_ARG_ENABLE([seccomp],
+	[AC_HELP_STRING([--enable-seccomp], [enable seccomp])],
+	[], [enable_seccomp=yes])
+AM_CONDITIONAL([ENABLE_SECCOMP], [test "x$enable_seccomp" = "xyes"])
+
 AC_ARG_ENABLE([doc],
 	[AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])],
 	[], [enable_doc=auto])
@@ -39,6 +44,11 @@ AM_COND_IF([ENABLE_APPARMOR],
      AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
      AC_SUBST([APPARMOR_LIBS], [-lapparmor])])
 
+AM_COND_IF([ENABLE_SECCOMP],
+    [AC_CHECK_HEADER([seccomp.h],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])])
+     AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])])
+     AC_SUBST([SECCOMP_LIBS], [-lseccomp])])
+
 AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"])
 
 AC_ARG_ENABLE([examples],
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 50e67bb..29dfa00 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -50,6 +50,7 @@ liblxc_so_SOURCES = \
         genl.c genl.h \
 	\
 	caps.c caps.h \
+	seccomp.c seccomp.h \
 	mainloop.c mainloop.h \
 	af_unix.c af_unix.h \
 	\
@@ -65,13 +66,17 @@ if ENABLE_APPARMOR
 AM_CFLAGS += -DHAVE_APPARMOR
 endif
 
+if ENABLE_SECCOMP
+AM_CFLAGS += -DHAVE_SECCOMP
+endif
+
 liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
 
 liblxc_so_LDFLAGS = \
 	-shared \
 	-Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION)))
 
-liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS)
+liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS)
 
 bin_SCRIPTS = \
 	lxc-ps \
@@ -110,7 +115,7 @@ AM_LDFLAGS = -Wl,-E
 if ENABLE_RPATH
 AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
 endif
-LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@
+LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@
 
 lxc_attach_SOURCES = lxc_attach.c
 lxc_cgroup_SOURCES = lxc_cgroup.c
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 7c1ded0..f76efd1 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -229,6 +229,7 @@ struct lxc_conf {
 #if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
 	int lsm_umount_proc;
 #endif
+	char *seccomp;  // filename with the seccomp rules
 };
 
 int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 87608cc..67cf821 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -75,6 +75,7 @@ static int config_network_ipv6(const char *, char *, struct lxc_conf *);
 static int config_network_ipv6_gateway(const char *, char *, struct lxc_conf *);
 static int config_cap_drop(const char *, char *, struct lxc_conf *);
 static int config_console(const char *, char *, struct lxc_conf *);
+static int config_seccomp(const char *, char *, struct lxc_conf *);
 
 typedef int (*config_cb)(const char *, char *, struct lxc_conf *);
 
@@ -118,6 +119,7 @@ static struct config config[] = {
 	{ "lxc.network.ipv6",         config_network_ipv6         },
 	{ "lxc.cap.drop",             config_cap_drop             },
 	{ "lxc.console",              config_console              },
+	{ "lxc.seccomp",              config_seccomp              },
 };
 
 static const size_t config_size = sizeof(config)/sizeof(struct config);
@@ -609,6 +611,26 @@ static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook)
 	return 0;
 }
 
+static int config_seccomp(const char *key, char *value,
+				 struct lxc_conf *lxc_conf)
+{
+	char *path;
+
+	if (lxc_conf->seccomp) {
+		ERROR("seccomp already defined");
+		return -1;
+	}
+	path = strdup(value);
+	if (!path) {
+		SYSERROR("failed to strdup '%s': %m", value);
+		return -1;
+	}
+
+	lxc_conf->seccomp = path;
+
+	return 0;
+}
+
 static int config_hook(const char *key, char *value,
 				 struct lxc_conf *lxc_conf)
 {
diff --git a/src/lxc/lxc-clone.in b/src/lxc/lxc-clone.in
index d9ed78c..9292e10 100644
--- a/src/lxc/lxc-clone.in
+++ b/src/lxc/lxc-clone.in
@@ -176,7 +176,7 @@ cp $lxc_path/$lxc_orig/config $lxc_path/$lxc_new/config
 sed -i '/lxc.utsname/d' $lxc_path/$lxc_new/config
 echo "lxc.utsname = $hostname" >> $lxc_path/$lxc_new/config
 
-grep "lxc.mount[ \t]" $lxc_path/$lxc_new/config >/dev/null 2>&1 && { sed -i '/lxc.mount[ \t]/d' $lxc_path/$lxc_new/config; echo "lxc.mount = $lxc_path/$lxc_new/fstab" >> $lxc_path/$lxc_new/config; }
+grep "lxc.mount =" $lxc_path/$lxc_new/config >/dev/null 2>&1 && { sed -i '/lxc.mount =/d' $lxc_path/$lxc_new/config; echo "lxc.mount = $lxc_path/$lxc_new/fstab" >> $lxc_path/$lxc_new/config; }
 
 if [ -e  $lxc_path/$lxc_orig/fstab ];then
     cp $lxc_path/$lxc_orig/fstab $lxc_path/$lxc_new/fstab
diff --git a/src/lxc/lxcseccomp.h b/src/lxc/lxcseccomp.h
new file mode 100644
index 0000000..00262a5
--- /dev/null
+++ b/src/lxc/lxcseccomp.h
@@ -0,0 +1,41 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright Canonical, Inc. 2012
+ *
+ * Authors:
+ * Serge Hallyn <serge.hallyn at canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _lxc_seccomp_h
+
+#include "conf.h"
+
+#ifdef HAVE_SECCOMP
+int lxc_seccomp_load(struct lxc_conf *conf);
+int lxc_read_seccomp_config(struct lxc_conf *conf);
+#else
+static inline int lxc_seccomp_load(struct lxc_conf *conf) {
+	return 0;
+}
+
+static inline int lxc_read_seccomp_config(struct lxc_conf *conf) {
+	return 0;
+}
+#endif
+
+#endif
diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c
new file mode 100644
index 0000000..f2c5d00
--- /dev/null
+++ b/src/lxc/seccomp.c
@@ -0,0 +1,121 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright Canonical, Inc. 2012
+ *
+ * Authors:
+ * Serge Hallyn <serge.hallyn at canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <seccomp.h>
+#include <errno.h>
+#include <seccomp.h>
+#include "lxcseccomp.h"
+
+#include "log.h"
+
+lxc_log_define(lxc_seccomp, lxc);
+
+/*
+ * The first line of the config file has a policy language version
+ * the second line has some directives
+ * then comes policy subject to the directives
+ * right now version must be '1'
+ * the directives must include 'whitelist' (only type of policy currently
+ * supported) and can include 'debug' (though debug is not yet supported).
+ */
+static int parse_config(FILE *f, struct lxc_conf *conf)
+{
+	char line[1024];
+	int ret, version;
+
+	ret = fscanf(f, "%d\n", &version);
+	if (ret != 1 || version != 1) {
+		ERROR("invalid version");
+		return -1;
+	}
+	if (!fgets(line, 1024, f)) {
+		ERROR("invalid config file");
+		return -1;
+	}
+	if (!strstr(line, "whitelist")) {
+		ERROR("only whitelist policy is supported");
+		return -1;
+	}
+	if (strstr(line, "debug")) {
+		ERROR("debug not yet implemented");
+		return -1;
+	}
+	/* now read in the whitelist entries one per line */
+	while (fgets(line, 1024, f)) {
+		int nr;
+		ret = sscanf(line, "%d", &nr);
+		if (ret != 1)
+			return -1;
+		ret = seccomp_rule_add(SCMP_ACT_ALLOW, nr, 0);
+		if (ret < 0) {
+			ERROR("failed loading allow rule for %d\n", nr);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+int lxc_read_seccomp_config(struct lxc_conf *conf)
+{
+	FILE *f;
+	int ret;
+
+	if (seccomp_init(SCMP_ACT_ERRNO(31)) < 0)  { /* for debug, pass in SCMP_ACT_TRAP */
+		ERROR("failed initializing seccomp");
+		return -1;
+	}
+	if (!conf->seccomp)
+		return 0;
+
+	/* turn of no-new-privs.  We don't want it in lxc, and it breaks
+	 * with apparmor */
+	if (seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0)) {
+		ERROR("failed to turn off n-new-privs\n");
+		return -1;
+	}
+
+	f = fopen(conf->seccomp, "r");
+	if (!f) {
+		SYSERROR("failed to open seccomp policy file %s\n", conf->seccomp);
+		return -1;
+	}
+	ret = parse_config(f, conf);
+	fclose(f);
+	return ret;
+}
+
+int lxc_seccomp_load(struct lxc_conf *conf)
+{
+	int ret;
+	if (!conf->seccomp)
+		return 0;
+	ret = seccomp_load();
+	if (ret < 0) {
+		ERROR("Error loading the seccomp policy");
+		return -1;
+	}
+	return 0;
+}
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 48e9962..f3ca569 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -353,6 +353,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
 		goto out_free;
 	}
 
+	if (lxc_read_seccomp_config(conf) != 0) {
+		ERROR("failed loading seccomp policy");
+		goto out_free_name;
+	}
+
 	/* Begin the set the state to STARTING*/
 	if (lxc_set_state(name, handler, STARTING)) {
 		ERROR("failed to set state '%s'", lxc_state2str(STARTING));
@@ -526,6 +531,9 @@ static int do_start(void *data)
 	if (apparmor_load(handler) < 0)
 		goto out_warn_father;
 
+	if (lxc_seccomp_load(handler->conf) != 0)
+		goto out_warn_father;
+
 	close(handler->sigfd);
 
 	HOOK(handler->name, "start", handler->conf);
-- 
1.7.10.4





More information about the lxc-devel mailing list