Commit 8f2c3a70 by Serge Hallyn Committed by Stéphane Graber

Introduce support for seccomp.

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-seccompSigned-off-by: 's avatarSerge Hallyn <serge.hallyn@canonical.com>
parent b486346a
...@@ -52,3 +52,27 @@ Portability: ...@@ -52,3 +52,27 @@ Portability:
AUTHOR AUTHOR
Daniel Lezcano <daniel.lezcano@free.fr> Daniel Lezcano <daniel.lezcano@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@ubuntu.com> Fri, 27 Jul 2012 15:47:02 +0600
...@@ -23,6 +23,11 @@ AC_ARG_ENABLE([apparmor], ...@@ -23,6 +23,11 @@ AC_ARG_ENABLE([apparmor],
[], [enable_apparmor=yes]) [], [enable_apparmor=yes])
AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"]) 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_ARG_ENABLE([doc],
[AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])], [AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])],
[], [enable_doc=auto]) [], [enable_doc=auto])
...@@ -39,6 +44,11 @@ AM_COND_IF([ENABLE_APPARMOR], ...@@ -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_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])]) 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"]) AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"])
AC_ARG_ENABLE([examples], AC_ARG_ENABLE([examples],
......
...@@ -50,6 +50,7 @@ liblxc_so_SOURCES = \ ...@@ -50,6 +50,7 @@ liblxc_so_SOURCES = \
genl.c genl.h \ genl.c genl.h \
\ \
caps.c caps.h \ caps.c caps.h \
seccomp.c seccomp.h \
mainloop.c mainloop.h \ mainloop.c mainloop.h \
af_unix.c af_unix.h \ af_unix.c af_unix.h \
\ \
...@@ -65,13 +66,17 @@ if ENABLE_APPARMOR ...@@ -65,13 +66,17 @@ if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR
endif endif
if ENABLE_SECCOMP
AM_CFLAGS += -DHAVE_SECCOMP
endif
liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
liblxc_so_LDFLAGS = \ liblxc_so_LDFLAGS = \
-shared \ -shared \
-Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION))) -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 = \ bin_SCRIPTS = \
lxc-ps \ lxc-ps \
...@@ -110,7 +115,7 @@ AM_LDFLAGS = -Wl,-E ...@@ -110,7 +115,7 @@ AM_LDFLAGS = -Wl,-E
if ENABLE_RPATH if ENABLE_RPATH
AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
endif endif
LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@
lxc_attach_SOURCES = lxc_attach.c lxc_attach_SOURCES = lxc_attach.c
lxc_cgroup_SOURCES = lxc_cgroup.c lxc_cgroup_SOURCES = lxc_cgroup.c
......
...@@ -229,6 +229,7 @@ struct lxc_conf { ...@@ -229,6 +229,7 @@ struct lxc_conf {
#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */ #if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
int lsm_umount_proc; int lsm_umount_proc;
#endif #endif
char *seccomp; // filename with the seccomp rules
}; };
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
......
...@@ -75,6 +75,7 @@ static int config_network_ipv6(const char *, char *, struct lxc_conf *); ...@@ -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_network_ipv6_gateway(const char *, char *, struct lxc_conf *);
static int config_cap_drop(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_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 *); typedef int (*config_cb)(const char *, char *, struct lxc_conf *);
...@@ -118,6 +119,7 @@ static struct config config[] = { ...@@ -118,6 +119,7 @@ static struct config config[] = {
{ "lxc.network.ipv6", config_network_ipv6 }, { "lxc.network.ipv6", config_network_ipv6 },
{ "lxc.cap.drop", config_cap_drop }, { "lxc.cap.drop", config_cap_drop },
{ "lxc.console", config_console }, { "lxc.console", config_console },
{ "lxc.seccomp", config_seccomp },
}; };
static const size_t config_size = sizeof(config)/sizeof(struct config); static const size_t config_size = sizeof(config)/sizeof(struct config);
...@@ -610,6 +612,26 @@ static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) ...@@ -610,6 +612,26 @@ static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook)
return 0; 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, static int config_hook(const char *key, char *value,
struct lxc_conf *lxc_conf) struct lxc_conf *lxc_conf)
{ {
......
...@@ -175,7 +175,7 @@ cp $lxc_path/$lxc_orig/config $lxc_path/$lxc_new/config ...@@ -175,7 +175,7 @@ cp $lxc_path/$lxc_orig/config $lxc_path/$lxc_new/config
sed -i '/lxc.utsname/d' $lxc_path/$lxc_new/config sed -i '/lxc.utsname/d' $lxc_path/$lxc_new/config
echo "lxc.utsname = $hostname" >> $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 if [ -e $lxc_path/$lxc_orig/fstab ];then
cp $lxc_path/$lxc_orig/fstab $lxc_path/$lxc_new/fstab cp $lxc_path/$lxc_orig/fstab $lxc_path/$lxc_new/fstab
......
/*
* lxc: linux Container library
*
* (C) Copyright Canonical, Inc. 2012
*
* Authors:
* Serge Hallyn <serge.hallyn@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
/*
* lxc: linux Container library
*
* (C) Copyright Canonical, Inc. 2012
*
* Authors:
* Serge Hallyn <serge.hallyn@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;
}
...@@ -353,6 +353,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf) ...@@ -353,6 +353,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
goto out_free; 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*/ /* Begin the set the state to STARTING*/
if (lxc_set_state(name, handler, STARTING)) { if (lxc_set_state(name, handler, STARTING)) {
ERROR("failed to set state '%s'", lxc_state2str(STARTING)); ERROR("failed to set state '%s'", lxc_state2str(STARTING));
...@@ -530,6 +535,9 @@ static int do_start(void *data) ...@@ -530,6 +535,9 @@ static int do_start(void *data)
if (apparmor_load(handler) < 0) if (apparmor_load(handler) < 0)
goto out_warn_father; goto out_warn_father;
if (lxc_seccomp_load(handler->conf) != 0)
goto out_warn_father;
if (run_lxc_hooks(handler->name, "start", handler->conf)) { if (run_lxc_hooks(handler->name, "start", handler->conf)) {
ERROR("failed to run start hooks for container '%s'.", handler->name); ERROR("failed to run start hooks for container '%s'.", handler->name);
goto out_warn_father; goto out_warn_father;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment