[lxc-devel] [PATCH] refactor AppArmor into LSM backend, add SELinux support
Dwight Engen
dwight.engen at oracle.com
Tue Sep 24 14:20:53 UTC 2013
Currently, a maximum of one LSM within LXC will be initialized and
used. If in the future stacked LSMs become a reality, we can support it
without changing the configuration syntax and add support for more than
a single LSM at a time to the lsm code.
Generic LXC code should note that lsm_process_label_set() will take
effect "now" for AppArmor, and upon exec() for SELinux.
- fix Oracle template mounting of proc and sysfs, needed when using SELinux
Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
---
configure.ac | 14 ++++
doc/lxc.conf.sgml.in | 25 ++++++
src/lxc/Makefile.am | 21 ++++-
src/lxc/apparmor.c | 219 ------------------------------------------------
src/lxc/apparmor.h | 56 -------------
src/lxc/attach.c | 31 +++----
src/lxc/attach.h | 2 +-
src/lxc/conf.c | 43 +++-------
src/lxc/conf.h | 12 +--
src/lxc/confile.c | 56 ++++++++-----
src/lxc/lsm/apparmor.c | 168 +++++++++++++++++++++++++++++++++++++
src/lxc/lsm/lsm.c | 156 ++++++++++++++++++++++++++++++++++
src/lxc/lsm/lsm.h | 52 ++++++++++++
src/lxc/lsm/nop.c | 46 ++++++++++
src/lxc/lsm/selinux.c | 101 ++++++++++++++++++++++
src/lxc/start.c | 15 +++-
src/lxc/start.h | 3 -
templates/lxc-oracle.in | 9 +-
18 files changed, 663 insertions(+), 366 deletions(-)
delete mode 100644 src/lxc/apparmor.c
delete mode 100644 src/lxc/apparmor.h
create mode 100644 src/lxc/lsm/apparmor.c
create mode 100644 src/lxc/lsm/lsm.c
create mode 100644 src/lxc/lsm/lsm.h
create mode 100644 src/lxc/lsm/nop.c
create mode 100644 src/lxc/lsm/selinux.c
diff --git a/configure.ac b/configure.ac
index cffbdac..9d77bb5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -115,6 +115,20 @@ 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])])
+# SELinux
+AC_ARG_ENABLE([selinux],
+ [AC_HELP_STRING([--enable-selinux], [enable SELinux support])],
+ [], [enable_selinux=check])
+
+if test "x$enable_selinux" = xcheck; then
+ AC_CHECK_LIB([selinux],[setexeccon_raw],[enable_selinux=yes],[enable_selinux=no])
+fi
+AM_CONDITIONAL([ENABLE_SELINUX], [test "x$enable_selinux" = "xyes"])
+AM_COND_IF([ENABLE_SELINUX],
+ [AC_CHECK_HEADER([selinux/selinux.h],[],[AC_MSG_ERROR([You must install the SELinux development package in order to compile lxc])])
+ AC_CHECK_LIB([selinux], [setexeccon_raw],[],[AC_MSG_ERROR([You must install the SELinux development package in order to compile lxc])])
+ AC_SUBST([SELINUX_LIBS])])
+
# Seccomp syscall filter
AC_ARG_ENABLE([seccomp],
[AC_HELP_STRING([--enable-seccomp], [enable seccomp])],
diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in
index dc416e8..bad553c 100644
--- a/doc/lxc.conf.sgml.in
+++ b/doc/lxc.conf.sgml.in
@@ -811,6 +811,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</refsect2>
<refsect2>
+ <title>SELinux context</title>
+ <para>
+ If lxc was compiled and installed with SELinux support, and the host
+ system has SELinux enabled, then the SELinux context under which the
+ container should be run can be specified in the container
+ configuration. The default is <command>unconfined_t</command>,
+ which means that lxc will not attempt to change contexts.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>lxc.se_context</option>
+ </term>
+ <listitem>
+ <para>
+ Specify the SELinux context under which the container should
+ be run or <command>unconfined_t</command>. For example
+ </para>
+ <programlisting>lxc.se_context = unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023</programlisting>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
+ <refsect2>
<title>Seccomp configuration</title>
<para>
A container can be started with a reduced set of available
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index f19a994..873b97d 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -37,6 +37,18 @@ sodir=$(libdir)
# use PROGRAMS to avoid complains from automake
so_PROGRAMS = liblxc.so
+LSM_SOURCES = \
+ lsm/nop.c \
+ lsm/lsm.h lsm/lsm.c
+
+if ENABLE_APPARMOR
+LSM_SOURCES += lsm/apparmor.c
+endif
+
+if ENABLE_SELINUX
+LSM_SOURCES += lsm/selinux.c
+endif
+
liblxc_so_SOURCES = \
arguments.c arguments.h \
bdev.c bdev.h \
@@ -73,10 +85,11 @@ liblxc_so_SOURCES = \
af_unix.c af_unix.h \
\
lxcutmp.c lxcutmp.h \
- apparmor.c apparmor.h \
lxclock.h lxclock.c \
lxccontainer.c lxccontainer.h \
- version.c version.h
+ version.c version.h \
+ \
+ $(LSM_SOURCES)
if IS_BIONIC
liblxc_so_SOURCES += \
@@ -107,6 +120,10 @@ if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR
endif
+if ENABLE_SELINUX
+AM_CFLAGS += -DHAVE_SELINUX
+endif
+
if HAVE_NEWUIDMAP
AM_CFLAGS += -DHAVE_NEWUIDMAP
endif
diff --git a/src/lxc/apparmor.c b/src/lxc/apparmor.c
deleted file mode 100644
index 4dad801..0000000
--- a/src/lxc/apparmor.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/* apparmor
- *
- * Copyright © 2012 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2012 Canonical Ltd.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
-
-#include "log.h"
-#include "apparmor.h"
-
-lxc_log_define(lxc_apparmor, lxc);
-
-#if HAVE_APPARMOR
-#include <sys/apparmor.h>
-
-#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
-#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
-
-
-/* caller must free the returned profile */
-extern char *aa_get_profile(pid_t pid)
-{
- char path[100], *space;
- int ret;
- char *buf = NULL;
- int sz = 0;
- FILE *f;
-
- ret = snprintf(path, 100, "/proc/%d/attr/current", pid);
- if (ret < 0 || ret >= 100) {
- ERROR("path name too long");
- return NULL;
- }
-again:
- f = fopen(path, "r");
- if (!f) {
- SYSERROR("opening %s\n", path);
- if (buf)
- free(buf);
- return NULL;
- }
- sz += 1024;
- buf = realloc(buf, sz);
- memset(buf, 0, sz);
- if (!buf) {
- ERROR("out of memory");
- fclose(f);
- return NULL;
- }
- ret = fread(buf, 1, sz - 1, f);
- fclose(f);
- if (ret >= sz)
- goto again;
- if (ret < 0) {
- ERROR("reading %s\n", path);
- free(buf);
- return NULL;
- }
- space = index(buf, '\n');
- if (space)
- *space = '\0';
- space = index(buf, ' ');
- if (space)
- *space = '\0';
- return buf;
-}
-
-static int aa_am_unconfined(void)
-{
- char *p = aa_get_profile(getpid());
- int ret = 0;
- if (!p || strcmp(p, "unconfined") == 0)
- ret = 1;
- if (p)
- free(p);
- return ret;
-}
-
-/* aa_getcon is not working right now. Use our hand-rolled version below */
-static int check_apparmor_enabled(void)
-{
- struct stat statbuf;
- FILE *fin;
- char e;
- int ret;
-
- ret = stat(AA_MOUNT_RESTR, &statbuf);
- if (ret != 0)
- return 0;
- fin = fopen(AA_ENABLED_FILE, "r");
- if (!fin)
- return 0;
- ret = fscanf(fin, "%c", &e);
- fclose(fin);
- if (ret == 1 && e == 'Y')
- return 1;
- return 0;
-}
-
-extern void apparmor_handler_init(struct lxc_handler *handler)
-{
- handler->aa_enabled = check_apparmor_enabled();
- INFO("aa_enabled set to %d\n", handler->aa_enabled);
-}
-
-#define AA_DEF_PROFILE "lxc-container-default"
-
-extern int do_apparmor_load(int aa_enabled, char *aa_profile,
- int umount_proc, int dropprivs)
-{
- if (!aa_enabled) {
- INFO("apparmor not enabled");
- return 0;
- }
- INFO("setting up apparmor");
-
- if (!aa_profile)
- aa_profile = AA_DEF_PROFILE;
-
- if (strcmp(aa_profile, "unconfined") == 0 && !dropprivs && aa_am_unconfined()) {
- INFO("apparmor profile unchanged");
- return 0;
- }
-
- //if (aa_change_onexec(aa_profile) < 0) {
- if (aa_change_profile(aa_profile) < 0) {
- SYSERROR("failed to change apparmor profile to %s", aa_profile);
- return -1;
- }
- if (umount_proc == 1)
- umount("/proc");
-
- INFO("changed apparmor profile to %s", aa_profile);
-
- return 0;
-}
-
-extern int apparmor_load(struct lxc_handler *handler)
-{
- if (!handler->conf->aa_profile)
- handler->conf->aa_profile = AA_DEF_PROFILE;
- return do_apparmor_load(handler->aa_enabled,
- handler->conf->aa_profile,
- handler->conf->lsm_umount_proc, 0);
-}
-
-extern int attach_apparmor(char *profile)
-{
- if (!profile)
- return 0;
- if (!check_apparmor_enabled())
- return 0;
- if (strcmp(profile, "unconfined") == 0)
- return 0;
- return do_apparmor_load(1, profile, 0, 1);
-}
-
-/*
- * this will likely move to a generic lsm.c, as selinux and smack will both
- * also want proc mounted in the container so as to transition
- */
-extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt)
-{
- char path[MAXPATHLEN];
- char link[20];
- int linklen, ret;
-
- ret = snprintf(path, MAXPATHLEN, "%s/proc/self", root_src ? rootfs_tgt : "");
- if (ret < 0 || ret >= MAXPATHLEN) {
- SYSERROR("proc path name too long");
- return -1;
- }
- memset(link, 0, 20);
- linklen = readlink(path, link, 20);
- INFO("I am %d, /proc/self points to %s\n", getpid(), link);
- ret = snprintf(path, MAXPATHLEN, "%s/proc", root_src ? rootfs_tgt : "");
- if (linklen < 0) /* /proc not mounted */
- goto domount;
- /* can't be longer than rootfs/proc/1 */
- if (strncmp(link, "1", linklen) != 0) {
- /* wrong /procs mounted */
- umount2(path, MNT_DETACH); /* ignore failure */
- goto domount;
- }
- /* the right proc is already mounted */
- return 0;
-
-domount:
- if (mount("proc", path, "proc", 0, NULL))
- return -1;
- INFO("Mounted /proc for the container\n");
- return 1;
-}
-#else
-extern void apparmor_handler_init(struct lxc_handler *handler) {
- INFO("apparmor_load - apparmor is disabled");
-}
-#endif
diff --git a/src/lxc/apparmor.h b/src/lxc/apparmor.h
deleted file mode 100644
index e27a728..0000000
--- a/src/lxc/apparmor.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* apparmor
- *
- * Copyright © 2012 Serge Hallyn <serge.hallyn at ubuntu.com>.
- * Copyright © 2012 Canonical Ltd.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <lxc/start.h> /* for lxc_handler */
-#include <lxc/conf.h>
-
-struct lxc_handler;
-
-/*
- * apparmor_handler_init is really just a wrapper around check_apparmor_enabled
- * to allow us to keep from having #ifdef APPARMOR in start.c
- */
-extern void apparmor_handler_init(struct lxc_handler *handler);
-
-#if HAVE_APPARMOR
-extern char *aa_get_profile(pid_t pid);
-extern int do_apparmor_load(int aa_enabled, char *aa_profile,
- int umount_proc, int dropprivs);
-extern int apparmor_load(struct lxc_handler *handler);
-extern int attach_apparmor(char *profile);
-extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt);
-#else
-static inline char *aa_get_profile(pid_t pid) {
- return NULL;
-}
-static inline int do_apparmor_load(int aa_enabled, char *aa_profile,
- int umount_proc, int dropprivs) {
- return 0;
-}
-static inline int attach_apparmor(char *profile) {
- return 0;
-}
-static inline int apparmor_load(struct lxc_handler *handler) {
- return 0;
-}
-static inline int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt) {
- return 0;
-}
-#endif
diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 6f33252..e2f1308 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -46,10 +46,10 @@
#include "attach.h"
#include "caps.h"
#include "config.h"
-#include "apparmor.h"
#include "utils.h"
#include "commands.h"
#include "cgroup.h"
+#include "lsm/lsm.h"
#if HAVE_SYS_PERSONALITY_H
#include <sys/personality.h>
@@ -120,7 +120,7 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
errno = ENOENT;
goto out_error;
}
- info->aa_profile = aa_get_profile(pid);
+ info->lsm_label = lsm_process_label_get(pid);
return info;
@@ -129,6 +129,13 @@ out_error:
return NULL;
}
+static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx)
+{
+ if (ctx->lsm_label)
+ free(ctx->lsm_label);
+ free(ctx);
+}
+
int lxc_attach_to_ns(pid_t pid, int which)
{
char path[MAXPATHLEN];
@@ -618,8 +625,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
ERROR("failed to automatically determine the "
"namespaces which the container unshared");
free(cwd);
- free(init_ctx->aa_profile);
- free(init_ctx);
+ lxc_proc_put_context_info(init_ctx);
return -1;
}
}
@@ -655,8 +661,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
if (ret < 0) {
SYSERROR("could not set up required IPC mechanism for attaching");
free(cwd);
- free(init_ctx->aa_profile);
- free(init_ctx);
+ lxc_proc_put_context_info(init_ctx);
return -1;
}
@@ -677,8 +682,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
if (pid < 0) {
SYSERROR("failed to create first subprocess");
free(cwd);
- free(init_ctx->aa_profile);
- free(init_ctx);
+ lxc_proc_put_context_info(init_ctx);
return -1;
}
@@ -762,8 +766,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
/* now shut down communication with child, we're done */
shutdown(ipc_sockets[0], SHUT_RDWR);
close(ipc_sockets[0]);
- free(init_ctx->aa_profile);
- free(init_ctx);
+ lxc_proc_put_context_info(init_ctx);
/* we're done, the child process should now execute whatever
* it is that the user requested. The parent can now track it
@@ -781,8 +784,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
close(ipc_sockets[0]);
if (to_cleanup_pid)
(void) wait_for_pid(to_cleanup_pid);
- free(init_ctx->aa_profile);
- free(init_ctx);
+ lxc_proc_put_context_info(init_ctx);
return -1;
}
@@ -883,7 +885,7 @@ int attach_child_main(void* data)
/* load apparmor profile */
if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_APPARMOR)) {
- ret = attach_apparmor(init_ctx->aa_profile);
+ ret = lsm_process_label_set(init_ctx->lsm_label, 0);
if (ret < 0) {
shutdown(ipc_socket, SHUT_RDWR);
rexit(-1);
@@ -986,8 +988,7 @@ int attach_child_main(void* data)
shutdown(ipc_socket, SHUT_RDWR);
close(ipc_socket);
- free(init_ctx->aa_profile);
- free(init_ctx);
+ lxc_proc_put_context_info(init_ctx);
/* The following is done after the communication socket is
* shut down. That way, all errors that might (though
diff --git a/src/lxc/attach.h b/src/lxc/attach.h
index 518d086..b5f3ea1 100644
--- a/src/lxc/attach.h
+++ b/src/lxc/attach.h
@@ -28,7 +28,7 @@
#include "attach_options.h"
struct lxc_proc_context_info {
- char *aa_profile;
+ char *lsm_label;
unsigned long personality;
unsigned long long capability_mask;
};
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 79220d1..3f2e15b 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -73,10 +73,7 @@
#include "caps.h" /* for lxc_caps_last_cap() */
#include "bdev.h"
#include "cgroup.h"
-
-#if HAVE_APPARMOR
-#include <apparmor.h>
-#endif
+#include "lsm/lsm.h"
#if HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
@@ -2332,12 +2329,9 @@ struct lxc_conf *lxc_conf_init(void)
lxc_list_init(&new->id_map);
for (i=0; i<NUM_LXC_HOOKS; i++)
lxc_list_init(&new->hooks[i]);
-#if HAVE_APPARMOR
- new->aa_profile = NULL;
-#endif
-#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
+ new->lsm_aa_profile = NULL;
+ new->lsm_se_context = NULL;
new->lsm_umount_proc = 0;
-#endif
return new;
}
@@ -2968,10 +2962,6 @@ int uid_shift_ttys(int pid, struct lxc_conf *conf)
int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath, struct cgroup_process_info *cgroup_info)
{
-#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
- int mounted;
-#endif
-
if (setup_utsname(lxc_conf->utsname)) {
ERROR("failed to setup the utsname for '%s'", name);
return -1;
@@ -3065,24 +3055,11 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath,
return -1;
}
-#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
- INFO("rootfs path is .%s., mount is .%s.", lxc_conf->rootfs.path,
- lxc_conf->rootfs.mount);
- if (lxc_conf->rootfs.path == NULL || strlen(lxc_conf->rootfs.path) == 0) {
- if (mount("proc", "/proc", "proc", 0, NULL)) {
- SYSERROR("Failed mounting /proc, proceeding");
- mounted = 0;
- } else
- mounted = 1;
- } else
- mounted = lsm_mount_proc_if_needed(lxc_conf->rootfs.path, lxc_conf->rootfs.mount);
- if (mounted == -1) {
- SYSERROR("failed to mount /proc in the container.");
+ /* mount /proc if needed for LSM transition */
+ if (lsm_proc_mount(lxc_conf) < 0) {
+ ERROR("failed to LSM mount proc for '%s'", name);
return -1;
- } else if (mounted == 1) {
- lxc_conf->lsm_umount_proc = 1;
}
-#endif
if (setup_pivot_root(&lxc_conf->rootfs)) {
ERROR("failed to set rootfs for '%s'", name);
@@ -3413,10 +3390,10 @@ void lxc_conf_free(struct lxc_conf *conf)
if (conf->rcfile)
free(conf->rcfile);
lxc_clear_config_network(conf);
-#if HAVE_APPARMOR
- if (conf->aa_profile)
- free(conf->aa_profile);
-#endif
+ if (conf->lsm_aa_profile)
+ free(conf->lsm_aa_profile);
+ if (conf->lsm_se_context)
+ free(conf->lsm_se_context);
lxc_seccomp_free(conf);
lxc_clear_config_caps(conf);
lxc_clear_config_keepcaps(conf);
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index d99bdfe..8c2dc4e 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -247,9 +247,8 @@ enum {
* @tty_info : tty data
* @console : console data
* @ttydir : directory (under /dev) in which to create console and ttys
-#if HAVE_APPARMOR
- * @aa_profile : apparmor profile to switch to
-#endif
+ * @lsm_aa_profile : apparmor profile to switch to or NULL
+ * @lsm_se_context : selinux type to switch to or NULL
*/
enum lxchooks {
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV,
@@ -285,13 +284,10 @@ struct lxc_conf {
char *ttydir;
int close_all_fds;
struct lxc_list hooks[NUM_LXC_HOOKS];
-#if HAVE_APPARMOR
- char *aa_profile;
-#endif
-#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
+ char *lsm_aa_profile;
+ char *lsm_se_context;
int lsm_umount_proc;
-#endif
char *seccomp; // filename with the seccomp rules
#if HAVE_SCMP_FILTER_CTX
scmp_filter_ctx *seccomp_ctx;
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index b378c3a..a623d88 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -57,9 +57,8 @@ static int config_pts(const char *, const char *, struct lxc_conf *);
static int config_tty(const char *, const char *, struct lxc_conf *);
static int config_ttydir(const char *, const char *, struct lxc_conf *);
static int config_kmsg(const char *, const char *, struct lxc_conf *);
-#if HAVE_APPARMOR
-static int config_aa_profile(const char *, const char *, struct lxc_conf *);
-#endif
+static int config_lsm_aa_profile(const char *, const char *, struct lxc_conf *);
+static int config_lsm_se_context(const char *, const char *, struct lxc_conf *);
static int config_cgroup(const char *, const char *, struct lxc_conf *);
static int config_idmap(const char *, const char *, struct lxc_conf *);
static int config_loglevel(const char *, const char *, struct lxc_conf *);
@@ -100,9 +99,8 @@ static struct lxc_config_t config[] = {
{ "lxc.tty", config_tty },
{ "lxc.devttydir", config_ttydir },
{ "lxc.kmsg", config_kmsg },
-#if HAVE_APPARMOR
- { "lxc.aa_profile", config_aa_profile },
-#endif
+ { "lxc.aa_profile", config_lsm_aa_profile },
+ { "lxc.se_context", config_lsm_se_context },
{ "lxc.cgroup", config_cgroup },
{ "lxc.id_map", config_idmap },
{ "lxc.loglevel", config_loglevel },
@@ -967,9 +965,8 @@ static int config_kmsg(const char *key, const char *value,
return 0;
}
-#if HAVE_APPARMOR
-static int config_aa_profile(const char *key, const char *value,
- struct lxc_conf *lxc_conf)
+static int config_lsm_aa_profile(const char *key, const char *value,
+ struct lxc_conf *lxc_conf)
{
char *path;
@@ -981,13 +978,32 @@ static int config_aa_profile(const char *key, const char *value,
return -1;
}
- if (lxc_conf->aa_profile)
- free(lxc_conf->aa_profile);
- lxc_conf->aa_profile = path;
+ if (lxc_conf->lsm_aa_profile)
+ free(lxc_conf->lsm_aa_profile);
+ lxc_conf->lsm_aa_profile = path;
+
+ return 0;
+}
+
+static int config_lsm_se_context(const char *key, const char *value,
+ struct lxc_conf *lxc_conf)
+{
+ char *path;
+
+ if (!value || strlen(value) == 0)
+ return 0;
+ path = strdup(value);
+ if (!path) {
+ SYSERROR("failed to strdup '%s': %m", value);
+ return -1;
+ }
+
+ if (lxc_conf->lsm_se_context)
+ free(lxc_conf->lsm_se_context);
+ lxc_conf->lsm_se_context = path;
return 0;
}
-#endif
static int config_logfile(const char *key, const char *value,
struct lxc_conf *lxc_conf)
@@ -1913,10 +1929,10 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
v = c->ttydir;
else if (strcmp(key, "lxc.arch") == 0)
return lxc_get_arch_entry(c, retv, inlen);
-#if HAVE_APPARMOR
else if (strcmp(key, "lxc.aa_profile") == 0)
- v = c->aa_profile;
-#endif
+ v = c->lsm_aa_profile;
+ else if (strcmp(key, "lxc.se_context") == 0)
+ v = c->lsm_se_context;
else if (strcmp(key, "lxc.logfile") == 0)
v = lxc_log_get_file();
else if (strcmp(key, "lxc.loglevel") == 0)
@@ -2000,10 +2016,10 @@ void write_config(FILE *fout, struct lxc_conf *c)
default: break;
}
#endif
-#if HAVE_APPARMOR
- if (c->aa_profile)
- fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile);
-#endif
+ if (c->lsm_aa_profile)
+ fprintf(fout, "lxc.aa_profile = %s\n", c->lsm_aa_profile);
+ if (c->lsm_se_context)
+ fprintf(fout, "lxc.se_context = %s\n", c->lsm_se_context);
if (c->loglevel != LXC_LOG_PRIORITY_NOTSET)
fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel));
if (c->logfile)
diff --git a/src/lxc/lsm/apparmor.c b/src/lxc/lsm/apparmor.c
new file mode 100644
index 0000000..82839eb
--- /dev/null
+++ b/src/lxc/lsm/apparmor.c
@@ -0,0 +1,168 @@
+/* apparmor
+ *
+ * Copyright © 2012 Serge Hallyn <serge.hallyn at ubuntu.com>.
+ * Copyright © 2012 Canonical Ltd.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/apparmor.h>
+#include "log.h"
+#include "lsm/lsm.h"
+
+lxc_log_define(lxc_apparmor, lxc);
+
+#define AA_DEF_PROFILE "lxc-container-default"
+#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
+#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
+
+/* aa_getcon is not working right now. Use our hand-rolled version below */
+static int apparmor_enabled(void)
+{
+ struct stat statbuf;
+ FILE *fin;
+ char e;
+ int ret;
+
+ ret = stat(AA_MOUNT_RESTR, &statbuf);
+ if (ret != 0)
+ return 0;
+ fin = fopen(AA_ENABLED_FILE, "r");
+ if (!fin)
+ return 0;
+ ret = fscanf(fin, "%c", &e);
+ fclose(fin);
+ if (ret == 1 && e == 'Y')
+ return 1;
+ return 0;
+}
+
+static char *apparmor_process_label_get(pid_t pid)
+{
+ char path[100], *space;
+ int ret;
+ char *buf = NULL;
+ int sz = 0;
+ FILE *f;
+
+ ret = snprintf(path, 100, "/proc/%d/attr/current", pid);
+ if (ret < 0 || ret >= 100) {
+ ERROR("path name too long");
+ return NULL;
+ }
+again:
+ f = fopen(path, "r");
+ if (!f) {
+ SYSERROR("opening %s\n", path);
+ if (buf)
+ free(buf);
+ return NULL;
+ }
+ sz += 1024;
+ buf = realloc(buf, sz);
+ if (!buf) {
+ ERROR("out of memory");
+ fclose(f);
+ return NULL;
+ }
+ memset(buf, 0, sz);
+ ret = fread(buf, 1, sz - 1, f);
+ fclose(f);
+ if (ret < 0) {
+ ERROR("reading %s\n", path);
+ free(buf);
+ return NULL;
+ }
+ if (ret >= sz)
+ goto again;
+ space = index(buf, '\n');
+ if (space)
+ *space = '\0';
+ space = index(buf, ' ');
+ if (space)
+ *space = '\0';
+ return buf;
+}
+
+static int apparmor_am_unconfined(void)
+{
+ char *p = apparmor_process_label_get(getpid());
+ int ret = 0;
+ if (!p || strcmp(p, "unconfined") == 0)
+ ret = 1;
+ if (p)
+ free(p);
+ return ret;
+}
+
+/*
+ * apparmor_process_label_set: Set AppArmor process profile
+ *
+ * @label : the profile to set
+ * @default : use the default profile if label is NULL
+ *
+ * Returns 0 on success, < 0 on failure
+ *
+ * Notes: This relies on /proc being available. The new context
+ * will take effect immediately.
+ */
+static int apparmor_process_label_set(const char *label, int use_default)
+{
+ if (!apparmor_enabled())
+ return 0;
+
+ if (!label) {
+ if (use_default)
+ label = AA_DEF_PROFILE;
+ else
+ return 0;
+ }
+
+ if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
+ INFO("apparmor profile unchanged");
+ return 0;
+ }
+
+ /* XXX: instant instead of aa_change_onexec(), may be used by attach
+ * when using a function that doesn't exec
+ */
+ if (aa_change_profile(label) < 0) {
+ SYSERROR("failed to change apparmor profile to %s", label);
+ return -1;
+ }
+
+ INFO("changed apparmor profile to %s", label);
+ return 0;
+}
+
+static struct lsm_drv apparmor_drv = {
+ .name = "AppArmor",
+ .process_label_get = apparmor_process_label_get,
+ .process_label_set = apparmor_process_label_set,
+};
+
+struct lsm_drv *lsm_apparmor_drv_init(void)
+{
+ if (!apparmor_enabled())
+ return NULL;
+ return &apparmor_drv;
+}
diff --git a/src/lxc/lsm/lsm.c b/src/lxc/lsm/lsm.c
new file mode 100644
index 0000000..3974f11
--- /dev/null
+++ b/src/lxc/lsm/lsm.c
@@ -0,0 +1,156 @@
+/*
+ * lxc: linux Container library
+ *
+ * Authors:
+ * Copyright © 2012 Serge Hallyn <serge.hallyn at ubuntu.com>
+ * Copyright © 2012 Canonical Ltd.
+ * Dwight Engen <dwight.engen at oracle.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if HAVE_APPARMOR || HAVE_SELINUX
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+
+#include "conf.h"
+#include "log.h"
+#include "lsm/lsm.h"
+
+lxc_log_define(lxc_lsm, lxc);
+
+static struct lsm_drv *drv = NULL;
+
+extern struct lsm_drv *lsm_apparmor_drv_init(void);
+extern struct lsm_drv *lsm_selinux_drv_init(void);
+extern struct lsm_drv *lsm_nop_drv_init(void);
+
+__attribute__((constructor))
+void lsm_init(void)
+{
+ if (drv) {
+ INFO("LSM security driver %s", drv->name);
+ return;
+ }
+
+ #if HAVE_APPARMOR
+ drv = lsm_apparmor_drv_init();
+ #endif
+ #if HAVE_SELINUX
+ if (!drv)
+ drv = lsm_selinux_drv_init();
+ #endif
+
+ if (!drv)
+ drv = lsm_nop_drv_init();
+ INFO("Initialized LSM security driver %s", drv->name);
+}
+
+char *lsm_process_label_get(pid_t pid)
+{
+ if (!drv) {
+ ERROR("LSM driver not inited");
+ return NULL;
+ }
+ return drv->process_label_get(pid);
+}
+
+int lsm_process_label_set(const char *label, int use_default)
+{
+ if (!drv) {
+ ERROR("LSM driver not inited");
+ return -1;
+ }
+ return drv->process_label_set(label, use_default);
+}
+
+/*
+ * _lsm_mount_proc: Mount /proc inside container to enable
+ * security domain transition
+ *
+ * @rootfs : the rootfs where proc should be mounted
+ *
+ * Returns < 0 on failure, 0 if the correct proc was already mounted
+ * and 1 if a new proc was mounted.
+ */
+static int _lsm_proc_mount(const char *rootfs)
+{
+ char path[MAXPATHLEN];
+ char link[20];
+ int linklen, ret;
+
+ ret = snprintf(path, MAXPATHLEN, "%s/proc/self", rootfs);
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ SYSERROR("proc path name too long");
+ return -1;
+ }
+ memset(link, 0, 20);
+ linklen = readlink(path, link, 20);
+ INFO("I am %d, /proc/self points to '%s'", getpid(), link);
+ ret = snprintf(path, MAXPATHLEN, "%s/proc", rootfs);
+ if (linklen < 0) /* /proc not mounted */
+ goto domount;
+ /* can't be longer than rootfs/proc/1 */
+ if (strncmp(link, "1", linklen) != 0) {
+ /* wrong /procs mounted */
+ umount2(path, MNT_DETACH); /* ignore failure */
+ goto domount;
+ }
+ /* the right proc is already mounted */
+ return 0;
+
+domount:
+ if (mount("proc", path, "proc", 0, NULL))
+ return -1;
+ INFO("Mounted /proc in container for security transition");
+ return 1;
+}
+
+int lsm_proc_mount(struct lxc_conf *lxc_conf)
+{
+ int mounted;
+
+ if (!drv || strcmp(drv->name, "nop") == 0)
+ return 0;
+
+ if (lxc_conf->rootfs.path == NULL || strlen(lxc_conf->rootfs.path) == 0) {
+ if (mount("proc", "/proc", "proc", 0, NULL)) {
+ SYSERROR("Failed mounting /proc, proceeding");
+ mounted = 0;
+ } else
+ mounted = 1;
+ } else
+ mounted = _lsm_proc_mount(lxc_conf->rootfs.mount);
+ if (mounted == -1) {
+ SYSERROR("failed to mount /proc in the container.");
+ return -1;
+ } else if (mounted == 1) {
+ lxc_conf->lsm_umount_proc = 1;
+ }
+ return 0;
+}
+
+void lsm_proc_unmount(struct lxc_conf *lxc_conf)
+{
+ if (lxc_conf->lsm_umount_proc == 1) {
+ umount("/proc");
+ lxc_conf->lsm_umount_proc = 0;
+ }
+}
+#endif
diff --git a/src/lxc/lsm/lsm.h b/src/lxc/lsm/lsm.h
new file mode 100644
index 0000000..2a82c66
--- /dev/null
+++ b/src/lxc/lsm/lsm.h
@@ -0,0 +1,52 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2013 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen at oracle.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __lxc_lsm_h
+#define __lxc_lsm_h
+
+struct lxc_conf;
+
+#include <sys/types.h>
+
+struct lsm_drv {
+ const char *name;
+
+ char *(*process_label_get)(pid_t pid);
+ int (*process_label_set)(const char *label, int use_default);
+};
+
+#if HAVE_APPARMOR || HAVE_SELINUX
+void lsm_init(void);
+char *lsm_process_label_get(pid_t pid);
+int lsm_process_label_set(const char *label, int use_default);
+int lsm_proc_mount(struct lxc_conf *lxc_conf);
+void lsm_proc_unmount(struct lxc_conf *lxc_conf);
+#else
+static inline void lsm_init(void) { }
+static inline char *lsm_process_label_get(pid_t pid) { return NULL; }
+static inline int lsm_process_label_set(char *label, int use_default) { return 0; }
+static inline int lsm_proc_mount(struct lxc_conf *lxc_conf) { return 0; }
+static inline void lsm_proc_unmount(struct lxc_conf *lxc_conf) { }
+#endif
+
+#endif
diff --git a/src/lxc/lsm/nop.c b/src/lxc/lsm/nop.c
new file mode 100644
index 0000000..9184e6b
--- /dev/null
+++ b/src/lxc/lsm/nop.c
@@ -0,0 +1,46 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2013 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen at oracle.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include "lsm/lsm.h"
+
+static char *nop_process_label_get(pid_t pid)
+{
+ return NULL;
+}
+
+static int nop_process_label_set(const char *label, int use_default)
+{
+ return 0;
+}
+
+static struct lsm_drv nop_drv = {
+ .name = "nop",
+ .process_label_get = nop_process_label_get,
+ .process_label_set = nop_process_label_set,
+};
+
+struct lsm_drv *lsm_nop_drv_init(void)
+{
+ return &nop_drv;
+}
diff --git a/src/lxc/lsm/selinux.c b/src/lxc/lsm/selinux.c
new file mode 100644
index 0000000..6e44e8b
--- /dev/null
+++ b/src/lxc/lsm/selinux.c
@@ -0,0 +1,101 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2013 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen at oracle.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <selinux/selinux.h>
+#include "log.h"
+#include "lsm/lsm.h"
+
+#define DEFAULT_LABEL "unconfined_t"
+
+lxc_log_define(lxc_lsm_selinux, lxc);
+
+/*
+ * selinux_process_label_get: Get SELinux context of a process
+ *
+ * @pid : the pid to get, or 0 for self
+ *
+ * Returns the context of the given pid. The caller must free()
+ * the returned string.
+ *
+ * Note that this relies on /proc being available.
+ */
+static char *selinux_process_label_get(pid_t pid)
+{
+ security_context_t ctx;
+ char *label;
+
+ if (getpidcon_raw(pid, &ctx) < 0) {
+ SYSERROR("failed to get SELinux context for pid %d", pid);
+ return NULL;
+ }
+ label = strdup((char *)ctx);
+ freecon(ctx);
+ return label;
+}
+
+/*
+ * selinux_process_label_set: Set SELinux context of a process
+ *
+ * @label : the context to set
+ * @default : use the default context if label is NULL
+ *
+ * Returns 0 on success, < 0 on failure
+ *
+ * Notes: This relies on /proc being available. The new context
+ * will take effect on the next exec(2).
+ */
+static int selinux_process_label_set(const char *label, int use_default)
+{
+ if (!label) {
+ if (use_default)
+ label = DEFAULT_LABEL;
+ else
+ return -1;
+ }
+ if (!strcmp(label, "unconfined_t"))
+ return 0;
+
+ if (setexeccon_raw((char *)label) < 0) {
+ SYSERROR("failed to set new SELinux context %s", label);
+ return -1;
+ }
+
+ INFO("changed SELinux context to %s", label);
+ return 0;
+}
+
+static struct lsm_drv selinux_drv = {
+ .name = "SELinux",
+ .process_label_get = selinux_process_label_get,
+ .process_label_set = selinux_process_label_set,
+};
+
+struct lsm_drv *lsm_selinux_drv_init(void)
+{
+ if (!is_selinux_enabled())
+ return NULL;
+ return &selinux_drv;
+}
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 48a06cf..53a70bf 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -68,9 +68,9 @@
#include "console.h"
#include "sync.h"
#include "namespace.h"
-#include "apparmor.h"
#include "lxcseccomp.h"
#include "caps.h"
+#include "lsm/lsm.h"
lxc_log_define(lxc_start, lxc);
@@ -276,7 +276,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
handler->lxcpath = lxcpath;
handler->pinfd = -1;
- apparmor_handler_init(handler);
+ lsm_init();
+
handler->name = strdup(name);
if (!handler->name) {
ERROR("failed to allocate memory");
@@ -534,8 +535,16 @@ static int do_start(void *data)
if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP))
return -1;
- if (apparmor_load(handler) < 0)
+ /* XXX: hmm apparmor switches right away since it uses
+ * aa_change_profile() and not aa_change_onexec(). SELinux on the other
+ * hand is going to transition on exec(). Is it bad to run the stuff
+ * between here and exec() in the more privileged context?
+ */
+ if (lsm_process_label_set(handler->conf->lsm_aa_profile ?
+ handler->conf->lsm_aa_profile :
+ handler->conf->lsm_se_context, 1) < 0)
goto out_warn_father;
+ lsm_proc_unmount(handler->conf);
if (lxc_seccomp_load(handler->conf) != 0)
goto out_warn_father;
diff --git a/src/lxc/start.h b/src/lxc/start.h
index 9bf6024..c35c5c4 100644
--- a/src/lxc/start.h
+++ b/src/lxc/start.h
@@ -50,9 +50,6 @@ struct lxc_handler {
struct lxc_operations *ops;
void *data;
int sv[2];
-#if HAVE_APPARMOR
- int aa_enabled;
-#endif
int pinfd;
const char *lxcpath;
struct cgroup_process_info *cgroup;
diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in
index 98ea609..c0fcd30 100644
--- a/templates/lxc-oracle.in
+++ b/templates/lxc-oracle.in
@@ -110,10 +110,8 @@ EOF
# this file has to exist for libvirt/Virtual machine monitor to boot the container
touch $container_rootfs/etc/mtab
- # don't put devpts in here, it will already be mounted for us by lxc/libvirt
+ # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt
cat <<EOF > $container_rootfs/etc/fstab
-proc /proc proc nodev,noexec,nosuid 0 0
-sysfs /sys sysfs defaults 0 0
EOF
# remove module stuff for iptables it just shows errors that are not
@@ -404,9 +402,8 @@ lxc.cgroup.devices.allow = c 5:2 rwm # /dev/ptmx pty master
EOF
cat <<EOF > $cfg_dir/fstab || die "unable to create $cfg_dir/fstab"
-proc $container_rootfs/proc proc nodev,noexec,nosuid 0 0
-devpts $container_rootfs/dev/pts devpts defaults 0 0
-sysfs $container_rootfs/sys sysfs defaults 0 0
+proc proc proc nodev,noexec,nosuid 0 0
+sysfs sys sysfs defaults 0 0
EOF
}
--
1.8.1.4
More information about the lxc-devel
mailing list