[CRIU] [PATCH v2 3/9] lsm: support checkpoint/restore of stacked apparmor profiles
Pavel Emelyanov
xemul at virtuozzo.com
Tue Oct 25 05:45:59 PDT 2016
On 10/24/2016 06:51 PM, Tycho Andersen wrote:
> Support for apparmor namespaces and stacking is coming to Ubuntu kernels in
> 16.10, and should hopefully be upstreamed Soon (TM) :).
Is there any POC patch on lkml about it?
> The basic idea is similar to how cgroups are done: we can restore the
> apparmor namespace and profile blobs independently of the tasks, and then
> at the end we can just set the task's label appropriately. This means the
> code that moves tasks under a label stays the same, and the only new code
> is the stuff that dumps and restores the policy blobs that are in the
> namespace that were loaded by the container.
>
> v2: dump profiles in timestamp order instead of name order; the timestamp
> indicates what order profiles are installed in, and users can
> potentially reload a profile that changes others, so we should insert
> the blobs in timestamp order
>
> Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
> ---
> criu/Makefile.crtools | 1 +
> criu/apparmor.c | 532 +++++++++++++++++++++++++++++++++++++++++++
> criu/cr-dump.c | 7 +-
> criu/cr-restore.c | 4 +
> criu/image-desc.c | 1 +
> criu/include/apparmor.h | 12 +
> criu/include/image-desc.h | 1 +
> criu/include/magic.h | 1 +
> criu/include/protobuf-desc.h | 1 +
> criu/lsm.c | 7 +
> criu/protobuf-desc.c | 1 +
> images/Makefile | 1 +
> images/apparmor.proto | 16 ++
> images/creds.proto | 3 +-
> lib/py/images/images.py | 1 +
> 15 files changed, 587 insertions(+), 2 deletions(-)
> create mode 100644 criu/apparmor.c
> create mode 100644 criu/include/apparmor.h
> create mode 100644 images/apparmor.proto
>
> diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
> index c435748..a10b605 100644
> --- a/criu/Makefile.crtools
> +++ b/criu/Makefile.crtools
> @@ -6,6 +6,7 @@ ccflags-y += -iquote compel/arch/$(ARCH)/plugins/std
> obj-y += action-scripts.o
> obj-y += external.o
> obj-y += aio.o
> +obj-y += apparmor.o
> obj-y += bfd.o
> obj-y += bitmap.o
> obj-y += cgroup.o
> diff --git a/criu/apparmor.c b/criu/apparmor.c
> new file mode 100644
> index 0000000..b23186f
> --- /dev/null
> +++ b/criu/apparmor.c
> @@ -0,0 +1,532 @@
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +#include <ftw.h>
> +
> +#include "config.h"
> +#include "imgset.h"
> +#include "pstree.h"
> +#include "util.h"
> +#include "lsm.h"
> +#include "cr_options.h"
> +
> +#include "protobuf.h"
> +#include "images/inventory.pb-c.h"
> +#include "images/apparmor.pb-c.h"
> +
> +/*
> + * Apparmor stacked profile checkpoint restore. Previously, we just saved the
> + * profile that was in use by the task, and we expected it to be present on the
> + * target host. Now with stacking, containers are able to load their own
> + * profiles, so we can't rely on this.
> + *
> + * The basic idea here is that there is some (collection) of (potentially
> + * nested) namespaces that a container uses. We don't collect everything on the
> + * host level, but we *do* collect everything inside the namespace; a container
> + * could have loaded a profile but not yet used it when we start to checkpoint.
> + *
> + * Thus, the old code that saves and restores AA profiles is still relevant, we
> + * just need to add the new code in this file to walk the namespace and dump
> + * any blobs in that AA namespace, and then restore these blobs on restore so
> + * that the profiles the old code tries to use are actualy present.
> + */
> +
> +static AaNamespace **namespaces = NULL;
> +static int n_namespaces = 0;
> +
> +bool ns_dumping_enabled = false;
> +
> +static AaNamespace *new_namespace(char *name, AaNamespace *parent)
> +{
> + void *m;
> + AaNamespace *ret;
> +
> + ret = xmalloc(sizeof(*ret));
> + if (!ret)
> + return NULL;
> + aa_namespace__init(ret);
> +
> + ret->name = xstrdup(name);
> + if (!ret->name) {
> + xfree(ret);
> + return NULL;
> + }
> +
> + if (parent) {
> + m = xrealloc(parent->namespaces, sizeof(*parent->namespaces) * (parent->n_namespaces + 1));
> + if (!m) {
> + xfree(ret->name);
> + xfree(ret);
> + return NULL;
> + }
> +
> + parent->namespaces = m;
> + parent->namespaces[parent->n_namespaces++] = ret;
> + }
> +
> + m = xrealloc(namespaces, sizeof(*namespaces) * (n_namespaces + 1));
> + if (!m) {
> + if (parent)
> + parent->n_namespaces--;
> +
> + xfree(ret->name);
> + xfree(ret);
> + return NULL;
> + }
> +
> + namespaces = m;
> + namespaces[n_namespaces++] = ret;
> +
> + return ret;
> +}
> +
> +static int collect_profile(char *path, char *name, AaNamespace *ns)
> +{
> + AaPolicy *cur;
> + char *c;
> + int fd;
> + struct stat sb;
> + ssize_t n;
> + void *m;
> +
> + strcat(path, name);
> + strcat(path, "/raw_data");
> +
> + /* the apparmor kernel stuff puts an extra .N on the profile name that
> + * is the directory name which is annoying. We strip it off here. */
> + c = strrchr(name, '.');
> + if (!c) {
> + pr_err("malformed apparmor profile name %s\n", name);
> + return -1;
> + }
> + *c = 0;
> +
> + pr_info("dumping profile %s\n", path);
> +
> + cur = xmalloc(sizeof(*cur));
> + if (!cur)
> + return -1;
> + aa_policy__init(cur);
> +
> + cur->name = xstrdup(name);
> + if (!cur->name) {
> + xfree(cur);
> + return -1;
> + }
> +
> + fd = open(path, O_RDONLY);
> + if (fd < 0) {
> + pr_perror("failed to open aa policy %s", path);
> + goto err;
> + }
> +
> + if (fstat(fd, &sb) < 0) {
> + pr_perror("failed to stat %s", path);
> + goto close;
> + }
> +
> + cur->blob.len = sb.st_size;
> + cur->blob.data = xmalloc(sb.st_size);
> + if (!cur->blob.data)
> + goto close;
> +
> + n = read(fd, cur->blob.data, sb.st_size);
> + if (n < 0) {
> + pr_perror("failed to read %s", path);
> + goto close;
> + }
> +
> + if (n != sb.st_size) {
> + pr_err("didn't read all of %s\n", path);
> + goto close;
> + }
> +
> + close(fd);
> +
> + m = xrealloc(ns->policies, sizeof(*ns->policies) * (ns->n_policies + 1));
> + if (!m)
> + goto err;
> + ns->policies = m;
> + ns->policies[ns->n_policies++] = cur;
> +
> + return 0;
> +
> +close:
> + close(fd);
> +
> +err:
> + xfree(cur->name);
> + xfree(cur);
> + return -1;
> +}
> +
> +char *ns_path;
> +int sort_err;
> +
> +static int no_dirdots(const struct dirent *de)
> +{
> + return !dir_dots(de);
> +}
> +
> +static int by_time(const struct dirent **de1, const struct dirent **de2)
> +{
> + char path[PATH_MAX];
> + struct stat sb1, sb2;
> +
> + snprintf(path, sizeof(path), "%s/%s", ns_path, (*de1)->d_name);
> + if (stat(path, &sb1) < 0) {
> + pr_perror("couldn't stat %s\n", path);
> + sort_err = errno;
> + return 0;
> + }
> +
> + snprintf(path, sizeof(path), "%s/%s", ns_path, (*de2)->d_name);
> + if (stat(path, &sb2) < 0) {
> + pr_perror("couldn't state %s\n", path);
> + sort_err = errno;
> + return 0;
> + }
> +
> + if (sb1.st_mtim.tv_sec == sb2.st_mtim.tv_sec) {
> + if (sb1.st_mtim.tv_nsec < sb2.st_mtim.tv_nsec)
> + return -1;
> + if (sb1.st_mtim.tv_nsec == sb2.st_mtim.tv_nsec)
> + return 0;
> + return 1;
> + } else {
> + if (sb1.st_mtim.tv_sec < sb2.st_mtim.tv_sec)
> + return -1;
> + if (sb1.st_mtim.tv_sec == sb2.st_mtim.tv_sec)
> + return 0;
> + return 1;
> + }
> +}
> +
> +static int walk_namespace(char *path, size_t offset, AaNamespace *ns)
> +{
> + DIR *dir = NULL;
> + struct dirent *de, **namelist = NULL;
> + int ret = -1, n_names = 0, i;
> + size_t my_offset;
> +
> + /* collect all the child namespaces */
> + strcat(path, "/namespaces/");
> + my_offset = offset + 12;
> +
> + dir = opendir(path);
> + if (!dir)
> + goto out;
> +
> + while((de = readdir(dir))) {
> + AaNamespace *cur;
> +
> + if (dir_dots(de))
> + continue;
> +
> + path[my_offset] = '\0';
> + strcat(path, de->d_name);
> +
> + cur = new_namespace(de->d_name, ns);
> + if (!cur)
> + goto out;
> +
> + if (walk_namespace(path, my_offset + strlen(de->d_name), cur) < 0) {
> + aa_namespace__free_unpacked(cur, NULL);
> + ns->n_namespaces--;
> + goto out;
> + }
> + }
> +
> + closedir(dir);
> + dir = NULL;
> +
> + /* now collect the profiles for this namespace */
> + path[offset] = '\0';
> + strcat(path, "/profiles/");
> + my_offset = offset + 10;
> +
> + sort_err = 0;
> + ns_path = path;
> + n_names = scandir(path, &namelist, no_dirdots, by_time);
> + if (n_names < 0 || sort_err != 0) {
> + pr_perror("scandir failed");
> + goto out;
> + }
> +
> + for (i = 0; i < n_names; i++) {
> + de = namelist[i];
> +
> + path[my_offset] = 0;
> + if (collect_profile(path, de->d_name, ns) < 0)
> + goto out;
> + }
> +
> + ret = 0;
> +out:
> + if (dir)
> + closedir(dir);
> +
> + if (namelist) {
> + for (i = 0; i < n_names; i++)
> + xfree(namelist[i]);
> + xfree(namelist);
> + }
> +
> + return ret;
> +}
> +
> +int collect_aa_namespace(char *profile)
> +{
> + char path[PATH_MAX], *namespace, *end;
> + int ret, i;
> + AaNamespace *ns;
> +
> + if (!profile)
> + return 0;
> +
> + namespace = strchr(profile, ':');
> + if (!namespace)
> + return 0; /* no namespace to dump */
> + namespace++;
> +
> + if (!ns_dumping_enabled) {
> + pr_warn("Apparmor namespace present but dumping not enabled\n");
> + return 0;
> + }
> +
> + /* XXX: this is not strictly correct; if something is using namespace
> + * views, extra //s can indicate a namespace separation. However, I
> + * think only the apparmor developers use this feature :)
> + */
> + end = strchr(namespace, ':');
> + if (!end) {
> + pr_err("couldn't find AA namespace end in: %s", namespace);
> + return -1;
> + }
> +
> + *end = '\0';
> +
> + for (i = 0; i < n_namespaces; i++) {
> + /* did we already dump this namespace? */
> + if (!strcmp(namespaces[i]->name, namespace)) {
> + *end = ':';
> + return 0;
> + }
> + }
> +
> + pr_info("dumping AA namespace %s\n", namespace);
> +
> + ns = new_namespace(namespace, NULL);
> + *end = ':';
> + if (!ns)
> + return -1;
> +
> + ret = snprintf(path, sizeof(path), AA_SECURITYFS_PATH "/policy/namespaces/%s", ns->name);
> + if (ret < 0 || ret >= sizeof(path)) {
> + pr_err("snprintf failed?\n");
> + goto err;
> + }
> +
> + if (walk_namespace(path, ret, ns) < 0) {
> + pr_err("walking AA namespace %s failed\n", ns->name);
> + goto err;
> + }
> +
> + return 0;
> +
> +err:
> + aa_namespace__free_unpacked(ns, NULL);
> + n_namespaces--;
> + return -1;
> +}
> +
> +int dump_aa_namespaces(void)
> +{
> + ApparmorEntry *ae = NULL;
> + int ret;
> +
> + if (n_namespaces == 0)
> + return 0;
> +
> + ae = xmalloc(sizeof(*ae));
> + if (!ae)
> + return -1;
> + apparmor_entry__init(ae);
> +
> + ae->n_namespaces = n_namespaces;
> + ae->namespaces = namespaces;
> +
> + ret = pb_write_one(img_from_set(glob_imgset, CR_FD_APPARMOR), ae, PB_APPARMOR);
> +
> + apparmor_entry__free_unpacked(ae, NULL);
> + n_namespaces = -1;
> + namespaces = NULL;
> +
> + return ret;
> +}
> +
> +bool check_aa_ns_dumping(void)
> +{
> + char contents[48];
> + int major, minor, ret;
> + FILE *f;
> +
> + f = fopen(AA_SECURITYFS_PATH "/features/domain/stack", "r");
> + if (!f)
> + return false;
> +
> + ret = fscanf(f, "%48s", contents);
> + fclose(f);
> + if (ret != 1) {
> + pr_err("scanning aa stack feature failed\n");
> + return false;
> + }
> +
> + if (strcmp("yes", contents)) {
> + pr_warn("aa stack featured disabled: %s\n", contents);
> + return false;
> + }
> +
> + f = fopen(AA_SECURITYFS_PATH "/features/domain/version", "r");
> + if (!f)
> + return false;
> +
> + ret = fscanf(f, "%d.%d", &major, &minor);
> + fclose(f);
> + if (ret != 2) {
> + pr_err("scanning aa stack version failed\n");
> + return false;
> + }
> +
> + return major >= 1 && minor >= 2;
> +}
> +
> +static int restore_aa_namespace(AaNamespace *ns, char *path, int offset)
> +{
> + pid_t pid;
> + int status;
> +
> + pid = fork();
> + if (pid < 0) {
> + pr_perror("fork failed");
> + return -1;
> + }
> +
> + if (!pid) {
> + int i, my_offset, ret, fd;
> + char buf[1024];
> +
> + ret = snprintf(buf, sizeof(buf), "changeprofile :%s:", ns->name);
> + if (ret < 0 || ret >= sizeof(buf)) {
> + pr_err("profile %s too big\n", ns->name);
> + exit(1);
> + }
> +
> + my_offset = snprintf(path+offset, PATH_MAX-offset, "/namespaces/%s", ns->name);
> + if (my_offset < 0 || my_offset >= PATH_MAX-offset) {
> + pr_err("snprintf'd too many characters\n");
> + exit(1);
> + }
> +
> + if (mkdir(path, 0755) < 0) {
> + if (errno == EEXIST) {
> + pr_warn("apparmor namespace %s already exists, restoring into it\n", path);
> + } else {
> + pr_perror("failed to create namespace %s", path);
> + exit(1);
> + }
> + }
> +
> + fd = open_proc_rw(PROC_SELF, "attr/current");
> + if (fd < 0) {
> + pr_perror("couldn't open attr/current");
> + goto fail;
> + }
> +
> + errno = 0;
> + ret = write(fd, buf, strlen(buf));
> + close(fd);
> + if (ret != strlen(buf)) {
> + pr_perror("failed to change aa namespace %s", buf);
> + goto fail;
> + }
> +
> + for (i = 0; i < ns->n_namespaces; i++) {
> + if (restore_aa_namespace(ns, path, offset + my_offset) < 0)
> + goto fail;
> + }
> +
> + for (i = 0; i < ns->n_policies; i++) {
> + int fd, n;
> + AaPolicy *p = ns->policies[i];
> +
> + fd = open(AA_SECURITYFS_PATH "/.replace", O_WRONLY);
> + if (fd < 0) {
> + pr_perror("couldn't open apparmor load file");
> + goto fail;
> + }
> +
> + n = write(fd, p->blob.data, p->blob.len);
> + close(fd);
> + if (n != p->blob.len) {
> + pr_perror("write AA policy failed");
> + goto fail;
> + }
> + }
> +
> + exit(0);
> +fail:
> + rmdir(path);
> + exit(1);
> + }
> +
> + if (waitpid(pid, &status, 0) < 0) {
> + pr_perror("waitpid failed");
> + return -1;
> + }
> +
> + if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
> + return 0;
> +
> + pr_err("failed to restore aa namespace, worker exited: %d\n", status);
> + return -1;
> +}
> +
> +int prepare_apparmor_namespaces(void)
> +{
> + struct cr_img *img;
> + int ret, i;
> + ApparmorEntry *ae;
> +
> + img = open_image(CR_FD_APPARMOR, O_RSTR);
> + if (!img)
> + return -1;
> +
> + ret = pb_read_one_eof(img, &ae, PB_APPARMOR);
> + close_image(img);
> + if (ret <= 0)
> + return 0; /* there was no AA namespace entry */
> +
> + BUG_ON(!ae);
> +
> + /* no real reason we couldn't do this in parallel, but in usually we
> + * expect one namespace so there's probably not a lot to be gained.
> + */
> + for (i = 0; i < ae->n_namespaces; i++) {
> + char path[PATH_MAX] = AA_SECURITYFS_PATH "/policy";
> +
> + if (restore_aa_namespace(ae->namespaces[i], path, strlen(path)) < 0) {
> + ret = -1;
> + goto out;
> + }
> + }
> +
> + ret = 0;
> +out:
> + apparmor_entry__free_unpacked(ae, NULL);
> + return ret;
> +}
> diff --git a/criu/cr-dump.c b/criu/cr-dump.c
> index e15f178..360bfcc 100644
> --- a/criu/cr-dump.c
> +++ b/criu/cr-dump.c
> @@ -81,7 +81,9 @@
> #include "seccomp.h"
> #include "seize.h"
> #include "fault-injection.h"
> -#include "dump.h"
> +#include "apparmor.h"
> +
> +#include "asm/dump.h"
>
> static char loc_buf[PAGE_SIZE];
>
> @@ -1811,6 +1813,9 @@ int cr_dump_tasks(pid_t pid)
> if (dump_namespaces(root_item, root_ns_mask) < 0)
> goto err;
>
> + if (dump_aa_namespaces() < 0)
> + goto err;
> +
> ret = dump_cgroups();
> if (ret)
> goto err;
> diff --git a/criu/cr-restore.c b/criu/cr-restore.c
> index 9629c7b..1a03179 100644
> --- a/criu/cr-restore.c
> +++ b/criu/cr-restore.c
> @@ -75,6 +75,7 @@
> #include "seccomp.h"
> #include "fault-injection.h"
> #include "sk-queue.h"
> +#include "apparmor.h"
>
> #include "parasite-syscall.h"
> #include "files-reg.h"
> @@ -144,6 +145,9 @@ static int crtools_prepare_shared(void)
> if (prepare_cgroup())
> return -1;
>
> + if (prepare_apparmor_namespaces())
> + return -1;
> +
> return 0;
> }
>
> diff --git a/criu/image-desc.c b/criu/image-desc.c
> index bac7ca2..3728aa3 100644
> --- a/criu/image-desc.c
> +++ b/criu/image-desc.c
> @@ -99,6 +99,7 @@ struct cr_fd_desc_tmpl imgset_template[CR_FD_MAX] = {
> FD_ENTRY(USERNS, "userns-%d"),
> FD_ENTRY(NETNF_CT, "netns-ct-%d"),
> FD_ENTRY(NETNF_EXP, "netns-exp-%d"),
> + FD_ENTRY(APPARMOR, "apparmor"),
>
> [CR_FD_STATS] = {
> .fmt = "stats-%s",
> diff --git a/criu/include/apparmor.h b/criu/include/apparmor.h
> new file mode 100644
> index 0000000..447e015
> --- /dev/null
> +++ b/criu/include/apparmor.h
> @@ -0,0 +1,12 @@
> +#ifndef __CR_APPARMOR_H__
> +#define __CR_APPARMOR_H__
> +
> +int collect_aa_namespace(char *profile);
> +int dump_aa_namespaces(void);
> +
> +extern bool ns_dumping_enabled;
> +bool check_aa_ns_dumping(void);
> +
> +int prepare_apparmor_namespaces(void);
> +
> +#endif /* __CR_APPARMOR_H__ */
> diff --git a/criu/include/image-desc.h b/criu/include/image-desc.h
> index 09d187d..624fcd7 100644
> --- a/criu/include/image-desc.h
> +++ b/criu/include/image-desc.h
> @@ -81,6 +81,7 @@ enum {
> CR_FD_TIMERFD,
> CR_FD_FILE_LOCKS,
> CR_FD_SECCOMP,
> + CR_FD_APPARMOR,
> _CR_FD_GLOB_TO,
>
> CR_FD_TMPFS_IMG,
> diff --git a/criu/include/magic.h b/criu/include/magic.h
> index deb54b1..9eff82b 100644
> --- a/criu/include/magic.h
> +++ b/criu/include/magic.h
> @@ -93,6 +93,7 @@
> #define SECCOMP_MAGIC 0x64413049 /* Kostomuksha */
> #define BINFMT_MISC_MAGIC 0x67343323 /* Apatity */
> #define AUTOFS_MAGIC 0x49353943 /* Sochi */
> +#define APPARMOR_MAGIC 0x59423047 /* Sablino */
>
> #define IFADDR_MAGIC RAW_IMAGE_MAGIC
> #define ROUTE_MAGIC RAW_IMAGE_MAGIC
> diff --git a/criu/include/protobuf-desc.h b/criu/include/protobuf-desc.h
> index 6c76b49..9c3a9c7 100644
> --- a/criu/include/protobuf-desc.h
> +++ b/criu/include/protobuf-desc.h
> @@ -59,6 +59,7 @@ enum {
> PB_BINFMT_MISC, /* 50 */
> PB_TTY_DATA,
> PB_AUTOFS,
> + PB_APPARMOR,
>
> /* PB_AUTOGEN_STOP */
>
> diff --git a/criu/lsm.c b/criu/lsm.c
> index 27ca004..cd3ef6d 100644
> --- a/criu/lsm.c
> +++ b/criu/lsm.c
> @@ -10,6 +10,7 @@
> #include "util.h"
> #include "cr_options.h"
> #include "lsm.h"
> +#include "apparmor.h"
>
> #include "protobuf.h"
> #include "images/inventory.pb-c.h"
> @@ -121,6 +122,7 @@ void kerndat_lsm(void)
> get_label = apparmor_get_label;
> lsmtype = LSMTYPE__APPARMOR;
> name = "apparmor";
> + ns_dumping_enabled = check_aa_ns_dumping();
> return;
> }
>
> @@ -158,6 +160,11 @@ int collect_lsm_profile(pid_t pid, CredsEntry *ce)
> if (get_label(pid, &ce->lsm_profile) < 0)
> return -1;
>
> + if (lsmtype == LSMTYPE__APPARMOR && collect_aa_namespace(ce->lsm_profile) < 0) {
> + pr_err("failed to collect AA namespace\n");
> + return -1;
> + }
> +
> if (ce->lsm_profile)
> pr_info("%d has lsm profile %s\n", pid, ce->lsm_profile);
>
> diff --git a/criu/protobuf-desc.c b/criu/protobuf-desc.c
> index a6807e2..ca84711 100644
> --- a/criu/protobuf-desc.c
> +++ b/criu/protobuf-desc.c
> @@ -62,6 +62,7 @@
> #include "images/seccomp.pb-c.h"
> #include "images/binfmt-misc.pb-c.h"
> #include "images/autofs.pb-c.h"
> +#include "images/apparmor.pb-c.h"
>
> struct cr_pb_message_desc cr_pb_descs[PB_MAX];
>
> diff --git a/images/Makefile b/images/Makefile
> index eb18526..533c620 100644
> --- a/images/Makefile
> +++ b/images/Makefile
> @@ -61,6 +61,7 @@ proto-obj-y += time.o
> proto-obj-y += sysctl.o
> proto-obj-y += autofs.o
> proto-obj-y += macvlan.o
> +proto-obj-y += apparmor.o
>
> CFLAGS += -iquote $(obj)/
>
> diff --git a/images/apparmor.proto b/images/apparmor.proto
> new file mode 100644
> index 0000000..0c84f80
> --- /dev/null
> +++ b/images/apparmor.proto
> @@ -0,0 +1,16 @@
> +syntax = "proto2";
> +
> +message aa_policy {
> + required string name = 1;
> + required bytes blob = 2;
> +}
> +
> +message aa_namespace {
> + required string name = 1;
> + repeated aa_policy policies = 2;
> + repeated aa_namespace namespaces = 3;
> +}
> +
> +message apparmor_entry {
> + repeated aa_namespace namespaces = 1;
> +}
> diff --git a/images/creds.proto b/images/creds.proto
> index 29fb865..467a810 100644
> --- a/images/creds.proto
> +++ b/images/creds.proto
> @@ -19,5 +19,6 @@ message creds_entry {
>
> repeated uint32 groups = 14;
>
> - optional string lsm_profile = 15;
> + optional string lsm_profile = 15;
> + optional bytes apparmor_data = 16;
> }
> diff --git a/lib/py/images/images.py b/lib/py/images/images.py
> index c593a3b..127f2b7 100644
> --- a/lib/py/images/images.py
> +++ b/lib/py/images/images.py
> @@ -454,6 +454,7 @@ handlers = {
> 'USERNS' : entry_handler(userns_entry),
> 'SECCOMP' : entry_handler(seccomp_entry),
> 'AUTOFS' : entry_handler(autofs_entry),
> + 'APPARMOR' : entry_handler(apparmor_entry),
> }
>
> def __rhandler(f):
>
More information about the CRIU
mailing list