Commit 1800f924 by Wolfgang Bumiller

apparmor: profile generation

This copies lxd's apparmor profile generation. This tries to detect features such as cgroup namespaces, apparmor namespaces and stacking support, and has profile parts conditionally for unprivileged containers. This introduces the following changes to the configuration: lxc.apparmor.profile = generated The fixed value 'generated' will cause this functionality to be used, otherwise there should be no functional changes happening unless specifically requested with the next key: lxc.apparmor.allow_nesting This is a boolean which, if enabled, causes the following changes: When generated apparmor profiles are used, they will contain the necessary changes to allow creating a nested container. In addition to the usual mount points, /dev/.lxc/proc and /dev/.lxc/sys will contain procfs and sysfs mount points without the lxcfs overlays, which, if generated apparmor profiles are being used, will not be read/writable directly. lxc.apparmor.raw A list of raw apparmor profile lines to append to the profile. Only valid when using generated profiles. The following apparmor profile lines have not been copied from lxd: mount /var/lib/lxd/shmounts/ -> /var/lib/lxd/shmounts/, mount none -> /var/lib/lxd/shmounts/, mount options=bind /var/lib/lxd/shmounts/** -> /var/lib/lxd/**, They should be added via lxc.apparmor.raw entries by lxd. In order for apparmor_parser's cache to be of use, this adds a --with-apparmor-cache-dir ./configure option. Signed-off-by: 's avatarWolfgang Bumiller <w.bumiller@proxmox.com>
parent 6e6aca3e
......@@ -469,6 +469,13 @@ AC_ARG_WITH([cgroup-pattern],
[pattern for container cgroups]
)], [], [with_cgroup_pattern=['lxc/%n']])
# The path for the apparmor_parser's cache for generated apparmor profiles
AC_ARG_WITH([apparmor-cache-dir],
[AC_HELP_STRING(
[--with-apparmor-cache-dir=dir],
[path for apparmor_parser cache]
)], [], [with_apparmor_cache_dir=['${localstatedir}/cache/lxc/apparmor']])
# Container log path. By default, use $lxcpath.
AC_MSG_CHECKING([Whether to place logfiles in container config path])
AC_ARG_ENABLE([configpath-log],
......@@ -515,6 +522,7 @@ AS_AC_EXPAND(LXCBINHOOKDIR, "$libexecdir/lxc/hooks")
AS_AC_EXPAND(LXCINITDIR, "$libexecdir")
AS_AC_EXPAND(LOGPATH, "$with_log_path")
AS_AC_EXPAND(RUNTIME_PATH, "$with_runtime_path")
AS_AC_EXPAND(APPARMOR_CACHE_DIR, "$with_apparmor_cache_dir")
AC_SUBST(DEFAULT_CGROUP_PATTERN, ["$with_cgroup_pattern"])
# We need the install path so criu knows where to reference the hook scripts.
......
......@@ -174,6 +174,7 @@ AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
-DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \
-DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \
-DSBINDIR=\"$(SBINDIR)\" \
-DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/lxc \
-I $(top_srcdir)/src/lxc/storage \
......
......@@ -2360,7 +2360,23 @@ static int setup_mount(const struct lxc_conf *conf,
return ret;
}
FILE *make_anonymous_mount_file(struct lxc_list *mount)
/*
* In order for nested containers to be able to mount /proc and /sys they need
* to see a "pure" proc and sysfs mount points with nothing mounted on top
* (like lxcfs).
* For this we provide proc and sysfs in /dev/.lxc/{proc,sys} while using an
* apparmor rule to deny access to them. This is mostly for convenience: The
* container's root user can mount them anyway and thus has access to the two
* file systems. But a non-root user in the container should not be allowed to
* access them as a side effect without explicitly allowing it.
*/
static const char nesting_helpers[] =
"proc dev/.lxc/proc proc create=dir,optional\n"
"sys dev/.lxc/sys sysfs create=dir,optional\n"
;
FILE *make_anonymous_mount_file(struct lxc_list *mount,
bool include_nesting_helpers)
{
int ret;
char *mount_entry;
......@@ -2402,6 +2418,13 @@ FILE *make_anonymous_mount_file(struct lxc_list *mount)
goto on_error;
}
if (include_nesting_helpers) {
ret = lxc_write_nointr(fd, nesting_helpers,
sizeof(nesting_helpers) - 1);
if (ret != sizeof(nesting_helpers) - 1)
goto on_error;
}
ret = lseek(fd, 0, SEEK_SET);
if (ret < 0)
goto on_error;
......@@ -2422,7 +2445,7 @@ static int setup_mount_entries(const struct lxc_conf *conf,
int ret;
FILE *f;
f = make_anonymous_mount_file(mount);
f = make_anonymous_mount_file(mount, conf->lsm_aa_allow_nesting);
if (!f)
return -1;
......@@ -2738,6 +2761,7 @@ struct lxc_conf *lxc_conf_init(void)
lxc_list_init(&new->groups);
lxc_list_init(&new->state_clients);
new->lsm_aa_profile = NULL;
lxc_list_init(&new->lsm_aa_raw);
new->lsm_se_context = NULL;
new->tmp_umount_proc = false;
new->tmp_umount_proc = 0;
......@@ -4025,6 +4049,19 @@ void lxc_clear_includes(struct lxc_conf *conf)
}
}
int lxc_clear_apparmor_raw(struct lxc_conf *c)
{
struct lxc_list *it, *next;
lxc_list_for_each_safe (it, &c->lsm_aa_raw, next) {
lxc_list_del(it);
free(it->elem);
free(it);
}
return 0;
}
void lxc_conf_free(struct lxc_conf *conf)
{
if (!conf)
......@@ -4052,6 +4089,7 @@ void lxc_conf_free(struct lxc_conf *conf)
free(conf->syslog);
lxc_free_networks(&conf->network);
free(conf->lsm_aa_profile);
free(conf->lsm_aa_profile_computed);
free(conf->lsm_se_context);
lxc_seccomp_free(conf);
lxc_clear_config_caps(conf);
......@@ -4068,6 +4106,7 @@ void lxc_conf_free(struct lxc_conf *conf)
lxc_clear_limits(conf, "lxc.prlimit");
lxc_clear_sysctls(conf, "lxc.sysctl");
lxc_clear_procs(conf, "lxc.proc");
lxc_clear_apparmor_raw(conf);
free(conf->cgroup_meta.dir);
free(conf->cgroup_meta.controllers);
free(conf->shmount.path_host);
......
......@@ -275,7 +275,11 @@ struct lxc_conf {
};
char *lsm_aa_profile;
char *lsm_aa_profile_computed;
bool lsm_aa_profile_created;
unsigned int lsm_aa_allow_nesting;
unsigned int lsm_aa_allow_incomplete;
struct lxc_list lsm_aa_raw;
char *lsm_se_context;
bool tmp_umount_proc;
char *seccomp; /* filename with the seccomp rules */
......@@ -427,7 +431,8 @@ extern int parse_mntopts(const char *mntopts, unsigned long *mntflags,
extern void tmp_proc_unmount(struct lxc_conf *lxc_conf);
extern void remount_all_slave(void);
extern void suggest_default_idmap(void);
extern FILE *make_anonymous_mount_file(struct lxc_list *mount);
extern FILE *make_anonymous_mount_file(struct lxc_list *mount,
bool include_nesting_helpers);
extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings);
extern unsigned long add_required_remount_flags(const char *s, const char *d,
unsigned long flags);
......@@ -441,5 +446,6 @@ extern int setup_sysctl_parameters(struct lxc_list *sysctls);
extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key);
extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid);
extern int lxc_clear_procs(struct lxc_conf *c, const char *key);
extern int lxc_clear_apparmor_raw(struct lxc_conf *c);
#endif /* __LXC_CONF_H */
......@@ -84,7 +84,9 @@ lxc_log_define(confile, lxc);
lxc_config_define(autodev);
lxc_config_define(apparmor_allow_incomplete);
lxc_config_define(apparmor_allow_nesting);
lxc_config_define(apparmor_profile);
lxc_config_define(apparmor_raw);
lxc_config_define(cap_drop);
lxc_config_define(cap_keep);
lxc_config_define(cgroup_controller);
......@@ -158,6 +160,8 @@ static struct lxc_config_t config[] = {
{ "lxc.arch", set_config_personality, get_config_personality, clr_config_personality, },
{ "lxc.apparmor.profile", set_config_apparmor_profile, get_config_apparmor_profile, clr_config_apparmor_profile, },
{ "lxc.apparmor.allow_incomplete", set_config_apparmor_allow_incomplete, get_config_apparmor_allow_incomplete, clr_config_apparmor_allow_incomplete, },
{ "lxc.apparmor.allow_nesting", set_config_apparmor_allow_nesting, get_config_apparmor_allow_nesting, clr_config_apparmor_allow_nesting, },
{ "lxc.apparmor.raw", set_config_apparmor_raw, get_config_apparmor_raw, clr_config_apparmor_raw, },
{ "lxc.autodev", set_config_autodev, get_config_autodev, clr_config_autodev, },
{ "lxc.cap.drop", set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, },
{ "lxc.cap.keep", set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, },
......@@ -1132,6 +1136,52 @@ static int set_config_apparmor_allow_incomplete(const char *key,
return 0;
}
static int set_config_apparmor_allow_nesting(const char *key,
const char *value,
struct lxc_conf *lxc_conf,
void *data)
{
if (lxc_config_value_empty(value))
return clr_config_apparmor_allow_nesting(key, lxc_conf, NULL);
if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_nesting) < 0)
return -1;
if (lxc_conf->lsm_aa_allow_nesting > 1)
return -1;
return 0;
}
static int set_config_apparmor_raw(const char *key,
const char *value,
struct lxc_conf *lxc_conf,
void *data)
{
char *elem;
struct lxc_list *list;
if (lxc_config_value_empty(value))
return lxc_clear_apparmor_raw(lxc_conf);
list = malloc(sizeof(*list));
if (!list) {
errno = ENOMEM;
return -1;
}
elem = strdup(value);
if (!elem) {
free(list);
return -1;
}
list->elem = elem;
lxc_list_add_tail(&lxc_conf->lsm_aa_raw, list);
return 0;
}
static int set_config_selinux_context(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
......@@ -3004,6 +3054,34 @@ static int get_config_apparmor_allow_incomplete(const char *key, char *retv,
c->lsm_aa_allow_incomplete);
}
static int get_config_apparmor_allow_nesting(const char *key, char *retv,
int inlen, struct lxc_conf *c,
void *data)
{
return lxc_get_conf_int(c, retv, inlen,
c->lsm_aa_allow_nesting);
}
static int get_config_apparmor_raw(const char *key, char *retv,
int inlen, struct lxc_conf *c,
void *data)
{
int len;
struct lxc_list *it;
int fulllen = 0;
if (!retv)
inlen = 0;
else
memset(retv, 0, inlen);
lxc_list_for_each(it, &c->lsm_aa_raw) {
strprint(retv, inlen, "%s\n", (char *)it->elem);
}
return fulllen;
}
static int get_config_selinux_context(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
......@@ -3794,6 +3872,21 @@ static inline int clr_config_apparmor_allow_incomplete(const char *key,
return 0;
}
static inline int clr_config_apparmor_allow_nesting(const char *key,
struct lxc_conf *c,
void *data)
{
c->lsm_aa_allow_nesting = 0;
return 0;
}
static inline int clr_config_apparmor_raw(const char *key,
struct lxc_conf *c,
void *data)
{
return lxc_clear_apparmor_raw(c);
}
static inline int clr_config_selinux_context(const char *key,
struct lxc_conf *c, void *data)
{
......@@ -4986,7 +5079,9 @@ int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv,
if (!strcmp(key, "lxc.apparmor")) {
strprint(retv, inlen, "allow_incomplete\n");
strprint(retv, inlen, "allow_nesting\n");
strprint(retv, inlen, "profile\n");
strprint(retv, inlen, "raw\n");
} else if (!strcmp(key, "lxc.cgroup")) {
strprint(retv, inlen, "dir\n");
} else if (!strcmp(key, "lxc.selinux")) {
......
......@@ -378,7 +378,8 @@ static void exec_criu(struct cgroup_ops *cgroup_ops, struct criu_opts *opts)
DECLARE_ARG(opts->user->action_script);
}
mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list);
mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list,
opts->c->lxc_conf->lsm_aa_allow_nesting);
if (!mnts)
goto err;
......
......@@ -177,11 +177,37 @@ on_error:
}
int lsm_process_label_set(const char *label, struct lxc_conf *conf,
bool use_default, bool on_exec)
bool on_exec)
{
if (!drv) {
ERROR("LSM driver not inited");
return -1;
}
return drv->process_label_set(label, conf, use_default, on_exec);
return drv->process_label_set(label, conf, on_exec);
}
int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath)
{
if (!drv) {
ERROR("LSM driver not inited");
return 0;
}
if (!drv->prepare)
return 0;
return drv->prepare(conf, lxcpath);
}
void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath)
{
if (!drv) {
ERROR("LSM driver not inited");
return;
}
if (!drv->cleanup)
return;
drv->cleanup(conf, lxcpath);
}
......@@ -38,17 +38,21 @@ struct lsm_drv {
int (*enabled)(void);
char *(*process_label_get)(pid_t pid);
int (*process_label_set)(const char *label, struct lxc_conf *conf,
bool use_default, bool on_exec);
bool on_exec);
int (*prepare)(struct lxc_conf *conf, const char *lxcpath);
void (*cleanup)(struct lxc_conf *conf, const char *lxcpath);
};
extern void lsm_init(void);
extern int lsm_enabled(void);
extern const char *lsm_name(void);
extern char *lsm_process_label_get(pid_t pid);
extern int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath);
extern int lsm_process_label_set(const char *label, struct lxc_conf *conf,
bool use_default, bool on_exec);
bool on_exec);
extern int lsm_process_label_fd_get(pid_t pid, bool on_exec);
extern int lsm_process_label_set_at(int label_fd, const char *label,
bool on_exec);
extern void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath);
#endif /* __LXC_LSM_H */
......@@ -30,7 +30,7 @@ static char *nop_process_label_get(pid_t pid)
}
static int nop_process_label_set(const char *label, struct lxc_conf *conf,
bool use_default, bool on_exec)
bool on_exec)
{
return 0;
}
......
......@@ -75,15 +75,13 @@ static char *selinux_process_label_get(pid_t pid)
* Notes: This relies on /proc being available.
*/
static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf,
bool use_default, bool on_exec)
bool on_exec)
{
int ret;
const char *label;
label = inlabel ? inlabel : conf->lsm_se_context;
if (!label) {
if (!use_default)
return -EINVAL;
label = DEFAULT_LABEL;
}
......
......@@ -863,9 +863,19 @@ int lxc_init(const char *name, struct lxc_handler *handler)
}
TRACE("Initialized cgroup driver");
ret = lsm_process_prepare(conf, handler->lxcpath);
if (ret < 0) {
ERROR("Failed to initialize LSM");
goto out_destroy_cgroups;
}
TRACE("Initialized LSM");
INFO("Container \"%s\" is initialized", name);
return 0;
out_destroy_cgroups:
handler->cgroup_ops->destroy(handler->cgroup_ops, handler);
out_delete_terminal:
lxc_terminal_delete(&handler->conf->console);
......@@ -956,6 +966,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
while (namespace_count--)
free(namespaces[namespace_count]);
lsm_process_cleanup(handler->conf, handler->lxcpath);
cgroup_ops->destroy(cgroup_ops, handler);
cgroup_exit(cgroup_ops);
......@@ -1235,7 +1247,7 @@ static int do_start(void *data)
}
/* Set the label to change to when we exec(2) the container's init. */
ret = lsm_process_label_set(NULL, handler->conf, 1, 1);
ret = lsm_process_label_set(NULL, handler->conf, true);
if (ret < 0)
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