src/tests/lxc-test-device-add-remove
src/tests/lxc-test-attach
+src/tests/lxc-test-aparmor
src/tests/lxc-test-cgpath
src/tests/lxc-test-clonetest
src/tests/lxc-test-concurrent
EXTRA_DIST = \
abstractions/container-base \
+ abstractions/container-base.in \
abstractions/start-container \
+ container-rules \
+ container-rules.base \
+ lxc-containers \
+ lxc-generate-aa-rules.py \
profiles/lxc-default \
profiles/lxc-default-with-mounting \
profiles/lxc-default-with-nesting \
- lxc-containers \
usr.bin.lxc-start
+
if ENABLE_APPARMOR
install-apparmor:
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/apparmor.d/
--- /dev/null
+The abstractions/container-base file is partially automatically
+generated. The two source files are container-rules.base and
+abstractions/container-base.in. If these file are updated,
+then
+
+1. Generate a new container-rules file using
+
+./lxc-generate-aa-rules.py container-rules.base > container-rules
+
+2. Concatenate container-base.in with container-rules using
+
+cat abstractions/container-base.in container-rules > abstractions/container-base
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
deny @{PROC}/kmem rwklx,
- deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
- deny @{PROC}/sys/kernel/*/** wklx,
# deny writes in /sys except for /sys/fs/cgroup, also allow
# fusectl, securityfs and debugfs to be mounted there (read-only)
deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,
mount fstype=proc -> /proc/,
mount fstype=sysfs -> /sys/,
- deny /sys/[^f]*/** wklx,
- deny /sys/f[^s]*/** wklx,
- deny /sys/fs/[^c]*/** wklx,
- deny /sys/fs/c[^g]*/** wklx,
- deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
mount options=(move) /sys/fs/cgroup/cgmanager/ -> /sys/fs/cgroup/cgmanager.lower/,
+
+ # generated by: lxc-generate-aa-rules.py container-rules.base
+ deny /proc/sys/kernel/[^s]*{,/**} wklx,
+ deny /proc/sys/kernel/s[^h]*{,/**} wklx,
+ deny /proc/sys/kernel/sh[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/shm*/** wklx,
+ deny /sys/[^fdc]*{,/**} wklx,
+ deny /sys/c[^l]*{,/**} wklx,
+ deny /sys/cl[^a]*{,/**} wklx,
+ deny /sys/cla[^s]*{,/**} wklx,
+ deny /sys/clas[^s]*{,/**} wklx,
+ deny /sys/class/[^n]*{,/**} wklx,
+ deny /sys/class/n[^e]*{,/**} wklx,
+ deny /sys/class/ne[^t]*{,/**} wklx,
+ deny /sys/class/net?*{,/**} wklx,
+ deny /sys/class?*{,/**} wklx,
+ deny /sys/d[^e]*{,/**} wklx,
+ deny /sys/de[^v]*{,/**} wklx,
+ deny /sys/dev[^i]*{,/**} wklx,
+ deny /sys/devi[^c]*{,/**} wklx,
+ deny /sys/devic[^e]*{,/**} wklx,
+ deny /sys/device[^s]*{,/**} wklx,
+ deny /sys/devices/[^v]*{,/**} wklx,
+ deny /sys/devices/v[^i]*{,/**} wklx,
+ deny /sys/devices/vi[^r]*{,/**} wklx,
+ deny /sys/devices/vir[^t]*{,/**} wklx,
+ deny /sys/devices/virt[^u]*{,/**} wklx,
+ deny /sys/devices/virtu[^a]*{,/**} wklx,
+ deny /sys/devices/virtua[^l]*{,/**} wklx,
+ deny /sys/devices/virtual/[^n]*{,/**} wklx,
+ deny /sys/devices/virtual/n[^e]*{,/**} wklx,
+ deny /sys/devices/virtual/ne[^t]*{,/**} wklx,
+ deny /sys/devices/virtual/net?*{,/**} wklx,
+ deny /sys/devices/virtual?*{,/**} wklx,
+ deny /sys/devices?*{,/**} wklx,
+ deny /sys/f[^s]*{,/**} wklx,
+ deny /sys/fs/[^c]*{,/**} wklx,
+ deny /sys/fs/c[^g]*{,/**} wklx,
+ deny /sys/fs/cg[^r]*{,/**} wklx,
+ deny /sys/fs/cgr[^o]*{,/**} wklx,
+ deny /sys/fs/cgro[^u]*{,/**} wklx,
+ deny /sys/fs/cgrou[^p]*{,/**} wklx,
+ deny /sys/fs/cgroup?*{,/**} wklx,
+ deny /sys/fs?*{,/**} wklx,
--- /dev/null
+ network,
+ capability,
+ file,
+ umount,
+ dbus,
+
+ # ignore DENIED message on / remount
+ deny mount options=(ro, remount) -> /,
+
+ # allow tmpfs mounts everywhere
+ mount fstype=tmpfs,
+
+ # allow mqueue mounts everywhere
+ mount fstype=mqueue,
+
+ # allow fuse mounts everywhere
+ mount fstype=fuse.*,
+
+ # allow bind mount of /lib/init/fstab for lxcguest
+ mount options=(rw, bind) /lib/init/fstab.lxc/ -> /lib/init/fstab/,
+
+ # deny writes in /proc/sys/fs but allow binfmt_misc to be mounted
+ mount fstype=binfmt_misc -> /proc/sys/fs/binfmt_misc/,
+ deny @{PROC}/sys/fs/** wklx,
+
+ # allow efivars to be mounted, writing to it will be blocked though
+ mount fstype=efivarfs -> /sys/firmware/efi/efivars/,
+
+ # block some other dangerous paths
+ deny @{PROC}/sysrq-trigger rwklx,
+ deny @{PROC}/mem rwklx,
+ deny @{PROC}/kmem rwklx,
+
+ # deny writes in /sys except for /sys/fs/cgroup, also allow
+ # fusectl, securityfs and debugfs to be mounted there (read-only)
+ mount fstype=fusectl -> /sys/fs/fuse/connections/,
+ mount fstype=securityfs -> /sys/kernel/security/,
+ mount fstype=debugfs -> /sys/kernel/debug/,
+ deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,
+ mount fstype=proc -> /proc/,
+ mount fstype=sysfs -> /sys/,
+ deny /sys/firmware/efi/efivars/** rwklx,
+ deny /sys/kernel/security/** rwklx,
+ mount options=(move) /sys/fs/cgroup/cgmanager/ -> /sys/fs/cgroup/cgmanager.lower/,
+
--- /dev/null
+ # generated by: lxc-generate-aa-rules.py container-rules.base
+ deny /proc/sys/kernel/[^s]*{,/**} wklx,
+ deny /proc/sys/kernel/s[^h]*{,/**} wklx,
+ deny /proc/sys/kernel/sh[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/shm*/** wklx,
+ deny /sys/[^fdc]*{,/**} wklx,
+ deny /sys/c[^l]*{,/**} wklx,
+ deny /sys/cl[^a]*{,/**} wklx,
+ deny /sys/cla[^s]*{,/**} wklx,
+ deny /sys/clas[^s]*{,/**} wklx,
+ deny /sys/class/[^n]*{,/**} wklx,
+ deny /sys/class/n[^e]*{,/**} wklx,
+ deny /sys/class/ne[^t]*{,/**} wklx,
+ deny /sys/class/net?*{,/**} wklx,
+ deny /sys/class?*{,/**} wklx,
+ deny /sys/d[^e]*{,/**} wklx,
+ deny /sys/de[^v]*{,/**} wklx,
+ deny /sys/dev[^i]*{,/**} wklx,
+ deny /sys/devi[^c]*{,/**} wklx,
+ deny /sys/devic[^e]*{,/**} wklx,
+ deny /sys/device[^s]*{,/**} wklx,
+ deny /sys/devices/[^v]*{,/**} wklx,
+ deny /sys/devices/v[^i]*{,/**} wklx,
+ deny /sys/devices/vi[^r]*{,/**} wklx,
+ deny /sys/devices/vir[^t]*{,/**} wklx,
+ deny /sys/devices/virt[^u]*{,/**} wklx,
+ deny /sys/devices/virtu[^a]*{,/**} wklx,
+ deny /sys/devices/virtua[^l]*{,/**} wklx,
+ deny /sys/devices/virtual/[^n]*{,/**} wklx,
+ deny /sys/devices/virtual/n[^e]*{,/**} wklx,
+ deny /sys/devices/virtual/ne[^t]*{,/**} wklx,
+ deny /sys/devices/virtual/net?*{,/**} wklx,
+ deny /sys/devices/virtual?*{,/**} wklx,
+ deny /sys/devices?*{,/**} wklx,
+ deny /sys/f[^s]*{,/**} wklx,
+ deny /sys/fs/[^c]*{,/**} wklx,
+ deny /sys/fs/c[^g]*{,/**} wklx,
+ deny /sys/fs/cg[^r]*{,/**} wklx,
+ deny /sys/fs/cgr[^o]*{,/**} wklx,
+ deny /sys/fs/cgro[^u]*{,/**} wklx,
+ deny /sys/fs/cgrou[^p]*{,/**} wklx,
+ deny /sys/fs/cgroup?*{,/**} wklx,
+ deny /sys/fs?*{,/**} wklx,
--- /dev/null
+# Run lxc-generate-aa-rules.py on this file after any modification, to generate
+# the container-rules file which is appended to container-base.in to create the
+# final abstractions/container-base.
+
+block /sys
+allow /sys/fs/cgroup/**
+allow /sys/devices/virtual/net/**
+allow /sys/class/net/**
+block /proc/sys/kernel
+allow /proc/sys/kernel/shm*
--- /dev/null
+#!/usr/bin/python3
+
+import sys
+
+blocks = []
+
+#
+# blocks is an array of paths under which we want to block by
+# default.
+#
+# blocks[0] = ['path' = '/sys', 'children' = [A,B] ]
+# blocks[1] = ['path' = '/proc/sys', 'children' = [ E ] ]
+# A = [ 'path' = 'fs', children = [C] ]
+# C = [ 'path' = 'cgroup', children = [F] ]
+# B = [ 'path' = 'class', children = [D] ]
+# D = [ 'path' = 'net', children = [F] ]
+# E = [ 'path' = 'shm*' ]
+# F = [ 'path' = '**' ]
+
+
+def add_block(path):
+ for b in blocks:
+ if b['path'] == path:
+ # duplicate
+ return
+ blocks.append({'path': path.strip(), 'children': []})
+
+
+def child_get(prev, path):
+ for p in prev:
+ if p['path'] == path:
+ return p
+ return None
+
+
+def add_allow(path):
+ # find which block we belong to
+ found = None
+ for b in blocks:
+ l = len(b['path'])
+ if len(path) <= l:
+ continue
+ if path[0:l] == b['path']:
+ found = b
+ break
+ if found is None:
+ print("allow with no previous block at %s" % path)
+ sys.exit(1)
+ p = path[l:].strip()
+ while p[:1] == "/":
+ p = p[1:]
+ prev = b['children']
+ for s in p.split('/'):
+ n = {'path': s.strip(), 'children': []}
+ tmp = child_get(prev, n['path'])
+ if tmp is not None:
+ prev = tmp
+ else:
+ prev.append(n)
+ prev = n['children']
+
+config = "config"
+if len(sys.argv) > 1:
+ config = sys.argv[1]
+with open(config) as f:
+ for x in f.readlines():
+ x.strip()
+ if x[:1] == '#':
+ continue
+ try:
+ (cmd, path) = x.split(' ')
+ except: # blank line
+ continue
+ if cmd == "block":
+ add_block(path)
+ elif cmd == "allow":
+ add_allow(path)
+ else:
+ print("Unknown command: %s" % cmd)
+ sys.exit(1)
+
+denies = []
+
+
+def collect_chars(children, ref, index):
+ r = ""
+ for c in children:
+ if index >= len(c['path']):
+ continue
+ if ref[0:index] != c['path'][0:index]:
+ continue
+ if c['path'][index] not in r:
+ r = r + c['path'][index]
+ return r
+
+
+def append_deny(s):
+ s = "%s wklx," % s
+ if s not in denies:
+ denies.append(s)
+
+
+def gen_denies(pathsofar, children):
+ for c in children:
+ for char in range(len(c['path'])):
+ if char == len(c['path'])-1 and c['path'][char] == '*':
+ continue
+ if char == len(c['path'])-2:
+ if c['path'][char:char+2] == '**':
+ continue
+ x = collect_chars(children, c['path'], char)
+ newdeny = "deny %s/%s[^%s]*{,/**}" % (pathsofar,
+ c['path'][0:char], x)
+ append_deny(newdeny)
+ if c['path'] != '**' and c['path'][len(c['path'])-1] != '*':
+ newdeny = "deny %s/%s?*{,/**}" % (pathsofar, c['path'])
+ append_deny(newdeny)
+ elif c['path'] != '**':
+ newdeny = "deny %s/%s/**" % (pathsofar, c['path'])
+ append_deny(newdeny)
+ if len(c['children']) != 0:
+ newpath = "%s/%s" % (pathsofar, c['path'])
+ gen_denies(newpath, c['children'])
+
+for b in blocks:
+ gen_denies(b['path'], b['children'])
+
+denies.sort()
+
+genby = " # generated by: lxc-generate-aa-rules.py"
+for a in sys.argv[1:]:
+ genby += " %s" % a
+print(genby)
+for d in denies:
+ print(" %s" % d)
lxc_test_list_SOURCES = list.c
lxc_test_attach_SOURCES = attach.c
lxc_test_device_add_remove_SOURCES = device_add_remove.c
+lxc_test_apparmor_SOURCES = aa.c
AM_CFLAGS=-I$(top_srcdir)/src \
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys lxc-test-lxcpath \
lxc-test-cgpath lxc-test-clonetest lxc-test-console \
lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \
- lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove
+ lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
+ lxc-test-apparmor
bin_SCRIPTS = lxc-test-autostart
--- /dev/null
+/* liblxcapi
+ *
+ * Copyright © 2014 Serge Hallyn <serge.hallyn@ubuntu.com>.
+ * Copyright © 2014 Canonical Ltd.
+ *
+ * This program 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.
+ */
+
+/* Test apparmor rules */
+#include <lxc/lxccontainer.h>
+#include "lxc/utils.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#define MYNAME "test-aa"
+
+static void try_to_remove(void)
+{
+ struct lxc_container *c;
+ c = lxc_container_new(MYNAME, NULL);
+ if (c) {
+ if (c->is_defined(c))
+ c->destroy(c);
+ lxc_container_put(c);
+ }
+}
+
+static int test_attach_write_file(void* payload)
+{
+ char *fnam = payload;
+ FILE *f;
+
+ f = fopen(fnam, "w");
+ if (f) {
+ printf("yes\n");
+ fclose(f);
+ return 1;
+ }
+ printf("no\n");
+ return 0;
+}
+
+/*
+ * try opening a file attached to a container. Return 0 on open fail. Return
+ * 1 if the file open succeeded. Return -1 if attach itself failed - perhas an
+ * older kernel.
+ */
+static int do_test_file_open(struct lxc_container *c, char *fnam)
+{
+ int fret = -1;
+ int ret;
+ pid_t pid;
+ int pipefd[2];
+ char result[1024];
+ lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
+
+ ret = pipe(pipefd);
+ if (ret < 0) {
+ fprintf(stderr, "pipe failed %d", ret);
+ return fret;
+ }
+ attach_options.stdout_fd = pipefd[1];
+ attach_options.attach_flags &= ~(LXC_ATTACH_LSM_EXEC|LXC_ATTACH_DROP_CAPABILITIES);
+ attach_options.attach_flags |= LXC_ATTACH_LSM_NOW;
+ ret = c->attach(c, test_attach_write_file, fnam, &attach_options, &pid);
+ if (ret < 0) {
+ fprintf(stderr, "attach failed");
+ goto err1;
+ }
+
+ ret = read(pipefd[0], result, sizeof(result)-1);
+ if (ret < 0) {
+ fprintf(stderr, "read failed %d", ret);
+ goto err2;
+ }
+
+ fret = 1;
+ if (strncmp(result, "no", 2) == 0)
+ fret = 0;
+
+err2:
+ wait_for_pid(pid);
+err1:
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return fret;
+}
+
+char *files_to_allow[] = { "/sys/class/net/lo/ifalias",
+ "/proc/sys/kernel/shmmax",
+ NULL };
+
+char *files_to_deny[] = { "/proc/mem", "/proc/kmem",
+ "/sys/kernel/uevent_helper",
+ "/proc/sys/fs/file-nr",
+ "/sys/kernel/mm/ksm/pages_to_scan",
+ "/proc/sys/kernel/sysrq",
+ NULL };
+
+static bool test_aa_policy(struct lxc_container *c)
+{
+ int i, ret;
+
+ for (i = 0; files_to_deny[i]; i++) {
+ ret = do_test_file_open(c, files_to_deny[i]);
+ if (ret < 0) {
+ fprintf(stderr, "attach failed; skipping test");
+ return true;
+ }
+ if (ret > 0) {
+ fprintf(stderr, "failed - opened %s\n",
+ files_to_deny[i]);
+ return false;
+ }
+ fprintf(stderr, "passed with %s\n", files_to_deny[i]);
+ }
+
+ for (i = 0; files_to_allow[i]; i++) {
+ ret = do_test_file_open(c, files_to_allow[i]);
+ if (ret < 0) {
+ fprintf(stderr, "attach failed; skipping test");
+ return true;
+ }
+ if (ret == 0) {
+ fprintf(stderr, "failed - could not open %s\n",
+ files_to_allow[i]);
+ return false;
+ }
+ fprintf(stderr, "passed with %s\n", files_to_allow[i]);
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ try_to_remove();
+ c = lxc_container_new(MYNAME, NULL);
+ if (!c) {
+ fprintf(stderr, "%s: %d: failed to load first container\n", __FILE__, __LINE__);
+ exit(1);
+ }
+
+ if (c->is_defined(c)) {
+ fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
+ goto err;
+ }
+ if (!c->set_config_item(c, "lxc.network.type", "empty")) {
+ fprintf(stderr, "%s: %d: failed to set network type\n", __FILE__, __LINE__);
+ goto err;
+ }
+ c->save_config(c, NULL);
+ if (!c->createl(c, "download", NULL, NULL, 0, "-d", "ubuntu", "-r", "trusty", "-a", "amd64", NULL)) {
+ fprintf(stderr, "%s: %d: failed to create container\n", __FILE__, __LINE__);
+ goto err;
+ }
+ c->want_daemonize(c, true);
+ if (!c->startl(c, 0, NULL)) {
+ fprintf(stderr, "Error starting container");
+ goto err;
+ }
+
+ if (!test_aa_policy(c)) {
+ c->stop(c);
+ goto err;
+ }
+
+ c->stop(c);
+
+ try_to_remove();
+ exit(0);
+
+err:
+ try_to_remove();
+ exit(1);
+}